Solarian Programmer

My programming ramblings

Clang 5 in a Docker container for C++17 development

Posted on December 14, 2017 by Sol

If you want to try the new C++17, using Clang in a Docker container, you are in the right place. Running Clang in a container has the advantage that it is light on resources and won’t mess with your underlying OS. The last point is especially important if your host operating system is macOS, on which it is a really bad idea to directly install a binary Clang other than the one that comes with Xcode. I’ve tested the approach presented in this article on Windows 10, macOS High Sierra and Ubuntu Linux.

I assume that you have Docker installed on your machine, if not go to the Docker website and install it. After the installation, open a Terminal or, if you are on Windows, a PowerShell window and check if Docker was properly installed with:

1 docker version

This is what I see on a Mac:

 1 ~ $ docker version
 2 Client:
 3  Version: 17.12.0-ce
 4  API version: 1.35
 5  Go version:  go1.9.2
 6  Git commit:  c97c6d6
 7  Built: Wed Dec 27 20:03:51 2017
 8  OS/Arch: darwin/amd64
 9 
10 Server:
11  Engine:
12   Version:  17.12.0-ce
13   API version:  1.35 (minimum version 1.12)
14   Go version: go1.9.2
15   Git commit: c97c6d6
16   Built:  Wed Dec 27 20:12:29 2017
17   OS/Arch:  linux/amd64
18   Experimental: true
19 ~ $

Next, we are going to use a Dockerfile that will prepare a Ubuntu image with the latest Clang:

 1 # Use the latest stable Ubuntu version (currently 16.04)
 2 FROM ubuntu:16.04
 3 
 4 # Make sure the image is updated, install some prerequisites,
 5 # Download the latest version of Clang (official binary) for Ubuntu
 6 # Extract the archive and add Clang to the PATH
 7 RUN apt update && apt install -y \
 8   xz-utils \
 9   build-essential \
10   curl \
11   && rm -rf /var/lib/apt/lists/* \
12   && curl -SL http://releases.llvm.org/5.0.1/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz \
13   | tar -xJC . && \
14   mv clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-16.04 clang_5.0.1 && \
15   echo 'export PATH=/clang_5.0.1/bin:$PATH' >> ~/.bashrc && \
16   echo 'export LD_LIBRARY_PATH=/clang_5.0.1/lib:LD_LIBRARY_PATH' >> ~/.bashrc
17 
18 # Start from a Bash prompt
19 CMD [ "/bin/bash" ]

You can get the last version of the above Dockerfile from my GitHub (Hint: use the Download button if you don’t have a Git client on your machine):

1 git clone https://github.com/sol-prog/Clang-in-Docker.git
2 cd Clang-in-Docker

In order to build an up do date image, you can use (make sure you are in the same folder as the Dockerfile):

1 docker build -t ubuntu_clang_image .

the above will create a new image named ubuntu_clang_image. Once the process is finished, you can check the list of available images with:

1 docker image ls

Now, we need to create a container from ubuntu_clang_image. First, we’ll create a folder that will be shared between our host OS and the Docker container, this will let us use a our preferred C++ text editor (or IDE) from our host:

1 cd ~
2 mkdir clang_tests

If your host OS is Windows 7, 8 or 10, you need to do an extra step. Make sure that you shared your drives with Docker (this will let you share a local Windows folder with a Docker container). For example, if you want to share drive C, open the Docker’s Settings panel check the drive and press Apply:

Sharing drive C with Docker on Windows 10

Next, we’ll create a container named ubuntu_clang from the above image:

1 docker run -it -v $PWD/clang_tests:/clang_tests --name ubuntu_clang ubuntu_clang_image /bin/bash

Please note that you need to run the above command only once!

At this point, you should be at a Bash prompt in your container, e.g. this is what I see on my machine:

1 ~ $ docker run -it -v $PWD/clang_tests:/clang_tests --name ubuntu_clang ubuntu_clang_image /bin/bash
2 root@706615db0f9c:/#

Check the version of Clang with:

1 clang --version

you should see something like this:

1 root@706615db0f9c:/# clang --version
2 clang version 5.0.1 (tags/RELEASE_501/final)
3 Target: x86_64-unknown-linux-gnu
4 Thread model: posix
5 InstalledDir: /clang_5.0.1/bin
6 root@706615db0f9c:/#

Now, from your host machine, go to clang_tests and create a new file named if_test.cpp, with the next C++ code:

 1 // if block with init-statement, the example is a bit silly, 
 2 // but you can use it to check if your compiler supports C++17:
 3 #include <iostream>
 4 
 5 int main() {
 6     // if block with init-statement:
 7     if(int a = 5; a < 8) {
 8         std::cout << "Local variable a is < 8\n";
 9     } else {
10         std::cout << "Local variable a is >= 8\n";
11     }
12     return 0;
13 }

The file should be visible from the container too, cd to clang_tests and check what files are present, this is what I see:

1 root@706615db0f9c:/# cd clang_tests/
2 root@706615db0f9c:/clang_tests# ls
3 if_test.cpp
4 root@706615db0f9c:/clang_tests#

You can compile and run the above C++ program with:

1 clang++ -std=c++17 -stdlib=libc++ -Wall -pedantic if_test.cpp -o if_test
2 ./if_test

This is what I see on my container:

1 root@706615db0f9c:/clang_tests# clang++ -std=c++17 -stdlib=libc++ -Wall -pedantic if_test.cpp -o if_test
2 root@706615db0f9c:/clang_tests# ./if_test
3 Local variable a is < 8
4 root@706615db0f9c:/clang_tests#

Next, let’s try to compile a program that uses the C++17 Filesystem. Please note, that at the time of this writing the filesystem header is still marked as experimental by Clang 5.0.1 so, for now, you need to use the experimental namespace:

1 #include <iostream>
2 #include <experimental/filesystem>
3 
4 int main() {
5     for(auto &file : std::experimental::filesystem::recursive_directory_iterator("./")) {
6         std::cout << file << '\n';
7     }
8 }

Save the above file as test_fs.cpp and compile it with:

1 clang++ -std=c++17 -stdlib=libc++ -Wall -pedantic test_fs.cpp -o test_fs -lc++experimental

This is what I see on my container if I run the above code (you should see a list of files that are present in the folder where you have the executable):

1 root@706615db0f9c:/clang_tests# clang++ -std=c++17 -stdlib=libc++ -Wall -pedantic test_fs.cpp -o test_fs -lc++experimental
2 root@706615db0f9c:/clang_tests# ./test_fs
3 "./test_fs"
4 "./if_test"
5 "./if_test.cpp"
6 "./test_fs.cpp"
7 root@706615db0f9c:/clang_tests#

If you want to leave the container, just write exit and you should be at your default shell prompt, e.g.:

1 root@706615db0f9c:/clang_tests# exit
2 exit
3 ~ $

Next time when you want to start the container, you can do it with:

1 docker start -ia ubuntu_clang

If you don’t remember the name of your Docker container, use the next command to list all available containers:

1 docker ps -a

If you are interested to learn more about modern C++ I would recommend reading A tour of C++ by Bjarne Stroustrup.

or Effective Modern C++ by Scott Meyers.

comments powered by Disqus