[docker] Refactor container to be more developer-friendly
Clean up the Docker container image to make it more developer-friendly.
- Ensure that all OpenTitan dependencies are available.
- Provide a convenient way to run commands within the container as
current local user, ensuring that file permissions of mounted volumes
match.
- Give the user within the container sudo rights, if it wants to use it
e.g. to install additional packages.
- Provide a convenient way to load additional configuration into the
environment, e.g. the path to a license server.
- Generally clean up the Dockerfile and make it consistent.
- Remove some additionally installed packages where we don't know what
they are used for. We can always add them back if we find out the use
case.
- Use the general mailing list as "maintainer" email address.
Signed-off-by: Philipp Wagner <phw@lowrisc.org>
diff --git a/util/container/Dockerfile b/util/container/Dockerfile
index 9d9e14e..c64eb47 100644
--- a/util/container/Dockerfile
+++ b/util/container/Dockerfile
@@ -20,12 +20,13 @@
ARG RUST_VERSION
LABEL version="1.0"
-LABEL description="OpenTitan container for hardware development."
-LABEL maintainer="miguelosorio@google.com"
+LABEL description="OpenTitan development container."
+LABEL maintainer="opentitan-dev@opentitan.org"
-WORKDIR /tools
+# Use bash as default shell.
+RUN ln -sf /bin/bash /bin/sh
-# Add OBS repository to apt sources
+# Add OBS repository to apt sources.
RUN OBS_URL="https://download.opensuse.org/repositories"; \
OBS_PATH="/home:/phiwag:/edatools/xUbuntu_18.04"; \
REPO_URL="${OBS_URL}${OBS_PATH}"; \
@@ -41,68 +42,76 @@
} && \
echo "$EDATOOLS_REPO" > "$TMPDIR/obs.list" && \
mv "$TMPDIR/obs.asc" /etc/apt/trusted.gpg.d/obs.asc && \
- mv "$TMPDIR/obs.list" /etc/apt/sources.list.d/edatools.list && \
- apt-get update
+ mv "$TMPDIR/obs.list" /etc/apt/sources.list.d/edatools.list
-# Install (and cleanup) required packages (from apt-requirements.txt)
-# The list of extra packages is leftover from before this Dockerfile used
-# apt-requirements.txt
+# Install system packages
#
-# This also adds `locales` and `locales-all` so we can set the locale to utf-8
+# Install (and cleanup) required packages (from apt-requirements.txt).
+# Also add some additional packages for the use within this container and for
+# developer convenience:
+# - gosu and sudo are used by the scripting to make the image more convenient
+# to use.
+# - locales and locales-all are required to set the locale.
+# - minicom and screen are useful to see UART communication.
+# - dc and time are requirements of Synopsys VCS.
COPY apt-requirements.txt /tmp/apt-requirements.txt
-RUN echo "verilator-${VERILATOR_VERSION}" >>/tmp/apt-requirements.txt && \
- echo "openocd-${OPENOCD_VERSION}" >>/tmp/apt-requirements.txt && \
- sed -i -e '/^$/d' -e '/^#/d' -e 's/#.*//' /tmp/apt-requirements.txt && \
- xargs apt-get install -y </tmp/apt-requirements.txt && \
- apt-get update && apt-get install -y \
+RUN echo "verilator-${VERILATOR_VERSION}" >>/tmp/apt-requirements.txt \
+ && echo "openocd-${OPENOCD_VERSION}" >>/tmp/apt-requirements.txt \
+ && sed -i -e '/^$/d' -e '/^#/d' -e 's/#.*//' /tmp/apt-requirements.txt \
+ && apt-get update \
+ && xargs apt-get install -y </tmp/apt-requirements.txt \
+ && apt-get install -y \
+ sudo \
+ gosu \
locales \
locales-all \
- gnupg2 \
- libc6-i386 \
- libtool \
minicom \
- screen && \
- apt-get clean; \
+ screen \
+ dc \
+ time \
+ && apt-get clean; \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
+# RISC-V device toolchain
+COPY util/get-toolchain.py /tmp/get-toolchain.py
+RUN /tmp/get-toolchain.py -r ${RISCV_TOOLCHAIN_TAR_VERSION} \
+ && rm -f /tmp/get-toolchain.py
+
# Set Locale to utf-8 everywhere
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
-ENV PATH "/root/.local/bin:${PATH}"
+# Scripting for use within this container.
+COPY util/container/start.sh /start.sh
+COPY util/container/sudoconf /etc/sudoers.d/dev
+
+# Add the development user (UID/GID to be replaced).
+RUN groupadd dev \
+ && useradd --create-home -g dev dev \
+ && usermod -p '*' dev \
+ && passwd -u dev
+
+# All subsequent steps are performed as user.
+USER dev
+
+# Install Rust plus packages.
+COPY sw/vendor/rustup/rustup-init.sh /tmp/rustup-init.sh
+RUN /tmp/rustup-init.sh -y --default-toolchain ${RUST_VERSION}
+
+# Install Python plus packages.
+#
# Explicitly updating pip and setuptools is required to have these tools
# properly parse Python-version metadata, which some packages uses to
# specify that an older version of a package must be used for a certain
# Python version. If that information is not read, pip installs the latest
# version, which then fails to run.
-RUN python3 -m pip install --user -U pip setuptools
-
+ENV PATH "/home/dev/.local/bin:${PATH}"
COPY python-requirements.txt /tmp/python-requirements.txt
-RUN pip3 install --user -r /tmp/python-requirements.txt
+RUN python3 -m pip install --user -U pip setuptools \
+ && python3 -m pip install --user -r /tmp/python-requirements.txt \
+ --no-warn-script-location
-COPY util/get-toolchain.py /tmp/get-toolchain.py
-RUN /tmp/get-toolchain.py -r ${RISCV_TOOLCHAIN_TAR_VERSION}
-RUN rm /tmp/python-requirements.txt /tmp/get-toolchain.py
+USER root
-COPY sw/vendor/rustup/rustup-init.sh /tmp/rustup-init.sh
-# This file does not create a user, so we install rustup and cargo under
-# /tools.
-ENV RUSTUP_HOME /tools/.rustup
-ENV CARGO_HOME /tools/.cargo
-# Permissions are relaxed so that the container user can also download
-# dependencies during build.
-RUN /tmp/rustup-init.sh -y \
- --default-toolchain ${RUST_VERSION} && \
- chmod -R o=u ${RUSTUP_HOME} ${CARGO_HOME}
-
-# Use bash as default shell
-RUN ln -sf /bin/bash /bin/sh
-
-# Include tools in PATH.
-ENV PATH "/tools/verilator/${VERILATOR_VERSION}/bin:${CARGO_HOME}/bin:${PATH}"
-
-# Configures default container user.
-ENV USER ot
-
-ENTRYPOINT /bin/bash
+ENTRYPOINT [ "/start.sh" ]
diff --git a/util/container/README.md b/util/container/README.md
index 256984b..b4fa6cc 100644
--- a/util/container/README.md
+++ b/util/container/README.md
@@ -16,8 +16,25 @@
## Using the Container
-To run container in interactive mode:
+Run the container with `docker run`, mapping the current working directory to
+`/home/dev/src`. The user `dev` will have the same user ID as the current user
+on the host (you!), causing all files created by the `dev` user in the container
+to be owned by the current user on the host.
-```shell
-$ docker run -it -v $REPO_TOP:/repo -w /repo opentitan --user $(id -u):$(id -g)
+If you'd like to initialize your shell environment in a specific way, you can
+pass an environment variable `USER_CONFIG=/path/to/a/script.sh`. Otherwise,
+remove the `--env USER_CONFIG` argument from the invocation shown below.
+
+The script passed through this mechanism will be sourced. The path of the script
+must be within the container, e.g. in the OpenTitan repository directory.
+
```
+docker run -t -i \
+ -v $(pwd):/home/dev/src \
+ --env DEV_UID=$(id -u) --env DEV_GID=$(id -g) \
+ --env USER_CONFIG=/home/dev/src/docker-user-config.sh \
+ opentitan:latest \
+ bash
+```
+
+You can use `sudo` within the container to gain root permissions.
diff --git a/util/container/start.sh b/util/container/start.sh
new file mode 100755
index 0000000..07fde16
--- /dev/null
+++ b/util/container/start.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# Map the "dev" user to an UID passed in as environment variable to ensure
+# files are written by the same UID/GID into mounted volumes.
+DEV_UID=${DEV_UID:-1000}
+DEV_GID=${DEV_GID:-1000}
+groupmod -o -g "$DEV_GID" dev >/dev/null 2>&1
+usermod -o -u "$DEV_UID" dev >/dev/null 2>&1
+
+# Load user configuration.
+test -f "${USER_CONFIG}" && export BASH_ENV=${USER_CONFIG}
+
+cd /home/dev
+exec gosu dev:dev /bin/bash -c "$@"
diff --git a/util/container/sudoconf b/util/container/sudoconf
new file mode 100644
index 0000000..0f89e62
--- /dev/null
+++ b/util/container/sudoconf
@@ -0,0 +1,2 @@
+# Give dev user account root permissions in container
+dev ALL=(ALL) NOPASSWD:ALL