class ConanFileMock(ConanFile): def __init__(self, shared=None, options=None, options_values=None): options = options or "" self.command = None self.path = None self.settings = None self.options = Options(PackageOptions.loads(options)) if options_values: for var, value in options_values.items(): self.options._data[var] = value self.deps_cpp_info = MockDepsCppInfo( ) # ("deps_cpp_info", "sysroot")("/path/to/sysroot") self.deps_cpp_info.sysroot = "/path/to/sysroot" self.output = TestBufferConanOutput() self.in_local_cache = False if shared is not None: self.options = namedtuple("options", "shared")(shared) self.should_configure = True self.should_build = True self.should_install = True self.should_test = True self.generators = [] self.captured_env = {} self.deps_env_info = DepsEnvInfo() self.env_info = EnvInfo() self.deps_user_info = DepsUserInfo() self._conan_env_values = EnvValues() self.folders = Folders() self.folders.set_base_source(".") self.folders.set_base_build(".") self.folders.set_base_install("myinstallfolder") self.folders.set_base_generators(".") self._conan_user = None self._conan_channel = None self.environment_scripts = [] def run(self, command, win_bash=False, subsystem=None, env=None): assert win_bash is False assert subsystem is None self.command = command self.path = os.environ["PATH"] self.captured_env = {key: value for key, value in os.environ.items()}
def __init__(self, output, runner, display_name="", user=None, channel=None): # an output stream (writeln, info, warn error) self.output = ScopedOutput(display_name, output) self.display_name = display_name # something that can run commands, as os.sytem self._conan_runner = runner self._conan_user = user self._conan_channel = channel self.compatible_packages = [] self._conan_using_build_profile = False self._conan_requester = None from conan.tools.env import Environment self.buildenv_info = Environment() self.runenv_info = Environment() # At the moment only for build_requires, others will be ignored self.conf_info = Conf() self._conan_buildenv = None # The profile buildenv, will be assigned initialize() self._conan_node = None # access to container Node object, to access info, context, deps... self._conan_new_cpp_info = None # Will be calculated lazy in the getter self._conan_dependencies = None self.env_scripts = {} # Accumulate the env scripts generated in order # layout() method related variables: self.folders = Folders() self.cpp = Infos() self.cpp.package.includedirs = ["include"] self.cpp.package.libdirs = ["lib"] self.cpp.package.bindirs = ["bin"] self.cpp.package.resdirs = ["res"] self.cpp.package.builddirs = [""] self.cpp.package.frameworkdirs = ["Frameworks"]
class ConanFile(object): """ The base class for all package recipes """ name = None version = None # Any str, can be "1.1" or whatever url = None # The URL where this File is located, as github, to collaborate in package # The license of the PACKAGE, just a shortcut, does not replace or # change the actual license of the source code license = None author = None # Main maintainer/responsible for the package, any format description = None topics = None homepage = None build_policy = None short_paths = False apply_env = True # Apply environment variables from requires deps_env_info and profiles exports = None exports_sources = None generators = ["txt"] revision_mode = "hash" # Vars to control the build steps (build(), package()) should_configure = True should_build = True should_install = True should_test = True in_local_cache = True develop = False # Defaulting the reference fields default_channel = None default_user = None # Settings and Options settings = None options = None default_options = None provides = None deprecated = None # Folders folders = None patterns = None # Run in windows bash win_bash = None def __init__(self, output, runner, display_name="", user=None, channel=None): # an output stream (writeln, info, warn error) self.output = ScopedOutput(display_name, output) self.display_name = display_name # something that can run commands, as os.sytem self._conan_runner = runner self._conan_user = user self._conan_channel = channel self.compatible_packages = [] self._conan_using_build_profile = False self._conan_requester = None self.buildenv_info = Environment(self) self.runenv_info = Environment(self) # At the moment only for build_requires, others will be ignored self.conf_info = Conf() self._conan_buildenv = None # The profile buildenv, will be assigned initialize() self._conan_node = None # access to container Node object, to access info, context, deps... self._conan_new_cpp_info = None # Will be calculated lazy in the getter self._conan_dependencies = None self.env_scripts = {} # Accumulate the env scripts generated in order # layout() method related variables: self.folders = Folders() self.patterns = Patterns() self.cpp = Infos() self.patterns.source.include = ["*.h", "*.hpp", "*.hxx"] self.patterns.source.lib = [] self.patterns.source.bin = [] self.patterns.build.include = ["*.h", "*.hpp", "*.hxx"] self.patterns.build.lib = ["*.so", "*.so.*", "*.a", "*.lib", "*.dylib"] self.patterns.build.bin = ["*.exe", "*.dll"] self.cpp.package.includedirs = ["include"] self.cpp.package.libdirs = ["lib"] self.cpp.package.bindirs = ["bin"] self.cpp.package.resdirs = ["res"] self.cpp.package.builddirs = [""] self.cpp.package.frameworkdirs = ["Frameworks"] @property def context(self): return self._conan_node.context @property def dependencies(self): # Caching it, this object is requested many times if self._conan_dependencies is None: self._conan_dependencies = ConanFileDependencies.from_node( self._conan_node) return self._conan_dependencies @property def ref(self): return self._conan_node.ref @property def pref(self): return self._conan_node.pref @property def buildenv(self): # Lazy computation of the package buildenv based on the profileone if not isinstance(self._conan_buildenv, Environment): # TODO: missing user/channel ref_str = "{}/{}".format(self.name, self.version) self._conan_buildenv = self._conan_buildenv.get_env(self, ref_str) return self._conan_buildenv def initialize(self, settings, env, buildenv=None): self._conan_buildenv = buildenv if isinstance(self.generators, str): self.generators = [self.generators] # User defined options self.options = create_options(self) self.requires = create_requirements(self) self.settings = create_settings(self, settings) conan_v2_error( "Setting 'cppstd' is deprecated in favor of 'compiler.cppstd'," " please update your recipe.", 'cppstd' in self.settings.fields) # needed variables to pack the project self.cpp_info = None # Will be initialized at processing time self._conan_dep_cpp_info = None # Will be initialized at processing time self.deps_cpp_info = DepsCppInfo() # environment variables declared in the package_info self.env_info = None # Will be initialized at processing time self.deps_env_info = DepsEnvInfo() # user declared variables self.user_info = None # Keys are the package names (only 'host' if different contexts) self.deps_user_info = DepsUserInfo() # user specified env variables self._conan_env_values = env.copy() # user specified -e if self.description is not None and not isinstance( self.description, six.string_types): raise ConanException("Recipe 'description' must be a string.") if not hasattr(self, "virtualbuildenv" ): # Allow the user to override it with True or False self.virtualbuildenv = True if not hasattr(self, "virtualrunenv" ): # Allow the user to override it with True or False self.virtualrunenv = True @property def new_cpp_info(self): if not self._conan_new_cpp_info: self._conan_new_cpp_info = from_old_cppinfo(self.cpp_info) return self._conan_new_cpp_info @property def source_folder(self): return self.folders.source_folder @source_folder.setter def source_folder(self, folder): self.folders.set_base_source(folder) @property def build_folder(self): return self.folders.build_folder @build_folder.setter def build_folder(self, folder): self.folders.set_base_build(folder) @property def package_folder(self): return self.folders.package_folder @package_folder.setter def package_folder(self, folder): self.folders.set_base_package(folder) @property def install_folder(self): # FIXME: Remove in 2.0, no self.install_folder return self.folders.base_install @install_folder.setter def install_folder(self, folder): # FIXME: Remove in 2.0, no self.install_folder self.folders.set_base_install(folder) @property def generators_folder(self): # FIXME: Remove in 2.0, no self.install_folder return self.folders.generators_folder if self.folders.generators else self.install_folder @property def imports_folder(self): return self.folders.imports_folder @imports_folder.setter def imports_folder(self, folder): self.folders.set_base_imports(folder) @property def env(self): """Apply the self.deps_env_info into a copy of self._conan_env_values (will prioritize the self._conan_env_values, user specified from profiles or -e first, then inherited)""" # Cannot be lazy cached, because it's called in configure node, and we still don't have # the deps_env_info objects available tmp_env_values = self._conan_env_values.copy() tmp_env_values.update(self.deps_env_info) ret, multiple = tmp_env_values.env_dicts(self.name, self.version, self._conan_user, self._conan_channel) ret.update(multiple) return ret @property def channel(self): if not self._conan_channel: _env_channel = os.getenv("CONAN_CHANNEL") conan_v2_error( "Environment variable 'CONAN_CHANNEL' is deprecated", _env_channel) self._conan_channel = _env_channel or self.default_channel if not self._conan_channel: raise ConanException( "channel not defined, but self.channel is used in conanfile" ) return self._conan_channel @property def user(self): if not self._conan_user: _env_username = os.getenv("CONAN_USERNAME") conan_v2_error( "Environment variable 'CONAN_USERNAME' is deprecated", _env_username) self._conan_user = _env_username or self.default_user if not self._conan_user: raise ConanException( "user not defined, but self.user is used in conanfile") return self._conan_user def collect_libs(self, folder=None): conan_v2_error( "'self.collect_libs' is deprecated, use 'tools.collect_libs(self)' instead" ) return tools.collect_libs(self, folder=folder) @property def build_policy_missing(self): return self.build_policy == "missing" @property def build_policy_always(self): return self.build_policy == "always" def source(self): pass def system_requirements(self): """ this method can be overwritten to implement logic for system package managers, as apt-get You can define self.global_system_requirements = True, if you want the installation to be for all packages (not depending on settings/options/requirements) """ def config_options(self): """ modify options, probably conditioned to some settings. This call is executed before config_settings. E.g. if self.settings.os == "Windows": del self.options.shared # shared/static not supported in win """ def configure(self): """ modify settings, probably conditioned to some options. This call is executed after config_options. E.g. if self.options.header_only: self.settings.clear() This is also the place for conditional requirements """ def build(self): """ build your project calling the desired build tools as done in the command line. E.g. self.run("cmake --build .") Or use the provided build helpers. E.g. cmake.build() """ self.output.warn("This conanfile has no build step") def package(self): """ package the needed files from source and build folders. E.g. self.copy("*.h", src="src/includes", dst="includes") """ self.output.warn("This conanfile has no package step") def package_info(self): """ define cpp_build_info, flags, etc """ def run(self, command, output=True, cwd=None, win_bash=False, subsystem=None, msys_mingw=True, ignore_errors=False, run_environment=False, with_login=True, env=None): # NOTE: "self.win_bash" is the new parameter "win_bash" for Conan 2.0 def _run(cmd, _env): # FIXME: run in windows bash is not using output if platform.system() == "Windows": if win_bash: return tools.run_in_windows_bash(self, bashcmd=cmd, cwd=cwd, subsystem=subsystem, msys_mingw=msys_mingw, with_login=with_login) elif self.win_bash: # New, Conan 2.0 from conan.tools.microsoft.subsystems import run_in_windows_bash return run_in_windows_bash(self, command=cmd, cwd=cwd, env=_env) if _env is None: _env = "conanbuild" wrapped_cmd = environment_wrap_command(self, _env, cmd, cwd=self.generators_folder) return self._conan_runner(wrapped_cmd, output, os.path.abspath(RUN_LOG_NAME), cwd) if run_environment: # When using_build_profile the required environment is already applied through # 'conanfile.env' in the contextmanager 'get_env_context_manager' with tools.run_environment( self) if not self._conan_using_build_profile else no_op(): if OSInfo().is_macos and isinstance(command, string_types): # Security policy on macOS clears this variable when executing /bin/sh. To # keep its value, set it again inside the shell when running the command. command = 'DYLD_LIBRARY_PATH="%s" DYLD_FRAMEWORK_PATH="%s" %s' % \ (os.environ.get('DYLD_LIBRARY_PATH', ''), os.environ.get("DYLD_FRAMEWORK_PATH", ''), command) retcode = _run(command, env) else: retcode = _run(command, env) if not ignore_errors and retcode != 0: raise ConanException("Error %d while executing %s" % (retcode, command)) return retcode def package_id(self): """ modify the binary info, typically to narrow values e.g.: self.info.settings.compiler = "Any" => All compilers will generate same ID """ def test(self): """ test the generated executable. E.g. self.run("./example") """ raise ConanException( "You need to create a method 'test' in your test/conanfile.py") def __repr__(self): return self.display_name