Creating a Conan package using a Docker runner
Warning
This feature is experimental and subject to breaking changes. See the Conan stability section for more information.
First of all you need to have the Docker daemon installed and running, plus Conan and the docker
Python package. This tutorial assumes that you are running Conan inside a Python virtual environment, skip the first line if you already have the docker
Python package installed in your virtual environment.
# install docker in your virtual environment if you don't have it already installed
$ pip install conan docker
$ docker ps
$ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Now we are going to create create simple cmake_lib
Conan template to later run inside Docker using the runner feature. Let’s create the Conan package and a Dockerfile inside our project folder.
$ cd </my/runner/folder>
$ mkdir mylib
$ cd mylib
$ conan new cmake_lib -d name=mylib -d version=0.1
$ tree
.
├── CMakeLists.txt
├── conanfile.py
├── include
│ └── mylib.h
├── src
│ └── mylib.cpp
└── test_package
├── CMakeLists.txt
├── conanfile.py
└── src
└── example.cpp
Dockerfile
FROM ubuntu:22.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
cmake \
python3 \
python3-pip \
python3-venv \
&& rm -rf /var/lib/apt/lists/*
RUN pip install conan
$ cd </my/runner/folder>/mylib
$ tree
.
...
├── Dockerfile
...
Now, we need to define two new profiles inside the conan profiles
folder. Replace </my/runner/folder>
with your real project folder path.
docker_example_host
profile
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
[runner]
type=docker
dockerfile=</my/runner/folder>/mylib
cache=copy
remove=true
docker_example_build
profile
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
We are going to start from a totally clean environment, without any containers, images or conan package.
$ conan list "*:*"
Found 0 pkg/version recipes matching * in local cache
$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
Now, it’s time to create our library mylib
using our new runner definition.
$ conan create . --version 0.1 -pr:h docker_example_host -pr:b docker_example_build
If we split and analyze the command output, we can see what is happening and where the commands are being executed.
1. Standard conan execution.
======== Exporting recipe to the cache ========
mylib/0.1: Exporting package recipe: </my/runner/folder>/mylib/conanfile.py
mylib/0.1: Copied 1 '.py' file: conanfile.py
mylib/0.1: Copied 1 '.txt' file: CMakeLists.txt
mylib/0.1: Copied 1 '.h' file: mylib.h
mylib/0.1: Copied 1 '.cpp' file: mylib.cpp
mylib/0.1: Exported to cache folder: /Users/davidsanfal/.conan2/p/mylib4abd06a04bdaa/e
mylib/0.1: Exported: mylib/0.1#8760bf5a311f01cc26f3b95428203210 (2024-07-08 12:22:01 UTC)
======== Input profiles ========
Profile host:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
Profile build:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
2. Build docker image
**********************************************
* Building the Docker image: my-conan-runner *
**********************************************
Dockerfile path: '</my/runner/folder>/mylib/Dockerfile'
Docker build context: '</my/runner/folder>/mylib'
Step 1/4 : FROM ubuntu:22.04
...
---> 2bcf70201cce
Successfully built 2bcf70201cce
Successfully tagged conan-runner-default:latest
3. Save the local cache running conan cache save
.
***********************************************************************************
* Save host cache in: </my/runner/folder>/mylib/.conanrunner/local_cache_save.tgz *
***********************************************************************************
Found 1 pkg/version recipes matching * in local cache
Saving mylib/0.1: mylib4abd06a04bdaa
4. Create and initialize the docker container.
*********************************
* Creating the docker container *
*********************************
*****************************************
* Container conan-runner-docker running *
*****************************************
5. Check if the container has a conan version with the runner feature.
*******************************************
* Running in container: "conan --version" *
*******************************************
Conan version 2.5.0
6. Initialize the container conan cache using the host copy running conan cache restore
.
***********************************************************************************************************
* Running in container: "conan cache restore "/root/conanrunner/mylib/.conanrunner/local_cache_save.tgz"" *
***********************************************************************************************************
Restore: mylib/0.1 in mylib4abd06a04bdaa
Local Cache
mylib
mylib/0.1
revisions
8760bf5a311f01cc26f3b95428203210 (2024-07-08 12:22:19 UTC)
packages
recipe_folder: mylib4abd06a04bdaa
7. Run the conan create inside the container and build “mylib”.
*********************************************************************************************************************************************************
* Running in container: "conan create /root/conanrunner/mylib --version 0.1 -pr:h docker_example_host -pr:b docker_example_build -f json > create.json" *
*********************************************************************************************************************************************************
======== Exporting recipe to the cache ========
mylib/0.1: Exporting package recipe: /root/conanrunner/mylib/conanfile.py
mylib/0.1: Copied 1 '.py' file: conanfile.py
mylib/0.1: Copied 1 '.txt' file: CMakeLists.txt
mylib/0.1: Copied 1 '.cpp' file: mylib.cpp
mylib/0.1: Copied 1 '.h' file: mylib.h
mylib/0.1: Exported to cache folder: /root/.conan2/p/mylib4abd06a04bdaa/e
mylib/0.1: Exported: mylib/0.1#8760bf5a311f01cc26f3b95428203210 (2024-07-08 12:22:20 UTC)
======== Input profiles ========
Profile host:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
Profile build:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux
======== Computing dependency graph ========
Graph root
cli
Requirements
mylib/0.1#8760bf5a311f01cc26f3b95428203210 - Cache
======== Computing necessary packages ========
mylib/0.1: Forced build from source
Requirements
mylib/0.1#8760bf5a311f01cc26f3b95428203210:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe - Build
======== Installing packages ========
-------- Installing package mylib/0.1 (1 of 1) --------
...
[ 50%] Building CXX object CMakeFiles/example.dir/src/example.cpp.o
[100%] Linking CXX executable example
[100%] Built target example
======== Testing the package: Executing test ========
mylib/0.1 (test package): Running test()
mylib/0.1 (test package): RUN: ./example
mylib/0.1: Hello World Release!
mylib/0.1: __x86_64__ defined
mylib/0.1: _GLIBCXX_USE_CXX11_ABI 1
mylib/0.1: __cplusplus201703
mylib/0.1: __GNUC__11
mylib/0.1: __GNUC_MINOR__4
mylib/0.1 test_package
8. Copy just the package created inside the container using the pkglist.json
info from the previous conan create
, restore this new package inside the host cache running a conan cache save
and remove the container.
************************************************************************************************************************************
* Running in container: "conan cache save --list=pkglist.json --file "/root/conanrunner/mylib"/.conanrunner/docker_cache_save.tgz" *
************************************************************************************************************************************
Saving mylib/0.1: mylib4abd06a04bdaa
Saving mylib/0.1:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe: b/mylib503035e4ee8ae/p
Saving mylib/0.1:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe metadata: b/mylib503035e4ee8ae/d/metadata
Local Cache
mylib
mylib/0.1
revisions
8760bf5a311f01cc26f3b95428203210 (2024-07-08 12:22:20 UTC)
packages
8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe
revisions
ded6547554ff2306db5250451340fa43
package_folder: b/mylib503035e4ee8ae/p
metadata_folder: b/mylib503035e4ee8ae/d/metadata
info
settings
os: Linux
arch: x86_64
compiler: gcc
compiler.cppstd: gnu17
compiler.libcxx: libstdc++11
compiler.version: 11
build_type: Release
options
fPIC: True
shared: False
recipe_folder: mylib4abd06a04bdaa
******************************************************************************************
* Restore host cache from: </my/runner/folder>/mylib/.conanrunner/docker_cache_save.tgz *
******************************************************************************************
Restore: mylib/0.1 in mylib4abd06a04bdaa
Restore: mylib/0.1:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe in b/mylib503035e4ee8ae/p
Restore: mylib/0.1:8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe metadata in b/mylib503035e4ee8ae/d/metadata
**********************
* Stopping container *
**********************
**********************
* Removing container *
**********************
If we now check the status of our conan and docker cache, we will see the new mylib package compile for Linux and the new docker image but we don’t have any container because we define remove=true
$ conan list "*:*"
Found 1 pkg/version recipes matching * in local cache
Local Cache
mylib
mylib/0.1
revisions
8760bf5a311f01cc26f3b95428203210 (2024-07-08 12:33:28 UTC)
packages
8631cf963dbbb4d7a378a64a6fd1dc57558bc2fe
info
settings
arch: x86_64
build_type: Release
compiler: gcc
compiler.cppstd: gnu17
compiler.libcxx: libstdc++11
compiler.version: 11
os: Linux
options
fPIC: True
shared: False
$ docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-conan-runner latest 2bcf70201cce 11 minutes ago 531MB
What we have just done is to compile a library from scratch inside a Docker container without running any Docker command and retrieve the generated packages in a totally transparent and easily debuggable way thanks to our terminal output.
In this way, we can work as we have always done regardless of whether it is on our machine or in a container, without several open terminals and having the result of each operation in the same cache, being able to reuse the compiled packages from a previous compilation in another container automatically and transparently.