Containerized Development
Modern development is moving towards packaging tools in containers. There are several benefits to containerizing development tools:
- A simpler tool deployment process, the binary image with all dependencies can simply be pulled from a server and run.
- An automated and consistent deployment process that can be shared between development machines and cloud based build servers.
- A consistent set of tools for all team members.
- The ability to build with General Purpose IDE’s rather than whatever tools a device vendor may provide.
- Isolation of dependencies used for different devices and build targets.
For RISC-V there are a few more benefits:
- A “recipe” to consistently build the vendor neutral tools at, such as https://github.com/riscv-collab/riscv-gnu-toolchain.
- New versions of the tools with support for new extensions etc can be easily deployed.
I’ve put together a set of Docker images and Docker Compose build and run configurations with the aim of deploying them with GitHub’s workflows.
They are located here: https://github.com/five-embeddev/build-and-verify
Image Design
Each image creates a default WORKDIR
location /project
that is
owned by the docker user. The local development directory can be bind mounted to that path.
The path to the tools is always added to the PATH
environment variable.
The container runs everything as user docker_user
with UID of 1000 by default.
Examples
To build a target binary call docker
directly or setup a
docker-compose
configuration compose.yaml
with the container
environment. The command line to be run in the container is passed as the arguments following the container run
command and it’s arguments.
e.g. Compile test.c
in the current directory with the
riscv-gnu-toolchain
and delete the container after running:
docker \
run \
--rm \
-v `pwd`:/project \
five_embeddev/riscv_gnu_toolchain_dev_env \
riscv32-unknown-elf-gcc test.c -o test.elf
OR user docker-compose
with all of that captured in a compose.yaml
file:
version: "3"
services:
build_gnu_toolchain:
image: five_embeddev/riscv_gnu_toolchain_dev_env
volumes:
- .:/project
command: ["riscv32-unknown-elf-gcc", "test.c", "-o", "test.elf" ]
Then docker-compose
can simply run the build.
docker-compose run build_gnu_toolchain
Image Details
The images have been setup with a docker user (by default UID 1000),
and the tools are installed to /opt
or to the docker user’s home
directory and added to the PATH
environment variable. There are
Makefiles
to build the images, if those are used the current user
UID is used by the docker image default user. That allows the user’s
file system to be mounted within the container and file ownership
handled correctly. (By default docker containers runs as root
, which
can result in root owned target binaries.)
When tools are compiled from source the default method is to shallow
clone the source code within the container and build there. This
captures the build process in the Dockerfile
, but is inefficient due
to the docker caching mechanism. The riscv-gnu-toolchain
build
script does this but requires a large amount of resources.
Due to the resource requirements of building the entire toolchain the
riscv-gnu-toolchain-2
build script stores the source and build files
in host file system and mounts into the container via docker
volumes. Those can’t be used by a docker build process, so the build
process is more complex and relies on the Makefile
. This build
script should be preferred.