diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index aac022c1f5b..ca5d98685fe 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -3,7 +3,7 @@ from collections import OrderedDict from contextlib import contextmanager -from conans.client.generators import relativize_generated_file +from conans.client.generators import relativize_paths from conans.client.subsystems import deduce_subsystem, WINDOWS, subsystem_path from conan.errors import ConanException from conans.model.recipe_ref import ref_matches @@ -135,12 +135,14 @@ def compose_env_value(self, other): new_value[index:index + 1] = other._values # replace the placeholder self._values = new_value - def get_str(self, placeholder, subsystem, pathsep): + def get_str(self, placeholder, subsystem, pathsep, root_path=None, script_path=None): """ :param subsystem: :param placeholder: a OS dependant string pattern of the previous env-var value like $PATH, %PATH%, et :param pathsep: The path separator, typically ; or : + :param root_path: To do a relativize of paths, the base root path to be replaced + :param script_path: the replacement instead of the script path :return: a string representation of the env-var value, including the $NAME-like placeholder """ values = [] @@ -151,6 +153,13 @@ def get_str(self, placeholder, subsystem, pathsep): else: if self._path: v = subsystem_path(subsystem, v) + if root_path is not None: + if v.startswith(root_path): # relativize + v = v.replace(root_path, script_path, 1) + elif os.sep == "\\": # Just in case user specified C:/path/to/somewhere + r = root_path.replace("\\", "/") + if v.startswith(r): + v = v.replace(r, script_path.replace("\\", "/")) values.append(v) if self._path: return pathsep.join(values) @@ -329,7 +338,7 @@ class EnvVars: """ def __init__(self, conanfile, values, scope): - self._values = values # {var_name: _EnvValue}, just a reference to the Environment + self._values = values # {var_name: _EnvValue}, just a reference to the Environment self._conanfile = conanfile self._scope = scope self._subsystem = deduce_subsystem(conanfile, scope) @@ -418,12 +427,13 @@ def save_bat(self, file_location, generate_deactivate=True): {deactivate} """).format(deactivate=deactivate if generate_deactivate else "") result = [capture] + abs_base_path, new_path = relativize_paths(self._conanfile, "%~dp0") for varname, varvalues in self._values.items(): - value = varvalues.get_str("%{name}%", subsystem=self._subsystem, pathsep=self._pathsep) + value = varvalues.get_str("%{name}%", subsystem=self._subsystem, pathsep=self._pathsep, + root_path=abs_base_path, script_path=new_path) result.append('set "{}={}"'.format(varname, value)) content = "\n".join(result) - content = relativize_generated_file(content, self._conanfile, "%~dp0") # It is very important to save it correctly with utf-8, the Conan util save() is broken os.makedirs(os.path.dirname(os.path.abspath(file_location)), exist_ok=True) open(file_location, "w", encoding="utf-8").write(content) @@ -459,8 +469,10 @@ def save_ps1(self, file_location, generate_deactivate=True,): {deactivate} """).format(deactivate=deactivate if generate_deactivate else "") result = [capture] + abs_base_path, new_path = relativize_paths(self._conanfile, "$PSScriptRoot") for varname, varvalues in self._values.items(): - value = varvalues.get_str("$env:{name}", subsystem=self._subsystem, pathsep=self._pathsep) + value = varvalues.get_str("$env:{name}", subsystem=self._subsystem, pathsep=self._pathsep, + root_path=abs_base_path, script_path=new_path) if value: value = value.replace('"', '`"') # escape quotes result.append('$env:{}="{}"'.format(varname, value)) @@ -468,7 +480,6 @@ def save_ps1(self, file_location, generate_deactivate=True,): result.append('if (Test-Path env:{0}) {{ Remove-Item env:{0} }}'.format(varname)) content = "\n".join(result) - content = relativize_generated_file(content, self._conanfile, "$PSScriptRoot") # It is very important to save it correctly with utf-16, the Conan util save() is broken # and powershell uses utf-16 files!!! os.makedirs(os.path.dirname(os.path.abspath(file_location)), exist_ok=True) @@ -476,7 +487,7 @@ def save_ps1(self, file_location, generate_deactivate=True,): def save_sh(self, file_location, generate_deactivate=True): filepath, filename = os.path.split(file_location) - deactivate_file = os.path.join(filepath, "deactivate_{}".format(filename)) + deactivate_file = os.path.join("$script_folder", "deactivate_{}".format(filename)) deactivate = textwrap.dedent("""\ echo "echo Restoring environment" > "{deactivate_file}" for v in {vars} @@ -495,8 +506,10 @@ def save_sh(self, file_location, generate_deactivate=True): {deactivate} """).format(deactivate=deactivate if generate_deactivate else "") result = [capture] + abs_base_path, new_path = relativize_paths(self._conanfile, "$script_folder") for varname, varvalues in self._values.items(): - value = varvalues.get_str("${name}", self._subsystem, pathsep=self._pathsep) + value = varvalues.get_str("${name}", self._subsystem, pathsep=self._pathsep, + root_path=abs_base_path, script_path=new_path) value = value.replace('"', '\\"') if value: result.append('export {}="{}"'.format(varname, value)) @@ -504,7 +517,6 @@ def save_sh(self, file_location, generate_deactivate=True): result.append('unset {}'.format(varname)) content = "\n".join(result) - content = relativize_generated_file(content, self._conanfile, "$script_folder") content = f'script_folder="{os.path.abspath(filepath)}"\n' + content save(file_location, content) diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index c3f55c07b3d..e7292fe8257 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -198,14 +198,21 @@ def ps1_content(files): def relativize_generated_file(content, conanfile, placeholder): + abs_base_path, new_path = relativize_paths(conanfile, placeholder) + if abs_base_path is None: + return content + content = content.replace(abs_base_path, new_path) + content = content.replace(abs_base_path.replace("\\", "/"), new_path.replace("\\", "/")) + return content + + +def relativize_paths(conanfile, placeholder): abs_base_path = conanfile.folders._base_generators if not abs_base_path or not os.path.isabs(abs_base_path): - return content + return None, None abs_base_path = os.path.join(abs_base_path, "") # For the trailing / to dissambiguate matches generators_folder = conanfile.generators_folder rel_path = os.path.relpath(abs_base_path, generators_folder) new_path = placeholder if rel_path == "." else os.path.join(placeholder, rel_path) new_path = os.path.join(new_path, "") # For the trailing / to dissambiguate matches - content = content.replace(abs_base_path, new_path) - content = content.replace(abs_base_path.replace("\\", "/"), new_path.replace("\\", "/")) - return content + return abs_base_path, new_path diff --git a/conans/test/integration/environment/test_env.py b/conans/test/integration/environment/test_env.py index 0408c3329a9..49e5de986b6 100644 --- a/conans/test/integration/environment/test_env.py +++ b/conans/test/integration/environment/test_env.py @@ -841,3 +841,14 @@ def test(self): c.run("create tool --build-require -s:b build_type=Release -s:h build_type=Debug") assert "tool/0.1 (test package): Building TEST_PACKAGE IN Debug!!" in c.out assert "MYLIBVAR=MYLIBVALUE:Release" in c.out + + +def test_deactivate_relocatable_substitute(): + c = TestClient() + # this cannot be tested in CI, because permissions over root folder + # c.current_folder = "/build" + c.save({"conanfile.py": GenConanfile("pkg", "0.1")}) + c.run("install . -s os=Linux -s:b os=Linux") + conanbuild = c.load("conanbuildenv.sh") + result = os.path.join("$script_folder", "deactivate_conanbuildenv.sh") + assert f'"{result}"' in conanbuild