Summary
When integrating Boost, Boost.Asio, and a local library with custom include paths into a project using Conan, the core requirement is to correctly define local source paths in the conanfile.py alongside remote package dependencies. The mistake often lies in treating local libraries as Conan packages prematurely or failing to expose custom include directories to the build system via Conan’s environment and generation hooks.
Root Cause
The primary root cause in this scenario is the misconfiguration of the package_info() method or the absence of a CMakeToolchain/CMakeDeps generator setup for local headers. When a user adds a local library with .hpp files in a subdirectory (e.g., include/libs), they must explicitly tell Conan where these headers are located so consumers can find them.
Specifically:
- The user attempts to install Boost (remote) and link a local library (source) simultaneously.
- Without a custom
conanfile.pyfor the local library or a propercpp_infodefinition in the consumerconanfile.py, the build system (e.g., CMake) cannot locate the local.hppfiles.
Why This Happens in Real Systems
In real-world development, teams often mix third-party packages (like Boost) with proprietary in-house libraries.
- Conan’s Default Behavior: Conan primarily manages binary packages. It does not automatically include local source subdirectories in the global include path unless explicitly told to via
cpp_info.includedirs. - Include Path Conflicts: Developers often assume that adding a folder to their IDE’s include path is sufficient. However, for a reproducible build managed by Conan, the Conan generated files (e.g.,
conan_toolchain.cmake) must propagate these paths to the build system. - Boost Dependencies: Boost is modular.
boost-asiois often a header-only component, but it depends on system libraries and other Boost modules. If the local library requiresboost-asio, therequireslist inconanfile.pymust be precise to avoid linker errors.
Real-World Impact
- Build Failures: The immediate impact is build errors such as
fatal error: boost/asio.hpp: No such file or directoryorfatal error: mylib/header.hpp: No such file or directory. - Onboarding Friction: New developers waste hours configuring their local IDE include paths instead of coding, as the project dependencies are not self-contained in
conanfile.py. - CI/CD Pipeline Breakage: Automated pipelines fail because the environment lacks the manual IDE configurations, leading to inconsistent builds between local machines and CI servers.
- Version Conflicts: If Boost versions are not pinned, transitive dependencies might resolve to different versions, causing ABI incompatibilities at runtime.
Example or Code
Below is an example conanfile.py that defines a Conan package for a local library (named mylib) and a consumer conanfile.py that uses it alongside boost and boost-asio.
1. Local Library conanfile.py (in include/libs/mylib/conanfile.py)
This package exports its .hpp files so they can be consumed by other packages.
from conan import ConanFile
from conan.tools.files import copy
from os.path import join
class MyLibConan(ConanFile):
name = "mylib"
version = "1.0"
exports_sources = "*.hpp" # Exports header files in this directory
def package(self):
# Copy headers to the package include folder
copy(self, "*.hpp", src=self.source_folder, dst=join(self.package_folder, "include"))
def package_info(self):
# Define the include directory so consumers can find the headers
self.cpp_info.includedirs = ["include"]
2. Consumer conanfile.py (Main Application)
This file declares the dependencies for both Boost (remote) and the local library.
from conan import ConanFile
from conan.tools.cmake import cmake_layout
class MyAppConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
requires = "boost/1.81.0", "mylib/1.0" # Requires remote Boost and local mylib
generators = "CMakeDeps", "CMakeToolchain" # Essential for CMake integration
def layout(self):
cmake_layout(self)
How Senior Engineers Fix It
Senior engineers address this by ensuring complete dependency isolation and correct build system integration:
- Create a Conan Recipe for Local Code: Instead of treating local libraries as loose source folders, seniors wrap them in a minimal
conanfile.py(as shown above). This makes the local library a first-class citizen in the dependency graph. - Use Generators Correctly: They explicitly define
generators = "CMakeDeps", "CMakeToolchain"(orVirtualBuildEnv). This ensures that the paths to Boost and the local library headers are automatically injected into the build environment. - Leverage
cpp_info: They useself.cpp_info.libsandself.cpp_info.includedirsin thepackage_info()method to ensure the consumer’s build system knows exactly which libraries to link and where to find headers. - Contextualize Boost: They specifically verify if
boostis used as a header-only library or requires linking. Forboost-asio, they ensure system-level dependencies (like OpenSSL if applicable) are also included in therequireslist.
Why Juniors Miss It
Juniors often miss this configuration because they conflate source code organization with dependency management:
- Hardcoding Paths: Juniors often manually add relative paths (e.g.,
../include/libs) to theirCMakeLists.txtor compiler flags. This works on their machine but breaks when the project is cloned elsewhere or run in CI. - Overlooking
package_info: When creating a local package, they might forget to implementpackage_info(). Without it, Conan installs the package but doesn’t expose the headers or libraries to the consumer, resulting in “file not found” errors. - Confusion on Header-Only vs. Binary: They may not realize that
boost-asiois largely header-only but still requiresboostas a base. They might attempt to install only specific Boost modules without realizing the transitive dependencies required for linking. - Missing Generators: They might forget to add
generators = "CMakeDeps"in the consumerconanfile.py, leading to missingFindBoost.cmakeor similar configuration files during the CMake configuration step.