Building for multiple configurations: Release, Debug, Static and Shared

Please, first clone the sources to recreate this project. You can find them in the examples2 repository in GitHub:

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/different_configurations

So far, we built a simple CMake project that depended on the zlib library and learned about tool_requires, a special type of requirements for build-tools like CMake. In both cases, we did not specify anywhere that we wanted to build the application in Release or Debug mode, or if we wanted to link against static or shared libraries. That is because Conan, if not instructed otherwise, will use a default configuration declared in the 『default profile』. This default profile was created in the first example when we run the conan profile detect command. Conan stores this file in the /profiles folder, located in the Conan user home. You can check the contents of your default profile by running the conan config home command to get the location of the Conan user home and then showing the contents of the default profile in the /profiles folder:

$ conan config home
Current Conan home: /Users/tutorial_user/.conan2

# output the file contents
$ cat /Users/tutorial_user/.conan2/profiles/default
[settings]
os=Macos
arch=x86_64
compiler=apple-clang
compiler.version=14.0
compiler.libcxx=libc++
compiler.cppstd=gnu11
build_type=Release
[options]
[tool_requires]
[env]

# The default profile can also be checked with the command "conan profile show"

As you can see, the profile has different sections. The [settings] section is the one that has information about things like the operating system, architecture, compiler, and build configuration.

When you call a Conan command setting the --profile argument, Conan will take all the information from the profile and apply it to the packages you want to build or install. If you don’t specify that argument it’s equivalent to call it with --profile=default. These two commands will behave the same:

$ conan install . --build=missing
$ conan install . --build=missing --profile=default

You can store different profiles and use them to build for different settings. For example, to use a build_type=Debug, or adding a tool_requires to all the packages you build with that profile. We will create a debug profile to try building with different configurations:

<conan home>/profiles/debug
[settings]
os=Macos
arch=x86_64
compiler=apple-clang
compiler.version=14.0
compiler.libcxx=libc++
compiler.cppstd=gnu11
build_type=Debug

Modifying settings: use Debug configuration for the application and its dependencies

Using profiles is not the only way to set the configuration you want to use. You can also override the profile settings in the Conan command using the --settings argument. For example, you can build the project from the previous examples in Debug configuration instead of Release.

Before building, please check that we modified the source code from the previous example to show the build configuration the sources were built with:

#include <stdlib.h>
...

int main(void) {
    ...
    #ifdef NDEBUG
    printf("Release configuration!\n");
    #else
    printf("Debug configuration!\n");
    #endif

    return EXIT_SUCCESS;
}

Now let’s build our project for Debug configuration:

$ conan install . --output-folder=build --build=missing --settings=build_type=Debug

As we explained above, this is the equivalent of having debug profile and running these command using the --profile=debug argument instead of the --settings=build_type=Debug argument.

This conan install command will check if we already have the required libraries in the local cache (Zlib) for Debug configuration and obtain them if not. It will also update the build configuration in the conan_toolchain.cmake and CMakePresets.json files that the CMakeToolchain generator creates so that when we build the application it’s built in Debug configuration. Now build your project as you did in the previous examples and check in the output how it was built in Debug configuration:

Windows
# assuming Visual Studio 15 2017 is your VS version and that it matches your default profile
$ cd build
$ cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
$ cmake --build . --config Debug
$ Debug\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11
Debug configuration!
Linux, macOS
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug
$ cmake --build .
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
ZLIB VERSION: 1.2.11
Debug configuration!

Modifying options: linking the application dependencies as shared libraries

So far, we have been linking Zlib statically in our application. That’s because in the Zlib’s Conan package there’s an attribute set to build in that mode by default. We can change from static to shared linking by setting the shared option to True using the --options argument. To do so, please run:

$ conan install . --output-folder=build --build=missing --options=zlib/1.3.1:shared=True

Doing this, Conan will install the Zlib shared libraries, generate the files to build with them and, also the necessary files to locate those dynamic libraries when running the application.

備註

Options are defined per-package. In this case we were defining that we wanted that specific version of zlib/1.3.1 as a shared library. If we had other dependencies and we want all of our dependencies (whenever possible) as shared libraries, we would use -o *:shared=True, with the * pattern that matches all package references.

Let’s build the application again after configuring it to link Zlib as a shared library:

