Cross-Compilation with Conan
Learn to cross-compile C++ projects with Conan, using build and host profiles to manage dependencies for different platforms.
In the last lesson, we integrated Conan into our project. We saw how its binary-first approach can speed up clean builds by avoiding the need to compile every dependency from source. We used a single, default profile to describe our native build environment.
But, how does this work when wer're cross-compiling? What happens when the machine we're building on is different from the machine we're building for? Conan has a different approach here than what we've seen previously. Rather than manually writing toolchain files, we rely more heavily on Conan's profile system.
Recap: The Manual CMake Toolchain File
In our lesson on , we learned that the native CMake way to handle this is with a toolchain file. This file explicitly tells CMake about the target system and provides absolute paths to the cross-compilers.
A toolchain file for cross-compiling to Windows from a Unix-like host might look like this:
toolchains/manual-mingw.cmake
# 1. Set the target system
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# 2. Specify the cross-compilers
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_C_FLAGS_INIT "-static -static-libgcc")
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_CXX_FLAGS_INIT
"-static -static-libgcc -static-libstdc++")
set(CMAKE_EXE_LINKER_FLAGS_INIT
"-static-libgcc -static-libstdc++")
# 3. Configure search for external dependencies
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Conan's Abstraction: The Profile as a Toolchain
Conan offers a higher-level solution. Instead of writing a script that tells CMake where the tools are, you write a Conan profile that describes what the target platform is.
What needs to go in any given profile may require some research, as it depends on the the platform you're targetting and the compiler you're using. A fairly minimalist Conan profile for the same Windows target using mingw32-gcc
might look something like this:
profiles/mingw-windows.profile
[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=gcc
# Compiler-specific settings we want to use
compiler.version=15
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.exception=seh
compiler.threads=posix
[conf]
tools.build:compiler_executables={"c": "x86_64-w64-mingw32-gcc", "cpp": "x86_64-w64-mingw32-g++"}
# Static linking to remove dependency on MinGW DLLs
tools.build:sharedlinkflags=["-static"]
tools.build:exelinkflags=["-static"]
When we use this profile, Conan takes on the responsibility of:
- Finding a cross-compiler on our system that matches this description.
- Finding or building Conan packages that match this exact configuration.
- Generating a
conan_toolchain.cmake
file that contains all the low-level details that CMake needs.
The Build/Host Context
As usual with cross-compilation, we need to consider two different environments simultenously - the environment where we're building the project, and the environment where the code is eventually going to be run. In Conan terminology, this is the build/host context.
- The
build
context describes the machine you are building on (your machine). - The
host
context describes the machine you are building for (the target machine).
Note that the naming convention here is different to what other tools use, and what we introduced previously. Most other tools use the word "host" to refer to the machine that is performing the build. Unfortunately, Conan's terminology uses it in the exact opposite way. The "host" within Conan's context is the machine that our program will ultimately run on.
When you cross-compile, you need two profiles: one for the build
context and one for the host
context.
This is because some dependencies are tools that need to run during the build itself. The most obvious example is CMake itself, but we'll see some other examples soon, such as code generators and documentation generators. These tools must be compiled for the build
machine. Regular library dependencies, like spdlog
, must be compiled for the host
machine.

By using two separate profiles, Conan can resolve the entire dependency graph correctly by, for example, fetching macOS ARM libraries for our Apple build machine, but Windows binaries for the host (target) machine.
Practical Example: Building for Windows with MinGW
Let's walk through the process of cross-compiling our GreeterApp
from a macOS machine to a Windows executable again. But, this time, we'll use Conan.
As a test application, we can use the same Windows code we used in previous examples. The following program pops up a Windows MessageBox
, and uses spdlog
to create a log file next to the executable when we close the program:
app/src/main.cpp
#include <windows.h>
#include <spdlog/spdlog.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
auto logger{spdlog::basic_logger_mt(
"file_logger", "log.txt"
)};
logger->info("Program started");
MessageBox(
nullptr,
"I was cross-compiled from a Mac!",
"Hello Windows",
MB_OK
);
logger->info("Program shut down");
// Flush logs to ensure they're written
spdlog::shutdown();
return 0;
}
We'll also use the same conanfile.txt
from the previous lesson to handle the spdlog dependency:
conanfile.txt
[requires]
spdlog/1.15.3
[generators]
CMakeDeps
CMakeToolchain
Step 1: Create the Host Profile
For Conan profiles that are intended to be shared alongside our project, we should store them within a directory in our project structure. Let's call this directory profiles
and, inside it, we'll create the mingw-windows.profile
file we saw earlier.
profiles/mingw-windows.profile
[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=gcc
# Compiler-specific settings we want to use
compiler.version=15
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.exception=seh
compiler.threads=posix
[conf]
tools.build:compiler_executables={"c": "x86_64-w64-mingw32-gcc", "cpp": "x86_64-w64-mingw32-g++"}
# Static linking to remove dependency on MinGW DLLs
tools.build:sharedlinkflags=["-static"]
tools.build:exelinkflags=["-static"]
This profile assumes you have a MinGW-w64 cross-compiler installed (e.g., via Homebrew on macOS or apt
on Linux) and that its executables are in your system PATH
.
Step 2: Run conan install
with Two Profiles
Now, let's set up a clean build directory within our project root. Let's first navigate there using the cd
command:
cd /path/to/project-root
We can create a directory in our current location using the mkdir
command, and then navigate into it using cd
as usual. We'll call our clean build directory build-win
in this example.
mkdir build-win
cd build-win
From this location, we then run conan install
. This time, we provide both a build and a host profile:
conan install .. \
--profile:build=default \
--profile:host=../profiles/mingw-windows.profile \
--build=missing
...
Install finished successfully
Let's break down this command:
conan install ..
: Reads theconanfile.txt
from the parent directory.-profile:build=default
: For any build tools, use ourdefault
native profile.-profile:host=../profiles/mingw-windows.profile
: For our application's libraries, use the Windows target profile we just created.-build=missing
: As before, this tells Conan to compile from source if it can't find a matching pre-compiled binary for our target.
Conan will now resolve our required libraries, downloading or building Windows versions of spdlog
and its dependencies. It will then generate a conan_toolchain.cmake
file in our current directory (build-win
).
Step 3: Configure and Build with CMake
The generated conan_toolchain.cmake
file now contains all of our cross-compilation requirements, so the rest of the process looks the same as it did before. We simply invoke the configure step with this CMAKE_TOOLCHAIN_FILE
variable, and we provide the build type:
From inside the build-win
directory, the command would look like this:
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release
Note that we must match the CMAKE_BUILD_TYPE
to the build_type
we specified in our Conan host profile.
Now, build the project:
cmake --build .
If you inspect the build-win/app
directory, you'll find a GreeterApp.exe
file. We can verify it's a Windows executable using the file
command, or by running it in Windows or an emulator:
file app/GreeterApp.exe
app/GreeterApp.exe: PE32+ executable (console) x86-64, for MS Windows

After running the application, we should also see a log file created with help from our Conan-managed spdlog dependency:
logs.txt
[2025-08-22 10:57:21.499] [file_logger] [info] Program started
[2025-08-22 10:57:26.165] [file_logger] [info] Program shut down
Summary
Conan's profile-based approach provides an alternative solution for C++ cross-compilation, abstracting away the low-level details of toolchain management.
- Profiles as Abstractions: Instead of manual toolchain files, Conan uses profiles to describe the target platform. Conan finds the tools and binaries that match the description.
- Build vs. Host Context: Cross-compilation requires two profiles: one for the
build
machine (where tools run) and one for thehost
machine (where the final application runs). - Seamless CMake Integration: The
conan install
command, when given both profiles, generates a singleconan_toolchain.cmake
file that configures your entire cross-build, requiring no changes to yourCMakeLists.txt
. - Portable Workflow: This approach keeps your project's build logic clean and portable, while the platform-specific details are neatly encapsulated in shareable profile files.
CMake Presets
Learn the fundamentals of CMake Presets. This lesson introduces CMakePresets.json
to simplify complex build commands.