From 043c4442b7c464b462e198ce3d5396671f183899 Mon Sep 17 00:00:00 2001 From: danimtb Date: Thu, 3 Oct 2024 18:22:18 +0200 Subject: [PATCH 01/17] [feature] Add ROSEnv generator integration for ROS --- conan/internal/api/install/generators.py | 3 +- conan/tools/ros/__init__.py | 1 + conan/tools/ros/rosenv.py | 38 +++++++++++++ test/integration/tools/ros/__init__.py | 0 test/integration/tools/ros/test_rosenv.py | 68 +++++++++++++++++++++++ 5 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 conan/tools/ros/__init__.py create mode 100644 conan/tools/ros/rosenv.py create mode 100644 test/integration/tools/ros/__init__.py create mode 100644 test/integration/tools/ros/test_rosenv.py diff --git a/conan/internal/api/install/generators.py b/conan/internal/api/install/generators.py index 9b7de140d42..dbba425a797 100644 --- a/conan/internal/api/install/generators.py +++ b/conan/internal/api/install/generators.py @@ -32,7 +32,8 @@ "SConsDeps": "conan.tools.scons", "QbsDeps": "conan.tools.qbs", "QbsProfile": "conan.tools.qbs", - "CPSDeps": "conan.tools.cps" + "CPSDeps": "conan.tools.cps", + "ROSEnv": "conan.tools.ros" } diff --git a/conan/tools/ros/__init__.py b/conan/tools/ros/__init__.py new file mode 100644 index 00000000000..3b98b36555f --- /dev/null +++ b/conan/tools/ros/__init__.py @@ -0,0 +1 @@ +from conan.tools.ros.rosenv import ROSEnv diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py new file mode 100644 index 00000000000..f5899201c04 --- /dev/null +++ b/conan/tools/ros/rosenv.py @@ -0,0 +1,38 @@ +import os +from conan.api.output import Color +from conan.tools.files import save + + +class ROSEnv(object): + """ + Generator to serve as integration for Robot Operating System 2 development workspaces. + It generates a conanrosenv.bash file that when sources sets variables so the Conan + dependencies are found by CMake and the run environment is also set. + + IMPORTANT: This generator should be used together with CMakeDeps and CMakeToolchain generators. + """ + + def __init__(self, conanfile): + self._conanfile = conanfile + self.filename = "conanrosenv.bash" + self.variables = {} + + def generate(self): + output_folder = self._conanfile.generators_folder + self.variables["CMAKE_TOOLCHAIN_FILE"] = os.path.join(output_folder, "conan_toolchain.cmake") + build_type = self._conanfile.settings.get_safe("build_type") + if build_type: + self.variables["CMAKE_BUILD_TYPE"] = build_type + + content = [] + for key, value in self.variables.items(): + line = f"export {key}=\"{value}\"" + content.append(line) + conanrun_path = os.path.join(output_folder, "conanrun.sh") + content.append(f". \"{conanrun_path}\"") + conanrosenv_path = os.path.join(output_folder, self.filename) + save(self, conanrosenv_path, "\n".join(content)) + + msg = f"Generated ROSEnv Conan file: {self.filename}\n" + \ + f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" + self._conanfile.output.info(msg, fg=Color.CYAN) diff --git a/test/integration/tools/ros/__init__.py b/test/integration/tools/ros/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py new file mode 100644 index 00000000000..ec81783e48a --- /dev/null +++ b/test/integration/tools/ros/test_rosenv.py @@ -0,0 +1,68 @@ +import os +import textwrap +from sys import platform + +import pytest + +from conan.test.utils.tools import TestClient + + +@pytest.mark.skipif(platform.system() != "Windows", reason="Uses UNIX commands") +def test_rosenv(): + """ + Test that the amentdeps generator generates conan_ folders and place CMake files + in the correct path + """ + client = TestClient() + conanfile1 = textwrap.dedent(''' + import os + from conan import ConanFile + from conan.tools.files import save + class Recipe(ConanFile): + name = "lib1" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + + def package(self): + save(self, os.path.join(self.package_folder, "lib", "lib1"), "") + ''') + conanfile2 = textwrap.dedent(''' + import os + from conan import ConanFile + from conan.tools.files import save + + class Recipe(ConanFile): + name = "lib2" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + + def requirements(self): + self.requires('lib1/1.0') + + def package(self): + save(self, os.path.join(self.package_folder, "lib", "lib2"), "") + ''') + conanfile3 = textwrap.dedent(''' + [requires] + lib2/1.0 + [generators] + CMakeDeps + CMakeToolchain + ROSEnv + ''') + client.save({ + "conanfile1.py": conanfile1, + "conanfile2.py": conanfile2, + "conanfile3.txt": conanfile3 + }) + + client.run("create conanfile1.py") + client.run("create conanfile2.py") + client.run("install conanfile3.txt --output-folder install/conan") + assert "Generated ROSEnv Conan file: conanrosenv.bash" in client.out + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + assert os.path.exists(conanrosenv_path) + client.run_command(f"source \"{conanrosenv_path}\" && env") + toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") + assert f"CMAKE_TOOLCHAIN_FILE={toolchain_path}" in client.out + #TODO: Assert LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH paths are set in the environment From 9df601bfdd9f8b8808e995cb09da5723d2f33b64 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 09:20:39 +0200 Subject: [PATCH 02/17] fix --- test/integration/tools/ros/test_rosenv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index ec81783e48a..ab20b5e1fde 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -1,13 +1,13 @@ import os import textwrap -from sys import platform +import platform import pytest from conan.test.utils.tools import TestClient -@pytest.mark.skipif(platform.system() != "Windows", reason="Uses UNIX commands") +@pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands") def test_rosenv(): """ Test that the amentdeps generator generates conan_ folders and place CMake files From 23f22b7c3f7e29826320d918f26be0e5bd719768 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 09:54:34 +0200 Subject: [PATCH 03/17] change source --- test/integration/tools/ros/test_rosenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index ab20b5e1fde..a18b52e581c 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -62,7 +62,7 @@ def package(self): assert "Generated ROSEnv Conan file: conanrosenv.bash" in client.out conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") assert os.path.exists(conanrosenv_path) - client.run_command(f"source \"{conanrosenv_path}\" && env") + client.run_command(f". \"{conanrosenv_path}\" && env") toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") assert f"CMAKE_TOOLCHAIN_FILE={toolchain_path}" in client.out #TODO: Assert LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH paths are set in the environment From 564c2b5c64507c78915beaf123e17caeca1c3095 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 13:22:46 +0200 Subject: [PATCH 04/17] improve generator --- conan/tools/ros/rosenv.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index f5899201c04..da2e7a46458 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -14,25 +14,28 @@ class ROSEnv(object): def __init__(self, conanfile): self._conanfile = conanfile - self.filename = "conanrosenv.bash" + self.filename = "conanrosenv" self.variables = {} def generate(self): output_folder = self._conanfile.generators_folder - self.variables["CMAKE_TOOLCHAIN_FILE"] = os.path.join(output_folder, "conan_toolchain.cmake") + self.variables.setdefault("CMAKE_TOOLCHAIN_FILE", + os.path.join(output_folder, "conan_toolchain.cmake")) build_type = self._conanfile.settings.get_safe("build_type") if build_type: - self.variables["CMAKE_BUILD_TYPE"] = build_type + self.variables.setdefault("CMAKE_BUILD_TYPE", build_type) content = [] + # TODO: Support ps1 and zsh script generation for Windows/Macos for key, value in self.variables.items(): line = f"export {key}=\"{value}\"" content.append(line) conanrun_path = os.path.join(output_folder, "conanrun.sh") content.append(f". \"{conanrun_path}\"") - conanrosenv_path = os.path.join(output_folder, self.filename) + filename = f"{self.filename}.bash" + conanrosenv_path = os.path.join(output_folder, filename) save(self, conanrosenv_path, "\n".join(content)) - msg = f"Generated ROSEnv Conan file: {self.filename}\n" + \ + msg = f"Generated ROSEnv Conan file: {filename}\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" self._conanfile.output.info(msg, fg=Color.CYAN) From a2ea34e21fc8423778b81c2677af44c3d9b78818 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 13:22:59 +0200 Subject: [PATCH 05/17] add some tests --- test/integration/tools/ros/test_rosenv.py | 67 ++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index a18b52e581c..d523abdd0f4 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -4,6 +4,7 @@ import pytest +from conan.test.assets.genconanfile import GenConanfile from conan.test.utils.tools import TestClient @@ -65,4 +66,68 @@ def package(self): client.run_command(f". \"{conanrosenv_path}\" && env") toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") assert f"CMAKE_TOOLCHAIN_FILE={toolchain_path}" in client.out - #TODO: Assert LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH paths are set in the environment + assert "CMAKE_BUILD_TYPE=Release" in client.out + + +@pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands") +def test_rosenv_shared_libraries(): + """ + Test that the library paths env vars are set up correctly so that the executables built with + colcon can found the shared libraries of conan packages + """ + client = TestClient() + c1 = GenConanfile("lib1", "1.0").with_shared_option(False).with_package_file("lib/lib2", "lib-content") + c2 = GenConanfile("lib2", "1.0").with_shared_option(False).with_requirement("lib1/1.0").with_package_file("lib/lib2", "lib-content") + c3 = textwrap.dedent(''' + [requires] + lib2/1.0 + [generators] + CMakeDeps + CMakeToolchain + ROSEnv + ''') + client.save({ + "conanfile1.py": c1, + "conanfile2.py": c2, + "conanfile3.txt": c3 + }) + + client.run("create conanfile1.py -o *:shared=True") + client.run("create conanfile2.py -o *:shared=True") + client.run("install conanfile3.txt -o *:shared=True --output-folder install/conan") + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + client.run_command(f". \"{conanrosenv_path}\" && env") + environment_content = client.out + client.run( + "cache path lib1/1.0#a77a22ae2a9a8dba9b408d95db4e9880:1744785cb24e3bdca70e27041dc5abd20476f947") + lib1_lib_path = os.path.join(client.out.strip(), "lib") + assert lib1_lib_path in environment_content + client.run( + "cache path lib2/1.0#4b7a6063ba107d770458ce10385beb52:5c3c2e56259489f7ffbc8e494921eda4b747ef21") + lib2_lib_path = os.path.join(client.out.strip(), "lib") + assert lib2_lib_path in environment_content + + +@pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands") +def test_error_when_rosenv_not_used_with_cmake_generators(): + """ + ROSEnv generator should be used with CMake and CMakeToolchain generators + """ + client = TestClient() + c1 = GenConanfile("lib1", "1.0") + c2 = textwrap.dedent(''' + [requires] + lib2/1.0 + [generators] + ROSEnv + ''') + client.save({ + "conanfile1.py": c1, + "conanfile2.txt": c2 + }) + + client.run("create conanfile1.py") + client.run("install conanfile2.txt --output-folder install/conan") + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + client.run_command(f". \"{conanrosenv_path}\" && env", assert_error=True) + assert "kk" in client.out From 37dfbbaf2062c86d3586bc45eda2e219f6b2e598 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 13:57:05 +0200 Subject: [PATCH 06/17] reduce tests --- test/integration/tools/ros/test_rosenv.py | 35 +---------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index d523abdd0f4..644696f4281 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -15,50 +15,17 @@ def test_rosenv(): in the correct path """ client = TestClient() - conanfile1 = textwrap.dedent(''' - import os - from conan import ConanFile - from conan.tools.files import save - class Recipe(ConanFile): - name = "lib1" - version = "1.0" - settings = "os", "compiler", "build_type", "arch" - - def package(self): - save(self, os.path.join(self.package_folder, "lib", "lib1"), "") - ''') - conanfile2 = textwrap.dedent(''' - import os - from conan import ConanFile - from conan.tools.files import save - - class Recipe(ConanFile): - name = "lib2" - version = "1.0" - settings = "os", "compiler", "build_type", "arch" - - def requirements(self): - self.requires('lib1/1.0') - - def package(self): - save(self, os.path.join(self.package_folder, "lib", "lib2"), "") - ''') conanfile3 = textwrap.dedent(''' [requires] - lib2/1.0 [generators] CMakeDeps CMakeToolchain ROSEnv ''') client.save({ - "conanfile1.py": conanfile1, - "conanfile2.py": conanfile2, "conanfile3.txt": conanfile3 }) - client.run("create conanfile1.py") - client.run("create conanfile2.py") client.run("install conanfile3.txt --output-folder install/conan") assert "Generated ROSEnv Conan file: conanrosenv.bash" in client.out conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") @@ -117,7 +84,7 @@ def test_error_when_rosenv_not_used_with_cmake_generators(): c1 = GenConanfile("lib1", "1.0") c2 = textwrap.dedent(''' [requires] - lib2/1.0 + lib1/1.0 [generators] ROSEnv ''') From 4bfd5efa767ebce7b33b586add2f2c744588effb Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 14:02:50 +0200 Subject: [PATCH 07/17] remove assert error --- test/integration/tools/ros/test_rosenv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index 644696f4281..94efa88c3af 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -96,5 +96,5 @@ def test_error_when_rosenv_not_used_with_cmake_generators(): client.run("create conanfile1.py") client.run("install conanfile2.txt --output-folder install/conan") conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") - client.run_command(f". \"{conanrosenv_path}\" && env", assert_error=True) + client.run_command(f". \"{conanrosenv_path}\" && env") assert "kk" in client.out From fc0c2086f67c7aa4c6a09708f20ee15797e0d9ae Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 14:27:49 +0200 Subject: [PATCH 08/17] try --- test/integration/tools/ros/test_rosenv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index 94efa88c3af..fbace1dbb09 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -96,5 +96,6 @@ def test_error_when_rosenv_not_used_with_cmake_generators(): client.run("create conanfile1.py") client.run("install conanfile2.txt --output-folder install/conan") conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + print("CONTENT: ", client.load(conanrosenv_path)) client.run_command(f". \"{conanrosenv_path}\" && env") assert "kk" in client.out From 17ff35647ff21d6af261f7d09e318213a1026928 Mon Sep 17 00:00:00 2001 From: danimtb Date: Fri, 4 Oct 2024 16:15:50 +0200 Subject: [PATCH 09/17] improve --- conan/tools/ros/rosenv.py | 17 +++++++++++++++-- test/integration/tools/ros/test_rosenv.py | 10 +++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index da2e7a46458..968d35d9ad3 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -1,6 +1,16 @@ import os from conan.api.output import Color from conan.tools.files import save +from conan.errors import ConanException + + +cmake_toolchain_exists_bash = """\ +if [ ! -e "$CMAKE_TOOLCHAIN_FILE" ]; then + echo "Error: CMAKE_TOOLCHAIN_FILE path not found at '$CMAKE_TOOLCHAIN_FILE'." + echo "Make sure you are using CMakeToolchain and CMakeDeps generators too." + exit 1 +fi +""" class ROSEnv(object): @@ -27,12 +37,15 @@ def generate(self): content = [] # TODO: Support ps1 and zsh script generation for Windows/Macos + if self._conanfile.settings.get_safe("os") == "Windows": + raise ConanException("ROSEnv generator does not support Windows") for key, value in self.variables.items(): - line = f"export {key}=\"{value}\"" - content.append(line) + content.append(f"{key}=\"{value}\"") + content.append(f"export {key}") conanrun_path = os.path.join(output_folder, "conanrun.sh") content.append(f". \"{conanrun_path}\"") filename = f"{self.filename}.bash" + content.append(cmake_toolchain_exists_bash) conanrosenv_path = os.path.join(output_folder, filename) save(self, conanrosenv_path, "\n".join(content)) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index fbace1dbb09..5743198ec42 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -81,21 +81,17 @@ def test_error_when_rosenv_not_used_with_cmake_generators(): ROSEnv generator should be used with CMake and CMakeToolchain generators """ client = TestClient() - c1 = GenConanfile("lib1", "1.0") c2 = textwrap.dedent(''' [requires] - lib1/1.0 [generators] ROSEnv ''') client.save({ - "conanfile1.py": c1, "conanfile2.txt": c2 }) - client.run("create conanfile1.py") client.run("install conanfile2.txt --output-folder install/conan") conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") - print("CONTENT: ", client.load(conanrosenv_path)) - client.run_command(f". \"{conanrosenv_path}\" && env") - assert "kk" in client.out + client.run_command(f". \"{conanrosenv_path}\"", assert_error=True) + assert "CMAKE_TOOLCHAIN_FILE path not found" in client.out + assert "Make sure you are using CMakeToolchain and CMakeDeps generators too" in client.out From 326b7c2f6c45ab60a21a2b2a59e669e6dca7435c Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 14 Oct 2024 10:56:06 +0200 Subject: [PATCH 10/17] use virtualbuildenv and virtualrunenv --- conan/tools/ros/rosenv.py | 31 ++++++++++----------- test/integration/tools/ros/test_rosenv.py | 34 +++++------------------ 2 files changed, 21 insertions(+), 44 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 968d35d9ad3..eba536fd74c 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -1,5 +1,6 @@ import os from conan.api.output import Color +from conan.tools.env import VirtualBuildEnv, VirtualRunEnv, Environment from conan.tools.files import save from conan.errors import ConanException @@ -24,8 +25,10 @@ class ROSEnv(object): def __init__(self, conanfile): self._conanfile = conanfile - self.filename = "conanrosenv" self.variables = {} + self._virtualbuildenv = VirtualBuildEnv(conanfile) + self._virtualbuildenv.basename = "conanrosenv" + self._virtualrunenv = VirtualRunEnv(conanfile) def generate(self): output_folder = self._conanfile.generators_folder @@ -34,21 +37,15 @@ def generate(self): build_type = self._conanfile.settings.get_safe("build_type") if build_type: self.variables.setdefault("CMAKE_BUILD_TYPE", build_type) - - content = [] - # TODO: Support ps1 and zsh script generation for Windows/Macos - if self._conanfile.settings.get_safe("os") == "Windows": - raise ConanException("ROSEnv generator does not support Windows") - for key, value in self.variables.items(): - content.append(f"{key}=\"{value}\"") - content.append(f"export {key}") - conanrun_path = os.path.join(output_folder, "conanrun.sh") - content.append(f". \"{conanrun_path}\"") - filename = f"{self.filename}.bash" - content.append(cmake_toolchain_exists_bash) - conanrosenv_path = os.path.join(output_folder, filename) - save(self, conanrosenv_path, "\n".join(content)) - - msg = f"Generated ROSEnv Conan file: {filename}\n" + \ + # Add ROS required variables to VirtualBuildEnv + rosbuildenv = Environment() + for k, v in self.variables.items(): + rosbuildenv.define(k, v) + self._virtualbuildenv._buildenv = rosbuildenv + # Add VirtualRunEnv variables to VirtualBuildEnv + self._virtualbuildenv._buildenv.compose_env(self._virtualrunenv.environment()) + self._virtualbuildenv.generate() + conanrosenv_path = os.path.join(self._conanfile.generators_folder, "conanrosenv.sh") + msg = f"Generated ROSEnv Conan file: conanrosenv.sh\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" self._conanfile.output.info(msg, fg=Color.CYAN) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index 5743198ec42..aa1d59bfbf3 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -27,8 +27,9 @@ def test_rosenv(): }) client.run("install conanfile3.txt --output-folder install/conan") - assert "Generated ROSEnv Conan file: conanrosenv.bash" in client.out - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + # assert "Generated ROSEnv Conan file: conanrosenv.sh" in client.out + # FIXME: This file should be conanrosenv.sh, without configuration, but conanbuild.sh is generated instead + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv-release-x86_64.sh") assert os.path.exists(conanrosenv_path) client.run_command(f". \"{conanrosenv_path}\" && env") toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") @@ -43,7 +44,7 @@ def test_rosenv_shared_libraries(): colcon can found the shared libraries of conan packages """ client = TestClient() - c1 = GenConanfile("lib1", "1.0").with_shared_option(False).with_package_file("lib/lib2", "lib-content") + c1 = GenConanfile("lib1", "1.0").with_shared_option(False).with_package_file("lib/lib1", "lib-content") c2 = GenConanfile("lib2", "1.0").with_shared_option(False).with_requirement("lib1/1.0").with_package_file("lib/lib2", "lib-content") c3 = textwrap.dedent(''' [requires] @@ -62,36 +63,15 @@ def test_rosenv_shared_libraries(): client.run("create conanfile1.py -o *:shared=True") client.run("create conanfile2.py -o *:shared=True") client.run("install conanfile3.txt -o *:shared=True --output-folder install/conan") - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") + # FIXME: This file should be conanrosenv.sh, without configuration, but conanbuild.sh is generated instead + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv-release-x86_64.sh") client.run_command(f". \"{conanrosenv_path}\" && env") environment_content = client.out client.run( - "cache path lib1/1.0#a77a22ae2a9a8dba9b408d95db4e9880:1744785cb24e3bdca70e27041dc5abd20476f947") + "cache path lib1/1.0#58723f478a96866dcbd9456d8eefd7c4:1744785cb24e3bdca70e27041dc5abd20476f947") lib1_lib_path = os.path.join(client.out.strip(), "lib") assert lib1_lib_path in environment_content client.run( "cache path lib2/1.0#4b7a6063ba107d770458ce10385beb52:5c3c2e56259489f7ffbc8e494921eda4b747ef21") lib2_lib_path = os.path.join(client.out.strip(), "lib") assert lib2_lib_path in environment_content - - -@pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands") -def test_error_when_rosenv_not_used_with_cmake_generators(): - """ - ROSEnv generator should be used with CMake and CMakeToolchain generators - """ - client = TestClient() - c2 = textwrap.dedent(''' - [requires] - [generators] - ROSEnv - ''') - client.save({ - "conanfile2.txt": c2 - }) - - client.run("install conanfile2.txt --output-folder install/conan") - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.bash") - client.run_command(f". \"{conanrosenv_path}\"", assert_error=True) - assert "CMAKE_TOOLCHAIN_FILE path not found" in client.out - assert "Make sure you are using CMakeToolchain and CMakeDeps generators too" in client.out From 231e048e6b6c7a6db028dd38c1bf6f7b6511d5cf Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 14 Oct 2024 11:51:46 +0200 Subject: [PATCH 11/17] use new scope --- conan/tools/ros/rosenv.py | 15 ++------------- test/integration/tools/ros/test_rosenv.py | 8 +++----- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index eba536fd74c..3a07605d7d0 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -1,23 +1,12 @@ import os from conan.api.output import Color from conan.tools.env import VirtualBuildEnv, VirtualRunEnv, Environment -from conan.tools.files import save -from conan.errors import ConanException - - -cmake_toolchain_exists_bash = """\ -if [ ! -e "$CMAKE_TOOLCHAIN_FILE" ]; then - echo "Error: CMAKE_TOOLCHAIN_FILE path not found at '$CMAKE_TOOLCHAIN_FILE'." - echo "Make sure you are using CMakeToolchain and CMakeDeps generators too." - exit 1 -fi -""" class ROSEnv(object): """ Generator to serve as integration for Robot Operating System 2 development workspaces. - It generates a conanrosenv.bash file that when sources sets variables so the Conan + It generates a conanrosenv.sh file that when sources sets variables so the Conan dependencies are found by CMake and the run environment is also set. IMPORTANT: This generator should be used together with CMakeDeps and CMakeToolchain generators. @@ -44,7 +33,7 @@ def generate(self): self._virtualbuildenv._buildenv = rosbuildenv # Add VirtualRunEnv variables to VirtualBuildEnv self._virtualbuildenv._buildenv.compose_env(self._virtualrunenv.environment()) - self._virtualbuildenv.generate() + self._virtualbuildenv.generate(scope="rosenv") # Create new scope to generate conanrosenv.sh conanrosenv_path = os.path.join(self._conanfile.generators_folder, "conanrosenv.sh") msg = f"Generated ROSEnv Conan file: conanrosenv.sh\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index aa1d59bfbf3..c7ab0dbb0aa 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -27,9 +27,8 @@ def test_rosenv(): }) client.run("install conanfile3.txt --output-folder install/conan") - # assert "Generated ROSEnv Conan file: conanrosenv.sh" in client.out - # FIXME: This file should be conanrosenv.sh, without configuration, but conanbuild.sh is generated instead - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv-release-x86_64.sh") + assert "Generated ROSEnv Conan file: conanrosenv.sh" in client.out + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.sh") assert os.path.exists(conanrosenv_path) client.run_command(f". \"{conanrosenv_path}\" && env") toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") @@ -63,8 +62,7 @@ def test_rosenv_shared_libraries(): client.run("create conanfile1.py -o *:shared=True") client.run("create conanfile2.py -o *:shared=True") client.run("install conanfile3.txt -o *:shared=True --output-folder install/conan") - # FIXME: This file should be conanrosenv.sh, without configuration, but conanbuild.sh is generated instead - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv-release-x86_64.sh") + conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.sh") client.run_command(f". \"{conanrosenv_path}\" && env") environment_content = client.out client.run( From 6488e817c4628f39897f374a136d3141585f6bef Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 18 Oct 2024 10:37:35 +0200 Subject: [PATCH 12/17] Update test/integration/tools/ros/test_rosenv.py Co-authored-by: Carlos Zoido --- test/integration/tools/ros/test_rosenv.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index c7ab0dbb0aa..12805945652 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -11,8 +11,7 @@ @pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands") def test_rosenv(): """ - Test that the amentdeps generator generates conan_ folders and place CMake files - in the correct path + Test that the ROSEnv generator generates the environment files and that the environment variables are correctly set """ client = TestClient() conanfile3 = textwrap.dedent(''' From 160745a925fd986d68386c287b8275fe5c4d80b5 Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 28 Oct 2024 16:19:56 +0100 Subject: [PATCH 13/17] change approach --- conan/tools/ros/rosenv.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 3a07605d7d0..552a990f8ff 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -1,9 +1,10 @@ import os from conan.api.output import Color -from conan.tools.env import VirtualBuildEnv, VirtualRunEnv, Environment +from conan.tools.env import VirtualBuildEnv, Environment +from conan.tools.files import save -class ROSEnv(object): +class ROSEnv: """ Generator to serve as integration for Robot Operating System 2 development workspaces. It generates a conanrosenv.sh file that when sources sets variables so the Conan @@ -14,27 +15,36 @@ class ROSEnv(object): def __init__(self, conanfile): self._conanfile = conanfile + self._variables = {} self.variables = {} - self._virtualbuildenv = VirtualBuildEnv(conanfile) + self._virtualbuildenv = VirtualBuildEnv(self._conanfile, auto_generate=True) self._virtualbuildenv.basename = "conanrosenv" - self._virtualrunenv = VirtualRunEnv(conanfile) + self._rosenv_wrapper = "conanrosenv.sh" def generate(self): output_folder = self._conanfile.generators_folder - self.variables.setdefault("CMAKE_TOOLCHAIN_FILE", - os.path.join(output_folder, "conan_toolchain.cmake")) + self._variables["CMAKE_TOOLCHAIN_FILE"] = os.path.join(output_folder, "conan_toolchain.cmake") build_type = self._conanfile.settings.get_safe("build_type") if build_type: - self.variables.setdefault("CMAKE_BUILD_TYPE", build_type) + self._variables["CMAKE_BUILD_TYPE"] = build_type + self.variables.update(self._variables) + # Add ROS required variables to VirtualBuildEnv rosbuildenv = Environment() - for k, v in self.variables.items(): + for k, v in self._variables.items(): rosbuildenv.define(k, v) self._virtualbuildenv._buildenv = rosbuildenv - # Add VirtualRunEnv variables to VirtualBuildEnv - self._virtualbuildenv._buildenv.compose_env(self._virtualrunenv.environment()) - self._virtualbuildenv.generate(scope="rosenv") # Create new scope to generate conanrosenv.sh - conanrosenv_path = os.path.join(self._conanfile.generators_folder, "conanrosenv.sh") + self._virtualbuildenv.generate() + + # Generate conanrosenv.sh script wrapper that calls conanbuild.sh and conanrun.sh + conanbuild_path = os.path.join(self._conanfile.generators_folder, "conanbuild.sh") + cmd_wrapper = [f". {conanbuild_path}"] + conanrun_path = os.path.join(self._conanfile.generators_folder, "conanrun.sh") + if os.path.exists(conanrun_path): + cmd_wrapper.append(f". {conanrun_path}") + conanrosenv_path = os.path.join(self._conanfile.generators_folder, self._rosenv_wrapper) + save(self._conanfile, conanrosenv_path, "\n".join(cmd_wrapper)) + msg = f"Generated ROSEnv Conan file: conanrosenv.sh\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" self._conanfile.output.info(msg, fg=Color.CYAN) From 88bd8343bd360972e6f812410c233ba1bbc590bb Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 28 Oct 2024 16:33:35 +0100 Subject: [PATCH 14/17] fix --- conan/tools/ros/rosenv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 552a990f8ff..29b1fcc5b27 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -38,10 +38,10 @@ def generate(self): # Generate conanrosenv.sh script wrapper that calls conanbuild.sh and conanrun.sh conanbuild_path = os.path.join(self._conanfile.generators_folder, "conanbuild.sh") - cmd_wrapper = [f". {conanbuild_path}"] + cmd_wrapper = [f". \"{conanbuild_path}\""] conanrun_path = os.path.join(self._conanfile.generators_folder, "conanrun.sh") if os.path.exists(conanrun_path): - cmd_wrapper.append(f". {conanrun_path}") + cmd_wrapper.append(f". \"{conanrun_path}\"") conanrosenv_path = os.path.join(self._conanfile.generators_folder, self._rosenv_wrapper) save(self._conanfile, conanrosenv_path, "\n".join(cmd_wrapper)) From d1b71100c7b3d60fb5c96fd28d80c223f629d774 Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 28 Oct 2024 17:04:07 +0100 Subject: [PATCH 15/17] fixes --- conan/tools/ros/rosenv.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 29b1fcc5b27..37763bc328f 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -38,12 +38,10 @@ def generate(self): # Generate conanrosenv.sh script wrapper that calls conanbuild.sh and conanrun.sh conanbuild_path = os.path.join(self._conanfile.generators_folder, "conanbuild.sh") - cmd_wrapper = [f". \"{conanbuild_path}\""] conanrun_path = os.path.join(self._conanfile.generators_folder, "conanrun.sh") - if os.path.exists(conanrun_path): - cmd_wrapper.append(f". \"{conanrun_path}\"") + rosenv_wrapper_content = [f". \"{conanbuild_path}\"", f". \"{conanrun_path}\""] conanrosenv_path = os.path.join(self._conanfile.generators_folder, self._rosenv_wrapper) - save(self._conanfile, conanrosenv_path, "\n".join(cmd_wrapper)) + save(self._conanfile, conanrosenv_path, "\n".join(rosenv_wrapper_content)) msg = f"Generated ROSEnv Conan file: conanrosenv.sh\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" From 1cab5d60dd36e1b7493cfa70b9c54d303cd70b42 Mon Sep 17 00:00:00 2001 From: danimtb Date: Mon, 28 Oct 2024 17:13:33 +0100 Subject: [PATCH 16/17] small fix and todo --- conan/tools/ros/rosenv.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 37763bc328f..06268fc0ef7 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -31,12 +31,13 @@ def generate(self): # Add ROS required variables to VirtualBuildEnv rosbuildenv = Environment() - for k, v in self._variables.items(): + for k, v in self.variables.items(): rosbuildenv.define(k, v) self._virtualbuildenv._buildenv = rosbuildenv self._virtualbuildenv.generate() # Generate conanrosenv.sh script wrapper that calls conanbuild.sh and conanrun.sh + # TODO: Windows .bat/.ps1 files still not supported for the wrapper conanbuild_path = os.path.join(self._conanfile.generators_folder, "conanbuild.sh") conanrun_path = os.path.join(self._conanfile.generators_folder, "conanrun.sh") rosenv_wrapper_content = [f". \"{conanbuild_path}\"", f". \"{conanrun_path}\""] From 103c25409c94cb347ffa9d993b57ac73427fd031 Mon Sep 17 00:00:00 2001 From: danimtb Date: Tue, 29 Oct 2024 11:47:23 +0100 Subject: [PATCH 17/17] review --- conan/tools/ros/rosenv.py | 25 +++++++++++------------ test/integration/tools/ros/test_rosenv.py | 7 +++++-- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/conan/tools/ros/rosenv.py b/conan/tools/ros/rosenv.py index 06268fc0ef7..5a6eb644bcf 100644 --- a/conan/tools/ros/rosenv.py +++ b/conan/tools/ros/rosenv.py @@ -1,6 +1,7 @@ import os from conan.api.output import Color from conan.tools.env import VirtualBuildEnv, Environment +from conan.tools.env.environment import create_env_script from conan.tools.files import save @@ -15,35 +16,33 @@ class ROSEnv: def __init__(self, conanfile): self._conanfile = conanfile - self._variables = {} self.variables = {} - self._virtualbuildenv = VirtualBuildEnv(self._conanfile, auto_generate=True) - self._virtualbuildenv.basename = "conanrosenv" - self._rosenv_wrapper = "conanrosenv.sh" + self._build_script_file = "conanrosenv-build.sh" + self._wrapper_script_file = "conanrosenv.sh" def generate(self): - output_folder = self._conanfile.generators_folder - self._variables["CMAKE_TOOLCHAIN_FILE"] = os.path.join(output_folder, "conan_toolchain.cmake") + cmake_toolchain_path = os.path.join(self._conanfile.generators_folder, + "conan_toolchain.cmake") + self.variables["CMAKE_TOOLCHAIN_FILE"] = f"\"{cmake_toolchain_path}\"" build_type = self._conanfile.settings.get_safe("build_type") if build_type: - self._variables["CMAKE_BUILD_TYPE"] = build_type - self.variables.update(self._variables) + self.variables["CMAKE_BUILD_TYPE"] = build_type # Add ROS required variables to VirtualBuildEnv rosbuildenv = Environment() for k, v in self.variables.items(): rosbuildenv.define(k, v) - self._virtualbuildenv._buildenv = rosbuildenv - self._virtualbuildenv.generate() + rosbuildenv.vars(self._conanfile, "build").save_script(self._build_script_file) # Generate conanrosenv.sh script wrapper that calls conanbuild.sh and conanrun.sh # TODO: Windows .bat/.ps1 files still not supported for the wrapper conanbuild_path = os.path.join(self._conanfile.generators_folder, "conanbuild.sh") conanrun_path = os.path.join(self._conanfile.generators_folder, "conanrun.sh") rosenv_wrapper_content = [f". \"{conanbuild_path}\"", f". \"{conanrun_path}\""] - conanrosenv_path = os.path.join(self._conanfile.generators_folder, self._rosenv_wrapper) - save(self._conanfile, conanrosenv_path, "\n".join(rosenv_wrapper_content)) + create_env_script(self._conanfile, "\n".join(rosenv_wrapper_content), + self._wrapper_script_file, None) - msg = f"Generated ROSEnv Conan file: conanrosenv.sh\n" + \ + conanrosenv_path = os.path.join(self._conanfile.generators_folder, self._wrapper_script_file) + msg = f"Generated ROSEnv Conan file: {self._wrapper_script_file}\n" + \ f"Use 'source {conanrosenv_path}' to set the ROSEnv Conan before 'colcon build'" self._conanfile.output.info(msg, fg=Color.CYAN) diff --git a/test/integration/tools/ros/test_rosenv.py b/test/integration/tools/ros/test_rosenv.py index 12805945652..2865fb6c431 100644 --- a/test/integration/tools/ros/test_rosenv.py +++ b/test/integration/tools/ros/test_rosenv.py @@ -27,11 +27,14 @@ def test_rosenv(): client.run("install conanfile3.txt --output-folder install/conan") assert "Generated ROSEnv Conan file: conanrosenv.sh" in client.out - conanrosenv_path = os.path.join(client.current_folder, "install", "conan", "conanrosenv.sh") + install_folder = os.path.join(client.current_folder, "install", "conan") + conanrosenv_path = os.path.join(install_folder, "conanrosenv.sh") assert os.path.exists(conanrosenv_path) + conanrosenvbuild_path = os.path.join(install_folder, "conanrosenv-build.sh") + assert os.path.exists(conanrosenvbuild_path) client.run_command(f". \"{conanrosenv_path}\" && env") toolchain_path = os.path.join(client.current_folder, "install", "conan", "conan_toolchain.cmake") - assert f"CMAKE_TOOLCHAIN_FILE={toolchain_path}" in client.out + assert f"CMAKE_TOOLCHAIN_FILE=\"{toolchain_path}\"" in client.out assert "CMAKE_BUILD_TYPE=Release" in client.out