使用 Docker 运行器创建 Conan 包

警告

此功能处于实验阶段,可能会发生重大更改。有关更多信息,请参阅Conan 稳定性部分。

首先,您需要安装并运行 Docker 守护程序,以及 Conan 和 docker Python 包。本教程假设您在 Python 虚拟环境中运行 Conan,如果您已经在虚拟环境中安装了 docker Python 包,请跳过第一行。

# 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

现在,我们将创建一个简单的 cmake_lib Conan 模板,以便稍后使用运行器功能在 Docker 中运行。让我们在项目文件夹中创建 Conan 包和 Dockerfile。

$ 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
...

现在,我们需要在 conan 的 profiles 文件夹中定义两个新的配置文件。将 </my/runner/folder> 替换为您真实的项目文件夹路径。

docker_example_host 配置文件

[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 配置文件

[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=11
os=Linux

我们将从一个完全干净的环境开始,没有任何容器、镜像或 Conan 包。

$ 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

现在,是时候使用我们新的运行器定义创建我们的库 mylib 了。

$ conan create . --version 0.1 -pr:h docker_example_host -pr:b docker_example_build

如果我们拆分并分析命令输出,我们可以看到发生了什么以及命令在哪里执行。

1. 标准 Conan 执行。

======== 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. 构建 Docker 镜像

**********************************************
* 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. 运行 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. 创建并初始化 Docker 容器。

*********************************
* Creating the docker container *
*********************************

*****************************************
* Container conan-runner-docker running *
*****************************************

5. 检查容器是否具有具有运行器功能的 Conan 版本。

*******************************************
* Running in container: "conan --version" *
*******************************************

Conan version 2.5.0

6. 使用主机副本运行 conan cache restore 初始化容器 Conan 缓存。

***********************************************************************************************************
* 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. 在容器内运行 conan create 并构建“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. 使用上一个 conan create 中的 pkglist.json 信息复制容器内创建的包,运行 conan cache save 将此新包恢复到主机缓存中,并删除容器。

************************************************************************************************************************************
* 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 *
**********************

如果我们现在检查 Conan 和 Docker 缓存的状态,我们将看到为 Linux 编译的新 mylib 包和新的 Docker 镜像,但我们没有任何容器,因为我们定义了 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

我们刚刚做的是在 Docker 容器内从头开始编译库,无需运行任何 Docker 命令,并通过终端输出以完全透明且易于调试的方式检索生成的包。

这样,无论是在我们的机器上还是在容器中,我们都可以像往常一样工作,无需多个打开的终端,并且每个操作的结果都在同一个缓存中,能够自动且透明地重用先前编译中其他容器的已编译包。