Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Add ROSEnv generator integration for ROS2 builds #17110

Merged
merged 18 commits into from
Oct 29, 2024
3 changes: 2 additions & 1 deletion conan/internal/api/install/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}


Expand Down
1 change: 1 addition & 0 deletions conan/tools/ros/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from conan.tools.ros.rosenv import ROSEnv
48 changes: 48 additions & 0 deletions conan/tools/ros/rosenv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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


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
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.variables = {}
self._build_script_file = "conanrosenv-build.sh"
self._wrapper_script_file = "conanrosenv.sh"

def generate(self):
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

# Add ROS required variables to VirtualBuildEnv
rosbuildenv = Environment()
for k, v in self.variables.items():
rosbuildenv.define(k, v)
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}\""]
create_env_script(self._conanfile, "\n".join(rosenv_wrapper_content),
self._wrapper_script_file, None)

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)
Empty file.
77 changes: 77 additions & 0 deletions test/integration/tools/ros/test_rosenv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import os
import textwrap
import platform

import pytest

from conan.test.assets.genconanfile import GenConanfile
from conan.test.utils.tools import TestClient


@pytest.mark.skipif(platform.system() == "Windows", reason="Uses UNIX commands")
def test_rosenv():
"""
Test that the ROSEnv generator generates the environment files and that the environment variables are correctly set
"""
client = TestClient()
conanfile3 = textwrap.dedent('''
[requires]
[generators]
CMakeDeps
CMakeToolchain
ROSEnv
''')
client.save({
"conanfile3.txt": conanfile3
})

client.run("install conanfile3.txt --output-folder install/conan")
assert "Generated ROSEnv Conan file: conanrosenv.sh" in client.out
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 "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/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]
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.sh")
client.run_command(f". \"{conanrosenv_path}\" && env")
environment_content = client.out
client.run(
"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