From 24ba9d33bedc9401b8884915ed10e8d91933c88f Mon Sep 17 00:00:00 2001 From: Aljoscha Schmidt Date: Fri, 8 Aug 2025 21:14:24 +0200 Subject: [PATCH 1/3] Detect missing test dependencies --- .../package_xml_validator.py | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/package_xml_validation/package_xml_validator.py b/package_xml_validation/package_xml_validator.py index 23e5fb9..ea2dd74 100644 --- a/package_xml_validation/package_xml_validator.py +++ b/package_xml_validation/package_xml_validator.py @@ -176,44 +176,53 @@ def extract_launch_deps(folder_names: List[str]) -> List[str]: launch_deps.extend(scan_files(launch_dir)) return launch_deps - launch_folder_names = ["launch", "components"] - launch_deps = extract_launch_deps(launch_folder_names) - if not launch_deps: - self.logger.debug( - f"No launch dependencies found in {package_name}/package.xml." - ) - return True - - missing_deps = [ - dep for dep in launch_deps if dep not in exec_deps and dep != package_name - ] - if missing_deps: - sep = "\n\t - " - self.logger.warning( - f"Missing launch dependencies in {package_name}/package.xml: {sep}{sep.join(missing_deps)}" - ) - - if self.check_only: - return False - else: - self.logger.info( - f"Auto-filling {len(missing_deps)} missing launch dependencies in {package_name}/package.xml." + def validate_launch_folders(launch_folder_names: List[str], depend_tag) -> bool: + launch_deps = extract_launch_deps(launch_folder_names) + if not launch_deps: + self.logger.debug( + f"No launch dependencies found in {package_name}/package.xml." ) - # before adding dependencies make sure they are valid rosdeps - if self.check_rosdeps: - invalid_deps = self.rosdep_validator.check_rosdeps_and_local_pkgs( - missing_deps + return True + + missing_deps = [ + dep + for dep in launch_deps + if dep not in exec_deps and dep != package_name + ] + if missing_deps: + sep = "\n\t - " + self.logger.warning( + f"Missing <{depend_tag}> dependencies in {package_name}/package.xml: {sep}{sep.join(missing_deps)}" + ) + + if self.check_only: + return False + else: + self.logger.info( + f"Auto-filling {len(missing_deps)} missing <{depend_tag}> dependencies in {package_name}/package.xml." ) - valid_deps = [d for d in missing_deps if d not in invalid_deps] - if invalid_deps: - self.logger.error( - f"Cannot auto-fill invalid launch dependencies: {', '.join(invalid_deps)}" + # before adding dependencies make sure they are valid rosdeps + if self.check_rosdeps: + invalid_deps = ( + self.rosdep_validator.check_rosdeps_and_local_pkgs( + missing_deps + ) ) - return False - missing_deps = valid_deps - self.formatter.add_dependencies(root, missing_deps, "exec_depend") - return False - return True + valid_deps = [d for d in missing_deps if d not in invalid_deps] + if invalid_deps: + self.logger.error( + f"Cannot auto-fill invalid launch dependencies: {', '.join(invalid_deps)}" + ) + return False + missing_deps = valid_deps + self.formatter.add_dependencies(root, missing_deps, depend_tag) + return False + return True + + launch_folder_names = ["launch", "components"] + launch_deps_valid = validate_launch_folders(launch_folder_names, "exec_depend") + test_deps_valid = validate_launch_folders(["test"], "test_depend") + return launch_deps_valid and test_deps_valid def validate_ament_exports(self, root, xml_file: str): """Validate ament_export tags in the package.xml file. From a0e48b29f3330e704e275b9de1931f3388c579aa Mon Sep 17 00:00:00 2001 From: Aljoscha Schmidt Date: Fri, 8 Aug 2025 21:21:52 +0200 Subject: [PATCH 2/3] Updated Readme --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index b5c1e01..1125608 100644 --- a/Readme.md +++ b/Readme.md @@ -14,6 +14,7 @@ Validates and formats `package.xml` files to enforce consistency and ROS 2 schem - Launch-File Dependency Validation - Scans Python (.py), YAML (.yaml/.yml), and XML (.xml) launch files for package references - validates and corrects that all referenced pkgs are declared in the package xml (as `` or ``) + - similarly the test folder is parsed to extract missing `` dependencies - Rosdep Key Checking - verifies that all declared pkgs exist as rodsdep key (optional) - CMakeFile Comparison and Synchronization From ed6b4b33f2d2716acd92220297d8c906e7d50ca2 Mon Sep 17 00:00:00 2001 From: Aljoscha Schmidt Date: Fri, 8 Aug 2025 21:28:33 +0200 Subject: [PATCH 3/3] Bugifx --- .../helpers/pkg_xml_formatter.py | 2 +- .../package_xml_validator.py | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/package_xml_validation/helpers/pkg_xml_formatter.py b/package_xml_validation/helpers/pkg_xml_formatter.py index 7c20938..f035ece 100644 --- a/package_xml_validation/helpers/pkg_xml_formatter.py +++ b/package_xml_validation/helpers/pkg_xml_formatter.py @@ -363,7 +363,7 @@ def retrieve_build_dependencies(self, root): def retrieve_test_dependencies(self, root): """Retrieve all test dependencies from the XML file.""" test_dependencies = [] - test_deps = ["test_depend"] + test_deps = ["test_depend", "depend"] for elem in root: if isinstance(elem.tag, str) and elem.tag in test_deps and elem.text: test_dependencies.append(elem.text.strip()) diff --git a/package_xml_validation/package_xml_validator.py b/package_xml_validation/package_xml_validator.py index ea2dd74..78266c8 100644 --- a/package_xml_validation/package_xml_validator.py +++ b/package_xml_validation/package_xml_validator.py @@ -163,7 +163,12 @@ def validate_xml_with_xmllint(self, xml_file): return False def validate_launch_dependencies( - self, root, package_xml_file: str, package_name: str, exec_deps: List[str] + self, + root, + package_xml_file: str, + package_name: str, + exec_deps: List[str], + test_deps: List[str] = [], ): """Validate launch dependencies in the package.xml file.""" @@ -176,7 +181,9 @@ def extract_launch_deps(folder_names: List[str]) -> List[str]: launch_deps.extend(scan_files(launch_dir)) return launch_deps - def validate_launch_folders(launch_folder_names: List[str], depend_tag) -> bool: + def validate_launch_folders( + launch_folder_names: List[str], xml_deps: List[str], depend_tag: str + ) -> bool: launch_deps = extract_launch_deps(launch_folder_names) if not launch_deps: self.logger.debug( @@ -187,7 +194,7 @@ def validate_launch_folders(launch_folder_names: List[str], depend_tag) -> bool: missing_deps = [ dep for dep in launch_deps - if dep not in exec_deps and dep != package_name + if dep not in xml_deps and dep != package_name ] if missing_deps: sep = "\n\t - " @@ -220,8 +227,10 @@ def validate_launch_folders(launch_folder_names: List[str], depend_tag) -> bool: return True launch_folder_names = ["launch", "components"] - launch_deps_valid = validate_launch_folders(launch_folder_names, "exec_depend") - test_deps_valid = validate_launch_folders(["test"], "test_depend") + launch_deps_valid = validate_launch_folders( + launch_folder_names, exec_deps, "exec_depend" + ) + test_deps_valid = validate_launch_folders(["test"], test_deps, "test_depend") return launch_deps_valid and test_deps_valid def validate_ament_exports(self, root, xml_file: str): @@ -368,6 +377,7 @@ def check_and_format_files(self, package_xml_files): xml_file, self.formatter.get_package_name(root), self.formatter.retrieve_exec_dependencies(root), + self.formatter.retrieve_test_dependencies(root), ) self.perform_check(