Using components and editable packages
It is possible to define components in the layout()
method, to support the case of editable
packages. That is, if we want to put a package in editable
mode, and that package defines components
, it is necessary to define the components layout correctly in the layout()
method. Let’s see it in a real example.
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/examples/conanfile/layout/editable_components
There we find a greetings
subfolder and package, that contains 2 libraries, the hello
library and the bye
library. Each one is modeled as a component
inside the package recipe:
greetings/conanfile.py
class GreetingsConan(ConanFile):
name = "greetings"
version = "0.1"
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain"
exports_sources = "src/*"
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def layout(self):
cmake_layout(self, src_folder="src")
# This "includedirs" starts in the source folder, which is "src"
# So the components include dirs is the "src" folder (includes are
# intended to be included as ``#include "hello/hello.h"``)
self.cpp.source.components["hello"].includedirs = ["."]
self.cpp.source.components["bye"].includedirs = ["."]
# compiled libraries "libdirs" will be inside the "build" folder, depending
# on the platform they will be in "build/Release" or directly in "build" folder
bt = "." if self.settings.os != "Windows" else str(self.settings.build_type)
self.cpp.build.components["hello"].libdirs = [bt]
self.cpp.build.components["bye"].libdirs = [bt]
def package(self):
copy(self, "*.h", src=self.source_folder,
dst=join(self.package_folder, "include"))
copy(self, "*.lib", src=self.build_folder,
dst=join(self.package_folder, "lib"), keep_path=False)
copy(self, "*.a", src=self.build_folder,
dst=join(self.package_folder, "lib"), keep_path=False)
def package_info(self):
self.cpp_info.components["hello"].libs = ["hello"]
self.cpp_info.components["bye"].libs = ["bye"]
self.cpp_info.set_property("cmake_file_name", "MYG")
self.cpp_info.set_property("cmake_target_name", "MyGreetings::MyGreetings")
self.cpp_info.components["hello"].set_property("cmake_target_name", "MyGreetings::MyHello")
self.cpp_info.components["bye"].set_property("cmake_target_name", "MyGreetings::MyBye")
While the location of the hello
and bye
libraries in the final package is in the final lib
folder, then nothing special is needed in the package_info()
method, beyond the definition of the components. In this case, the customization of the CMake generated filenames and targets is also included, but it is not necessary for this example.
The important part is the layout()
definition. Besides the common cmake_layout
, it is necessary to define the location of the components headers (self.cpp.source
as they are source code) and the location of the locally built libraries. As the location of the libraries depends on the platform, the final self.cpp.build.components["component"].libdirs
depends on the platform.
With this recipe we can put the package in editable mode and locally build it with:
$ conan editable add greetings
$ conan build greetings
# we might want to also build the debug config
In the app
folder we have a package recipe to build 2 executables, that link with the greeting
package components. The app/conanfile.py
recipe there is simple, the build()
method builds and runs both example
and example2
executables that are built with CMakeLists.txt
:
# Note the MYG file name, not matching the package name,
# because the recipe defined "cmake_file_name"
find_package(MYG)
add_executable(example example.cpp)
# Note the MyGreetings::MyGreetings target name, not matching the package name,
# because the recipe defined "cmake_target_name"
# "example" is linking with the whole package, both "hello" and "bye" components
target_link_libraries(example MyGreetings::MyGreetings)
add_executable(example2 example2.cpp)
# "example2" is only using and linking "hello" component, but not "bye"
target_link_libraries(example2 MyGreetings::MyHello)
$ conan build app
hello: Release!
bye: Release!
If you now go to the bye.cpp
source file and modify the output message, then build greetings
and app
locally, the final output message for the “bye” component library should change:
$ conan build greetings
$ conan build app
hello: Release!
adios: Release!