Windows
$ cd build
# assuming Visual Studio 15 2017 is your VS version and that it matches your default profile
$ cmake .. -G "Visual Studio 15 2017" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
$ cmake --build . --config Release
...
[100%] Built target compressor
Linux, macOS
$ cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build .
...
[100%] Built target compressor

Now, if you try to run the compiled executable you will see an error because the executable can’t find the shared libraries for Zlib that we just installed.

Windows
$ Release\compressor.exe
(on a pop-up window) The code execution cannot proceed because zlib1.dll was not found. Reinstalling the program may fix this problem.
# This error depends on the console being used and may not always pop up.
# It could run correctly if the console gets the zlib dll from a different path.
Linux
$ ./compressor
./compressor: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
Macos
$ ./compressor
./compressor: dyld[41259]: Library not loaded: @rpath/libz.1.dylib

This is because shared libraries (.dll in windows, .dylib in macOS and .so in Linux), are loaded at runtime. That means that the application executable needs to know where the required shared libraries are when it runs. On Windows, the dynamic linker will search in the same directory, then in the PATH directories. On macOS, it will search in the directories declared in DYLD_LIBRARY_PATH, and on Linux it will use LD_LIBRARY_PATH.

Conan provides a mechanism to define those variables and make it possible, for executables, to find and load these shared libraries. This mechanism is the VirtualRunEnv generator. If you check the output folder, you will see that Conan generated a new file called conanrun.sh/bat. This is the result of automatically invoking that VirtualRunEnv generator when we activated the shared option when doing the conan install. This generated script will set the PATH, LD_LIBRARY_PATH, DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH environment variables so that executables can find the shared libraries.

Activate the virtual environment, and run the executables again:

Windows
$ conanrun.bat
$ Release\compressor.exe
Uncompressed size is: 233
Compressed size is: 147
...
Linux, macOS
$ source conanrun.sh
$ ./compressor
Uncompressed size is: 233
Compressed size is: 147
...

Just as in the previous example with the VirtualBuildEnv generator, when we run the conanrun.sh/bat script, a deactivation script called deactivate_conanrun.sh/bat is created to restore the environment. Source or run it to do so:

Windows
$ deactivate_conanrun.bat
Linux, macOS
$ source deactivate_conanrun.sh

設定(settings)和選項(options)之間的區別

You may have noticed that for changing between Debug and Release configuration we used a Conan setting, but when we set shared mode for our executable we used a Conan option. Please note the difference between settings and options:

  • 設定(settings)通常是由客戶端機器定義的專案級組態。例如作業系統、編譯器或建置組態等,這些將是多個 Conan 軟體包共有的,並且為其中一個設定定義預設值是沒有意義的。舉例來說,Conan 軟體包宣告「Visual Studio」作為預設編譯器是沒有意義的,因為這是由最終使用者定義的,如果他們在 Linux 環境下工作,這可能就沒有意義。

  • options are intended for package-specific configuration that can be set to a default value in the recipe. For example, one package can define that its default linkage is static, and this is the linkage that should be used if consumers don’t specify otherwise.

軟體包 ID 的概念簡介

When consuming packages like Zlib with different settings and options, you might wonder how Conan determines which binary to retrieve from the remote. The answer lies in the concept of the package_id.

The package_id is an identifier that Conan uses to determine the binary compatibility of packages. It is computed based on several factors, including the package’s settings, options, and dependencies. When you modify any of these factors, Conan computes a new package_id to reference the corresponding binary.

Here’s a breakdown of the process:

  1. 確定設定和選項:Conan 首先會取得使用者的輸入設定和選項。這些可以來自命令行或設定檔,例如 –settings=build_type=Debug–profile=debug

  2. 計算軟體包 ID:Conan 會根據 settingsoptions 和相依項的目前值計算一個雜湊值。該雜湊值即為 package_id,代表二進位軟體包的唯一識別。

  3. Fetch the Binary: Conan then checks its cache or the specified remote for a binary package with the computed package_id. If it finds a match, it retrieves that binary. If not, Conan can build the package from source or indicate that the binary is missing.

在我們的教學中,當我們使用不同的 settingsoptions 來使用 Zlib 時,Conan 會使用 package_id 來確保它擷取了與我們指定組態相符的正確二進位軟體包。