Migrating the recipes
We introduced changes to Conan 1.X versions so you can start migrating your recipes to do a smooth transition to
Conan 2.0.
Python import statements
All the imports from the
conanspackage have to be replaced. The Conan 2.0 ones are in theconanpackage. Note the plural.The “tools” functions are now organized in different packages, you can check the complete reference here.
from conans import ConanFile, tools
from conan import ConanFile
from conan.tools.files import save, load
from conan.tools.gnu import AutotoolsToolchain, AutotoolsDeps
from conan.tools.microsoft import unix_path, VCVars, is_msvc
from conan.errors import ConanInvalidConfiguration
from conan.errors import ConanException
...
Requirements
Use
self.test_requires()to define test requirements instead of the legacyself.build_requires(..., force_host_context).Use
self.tool_requires()to define the legacy build_requires.
from conans import ConanFile
class Pkg(Conanfile):
...
def build_requirements(self):
self.build_requires("nasm/2.15.05")
self.build_requires("gtest/0.1", force_host_context=True)
from conan import ConanFile
class Pkg(Conanfile):
...
def build_requirements(self):
self.tool_requires("nasm/2.15.05")
self.test_requires("gtest/0.1")
Settings
Do not use dictionary expressions in your recipe
settingsdefinition (likesettings = {"os": ["Windows", "Linux"]}. This way of limiting supported configurations by one recipe will be removed. Use thevalidate()method instead to raiseConanInvalidConfigurationif strictly necessary to fail fast for unsupported configurations.from conan import ConanFile class Pkg(Conanfile): settings = "os", "arch", "compiler" ... def validate(self): if self.settings.os == "Macos": raise ConanInvalidConfiguration("Macos not supported")
In Conan 2, removing a setting, for example,
del self.settings.compiler.libcxxin theconfigure()method, will raise an exception if the setting doesn’t exist. It has to be protected with try/except:def configure(self): try: # In windows, with msvc, the compiler.libcxx doesn't exist, so it will raise. del self.settings.compiler.libcxx except Exception: pass
Options
The definition of the default_options attribute has changed when referring to a dependency. It is related to the
unified patterns in the command line.
from conans import ConanFile
class Pkg(Conanfile):
default_options = {"pkg:some_option": "value"}
from conan import ConanFile
class Pkg(Conanfile):
# "pkg/*:some_option" or ""pkg/1.0:some_option" or "pkg*:some_option" would be valid
default_options = {"pkg/*:some_option": "value"}
The layout() method
The layout method is not mandatory but very recommended to:
Give better support for
editablepackages.Work with local commands,
conan install+conan source+conan build.
If your recipe is using CMake, you might want to use the cmake_layout(self):
from conan import ConanFile from conan.tools.cmake import cmake_layout class Pkg(Conanfile): def layout(self): cmake_layout(self)
A typical anti-pattern in the recipes that can be solved with a layout() declaration would be:
From:from conans import ConanFile, tools class Pkg(Conanfile): @property def _source_subfolder(self): return "source_subfolder" def source(self): tools.get(**self.conan_data["sources"][self.version], destination=self._source_subfolder, strip_root=True)To:from conan import ConanFile from conan.tools.layout import basic_layout from conan.tools.files import get class Pkg(Conanfile): @property def layout(self): basic_layout(self, src_folder="source") def source(self): get(self, **self.conan_data["sources"][self.version], strip_root=True)
Declaring the layout, the variables self.source_folder, self.build_folder will point to the correct folder, both in
the cache or locally when using local methods, it is always recommended to use these when performing disk operations
(read, write, copy, etc).
If you are using editables, the external template files are going to be removed. Use
the layout() method definition instead.
Read more about the layout feature and the reference of the layout() method.
Adjusting the cpp_info objects
You can adjust the cpp_info in the layout method too, not only for a package in the cache, that was typically done
in the package_info() method using the self.cpp_info, but for editable packages (to reuse a conan package
that is being developed in a local directory):
def layout(self):
# This will be automatically copied to self.cpp_info
# This information is relative to the self.package_folder
self.cpp.package.includedirs.append("other_includes")
# This information is relative to the self.build_folder
self.cpp.build.libdirs = ["."]
self.cpp.build.bindirs = ["bin"]
# This information is relative to the self.source_folder
self.cpp.source.includedirs = ["."]
The scm attribute
The scm attribute won’t exist in Conan 2.0. You have to start using the export() and source() methods
to mimic the same behavior:
The
export()method is responsible for capturing the “coordinates” of the current URL and commit. The newconan.tools.scm.Gitcan be used for this (do not use the legacyGithelper but this one)The
export()method, after capturing the coordinates, can store them in theconandata.ymlusing theupdate_conandata()helper functionThe
source()method can use the information inself.conan_datacoming from exportedconandata.ymlfile to do a clone and checkout of the matching code. The newconan.tools.scm.Gitcan be used for this purpose.
from conans import ConanFile, tools
class Pkg(Conanfile):
scm = {
"type": "git",
"url": "auto",
"revision": "auto",
}
from conan import ConanFile
from conan.tools.scm import Git
from conan.tools.files import load, update_conandata
class Pkg(Conanfile):
def export(self):
git = Git(self, self.recipe_folder)
scm_url, scm_commit = git.get_url_and_commit()
update_conandata(self, {"sources": {"commit": scm_commit, "url": scm_url}})
def source(self):
git = Git(self)
sources = self.conan_data["sources"]
git.clone(url=sources["url"], target=".")
git.checkout(commit=sources["commit"])
Please check the full example on the conan.tools.scm.Git section.
The generate() method
This is a key method to understand how Conan 2.0 works. This method is called during the “install” process, before
calling the “build()” method.
All information needed to build the current package has to be calculated and written in disk (in the self.generators_folder)
by the generate() method. That information is about:
The dependencies of the recipe: Typically called “generators”.
The configuration (settings, options…): Typically called “toolchains”.
The goal of the generate() method is to have a very simple build process (the more dummy, the better),
calling the build system passing some files or arguments and activating some environment launchers.
This improves a lot the local development, a simple conan install will generate everything we need to build our
project in the IDE or just call the build system. This example is using the CMake integration, but if you use
other build systems, even a custom one, remember you should generate everything needed in the generate() method:
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMakeDeps, CMake, cmake_layout
class Pkg(ConanFile):
...
requires = "foo/1.0", "bar/1.0"
def layout(self):
cmake_layout(self)
def generate(self):
# This generates "conan_toolchain.cmake" in self.generators_folder
tc = CMakeToolchain(self)
tc.variables["MYVAR"] = "1"
tc.preprocessor_definitions["MYDEFINE"] = "2"
tc.generate()
# This generates "foo-config.cmake" and "bar-config.cmake" in self.generators_folder
deps = CMakeDeps(self)
deps.generate()
...
If we are using that recipe for our project we can build it by typing:
$ conan install .
# This will generate the config files from the dependencies and the toolchain
$ cmake . -DCMAKE_TOOLCHAIN_FILE=./cmake-build-release/conan/conan_toolchain.cmake
$ cmake --build .
You can check all the generators and toolchains for different build systems in the tools reference page.
It is also very important to know that every access to the information from the dependencies must be done in the
generate() method using the self.dependencies access.
Do not use self.deps_cpp_info, self.deps_env_info or self.deps_user_info, these have been removed in 2.0.
Note
If you don’t need to customize anything in a generator you can specify it in the generators attribute and skip
using the generate() method for that:
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
class Pkg(ConanFile):
...
requires = "foo/1.0", "bar/1.0"
generators = "CMakeToolchain", "CMakeDeps"
...
The build() method
There is nothing special in the build() method, just emphasize the concept of dummy build explained before.
The package() method
The self.copy has been replaced by the explicit tool copy.
def package(self):
...
self.copy("*.h", dst="include", src="src")
self.copy("*.lib", dst="lib", keep_path=False)
self.copy("*.dll", dst="bin", keep_path=False)
from conan.tools.files import copy
def package(self):
...
copy(self, "*.h", self.source_folder, join(self.package_folder, "include"), keep_path=False)
copy(self, "*.lib", self.build_folder, join(self.package_folder, "lib"), keep_path=False)
copy(self, "*.dll", self.build_folder, join(self.package_folder, "bin"), keep_path=False)
The package_info() method
Removed cpp_info defaults in components
There are some defaults in the general self.cpp_info object:
self.cpp_info.includedirs => ["include"]
self.cpp_info.libdirs => ["lib"]
self.cpp_info.resdirs => ["res"]
self.cpp_info.bindirs => ["bin"]
self.cpp_info.builddirs => []
self.cpp_info.frameworkdirs => ["Frameworks"]
If you declare components, you need to explicitly specify these directories, because by default are empty:
def cpp_info(self):
self.cpp_info.components["mycomponent"].includedirs = ["my_include"]
self.cpp_info.components["myothercomponent"].bindirs = ["myother_bin"]
Note
Remember that now is possible to declare the cpp_info in the layout() method
using the self.cpp.package instead of using self.cpp_info in the package_info().
Removed self.user_info
Replaced by the self.conf_info object, much more versatile than the previous self.user_info.
Check the complete usage of self.conf_info.
Example:
import os
from conans import ConanFile
class Pkg(ConanFile):
name = "pkg"
version = "1.0"
def package_info(self):
self.user_info.FOO = "bar"
import os
from conans import ConanFile
class Pkg(ConanFile):
name = "pkg"
version = "1.0"
def package_info(self):
self.conf_info.define("user.myconf:foo", "bar")
In a consumer recipe:
import os
from conans import ConanFile
class Pkg(ConanFile):
requires = "pkg/1.0"
def generate(self):
my_value = self.dependencies[pkg].conf_info.get("user.myconf:foo")
...
Note
The consumer recipes will have a self.conf object available with the aggregated configuration from all the
recipes in the build context:
from conan import ConanFile
class Pkg(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain"
build_requires = "android_ndk/1.0"
def generate(self):
self.output.info("NDK: %s" % self.conf.get("tools.android:ndk_path"))
Removed self.env_info
The attribute self.env_info has been replaced by:
self.buildenv_info: For the dependant recipes, the environment variables will be present during the build process.self.runenv_info: For the dependant recipes, environment variables will be present during the runtime.
Read more about how to use them in the environment management of Conan 2.0.
Remember that if you want to pass general information to the dependant recipes, you should use the self.conf_info
and not environment variables if they are not supposed to be reused as environment variables in the dependent recipes.
Removed self.cpp_info.builddirs
The default value (pointing to the package root folder) form self.cpp_info.builddirs has been removed.
Also assigning it will be discouraged because it affects how CMakeToolchain and
CMakeDeps locate executables, libraries, headers… from the right context (host vs build).
To be prepared for Conan 2.0:
If you have cmake modules or cmake config files at the root of the package, it is strongly recommended to move them to a subfolder
cmakeand assing it:self.cpp_info.builddirs = ["cmake"]If you are not assigning any
self.cpp_info.builddirsassign an empty list:self.cpp_info.builddirs = [].Instead of appending new values to the default list, assign it:
self.cpp_info.builddirs = ["cmake"]
New properties model
Using .names, .filenames and .build_modules will not work anymore for new
generators, like CMakeDeps and PkgConfigDeps.
They have a new way of setting this information using set_property and
get_property methods of the cpp_info object (available since Conan 1.36).
def set_property(self, property_name, value)
def get_property(self, property_name):
New properties cmake_target_name, cmake_file_name, cmake_module_target_name,
cmake_module_file_name, pkg_config_name and cmake_build_modules are defined to allow
migrating names, filenames and build_modules properties to this model. In Conan 2.0 this
will be the default way of setting these properties for all generators and also passing
custom properties to generators.
Important
The 2 mechanisms are completely independent:
Old way using
.names,.filenameswill work exclusively for legacy generators likecmake_find_packageNew properties, like
set_property("cmake_target_name")will work exclusively for new generators likeCMakeDeps. They have changed to be absolute, and that would break legacy generators.Recipes that want to provide support for both generators need to provide the 2 definitions in their
package_info()
New properties defined for CMake generators family, used by CMakeDeps generator:
cmake_file_name property will define in
CMakeDepsthe name of the generated config file (xxx-config.cmake)cmake_target_name property will define the absolute target name in
CMakeDepscmake_module_file_name property defines the generated filename for modules (
Findxxxx.cmake)cmake_module_target_name defines the absolute target name for find modules.
cmake_build_modules property replaces the
build_modulesproperty.cmake_find_mode will tell CMakeDeps to generate config files, modules files, both or none of them, depending on the value set (
config,module,bothornone)
Properties related to pkg_config, supported by both legacy pkg_config and new PkgConfigDeps:
pkg_config_name property equivalent to the
namesattribute.pkg_config_custom_content property supported by both generators that will add user-defined content to the .pc files created by the generator
component_version property supported by both generators that set a custom version to be used in the
Versionfield belonging to the created*.pcfile for that component.
Properties related to pkg_config, only supported by new PkgConfigDeps:
pkg_config_aliases property sets some aliases of any package/component name for the
PkgConfigDepsgenerator only, it doesn’t work inpkg_config. This property only accepts list-like Python objects.
All of these properties, but cmake_file_name and cmake_module_file_name can be defined at the
global cpp_info level or at the component level.
The set/get_property model is very useful if you are creating a custom generator.
Using set_property() you can pass the parameters of your choice and read them using the
get_property() method inside the generator.
def package_info(self):
...
# you have created a custom generator that reads the 'custom_property' property and you set here
# the value to 'prop_value'
self.cpp_info.components["mycomponent"].set_property("custom_property", "prop_value")
...
Please check a detailed migration guide in the dedicated section.
Removed imports() method
The def imports(self) method from the conanfile has been removed. If you need to import files from your
dependencies you can do it in the generate(self) method with the new copy tool:
from conan.tools.files import copy
def generate(self):
for dep in self.dependencies.values():
copy(self, "*.dylib", dep.cpp_info.libdirs[0], self.build_folder)
copy(self, "*.dll", dep.cpp_info.libdirs[0], self.build_folder)
Changes in the test_package recipe
In Conan 2.0, the test_package/conanfile.py needs to declare the requirement being tested explicitly.
To be prepared you have to set the attribute test_type="explicit" (this will be ignored in 2.0) to make Conan
activate the explicit mode, then declaring the requirement using the self.tested_reference_str that contains the
reference being tested.
from conan import ConanFile
class MyTestPkg(ConanFile):
test_type = "explicit"
def requirements(self):
# A regular requirement
self.requires(self.tested_reference_str)
def build_requirements(self):
# If we want to test the package as a tool_require (formerly `test_type = "build_requires"`)
# Keep both "requires()" and "tool_requires()" if you want to test the same package both as a regular
# require and a tool_require (formerly `test_type = "build_requires", "requires"`)
self.tool_requires(self.tested_reference_str)
Other recipe changes
The environment management
The environment management has changed quite a bit. In Conan 1.X the environment was managed by modifying the environment
of Python (of the running process), often using the environment_append tool, which is not available in 2.0 anymore.
In Conan 2.0, all the applied environment variables are managed by script files
(sh, bat) that will be run just before calling the command specified in every self.run("mycommand").
These “environment launchers” can be organized by scopes. Conan will aggregate all the launchers of the same scope in a single
launcher called conan<scope_name>.bat/sh.
For example, if you need to call your build system, passing some environment variables:
from conan import ConanFile
from conan.tools.env import Environment
class MyTestPkg(ConanFile):
...
def generate(self):
env = Environment()
env.define("foo", "var")
# scope="build" is the default
envvars = env.vars(self, scope="build")
# This will generate a my_launcher.sh but also will create a "conan_build.sh" calling "my_launcher.sh"
envvars.save_script("my_launcher")
def build(self):
# by default env="conanbuild"
self.run("my_build_system.exe", env="conanbuild")
The resulting command executed in the build() method would be something like:
$ conan_build.sh && my_build_system.exe
So the environment variable foo declared in the generate() method will be automatically passed to the my_build_system.exe.
There are two generators managing the environment, the VirtualBuildEnv and the VirtualRunEnv. By default, these
generators are automatically declared in Conan 2.0 but you have to explicitly declare them in Conan 1.X otherwise you
can set tools.env.virtualenv:auto_use=True in the global.conf.
VirtualBuildEnv: It will generate a conanbuildenv .bat or .sh script containing environment variables of the build time environment. That information is collected from the direct
tool_requiresin “build” context recipes from theself.buildenv_infodefinition plus theself.runenv_infoof the transitive dependencies of thosetool_requires.The scope used by the
VirtualBuildEnvisbuildso, as explained before, it will be applied by default before calling any command.Check more details here.
VirtualRunEnv: It will generate a conanrunenv .bat or .sh script containing environment variables of the run time environment. The launcher contains the runtime environment information, anything that is necessary for the environment to actually run the compiled executables and applications. The information is obtained from the
self.runenv_infoand also automatically deducted from theself.cpp_infodefinition of the package, to definePATH,LD_LIBRARY_PATH,DYLD_LIBRARY_PATH, andDYLD_FRAMEWORK_PATHenvironment variables.The scope used by the
VirtualRunEnvisrunso if you need that environment applied you need to specify it in theself.runcommand.An example of usage of the
conanrunis the test_package of a recipe that builds a shared library:import os from conan import ConanFile from conan.tools.env import Environment class MyTestPkg(ConanFile): generators = "VirtualRunEnv" ... def test(self): my_app_path = os.path.join(self.cpp.build.bindirs[0], "my_app") # The default env is "conanbuild" but we want the runtime here to locate the shared library self.run(my_app_path, env="conanrun")
Check more details here.
Windows Subsystems
If you want to run commands inside a Windows subsystem (e.g bash from msys2) you have to set the self.win_bash=True
in your recipe, instead of using the deprecated self.run(..., win_bash=True) from 1.X.
You need to configure how to run the commands with two config variables:
tools.microsoft.bash:subsystem: Possible values: ‘msys2’, ‘msys’, ‘cygwin’, ‘wsl’ and ‘sfu’
tools.microsoft.bash:path (Default “bash”): Path to the shell executable.
Any command run with self.run, if self.win_bash == True will run the command inside the specified shell.
Symlinks
Conan won’t alter any symlink while exporting or packaging files. If any manipulation to the symlinks is required, the package conan.tools.files.symlinks contains some tools to help with that.