pax_global_header00006660000000000000000000000064145075664210014524gustar00rootroot0000000000000052 comment=6d65444f52b1afac179c38db6531123bc1167d65 colcon-ros-0.4.1/000077500000000000000000000000001450756642100136045ustar00rootroot00000000000000colcon-ros-0.4.1/.github/000077500000000000000000000000001450756642100151445ustar00rootroot00000000000000colcon-ros-0.4.1/.github/workflows/000077500000000000000000000000001450756642100172015ustar00rootroot00000000000000colcon-ros-0.4.1/.github/workflows/ci.yaml000066400000000000000000000012061450756642100204570ustar00rootroot00000000000000name: Run tests on: push: branches: ['master'] pull_request: jobs: setup: runs-on: ubuntu-latest outputs: strategy: ${{steps.load.outputs.strategy}} steps: - uses: actions/checkout@v3 with: repository: colcon/ci - id: load run: echo "strategy=$(echo $(cat strategy.json))" >> $GITHUB_OUTPUT pytest: needs: [setup] strategy: ${{fromJson(needs.setup.outputs.strategy)}} runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ${{matrix.python}} - uses: colcon/ci@v1 colcon-ros-0.4.1/LICENSE000066400000000000000000000261361450756642100146210ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. colcon-ros-0.4.1/README.rst000066400000000000000000000002201450756642100152650ustar00rootroot00000000000000colcon-ros ========== An extension for `colcon-core `_ to support `ROS packages `_. colcon-ros-0.4.1/colcon_ros/000077500000000000000000000000001450756642100157445ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/__init__.py000066400000000000000000000001521450756642100200530ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 __version__ = '0.4.1' colcon-ros-0.4.1/colcon_ros/argcomplete_completer/000077500000000000000000000000001450756642100223205ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/argcomplete_completer/__init__.py000066400000000000000000000000001450756642100244170ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/argcomplete_completer/catkin_cmake_args.py000066400000000000000000000040421450756642100263170ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 # try import since this package doesn't depend on colcon-argcomplete # and needs to fallback gracefully if it is not available try: from colcon_argcomplete.argcomplete_completer \ import ArgcompleteCompleterExtensionPoint except ImportError: class ArgcompleteCompleterExtensionPoint: # noqa: D101 pass from colcon_cmake.argcomplete_completer.cmake_args \ import get_cmake_args_completer_choices from colcon_core.plugin_system import satisfies_version class CatkinCmakeArgcompleteCompleter(ArgcompleteCompleterExtensionPoint): """Completion of catkin CMake arguments.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version( ArgcompleteCompleterExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def get_completer(self, parser, *args, **kwargs): # noqa: D102 # only contribute a complete for CMake arguments if '--catkin-cmake-args' not in args: return None # check that the argcomplete version has the necessary completer try: from argcomplete.completers import ChoicesCompleter except ImportError: return None # since only one extension can contribute a completer # include the CMake choices in addition to the catkin ones return ChoicesCompleter( get_cmake_args_completer_choices() + get_catkin_cmake_args_completer_choices()) def get_catkin_cmake_args_completer_choices(): """Get completer choices for CMake arguments for catkin packages.""" # HACK the quote and equal characters are currently a problem # see https://github.com/kislyuk/argcomplete/issues/94 return \ [ f' -DCATKIN_ENABLE_TESTING={flag}' for flag in ('OFF', 'ON')] + \ [ f' -DCATKIN_SKIP_TESTING={flag}' for flag in ('OFF', 'ON')] + \ [ f' -DSETUPTOOLS_DEB_LAYOUT={flag}' for flag in ('OFF', 'ON')] colcon-ros-0.4.1/colcon_ros/installed_packages.py000066400000000000000000000014641450756642100221400ustar00rootroot00000000000000# Copyright 2016-2021 Dirk Thomas # Licensed under the Apache License, Version 2.0 from pathlib import Path from colcon_core.shell import FindInstalledPackagesExtensionPoint class AmentInstalledPackageFinder(FindInstalledPackagesExtensionPoint): """ Find installed packages in an ament workspace. This is almost always /opt/ros/ """ def find_installed_packages(self, install_base: Path): """Find installed packages in an ament workspace.""" package_index = install_base.joinpath( 'share', 'ament_index', 'resource_index', 'packages') if not package_index.is_dir(): return None packages = {} for marker_path in package_index.iterdir(): packages[marker_path.name] = install_base return packages colcon-ros-0.4.1/colcon_ros/package_augmentation/000077500000000000000000000000001450756642100221125ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/package_augmentation/__init__.py000066400000000000000000000000001450756642100242110ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/package_augmentation/ros_ament_index.py000066400000000000000000000030171450756642100256430ustar00rootroot00000000000000# Copyright 2022 Open Source Robotics Foundation, Inc. # Licensed under the Apache License, Version 2.0 from colcon_core.dependency_descriptor import DependencyDescriptor from colcon_core.package_augmentation \ import PackageAugmentationExtensionPoint from colcon_core.plugin_system import satisfies_version class RosAmentIndexPackageAugmentation(PackageAugmentationExtensionPoint): """ Augment installed packages with information from an ament index. Only packages of the `installed` type are considered. """ # The priority needs to be higher than the extensions augmenting # 'installed' packages for the underlying package information sources # supported by ROS (colcon index and Python distributions) PRIORITY = 130 def __init__(self): # noqa: D107 super().__init__() satisfies_version( PackageAugmentationExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def augment_package( # noqa: D102 self, desc, *, additional_argument_names=None ): if desc.type != 'installed': return index_path = desc.path / 'share' / 'ament_index' / 'resource_index' marker_file = index_path / 'package_run_dependencies' / desc.name if not marker_file.is_file(): return with marker_file.open() as f: raw_deps = [d for d in f.read().split(';') if d] desc.type = 'installed.ros.ament' desc.dependencies['run'].update( DependencyDescriptor(dep) for dep in raw_deps) colcon-ros-0.4.1/colcon_ros/package_augmentation/ros_ament_python.py000066400000000000000000000041751450756642100260630ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_core.package_augmentation import PackageAugmentationExtensionPoint from colcon_core.package_identification.python import get_configuration from colcon_core.package_identification.python import is_reading_cfg_sufficient from colcon_core.plugin_system import satisfies_version from colcon_python_setup_py.package_identification.python_setup_py \ import get_setup_information class RosAmentPythonPackageAugmentation(PackageAugmentationExtensionPoint): """Augment ament_python packages with information from setup files.""" # the priority needs to be higher than the extensions augmenting packages # using the setup.py/setup.cfg files PRIORITY = 150 def __init__(self): # noqa: D107 super().__init__() satisfies_version( PackageAugmentationExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def augment_package( # noqa: D102 self, desc, *, additional_argument_names=None ): if desc.type != 'ros.ament_python': return if 'get_python_setup_options' in desc.metadata: return setup_py = desc.path / 'setup.py' if not setup_py.is_file(): return setup_cfg = desc.path / 'setup.cfg' for _ in (1, ): # try to get information from setup.cfg file if setup_cfg.is_file(): if is_reading_cfg_sufficient(setup_py): config = get_configuration(setup_cfg) name = config.get('metadata', {}).get('name') if name: options = config.get('options', {}) def getter(env): nonlocal options return options break else: # use information from setup.py file def getter(env): # noqa: F811 nonlocal desc return get_setup_information( desc.path / 'setup.py', env=env) desc.metadata['get_python_setup_options'] = getter colcon-ros-0.4.1/colcon_ros/package_identification/000077500000000000000000000000001450756642100224105ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/package_identification/__init__.py000066400000000000000000000000001450756642100245070ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/package_identification/ignore.py000066400000000000000000000024651450756642100242540ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_core.package_identification import IgnoreLocationException from colcon_core.package_identification \ import PackageIdentificationExtensionPoint from colcon_core.plugin_system import satisfies_version class IgnorePackageIdentification(PackageIdentificationExtensionPoint): """ Ignore paths containing files belonging to `catkin` or `ament_tools`. Workspaces built with `catkin` or `ament_tools` don't contain any IGNORE marker files in the install space. To avoid packages being found in these directories by the discovery extensions using default values they is being ignored by this extension. """ # the priority needs to be higher than all other extensions PRIORITY = 1000 def __init__(self): # noqa: D107 super().__init__() satisfies_version( PackageIdentificationExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def identify(self, desc): # noqa: D102 filenames = ( '_order_isolated_packages.py', # ament_tools '_setup_util.py', # catkin ) for filename in filenames: ignore = desc.path / filename if ignore.exists(): raise IgnoreLocationException() colcon-ros-0.4.1/colcon_ros/package_identification/ros.py000066400000000000000000000164431450756642100235750ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from colcon_core.dependency_descriptor import DependencyDescriptor from colcon_core.package_augmentation import PackageAugmentationExtensionPoint from colcon_core.package_identification import IgnoreLocationException from colcon_core.package_identification import logger from colcon_core.package_identification \ import PackageIdentificationExtensionPoint from colcon_core.plugin_system import satisfies_version from colcon_core.plugin_system import SkipExtensionException # mapping paths to tuples containing the ROS package and its build type _cached_packages = {} class RosPackageIdentification( PackageIdentificationExtensionPoint, PackageAugmentationExtensionPoint ): """Identify ROS packages with `package.xml` files.""" # the priority needs to be higher than the extensions identifying packages # using the build systems supported by ROS (CMake and Python) PRIORITY = 150 def __init__(self): # noqa: D107 satisfies_version( PackageIdentificationExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') satisfies_version( PackageAugmentationExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') # check if the necessary dependency to parse the manifest is available try: import catkin_pkg # noqa: F401 except ImportError: raise SkipExtensionException( "The Python module 'catkin_pkg' could not be imported, " 'therefore ROS packages can not be identified') def identify(self, desc): # noqa: D102 # ignore packages which have been identified with a different type if desc.type is not None and desc.type != 'ros': return # skip paths with an ignore marker file if (desc.path / 'CATKIN_IGNORE').exists(): raise IgnoreLocationException() if (desc.path / 'AMENT_IGNORE').exists(): raise IgnoreLocationException() # parse package manifest and get build type pkg, build_type = get_package_with_build_type(str(desc.path)) if not pkg or not build_type: # if it is not a wet ROS package check for a dry ROS package if (desc.path / 'manifest.xml').exists(): # ignore location to avoid being identified as a CMake package raise IgnoreLocationException() return desc.type = f'ros.{build_type}' # use package name from manifest if not already set # e.g. from external configuration if desc.name is None: desc.name = pkg.name def augment_package( # noqa: D102 self, desc, *, additional_argument_names=None ): if not desc.type.startswith('ros.'): return pkg, build_type = get_package_with_build_type(str(desc.path)) if not pkg: return desc.metadata['version'] = pkg.version # get dependencies for d in pkg.build_depends + pkg.buildtool_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['build'].add(DependencyDescriptor( d.name, metadata=_create_metadata(d))) for d in ( pkg.build_export_depends + pkg.buildtool_export_depends + pkg.exec_depends ): assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['run'].add(DependencyDescriptor( d.name, metadata=_create_metadata(d))) for d in pkg.test_depends: assert d.evaluated_condition is not None if d.evaluated_condition: desc.dependencies['test'].add(DependencyDescriptor( d.name, metadata=_create_metadata(d))) # get any maintainers which list an E-mail address maintainers = [str(m) for m in pkg.maintainers if m.email] if maintainers: desc.metadata.setdefault('maintainers', []) desc.metadata['maintainers'] += maintainers def augment_packages( # noqa: D102 self, descs, *, additional_argument_names=None ): super().augment_packages( descs, additional_argument_names=additional_argument_names) # get all parsed ROS package manifests global _cached_packages pkgs = {} for desc in descs: if str(desc.path) not in _cached_packages: continue pkg = _cached_packages[str(desc.path)][0] if pkg: pkgs[pkg] = desc metadata = { 'origin': 'ros', } # resolve group members and add them to the descriptor dependencies for pkg, desc in pkgs.items(): for group_depend in pkg.group_depends: assert group_depend.evaluated_condition is not None if not group_depend.evaluated_condition: continue group_depend.extract_group_members(pkgs) for name in group_depend.members: desc.dependencies['build'].add(DependencyDescriptor( name, metadata=metadata)) desc.dependencies['run'].add(DependencyDescriptor( name, metadata=metadata)) def get_package_with_build_type(path: str): """Get the ROS package and its build type for the given path.""" global _cached_packages if path not in _cached_packages: pkg = _get_package(path) build_type = _get_build_type(pkg, path) if pkg else None _cached_packages[path] = (pkg, build_type) return _cached_packages[path] def _get_package(path: str): """Get the ROS package for the given path.""" from catkin_pkg.package import has_ros_schema_reference from catkin_pkg.package import InvalidPackage from catkin_pkg.package import package_exists_at from catkin_pkg.package import parse_package if not package_exists_at(path): return None try: pkg = parse_package(path) except (AssertionError, InvalidPackage) as e: # noqa: F841 if has_ros_schema_reference(path): logger.debug( f"Found ROS schema reference in package manifest in '{path}'") logger.warn( f"Failed to parse ROS package manifest in '{path}': {e}") else: logger.debug( 'Failed to parse potential ROS package manifest in' f"'{path}': {e}") return None pkg.evaluate_conditions(os.environ) return pkg def _get_build_type(pkg, path): """Get the build type of the ROS package.""" from catkin_pkg.package import InvalidPackage try: return pkg.get_build_type() except InvalidPackage: logger.warning( f"ROS package '{pkg.name}' in '{path}' has more than one " 'build type') return None def _create_metadata(dependency): metadata = { 'origin': 'ros', } attributes = ( 'version_lte', 'version_lt', 'version_gte', 'version_gt', 'version_eq', ) for attr in attributes: if getattr(dependency, attr, None) is not None: metadata[attr] = getattr(dependency, attr) return metadata colcon-ros-0.4.1/colcon_ros/prefix_path/000077500000000000000000000000001450756642100202555ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/prefix_path/__init__.py000066400000000000000000000000001450756642100223540ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/prefix_path/ament.py000066400000000000000000000052771450756642100217460ustar00rootroot00000000000000# Copyright 2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.prefix_path import PrefixPathExtensionPoint logger = colcon_logger.getChild(__name__) _get_ament_prefix_path_warnings = set() class AmentPrefixPath(PrefixPathExtensionPoint): """Prefix path defined in the `AMENT_PREFIX_PATH` environment variable.""" # the priority needs to be lower than the colcon prefix path extension PRIORITY = 90 def __init__(self): # noqa: D107 super().__init__() satisfies_version( PrefixPathExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def extend_prefix_path(self, paths): # noqa: D102 global _get_ament_prefix_path_warnings ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH', '') for path in ament_prefix_path.split(os.pathsep): if not path: continue if not os.path.exists(path): if path not in _get_ament_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " "AMENT_PREFIX_PATH doesn't exist") _get_ament_prefix_path_warnings.add(path) continue for filename in os.listdir(path): if filename.startswith('local_setup.'): break else: parent_path = os.path.dirname(path) marker_file = os.path.join( parent_path, '.colcon_install_layout') if not os.path.exists(marker_file): if path not in _get_ament_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " "AMENT_PREFIX_PATH doesn't contain any " "'local_setup.*' files.") _get_ament_prefix_path_warnings.add(path) continue with open(marker_file, 'r') as h: install_layout = h.read().rstrip() if install_layout != 'isolated': if path not in _get_ament_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " "AMENT_PREFIX_PATH doesn't use the expected " "install layout 'isolated'.") _get_ament_prefix_path_warnings.add(path) continue path = parent_path paths.append(path) colcon-ros-0.4.1/colcon_ros/prefix_path/catkin.py000066400000000000000000000057251450756642100221110ustar00rootroot00000000000000# Copyright 2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.prefix_path import PrefixPathExtensionPoint logger = colcon_logger.getChild(__name__) _get_cmake_prefix_path_warnings = set() class CmakePrefixPath(PrefixPathExtensionPoint): """ Prefix path defined in the `CMAKE_PREFIX_PATH` environment variable. The path must contain a `.catkin` file to be considered. """ # the priority needs to be lower than the ament prefix path extension PRIORITY = 80 def __init__(self): # noqa: D107 super().__init__() satisfies_version( PrefixPathExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def extend_prefix_path(self, paths): # noqa: D102 global _get_cmake_prefix_path_warnings cmake_prefix_path = os.environ.get('CMAKE_PREFIX_PATH', '') for path in cmake_prefix_path.split(os.pathsep): if not path: continue if not os.path.exists(path): if path not in _get_cmake_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " "CMAKE_PREFIX_PATH doesn't exist") _get_cmake_prefix_path_warnings.add(path) continue if not os.path.exists(os.path.join(path, '.catkin')): continue for filename in os.listdir(path): if filename.startswith('local_setup.'): break else: parent_path = os.path.dirname(path) marker_file = os.path.join( parent_path, '.colcon_install_layout') if not os.path.exists(marker_file): if path not in _get_cmake_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " 'CMAKE_PREFIX_PATH seems to be a catkin workspace ' "but it doesn't contain any 'local_setup.*' files." ' Maybe the catkin version is not up-to-date?') _get_cmake_prefix_path_warnings.add(path) continue with open(marker_file, 'r') as h: install_layout = h.read().rstrip() if install_layout != 'isolated': if path not in _get_cmake_prefix_path_warnings: logger.warning( f"The path '{path}' in the environment variable " "CMAKE_PREFIX_PATH doesn't use the expected " "install layout 'isolated'.") _get_cmake_prefix_path_warnings.add(path) continue path = parent_path paths.append(path) colcon-ros-0.4.1/colcon_ros/task/000077500000000000000000000000001450756642100167065ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/__init__.py000066400000000000000000000057621450756642100210310ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os import warnings def add_app_to_cpp(env): """ Add AMENT_PREFIX_PATH to CMAKE_PREFIX_PATH. Each ament prefix path is inserted before the first catkin prefix path or at the end if none of the CMake prefix paths has a '.catkin' marker file. """ ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH') if ament_prefix_path: cmake_prefix_path = env.get('CMAKE_PREFIX_PATH') cpp = cmake_prefix_path.split(os.pathsep) if cmake_prefix_path else [] cpp_has_dot_catkin = [ os.path.exists(os.path.join(p, '.catkin')) for p in cpp] app = ament_prefix_path.split(os.pathsep) for p in app: if p not in cpp: try: index = cpp_has_dot_catkin.index(True) cpp.insert(index, p) cpp_has_dot_catkin.insert(index, False) except ValueError: cpp.append(p) env['CMAKE_PREFIX_PATH'] = os.pathsep.join(cpp) def append_app_to_cpp(env): """Append AMENT_PREFIX_PATH to CMAKE_PREFIX_PATH.""" warnings.warn( 'colcon_ros.task.append_app_to_cpp() will be removed in the future', DeprecationWarning, stacklevel=2) ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH') if ament_prefix_path: cmake_prefix_path = env.get('CMAKE_PREFIX_PATH') cpp = cmake_prefix_path.split(os.pathsep) if cmake_prefix_path else [] app = ament_prefix_path.split(os.pathsep) for p in app: if p not in cpp: cpp.append(p) env['CMAKE_PREFIX_PATH'] = os.pathsep.join(cpp) def extend_cpp_with_app(args): """Extend CMAKE_PREFIX_PATH with AMENT_PREFIX_PATH.""" warnings.warn( 'colcon_ros.task.extend_cpp_with_app() will be removed in the future', DeprecationWarning, stacklevel=2) ament_prefix_path = os.environ.get('AMENT_PREFIX_PATH') if ament_prefix_path: ament_prefix_path = ament_prefix_path.replace( os.pathsep, ';') if args.cmake_args is None: args.cmake_args = [] # check if the CMAKE_PREFIX_PATH is explicitly set prefix = '-DCMAKE_PREFIX_PATH=' for i, value in reversed(list(enumerate(args.cmake_args))): if not value.startswith(prefix): continue # extend the last existing entry existing = value[len(prefix):] if existing: existing = ';' + existing args.cmake_args[i] = \ f'-DCMAKE_PREFIX_PATH={ament_prefix_path}{existing}' break else: # otherwise extend the environment variable existing = os.environ.get('CMAKE_PREFIX_PATH', '') if existing: existing = ';' + existing.replace( os.pathsep, ';') args.cmake_args.append( f'-DCMAKE_PREFIX_PATH={ament_prefix_path}{existing}') colcon-ros-0.4.1/colcon_ros/task/ament_cmake/000077500000000000000000000000001450756642100211525ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/ament_cmake/__init__.py000066400000000000000000000000001450756642100232510ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/ament_cmake/build.py000066400000000000000000000063571450756642100226360ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from colcon_cmake.task.cmake import has_target from colcon_cmake.task.cmake.build import CmakeBuildTask from colcon_core.environment import create_environment_scripts from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.shell import get_shell_extensions from colcon_core.task import TaskExtensionPoint from colcon_ros.task import add_app_to_cpp logger = colcon_logger.getChild(__name__) class AmentCmakeBuildTask(TaskExtensionPoint): """Build ROS packages with the build type 'ament_cmake'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def add_arguments(self, *, parser): # noqa: D102 parser.add_argument( '--ament-cmake-args', nargs='*', metavar='*', type=str.lstrip, help="Pass arguments to 'ament_cmake' packages. " 'Arguments matching other options must be prefixed by a space') async def build(self): # noqa: D102 args = self.context.args logger.info( f"Building ROS package in '{args.path}' with build type " "'ament_cmake'") # reuse CMake build task with additional logic extension = CmakeBuildTask() extension.set_context(context=self.context) # additional arguments if args.test_result_base: if args.cmake_args is None: args.cmake_args = [] # ament_cmake appends the project name itself args.cmake_args.append( '-DAMENT_TEST_RESULTS_DIR=' + os.path.dirname(args.test_result_base)) if args.symlink_install: if args.cmake_args is None: args.cmake_args = [] args.cmake_args.append('-DAMENT_CMAKE_SYMLINK_INSTALL=1') if args.ament_cmake_args: if args.cmake_args is None: args.cmake_args = [] args.cmake_args += args.ament_cmake_args rc = await extension.build( skip_hook_creation=False, environment_callback=add_app_to_cpp) # if the build has failed getting targets might not be possible try: has_install_target = await has_target(args.build_base, 'install') except Exception: # noqa: B902 if not rc: raise has_install_target = False # add a hook for each available shell # only if the package has an install target additional_hooks = [] if has_install_target: shell_extensions = get_shell_extensions() file_extensions = [] for shell_extensions_same_prio in shell_extensions.values(): for shell_extension in shell_extensions_same_prio.values(): file_extensions += shell_extension.get_file_extensions() for ext in sorted(file_extensions): additional_hooks.append( f'share/{self.context.pkg.name}/local_setup.{ext}') create_environment_scripts( self.context.pkg, args, additional_hooks=additional_hooks) return rc colcon-ros-0.4.1/colcon_ros/task/ament_cmake/test.py000066400000000000000000000016541450756642100225110ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_cmake.task.cmake.test import CmakeTestTask from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.task import TaskExtensionPoint logger = colcon_logger.getChild(__name__) class AmentCmakeTestTask(TaskExtensionPoint): """Test ROS packages with the build type 'ament_cmake'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def test(self): # noqa: D102 args = self.context.args logger.info( f"Testing ROS package in '{args.path}' with build type " "'ament_cmake'") # reuse CMake test task extension = CmakeTestTask() extension.set_context(context=self.context) return await extension.test() colcon-ros-0.4.1/colcon_ros/task/ament_python/000077500000000000000000000000001450756642100214135ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/ament_python/__init__.py000066400000000000000000000000001450756642100235120ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/ament_python/build.py000066400000000000000000000101021450756642100230560ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from pathlib import Path import sys from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.shell import create_environment_hook from colcon_core.shell import get_command_environment from colcon_core.task import create_file from colcon_core.task import install from colcon_core.task import TaskExtensionPoint from colcon_core.task.python import get_data_files_mapping from colcon_core.task.python import get_setup_data from colcon_core.task.python.build import PythonBuildTask logger = colcon_logger.getChild(__name__) class AmentPythonBuildTask(TaskExtensionPoint): """Build ROS packages with the build type 'ament_python'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def build(self): # noqa: D102 args = self.context.args logger.info( f"Building ROS package in '{args.path}' with build type " "'ament_python'") # reuse Python build task with additional logic extension = PythonBuildTask() extension.set_context(context=self.context) # additional hooks additional_hooks = create_environment_hook( 'ament_prefix_path', Path(args.install_base), self.context.pkg.name, 'AMENT_PREFIX_PATH', '', mode='prepend') # get options from the Python manifest try: env = await get_command_environment( 'setup_py', args.build_base, self.context.dependencies) except RuntimeError as e: logger.error(str(e)) return 1 setup_py_data = get_setup_data(self.context.pkg, env) # check if the package index and manifest are being installed data_files = get_data_files_mapping( setup_py_data.get('data_files', []) or []) installs_package_index = False installs_package_manifest = False # check if package index and manifest are being installed for source, destination in data_files.items(): if sys.platform == 'win32': destination = Path(destination).as_posix() # work around data files incorrectly defined as not relative if os.path.isabs(source): source = os.path.relpath(source, args.path) if ( destination == 'share/ament_index/resource_index/packages/' + self.context.pkg.name ): installs_package_index = True elif ( source == 'package.xml' and destination == f'share/{self.context.pkg.name}/package.xml' ): installs_package_manifest = True # warn about missing explicit installation # for now implicitly install the marker and the manifest if not installs_package_index: # TODO remove magic helper in the future logger.warn( f"Package '{self.context.pkg.name}' doesn't explicitly " 'install a marker in the package index (colcon-ros currently ' 'does it implicitly but that fallback will be removed in the ' 'future)') # create package marker in ament resource index create_file( args, 'share/ament_index/resource_index/packages/' f'{self.context.pkg.name}') if not installs_package_manifest: # TODO remove magic helper in the future logger.warn( f"Package '{self.context.pkg.name}' doesn't explicitly " "install the 'package.xml' file (colcon-ros currently does it " 'implicitly but that fallback will be removed in the future)') # copy / symlink package manifest install( args, 'package.xml', f'share/{self.context.pkg.name}/package.xml') return await extension.build(additional_hooks=additional_hooks) colcon-ros-0.4.1/colcon_ros/task/ament_python/test.py000066400000000000000000000016611450756642100227500ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.task import TaskExtensionPoint from colcon_core.task.python.test import PythonTestTask logger = colcon_logger.getChild(__name__) class AmentPythonTestTask(TaskExtensionPoint): """Test ROS packages with the build type 'ament_python'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def test(self): # noqa: D102 args = self.context.args logger.info( f"Testing ROS package in '{args.path}' with build type" "'ament_python'") # reuse Python test task extension = PythonTestTask() extension.set_context(context=self.context) return await extension.test() colcon-ros-0.4.1/colcon_ros/task/catkin/000077500000000000000000000000001450756642100201575ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/catkin/__init__.py000066400000000000000000000026311450756642100222720ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_cmake.task.cmake import get_variable_from_cmake_cache from colcon_core.logging import colcon_logger from colcon_core.shell import create_environment_hook logger = colcon_logger.getChild(__name__) def create_pythonpath_environment_hook(build_base, install_base, pkg_name): """ Create a hook script for each primary shell to prepend to the PYTHONPATH. :param str build_base: The path of the build directory :param Path install_base: The path of the install prefix :param str pkg_name: The package name :returns: The relative paths to the created hook scripts :rtype: list """ hooks = [] # catkin packages might use `--install-layout deb` # therefore getting the used install directory from the CMake cache # since it might not match distutils.sysconfig.get_python_lib() rel_python_path = get_variable_from_cmake_cache( build_base, 'PYTHON_INSTALL_DIR') # prepend Python specific path to PYTHONPATH if it exists if rel_python_path: abs_python_path = install_base / rel_python_path logger.log(1, f"checking '{abs_python_path}'") if abs_python_path.exists(): hooks += create_environment_hook( 'catkin_pythonpath', install_base, pkg_name, 'PYTHONPATH', rel_python_path, mode='prepend') return hooks colcon-ros-0.4.1/colcon_ros/task/catkin/build.py000066400000000000000000000134361450756642100216370ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 from collections import OrderedDict from contextlib import suppress import os from pathlib import Path from colcon_cmake.task.cmake import has_target from colcon_cmake.task.cmake.build import CmakeBuildTask from colcon_core.environment import create_environment_scripts from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.shell import create_environment_hook from colcon_core.shell import get_shell_extensions from colcon_core.task import TaskExtensionPoint from colcon_ros.task.catkin import create_pythonpath_environment_hook from colcon_ros.task.cmake import create_pkg_config_path_environment_hooks logger = colcon_logger.getChild(__name__) class CatkinBuildTask(TaskExtensionPoint): """Build ROS packages with the build type 'catkin'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') def add_arguments(self, *, parser): # noqa: D102 parser.add_argument( '--catkin-cmake-args', nargs='*', metavar='*', type=str.lstrip, help="Pass arguments to 'catkin' packages. " 'Arguments matching other options must be prefixed by a space') parser.add_argument( '--catkin-skip-building-tests', action='store_true', help="By default the 'tests' target of 'catkin' packages is " "invoked. If running 'colcon test' later isn't intended this can " 'be skipped') async def build(self): # noqa: D102 args = self.context.args logger.info( f"Building ROS package in '{args.path}' with build type 'catkin'") # reuse CMake build task with additional logic extension = CmakeBuildTask() extension.set_context(context=self.context) # additional arguments if args.cmake_args is None: args.cmake_args = [] args.cmake_args += ['-DCATKIN_INSTALL_INTO_PREFIX_ROOT=0'] if args.test_result_base: # catkin appends the project name itself args.cmake_args.append( '-DCATKIN_TEST_RESULTS_DIR=' + os.path.dirname(args.test_result_base)) if args.symlink_install: args.cmake_args.append('-DCATKIN_SYMLINK_INSTALL=ON') if args.catkin_cmake_args: args.cmake_args += args.catkin_cmake_args # invoke the build additional_targets = [] # if no specific target is specified consider building the 'tests' # target and continue if such a target doesn't exist if args.cmake_target is None: if not args.catkin_skip_building_tests: additional_targets.append('tests') args.cmake_target_skip_unavailable = True rc = await extension.build( skip_hook_creation=True, additional_targets=additional_targets) # if the build has failed getting targets might not be possible try: has_install_target = await has_target(args.build_base, 'install') except Exception: # noqa: B902 if not rc: raise has_install_target = False # for catkin packages add additional hooks after the package has # been built and installed depending on the installed files # only if the package has an install target additional_hooks = [] if has_install_target: additional_hooks += create_environment_hook( 'ros_package_path', Path(args.install_base), self.context.pkg.name, 'ROS_PACKAGE_PATH', 'share', mode='prepend') additional_hooks += create_pythonpath_environment_hook( args.build_base, Path(args.install_base), self.context.pkg.name) additional_hooks += create_pkg_config_path_environment_hooks( Path(args.install_base), self.context.pkg.name) # register hooks created via catkin_add_env_hooks shell_extensions = get_shell_extensions() file_extensions = OrderedDict() for shell_extensions_same_prio in shell_extensions.values(): for shell_extension in shell_extensions_same_prio.values(): for file_ext in shell_extension.get_file_extensions(): file_extensions[file_ext] = shell_extension custom_hooks_path = Path(args.install_base) / \ 'share' / self.context.pkg.name / 'catkin_env_hook' for file_extension, shell_extension in file_extensions.items(): file_extension_hooks = sorted(custom_hooks_path.glob( f'*.{file_extension}')) if file_extension_hooks: # since not all shell extensions might implement this with suppress(NotImplementedError): # try to set CATKIN_ENV_HOOK_WORKSPACE explicitly # before sourcing these hooks additional_hooks.append( shell_extension.create_hook_set_value( 'catkin_env_hook_workspace', Path(args.install_base), self.context.pkg.name, 'CATKIN_ENV_HOOK_WORKSPACE', '')) additional_hooks += file_extension_hooks create_environment_scripts( self.context.pkg, args, additional_hooks=additional_hooks) # ensure that the install base has the marker file # identifying it as a catkin workspace marker = Path(args.install_base) / '.catkin' marker.touch(exist_ok=True) return rc colcon-ros-0.4.1/colcon_ros/task/catkin/test.py000066400000000000000000000037411450756642100215150ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from pathlib import Path from colcon_cmake.task.cmake.test import CmakeTestTask from colcon_core.environment import create_environment_scripts_only from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.shell import create_environment_hook from colcon_core.task import TaskExtensionPoint from colcon_ros.task.catkin import create_pythonpath_environment_hook logger = colcon_logger.getChild(__name__) class CatkinTestTask(TaskExtensionPoint): """Test ROS packages with the build type 'catkin'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def test(self): # noqa: D102 args = self.context.args logger.info( f"Testing ROS package in '{args.path}' with build type 'catkin'") # for catkin packages it is expected that the devel space # and not the install space is in the environment for testing self.context.dependencies[self.context.pkg.name] = \ os.path.join(self.context.args.build_base, 'devel') # additional hooks additional_hooks = create_environment_hook( 'ros_package_path', Path(args.build_base) / 'devel', self.context.pkg.name, 'ROS_PACKAGE_PATH', args.path, mode='prepend') additional_hooks += create_pythonpath_environment_hook( args.build_base, Path(args.build_base) / 'devel', self.context.pkg.name) # generate the necessary setup files for the devel space create_environment_scripts_only( Path(args.build_base) / 'devel', self.context.pkg, additional_hooks=additional_hooks) # reuse CMake test task extension = CmakeTestTask() extension.set_context(context=self.context) return await extension.test() colcon-ros-0.4.1/colcon_ros/task/cmake/000077500000000000000000000000001450756642100177665ustar00rootroot00000000000000colcon-ros-0.4.1/colcon_ros/task/cmake/__init__.py000066400000000000000000000033331450756642100221010ustar00rootroot00000000000000# Copyright 2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os import platform import subprocess from colcon_core.shell import create_environment_hook def create_pkg_config_path_environment_hooks(basepath, pkg_name): """ Create hook scripts to prepend to the PKG_CONFIG_PATH. :param Path basepath: The path of the prefix :param str pkg_name: The package name :returns: The relative paths to the created hook scripts :rtype: list """ hooks = create_environment_hook( 'pkg_config_path', basepath, pkg_name, 'PKG_CONFIG_PATH', os.path.join('lib', 'pkgconfig'), mode='prepend') if platform.system() == 'Linux': multiarch = get_multiarch() if multiarch: hooks += create_environment_hook( 'pkg_config_path_multiarch', basepath, pkg_name, 'PKG_CONFIG_PATH', os.path.join('lib', multiarch, 'pkgconfig'), mode='prepend') return hooks _multiarch = None def get_multiarch(): """ Get the multiarch name using normalized GNU triplets. :returns: The multiarch name or None :rtype: str """ global _multiarch if _multiarch is None: try: output = subprocess.check_output(['gcc', '-print-multiarch']) except subprocess.CalledProcessError: pass else: _multiarch = output.decode().rstrip() if _multiarch is None: try: output = subprocess.check_output( ['dpkg-architecture', '-qDEB_HOST_MULTIARCH']) except subprocess.CalledProcessError: pass else: _multiarch = output.decode().rstrip() return _multiarch colcon-ros-0.4.1/colcon_ros/task/cmake/build.py000066400000000000000000000035531450756642100214450ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from pathlib import Path from colcon_cmake.task.cmake import has_target from colcon_cmake.task.cmake.build import CmakeBuildTask as CmakeBuildTask_ from colcon_core.environment import create_environment_scripts from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.task import TaskExtensionPoint from colcon_ros.task import add_app_to_cpp from colcon_ros.task.cmake import create_pkg_config_path_environment_hooks logger = colcon_logger.getChild(__name__) class CmakeBuildTask(TaskExtensionPoint): """Build ROS packages with the build type 'cmake'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def build(self): # noqa: D102 args = self.context.args logger.info( f"Building ROS package in '{args.path}' with build type 'cmake'") # reuse CMake build task with additional logic extension = CmakeBuildTask_() extension.set_context(context=self.context) rc = await extension.build( skip_hook_creation=True, environment_callback=add_app_to_cpp) # if the build has failed getting targets might not be possible try: has_install_target = await has_target(args.build_base, 'install') except Exception: # noqa: B902 if not rc: raise has_install_target = False additional_hooks = [] if has_install_target: additional_hooks += create_pkg_config_path_environment_hooks( Path(args.install_base), self.context.pkg.name) create_environment_scripts( self.context.pkg, args, additional_hooks=additional_hooks) return rc colcon-ros-0.4.1/colcon_ros/task/cmake/test.py000066400000000000000000000016371450756642100213260ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from colcon_cmake.task.cmake.test import CmakeTestTask as CmakeTestTask_ from colcon_core.logging import colcon_logger from colcon_core.plugin_system import satisfies_version from colcon_core.task import TaskExtensionPoint logger = colcon_logger.getChild(__name__) class CmakeTestTask(TaskExtensionPoint): """Test ROS packages with the build type 'cmake'.""" def __init__(self): # noqa: D107 super().__init__() satisfies_version(TaskExtensionPoint.EXTENSION_POINT_VERSION, '^1.0') async def test(self): # noqa: D102 args = self.context.args logger.info( f"Testing ROS package in '{args.path}' with build type 'cmake'") # reuse CMake test task extension = CmakeTestTask_() extension.set_context(context=self.context) return await extension.test() colcon-ros-0.4.1/debian/000077500000000000000000000000001450756642100150265ustar00rootroot00000000000000colcon-ros-0.4.1/debian/patches/000077500000000000000000000000001450756642100164555ustar00rootroot00000000000000colcon-ros-0.4.1/debian/patches/setup.cfg.patch000066400000000000000000000016661450756642100214050ustar00rootroot00000000000000Description: Remove dependencies on catkin_pkg to avoid conflict with ROS 1 While the package is available from Debian this Debian package only depends on python3-catkin-pkg-modules to avoid the collision with the Python 2 version of the package. Otherwise being listed as a install_requires but not being installed would result in a runtime error by pkg_resources. Author: Dirk Thomas --- setup.cfg 2018-06-28 13:13:13.000000000 -0700 +++ setup.cfg.patched 2018-06-28 13:13:13.000000000 -0700 @@ -27,7 +27,11 @@ [options] python_requires = >=3.6 install_requires = - catkin_pkg>=0.4.14 + # to avoid conflicts with ROS 1 this Debian package only depends on + # python3-catkin-pkg-modules and not python3-catkin-pkg + # listing it here but not installing it in the Debian package + # would result in a runtime error by pkg_resources + # catkin_pkg>=0.4.14 colcon-cmake>=0.2.6 colcon-core>=0.7.0 colcon-pkg-config colcon-ros-0.4.1/publish-python.yaml000066400000000000000000000004671450756642100174640ustar00rootroot00000000000000artifacts: - type: wheel uploads: - type: pypi - type: stdeb uploads: - type: packagecloud config: repository: dirk-thomas/colcon distributions: - ubuntu:focal - ubuntu:jammy - debian:bullseye - debian:bookworm colcon-ros-0.4.1/setup.cfg000066400000000000000000000074031450756642100154310ustar00rootroot00000000000000[metadata] name = colcon-ros version = attr: colcon_ros.__version__ url = https://colcon.readthedocs.io project_urls = Changelog = https://github.com/colcon/colcon-ros/milestones?direction=desc&sort=due_date&state=closed GitHub = https://github.com/colcon/colcon-ros/ author = Dirk Thomas author_email = web@dirk-thomas.net maintainer = Dirk Thomas maintainer_email = web@dirk-thomas.net classifiers = Development Status :: 3 - Alpha Environment :: Plugins Intended Audience :: Developers License :: OSI Approved :: Apache Software License Operating System :: MacOS Operating System :: Microsoft :: Windows Operating System :: POSIX Programming Language :: Python Topic :: Software Development :: Build Tools license = Apache License, Version 2.0 description = Extension for colcon to support ROS packages. long_description = file: README.rst keywords = colcon [options] python_requires = >=3.6 install_requires = catkin_pkg>=0.4.14 colcon-cmake>=0.2.6 colcon-core>=0.7.0 # technically not a required dependency but "very common" for ROS 1 users colcon-pkg-config colcon-python-setup-py>=0.2.4 # technically not a required dependency but "very common" for ROS users colcon-recursive-crawl packages = find: zip_safe = true [options.extras_require] test = flake8>=3.6.0 flake8-blind-except flake8-builtins flake8-class-newline flake8-comprehensions flake8-deprecated flake8-docstrings flake8-import-order flake8-quotes pep8-naming pylint pytest pytest-cov scspell3k>=2.2 [tool:pytest] filterwarnings = error # Suppress deprecation warnings in other packages ignore:lib2to3 package is deprecated::scspell ignore:pkg_resources is deprecated as an API::flake8_import_order ignore:SelectableGroups dict interface is deprecated::flake8 ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated::pyreadline ignore:the imp module is deprecated in favour of importlib.*:DeprecationWarning ignore:the imp module is deprecated in favour of importlib.*:PendingDeprecationWarning junit_suite_name = colcon-ros [options.entry_points] colcon_argcomplete.argcomplete_completer = catkin_cmake_args = colcon_ros.argcomplete_completer.catkin_cmake_args:CatkinCmakeArgcompleteCompleter colcon_core.package_augmentation = ros = colcon_ros.package_identification.ros:RosPackageIdentification ros_ament_python = colcon_ros.package_augmentation.ros_ament_python:RosAmentPythonPackageAugmentation colcon_core.package_identification = ignore_ament_install = colcon_ros.package_identification.ignore:IgnorePackageIdentification ros = colcon_ros.package_identification.ros:RosPackageIdentification colcon_core.prefix_path = ament = colcon_ros.prefix_path.ament:AmentPrefixPath catkin = colcon_ros.prefix_path.catkin:CmakePrefixPath colcon_core.shell.find_installed_packages = ament = colcon_ros.installed_packages:AmentInstalledPackageFinder colcon_core.task.build = ros.ament_cmake = colcon_ros.task.ament_cmake.build:AmentCmakeBuildTask ros.ament_python = colcon_ros.task.ament_python.build:AmentPythonBuildTask ros.catkin = colcon_ros.task.catkin.build:CatkinBuildTask ros.cmake = colcon_ros.task.cmake.build:CmakeBuildTask colcon_core.task.test = ros.ament_cmake = colcon_ros.task.ament_cmake.test:AmentCmakeTestTask ros.ament_python = colcon_ros.task.ament_python.test:AmentPythonTestTask ros.catkin = colcon_ros.task.catkin.test:CatkinTestTask ros.cmake = colcon_ros.task.cmake.test:CmakeTestTask colcon_installed_package_information.package_augmentation = ros_ament_index = colcon_ros.package_augmentation.ros_ament_index:RosAmentIndexPackageAugmentation [flake8] import-order-style = google [coverage:run] source = colcon_ros colcon-ros-0.4.1/setup.py000066400000000000000000000036651450756642100153300ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 import os from setuptools import setup cmdclass = {} try: from stdeb.command.sdist_dsc import sdist_dsc except ImportError: pass else: class CustomSdistDebCommand(sdist_dsc): """Weird approach to apply the Debian patches during packaging.""" def run(self): # noqa: D102 from stdeb.command import sdist_dsc build_dsc = sdist_dsc.build_dsc def custom_build_dsc(*args, **kwargs): nonlocal build_dsc debinfo = self.get_debinfo() repackaged_dirname = \ debinfo.source + '-' + debinfo.upstream_version dst_directory = os.path.join( self.dist_dir, repackaged_dirname, 'debian', 'patches') os.makedirs(dst_directory, exist_ok=True) # read patch with open('debian/patches/setup.cfg.patch', 'r') as h: lines = h.read().splitlines() print( "writing customized patch '%s'" % os.path.join(dst_directory, 'setup.cfg.patch')) # write patch with modified path with open( os.path.join(dst_directory, 'setup.cfg.patch'), 'w' ) as h: for line in lines: if line.startswith('--- ') or line.startswith('+++ '): line = \ line[0:4] + repackaged_dirname + '/' + line[4:] h.write(line + '\n') with open(os.path.join(dst_directory, 'series'), 'w') as h: h.write('setup.cfg.patch\n') return build_dsc(*args, **kwargs) sdist_dsc.build_dsc = custom_build_dsc super().run() cmdclass['sdist_dsc'] = CustomSdistDebCommand setup(cmdclass=cmdclass) colcon-ros-0.4.1/stdeb.cfg000066400000000000000000000004601450756642100153660ustar00rootroot00000000000000[colcon-ros] No-Python2: Depends3: python3-catkin-pkg-modules (>= 0.4.14), python3-colcon-cmake (>= 0.2.6), python3-colcon-core (>= 0.7.0), python3-colcon-pkg-config, python3-colcon-python-setup-py (>= 0.2.4), python3-colcon-recursive-crawl Suite: focal jammy bullseye bookworm X-Python3-Version: >= 3.6 colcon-ros-0.4.1/test/000077500000000000000000000000001450756642100145635ustar00rootroot00000000000000colcon-ros-0.4.1/test/spell_check.words000066400000000000000000000006451450756642100201240ustar00rootroot00000000000000ament apache argcomplete basepath buildtool cmake colcon completers contextlib contraint dament dcatkin dcmake debian debinfo deps descs dpkg dsetuptools github https iterdir joinpath kislyuk linux lstrip multiarch nargs noqa pathlib pkgconfig pkgs plugin prepend pytest pythonpath relpath rosdistro rstrip rtype scspell sdist setuptools stacklevel stdeb symlink sysconfig tempfile thomas todo tuples unittest workspaces colcon-ros-0.4.1/test/test_ament_index_augmentation.py000066400000000000000000000037561450756642100232550ustar00rootroot00000000000000# Copyright 2022 Open Source Robotics Foundation, Inc. # Licensed under the Apache License, Version 2.0 from pathlib import Path from tempfile import TemporaryDirectory from colcon_core.package_descriptor import PackageDescriptor from colcon_ros.package_augmentation.ros_ament_index \ import RosAmentIndexPackageAugmentation def test_ament_index_augmentation(): with TemporaryDirectory() as mock_prefix: mock_prefix = Path(mock_prefix) index_path = mock_prefix / 'share' / 'ament_index' / 'resource_index' dep_index_path = index_path / 'package_run_dependencies' dep_index_path.mkdir(parents=True) (dep_index_path / 'pkg_no_deps').write_text('') (dep_index_path / 'pkg_with_deps').write_text('dep1;dep2') extension = RosAmentIndexPackageAugmentation() # Non-existing desc = PackageDescriptor(mock_prefix) desc.name = 'pkg_not_in_index' desc.type = 'installed' extension.augment_packages((desc,)) assert 'installed' == desc.type assert not desc.get_dependencies(categories=('run',)) # Wrong type desc = PackageDescriptor(mock_prefix) desc.name = 'pkg_with_deps' desc.type = 'installed.other_type' extension.augment_packages((desc,)) assert 'installed.other_type' == desc.type assert not desc.get_dependencies(categories=('run',)) # Existing with deps desc = PackageDescriptor(mock_prefix) desc.name = 'pkg_with_deps' desc.type = 'installed' extension.augment_packages((desc,)) assert 'installed.ros.ament' == desc.type assert {'dep1', 'dep2'} == desc.get_dependencies(categories=('run',)) # Existing without deps desc = PackageDescriptor(mock_prefix) desc.name = 'pkg_no_deps' desc.type = 'installed' extension.augment_packages((desc,)) assert 'installed.ros.ament' == desc.type assert not desc.get_dependencies(categories=('run',)) colcon-ros-0.4.1/test/test_copyright_license.py000066400000000000000000000024271450756642100217130ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from pathlib import Path import sys def test_copyright_license(): missing = check_files([Path(__file__).parents[1]]) assert not len(missing), \ 'In some files no copyright / license line was found' def check_files(paths): missing = [] for path in paths: if path.is_dir(): for p in sorted(path.iterdir()): if p.name.startswith('.'): continue if p.name.endswith('.py') or p.is_dir(): missing += check_files([p]) if path.is_file(): content = path.read_text() if not content: continue lines = content.splitlines() has_copyright = \ any(line for line in lines if line.startswith('# Copyright')) has_license = \ '# Licensed under the Apache License, Version 2.0' in lines if not has_copyright or not has_license: print( 'Could not find copyright / license in:', path, file=sys.stderr) missing .append(path) else: print('Found copyright / license in:', path) return missing colcon-ros-0.4.1/test/test_dependency_metadata.py000066400000000000000000000037641450756642100221640ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 from unittest.mock import Mock from colcon_ros.package_identification.ros import _create_metadata def test_version_contraint_eq(): mock = Mock() mock.version_gt = None mock.version_lt = None mock.version_lte = None mock.version_gte = None mock.version_eq = '1.1.1' metadata = _create_metadata(mock) assert len(metadata.keys()) == 2 assert metadata['version_eq'] == '1.1.1' def test_version_contraint_gt(): mock = Mock() mock.version_eq = None mock.version_lt = None mock.version_lte = None mock.version_gte = None mock.version_gt = '1.1.2' metadata = _create_metadata(mock) assert len(metadata.keys()) == 2 assert metadata['version_gt'] == '1.1.2' def test_version_contraint_lt(): mock = Mock() mock.version_eq = None mock.version_gt = None mock.version_lte = None mock.version_gte = None mock.version_lt = '1.1.5' metadata = _create_metadata(mock) assert len(metadata.keys()) == 2 assert metadata['version_lt'] == '1.1.5' def test_version_contraint_lte(): mock = Mock() mock.version_eq = None mock.version_gt = None mock.version_lt = None mock.version_gte = None mock.version_lte = '10.1.3' metadata = _create_metadata(mock) assert len(metadata.keys()) == 2 assert metadata['version_lte'] == '10.1.3' def test_version_contraint_gte(): mock = Mock() mock.version_eq = None mock.version_gt = None mock.version_lt = None mock.version_lte = None mock.version_gte = '10.45.5' metadata = _create_metadata(mock) assert len(metadata.keys()) == 2 assert metadata['version_gte'] == '10.45.5' def test_version_constraint_none(): mock = Mock() mock.version_eq = None mock.version_gt = None mock.version_lt = None mock.version_lte = None mock.version_gte = None metadata = _create_metadata(mock) assert len(metadata.keys()) == 1 colcon-ros-0.4.1/test/test_flake8.py000066400000000000000000000027751450756642100173610ustar00rootroot00000000000000# Copyright 2016-2018 Dirk Thomas # Licensed under the Apache License, Version 2.0 import logging from pathlib import Path import sys from flake8 import LOG from flake8.api.legacy import get_style_guide # avoid debug and info messages from flake8 internals LOG.setLevel(logging.WARN) def test_flake8(): style_guide = get_style_guide( extend_ignore=['D100', 'D104'], show_source=True, ) style_guide_tests = get_style_guide( extend_ignore=['D100', 'D101', 'D102', 'D103', 'D104', 'D105', 'D107'], show_source=True, ) stdout = sys.stdout sys.stdout = sys.stderr # implicitly calls report_errors() report = style_guide.check_files([ str(Path(__file__).parents[1] / 'colcon_ros'), ]) report_tests = style_guide_tests.check_files([ str(Path(__file__).parents[1] / 'test'), ]) sys.stdout = stdout total_errors = report.total_errors + report_tests.total_errors if total_errors: # pragma: no cover # output summary with per-category counts print() if report.total_errors: report._application.formatter.show_statistics(report._stats) if report_tests.total_errors: report_tests._application.formatter.show_statistics( report_tests._stats) print( 'flake8 reported {total_errors} errors' .format_map(locals()), file=sys.stderr) assert not total_errors, \ 'flake8 reported {total_errors} errors'.format_map(locals()) colcon-ros-0.4.1/test/test_spell_check.py000066400000000000000000000035161450756642100204550ustar00rootroot00000000000000# Copyright 2016-2019 Dirk Thomas # Licensed under the Apache License, Version 2.0 from pathlib import Path import pytest from scspell import Report from scspell import SCSPELL_BUILTIN_DICT from scspell import spell_check spell_check_words_path = Path(__file__).parent / 'spell_check.words' @pytest.fixture(scope='module') def known_words(): global spell_check_words_path return spell_check_words_path.read_text().splitlines() def test_spell_check(known_words): source_filenames = [Path(__file__).parents[1] / 'setup.py'] + \ list((Path(__file__).parents[1] / 'colcon_ros').glob('**/*.py')) + \ list((Path(__file__).parents[1] / 'test').glob('**/*.py')) for source_filename in sorted(source_filenames): print('Spell checking:', source_filename) # check all files report = Report(known_words) spell_check( [str(p) for p in source_filenames], base_dicts=[SCSPELL_BUILTIN_DICT], report_only=report, additional_extensions=[('', 'Python')]) unknown_word_count = len(report.unknown_words) assert unknown_word_count == 0, \ 'Found {unknown_word_count} unknown words: '.format_map(locals()) + \ ', '.join(sorted(report.unknown_words)) unused_known_words = set(known_words) - report.found_known_words unused_known_word_count = len(unused_known_words) assert unused_known_word_count == 0, \ '{unused_known_word_count} words in the word list are not used: ' \ .format_map(locals()) + ', '.join(sorted(unused_known_words)) def test_spell_check_word_list_order(known_words): assert known_words == sorted(known_words), \ 'The word list should be ordered alphabetically' def test_spell_check_word_list_duplicates(known_words): assert len(known_words) == len(set(known_words)), \ 'The word list should not contain duplicates'