def deserialize(data): res = ConanInfo() res.settings = Values.deserialize(data["settings"]) res.full_settings = Values.deserialize(data["full_settings"]) res.options = OptionsValues.deserialize(data["options"]) res.full_options = OptionsValues.deserialize(data["full_options"]) res.requires = RequirementsInfo.deserialize(data["requires"]) res.full_requires = RequirementsList.deserialize(data["full_requires"]) return res
def basic_test(self): options = OptionsValues.loads("""other_option=True optimized_var=3 Poco:deps_bundled=True Boost:static=False Boost:thread=True Boost:thread.multi=off Hello1:static=False Hello1:optimized=4 """) down_ref = ConanFileReference.loads("Hello0/0.1@diego/testing") own_ref = ConanFileReference.loads("Hello1/0.1@diego/testing") output = TestBufferConanOutput() self.sut.propagate_upstream(options, down_ref, own_ref, output) self.assertEqual(self.sut.values.as_list(), [("optimized", "4"), ("path", "NOTDEF"), ("static", "False"), ("Boost:static", "False"), ("Boost:thread", "True"), ("Boost:thread.multi", "off"), ("Poco:deps_bundled", "True")]) options2 = OptionsValues.loads("""other_option=True optimized_var=3 Poco:deps_bundled=What Boost:static=2 Boost:thread=Any Boost:thread.multi=on Hello1:static=True Hello1:optimized=2 """) down_ref = ConanFileReference.loads("Hello2/0.1@diego/testing") self.sut.propagate_upstream(options2, down_ref, own_ref, output) self.assertIn("""WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option optimized to 2 but it was already assigned to 4 by Hello0/0.1@diego/testing WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option static to True but it was already assigned to False by Hello0/0.1@diego/testing WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option Boost:static to 2 but it was already assigned to False by Hello0/0.1@diego/testing WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option Boost:thread to Any but it was already assigned to True by Hello0/0.1@diego/testing WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option Boost:thread.multi to on but it was already assigned to off by Hello0/0.1@diego/testing WARN: Hello2/0.1@diego/testing tried to change Hello1/0.1@diego/testing option Poco:deps_bundled to What but it was already assigned to True by Hello0/0.1@diego/testing""", str(output)) self.assertEqual(self.sut.values.dumps(), """optimized=4 path=NOTDEF static=False Boost:static=False Boost:thread=True Boost:thread.multi=off Poco:deps_bundled=True""")
def loads(text): parser = ConfigParser(text, ["settings", "full_settings", "options", "full_options", "requires", "full_requires"]) result = ConanInfo() result.settings = Values.loads(parser.settings) result.full_settings = Values.loads(parser.full_settings) result.options = OptionsValues.loads(parser.options) result.full_options = OptionsValues.loads(parser.full_options) result.full_requires = RequirementsList.loads(parser.full_requires) result.requires = RequirementsInfo(result.full_requires) # TODO: Missing handling paring of requires, but not necessary now return result
def _parse_conan_txt(self, contents, path, output): conanfile = ConanFile(output, self._runner, Settings()) # It is necessary to copy the settings, because the above is only a constraint of # conanfile settings, and a txt doesn't define settings. Necessary for generators, # as cmake_multi, that check build_type. conanfile.settings = self._settings.copy_values() try: parser = ConanFileTextLoader(contents) except Exception as e: raise ConanException("%s:\n%s" % (path, str(e))) for requirement_text in parser.requirements: ConanFileReference.loads(requirement_text) # Raise if invalid conanfile.requires.add(requirement_text) for build_requirement_text in parser.build_requirements: ConanFileReference.loads(build_requirement_text) if not hasattr(conanfile, "build_requires"): conanfile.build_requires = [] conanfile.build_requires.append(build_requirement_text) conanfile.generators = parser.generators options = OptionsValues.loads(parser.options) conanfile.options.values = options conanfile.options.initialize_upstream(self._user_options) # imports method conanfile.imports = parser.imports_method(conanfile) conanfile._env_values.update(self._env_values) return conanfile
def build_graph(self, content, profile_build_requires=None, ref=None, create_ref=None, install=True): path = temp_folder() path = os.path.join(path, "conanfile.py") save(path, str(content)) self.loader.cached_conanfiles = {} profile = Profile() if profile_build_requires: profile.build_requires = profile_build_requires profile.process_settings(self.cache) update = check_updates = False recorder = ActionRecorder() remotes = Remotes() build_mode = [] # Means build all ref = ref or ConanFileReference(None, None, None, None, validate=False) options = OptionsValues() graph_info = GraphInfo(profile, options, root_ref=ref) deps_graph, _ = self.manager.load_graph(path, create_ref, graph_info, build_mode, check_updates, update, remotes, recorder) if install: self.binary_installer.install(deps_graph, None, False, graph_info) return deps_graph
def _parse_conan_txt(self, contents, path, display_name, profile): conanfile = ConanFile(self._output, self._runner, display_name) conanfile.initialize(Settings(), profile.env_values) # It is necessary to copy the settings, because the above is only a constraint of # conanfile settings, and a txt doesn't define settings. Necessary for generators, # as cmake_multi, that check build_type. conanfile.settings = profile.processed_settings.copy_values() try: parser = ConanFileTextLoader(contents) except Exception as e: raise ConanException("%s:\n%s" % (path, str(e))) for reference in parser.requirements: ref = ConanFileReference.loads(reference) # Raise if invalid conanfile.requires.add_ref(ref) for build_reference in parser.build_requirements: ConanFileReference.loads(build_reference) if not hasattr(conanfile, "build_requires"): conanfile.build_requires = [] conanfile.build_requires.append(build_reference) conanfile.generators = parser.generators try: options = OptionsValues.loads(parser.options) except Exception: raise ConanException( "Error while parsing [options] in conanfile\n" "Options should be specified as 'pkg:option=value'") conanfile.options.values = options conanfile.options.initialize_upstream(profile.user_options) # imports method conanfile.imports = parser.imports_method(conanfile) return conanfile
def __init__(self): # Sections self.settings = OrderedDict() self.package_settings = defaultdict(OrderedDict) self.env_values = EnvValues() self.options = OptionsValues() self.build_requires = OrderedDict() # conan_ref Pattern: list of conan_ref
def _loader(self, current_path=None, user_settings_values=None, user_options_values=None, scopes=None): # The disk settings definition, already including the default disk values settings = self._paths.settings options = OptionsValues() conaninfo_scopes = Scopes() if current_path: conan_info_path = os.path.join(current_path, CONANINFO) if os.path.exists(conan_info_path): existing_info = ConanInfo.load_file(conan_info_path) settings.values = existing_info.full_settings options = existing_info.full_options # Take existing options from conaninfo.txt conaninfo_scopes = existing_info.scope if user_settings_values: aux_values = Values.from_list(user_settings_values) settings.values = aux_values if user_options_values is not None: # Install will pass an empty list [] # Install OVERWRITES options, existing options in CONANINFO are not taken # into account, just those from CONANFILE + user command line options = OptionsValues.from_list(user_options_values) if scopes: conaninfo_scopes.update_scope(scopes) self._current_scopes = conaninfo_scopes return ConanFileLoader(self._runner, settings, options=options, scopes=conaninfo_scopes)
def loads(text): parser = ConfigParser(text, ["settings", "full_settings", "options", "full_options", "requires", "full_requires", "scope", "recipe_hash", "env"], raise_unexpected_field=False) result = ConanInfo() result.settings = Values.loads(parser.settings) result.full_settings = Values.loads(parser.full_settings) result.options = OptionsValues.loads(parser.options) result.full_options = OptionsValues.loads(parser.full_options) result.full_requires = RequirementsList.loads(parser.full_requires) result.requires = RequirementsInfo(result.full_requires) result.recipe_hash = parser.recipe_hash or None # TODO: Missing handling paring of requires, but not necessary now result.env_values = EnvValues.loads(parser.env) return result
def create_profile(folder, name, settings=None, scopes=None, package_settings=None, env=None, package_env=None, options=None): package_env = package_env or {} profile = Profile() profile.settings = settings or {} if scopes: profile.scopes = Scopes.from_list( ["%s=%s" % (key, value) for key, value in scopes.items()]) if package_settings: profile.package_settings = package_settings if options: profile.options = OptionsValues(options) for package_name, envs in package_env.items(): for var_name, value in envs: profile.env_values.add(var_name, value, package_name) for var_name, value in env or {}: profile.env_values.add(var_name, value) save(os.path.join(folder, name), profile.dumps())
def build_consumer(self, path, profile_build_requires=None, ref=None, create_ref=None, install=True): profile = Profile() if profile_build_requires: profile.build_requires = profile_build_requires profile.process_settings(self.cache) update = check_updates = False recorder = ActionRecorder() remotes = Remotes() build_mode = [] # Means build all ref = ref or ConanFileReference(None, None, None, None, validate=False) options = OptionsValues() graph_info = GraphInfo(profile, options=options, root_ref=ref) app = self._get_app() deps_graph = app.graph_manager.load_graph(path, create_ref, graph_info, build_mode, check_updates, update, remotes, recorder) if install: binary_installer = BinaryInstaller(app, recorder) build_mode = BuildMode(build_mode, app.out) binary_installer.install(deps_graph, None, build_mode, update, False, graph_info) return deps_graph
def load_conan_txt(self, conan_requirements_path, output): if not os.path.exists(conan_requirements_path): raise NotFoundException("Conanfile not found!") conanfile = ConanFile(output, self._runner, self._settings.copy(), os.path.dirname(conan_requirements_path)) try: parser = ConanFileTextLoader(load(conan_requirements_path)) except Exception as e: raise ConanException("%s:\n%s" % (conan_requirements_path, str(e))) for requirement_text in parser.requirements: ConanFileReference.loads(requirement_text) # Raise if invalid conanfile.requires.add(requirement_text) conanfile.generators = parser.generators options = OptionsValues.loads(parser.options) conanfile.options.values = options conanfile.options.initialize_upstream(self._options) # imports method conanfile.imports = ConanFileTextLoader.imports_method(conanfile, parser.import_parameters) conanfile.scope = self._scopes.package_scope() return conanfile
def test_replace_in_file(self): file_content = ''' from conans import ConanFile from conans.tools import download, unzip, replace_in_file import os class ConanFileToolsTest(ConanFile): name = "test" version = "1.9.10" settings = [] def source(self): pass def build(self): replace_in_file("otherfile.txt", "ONE TWO THREE", "FOUR FIVE SIX") ''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "conanfile.py") other_file = os.path.join(tmp_dir, "otherfile.txt") save(file_path, file_content) save(other_file, "ONE TWO THREE") loader = ConanFileLoader(None, None, Settings(), OptionsValues.loads("")) ret = loader.load_conan(file_path) curdir = os.path.abspath(os.curdir) os.chdir(tmp_dir) try: ret.build() finally: os.chdir(curdir) content = load(other_file) self.assertEquals(content, "FOUR FIVE SIX")
def _profile_parse_args(settings, options, envs, conf): """ return a Profile object result of parsing raw data """ def _get_tuples_list_from_extender_arg(items): if not items: return [] # Validate the pairs for item in items: chunks = item.split("=", 1) if len(chunks) != 2: raise ConanException("Invalid input '%s', use 'name=value'" % item) return [(item[0], item[1]) for item in [item.split("=", 1) for item in items]] def _get_simple_and_package_tuples(items): """Parse items like "thing:item=value or item2=value2 and returns a tuple list for the simple items (name, value) and a dict for the package items {package: [(item, value)...)], ...} """ simple_items = [] package_items = defaultdict(list) tuples = _get_tuples_list_from_extender_arg(items) for name, value in tuples: if ":" in name: # Scoped items tmp = name.split(":", 1) ref_name = tmp[0] name = tmp[1] package_items[ref_name].append((name, value)) else: simple_items.append((name, value)) return simple_items, package_items def _get_env_values(_env, _package_env): _env_values = EnvValues() for name, value in _env: _env_values.add(name, EnvValues.load_value(value)) for package, data in _package_env.items(): for name, value in data: _env_values.add(name, EnvValues.load_value(value), package) return _env_values options = _get_tuples_list_from_extender_arg(options) env, package_env = _get_simple_and_package_tuples(envs) env_values = _get_env_values(env, package_env) settings, package_settings = _get_simple_and_package_tuples(settings) result = Profile() result.options = OptionsValues(options) result.env_values = env_values result.settings = OrderedDict(settings) if conf: result.conf = ConfDefinition() result.conf.loads("\n".join(conf)) for pkg, values in package_settings.items(): result.package_settings[pkg] = OrderedDict(values) return result
def loads(text): parser = ConfigParser(text, [ "settings", "full_settings", "options", "full_options", "requires", "full_requires", "scope", "recipe_hash" ]) result = ConanInfo() result.settings = Values.loads(parser.settings) result.full_settings = Values.loads(parser.full_settings) result.options = OptionsValues.loads(parser.options) result.full_options = OptionsValues.loads(parser.full_options) result.full_requires = RequirementsList.loads(parser.full_requires) result.requires = RequirementsInfo(result.full_requires, None) result.recipe_hash = parser.recipe_hash or None # TODO: Missing handling paring of requires, but not necessary now result.scope = Scopes.loads(parser.scope) return result
def setUp(self): self.sut = OptionsValues.loads("""static=True optimized=3 Poco:deps_bundled=True Boost:static=False Boost:thread=True Boost:thread.multi=off """)
def _apply_inner_profile(doc, base_profile): """ :param doc: ConfigParser object from the current profile (excluding includes and vars, and with values already replaced) :param base_profile: Profile inherited, it's used as a base profile to modify it. :return: None """ def get_package_name_value(item): """Parse items like package:name=value or name=value""" packagename = None if ":" in item: tmp = item.split(":", 1) packagename, item = tmp result_name, result_value = item.split("=", 1) result_name = result_name.strip() result_value = unquote(result_value) return packagename, result_name, result_value for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: base_profile.package_settings[package_name][name] = value else: base_profile.settings[name] = value if doc.build_requires: # FIXME CHECKS OF DUPLICATED? for req in doc.build_requires.splitlines(): _load_single_build_require(base_profile, req) if doc.tool_requires: for req in doc.tool_requires.splitlines(): _load_single_build_require(base_profile, req) if doc.options: base_profile.options.update(OptionsValues.loads(doc.options)) # The env vars from the current profile (read in doc) # are updated with the included profiles (base_profile) # the current env values has priority current_env_values = EnvValues.loads(doc.env) current_env_values.update(base_profile.env_values) base_profile.env_values = current_env_values if doc.conf: new_prof = ConfDefinition() new_prof.loads(doc.conf, profile=True) base_profile.conf.update_conf_definition(new_prof) if doc.buildenv: buildenv = ProfileEnvironment.loads(doc.buildenv) base_profile.buildenv.update_profile_env(buildenv)
def test_consistency(self): def _check_equal(hs1, hs2, hs3, hs4): opt_values1 = OptionsValues(hs1) opt_values2 = OptionsValues(hs2) opt_values3 = OptionsValues(hs3) opt_values4 = OptionsValues(hs4) self.assertEqual(opt_values1.dumps(), opt_values2.dumps()) self.assertEqual(opt_values1.dumps(), opt_values3.dumps()) self.assertEqual(opt_values1.dumps(), opt_values4.dumps()) # Check that all possible input options give the same result _check_equal([('opt', 3)], [ ('opt', '3'), ], ('opt=3', ), {'opt': 3}) _check_equal([('opt', True)], [ ('opt', 'True'), ], ('opt=True', ), {'opt': True}) _check_equal([('opt', False)], [ ('opt', 'False'), ], ('opt=False', ), {'opt': False}) _check_equal([('opt', None)], [ ('opt', 'None'), ], ('opt=None', ), {'opt': None}) _check_equal([('opt', 0)], [ ('opt', '0'), ], ('opt=0', ), {'opt': 0}) _check_equal([('opt', '')], [ ('opt', ''), ], ('opt=', ), {'opt': ''}) # Check for leading and trailing spaces _check_equal([(' opt ', 3)], [ (' opt ', '3'), ], (' opt =3', ), {' opt ': 3}) _check_equal([('opt', ' value ')], [ ('opt', ' value '), ], ('opt= value ', ), {'opt': ' value '}) # This is expected behaviour: self.assertNotEqual( OptionsValues([ ('opt', ''), ]).dumps(), OptionsValues(('opt=""', )).dumps())
def loads(text): parser = ConfigParser(text, [ "settings", "full_settings", "options", "full_options", "requires", "full_requires", "scope", "recipe_hash", "env" ], raise_unexpected_field=False) result = ConanInfo() result.settings = Values.loads(parser.settings) result.full_settings = Values.loads(parser.full_settings) result.options = OptionsValues.loads(parser.options) result.full_options = OptionsValues.loads(parser.full_options) result.full_requires = RequirementsList.loads(parser.full_requires) result.requires = RequirementsInfo(result.full_requires) result.recipe_hash = parser.recipe_hash or None # TODO: Missing handling paring of requires, but not necessary now result.env_values = EnvValues.loads(parser.env) return result
def create_options(conanfile): try: package_options = PackageOptions(getattr(conanfile, "options", None)) options = Options(package_options) default_options = getattr(conanfile, "default_options", None) if default_options: if isinstance(default_options, (list, tuple, dict)): default_values = OptionsValues(default_options) elif isinstance(default_options, str): default_values = OptionsValues.loads(default_options) else: raise ConanException("Please define your default_options as list, " "multiline string or dictionary") options.values = default_values return options except Exception as e: raise ConanException("Error while initializing options. %s" % str(e))
def _parse_conan_txt(self, contents, path, display_name, profile): conanfile = ConanFile(self._output, self._runner, display_name) tmp_settings = profile.processed_settings.copy() package_settings_values = profile.package_settings_values if "&" in package_settings_values: pkg_settings = package_settings_values.get("&") if pkg_settings: tmp_settings.update_values(pkg_settings) conanfile.initialize(Settings(), profile.env_values, profile.buildenv) conanfile.conf = profile.conf.get_conanfile_conf(None) # It is necessary to copy the settings, because the above is only a constraint of # conanfile settings, and a txt doesn't define settings. Necessary for generators, # as cmake_multi, that check build_type. conanfile.settings = tmp_settings.copy_values() try: parser = ConanFileTextLoader(contents) except Exception as e: raise ConanException("%s:\n%s" % (path, str(e))) for reference in parser.requirements: ref = ConanFileReference.loads(reference) # Raise if invalid conanfile.requires.add_ref(ref) for build_reference in parser.build_requirements: ConanFileReference.loads(build_reference) if not hasattr(conanfile, "build_requires"): conanfile.build_requires = [] conanfile.build_requires.append(build_reference) if parser.layout: layout_method = { "cmake_layout": cmake_layout, "vs_layout": vs_layout, "bazel_layout": bazel_layout }.get(parser.layout) if not layout_method: raise ConanException( "Unknown predefined layout '{}' declared in " "conanfile.txt".format(parser.layout)) def layout(self): layout_method(self) conanfile.layout = types.MethodType(layout, conanfile) conanfile.generators = parser.generators try: options = OptionsValues.loads(parser.options) except Exception: raise ConanException( "Error while parsing [options] in conanfile\n" "Options should be specified as 'pkg:option=value'") conanfile.options.values = options conanfile.options.initialize_upstream(profile.user_options) # imports method conanfile.imports = parser.imports_method(conanfile) return conanfile
def create_options(conanfile): try: package_options = PackageOptions(getattr(conanfile, "options", None)) options = Options(package_options) default_options = getattr(conanfile, "default_options", None) if default_options: if isinstance(default_options, tuple): default_values = OptionsValues.loads("\n".join(default_options)) elif isinstance(default_options, list): default_values = OptionsValues.from_list(default_options) elif isinstance(default_options, basestring): default_values = OptionsValues.loads(default_options) else: raise ConanException("Please define your default_options as list or " "multiline string") options.values = default_values return options except Exception as e: raise ConanException("Error while initializing options. %s" % str(e))
def _loader(self, current_path=None, user_settings_values=None, package_settings=None, user_options_values=None, scopes=None, env=None, package_env=None): # The disk settings definition, already including the default disk values settings = self._client_cache.settings options = OptionsValues() conaninfo_scopes = Scopes() if current_path: conan_info_path = os.path.join(current_path, CONANINFO) if os.path.exists(conan_info_path): existing_info = ConanInfo.load_file(conan_info_path) settings.values = existing_info.full_settings options = existing_info.full_options # Take existing options from conaninfo.txt conaninfo_scopes = existing_info.scope if user_settings_values: aux_values = Values.from_list(user_settings_values) settings.values = aux_values if user_options_values is not None: # Install will pass an empty list [] # Install OVERWRITES options, existing options in CONANINFO are not taken # into account, just those from CONANFILE + user command line options = OptionsValues.from_list(user_options_values) if scopes: conaninfo_scopes.update_scope(scopes) self._current_scopes = conaninfo_scopes return ConanFileLoader(self._runner, settings, package_settings=package_settings, options=options, scopes=conaninfo_scopes, env=env, package_env=package_env)
def loads(text): # This is used for search functionality, search prints info from this file # Other use is from the BinariesAnalyzer, to get the recipe_hash and know # if package is outdated parser = ConfigParser(text, ["settings", "full_settings", "options", "full_options", "requires", "full_requires", "scope", "recipe_hash", "env"], raise_unexpected_field=False) result = ConanInfo() result.settings = Values.loads(parser.settings) result.full_settings = Values.loads(parser.full_settings) result.options = OptionsValues.loads(parser.options) result.full_options = OptionsValues.loads(parser.full_options) result.full_requires = _PackageReferenceList.loads(parser.full_requires) # Requires after load are not used for any purpose, CAN'T be used, they are not correct result.requires = RequirementsInfo(result.full_requires, "semver_direct_mode") result.recipe_hash = parser.recipe_hash or None # TODO: Missing handling paring of requires, but not necessary now result.env_values = EnvValues.loads(parser.env) return result
def loads(text): graph_json = json.loads(text) profile = graph_json["profile"] # FIXME: Reading private very ugly profile, _ = _load_profile(profile, None, None) try: options = graph_json["options"] except KeyError: options = None else: options = OptionsValues(options) return GraphInfo(profile=profile, options=options)
def _apply_inner_profile(doc, base_profile): """ :param doc: ConfigParser object from the current profile (excluding includes and vars, and with values already replaced) :param base_profile: Profile inherited, it's used as a base profile to modify it. :return: None """ def get_package_name_value(item): """Parse items like package:name=value or name=value""" package_name = None if ":" in item: tmp = item.split(":", 1) package_name, item = tmp name, value = item.split("=", 1) name = name.strip() value = unquote(value) return package_name, name, value for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: base_profile.package_settings[package_name][name] = value else: base_profile.settings[name] = value if doc.build_requires: # FIXME CHECKS OF DUPLICATED? for req in doc.build_requires.splitlines(): tokens = req.split(":", 1) if len(tokens) == 1: pattern, req_list = "*", req else: pattern, req_list = tokens req_list = [ ConanFileReference.loads(r.strip()) for r in req_list.split(",") ] base_profile.build_requires.setdefault(pattern, []).extend(req_list) if doc.scopes: base_profile.update_scopes(Scopes.from_list(doc.scopes.splitlines())) if doc.options: base_profile.options.update(OptionsValues.loads(doc.options)) base_profile.env_values.update(EnvValues.loads(doc.env))
def create_options(conanfile): try: package_options = PackageOptions(getattr(conanfile, "options", None)) options = Options(package_options) default_options = getattr(conanfile, "default_options", None) if default_options: if isinstance(default_options, tuple): default_values = OptionsValues.loads( "\n".join(default_options)) elif isinstance(default_options, list): default_values = OptionsValues.from_list(default_options) elif isinstance(default_options, basestring): default_values = OptionsValues.loads(default_options) else: raise ConanException( "Please define your default_options as list or " "multiline string") options.values = default_values return options except Exception as e: raise ConanException("Error while initializing options. %s" % str(e))
def _build_and_check(self, tmp_dir, file_path, text_file, msg): loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None) ret = loader.load_conan(file_path, None) curdir = os.path.abspath(os.curdir) os.chdir(tmp_dir) try: ret.build() finally: os.chdir(curdir) content = load(text_file) self.assertEquals(content, msg)
def _build_and_check(self, tmp_dir, file_path, text_file, msg): loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) ret = loader.load_conan(file_path, None) curdir = os.path.abspath(os.curdir) os.chdir(tmp_dir) try: ret.build() finally: os.chdir(curdir) content = load(text_file) self.assertEquals(content, msg)
def test_package_settings(self): # CREATE A CONANFILE TO LOAD tmp_dir = temp_folder() conanfile_path = os.path.join(tmp_dir, "conanfile.py") conanfile = """from conans import ConanFile class MyTest(ConanFile): requires = {} name = "MyPackage" version = "1.0" settings = "os" """ save(conanfile_path, conanfile) # Apply windows for MyPackage package_settings = {"MyPackage": [("os", "Windows")]} loader = ConanFileLoader(None, Settings({"os": ["Windows", "Linux"]}), package_settings, OptionsValues.loads(""), Scopes(), None, None) recipe = loader.load_conan(conanfile_path, None) self.assertEquals(recipe.settings.os, "Windows") # Apply Linux for MyPackage package_settings = {"MyPackage": [("os", "Linux")]} loader = ConanFileLoader(None, Settings({"os": ["Windows", "Linux"]}), package_settings, OptionsValues.loads(""), Scopes(), None, None) recipe = loader.load_conan(conanfile_path, None) self.assertEquals(recipe.settings.os, "Linux") # If the package name is different from the conanfile one, it wont apply package_settings = {"OtherPACKAGE": [("os", "Linux")]} loader = ConanFileLoader(None, Settings({"os": ["Windows", "Linux"]}), package_settings, OptionsValues.loads(""), Scopes(), None, None) recipe = loader.load_conan(conanfile_path, None) self.assertIsNone(recipe.settings.os.value)
def loads(text): graph_json = json.loads(text) try: options = graph_json["options"] except KeyError: options = None else: options = OptionsValues(options) root = graph_json.get("root", {"name": None, "version": None, "user": None, "channel": None}) root_ref = ConanFileReference(root["name"], root["version"], root["user"], root["channel"], validate=False) return GraphInfo(options=options, root_ref=root_ref)
def from_dict(data): """ constructs a GraphLockNode from a json like dict """ json_pref = data["pref"] pref = PackageReference.loads(json_pref, validate=False) if json_pref else None python_requires = data.get("python_requires") if python_requires: python_requires = [ConanFileReference.loads(ref, validate=False) for ref in python_requires] options = OptionsValues.loads(data["options"]) modified = data.get("modified") requires = data.get("requires", {}) path = data.get("path") return GraphLockNode(pref, python_requires, options, modified, requires, path)
def _apply_inner_profile(doc, base_profile): """ :param doc: ConfigParser object from the current profile (excluding includes and vars, and with values already replaced) :param base_profile: Profile inherited, it's used as a base profile to modify it. :return: None """ def get_package_name_value(item): """Parse items like package:name=value or name=value""" package_name = None if ":" in item: tmp = item.split(":", 1) package_name, item = tmp name, value = item.split("=", 1) name = name.strip() value = unquote(value) return package_name, name, value for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: base_profile.package_settings[package_name][name] = value else: base_profile.settings[name] = value if doc.build_requires: # FIXME CHECKS OF DUPLICATED? for req in doc.build_requires.splitlines(): tokens = req.split(":", 1) if len(tokens) == 1: pattern, req_list = "*", req else: pattern, req_list = tokens req_list = [ConanFileReference.loads(r.strip()) for r in req_list.split(",")] base_profile.build_requires.setdefault(pattern, []).extend(req_list) if doc.scopes: base_profile.update_scopes(Scopes.from_list(doc.scopes.splitlines())) if doc.options: base_profile.options.update(OptionsValues.loads(doc.options)) base_profile.env_values.update(EnvValues.loads(doc.env))
def requires_init_test(self): loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) tmp_dir = temp_folder() conanfile_path = os.path.join(tmp_dir, "conanfile.py") conanfile = """from conans import ConanFile class MyTest(ConanFile): requires = {} def requirements(self): self.requires("MyPkg/0.1@user/channel") """ for requires in ("''", "[]", "()", "None"): save(conanfile_path, conanfile.format(requires)) result = loader.load_conan(conanfile_path, output=None, consumer=True) result.requirements() self.assertEqual("MyPkg/0.1@user/channel", str(result.requires))
def _apply_inner_profile(doc, base_profile): """ :param doc: ConfigParser object from the current profile (excluding includes and vars, and with values already replaced) :param base_profile: Profile inherited, it's used as a base profile to modify it. :return: None """ def get_package_name_value(item): """Parse items like package:name=value or name=value""" package_name = None if ":" in item: tmp = item.split(":", 1) package_name, item = tmp name, value = item.split("=", 1) name = name.strip() value = unquote(value) return package_name, name, value for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: base_profile.package_settings[package_name][name] = value else: base_profile.settings[name] = value if doc.build_requires: # FIXME CHECKS OF DUPLICATED? for req in doc.build_requires.splitlines(): _load_single_build_require(base_profile, req) if doc.options: base_profile.options.update(OptionsValues.loads(doc.options)) # The env vars from the current profile (read in doc) # are updated with the included profiles (base_profile) # the current env values has priority current_env_values = EnvValues.loads(doc.env) current_env_values.update(base_profile.env_values) base_profile.env_values = current_env_values
def cmd_profile_update(profile_name, key, value, cache_profiles_path): first_key, rest_key = _get_profile_keys(key) profile, _ = read_profile(profile_name, get_cwd(), cache_profiles_path) if first_key == "settings": profile.settings[rest_key] = value elif first_key == "options": tmp = OptionsValues([(rest_key, value)]) profile.options.update(tmp) elif first_key == "env": profile.env_values.update_replace(rest_key, value) elif first_key == "build_requires": raise ConanException("Edit the profile manually to change the build_requires") contents = profile.dumps() profile_path = get_profile_path(profile_name, cache_profiles_path, get_cwd()) save(profile_path, contents)
def loads(text): graph_json = json.loads(text) profile = graph_json["profile"] # FIXME: Reading private very ugly profile, _ = _load_profile(profile, None, None) try: options = graph_json["options"] except KeyError: options = None else: options = OptionsValues(options) root = graph_json["root"] root_ref = ConanFileReference(root["name"], root["version"], root["user"], root["channel"], validate=False) return GraphInfo(profile=profile, options=options, root_ref=root_ref)
def loads(text): """ Parse and return a Profile object from a text config like representation """ def get_package_name_value(item): '''Parse items like package:name=value or name=value''' package_name = None if ":" in item: tmp = item.split(":", 1) package_name, item = tmp name, value = item.split("=", 1) name = name.strip() value = unquote(value) return package_name, name, value try: obj = Profile() doc = ConfigParser(text, allowed_fields=["settings", "env", "scopes", "options"]) for setting in doc.settings.splitlines(): setting = setting.strip() if setting and not setting.startswith("#"): if "=" not in setting: raise ConanException("Invalid setting line '%s'" % setting) package_name, name, value = get_package_name_value(setting) if package_name: obj.package_settings[package_name][name] = value else: obj.settings[name] = value if doc.scopes: obj.scopes = Scopes.from_list(doc.scopes.splitlines()) if doc.options: obj.options = OptionsValues.loads(doc.options) obj.env_values = EnvValues.loads(doc.env) return obj except ConanException: raise except Exception as exc: raise ConanException("Error parsing the profile text file: %s" % str(exc))
def print_inspect(self, inspect, raw=False): for k, v in inspect.items(): if k == "default_options": if isinstance(v, str): v = OptionsValues.loads(v) elif isinstance(v, tuple): v = OptionsValues(v) elif isinstance(v, list): v = OptionsValues(tuple(v)) elif isinstance(v, dict): v = OptionsValues(v) if raw: self._out.write(str(v)) else: if isinstance(v, (dict, OptionsValues)): self._out.writeln("%s:" % k) for ok, ov in sorted(v.items()): self._out.writeln(" %s: %s" % (ok, ov)) else: self._out.writeln("%s: %s" % (k, str(v)))
def setUp(self): self.output = TestBufferConanOutput() self.loader = ConanFileLoader(None, Settings.loads(""), None, OptionsValues.loads(""), Scopes(), None, None) self.retriever = Retriever(self.loader, self.output) self.remote_search = MockSearchRemote() self.resolver = RequireResolver(self.output, self.retriever, self.remote_search) self.builder = DepsBuilder(self.retriever, self.output, self.loader, self.resolver) for v in ["0.1", "0.2", "0.3", "1.1", "1.1.2", "1.2.1", "2.1", "2.2.1"]: say_content = """ from conans import ConanFile class SayConan(ConanFile): name = "Say" version = "%s" """ % v say_ref = ConanFileReference.loads("Say/%s@memsharded/testing" % v) self.retriever.conan(say_ref, say_content)
def deserialize(data, revisions_enabled): """ constructs a GraphLockNode from a json like dict """ json_ref = data.get("ref") ref = ConanFileReference.loads(json_ref) if json_ref else None package_id = data.get("package_id") prev = data.get("prev") python_requires = data.get("python_requires") if python_requires: python_requires = [ConanFileReference.loads(py_req, validate=False) for py_req in python_requires] options = OptionsValues.loads(data.get("options", "")) modified = data.get("modified") context = data.get("context") requires = data.get("requires", []) build_requires = data.get("build_requires", []) path = data.get("path") return GraphLockNode(ref, package_id, prev, python_requires, options, requires, build_requires, path, revisions_enabled, context, modified)
def test_patch_from_file(self): file_content = ''' from conans import ConanFile from conans.tools import patch import os class ConanFileToolsTest(ConanFile): name = "test" version = "1.9.10" settings = [] def source(self): pass def build(self): patch(patch_file="file.patch") ''' patch_content = '''--- text.txt\t2016-01-25 17:57:11.452848309 +0100 +++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100 @@ -1 +1 @@ -ONE TWO THREE +ONE TWO FOUR''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "conanfile.py") text_file = os.path.join(tmp_dir, "text.txt") patch_file = os.path.join(tmp_dir, "file.patch") save(file_path, file_content) save(text_file, "ONE TWO THREE") save(patch_file, patch_content) loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) ret = loader.load_conan(file_path, None) curdir = os.path.abspath(os.curdir) os.chdir(tmp_dir) try: ret.build() finally: os.chdir(curdir) content = load(text_file) self.assertEquals(content, "ONE TWO FOUR")
def update_profile(self, profile_name, key, value): first_key, rest_key = self._get_profile_keys(key) profile, _ = read_profile(profile_name, os.getcwd(), self._client_cache.profiles_path) if first_key == "settings": profile.settings[rest_key] = value elif first_key == "options": tmp = OptionsValues([(rest_key, value)]) profile.options.update(tmp) elif first_key == "env": profile.env_values.update(EnvValues.loads("%s=%s" % (rest_key, value))) elif first_key == "scopes": profile.update_scopes(Scopes.from_list(["%s=%s" % (rest_key, value)])) elif first_key == "build_requires": raise ConanException("Edit the profile manually to change the build_requires") contents = profile.dumps() profile_path = get_profile_path(profile_name, self._client_cache.profiles_path, os.getcwd()) save(profile_path, contents)
def test_patch_from_file(self): file_content = ''' from conans import ConanFile from conans.tools import patch import os class ConanFileToolsTest(ConanFile): name = "test" version = "1.9.10" settings = [] def source(self): pass def build(self): patch(patch_file="file.patch") ''' patch_content = '''--- text.txt\t2016-01-25 17:57:11.452848309 +0100 +++ text_new.txt\t2016-01-25 17:57:28.839869950 +0100 @@ -1 +1 @@ -ONE TWO THREE +ONE TWO FOUR''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "conanfile.py") text_file = os.path.join(tmp_dir, "text.txt") patch_file = os.path.join(tmp_dir, "file.patch") save(file_path, file_content) save(text_file, "ONE TWO THREE") save(patch_file, patch_content) loader = ConanFileLoader(None, Settings(), OptionsValues.loads(""), Scopes()) ret = loader.load_conan(file_path, None) curdir = os.path.abspath(os.curdir) os.chdir(tmp_dir) try: ret.build() finally: os.chdir(curdir) content = load(text_file) self.assertEquals(content, "ONE TWO FOUR")
def load_virtual(self, reference, path): fixed_options = [] # If user don't specify namespace in options, assume that it's for the reference (keep compatibility) for option_name, option_value in self._options.as_list(): if ":" not in option_name: tmp = ("%s:%s" % (reference.name, option_name), option_value) else: tmp = (option_name, option_value) fixed_options.append(tmp) options = OptionsValues.from_list(fixed_options) conanfile = ConanFile(None, self._runner, self._settings.copy(), path) conanfile.requires.add(str(reference)) # Convert to string necessary # conanfile.options.values = options conanfile.options.initialize_upstream(options) conanfile.generators = ["txt"] conanfile.scope = self._scopes.package_scope() return conanfile
def parse_conan_txt(self, contents, path, output): conanfile = ConanFile(output, self._runner, self._settings.copy(), path) try: parser = ConanFileTextLoader(contents) except Exception as e: raise ConanException("%s:\n%s" % (path, str(e))) for requirement_text in parser.requirements: ConanFileReference.loads(requirement_text) # Raise if invalid conanfile.requires.add(requirement_text) conanfile.generators = parser.generators options = OptionsValues.loads(parser.options) conanfile.options.values = options conanfile.options.initialize_upstream(self._options, conanfile.name) # imports method conanfile.imports = ConanFileTextLoader.imports_method(conanfile, parser.import_parameters) conanfile.scope = self._scopes.package_scope() return conanfile
def load_conan_txt(self, conan_requirements_path): if not os.path.exists(conan_requirements_path): raise NotFoundException("%s not found!" % CONANFILE_TXT) conanfile = ConanFile(self._output, self._runner, self._settings.copy()) parser = ConanFileTextLoader(load(conan_requirements_path)) for requirement_text in parser.requirements: ConanFileReference.loads(requirement_text) # Raise if invalid conanfile.requires.add(requirement_text) conanfile.generators = parser.generators options = OptionsValues.loads(parser.options) conanfile.options.values = options conanfile.options.initialize_upstream(self._options) # imports method conanfile.imports = ConanFileTextLoader.imports_method(conanfile, parser.import_parameters) return conanfile
def test_from_list(self): option_values = OptionsValues.from_list(self.sut.as_list()) self.assertEqual(option_values.dumps(), self.sut.dumps())
def complete_test(self): """ basic installation of a new conans """ servers = {} # All can write (for avoid authentication until we mock user_io) test_server = TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")]) servers["default"] = test_server conan_digest = FileTreeManifest(123123123, {}) client = TestClient(servers=servers) client.init_dynamic_vars() conan_ref = ConanFileReference.loads("Hello/1.2.1@frodo/stable") reg_folder = client.paths.export(conan_ref) files = hello_source_files() client.save(files, path=reg_folder) client.save({CONANFILE: myconan1, CONAN_MANIFEST: str(conan_digest), "include/math/lib1.h": "//copy", "my_lib/debug/libd.a": "//copy", "my_data/readme.txt": "//copy"}, path=reg_folder) package_ref = PackageReference(conan_ref, "fakeid") package_folder = client.paths.package(package_ref) save(os.path.join(package_folder, "include", "lib1.h"), "//header") save(os.path.join(package_folder, "lib", "my_lib", "libd.a"), "//lib") save(os.path.join(package_folder, "res", "shares", "readme.txt"), "//res") client.remote_manager.upload_conan(conan_ref) client.remote_manager.upload_package(package_ref) client2 = TestClient(servers=servers) client2.init_dynamic_vars() loader = ConanFileLoader(None, Settings(), OptionsValues.loads("")) installer = ConanInstaller(client2.paths, client2.user_io, loader, client2.remote_manager, "default") installer.retrieve_conanfile(conan_ref) installer._retrieve_remote_package(package_ref, TestBufferConanOutput()) reg_path = client2.paths.export(ConanFileReference.loads("Hello/1.2.1/frodo/stable")) pack_folder = client2.paths.package(package_ref) # Test the file in the downloaded conans files = ['CMakeLists.txt', 'my_lib/debug/libd.a', 'hello.cpp', 'hello0.h', CONANFILE, CONAN_MANIFEST, 'main.cpp', 'include/math/lib1.h', 'my_data/readme.txt'] for _file in files: self.assertTrue(os.path.exists(os.path.join(reg_path, _file))) self.assertTrue(os.path.exists(pack_folder)) # Test the file in the downloaded package self.assertTrue(os.path.exists(pack_folder)) self.assertTrue(os.path.exists(os.path.join(pack_folder, "include", "lib1.h"))) self.assertTrue(os.path.exists(os.path.join(pack_folder, "lib", "my_lib/libd.a"))) self.assertTrue(os.path.exists(os.path.join(pack_folder, "res", "shares/readme.txt")))
def complete_test(self): """ basic installation of a new conans """ client = TestClient() client.init_dynamic_vars() files = hello_source_files() conan_ref = ConanFileReference.loads("Hello/1.2.1/frodo/stable") reg_folder = client.paths.export(conan_ref) client.save(files, path=reg_folder) client.save({CONANFILE: myconan1, CONANINFO: "//empty", "include/no_copy/lib0.h": "NO copy", "include/math/lib1.h": "copy", "include/math/lib2.h": "copy", "include/physics/lib.hpp": "copy", "my_lib/debug/libd.a": "copy", "my_data/readme.txt": "copy", "my_data/readme.md": "NO copy", "contrib/math/math.h": "copy", "contrib/physics/gravity.h": "copy", "contrib/contrib.h": "copy", "include/opencv/opencv.hpp": "copy", "include/opencv2/opencv2.hpp": "copy", "modules/simu/src/simu.cpp": "NO copy", "modules/simu/include/opencv2/simu/simu.hpp": "copy", "modules/3D/doc/readme.md": "NO copy", "modules/3D/include/opencv2/3D/3D.hpp": "copy", "modules/dev/src/dev.cpp": "NO copy", "modules/dev/include/opencv2/dev/dev.hpp": "copy", "modules/opencv_mod.hpp": "copy"}, path=reg_folder) conanfile_path = os.path.join(reg_folder, CONANFILE) package_ref = PackageReference(conan_ref, "myfakeid") build_folder = client.paths.build(package_ref) package_folder = client.paths.package(package_ref) shutil.copytree(reg_folder, build_folder) loader = ConanFileLoader(None, Settings(), OptionsValues.loads("")) conanfile = loader.load_conan(conanfile_path, None) output = ScopedOutput("", TestBufferConanOutput()) create_package(conanfile, build_folder, package_folder, output) # test build folder self.assertTrue(os.path.exists(build_folder)) self.assertTrue(os.path.exists(os.path.join(package_folder, CONANINFO))) # test pack folder self.assertTrue(os.path.exists(package_folder)) def exist(rel_path): return os.path.exists(os.path.join(package_folder, rel_path)) # Expected files self.assertTrue(exist("include/lib1.h")) self.assertTrue(exist("include/lib2.h")) self.assertTrue(exist("include/physics/lib.hpp")) self.assertTrue(exist("include/contrib/math/math.h")) self.assertTrue(exist("include/contrib/physics/gravity.h")) self.assertTrue(exist("include/contrib/contrib.h")) self.assertTrue(exist("include/opencv/opencv.hpp")) self.assertTrue(exist("include/opencv2/opencv2.hpp")) self.assertTrue(exist("include/opencv2/simu/simu.hpp")) self.assertTrue(exist("include/opencv2/3D/3D.hpp")) self.assertTrue(exist("include/opencv2/dev/dev.hpp")) self.assertTrue(exist("lib/my_lib/libd.a")) self.assertTrue(exist("res/shares/readme.txt")) # Not expected files self.assertFalse(exist("include/opencv2/opencv_mod.hpp")) self.assertFalse(exist("include/opencv2/simu.hpp")) self.assertFalse(exist("include/opencv2/3D.hpp")) self.assertFalse(exist("include/opencv2/dev.hpp")) self.assertFalse(exist("include/modules/simu/src/simu.cpp")) self.assertFalse(exist("include/modules/3D/doc/readme.md")) self.assertFalse(exist("include/modules/dev/src/dev.cpp")) self.assertFalse(exist("include/opencv2/opencv_mod.hpp")) self.assertFalse(exist("include/include/no_copy/lib0.h")) self.assertFalse(exist("res/my_data/readme.md"))
def load_conan_txt_test(self): file_content = '''[requires] OpenCV/2.4.10@phil/stable OpenCV2/2.4.10@phil/stable [generators] one two [imports] OpenCV/bin, * -> ./bin # I need this binaries OpenCV/lib, * -> ./lib [options] OpenCV:use_python=True OpenCV:other_option=False OpenCV2:use_python2=1 OpenCV2:other_option=Cosa ''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "file.txt") save(file_path, file_content) loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) ret = loader.load_conan_txt(file_path, None) options1 = OptionsValues.loads("""OpenCV:use_python=True OpenCV:other_option=False OpenCV2:use_python2=1 OpenCV2:other_option=Cosa""") requirements = Requirements() requirements.add("OpenCV/2.4.10@phil/stable") requirements.add("OpenCV2/2.4.10@phil/stable") self.assertEquals(ret.requires, requirements) self.assertEquals(ret.generators, ["one", "two"]) self.assertEquals(ret.options.values.dumps(), options1.dumps()) ret.copy = Mock() ret.imports() self.assertTrue(ret.copy.call_args_list, [('*', './bin', 'OpenCV/bin'), ('*', './lib', 'OpenCV/lib')]) # Now something that fails file_content = '''[requires] OpenCV/2.4.104phil/stable <- use_python:True, other_option:False ''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "file.txt") save(file_path, file_content) loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) with self.assertRaisesRegexp(ConanException, "Wrong package recipe reference(.*)"): loader.load_conan_txt(file_path, None) file_content = '''[requires] OpenCV/2.4.10@phil/stable <- use_python:True, other_option:False [imports] OpenCV/bin/* - ./bin ''' tmp_dir = temp_folder() file_path = os.path.join(tmp_dir, "file.txt") save(file_path, file_content) loader = ConanFileLoader(None, Settings(), None, OptionsValues.loads(""), Scopes(), None, None) with self.assertRaisesRegexp(ConanException, "is too long. Valid names must contain"): loader.load_conan_txt(file_path, None)
def setUp(self): self.output = TestBufferConanOutput() self.loader = ConanFileLoader(None, Settings.loads(""), OptionsValues.loads(""), Scopes()) self.retriever = Retriever(self.loader, self.output) self.builder = DepsBuilder(self.retriever, self.output, self.loader)
class Profile(object): """A profile contains a set of setting (with values), environment variables and scopes""" def __init__(self): # Sections self.settings = OrderedDict() self.package_settings = defaultdict(OrderedDict) self.env_values = EnvValues() self.scopes = Scopes() self.options = OptionsValues() self.build_requires = OrderedDict() # conan_ref Pattern: list of conan_ref @property def settings_values(self): return Values.from_list(list(self.settings.items())) @property def package_settings_values(self): result = {} for pkg, settings in self.package_settings.items(): result[pkg] = list(settings.items()) return result def dumps(self): result = ["[build_requires]"] for pattern, req_list in self.build_requires.items(): result.append("%s: %s" % (pattern, ", ".join(str(r) for r in req_list))) result.append("[settings]") for name, value in self.settings.items(): result.append("%s=%s" % (name, value)) for package, values in self.package_settings.items(): for name, value in values.items(): result.append("%s:%s=%s" % (package, name, value)) result.append("[options]") result.append(self.options.dumps()) result.append("[scopes]") if self.scopes[_root].get("dev", None): # FIXME: Ugly _root import del self.scopes[_root]["dev"] # Do not include dev scopes_txt = self.scopes.dumps() result.append(scopes_txt) result.append("[env]") result.append(self.env_values.dumps()) return "\n".join(result).replace("\n\n", "\n") def update(self, other): self.update_settings(other.settings) self.update_package_settings(other.package_settings) self.update_scopes(other.scopes) # this is the opposite other.env_values.update(self.env_values) self.env_values = other.env_values self.options.update(other.options) for pattern, req_list in other.build_requires.items(): self.build_requires.setdefault(pattern, []).extend(req_list) def update_settings(self, new_settings): '''Mix the specified settings with the current profile. Specified settings are prioritized to profile''' # apply the current profile if new_settings: self.settings.update(new_settings) def update_package_settings(self, package_settings): '''Mix the specified package settings with the specified profile. Specified package settings are prioritized to profile''' for package_name, settings in package_settings.items(): self.package_settings[package_name].update(settings) def update_scopes(self, new_scopes): '''Mix the specified settings with the current profile. Specified settings are prioritized to profile''' # apply the current profile if new_scopes: self.scopes.update(new_scopes)
class Profile(object): """A profile contains a set of setting (with values), environment variables """ def __init__(self): # Sections self.settings = OrderedDict() self.package_settings = defaultdict(OrderedDict) self.env_values = EnvValues() self.options = OptionsValues() self.build_requires = OrderedDict() # conan_ref Pattern: list of conan_ref @property def settings_values(self): return Values.from_list(list(self.settings.items())) @property def package_settings_values(self): result = {} for pkg, settings in self.package_settings.items(): result[pkg] = list(settings.items()) return result def dumps(self): result = ["[settings]"] for name, value in self.settings.items(): result.append("%s=%s" % (name, value)) for package, values in self.package_settings.items(): for name, value in values.items(): result.append("%s:%s=%s" % (package, name, value)) result.append("[options]") result.append(self.options.dumps()) result.append("[build_requires]") for pattern, req_list in self.build_requires.items(): result.append("%s: %s" % (pattern, ", ".join(str(r) for r in req_list))) result.append("[env]") result.append(self.env_values.dumps()) return "\n".join(result).replace("\n\n", "\n") def update(self, other): self.update_settings(other.settings) self.update_package_settings(other.package_settings) # this is the opposite other.env_values.update(self.env_values) self.env_values = other.env_values self.options.update(other.options) for pattern, req_list in other.build_requires.items(): self.build_requires.setdefault(pattern, []).extend(req_list) def update_settings(self, new_settings): """Mix the specified settings with the current profile. Specified settings are prioritized to profile""" assert(isinstance(new_settings, OrderedDict)) # apply the current profile res = copy.copy(self.settings) if new_settings: # Invalidate the current subsettings if the parent setting changes # Example: new_settings declare a different "compiler", so invalidate the current "compiler.XXX" for name, value in new_settings.items(): if "." not in name: if name in self.settings and self.settings[name] != value: for cur_name, _ in self.settings.items(): if cur_name.startswith("%s." % name): del res[cur_name] # Now merge the new values res.update(new_settings) self.settings = res def update_package_settings(self, package_settings): """Mix the specified package settings with the specified profile. Specified package settings are prioritized to profile""" for package_name, settings in package_settings.items(): self.package_settings[package_name].update(settings)