| # A guide to Dockerfiles for building LLVM | |
| ## Introduction | |
| You can find a number of sources to build docker images with LLVM components in | |
| `llvm/utils/docker`. They can be used by anyone who wants to build the docker | |
| images for their own use, or as a starting point for someone who wants to write | |
| their own Dockerfiles. | |
| We currently provide Dockerfiles with `debian12` and `nvidia-cuda` base images. | |
| We also provide an `example` image, which contains placeholders that one would need | |
| to fill out in order to produce Dockerfiles for a new docker image. | |
| ### Why? | |
| Docker images provide a way to produce binary distributions of | |
| software inside a controlled environment. Having Dockerfiles to build docker images | |
| inside LLVM repo makes them much more discoverable than putting them into any other | |
| place. | |
| ### Docker basics | |
| If you've never heard about Docker before, you might find this section helpful | |
| to get a very basic explanation of it. | |
| [Docker](https://www.docker.com/) is a popular solution for running programs in | |
| an isolated and reproducible environment, especially to maintain releases for | |
| software deployed to large distributed fleets. | |
| It uses Linux kernel namespaces and cgroups to provide a lightweight isolation | |
| inside currently running Linux kernel. | |
| A single active instance of dockerized environment is called a *docker | |
| container*. | |
| A snapshot of a docker container filesystem is called a *docker image*. | |
| One can start a container from a prebuilt docker image. | |
| Docker images are built from a so-called *Dockerfile*, a source file written in | |
| a specialized language that defines instructions to be used when building | |
| the docker image (see [official | |
| documentation](https://docs.docker.com/engine/reference/builder/) for more | |
| details). A minimal Dockerfile typically contains a base image and a number | |
| of RUN commands that have to be executed to build the image. When building a new | |
| image, docker will first download your base image, mount its filesystem as | |
| read-only and then add a writable overlay on top of it to keep track of all | |
| filesystem modifications, performed while building your image. When the build | |
| process is finished, a diff between your image's final filesystem state and the | |
| base image's filesystem is stored in the resulting image. | |
| ## Overview | |
| The `llvm/utils/docker` folder contains Dockerfiles and simple bash scripts to | |
| serve as a basis for anyone who wants to create their own Docker image with | |
| LLVM components, compiled from sources. The sources are checked out from the | |
| upstream git repository when building the image. | |
| The resulting image contains only the requested LLVM components and a few extra | |
| packages to make the image minimally useful for C++ development, e.g. libstdc++ | |
| and binutils. | |
| The interface to run the build is `build_docker_image.sh` script. It accepts a | |
| list of LLVM repositories to checkout and arguments for CMake invocation. | |
| If you want to write your own docker image, start with an `example/` subfolder. | |
| It provides an incomplete Dockerfile with (very few) FIXMEs explaining the steps | |
| you need to take in order to make your Dockerfiles functional. | |
| ## Usage | |
| The `llvm/utils/build_docker_image.sh` script provides a rather high degree of | |
| control on how to run the build. It allows you to specify the projects to | |
| checkout from git and provide a list of CMake arguments to use during when | |
| building LLVM inside docker container. | |
| Here's a very simple example of getting a docker image with clang binary, | |
| compiled by the system compiler in the debian12 image: | |
| ```bash | |
| ./llvm/utils/docker/build_docker_image.sh \ | |
| --source debian12 \ | |
| --docker-repository clang-debian12 --docker-tag "staging" \ | |
| -p clang -i install-clang -i install-clang-resource-headers \ | |
| -- \ | |
| -DCMAKE_BUILD_TYPE=Release | |
| ``` | |
| Note that a build like that doesn't use a 2-stage build process that | |
| you probably want for clang. Running a 2-stage build is a little more intricate, | |
| this command will do that: | |
| ```bash | |
| # Run a 2-stage build. | |
| # LLVM_TARGETS_TO_BUILD=Native is to reduce stage1 compile time. | |
| # Options, starting with BOOTSTRAP_* are passed to stage2 cmake invocation. | |
| ./build_docker_image.sh \ | |
| --source debian12 \ | |
| --docker-repository clang-debian12 --docker-tag "staging" \ | |
| -p clang -i stage2-install-clang -i stage2-install-clang-resource-headers \ | |
| -- \ | |
| -DLLVM_TARGETS_TO_BUILD=Native -DCMAKE_BUILD_TYPE=Release \ | |
| -DBOOTSTRAP_CMAKE_BUILD_TYPE=Release \ | |
| -DCLANG_ENABLE_BOOTSTRAP=ON -DCLANG_BOOTSTRAP_TARGETS="install-clang;install-clang-resource-headers" | |
| ``` | |
| This will produce a new image `clang-debian12:staging` from the latest | |
| upstream revision. | |
| After the image is built you can run bash inside a container based on your image | |
| like this: | |
| ```bash | |
| docker run -ti clang-debian12:staging bash | |
| ``` | |
| Now you can run bash commands as you normally would: | |
| ```bash | |
| root@80f351b51825:/# clang -v | |
| clang version 19.1.7 (trunk 524462) | |
| Target: x86_64-unknown-linux-gnu | |
| Target: x86_64-unknown-linux-gnu | |
| Thread model: posix | |
| InstalledDir: /bin | |
| ``` | |
| ## Which image should I choose? | |
| We currently provide two images: Debian12-based and nvidia-cuda-based. They | |
| differ in the base image that they use, i.e. they have a different set of | |
| preinstalled binaries. Debian12 is very minimal, nvidia-cuda is larger, but has | |
| preinstalled CUDA libraries and allows access to a GPU, installed on your | |
| machine. | |
| If you need a minimal Linux distribution with only clang and libstdc++ included, | |
| you should try Debian12-based image. | |
| If you want to use CUDA libraries and have access to a GPU on your machine, | |
| you should choose nvidia-cuda-based image and use [nvidia-docker](https://github.com/NVIDIA/nvidia-docker) to run your docker containers. Note | |
| that you don't need nvidia-docker to build the images, but you need it in order | |
| to have access to a GPU from a docker container that is running the built | |
| image. | |
| If you have a different use-case, you could create your own image based on | |
| `example/` folder. | |
| Any docker image can be built and run using only the docker binary, i.e. you can | |
| run debian12 build on Fedora or any other Linux distribution. You don't need to | |
| install CMake, compilers or any other clang dependencies. It is all handled | |
| during the build process inside Docker's isolated environment. | |
| ## Stable build | |
| If you want a somewhat recent and somewhat stable build, use the | |
| `branches/google/stable` branch, i.e. the following command will produce a | |
| Debian12-based image using the latest `google/stable` sources for you: | |
| ```bash | |
| ./llvm/utils/docker/build_docker_image.sh \ | |
| -s debian12 --d clang-debian12 -t "staging" \ | |
| --branch branches/google/stable \ | |
| -p clang -i install-clang -i install-clang-resource-headers \ | |
| -- \ | |
| -DCMAKE_BUILD_TYPE=Release | |
| ``` | |
| ## Minimizing docker image size | |
| Due to how Docker's filesystem works, all intermediate writes are persisted in | |
| the resulting image, even if they are removed in the following commands. | |
| To minimize the resulting image size we use [multi-stage Docker builds](https://docs.docker.com/develop/develop-images/multistage-build/). | |
| Internally Docker builds two images. The first image does all the work: installs | |
| build dependencies, checks out LLVM source code, compiles LLVM, etc. | |
| The first image is only used during build and does not have a descriptive name, | |
| i.e. it is only accessible via the hash value after the build is finished. | |
| The second image is our resulting image. It contains only the built binaries | |
| and not any build dependencies. It is also accessible via a descriptive name | |
| (specified by `-d` and `-t` flags). | |