Customize the full_deploy deployer: handling editable packages
The built-in full_deploy deployer copies the package_folder of every
dependency to the deployer output folder. This is what it does with the different kinds of dependencies:
Packages in the Conan cache: the package contents are copied.
Dependencies with
package_folder=None, which happens when the binary is skipped because it is not necessary: they are not deployed, as there is no folder to copy.Packages in editable mode: the
package_folderis the local project root folder, where theconanfile.pyof the editable is. There is no actual “package” there, butfull_deploycopies the whole folder, including sources, build artifacts and anything else in it.
Deploying editables is not recommended in general. Deployers are intended
to be customized. For real production needs, it is recommended to use your own deployer, and install it with
conan config install or conan config install-pkg.
This is the implementation of the built-in full_deploy, slightly simplified, with some
extra code to handle editables. Copy it to a my_full_deploy.py file:
import os
import shutil
from conan.errors import ConanException
from conan.tools.files import rmdir
def deploy(graph, output_folder, **kwargs):
conanfile = graph.root.conanfile
conanfile.output.info(f"Custom full deployer to {output_folder}")
symlinks = conanfile.conf.get("tools.deployer:symlinks", check_type=bool, default=True)
for dep in conanfile.dependencies.values():
# Skipped binaries, there is nothing to deploy
if dep.package_folder is None:
continue
# Editable packages: the package_folder is the local project folder, not a package.
# Raise a ConanException instead of "continue" to fail when there are editables
if dep.recipe == "Editable":
conanfile.output.warning(f"Skipping deploy of editable package {dep}")
continue
folder_name = os.path.join("full_deploy", dep.context, dep.ref.name, str(dep.ref.version))
build_type = dep.info.settings.get_safe("build_type")
arch = dep.info.settings.get_safe("arch")
if build_type:
folder_name = os.path.join(folder_name, build_type)
if arch:
folder_name = os.path.join(folder_name, arch)
new_folder = os.path.join(output_folder, folder_name)
rmdir(conanfile, new_folder)
try:
shutil.copytree(dep.package_folder, new_folder, symlinks=symlinks)
except Exception as e:
raise ConanException(f"full_deploy: The copy of '{dep}' files failed: {e}.\n"
f"You can use 'tools.deployer:symlinks' conf to disable symlinks")
# Generators running after the deployer will point to the deployed folder
dep.set_deploy_folder(new_folder)
Then it can be used as any other deployer:
$ conan install . --deployer=my_full_deploy
Some notes about the code:
dep.package_foldercan beNonefor skipped binaries, so any deployer needs to check it.dep.recipe == "Editable"is a valid way to detect editable packages. It can also be used to implement other behaviors, like deploying only the artifacts defined in thecpp_infoof the editable package (dep.cpp_info.includedirs,dep.cpp_info.libdirs, etc.) instead of the whole folder.Generators like
CMakeDepsdo not need conditionals for editables: thelayout()method makescpp_infowork transparently for packages in the cache and editable packages.