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 AmentDeps generator for ROS2 support in colcon builds #17055

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
"AmentDeps": "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.amentdeps import AmentDeps
53 changes: 53 additions & 0 deletions conan/tools/ros/amentdeps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from conan.tools.files import save
from conan.tools.cmake import CMakeDeps

import os

class AmentDeps(object):
"""
Generator to serve as integration for Robot Operating System 2 development workspaces.
It is able to generate CMake files and place them correctly so they can be found by Colcon.
"""

def __init__(self, conanfile):
self.cmakedeps_files = None
self.cmakedeps = CMakeDeps(conanfile)
self._conanfile = conanfile

def generate(self):
# This is called here so the CMakeDeps output is only showed once
self.cmakedeps_files = self.cmakedeps.content

output_folder = self._conanfile.generators_folder
if not output_folder.endswith("install"):
self._conanfile.output.warning("The output folder for the Ament generator should be"
" always 'install'. Make sure you are using "
"'--output-folder install' in your 'conan install'"
"command")
# Get the name of the ROS package that requires the Conan packages
ros_package_name = os.path.basename(self._conanfile.source_folder)
self._conanfile.output.info(f"ROS2 workspace install folder: {output_folder}")

for require, _ in self._conanfile.dependencies.items():
self.generate_cmake_files(output_folder, ros_package_name, require.ref.name)

def generate_cmake_files(self, install_folder, ros_package_name, require_name):
"""
Generate CMakeDeps files in <install_folder>/<ros_package_name>/share/<require_name>/cmake
Fox example : install/consumer/share/bzip2/cmake

@param install_folder: folder to generate the
@param ros_package_name: name of ROS package that has de Conan dependencies
@param require_name: name of the dependency
"""
self._conanfile.output.info(f"Generating CMake files for {require_name} dependency")
for generator_file, content in self.cmakedeps_files.items():
# Create CMake files in install/<ros_package_name>/share/<require_name>/cmake directory
if require_name in generator_file.lower() or \
"cmakedeps_macros.cmake" in generator_file.lower():
self._conanfile.output.info(f"Generating CMake file {generator_file}")
# FIXME: This is a way to save only the require_name related cmake files
# (and helper cmake files), however, names might not match!!
file_path = os.path.join(install_folder, ros_package_name, "share", require_name,
"cmake", generator_file)
save(self._conanfile, file_path, content)
Empty file.
65 changes: 65 additions & 0 deletions test/integration/tools/ros/test_amentdeps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import os
import textwrap

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


def test_amentdeps():
"""
Test that the amentdeps generator generates conan_<name> 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]
AmentDeps
''')
client.save({
"conanfile1.py": conanfile1,
"conanfile2.py": conanfile2,
os.path.join("ros_package", "conanfile3.txt"): conanfile3
})

client.run("create conanfile1.py")
client.run("create conanfile2.py")
client.run("install ros_package/conanfile3.txt --output-folder install")
assert "Generating CMake files for lib2 dependency" in client.out
# Check CMake files generated
lib2_cmake_config = os.path.join(client.current_folder, "install", "ros_package", "share",
"lib2", "cmake", "lib2-config.cmake")
assert os.path.exists(lib2_cmake_config)
# Check lib2's transitive dependency
lib1_cmake_config = os.path.join(client.current_folder, "install", "ros_package", "share",
"lib1", "cmake", "lib1-config.cmake")
assert os.path.exists(lib1_cmake_config)