From 5f19714982c6b152cae5b75fdd2daba5631dd941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 23 Jan 2024 13:03:37 +0100 Subject: [PATCH 01/21] Add Fish support for environments --- conan/tools/env/environment.py | 51 +++++++++++++++++++++++++++++++++- conans/model/conf.py | 1 + 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index aac022c1f5b..6a5b2b6f51b 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -508,12 +508,52 @@ def save_sh(self, file_location, generate_deactivate=True): content = f'script_folder="{os.path.abspath(filepath)}"\n' + content save(file_location, content) + def save_fish(self, file_location, generate_deactivate=True): + filepath, filename = os.path.split(file_location) + deactivate_file = os.path.join(filepath, "deactivate_{}".format(filename)) + values = self._values.keys() + if len(values) == 0: + # Empty environment, nothing to restore (Easier to handle in Fish) + deactivate = ("""echo "echo Nothing to restore" > {deactivate_file}""" + .format(deactivate_file=deactivate_file)) + else: + deactivate = textwrap.dedent("""\ + echo "echo Restoring environment" > "{deactivate_file}" + for v in {vars} + set is_defined "true" + set value (printenv $v); or set is_defined "" + if test -n "$value" -o -n "$is_defined" + echo set -gx "$v" "$value" >> "{deactivate_file}" + else + echo set -ge "$v" >> "{deactivate_file}" + end + end + """.format(deactivate_file=deactivate_file, vars=" ".join(self._values.keys()))) + capture = textwrap.dedent("""\ + {deactivate} + """).format(deactivate=deactivate if generate_deactivate else "") + result = [capture] + for varname, varvalues in self._values.items(): + value = varvalues.get_str("${name}", self._subsystem, pathsep=self._pathsep) + value = value.replace('"', '\\"') + if value: + result.append('set -gx {} "{}"'.format(varname, value)) + else: + result.append('set -e {}'.format(varname)) + + content = "\n".join(result) + content = relativize_generated_file(content, self._conanfile, "$script_folder") + content = f'set script_folder "{os.path.abspath(filepath)}"\n' + content + save(file_location, content) + def save_script(self, filename): """ Saves a script file (bat, sh, ps1) with a launcher to set the environment. If the conf "tools.env.virtualenv:powershell" is set to True it will generate powershell launchers if Windows. + If the conf "tools.env.virtualenv:fish" is set to True it will generate fish launchers. + :param filename: Name of the file to generate. If the extension is provided, it will generate the launcher script for that extension, otherwise the format will be deduced checking if we are running inside Windows (checking also the subsystem) or not. @@ -522,18 +562,27 @@ def save_script(self, filename): if ext: is_bat = ext == ".bat" is_ps1 = ext == ".ps1" + is_fish = ext == ".fish" else: # Need to deduce it automatically is_bat = self._subsystem == WINDOWS is_ps1 = self._conanfile.conf.get("tools.env.virtualenv:powershell", check_type=bool) - if is_ps1: + is_fish = self._conanfile.conf.get("tools.env.virtualenv:fish", check_type=bool) + if is_fish: + filename = filename + ".fish" + is_bat = False + is_ps1 = False + elif is_ps1: filename = filename + ".ps1" is_bat = False + is_fish = False else: filename = filename + (".bat" if is_bat else ".sh") path = os.path.join(self._conanfile.generators_folder, filename) if is_bat: self.save_bat(path) + elif is_fish: + self.save_fish(path) elif is_ps1: self.save_ps1(path) else: diff --git a/conans/model/conf.py b/conans/model/conf.py index 30e1b169efd..fdf70581f65 100644 --- a/conans/model/conf.py +++ b/conans/model/conf.py @@ -104,6 +104,7 @@ "tools.apple:enable_arc": "(boolean) Enable/Disable ARC Apple Clang flags", "tools.apple:enable_visibility": "(boolean) Enable/Disable Visibility Apple Clang flags", "tools.env.virtualenv:powershell": "If it is set to True it will generate powershell launchers if os=Windows", + "tools.env.virtualenv:fish": "If it is set to True it will generate fish launchers", # Compilers/Flags configurations "tools.build:compiler_executables": "Defines a Python dict-like with the compilers path to be used. Allowed keys {'c', 'cpp', 'cuda', 'objc', 'objcxx', 'rc', 'fortran', 'asm', 'hip', 'ispc'}", "tools.build:cxxflags": "List of extra CXX flags used by different toolchains like CMakeToolchain, AutotoolsToolchain and MesonToolchain", From bf5d21ecf8cb0abb5dde93421dfb4348570a0963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 23 Jan 2024 13:05:31 +0100 Subject: [PATCH 02/21] Erase global scope flag --- conan/tools/env/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 6a5b2b6f51b..b8d4cb4da7f 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -539,7 +539,7 @@ def save_fish(self, file_location, generate_deactivate=True): if value: result.append('set -gx {} "{}"'.format(varname, value)) else: - result.append('set -e {}'.format(varname)) + result.append('set -ge {}'.format(varname)) content = "\n".join(result) content = relativize_generated_file(content, self._conanfile, "$script_folder") From b249c6dbbb2b747c87d5614bcb64ac0a54bee617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Thu, 1 Feb 2024 12:26:25 +0100 Subject: [PATCH 03/21] Add fish functional test --- conans/test/conftest.py | 5 +++ .../toolchains/env/test_virtualenv_fish.py | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 conans/test/functional/toolchains/env/test_virtualenv_fish.py diff --git a/conans/test/conftest.py b/conans/test/conftest.py index c0161ca4175..d047a67404b 100644 --- a/conans/test/conftest.py +++ b/conans/test/conftest.py @@ -180,6 +180,11 @@ # "exe": "dpcpp", # "2021.3": {"path": {"Linux": "/opt/intel/oneapi/compiler/2021.3.0/linux/bin"}} # } + # TODO: Fish is not yet installed in CI. Uncomment this line whenever it's done + # 'fish': { + # "default": "3.6", + # "3.6": {"path": {"Darwin": f"{homebrew_root}/bin"}} + # } } diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py new file mode 100644 index 00000000000..418b39fcab3 --- /dev/null +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -0,0 +1,43 @@ +import os + +import pytest + +from conans.test.assets.genconanfile import GenConanfile +from conans.test.utils.test_files import temp_folder +from conans.test.utils.tools import TestClient +from conans.util.files import save + + +@pytest.mark.tool("fish") +def test_virtualenv_fish(): + cache_folder = os.path.join(temp_folder(), "[sub] folder") + client = TestClient(cache_folder) + conanfile = str(GenConanfile("pkg", "0.1")) + conanfile += """ + + def package_info(self): + self.buildenv_info.define_path("MYPATH1", "/path/to/ar") + """ + client.save({"conanfile.py": conanfile}) + client.run("create .") + save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") + client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) + client.run("install . -s:a os=Linux") + + assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.sh")) + assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.bat")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.sh")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.bat")) + + assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) + assert os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) + + with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: + buildenv = f.read() + assert 'set -gx MYPATH1 "/path/to/ar"' in buildenv + + client.run_command("fish -c 'source conanbuildenv.fish && set'") + assert 'MYPATH1 /path/to/ar' in client.out + + client.run_command("fish -c 'source conanbuildenv.fish && set && source deactivate_conanbuildenv.fish && set'") + assert str(client.out).count('MYPATH1 /path/to/ar') == 1 From d42b388e93a6aec7ecfffa87fe4b77f60bb2550f Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 1 Apr 2024 11:30:25 +0200 Subject: [PATCH 04/21] Add missing import Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 8ec59e7963f..09797d5a584 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_paths +from conans.client.generators import relativize_paths, relativize_generated_file from conans.client.subsystems import deduce_subsystem, WINDOWS, subsystem_path from conan.errors import ConanException from conans.model.recipe_ref import ref_matches From e703b076bd30f994d0704c7baba51e92efa10432 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 9 Apr 2024 16:22:12 +0200 Subject: [PATCH 05/21] test: consume tool requires using fish Signed-off-by: Uilian Ries --- .../toolchains/env/test_virtualenv_fish.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 418b39fcab3..960e45d543e 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -1,6 +1,8 @@ import os +import platform import pytest +import textwrap from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.test_files import temp_folder @@ -41,3 +43,45 @@ def package_info(self): client.run_command("fish -c 'source conanbuildenv.fish && set && source deactivate_conanbuildenv.fish && set'") assert str(client.out).count('MYPATH1 /path/to/ar') == 1 + + +@pytest.mark.tool("fish") +@pytest.mark.parametrize("fish_value", [True, False]) +def test_transitive_tool_requires(fish_value): + """Generate a tool require package, which provides and binary and a custom environment variable. + Using fish, the binary should be available in the path, and the environment variable too. + """ + client = TestClient() + save(client.cache.new_config_path, f"tools.env.virtualenv:fish={fish_value}\n") + + # Generate the tool package with pkg-echo-tool binary that prints the value of LADY env var + cmd_line = "echo ${LADY}" if platform.system() != "Windows" else "echo %LADY%" + conanfile = str(GenConanfile("tool", "0.1.0") + .with_package_file("bin/pkg-echo-tool", cmd_line)) + package_info = """ + os.chmod(os.path.join(self.package_folder, "bin", "pkg-echo-tool"), 0o777) + + def package_info(self): + self.buildenv_info.define("LADY", "Dulcinea-del-Toboso") + """ + conanfile += package_info + client.save({"tool/conanfile.py": conanfile}) + client.run("create tool") + + assert "tool/0.1.0: package(): Packaged 1 file: pkg-echo-tool" in client.out + + # Generate the app package that uses the tool package. It should be able to run the binary and + # access the environment variable as well. + conanfile = str(GenConanfile("app", "0.1.0") + .with_tool_requires("tool/0.1.0") + .with_generator("VirtualBuildEnv")) + build = """ + def build(self): + self.run("pkg-echo-tool", env="conanbuild") + """ + conanfile += build + + client.save({"app/conanfile.py": conanfile}) + client.run("create app") + + assert "Dulcinea-del-Toboso" in client.out From 88a48b492c6c90a83293db0962dcd3d7ca3c4644 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Thu, 11 Apr 2024 18:24:26 +0200 Subject: [PATCH 06/21] Load fish environment Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 101 +++++++++++------- conans/client/generators/__init__.py | 12 +++ .../toolchains/env/test_virtualenv_fish.py | 11 +- 3 files changed, 81 insertions(+), 43 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 09797d5a584..d971ef12b37 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -1,9 +1,12 @@ import os import textwrap +from jinja2 import Template from collections import OrderedDict from contextlib import contextmanager +from pathlib import Path -from conans.client.generators import relativize_paths, 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 @@ -19,9 +22,9 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, if not env_filenames: return cmd filenames = [env_filenames] if not isinstance(env_filenames, list) else env_filenames - bats, shs, ps1s = [], [], [] + bats, shs, ps1s, fishs = [], [], [], [] - accept = accepted_extensions or ("ps1", "bat", "sh") + accept = accepted_extensions or ("ps1", "bat", "sh", "fish") # TODO: This implemantation is dirty, improve it for f in filenames: f = f if os.path.isabs(f) else os.path.join(env_folder, f) @@ -29,6 +32,10 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, if os.path.isfile(f) and "sh" in accept: f = subsystem_path(subsystem, f) shs.append(f) + elif f.lower().endswith(".fish"): + if os.path.isfile(f) and "fish" in accept: + f = subsystem_path(subsystem, f) + fishs.append(f) elif f.lower().endswith(".bat"): if os.path.isfile(f) and "bat" in accept: bats.append(f) @@ -39,6 +46,7 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, path_bat = "{}.bat".format(f) path_sh = "{}.sh".format(f) path_ps1 = "{}.ps1".format(f) + path_fish = "{}.fish".format(f) if os.path.isfile(path_bat) and "bat" in accept: bats.append(path_bat) if os.path.isfile(path_ps1) and "ps1" in accept: @@ -46,10 +54,13 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, if os.path.isfile(path_sh) and "sh" in accept: path_sh = subsystem_path(subsystem, path_sh) shs.append(path_sh) + if os.path.isfile(path_fish) and "fish" in accept: + path_fish = subsystem_path(subsystem, path_fish) + fishs.append(path_fish) - if bool(bats + ps1s) + bool(shs) > 1: + if bool(bats + ps1s) + bool(shs) > 1 + bool(fishs) > 1: raise ConanException("Cannot wrap command with different envs," - "{} - {}".format(bats+ps1s, shs)) + "{} - {} - {}".format(bats+ps1s, shs, fishs)) if bats: launchers = " && ".join('"{}"'.format(b) for b in bats) @@ -62,6 +73,10 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, elif shs: launchers = " && ".join('. "{}"'.format(f) for f in shs) return '{} && {}'.format(launchers, cmd) + elif fishs: + launchers = " && ".join('. "{}"'.format(f) for f in fishs) + print(f"LAUNCHERS: {launchers}") + return 'fish -c "{} && {}"'.format(launchers, cmd) elif ps1s: # TODO: at the moment it only works with path without spaces launchers = " ; ".join('"&\'{}\'"'.format(f) for f in ps1s) @@ -520,43 +535,55 @@ def save_sh(self, file_location, generate_deactivate=True): content = f'script_folder="{os.path.abspath(filepath)}"\n' + content save(file_location, content) - def save_fish(self, file_location, generate_deactivate=True): + def save_fish(self, file_location): + """Save a fish script file with the environment variables defined in the Environment object. + + It generates a function to deactivate the environment variables configured in the Environment object. + + :param file_location: The path to the file to save the fish script. + """ filepath, filename = os.path.split(file_location) - deactivate_file = os.path.join(filepath, "deactivate_{}".format(filename)) + function_name = f"deactivate_{Path(filename).stem}" + script_content = Template(textwrap.dedent("""\ + function {{ function_name }} + echo "echo Restoring environment" + {% for item in vars_unset %} + set -e {{ item }} + {% endfor %} + {% for item, value in vars_restore.items() %} + set -gx {{ item }} {{ value }} + {% endfor %} + end + {% for item, value in vars_prepend.items() %} + set -pgx {{ item }} '{{ value }}' + {% endfor %} + {% for item, value in vars_define.items() %} + set -gx {{ item }} '{{ value }}' + {% endfor %} + """)) values = self._values.keys() - if len(values) == 0: - # Empty environment, nothing to restore (Easier to handle in Fish) - deactivate = ("""echo "echo Nothing to restore" > {deactivate_file}""" - .format(deactivate_file=deactivate_file)) - else: - deactivate = textwrap.dedent("""\ - echo "echo Restoring environment" > "{deactivate_file}" - for v in {vars} - set is_defined "true" - set value (printenv $v); or set is_defined "" - if test -n "$value" -o -n "$is_defined" - echo set -gx "$v" "$value" >> "{deactivate_file}" - else - echo set -ge "$v" >> "{deactivate_file}" - end - end - """.format(deactivate_file=deactivate_file, vars=" ".join(self._values.keys()))) - capture = textwrap.dedent("""\ - {deactivate} - """).format(deactivate=deactivate if generate_deactivate else "") - result = [capture] + vars_unset = [] + vars_restore = {} + vars_prepend = {} + vars_define = {} for varname, varvalues in self._values.items(): - value = varvalues.get_str("${name}", self._subsystem, pathsep=self._pathsep) + current_value = os.getenv(varname) + abs_base_path, new_path = relativize_paths(self._conanfile, "$script_folder") + value = varvalues.get_str("", self._subsystem, pathsep=self._pathsep, + root_path=abs_base_path, script_path=new_path) value = value.replace('"', '\\"') - if value: - result.append('set -gx {} "{}"'.format(varname, value)) + if current_value: + vars_restore[varname] = current_value + vars_prepend[varname] = value else: - result.append('set -ge {}'.format(varname)) - - content = "\n".join(result) - content = relativize_generated_file(content, self._conanfile, "$script_folder") - content = f'set script_folder "{os.path.abspath(filepath)}"\n' + content - save(file_location, content) + vars_define[varname] = value + vars_unset.append(varname) + + if values: + content = script_content.render(function_name=function_name, vars_unset=vars_unset, + vars_restore=vars_restore, vars_prepend=vars_prepend, + vars_define=vars_define) + save(file_location, content) def save_script(self, filename): """ diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index 749e9ec2c71..e0a7ed79152 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -159,6 +159,7 @@ def deactivates(filenames): bats = [] shs = [] ps1s = [] + fishs = [] for env_script in env_scripts: path = os.path.join(conanfile.generators_folder, env_script) # Only the .bat and .ps1 are made relative to current script @@ -167,6 +168,8 @@ def deactivates(filenames): bats.append("%~dp0/"+path) elif env_script.endswith(".sh"): shs.append(subsystem_path(subsystem, path)) + elif env_script.endswith(".fish"): + fishs.append(subsystem_path(subsystem, path)) elif env_script.endswith(".ps1"): path = os.path.relpath(path, conanfile.generators_folder) # This $PSScriptRoot uses the current script directory @@ -179,6 +182,15 @@ def sh_content(files): save(os.path.join(conanfile.generators_folder, filename), sh_content(shs)) save(os.path.join(conanfile.generators_folder, "deactivate_{}".format(filename)), sh_content(deactivates(shs))) + if fishs: + def fish_content(files): + return ". " + " && . ".join('"{}"'.format(s) for s in files) + fishs = [it for it in fishs if os.path.isfile(it)] + if fishs: + filename = "conan{}.fish".format(group) + generated.append(filename) + save(os.path.join(conanfile.generators_folder, filename), fish_content(fishs)) + # TODO : Deactivate fish if bats: def bat_content(files): return "\r\n".join(["@echo off"] + ['call "{}"'.format(b) for b in files]) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 960e45d543e..47404a918b8 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -2,7 +2,6 @@ import platform import pytest -import textwrap from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.test_files import temp_folder @@ -32,7 +31,7 @@ def package_info(self): assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.bat")) assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) - assert os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: buildenv = f.read() @@ -41,17 +40,17 @@ def package_info(self): client.run_command("fish -c 'source conanbuildenv.fish && set'") assert 'MYPATH1 /path/to/ar' in client.out - client.run_command("fish -c 'source conanbuildenv.fish && set && source deactivate_conanbuildenv.fish && set'") + client.run_command("fish -c 'source conanbuildenv.fish && set && deactivate_conanbuildenv && set'") assert str(client.out).count('MYPATH1 /path/to/ar') == 1 @pytest.mark.tool("fish") -@pytest.mark.parametrize("fish_value", [True, False]) -def test_transitive_tool_requires(fish_value): +@pytest.mark.parametrize("fish_value, path_with_spaces", ([True, False], [False, True], [False, False], [True, True])) +def test_transitive_tool_requires(fish_value, path_with_spaces): """Generate a tool require package, which provides and binary and a custom environment variable. Using fish, the binary should be available in the path, and the environment variable too. """ - client = TestClient() + client = TestClient(path_with_spaces=path_with_spaces) save(client.cache.new_config_path, f"tools.env.virtualenv:fish={fish_value}\n") # Generate the tool package with pkg-echo-tool binary that prints the value of LADY env var From c60eb5a8243c07bd4539e55381820d24f2477ade Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 15 Apr 2024 16:20:28 +0200 Subject: [PATCH 07/21] Remove windows subsystems for Windows Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 53 +++++++++---------- conans/client/generators/__init__.py | 12 ----- .../toolchains/env/test_virtualenv_fish.py | 18 +++++-- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index d971ef12b37..e258624f8a1 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -1,5 +1,7 @@ import os import textwrap +import random +import string from jinja2 import Template from collections import OrderedDict from contextlib import contextmanager @@ -25,7 +27,7 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, bats, shs, ps1s, fishs = [], [], [], [] accept = accepted_extensions or ("ps1", "bat", "sh", "fish") - # TODO: This implemantation is dirty, improve it + # TODO: This implementation is dirty, improve it for f in filenames: f = f if os.path.isabs(f) else os.path.join(env_folder, f) if f.lower().endswith(".sh"): @@ -34,7 +36,6 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, shs.append(f) elif f.lower().endswith(".fish"): if os.path.isfile(f) and "fish" in accept: - f = subsystem_path(subsystem, f) fishs.append(f) elif f.lower().endswith(".bat"): if os.path.isfile(f) and "bat" in accept: @@ -55,7 +56,6 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, path_sh = subsystem_path(subsystem, path_sh) shs.append(path_sh) if os.path.isfile(path_fish) and "fish" in accept: - path_fish = subsystem_path(subsystem, path_fish) fishs.append(path_fish) if bool(bats + ps1s) + bool(shs) > 1 + bool(fishs) > 1: @@ -75,8 +75,7 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, return '{} && {}'.format(launchers, cmd) elif fishs: launchers = " && ".join('. "{}"'.format(f) for f in fishs) - print(f"LAUNCHERS: {launchers}") - return 'fish -c "{} && {}"'.format(launchers, cmd) + return 'fish -c "{}; and {}"'.format(launchers, cmd) elif ps1s: # TODO: at the moment it only works with path without spaces launchers = " ; ".join('"&\'{}\'"'.format(f) for f in ps1s) @@ -544,44 +543,44 @@ def save_fish(self, file_location): """ filepath, filename = os.path.split(file_location) function_name = f"deactivate_{Path(filename).stem}" - script_content = Template(textwrap.dedent("""\ + group = "".join(random.choices(string.ascii_letters + string.digits, k=8)) + # TODO: Use local variables to store previous values when running the activate script instead + # https://fishshell.com/docs/current/language.html#variable-scope + # set -l varname $varname + script_content = Template(textwrap.dedent(""" function {{ function_name }} echo "echo Restoring environment" - {% for item in vars_unset %} - set -e {{ item }} - {% endfor %} - {% for item, value in vars_restore.items() %} - set -gx {{ item }} {{ value }} + for var in $conan_{{ group }}_del + set -e $var + end + set -e $conan_{{ group }}_del + {% for item in vars_define.keys() %} + if set -q conan_{{ group }}_{{ item }} + set -gx {{ item }} "$conan_{{ group }}_{{ item }}" + end {% endfor %} end - {% for item, value in vars_prepend.items() %} - set -pgx {{ item }} '{{ value }}' - {% endfor %} {% for item, value in vars_define.items() %} - set -gx {{ item }} '{{ value }}' + if not set -q {{ item }} + set -ga conan_{{ group }}_del {{ item }} + set -gx {{ item }} "{{ value }}" + else + set -g conan_{{ group }}_{{ item }} "${{ item }}" + set -pgx {{ item }} "{{ value }}" + end {% endfor %} """)) values = self._values.keys() - vars_unset = [] - vars_restore = {} - vars_prepend = {} vars_define = {} for varname, varvalues in self._values.items(): - current_value = os.getenv(varname) abs_base_path, new_path = relativize_paths(self._conanfile, "$script_folder") value = varvalues.get_str("", self._subsystem, pathsep=self._pathsep, root_path=abs_base_path, script_path=new_path) value = value.replace('"', '\\"') - if current_value: - vars_restore[varname] = current_value - vars_prepend[varname] = value - else: - vars_define[varname] = value - vars_unset.append(varname) + vars_define[varname] = value if values: - content = script_content.render(function_name=function_name, vars_unset=vars_unset, - vars_restore=vars_restore, vars_prepend=vars_prepend, + content = script_content.render(function_name=function_name, group=group, vars_define=vars_define) save(file_location, content) diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index e0a7ed79152..749e9ec2c71 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -159,7 +159,6 @@ def deactivates(filenames): bats = [] shs = [] ps1s = [] - fishs = [] for env_script in env_scripts: path = os.path.join(conanfile.generators_folder, env_script) # Only the .bat and .ps1 are made relative to current script @@ -168,8 +167,6 @@ def deactivates(filenames): bats.append("%~dp0/"+path) elif env_script.endswith(".sh"): shs.append(subsystem_path(subsystem, path)) - elif env_script.endswith(".fish"): - fishs.append(subsystem_path(subsystem, path)) elif env_script.endswith(".ps1"): path = os.path.relpath(path, conanfile.generators_folder) # This $PSScriptRoot uses the current script directory @@ -182,15 +179,6 @@ def sh_content(files): save(os.path.join(conanfile.generators_folder, filename), sh_content(shs)) save(os.path.join(conanfile.generators_folder, "deactivate_{}".format(filename)), sh_content(deactivates(shs))) - if fishs: - def fish_content(files): - return ". " + " && . ".join('"{}"'.format(s) for s in files) - fishs = [it for it in fishs if os.path.isfile(it)] - if fishs: - filename = "conan{}.fish".format(group) - generated.append(filename) - save(os.path.join(conanfile.generators_folder, filename), fish_content(fishs)) - # TODO : Deactivate fish if bats: def bat_content(files): return "\r\n".join(["@echo off"] + ['call "{}"'.format(b) for b in files]) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 47404a918b8..9fc32c0d4d4 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -45,8 +45,11 @@ def package_info(self): @pytest.mark.tool("fish") -@pytest.mark.parametrize("fish_value, path_with_spaces", ([True, False], [False, True], [False, False], [True, True])) -def test_transitive_tool_requires(fish_value, path_with_spaces): +# TODO Pass variable with spaces +@pytest.mark.parametrize("fish_value", [True, False]) +@pytest.mark.parametrize("path_with_spaces", [True, False]) +@pytest.mark.parametrize("value", ["Dulcinea del Toboso", "Dulcinea-Del-Toboso"]) +def test_transitive_tool_requires(fish_value, path_with_spaces, value): """Generate a tool require package, which provides and binary and a custom environment variable. Using fish, the binary should be available in the path, and the environment variable too. """ @@ -57,11 +60,11 @@ def test_transitive_tool_requires(fish_value, path_with_spaces): cmd_line = "echo ${LADY}" if platform.system() != "Windows" else "echo %LADY%" conanfile = str(GenConanfile("tool", "0.1.0") .with_package_file("bin/pkg-echo-tool", cmd_line)) - package_info = """ + package_info = f""" os.chmod(os.path.join(self.package_folder, "bin", "pkg-echo-tool"), 0o777) def package_info(self): - self.buildenv_info.define("LADY", "Dulcinea-del-Toboso") + self.buildenv_info.define("LADY", "{value}") """ conanfile += package_info client.save({"tool/conanfile.py": conanfile}) @@ -83,4 +86,9 @@ def build(self): client.save({"app/conanfile.py": conanfile}) client.run("create app") - assert "Dulcinea-del-Toboso" in client.out + assert value in client.out + + # TODO: Check why is generating when running Conan create: + # - deactivate_conanbuildenv-release-armv8.fish + # - conanrunenv-release-armv8.fish + # No variables listed, only script_folder. Should not generate these files. From 1ab3a8d78efa3c0869e72fe49ee0cb37d5bc83e9 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 15 Apr 2024 18:09:24 +0200 Subject: [PATCH 08/21] Add more tests for fish env Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 8 ++- .../toolchains/env/test_virtualenv_fish.py | 55 ++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index e258624f8a1..dff8b1642cc 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -544,9 +544,6 @@ def save_fish(self, file_location): filepath, filename = os.path.split(file_location) function_name = f"deactivate_{Path(filename).stem}" group = "".join(random.choices(string.ascii_letters + string.digits, k=8)) - # TODO: Use local variables to store previous values when running the activate script instead - # https://fishshell.com/docs/current/language.html#variable-scope - # set -l varname $varname script_content = Template(textwrap.dedent(""" function {{ function_name }} echo "echo Restoring environment" @@ -561,6 +558,10 @@ def save_fish(self, file_location): {% endfor %} end {% for item, value in vars_define.items() %} + {% if item == "PATH" %} + set -g conan_{{ group }}_{{ item }} "${{ item }}" + fish_add_path -pg "{{ value }}" + {% else %} if not set -q {{ item }} set -ga conan_{{ group }}_del {{ item }} set -gx {{ item }} "{{ value }}" @@ -568,6 +569,7 @@ def save_fish(self, file_location): set -g conan_{{ group }}_{{ item }} "${{ item }}" set -pgx {{ item }} "{{ value }}" end + {% endif %} {% endfor %} """)) values = self._values.keys() diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 9fc32c0d4d4..9408212671f 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -10,7 +10,12 @@ @pytest.mark.tool("fish") -def test_virtualenv_fish(): +def test_define_new_vars(): + """Test when defining new path and new variable in buildenv_info. + + Variables should be available as environment variables in the buildenv script. + And, should be deleted when deactivate_conanbuildenv is called. + """ cache_folder = os.path.join(temp_folder(), "[sub] folder") client = TestClient(cache_folder) conanfile = str(GenConanfile("pkg", "0.1")) @@ -18,6 +23,8 @@ def test_virtualenv_fish(): def package_info(self): self.buildenv_info.define_path("MYPATH1", "/path/to/ar") + self.buildenv_info.define("MYVAR1", "myvalue") + self.buildenv_info.prepend_path("PATH", "/path/to/fake/folder") """ client.save({"conanfile.py": conanfile}) client.run("create .") @@ -36,12 +43,58 @@ def package_info(self): with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: buildenv = f.read() assert 'set -gx MYPATH1 "/path/to/ar"' in buildenv + assert 'set -gx MYVAR1 "myvalue"' in buildenv client.run_command("fish -c 'source conanbuildenv.fish && set'") assert 'MYPATH1 /path/to/ar' in client.out + assert 'MYVAR1 myvalue' in client.out client.run_command("fish -c 'source conanbuildenv.fish && set && deactivate_conanbuildenv && set'") assert str(client.out).count('MYPATH1 /path/to/ar') == 1 + assert str(client.out).count('MYVAR1 myvalue') == 1 + + +@pytest.mark.tool("fish") +def test_append_path(): + """Test when appending to an existing path in buildenv_info. + + Path should be available as environment variable in the buildenv script, including the new value + as first element. + Once deactivate_conanbuildenv is called, the path should be restored as before. + """ + fake_path = "/path/to/fake/folder" + cache_folder = os.path.join(temp_folder(), "[sub] folder") + client = TestClient(cache_folder) + conanfile = str(GenConanfile("pkg", "0.1")) + conanfile += f""" + + def package_info(self): + self.buildenv_info.prepend_path("PATH", "{fake_path}") + """ + client.save({"conanfile.py": conanfile}) + current_path = os.environ["PATH"] + client.run("create .") + save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") + client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) + client.run("install . -s:a os=Linux") + + assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.sh")) + assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.bat")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.sh")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.bat")) + + assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) + assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) + + with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: + buildenv = f.read() + assert f'set -pgx PATH "{fake_path}"' in buildenv + + client.run_command("fish -c 'source conanbuildenv.fish and set'") + assert f'PATH {fake_path}' in client.out + + client.run_command("deactivate_conanbuildenv && set'") + assert str(client.out).count(f'MYPATH1 {fake_path}') == 1 @pytest.mark.tool("fish") From 70c34ae906e43496951638a339e27c59f74215a2 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 15:19:01 +0200 Subject: [PATCH 09/21] Fix path remove element Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 27 ++++++++++++------- .../toolchains/env/test_virtualenv_fish.py | 26 +++++++++--------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index dff8b1642cc..b46d9635b5e 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -539,38 +539,47 @@ def save_fish(self, file_location): It generates a function to deactivate the environment variables configured in the Environment object. + # TODO: Honor append and prepend paths from recipe to the fish shell syntax. + :param file_location: The path to the file to save the fish script. """ filepath, filename = os.path.split(file_location) function_name = f"deactivate_{Path(filename).stem}" group = "".join(random.choices(string.ascii_letters + string.digits, k=8)) script_content = Template(textwrap.dedent(""" + function remove_path + set -l variable_name $argv[1] + set -l to_remove $argv[2] + if set -l index (contains -i $to_remove $$variable_name) + set -l list (string split " " $$variable_name) + set -e list[$index] + set -gx $variable_name $list + end + end function {{ function_name }} echo "echo Restoring environment" for var in $conan_{{ group }}_del - set -e $var + set -e var end - set -e $conan_{{ group }}_del + set -e conan_{{ group }}_del {% for item in vars_define.keys() %} if set -q conan_{{ group }}_{{ item }} - set -gx {{ item }} "$conan_{{ group }}_{{ item }}" + remove_path {{ item }} $conan_{{ group }}_{{ item }} + set -e conan_{{ group }}_{{ item }} end {% endfor %} end + {% for item, value in vars_define.items() %} - {% if item == "PATH" %} - set -g conan_{{ group }}_{{ item }} "${{ item }}" - fish_add_path -pg "{{ value }}" - {% else %} if not set -q {{ item }} set -ga conan_{{ group }}_del {{ item }} set -gx {{ item }} "{{ value }}" else - set -g conan_{{ group }}_{{ item }} "${{ item }}" + set -g conan_{{ group }}_{{ item }} "{{ value }}" set -pgx {{ item }} "{{ value }}" end - {% endif %} {% endfor %} + exit 0 """)) values = self._values.keys() vars_define = {} diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 9408212671f..971d151b125 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -1,4 +1,5 @@ import os +import glob import platform import pytest @@ -24,7 +25,6 @@ def test_define_new_vars(): def package_info(self): self.buildenv_info.define_path("MYPATH1", "/path/to/ar") self.buildenv_info.define("MYVAR1", "myvalue") - self.buildenv_info.prepend_path("PATH", "/path/to/fake/folder") """ client.save({"conanfile.py": conanfile}) client.run("create .") @@ -32,10 +32,9 @@ def package_info(self): client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) client.run("install . -s:a os=Linux") - assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.sh")) - assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.bat")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.sh")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.bat")) + for ext in ["*.sh", "*.bat"]: + not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) + assert not not_expected_files assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) @@ -72,29 +71,28 @@ def package_info(self): self.buildenv_info.prepend_path("PATH", "{fake_path}") """ client.save({"conanfile.py": conanfile}) - current_path = os.environ["PATH"] client.run("create .") save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) client.run("install . -s:a os=Linux") - assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.sh")) - assert not os.path.exists(os.path.join(client.current_folder, "conanbuildenv.bat")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.sh")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.bat")) + for ext in ["*.sh", "*.bat"]: + not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) + assert not not_expected_files assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) - with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: + script_path = os.path.join(client.current_folder, "conanbuildenv.fish") + with open(script_path, "r") as f: buildenv = f.read() assert f'set -pgx PATH "{fake_path}"' in buildenv - client.run_command("fish -c 'source conanbuildenv.fish and set'") + client.run_command(f'fish -c ". conanbuildenv.fish; set"') assert f'PATH {fake_path}' in client.out - client.run_command("deactivate_conanbuildenv && set'") - assert str(client.out).count(f'MYPATH1 {fake_path}') == 1 + client.run_command(f'fish -c ". conanbuildenv.fish; deactivate_conanbuildenv; set"') + assert f'PATH {fake_path}' not in client.out @pytest.mark.tool("fish") From 37eab05bcdd0b1e837464035d6c03472375097d5 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 16:19:16 +0200 Subject: [PATCH 10/21] generate script wrapper Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 2 +- conans/client/generators/__init__.py | 10 ++++++++++ .../functional/toolchains/env/test_virtualenv_fish.py | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index b46d9635b5e..f4cae0c6f2e 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -559,7 +559,7 @@ def save_fish(self, file_location): function {{ function_name }} echo "echo Restoring environment" for var in $conan_{{ group }}_del - set -e var + set -e $var end set -e conan_{{ group }}_del {% for item in vars_define.keys() %} diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index 749e9ec2c71..f7e73c92b9e 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -159,6 +159,7 @@ def deactivates(filenames): bats = [] shs = [] ps1s = [] + fishs = [] for env_script in env_scripts: path = os.path.join(conanfile.generators_folder, env_script) # Only the .bat and .ps1 are made relative to current script @@ -167,6 +168,9 @@ def deactivates(filenames): bats.append("%~dp0/"+path) elif env_script.endswith(".sh"): shs.append(subsystem_path(subsystem, path)) + elif env_script.endswith(".fish"): + path = os.path.relpath(path, conanfile.generators_folder) + fishs.append(path) elif env_script.endswith(".ps1"): path = os.path.relpath(path, conanfile.generators_folder) # This $PSScriptRoot uses the current script directory @@ -179,6 +183,12 @@ def sh_content(files): save(os.path.join(conanfile.generators_folder, filename), sh_content(shs)) save(os.path.join(conanfile.generators_folder, "deactivate_{}".format(filename)), sh_content(deactivates(shs))) + if fishs: + def fish_content(files): + return ". " + " && . ".join('"{}"'.format(s) for s in files) + filename = "conan{}.fish".format(group) + generated.append(filename) + save(os.path.join(conanfile.generators_folder, filename), fish_content(fishs)) if bats: def bat_content(files): return "\r\n".join(["@echo off"] + ['call "{}"'.format(b) for b in files]) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 971d151b125..322e226e467 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -48,13 +48,13 @@ def package_info(self): assert 'MYPATH1 /path/to/ar' in client.out assert 'MYVAR1 myvalue' in client.out - client.run_command("fish -c 'source conanbuildenv.fish && set && deactivate_conanbuildenv && set'") + client.run_command('fish -c ". conanbuildenv.fish && set && deactivate_conanbuildenv && set"') assert str(client.out).count('MYPATH1 /path/to/ar') == 1 assert str(client.out).count('MYVAR1 myvalue') == 1 @pytest.mark.tool("fish") -def test_append_path(): +def test_prepend_path(): """Test when appending to an existing path in buildenv_info. Path should be available as environment variable in the buildenv script, including the new value From e32b4206e8db9aabc76e4601627ff2d5113d6450 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 16:28:33 +0200 Subject: [PATCH 11/21] validate define with spaces Signed-off-by: Uilian Ries --- conans/test/functional/toolchains/env/test_virtualenv_fish.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 322e226e467..5a5dbdaa8d1 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -25,6 +25,7 @@ def test_define_new_vars(): def package_info(self): self.buildenv_info.define_path("MYPATH1", "/path/to/ar") self.buildenv_info.define("MYVAR1", "myvalue") + self.buildenv_info.define("MYVAR2", "var with spaces") """ client.save({"conanfile.py": conanfile}) client.run("create .") @@ -43,14 +44,17 @@ def package_info(self): buildenv = f.read() assert 'set -gx MYPATH1 "/path/to/ar"' in buildenv assert 'set -gx MYVAR1 "myvalue"' in buildenv + assert 'set -gx MYVAR2 "var with spaces"' in buildenv client.run_command("fish -c 'source conanbuildenv.fish && set'") assert 'MYPATH1 /path/to/ar' in client.out assert 'MYVAR1 myvalue' in client.out + assert "MYVAR2 'var with spaces'" in client.out client.run_command('fish -c ". conanbuildenv.fish && set && deactivate_conanbuildenv && set"') assert str(client.out).count('MYPATH1 /path/to/ar') == 1 assert str(client.out).count('MYVAR1 myvalue') == 1 + assert str(client.out).count("MYVAR2 'var with spaces'") == 1 @pytest.mark.tool("fish") From 8cdfd29b20f6db33494d81b669f7549f47e5a319 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 17:38:11 +0200 Subject: [PATCH 12/21] Add support path with space Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 2 +- conans/client/generators/__init__.py | 2 +- .../functional/toolchains/env/test_virtualenv_fish.py | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index f4cae0c6f2e..b5a86c0df75 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -74,7 +74,7 @@ def environment_wrap_command(env_filenames, env_folder, cmd, subsystem=None, launchers = " && ".join('. "{}"'.format(f) for f in shs) return '{} && {}'.format(launchers, cmd) elif fishs: - launchers = " && ".join('. "{}"'.format(f) for f in fishs) + launchers = " && ".join('. \\"{}\\"'.format(f) for f in fishs) return 'fish -c "{}; and {}"'.format(launchers, cmd) elif ps1s: # TODO: at the moment it only works with path without spaces diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index f7e73c92b9e..b7717bcf00e 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -169,7 +169,7 @@ def deactivates(filenames): elif env_script.endswith(".sh"): shs.append(subsystem_path(subsystem, path)) elif env_script.endswith(".fish"): - path = os.path.relpath(path, conanfile.generators_folder) + path = os.path.abspath(path) fishs.append(path) elif env_script.endswith(".ps1"): path = os.path.relpath(path, conanfile.generators_folder) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 5a5dbdaa8d1..4d430635592 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -16,6 +16,8 @@ def test_define_new_vars(): Variables should be available as environment variables in the buildenv script. And, should be deleted when deactivate_conanbuildenv is called. + + Avoided pytest.mark.parametrize to check how it works when having multiple definitions at same time. """ cache_folder = os.path.join(temp_folder(), "[sub] folder") client = TestClient(cache_folder) @@ -58,14 +60,14 @@ def package_info(self): @pytest.mark.tool("fish") -def test_prepend_path(): +@pytest.mark.parametrize("fake_path", ["/path/to/fake/folder", "/path/with space/to/folder"]) +def test_prepend_path(fake_path): """Test when appending to an existing path in buildenv_info. Path should be available as environment variable in the buildenv script, including the new value as first element. Once deactivate_conanbuildenv is called, the path should be restored as before. """ - fake_path = "/path/to/fake/folder" cache_folder = os.path.join(temp_folder(), "[sub] folder") client = TestClient(cache_folder) conanfile = str(GenConanfile("pkg", "0.1")) @@ -93,14 +95,13 @@ def package_info(self): assert f'set -pgx PATH "{fake_path}"' in buildenv client.run_command(f'fish -c ". conanbuildenv.fish; set"') - assert f'PATH {fake_path}' in client.out + assert f"PATH '{fake_path}'" in client.out client.run_command(f'fish -c ". conanbuildenv.fish; deactivate_conanbuildenv; set"') - assert f'PATH {fake_path}' not in client.out + assert f"PATH '{fake_path}'" not in client.out @pytest.mark.tool("fish") -# TODO Pass variable with spaces @pytest.mark.parametrize("fish_value", [True, False]) @pytest.mark.parametrize("path_with_spaces", [True, False]) @pytest.mark.parametrize("value", ["Dulcinea del Toboso", "Dulcinea-Del-Toboso"]) From 966c631468e84c156805831e5d93c79bcb18ce46 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 17:42:25 +0200 Subject: [PATCH 13/21] update todo Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index b5a86c0df75..8e73768e20e 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -539,7 +539,10 @@ def save_fish(self, file_location): It generates a function to deactivate the environment variables configured in the Environment object. - # TODO: Honor append and prepend paths from recipe to the fish shell syntax. + TODO: Honor append and prepend paths from recipe to the fish shell syntax. + buildenv_info.append_path should be fish set -a + buildenv_info.prepend_path should be fish set -p + buildenv_info.define_path should be fish set :param file_location: The path to the file to save the fish script. """ From dd517c60b1a9433601b55bb4a1a5f91b1800520c Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 18:01:25 +0200 Subject: [PATCH 14/21] only generates wrappers when needed Signed-off-by: Uilian Ries --- conans/client/generators/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/conans/client/generators/__init__.py b/conans/client/generators/__init__.py index b7717bcf00e..4dc0aa74715 100644 --- a/conans/client/generators/__init__.py +++ b/conans/client/generators/__init__.py @@ -186,9 +186,11 @@ def sh_content(files): if fishs: def fish_content(files): return ". " + " && . ".join('"{}"'.format(s) for s in files) - filename = "conan{}.fish".format(group) - generated.append(filename) - save(os.path.join(conanfile.generators_folder, filename), fish_content(fishs)) + fishs = [it for it in fishs if os.path.exists(it)] + if fishs: + filename = "conan{}.fish".format(group) + generated.append(filename) + save(os.path.join(conanfile.generators_folder, filename), fish_content(fishs)) if bats: def bat_content(files): return "\r\n".join(["@echo off"] + ['call "{}"'.format(b) for b in files]) From 325fe0612b94441d6b02f0d336931abe12fdc265 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 16 Apr 2024 18:32:22 +0200 Subject: [PATCH 15/21] simplify element remove Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 8e73768e20e..4a0a9c65197 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -554,9 +554,7 @@ def save_fish(self, file_location): set -l variable_name $argv[1] set -l to_remove $argv[2] if set -l index (contains -i $to_remove $$variable_name) - set -l list (string split " " $$variable_name) - set -e list[$index] - set -gx $variable_name $list + set -e {$variable_name}[$index] end end function {{ function_name }} From 2d202ed3fb18850a7b29110150d2f5bace8fcb22 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Thu, 18 Apr 2024 10:53:14 +0200 Subject: [PATCH 16/21] Skip fish test in Windows Signed-off-by: Uilian Ries --- .../toolchains/env/test_virtualenv_fish.py | 171 +++++++++++------- 1 file changed, 101 insertions(+), 70 deletions(-) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 4d430635592..93a29283db9 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -6,67 +6,25 @@ from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.test_files import temp_folder -from conans.test.utils.tools import TestClient +from conans.test.utils.tools import TestClient, load from conans.util.files import save +# INFO: Fish only supports Cygwin and WSL https://github.com/fish-shell/fish-shell?tab=readme-ov-file#windows +pytestmark = pytest.mark.skipif(platform.system() not in ("Darwin", "Linux"), reason="Fish is well supported only in Linux and MacOS") + @pytest.mark.tool("fish") -def test_define_new_vars(): +@pytest.mark.parametrize("fake_path", ["/path/to/fake/folder", "/path/with space/to/folder"]) +@pytest.mark.parametrize("my_var1", ["myvalue", "my value"]) +@pytest.mark.parametrize("my_var2", ["varwithspaces", "var with spaces"]) +@pytest.mark.parametrize("my_path", ["/path/to/ar", "/path/to space/ar"]) +def test_buildenv_define_new_vars(fake_path, my_var1, my_var2, my_path): """Test when defining new path and new variable in buildenv_info. Variables should be available as environment variables in the buildenv script. And, should be deleted when deactivate_conanbuildenv is called. - Avoided pytest.mark.parametrize to check how it works when having multiple definitions at same time. - """ - cache_folder = os.path.join(temp_folder(), "[sub] folder") - client = TestClient(cache_folder) - conanfile = str(GenConanfile("pkg", "0.1")) - conanfile += """ - - def package_info(self): - self.buildenv_info.define_path("MYPATH1", "/path/to/ar") - self.buildenv_info.define("MYVAR1", "myvalue") - self.buildenv_info.define("MYVAR2", "var with spaces") - """ - client.save({"conanfile.py": conanfile}) - client.run("create .") - save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") - client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) - client.run("install . -s:a os=Linux") - - for ext in ["*.sh", "*.bat"]: - not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) - assert not not_expected_files - - assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) - - with open(os.path.join(client.current_folder, "conanbuildenv.fish"), "r") as f: - buildenv = f.read() - assert 'set -gx MYPATH1 "/path/to/ar"' in buildenv - assert 'set -gx MYVAR1 "myvalue"' in buildenv - assert 'set -gx MYVAR2 "var with spaces"' in buildenv - - client.run_command("fish -c 'source conanbuildenv.fish && set'") - assert 'MYPATH1 /path/to/ar' in client.out - assert 'MYVAR1 myvalue' in client.out - assert "MYVAR2 'var with spaces'" in client.out - - client.run_command('fish -c ". conanbuildenv.fish && set && deactivate_conanbuildenv && set"') - assert str(client.out).count('MYPATH1 /path/to/ar') == 1 - assert str(client.out).count('MYVAR1 myvalue') == 1 - assert str(client.out).count("MYVAR2 'var with spaces'") == 1 - - -@pytest.mark.tool("fish") -@pytest.mark.parametrize("fake_path", ["/path/to/fake/folder", "/path/with space/to/folder"]) -def test_prepend_path(fake_path): - """Test when appending to an existing path in buildenv_info. - - Path should be available as environment variable in the buildenv script, including the new value - as first element. - Once deactivate_conanbuildenv is called, the path should be restored as before. + Variable with spaces should be wrapped by single quotes when exported. """ cache_folder = os.path.join(temp_folder(), "[sub] folder") client = TestClient(cache_folder) @@ -74,6 +32,9 @@ def test_prepend_path(fake_path): conanfile += f""" def package_info(self): + self.buildenv_info.define_path("MYPATH1", "{my_path}") + self.buildenv_info.define("MYVAR1", "{my_var1}") + self.buildenv_info.define("MYVAR2", "{my_var2}") self.buildenv_info.prepend_path("PATH", "{fake_path}") """ client.save({"conanfile.py": conanfile}) @@ -82,30 +43,39 @@ def package_info(self): client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) client.run("install . -s:a os=Linux") + # Only generated fish scripts when virtualenv:fish=True for ext in ["*.sh", "*.bat"]: not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) assert not not_expected_files - assert os.path.exists(os.path.join(client.current_folder, "conanbuildenv.fish")) - assert not os.path.exists(os.path.join(client.current_folder, "conanrunenv.fish")) - - script_path = os.path.join(client.current_folder, "conanbuildenv.fish") - with open(script_path, "r") as f: - buildenv = f.read() - assert f'set -pgx PATH "{fake_path}"' in buildenv - - client.run_command(f'fish -c ". conanbuildenv.fish; set"') - assert f"PATH '{fake_path}'" in client.out - - client.run_command(f'fish -c ". conanbuildenv.fish; deactivate_conanbuildenv; set"') - assert f"PATH '{fake_path}'" not in client.out + # It does not generate conanrun because there is no variables to be exported + expected_files = glob.glob(os.path.join(client.current_folder, "*.fish")) + assert [os.path.join(client.current_folder, "conanbuild.fish"), + os.path.join(client.current_folder, "conanbuildenv.fish")] == expected_files + + buildenv = load(os.path.join(client.current_folder, "conanbuildenv.fish")) + assert f'set -gx MYPATH1 "{my_path}"' in buildenv + assert f'set -gx MYVAR1 "{my_var1}"' in buildenv + assert f'set -gx MYVAR2 "{my_var2}"' in buildenv + assert f'set -pgx PATH "{fake_path}"' in buildenv + + wrap_with_quotes = lambda s: f"'{s}'" if ' ' in s else s + client.run_command('fish -c ". conanbuild.fish; and set; and deactivate_conanbuildenv; and set"') + # Define variables only once and remove after running deactivate_conanbuildenv + assert str(client.out).count(f"\nMYPATH1 {wrap_with_quotes(my_path)}") == 1 + assert str(client.out).count(f"\nMYVAR1 {wrap_with_quotes(my_var1)}") == 1 + assert str(client.out).count(f"\nMYVAR2 {wrap_with_quotes(my_var2)}") == 1 + assert str(client.out).count(f"\nPATH '{fake_path}'") == 1 + # Temporary variables to store names should be removed as well + assert str(client.out).count(f"_PATH {wrap_with_quotes(fake_path)}") == 1 + assert str(client.out).count("_del 'MYPATH1' 'MYVAR1' 'MYVAR2'") == 1 @pytest.mark.tool("fish") @pytest.mark.parametrize("fish_value", [True, False]) @pytest.mark.parametrize("path_with_spaces", [True, False]) @pytest.mark.parametrize("value", ["Dulcinea del Toboso", "Dulcinea-Del-Toboso"]) -def test_transitive_tool_requires(fish_value, path_with_spaces, value): +def test_buildenv_transitive_tool_requires(fish_value, path_with_spaces, value): """Generate a tool require package, which provides and binary and a custom environment variable. Using fish, the binary should be available in the path, and the environment variable too. """ @@ -144,7 +114,68 @@ def build(self): assert value in client.out - # TODO: Check why is generating when running Conan create: - # - deactivate_conanbuildenv-release-armv8.fish - # - conanrunenv-release-armv8.fish - # No variables listed, only script_folder. Should not generate these files. + +@pytest.mark.tool("fish") +@pytest.mark.parametrize("fake_path", ["/path/to/fake/folder", "/path/with space/to/folder"]) +@pytest.mark.parametrize("fake_define", ["FOOBAR", "FOO BAR"]) +def test_runenv_buildenv_define_new_vars(fake_path, fake_define): + """Test when defining new path and new variable using both buildenvinfo and runenvinfo. + + Variables should be available as environment variables in the buildenv and runenv scripts. + And, should be deleted when deactivate_conanbuildenv and deactivate_conanrunenv are called. + """ + cache_folder = os.path.join(temp_folder(), "[sub] folder") + client = TestClient(cache_folder) + conanfile = str(GenConanfile("pkg", "0.1")) + conanfile += f""" + + def package_info(self): + self.buildenv_info.define("FAKE_DEFINE", "{fake_define}") + self.runenv_info.define_path("FAKE_PATH", "{fake_path}") + """ + client.save({"conanfile.py": conanfile}) + client.run("create .") + save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") + client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) + client.run("install . -s:a os=Linux") + + # Only generated fish scripts when virtualenv:fish=True + for ext in ["*.sh", "*.bat"]: + not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) + assert not not_expected_files + + expected_files = glob.glob(os.path.join(client.current_folder, "*.fish")) + assert [os.path.join(client.current_folder, "conanrunenv.fish"), + os.path.join(client.current_folder, "conanrun.fish"), + os.path.join(client.current_folder, "conanbuild.fish"), + os.path.join(client.current_folder, "conanbuildenv.fish")] == expected_files + + # Do not mix buildenv and runenv variables + script_path = os.path.join(client.current_folder, "conanbuildenv.fish") + script_content = load(script_path) + assert f'set -gx FAKE_DEFINE "{fake_define}"' in script_content + assert 'FAKE_PATH' not in script_content + + script_path = os.path.join(client.current_folder, "conanrunenv.fish") + script_content = load(script_path) + assert f'set -pgx FAKE_PATH "{fake_path}"' in script_content + assert 'FAKE_DEFINE' not in script_content + + # Check if generated wrappers are sourcing the env scripts + for group in ['build', 'run']: + script_path = os.path.join(client.current_folder, f"conan{group}.fish") + script_content = load(script_path) + assert f'. "{os.path.join(client.current_folder, f"conan{group}env.fish")}"' in script_content + + # Check if the variables are available in the environment + client.run_command(f'fish -c ". conanbuild.fish; . conanrun.fish; set; deactivate_conanbuildenv; deactivate_conanrunenv; set"') + wrap_with_quotes = lambda s: f"'{s}'" if ' ' in s else s + assert f"FAKE_PATH {wrap_with_quotes(fake_path)}" in client.out + assert f"FAKE_DEFINE {wrap_with_quotes(fake_define)}" in client.out + # It finds 2 because there is a variable with all values names to be deleted + assert client.out.count('FAKE_PATH') == 2 + assert client.out.count('FAKE_DEFINE') == 2 + + deactivated_content = client.out[client.out.index("echo Restoring environment"):] + assert deactivated_content.count('FAKE_PATH') == 0 + assert deactivated_content.count('FAKE_DEFINE') == 0 From 66ca6a2258c6f0f5a9de3e5494e517f971749802 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Thu, 18 Apr 2024 11:39:35 +0200 Subject: [PATCH 17/21] Add more tests for fish env Signed-off-by: Uilian Ries --- conan/tools/env/environment.py | 2 +- .../toolchains/env/test_virtualenv_fish.py | 39 ++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/conan/tools/env/environment.py b/conan/tools/env/environment.py index 4a0a9c65197..fbe23c3203e 100644 --- a/conan/tools/env/environment.py +++ b/conan/tools/env/environment.py @@ -558,7 +558,7 @@ def save_fish(self, file_location): end end function {{ function_name }} - echo "echo Restoring environment" + echo "Restoring environment" for var in $conan_{{ group }}_del set -e $var end diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 93a29283db9..62d9033231f 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -62,13 +62,20 @@ def package_info(self): wrap_with_quotes = lambda s: f"'{s}'" if ' ' in s else s client.run_command('fish -c ". conanbuild.fish; and set; and deactivate_conanbuildenv; and set"') # Define variables only once and remove after running deactivate_conanbuildenv - assert str(client.out).count(f"\nMYPATH1 {wrap_with_quotes(my_path)}") == 1 - assert str(client.out).count(f"\nMYVAR1 {wrap_with_quotes(my_var1)}") == 1 - assert str(client.out).count(f"\nMYVAR2 {wrap_with_quotes(my_var2)}") == 1 + assert str(client.out).count(f"\nMYPATH1 {wrap_with_quotes(my_path)}\n") == 1 + assert str(client.out).count(f"\nMYVAR1 {wrap_with_quotes(my_var1)}\n") == 1 + assert str(client.out).count(f"\nMYVAR2 {wrap_with_quotes(my_var2)}\n") == 1 assert str(client.out).count(f"\nPATH '{fake_path}'") == 1 # Temporary variables to store names should be removed as well - assert str(client.out).count(f"_PATH {wrap_with_quotes(fake_path)}") == 1 - assert str(client.out).count("_del 'MYPATH1' 'MYVAR1' 'MYVAR2'") == 1 + assert str(client.out).count(f"_PATH {wrap_with_quotes(fake_path)}\n") == 1 + assert str(client.out).count("_del 'MYPATH1' 'MYVAR1' 'MYVAR2'\n") == 1 + + # Running conanbuild.fish twice should append variables, but not override them + client.run_command('fish -c ". conanbuild.fish; . conanbuild.fish; and set"') + assert str(client.out).count(f"\nMYPATH1 '{my_path}' '{my_path}'") == 1 + assert str(client.out).count(f"\nMYVAR1 '{my_var1}' '{my_var1}'") == 1 + assert str(client.out).count(f"\nMYVAR2 '{my_var2}' '{my_var2}'") == 1 + assert str(client.out).count(f"\nPATH '{fake_path}' '{fake_path}'") == 1 @pytest.mark.tool("fish") @@ -176,6 +183,26 @@ def package_info(self): assert client.out.count('FAKE_PATH') == 2 assert client.out.count('FAKE_DEFINE') == 2 - deactivated_content = client.out[client.out.index("echo Restoring environment"):] + deactivated_content = client.out[client.out.index("Restoring environment"):] assert deactivated_content.count('FAKE_PATH') == 0 assert deactivated_content.count('FAKE_DEFINE') == 0 + + +def test_no_generate_fish(): + """Test when not defining variables and using Fish as virtualenv + + Conan should not generate any .fish file as there is no variables to be exported + """ + cache_folder = os.path.join(temp_folder(), "[sub] folder") + client = TestClient(cache_folder) + conanfile = str(GenConanfile("pkg", "0.1")) + client.save({"conanfile.py": conanfile}) + client.run("create .") + save(client.cache.new_config_path, "tools.env.virtualenv:fish=True\n") + client.save({"conanfile.py": GenConanfile("app", "0.1").with_requires("pkg/0.1")}) + client.run("install . -s:a os=Linux") + + # Do not generate any virtualenv + for ext in ["*.sh", "*.bat", "*.fish"]: + not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) + assert not not_expected_files From f57f32f1d62a427379655b7138fecc78564174fa Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 22 Apr 2024 15:37:48 +0200 Subject: [PATCH 18/21] Sort the expected files Signed-off-by: Uilian Ries --- .../functional/toolchains/env/test_virtualenv_fish.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/conans/test/functional/toolchains/env/test_virtualenv_fish.py b/conans/test/functional/toolchains/env/test_virtualenv_fish.py index 62d9033231f..7659c956be9 100644 --- a/conans/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/conans/test/functional/toolchains/env/test_virtualenv_fish.py @@ -49,7 +49,7 @@ def package_info(self): assert not not_expected_files # It does not generate conanrun because there is no variables to be exported - expected_files = glob.glob(os.path.join(client.current_folder, "*.fish")) + expected_files = sorted(glob.glob(os.path.join(client.current_folder, "*.fish"))) assert [os.path.join(client.current_folder, "conanbuild.fish"), os.path.join(client.current_folder, "conanbuildenv.fish")] == expected_files @@ -151,11 +151,11 @@ def package_info(self): not_expected_files = glob.glob(os.path.join(client.current_folder, ext)) assert not not_expected_files - expected_files = glob.glob(os.path.join(client.current_folder, "*.fish")) - assert [os.path.join(client.current_folder, "conanrunenv.fish"), + expected_files = sorted(glob.glob(os.path.join(client.current_folder, "*.fish"))) + assert [os.path.join(client.current_folder, "conanbuild.fish"), + os.path.join(client.current_folder, "conanbuildenv.fish"), os.path.join(client.current_folder, "conanrun.fish"), - os.path.join(client.current_folder, "conanbuild.fish"), - os.path.join(client.current_folder, "conanbuildenv.fish")] == expected_files + os.path.join(client.current_folder, "conanrunenv.fish")] == expected_files # Do not mix buildenv and runenv variables script_path = os.path.join(client.current_folder, "conanbuildenv.fish") From 4535b7459c15f43904ce51844550919e2d45b719 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Mon, 22 Apr 2024 16:07:15 +0200 Subject: [PATCH 19/21] Add fish into test configuration Signed-off-by: Uilian Ries --- conans/test/conftest.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/conans/test/conftest.py b/conans/test/conftest.py index ad8e55d7eec..a130a011aa9 100644 --- a/conans/test/conftest.py +++ b/conans/test/conftest.py @@ -178,17 +178,16 @@ "path": {'Darwin': f'{homebrew_root}/share/android-ndk'} } }, + 'fish': { + "default": "3.6", + "3.6": {"path": {"Darwin": f"{homebrew_root}/bin"}} + } # TODO: Intel oneAPI is not installed in CI yet. Uncomment this line whenever it's done. # "intel_oneapi": { # "default": "2021.3", # "exe": "dpcpp", # "2021.3": {"path": {"Linux": "/opt/intel/oneapi/compiler/2021.3.0/linux/bin"}} # } - # TODO: Fish is not yet installed in CI. Uncomment this line whenever it's done - # 'fish': { - # "default": "3.6", - # "3.6": {"path": {"Darwin": f"{homebrew_root}/bin"}} - # } } From 35d682e8d4aa21cb7ff1ac7bb1e00dd73738f420 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 4 Jun 2024 11:23:24 +0200 Subject: [PATCH 20/21] Add missing comma Signed-off-by: Uilian Ries --- test/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/conftest.py b/test/conftest.py index 7eaa0d0be84..ae074bda698 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -184,7 +184,7 @@ 'fish': { "default": "3.6", "3.6": {"path": {"Darwin": f"{homebrew_root}/bin"}} - } + }, "qbs": {"disabled": True}, # TODO: Intel oneAPI is not installed in CI yet. Uncomment this line whenever it's done. # "intel_oneapi": { From d55eff5dbe1e2ab5c8ccff0a1f730644802eaac7 Mon Sep 17 00:00:00 2001 From: Uilian Ries Date: Tue, 4 Jun 2024 11:29:40 +0200 Subject: [PATCH 21/21] Adjust imports with new test folder location Signed-off-by: Uilian Ries --- test/functional/toolchains/env/test_virtualenv_fish.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/toolchains/env/test_virtualenv_fish.py b/test/functional/toolchains/env/test_virtualenv_fish.py index 7659c956be9..cc20698be81 100644 --- a/test/functional/toolchains/env/test_virtualenv_fish.py +++ b/test/functional/toolchains/env/test_virtualenv_fish.py @@ -4,9 +4,9 @@ import pytest -from conans.test.assets.genconanfile import GenConanfile -from conans.test.utils.test_files import temp_folder -from conans.test.utils.tools import TestClient, load +from conan.test.assets.genconanfile import GenConanfile +from conan.test.utils.test_files import temp_folder +from conan.test.utils.tools import TestClient, load from conans.util.files import save # INFO: Fish only supports Cygwin and WSL https://github.com/fish-shell/fish-shell?tab=readme-ov-file#windows