Conan: Your Embedded Cross-Compilation Champion
Tags: conan, cmake, embedded, crosscompile
Published: 2024-07-13 11:21:31

In the continuously changing field of software development, modern programming languages such as Rust and Go have established new standards by integrating built-in package managers. These tools have greatly simplified the development process, allowing developers to easily handle dependencies, improve their workflows, and boost productivity. The convenience provided by these package managers underscores the necessity for a strong solution for C++ development.

Conan is a revolutionary package manager created for C++ developers. Conan simplifies and streamlines package management within the C++ ecosystem, making library and dependency management easier than ever. One of Conan's standout features is its strong support for cross-compiling, which is essential for developers working on embedded systems and applications that target multiple architectures.

Conan's cross-compiling capabilities simplify the development process by enabling developers to build and test their applications for different target environments from a single setup. This feature is especially beneficial for projects that need to run on various hardware configurations, ensuring compatibility and performance across the board.

Moreover, Conan supports a wide range of libraries, offering a vast repository that developers can leverage to enhance their projects. This extensive library support ensures that developers have access to the tools and resources they need, reducing the time spent on configuring and managing dependencies.

For organizations with specific requirements, Conan provides the flexibility to manage private packages using Artifactory Community Edition. This integration ensures that sensitive or proprietary packages remain secure and accessible only to authorized personnel, maintaining the integrity and confidentiality of the development process.


![CMake](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sndkppb1u2jp0dqme4e7.png)

## 1. Integrate Conan as a Dependency Using CMake

CMake's `FetchContent` module is a powerful feature introduced in CMake 3.11 that allows you to download and incorporate external content (such as source code or scripts) directly into your build process. It is particularly useful for managing project dependencies without requiring pre-installed libraries or packages.

### 1.1. Key Features of `FetchContent`

- **Download and Include External Projects**: `FetchContent` can download content from URLs, Git repositories, or local paths. This content can then be included in your build as if it were part of your source tree.
- **Version Control**: By specifying the exact URL or Git tag/branch/commit, you can ensure that your project always uses a specific version of the external content, providing stability and reproducibility.
- **Automatic Integration**: The fetched content can be automatically integrated into the CMake build system, making it straightforward to compile and link against the downloaded content.
- **Configuration and Customization**: You can configure the fetched content using CMake variables, enabling customization and fine-tuning of how the external content is built and used.

### 1.2. Resolve Canon Dependency in CMake

To handle different host operating systems using CMake's FetchContent, you can conditionally fetch and configure dependencies based on the detected operating system. For our example, we focused on implementing it on a Linux host machine.

```cmake
include(FetchContent)
FetchContent_Declare(conan
URL https://github.com/conan-io/conan/releases/download/2.3.2/conan-2.3.2-linux-x86_64.tgz
)
FetchContent_Populate(conan)
if (conan_POPULATED)
    set(CONANEXE ${conan_SOURCE_DIR}/conan)
    set(CONAN_AVAILABLE TRUE)
endif()
```


![Configuration Profile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et2kgncl2wxe40tx8oni.png)

## 2. Defining Profiles for Cross-Compiling

In Conan, profiles are used to define settings, options, environment variables, and tools that should be used during the package creation and consumption process. When cross-compiling, it's essential to distinguish between the build and host profiles, and define appropriate toolchains for each.

### 2.1. Build Profile
The build profile specifies the environment where the build tools will run. This is typically your native machine (e.g., x86_64 Linux) which compiles code.

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

### 2.2. Host Profile

The host profile specifies the environment where the compiled package will run. This can be a different architecture or platform, such as ARM for embedded systems.

```ini
[settings]
arch=armv8
build_type=Release
compiler=clang
compiler.cppstd=17
compiler.libcxx=libc++
compiler.version=12
os=Linux
[buildenv]
CC=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/bin/clang
CXX=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/bin/clang++
LD=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/bin/lld
SYSROOT=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/sysroot
CXXFLAGS=--sysroot=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/sysroot
CFLAGS=--sysroot=/usr/local/mdc_sdk_llvm/dp_gea/mdc_cross_compiler/sysroot
```


![Third-Party Libraries](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b0k67h237zx4pw79plye.png)

## 3. Third-Party Libraries Using Conan

Conan Center is a central repository of open-source packages, which includes a wide range of libraries ready to be integrated into your projects.

### 3.1. Searching for Libraries in Conan Center

- **Visit Conan Center**: Go to [Conan Center](https://conan.io/center) to browse or search for libraries.
- **Search for a Library**: Use the search bar to find the library you need. For example, if you are looking for the `eigen` library, type "eigen" in the search bar.
- **Select the Library**: Click on the library from the search results to view its details, including versions, available options, and usage instructions.

### 3.2. Creating a `conanfile.txt` File

The `conanfile.txt` file is used to define the dependencies and configurations for your project. Here’s an example `conanfile.txt` for a project that depends on `Eigen` and `Boost` libraries:

```ini
[requires]
eigen/3.4.0
boost/1.85.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout
```


![CMake](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zdsieb7ueqyyyfd4hzwn.png)

## 4. Integrating Conan with CMake

Integrating Conan with CMake is a common practice to streamline dependency management in C++ projects. Conan handles the downloading and management of dependencies, while CMake orchestrates the build process.

```cmake
if (${CONAN_AVAILABLE})
    execute_process(COMMAND "${CONANEXE}" install conanfile.txt --build=missing  --profile:build=conan.build --profile:host=conan.host
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_BINARY_DIR}/Release/generators)
include(conan_toolchain)
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS filesystem)

add_executable(${PROJECT_NAME} main.cpp)

target_link_libraries(${PROJECT_NAME} Eigen3::Eigen Boost::filesystem)
```

![Libraries](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pszyr7d2d65zyvkbi26t.png)

## 5. Use Libraries in Your Code

Now you can include and use the `Eigen` and `Boost` libraries in your `main.cpp` or other source files.

```C++
#include <iostream>
#include <Eigen/Dense>
#include <boost/filesystem.hpp>

int main() {
  // Create a 2x2 matrix
  Eigen::MatrixXd m(2, 2);
  m << 1, 2,
       3, 4;

  // Add another matrix
  Eigen::MatrixXd v(2, 2);
  v << 5, 6,
       7, 8;
  m = m + v;

  // Print the resulting matrix
  std::cout << "Resulting matrix:\n" << m << std::endl;
  boost::filesystem::path my_file = "data.txt";

  // Check if the file exists
  if (boost::filesystem::exists(my_file)) {
    std::cout << my_file << " exists." << std::endl;
  } else {
    std::cout << my_file << " does not exist." << std::endl;
  }

  // Create a directory (if it doesn't exist)
  boost::filesystem::path my_dir = "test_dir";
  if (!boost::filesystem::exists(my_dir)) {
    boost::filesystem::create_directory(my_dir);
    std::cout << "Created directory: " << my_dir << std::endl;
  }

  return 0;
}
```

## References
- [Conan](https://docs.conan.io/2/)
- [CMake](https://cmake.org/documentation/)