def test_append(self): myprofile = textwrap.dedent(""" # define MyVar1+=MyValue1 MyPath1+=(path)/my/path1 """) env = ProfileEnvironment.loads(myprofile) text = env.dumps() assert text == textwrap.dedent("""\ MyVar1+=MyValue1 MyPath1+=(path)/my/path1 """)
def test_profile(): myprofile = textwrap.dedent(""" # define MyVar1 =MyValue1 # append MyVar2+=MyValue2 # multiple append works MyVar2+=MyValue2_2 # prepend MyVar3=+MyValue3 # unset MyVar4=! # Empty MyVar5= # PATHS # define MyPath1=(path)/my/path1 # append MyPath2+=(path)/my/path2 # multiple append works MyPath2+=(path)/my/path2_2 # prepend MyPath3=+(path)/my/path3 # unset MyPath4=! # PER-PACKAGE mypkg*:MyVar2=MyValue2 """) profile_env = ProfileEnvironment.loads(myprofile) env = profile_env.get_profile_env("") env = env.vars(ConanFileMock()) with environment_append({ "MyVar1": "$MyVar1", "MyVar2": "$MyVar2", "MyVar3": "$MyVar3", "MyVar4": "$MyVar4" }): assert env.get("MyVar1") == "MyValue1" assert env.get("MyVar2", "$MyVar2") == '$MyVar2 MyValue2 MyValue2_2' assert env.get("MyVar3", "$MyVar3") == 'MyValue3 $MyVar3' assert env.get("MyVar4") == "" assert env.get("MyVar5") == '' env = profile_env.get_profile_env("mypkg1/1.0") env = env.vars(ConanFileMock()) assert env.get("MyVar1") == "MyValue1" assert env.get("MyVar2", "$MyVar2") == 'MyValue2'
def test_combined(self): myprofile = textwrap.dedent(""" MyVar1=+MyValue11 MyVar1+=MyValue12 MyPath1=+(path)/my/path11 MyPath1+=(path)/my/path12 """) env = ProfileEnvironment.loads(myprofile) text = env.dumps() assert text == textwrap.dedent("""\ MyVar1=+MyValue11 MyVar1+=MyValue12 MyPath1=+(path)/my/path11 MyPath1+=(path)/my/path12 """)
def test_combined2(self): myprofile = textwrap.dedent(""" MyVar1+=MyValue11 MyVar1=+MyValue12 MyPath1+=(path)/my/path11 MyPath1=+(path)/my/path12 """) env = ProfileEnvironment.loads(myprofile) text = env.dumps() # NOTE: This is reversed order compared to origin, prepend always first assert text == textwrap.dedent("""\ MyVar1=+MyValue12 MyVar1+=MyValue11 MyPath1=+(path)/my/path12 MyPath1+=(path)/my/path11 """)
class Profile(object): """A profile contains a set of setting (with values), environment variables """ def __init__(self): # Input sections, as defined by user profile files and command line self.settings = OrderedDict() self.package_settings = defaultdict(OrderedDict) self.env_values = EnvValues() self.options = OptionsValues() self.build_requires = OrderedDict() # ref pattern: list of ref self.conf = ConfDefinition() self.buildenv = ProfileEnvironment() # Cached processed values self.processed_settings = None # Settings with values, and smart completion self._user_options = None self._package_settings_values = None self.dev_reference = None # Reference of the package being develop @property def user_options(self): if self._user_options is None: self._user_options = self.options.copy() return self._user_options @property def package_settings_values(self): if self._package_settings_values is None: self._package_settings_values = {} for pkg, settings in self.package_settings.items(): self._package_settings_values[pkg] = list(settings.items()) return self._package_settings_values def process_settings(self, cache, preprocess=True): assert self.processed_settings is None, "processed settings must be None" self.processed_settings = cache.settings.copy() self.processed_settings.values = Values.from_list( list(self.settings.items())) if preprocess: settings_preprocessor.preprocess(self.processed_settings) # Redefine the profile settings values with the preprocessed ones # FIXME: Simplify the values.as_list() self.settings = OrderedDict( self.processed_settings.values.as_list()) # Preprocess also scoped settings for pkg, pkg_settings in self.package_settings.items(): pkg_profile = Profile() pkg_profile.settings = self.settings pkg_profile.update_settings(pkg_settings) try: pkg_profile.process_settings(cache=cache, preprocess=True) except Exception as e: pkg_profile = [ "{}={}".format(k, v) for k, v in pkg_profile.settings.items() ] raise ConanException( "Error in resulting settings for package" " '{}': {}\n{}".format(pkg, e, '\n'.join(pkg_profile))) # TODO: Assign the _validated_ settings and do not compute again 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()) if self.conf: result.append("[conf]") result.append(self.conf.dumps()) if self.buildenv: result.append("[buildenv]") result.append(self.buildenv.dumps()) return "\n".join(result).replace("\n\n", "\n") def compose_profile(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) # It is possible that build_requires are repeated, or same package but different versions for pattern, req_list in other.build_requires.items(): existing_build_requires = self.build_requires.get(pattern) existing = OrderedDict() if existing_build_requires is not None: for br in existing_build_requires: # TODO: Understand why sometimes they are str and other are ConanFileReference r = ConanFileReference.loads(br) \ if not isinstance(br, ConanFileReference) else br existing[r.name] = br for req in req_list: r = ConanFileReference.loads(req) \ if not isinstance(req, ConanFileReference) else req existing[r.name] = req self.build_requires[pattern] = list(existing.values()) self.conf.update_conf_definition(other.conf) self.buildenv.update_profile_env( other.buildenv) # Profile composition, last has priority 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)