Its been a while since I’ve written anything here. The latest thing of interest to me is Docker. This has revolutionised how I build things and I plan to write a few articles detailing how to perform some tasks using it.
What is Docker? A quick summary of Docker is small user mode OS images (typically Linux) containing just the programs required to perform a particular task. The images can easily be built and then shared. Once built an image can be run on any computer that has Docker installed.
How is Docker useful for building software? One of the more annoying things with software development is setting up a build environment. The instructions for doing so can get fiddly and often forgotten when trying to setup another computer. Docker eliminates this problem by keeping the build environment inside static docker images that will operate the same wherever they are run. A development environment need only consist of the OS of your choice (Linux, Windows, or MacOS), Docker installed, your favourite editor/IDE (eg VSCode). And for convenience its a good idea to have python installed so you can script the building.
As an example here is a set of instructions you can run in order to build the WjCryptLib for Linux using any desktop platform.
- Install Docker on your OS. (Download)
- Download WjCryptLib. (Download)
Or you can use the command:
curl -O https://github.com/WaterJuice/WjCryptLib/archive/master.zip
- Extract the WjCryptLib zip file and go to its root directory
docker run --rm -v $PWD:$PWD -w $PWD wjxx/ubuntu20.04-build cmake -H. -G Ninja -Bbuild -DCMAKE_INSTALL_PREFIX=bin/linux -DCMAKE_BUILD_TYPE=release
docker run --rm -v $PWD:$PWD -w $PWD wjxx/ubuntu20.04-build cmake --build build --target install
That is all! This will have built the binaries and put them in
bin/linux. If you are running on a Linux system you can run it. Otherwise you can run them in another Linux docker container.
docker run --rm -v $PWD:$PWD -w $PWD ubuntu:20.04 bin/linux/WjCryptLibTest
This should show the output
WjCryptLibTest ------------ Test MD5 - Pass Test SHA1 - Pass Test SHA256 - Pass Test SHA512 - Pass Test RC4 - Pass Test AES - Pass Test AES CBC - Pass Test AES CTR - Pass Test AES OFB - Pass All tests passed.
You would have noticed that the first time you ran the docker run commands it would download an image from the Internet. Once you have downloaded it, it remains in Docker and can be reused. Running an image that is already downloaded is extremely quick.
So what actually happened?
The first part of the command:
docker run --rm -v $PWD:$PWD -w $PWD wjxx/ubuntu20.04-build
This tells docker to run the image
wjxx/ubuntu20.04-build which is an image I built that contains the linux gcc compiler, CMake, and Ninja. I will describe in a later article how this image was made, but for now just know that there are many many images available on hub.docker.com that people have created for all sorts of tasks. This is just one of them.
--rm means don’t keep the container around after use (as we are not running this as an on going service).
-v $PWD:$PWD means mount the current working directory into the same location inside the docker container, this allows the docker container to access the source files that are on the host, and to write out the binaries.
-w $PWD means set the working directory inside the Docker container as the same as the current one in the host.
The rest of the command line that follows is simply the command line to be run inside the container. In this case:
cmake -H. -G Ninja -Bbuild -DCMAKE_INSTALL_PREFIX=bin/linux -DCMAKE_BUILD_TYPE=release
Which says to generate a cmake build using ninja and to set the output to
bin/linux and set build type to release mode.
The commands I have listed will work on MacOS and Linux, its slightly different on Windows simply because $PWD does not exist on Windows. On Windows you would use:
-v %CD%:%CD% -w %CD%
Though note this only works if you are working from the C: directory. Otherwise you’ll have to map things slightly differently.
Note: You don’t have to have the directory inside the Docker container the same as the host one, it just makes it easier when getting error messages because the file paths match. But there may be times you don’t want to have the name exist inside Container (for example you may not want the names in your path to show up in the debug binaries, or in the case of Windows you maybe using a drive other than C:). In this case you could use something like
-v $PWD:/tmp -w /tmp
or (for Windows):
-v %CD%:/tmp -w /tmp
In this case we have said to mount the current directory on the host into the
/tmp directory of the image.
Okay this was just a quick sample of how Docker can be used for building. This example will work anywhere without anything more than Docker itself being installed. All the settings for the build environment are inside the docker image that you didn’t have to worry about. This makes building in teams and CIs very convenient as everyone can guarantee they are using the same build system settings, but without having to compromise on what platform they develop on.
The other huge advantage of using Docker images is that you can build using all sorts of exotic build environments without having them conflict with each other.