From d4049cccc3a87d33e9e24b25b0bb2e32e148ee99 Mon Sep 17 00:00:00 2001 From: memsharded Date: Sat, 24 Jul 2021 01:25:39 +0200 Subject: [PATCH 1/7] wip --- conan/tools/env/environment.py | 42 +++++++++++++++++++-- conans/test/unittests/tools/env/test_env.py | 42 ++++++++++++++------- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index a7fd3f59722..de8646df9f7 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -1,4 +1,5 @@ import fnmatch +import json import os import textwrap import platform @@ -53,13 +54,30 @@ def environment_wrap_command(conanfile, env_filenames, cmd, cwd=None): class _EnvValue: - def __init__(self, name, value=_EnvVarPlaceHolder, separator=" ", - path=False): + def __init__(self, name, value=_EnvVarPlaceHolder, separator=" ", path=False): self._name = name self._values = [] if value is None else value if isinstance(value, list) else [value] self._path = path self._sep = separator + def dumps(self): + result = [] + path = "(path)" if self._path else "" + if not self._values: # Empty means unset + result.append("{}=!".format(self._name)) + elif _EnvVarPlaceHolder in self._values: + index = self._values.index(_EnvVarPlaceHolder) + for v in self._values[:index]: + result.append("{}=+{}{}".format(self._name, path, v)) + for v in self._values[index+1:]: + result.append("{}+={}{}".format(self._name, path, v)) + else: + append = "" + for v in self._values: + result.append("{}{}={}{}".format(self._name, append, path, v)) + append = "+" + return "\n".join(result) + def copy(self): return _EnvValue(self._name, self._values, self._sep, self._path) @@ -138,9 +156,17 @@ def __bool__(self): __nonzero__ = __bool__ + def copy(self): + e = Environment(self._conanfile) + e._values = self._values.copy() + return e + def __repr__(self): return repr(self._values) + def dumps(self): + return "\n".join([v.dumps() for v in reversed(self._values.values())]) + def define(self, name, value, separator=" "): self._values[name] = _EnvValue(name, value, separator, path=False) @@ -327,7 +353,7 @@ def get_env(self, conanfile, ref): result = Environment(conanfile) for pattern, env in self._environments.items(): if pattern is None or fnmatch.fnmatch(str(ref), pattern): - result = env.compose(result) + result = env.copy().compose(result) # FIXME: Needed to assign _conanfile here too because in the env.compose returns env and it # hasn't conanfile @@ -345,6 +371,16 @@ def compose(self, other): else: self._environments[pattern] = environment + def dumps(self): + result = [] + for pattern, env in self._environments.items(): + if pattern is None: + result.append(env.dumps()) + else: + result.append("\n".join("{}:{}".format(pattern, line) if line else "" + for line in env.dumps().splitlines())) + return "\n".join(result) + @staticmethod def loads(text): result = ProfileEnvironment() diff --git a/conans/test/unittests/tools/env/test_env.py b/conans/test/unittests/tools/env/test_env.py index e92d410f819..15452a374e1 100644 --- a/conans/test/unittests/tools/env/test_env.py +++ b/conans/test/unittests/tools/env/test_env.py @@ -159,20 +159,34 @@ def test_profile(): """) profile_env = ProfileEnvironment.loads(myprofile) - env = profile_env.get_env(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_env(ConanFileMock(), "mypkg1/1.0") - assert env.get("MyVar1") == "MyValue1" - assert env.get("MyVar2", "$MyVar2") == 'MyValue2' + + print("*"*40, profile_env.dumps()) + + def verify(env): + 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_env(ConanFileMock(), "mypkg1/1.0") + assert env.get("MyVar1") == "MyValue1" + assert env.get("MyVar2", "$MyVar2") == 'MyValue2' + + e = profile_env.get_env(ConanFileMock(), "") + verify(e) + + # test round-trip + text = profile_env.dumps() + print(text) + # load again + round_trip_profile = ProfileEnvironment.loads(text) + e = profile_env.get_env(ConanFileMock(), "") + verify(e) def test_env_files(): From 009c3f3bd8e978a9db256a1bbd11af2c0ffea966 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 26 Jul 2021 01:29:39 +0200 Subject: [PATCH 2/7] wip --- conan/tools/env/environment.py | 4 +- conans/test/unittests/tools/env/test_env.py | 161 ++++++++++++++------ 2 files changed, 115 insertions(+), 50 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index de8646df9f7..b34022a118d 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -1,5 +1,4 @@ import fnmatch -import json import os import textwrap import platform @@ -119,6 +118,7 @@ def compose(self, other): def get_str(self, conanfile, placeholder, pathsep=os.pathsep): """ + :param conanfile: The conanfile is necessary to get win_bash, path separator, etc. :param placeholder: a OS dependant string pattern of the previous env-var value like $PATH, %PATH%, et :param pathsep: The path separator, typically ; or : @@ -379,6 +379,8 @@ def dumps(self): else: result.append("\n".join("{}:{}".format(pattern, line) if line else "" for line in env.dumps().splitlines())) + if result: + result.append("") return "\n".join(result) @staticmethod diff --git a/conans/test/unittests/tools/env/test_env.py b/conans/test/unittests/tools/env/test_env.py index 15452a374e1..70b53f6e619 100644 --- a/conans/test/unittests/tools/env/test_env.py +++ b/conans/test/unittests/tools/env/test_env.py @@ -14,18 +14,15 @@ from conans.util.files import save -conanfile = ConanFileMock() - - def test_compose(): - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.define("MyVar", "MyValue") env.define("MyVar2", "MyValue2") env.define("MyVar3", "MyValue3") env.define("MyVar4", "MyValue4") env.unset("MyVar5") - env2 = Environment(conanfile) + env2 = Environment(ConanFileMock()) env2.define("MyVar", "MyNewValue") env2.append("MyVar2", "MyNewValue2") env2.prepend("MyVar3", "MyNewValue3") @@ -41,13 +38,13 @@ def test_compose(): def test_define_append(): - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.define("MyVar", "MyValue") env.append("MyVar", "MyValue1") env.append("MyVar", ["MyValue2", "MyValue3"]) assert env.get("MyVar") == "MyValue MyValue1 MyValue2 MyValue3" - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.append("MyVar", "MyValue") env.append("MyVar", "MyValue1") env.define("MyVar", "MyValue2") @@ -78,12 +75,12 @@ def test_define_append(): ("unset", "", " ", "prepend", "Val2", "+", ""), ]) def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): - env = Environment(conanfile) + env = Environment(ConanFileMock()) if op1 != "unset": getattr(env, op1)("MyVar", v1, s1) else: env.unset("MyVar") - env2 = Environment(conanfile) + env2 = Environment(ConanFileMock()) if op2 != "unset": getattr(env2, op2)("MyVar", v2, s2) else: @@ -91,7 +88,7 @@ def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): env.compose(env2) with environment_append({"MyVar": "MyVar"}): assert env.get("MyVar") == result - assert env._values["MyVar"].get_str(conanfile, "{name}") == result + assert env._values["MyVar"].get_str(ConanFileMock(), "{name}") == result @pytest.mark.parametrize("op1, v1, op2, v2, result", @@ -113,18 +110,18 @@ def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): ("unset", "", "unset", "", ""), ]) def test_compose_path_combinations(op1, v1, op2, v2, result): - env = Environment(conanfile) + env = Environment(ConanFileMock()) if op1 != "unset": getattr(env, op1+"_path")("MyVar", v1) else: env.unset("MyVar") - env2 = Environment(conanfile) + env2 = Environment(ConanFileMock()) if op2 != "unset": getattr(env2, op2+"_path")("MyVar", v2) else: env2.unset("MyVar") env.compose(env2) - assert env._values["MyVar"].get_str(conanfile, "{name}", pathsep=":") == result + assert env._values["MyVar"].get_str(ConanFileMock(), "{name}", pathsep=":") == result def test_profile(): @@ -159,38 +156,24 @@ def test_profile(): """) profile_env = ProfileEnvironment.loads(myprofile) - - print("*"*40, profile_env.dumps()) - - def verify(env): - 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_env(ConanFileMock(), "mypkg1/1.0") - assert env.get("MyVar1") == "MyValue1" - assert env.get("MyVar2", "$MyVar2") == 'MyValue2' - - e = profile_env.get_env(ConanFileMock(), "") - verify(e) - - # test round-trip - text = profile_env.dumps() - print(text) - # load again - round_trip_profile = ProfileEnvironment.loads(text) - e = profile_env.get_env(ConanFileMock(), "") - verify(e) + env = profile_env.get_env(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_env(ConanFileMock(), "mypkg1/1.0") + assert env.get("MyVar1") == "MyValue1" + assert env.get("MyVar2", "$MyVar2") == 'MyValue2' def test_env_files(): - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.define("MyVar", "MyValue") env.define("MyVar1", "MyValue1") env.append("MyVar2", "MyValue2") @@ -297,7 +280,7 @@ def check(cmd_): @pytest.mark.skipif(platform.system() != "Windows", reason="Requires Windows") def test_windows_case_insensitive(): # Append and define operation over the same variable in Windows preserve order - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.define("MyVar", "MyValueA") env.define("MYVAR", "MyValueB") env.define("MyVar1", "MyValue1A") @@ -325,18 +308,18 @@ def test_windows_case_insensitive(): def test_dict_access(): - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.append("MyVar", "MyValue", separator="@") ret = env.items() assert dict(ret) == {"MyVar": "MyValue"} - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.prepend("MyVar", "MyValue", separator="@") ret = env.items() assert dict(ret) == {"MyVar": "MyValue"} assert env["MyVar"] == "MyValue" - env2 = Environment(conanfile) + env2 = Environment(ConanFileMock()) env2.define("MyVar", "MyValue2") env.compose(env2) ret = env.items() @@ -346,7 +329,7 @@ def test_dict_access(): _ = env["Missing"] # With previous values in the environment - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.prepend("MyVar", "MyValue", separator="@") old_env = dict(os.environ) os.environ.update({"MyVar": "PreviousValue"}) @@ -356,7 +339,7 @@ def test_dict_access(): os.environ.clear() os.environ.update(old_env) - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.append_path("MyVar", "MyValue") old_env = dict(os.environ) os.environ.update({"MyVar": "PreviousValue"}) @@ -391,7 +374,7 @@ def test_env_win_bash(): def test_public_access(): - env = Environment(conanfile) + env = Environment(ConanFileMock()) env.define("MyVar", "MyValue") env.append("MyVar", "MyValue2") env.define_path("MyPath", "c:/path/to/something") @@ -404,3 +387,83 @@ def test_public_access(): env.remove("MyPath", "D:/Otherpath") assert env.get("MyPath") == "c:/path/to/something" + + +class TestProfileEnvRoundTrip: + + def test_define(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_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_prepend(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_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 alwyas first + assert text == textwrap.dedent("""\ + MyVar1=+MyValue12 + MyVar1+=MyValue11 + MyPath1=+(path)/my/path12 + MyPath1+=(path)/my/path11 + """) From 41928c03a21df243f6c15286250b523dd045a945 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 26 Jul 2021 09:53:57 +0200 Subject: [PATCH 3/7] renaming methods to dissambiguate --- conan/tools/env/environment.py | 14 +++++++------- conan/tools/env/virtualbuildenv.py | 10 +++++----- conan/tools/env/virtualrunenv.py | 5 ++--- conans/client/profile_loader.py | 8 ++++---- conans/model/profile.py | 8 ++++++-- .../profile_loader/profile_loader_test.py | 18 ++++++++++++++++++ conans/test/unittests/model/profile_test.py | 6 +++--- conans/test/unittests/tools/env/test_env.py | 8 ++++---- 8 files changed, 49 insertions(+), 28 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index b34022a118d..309e6471c9f 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -103,7 +103,7 @@ def prepend(self, value, separator=None): else: self._values.insert(0, value) - def compose(self, other): + def compose_env_value(self, other): """ :type other: _EnvValue """ @@ -285,7 +285,7 @@ def save_script(self, name, auto_activate=True): if auto_activate: register_environment_script(self._conanfile, path) - def compose(self, other): + def compose_env(self, other): """ self has precedence, the "other" will add/append if possible and not conflicting, but self mandates what to do @@ -296,7 +296,7 @@ def compose(self, other): if existing is None: self._values[k] = v.copy() else: - existing.compose(v) + existing.compose_env_value(v) return self @@ -353,21 +353,21 @@ def get_env(self, conanfile, ref): result = Environment(conanfile) for pattern, env in self._environments.items(): if pattern is None or fnmatch.fnmatch(str(ref), pattern): - result = env.copy().compose(result) + result = env.copy().compose_env(result) # FIXME: Needed to assign _conanfile here too because in the env.compose returns env and it # hasn't conanfile result._conanfile = conanfile return result - def compose(self, other): + def compose_profile_env(self, other): """ :type other: ProfileEnvironment """ for pattern, environment in other._environments.items(): existing = self._environments.get(pattern) if existing is not None: - self._environments[pattern] = environment.compose(existing) + self._environments[pattern] = environment.compose_env(existing) else: self._environments[pattern] = environment @@ -415,7 +415,7 @@ def loads(text): if existing is None: result._environments[pattern] = env else: - result._environments[pattern] = env.compose(existing) + result._environments[pattern] = env.compose_env(existing) break else: raise ConanException("Bad env defintion: {}".format(line)) diff --git a/conan/tools/env/virtualbuildenv.py b/conan/tools/env/virtualbuildenv.py index 345d1825561..d4ffbf6958e 100644 --- a/conan/tools/env/virtualbuildenv.py +++ b/conan/tools/env/virtualbuildenv.py @@ -20,23 +20,23 @@ def environment(self): # Top priority: profile profile_env = self._conanfile.buildenv - build_env.compose(profile_env) + build_env.compose_env(profile_env) for require, build_require in self._conanfile.dependencies.build.items(): if require.direct: # higher priority, explicit buildenv_info if build_require.buildenv_info: - build_env.compose(build_require.buildenv_info) + build_env.compose_env(build_require.buildenv_info) # Lower priority, the runenv of all transitive "requires" of the build requires if build_require.runenv_info: - build_env.compose(build_require.runenv_info) + build_env.compose_env(build_require.runenv_info) # Then the implicit - build_env.compose(runenv_from_cpp_info(self._conanfile, build_require.cpp_info)) + build_env.compose_env(runenv_from_cpp_info(self._conanfile, build_require.cpp_info)) # Requires in host context can also bring some direct buildenv_info for require in self._conanfile.dependencies.host.values(): if require.buildenv_info: - build_env.compose(require.buildenv_info) + build_env.compose_env(require.buildenv_info) return build_env diff --git a/conan/tools/env/virtualrunenv.py b/conan/tools/env/virtualrunenv.py index 672d6af721f..8681dc9beb7 100644 --- a/conan/tools/env/virtualrunenv.py +++ b/conan/tools/env/virtualrunenv.py @@ -38,9 +38,8 @@ def environment(self): test_req = self._conanfile.dependencies.test for _, dep in list(host_req.items()) + list(test_req.items()): if dep.runenv_info: - runenv.compose(dep.runenv_info) - runenv.compose(runenv_from_cpp_info(self._conanfile, dep.cpp_info)) - + runenv.compose_env(dep.runenv_info) + runenv.compose_env(runenv_from_cpp_info(self._conanfile, dep.cpp_info)) return runenv diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index c3456a4ae06..38a0f2e5a96 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -149,7 +149,7 @@ def _load_profile(text, profile_path, default_folder): for include in profile_parser.get_includes(): # Recursion !! profile, included_vars = read_profile(include, cwd, default_folder) - inherited_profile.compose(profile) + inherited_profile.compose_profile(profile) profile_parser.update_vars(included_vars) # Apply the automatic PROFILE_DIR variable @@ -239,7 +239,7 @@ def get_package_name_value(item): if doc.buildenv: buildenv = ProfileEnvironment.loads(doc.buildenv) - base_profile.buildenv.compose(buildenv) + base_profile.buildenv.compose_env(buildenv) def profile_from_args(profiles, settings, options, env, conf, cwd, cache): @@ -253,12 +253,12 @@ def profile_from_args(profiles, settings, options, env, conf, cwd, cache): result = Profile() for p in profiles: tmp, _ = read_profile(p, cwd, cache.profiles_path) - result.compose(tmp) + result.compose_profile(tmp) args_profile = _profile_parse_args(settings, options, env, conf) if result: - result.compose(args_profile) + result.compose_profile(args_profile) else: result = args_profile return result diff --git a/conans/model/profile.py b/conans/model/profile.py index 30b538b09c3..56e8360332c 100644 --- a/conans/model/profile.py +++ b/conans/model/profile.py @@ -90,9 +90,13 @@ def dumps(self): 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(self, other): + def compose_profile(self, other): self.update_settings(other.settings) self.update_package_settings(other.package_settings) # this is the opposite @@ -116,7 +120,7 @@ def compose(self, other): self.build_requires[pattern] = list(existing.values()) self.conf.update_conf_definition(other.conf) - self.buildenv.compose(other.buildenv) + self.buildenv.compose_env(other.buildenv) def update_settings(self, new_settings): """Mix the specified settings with the current profile. diff --git a/conans/test/unittests/client/profile_loader/profile_loader_test.py b/conans/test/unittests/client/profile_loader/profile_loader_test.py index 96cd93d5a71..039f4bd8dfa 100644 --- a/conans/test/unittests/client/profile_loader/profile_loader_test.py +++ b/conans/test/unittests/client/profile_loader/profile_loader_test.py @@ -7,6 +7,7 @@ from conans.model.env_info import EnvValues from conans.model.profile import Profile from conans.model.ref import ConanFileReference +from conans.test.utils.mocks import ConanFileMock from conans.test.utils.profiles import create_profile as _create_profile from conans.test.utils.test_files import temp_folder from conans.util.files import load, save @@ -448,3 +449,20 @@ def test_profile_load_relative_path_pardir(): default_profile_folder) assert ({"BORSCHT": "RUSSIAN SOUP"}, {}) == profile.env_values.env_dicts("") assert current_profile_folder.replace("\\", "/") == variables["PROFILE_DIR"] + + +def test_profile_buildenv(): + tmp = temp_folder() + txt = textwrap.dedent(""" + [buildenv] + MyVar1=My Value; 11 + MyVar1+=MyValue12 + MyPath1=(path)/some/path11 + MyPath1+=(path)/other path/path12 + """) + profile, _ = get_profile(tmp, txt) + buildenv = profile.buildenv + env = buildenv.get_env(ConanFileMock(), None) + assert env.get("MyVar1") == "My Value; 11 MyValue12" + + assert env.get("MyPath1") == "/some/path11{}/other path/path12".format(os.pathsep) diff --git a/conans/test/unittests/model/profile_test.py b/conans/test/unittests/model/profile_test.py index ca89370f8c9..59235a6a868 100644 --- a/conans/test/unittests/model/profile_test.py +++ b/conans/test/unittests/model/profile_test.py @@ -119,17 +119,17 @@ def test_update_build_requires(): profile2 = Profile() profile2.build_requires["*"] = ["zlib/1.2.8"] - profile.compose(profile2) + profile.compose_profile(profile2) assert profile.build_requires["*"] == ["zlib/1.2.8"] profile3 = Profile() profile3.build_requires["*"] = ["zlib/1.2.11"] - profile.compose(profile3) + profile.compose_profile(profile3) assert profile.build_requires["*"] == ["zlib/1.2.11"] profile4 = Profile() profile4.build_requires["*"] = ["cmake/2.7"] - profile.compose(profile4) + profile.compose_profile(profile4) assert profile.build_requires["*"] == ["zlib/1.2.11", "cmake/2.7"] diff --git a/conans/test/unittests/tools/env/test_env.py b/conans/test/unittests/tools/env/test_env.py index 70b53f6e619..78ff561a384 100644 --- a/conans/test/unittests/tools/env/test_env.py +++ b/conans/test/unittests/tools/env/test_env.py @@ -29,7 +29,7 @@ def test_compose(): env2.unset("MyVar4") env2.define("MyVar5", "MyNewValue5") - env.compose(env2) + env.compose_env(env2) assert env.get("MyVar") == "MyValue" assert env.get("MyVar2") == 'MyValue2' assert env.get("MyVar3") == 'MyValue3' @@ -85,7 +85,7 @@ def test_compose_combinations(op1, v1, s1, op2, v2, s2, result): getattr(env2, op2)("MyVar", v2, s2) else: env2.unset("MyVar") - env.compose(env2) + env.compose_env(env2) with environment_append({"MyVar": "MyVar"}): assert env.get("MyVar") == result assert env._values["MyVar"].get_str(ConanFileMock(), "{name}") == result @@ -120,7 +120,7 @@ def test_compose_path_combinations(op1, v1, op2, v2, result): getattr(env2, op2+"_path")("MyVar", v2) else: env2.unset("MyVar") - env.compose(env2) + env.compose_env(env2) assert env._values["MyVar"].get_str(ConanFileMock(), "{name}", pathsep=":") == result @@ -321,7 +321,7 @@ def test_dict_access(): env2 = Environment(ConanFileMock()) env2.define("MyVar", "MyValue2") - env.compose(env2) + env.compose_env(env2) ret = env.items() assert dict(ret) == {"MyVar": "MyValue@MyValue2"} From fa25065f5c628851ebd5852c79c33be068a101ce Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 26 Jul 2021 10:08:48 +0200 Subject: [PATCH 4/7] fix tests --- conans/model/profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/model/profile.py b/conans/model/profile.py index 56e8360332c..936ecfe27a8 100644 --- a/conans/model/profile.py +++ b/conans/model/profile.py @@ -120,7 +120,7 @@ def compose_profile(self, other): self.build_requires[pattern] = list(existing.values()) self.conf.update_conf_definition(other.conf) - self.buildenv.compose_env(other.buildenv) + self.buildenv.compose_profile_env(other.buildenv) def update_settings(self, new_settings): """Mix the specified settings with the current profile. From 21de4e20627297302735f3eeddb68a3b61686d8a Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 26 Jul 2021 10:22:12 +0200 Subject: [PATCH 5/7] fix tests --- conan/tools/env/environment.py | 5 +++++ conans/client/profile_loader.py | 2 +- .../client/generators/generator_filter_error_test.py | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 309e6471c9f..e94a0acbdbe 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -345,6 +345,11 @@ def __init__(self): def __repr__(self): return repr(self._environments) + def __bool__(self): + return bool(self._environments) + + __nonzero__ = __bool__ + def get_env(self, conanfile, ref): """ computes package-specific Environment it is only called when conanfile.buildenv is called diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index 38a0f2e5a96..1f08d3008a3 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -239,7 +239,7 @@ def get_package_name_value(item): if doc.buildenv: buildenv = ProfileEnvironment.loads(doc.buildenv) - base_profile.buildenv.compose_env(buildenv) + base_profile.buildenv.compose_profile_env(buildenv) def profile_from_args(profiles, settings, options, env, conf, cwd, cache): diff --git a/conans/test/unittests/client/generators/generator_filter_error_test.py b/conans/test/unittests/client/generators/generator_filter_error_test.py index 6d3ef96a36f..718e17db29e 100644 --- a/conans/test/unittests/client/generators/generator_filter_error_test.py +++ b/conans/test/unittests/client/generators/generator_filter_error_test.py @@ -2,7 +2,6 @@ import unittest from conans.test.utils.tools import NO_SETTINGS_PACKAGE_ID, TestClient -from conans.util.files import load class MultiGeneratorFilterErrorTest(unittest.TestCase): From ceb73eee5a9d3fa3fe58615a3963ce537fc0becd Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 26 Jul 2021 11:33:03 +0200 Subject: [PATCH 6/7] minor fixes and comments --- conan/tools/env/environment.py | 14 +++++++------- conans/client/profile_loader.py | 2 +- conans/model/profile.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index e94a0acbdbe..9a07bc7156a 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -288,7 +288,7 @@ def save_script(self, name, auto_activate=True): def compose_env(self, other): """ self has precedence, the "other" will add/append if possible and not conflicting, but - self mandates what to do + self mandates what to do. If self has define(), without placeholder, that will remain :type other: Environment """ for k, v in other._values.items(): @@ -298,6 +298,7 @@ def compose_env(self, other): else: existing.compose_env_value(v) + self._conanfile = self._conanfile or other._conanfile return self # Methods to user access to the environment object as a dict @@ -358,16 +359,14 @@ def get_env(self, conanfile, ref): result = Environment(conanfile) for pattern, env in self._environments.items(): if pattern is None or fnmatch.fnmatch(str(ref), pattern): + # Latest declared has priority, copy() necessary to not destroy data result = env.copy().compose_env(result) - - # FIXME: Needed to assign _conanfile here too because in the env.compose returns env and it - # hasn't conanfile - result._conanfile = conanfile return result - def compose_profile_env(self, other): + def update_profile_env(self, other): """ :type other: ProfileEnvironment + :param other: The argument profile has priority/precedence over the current one. """ for pattern, environment in other._environments.items(): existing = self._environments.get(pattern) @@ -407,6 +406,7 @@ def loads(text): else: pattern, name = None, pattern_name[0] + # When loading from profile file, latest line has priority env = Environment(conanfile=None) if method == "unset": env.unset(name) @@ -423,7 +423,7 @@ def loads(text): result._environments[pattern] = env.compose_env(existing) break else: - raise ConanException("Bad env defintion: {}".format(line)) + raise ConanException("Bad env definition: {}".format(line)) return result diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index 1f08d3008a3..5683766361b 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -239,7 +239,7 @@ def get_package_name_value(item): if doc.buildenv: buildenv = ProfileEnvironment.loads(doc.buildenv) - base_profile.buildenv.compose_profile_env(buildenv) + base_profile.buildenv.update_profile_env(buildenv) def profile_from_args(profiles, settings, options, env, conf, cwd, cache): diff --git a/conans/model/profile.py b/conans/model/profile.py index 936ecfe27a8..f5112a41ee5 100644 --- a/conans/model/profile.py +++ b/conans/model/profile.py @@ -120,7 +120,7 @@ def compose_profile(self, other): self.build_requires[pattern] = list(existing.values()) self.conf.update_conf_definition(other.conf) - self.buildenv.compose_profile_env(other.buildenv) + 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. From 151000b8659ef23d6a6e29c3f9bd7b270229fa19 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 30 Jul 2021 16:45:22 +0200 Subject: [PATCH 7/7] Update conans/test/unittests/tools/env/test_env.py Co-authored-by: Luis Martinez de Bartolome Izquierdo --- conans/test/unittests/tools/env/test_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/test/unittests/tools/env/test_env.py b/conans/test/unittests/tools/env/test_env.py index 78ff561a384..8758892c10b 100644 --- a/conans/test/unittests/tools/env/test_env.py +++ b/conans/test/unittests/tools/env/test_env.py @@ -460,7 +460,7 @@ def test_combined2(self): env = ProfileEnvironment.loads(myprofile) text = env.dumps() - # NOTE: This is reversed order compared to origin, prepend alwyas first + # NOTE: This is reversed order compared to origin, prepend always first assert text == textwrap.dedent("""\ MyVar1=+MyValue12 MyVar1+=MyValue11