source()¶
The source()
method can be used to retrieve the necessary source code to build a package from source, and to apply patches to such source code if necessary. It will be called when a package is being built from source, like with conan create
or conan install --build=pkg*
, but it will not be called if a package pre-compiled binary is being used. That means that the source code will not be downloaded if a pre-compiled binary exists.
The source()
method can implement different strategies for retrieving the source code:
Fetching the source code for a third party library:
Using a
Git(self).clone()
to clone a Git repositoryExecuting a
download()
+unzip()
or a combinedget()
(internally does download + unzip) to download a tarball, tgz, or zip archive.
Fetching the source code for itself, from its repository, whose coordinates have been captured in the
conandata.yml
file in theexport()
method. This is the strategy that would be used to manage the source code for packages in which theconanfile.py
lives in the package itself, but that for some reason we don’t want to put the source code in the recipe (like not distributing our source code, but being able to distribute our package binaries).
The source()
method executes in the self.source_folder
, the current working directory will be equal to that folder (which value is derived from layout()
method).
A source()
implementation might use the convenient get()
helper, or use its own mechanisms or other Conan helpers for the task, something like:
import os
import shutil
from conan import ConanFile
from conan.tools.files import download, unzip, check_sha1
class PocoConan(ConanFile):
name = "poco"
version = "1.6.0"
def source(self):
zip_name = f"poco-{self.version}-release.zip"
# Immutable source .zip
download(self, f"https://github.com/pocoproject/poco/archive/poco-{self.version}-release.zip", zip_name)
# Recommended practice, always check hashes of downloaded files
check_sha1(self, zip_name, "8d87812ce591ced8ce3a022beec1df1c8b2fac87")
unzip(self, zip_name)
shutil.move(f"poco-poco-{self.version}-release", "poco")
os.unlink(zip_name)
Applying patches to downloaded sources can be done (and should be done) in the source()
method if those patches
apply to all possible configurations. As explained below, it is not possible to introduce conditionals in the
source()
method. If the patches are in file form, those patches must be exported together with the recipe, so they can be used whenever a build from source is fired.
It is possible to apply patches with:
Your own or
git
patches utilitiesThe Conan built-in
patch()
utility to explicitly apply patches one by oneApply the
apply_conandata_patches()
Conan utility to automatically apply all patches defined inconandata.yml
file following some conventions.
Source caching¶
Once the source()
method has been called, its result will be cached and reused for any build from source, for any configuration. That means that the retrieval of sources from the source()
method should be completely independent of the configuration. It is not possible to implement conditionals on the settings
, and in general, any attempt to apply any conditional logic to the source()
method is wrong.
def source(self):
if self.settings.compiler == "gcc": # ERROR, will raise
# download some source
Trying to bypass the Conan exception by using some other mechanism like:
def source(self):
# Might work, but NOT recommended, try to avoid as much as possible
if platform.system() == "Windows":
# download something
else:
# download something different
Might apparently work if not doing any cross-build, and not recollecting sources in a different OS, but could be problematic otherwise.
To be completely safe, if different source code is necessary for different configurations, the recommended approach would be to retrieve that code conditionally in the build()
method.
Forced retrieval of sources¶
When working with a recipe in a user folder, it is easy to call the source()
method and force the retrieval of the source code, that will be done in the same user folder, according to the layout()
definition:
$ conan source .
Calling the source()
method and forcing the retrieval of source code in the cache, for all or some dependencies, even if they are not being built from sources, is possible with the tools.build:download_source=True
configuration. For example:
$ conan graph info . -c tools.build:download_source=True
Will compute the dependency graph, then call the source()
method for all “host” packages in the graph (as the configuration by default is a “host” configuration, if you want also the sources for the “build” context tool_requires
, you could use -c:b tools.build:download_source=True
). It is possible to collect all the source folders from the json formatted output, or to automate recollection of all sources, a deployer
could be used.
Likewise, it is possible to retrieve the sources for packages in other create
and install
commands, just by passing the configuration. Finally, as also configuration can be defined per-package, using -c mypkg*:tools.build:download_source=True
would only retrieve the sources of packages matching the mypkg*
pattern.
Note that tools.build:download_source=True
will not have any effect on packages in editable mode. Downloading sources in that case could easily overwrite and destroy local developer changes over that code. The conan source
command must be used on packages in editable mode to download the sources.
Note
Best practices
The
source()
method should be the same for all configurations, it cannot be conditional to any configuration.The
source()
method should retrieve immutable sources. Using some branch name, HEAD, or a tarball whose URL is not immutable and is being overwritten is a bad practice and will lead to broken packages. Using a Git commit, a frozen Git release tag, or a fixed and versioned release tarballs is the expected input.Applying patches should be done by default in the
source()
method, except if the patches are exclusive for one configuration, in that case they could be applied inbuild()
method.The
source()
method should not access nor manipulate files in other folders different to theself.source_folder
. All the “exported” files are copied to theself.source_folder
before calling it.
See also
See the tutorial about managing recipe sources for more information.