Summary
The core issue was a missing Conan package definition for the mylib dependency. The myapp recipe attempted to use find_package(mylib) in CMake, but there was no Conan recipe to export or package the mylib headers. Consequently, the mylib::mylib target was undefined, causing the linker error. Additionally, the CMake configuration referenced a discovery_devices target, which did not match the defined myapp executable target.
Root Cause
The root cause was an incorrect dependency modeling within the Conan ecosystem.
- Missing Recipe: There was no
conanfile.pyformylibto generate the necessarymylibConfig.cmakefile. - Undefined Target: Because
mylibwas not a Conan package,find_package(mylib)failed silently or produced an empty result, leavingmylib::mylibundefined. - Target Mismatch: The
target_link_librariescommand inCMakeLists.txtreferenced a target nameddiscovery_devicesinstead of the actual executable targetmyapp.
Why This Happens in Real Systems
In real-world systems, this pattern occurs when developers try to integrate legacy code or internal libraries into a Conan-based workflow without fully encapsulating them as packages.
- Hybrid Build Systems: Teams often mix raw file copying with package management, assuming Conan can consume raw headers simply by listing them in
exports_sources. - Incomplete Transitions: When migrating from raw CMake to Conan, developers sometimes stop halfway, adding Conan logic to the app but treating the library as a simple subdirectory rather than a first-class package.
- Copy-Paste Errors: CMake boilerplate is frequently reused; referencing the wrong target name (
discovery_devicesinstead ofmyapp) is a common oversight during rapid prototyping.
Real-World Impact
The immediate impact is a broken build pipeline.
- Compilation Failure: The build fails with “undefined reference” or “target not found” errors because the linker cannot resolve symbols from
mylib. - Wasted Debugging Time: Engineers spend time investigating include paths and compiler flags when the actual issue is the missing CMake configuration generation.
- Inconsistent Environments: Without a proper package, the library is not versioned or cached correctly, leading to reproducibility issues across different developer machines or CI runners.
Example or Code
To fix this, mylib must be packaged. Below is the corrected conanfile.py for mylib and the corrected CMakeLists.txt for myapp.
mylib/conanfile.py
from conan import ConanFile
from conan.tools.cmake import cmake_layout, CMake, CMakeToolchain, CMakeDeps
import os
class MylibConan(ConanFile):
name = "mylib"
version = "1.0"
settings = "os", "compiler", "build_type", "arch"
exports_sources = "include/*", "src/*", "CMakeLists.txt"
package_type = "library"
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
deps = CMakeDeps(self)
deps.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
# Copy headers explicitly if not handled by cmake.install()
copy(self, "*.hpp", src=os.path.join(self.source_folder, "include"),
dst=os.path.join(self.package_folder, "include"))
def package_info(self):
self.cpp_info.libs = ["mylib"]
myapp/CMakeLists.txt (Corrected)
cmake_minimum_required(VERSION 3.15)
project(myapp CXX)
add_executable(myapp src/myapp.cpp src/main.cpp)
find_package(mylib REQUIRED)
# Corrected target name from 'discovery_devices' to 'myapp'
target_link_libraries(myapp PRIVATE mylib::mylib)
How Senior Engineers Fix It
Senior engineers approach this by enforcing strict separation of concerns and automated validation.
- Define the Package: Create a standalone
conanfile.pyformylib. This ensuresmylibis a versioned entity in the Conan cache. - Standardize CMake: Use
find_packageto locate the dependency andtarget_link_librariesto consume it. This leverages CMake’s modern target-based architecture. - Validate Locally: Before integrating, use
conan create mylib/1.0@to build and package the library. This proves the package is valid. - Correct CMake Targets: Ensure the target name in
target_link_librariesmatches the executable defined inadd_executable. - Automate: Set up a CI pipeline that builds the library as a package first, then consumes it in the application build.
Why Juniors Miss It
Juniors often miss this because they treat Conan as a magic file copier rather than a package manager.
- Assumption of “Include” Magic: They assume that simply adding
include/*toexports_sourcesmakes the headers available to the consumer’s compiler include path automatically, without the necessarypackage_info()configuration. - CMake Confusion: They may not fully understand that
find_packagerelies on config files generated by the package build, not just the presence of source files. - Copy-Paste Reliance: They copy CMake commands (like
target_link_libraries) without verifying that the target names (myappvsdiscovery_devices) match their specific project structure.