Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
4fb10bc
docs: define cross-distribution sync workflow
Tobias-Fischer Aug 27, 2026
8e515e5
ci: harden shared source and build caches
Tobias-Fischer Aug 27, 2026
da373ce
build: identify the platform in gap reports
Tobias-Fischer Aug 27, 2026
d7cfa0b
vinca: sync released package seeds
Tobias-Fischer Aug 28, 2026
2040bb4
vinca: skip unreleased rolling dependency chains
Tobias-Fischer Aug 28, 2026
a7de6f9
patch: refresh async web server compatibility
Tobias-Fischer Aug 30, 2026
fe47200
Merge remote-tracking branch 'origin/main' into codex/cross-distro-sync
Tobias-Fischer Sep 2, 2026
dbc7bcf
sync: rolling config from lyrical/kilted cross-distro sync
Tobias-Fischer Sep 2, 2026
4a3838e
fix: build_gap_report ignores check_patches_clean_apply.py leftover a…
Tobias-Fischer Sep 4, 2026
a92cf0a
sync: cross-distro pinning/tooling sync (opencv5/protobuf/vtk pins, v…
Tobias-Fischer Sep 4, 2026
8244207
Merge remote-tracking branch 'origin/main' into codex/cross-distro-sync
Tobias-Fischer Sep 4, 2026
41314a9
style: sort vinca.yaml after merge
Tobias-Fischer Sep 4, 2026
5f33d50
fix: macOS build fixes across ~20 packages for opencv5/Qt6/Boost1.90/…
Tobias-Fischer Sep 4, 2026
55e65ce
fix: remaining macOS build failures (ublox-dgnss-node ODR, rclc_examp…
Tobias-Fischer Sep 4, 2026
2bf95eb
fix: switch rmf_websocket to standalone asio (Boost 1.90 dropped io_s…
Tobias-Fischer Sep 4, 2026
9d06906
feat: port missing robostack.yaml mappings and seeds from humble for …
Tobias-Fischer Sep 4, 2026
d677dff
fix: drop rqt_image_overlay seed, make two recipe hand-edits durable
Tobias-Fischer Sep 4, 2026
b79234d
fix: build_continue_on_failure used the wrong (empty) robostack-rolli…
Tobias-Fischer Sep 4, 2026
286c10d
fix: macOS build fixes for 5 packages + exclude mujoco_ros2_control o…
Tobias-Fischer Sep 4, 2026
d7d8d23
fix: bump pcl_conversions build number, disable strict channel priority
Tobias-Fischer Sep 4, 2026
fb38615
fix: cartographer_ros duplicate cartographer dependency + NodeInterfa…
Tobias-Fischer Sep 4, 2026
82bf8f4
fix: loosen 7 needlessly linux-only packages to build on macOS + syst…
Tobias-Fischer Sep 5, 2026
01b5f39
chore: port 8 remaining robostack.yaml mappings from humble/jazzy
Tobias-Fischer Sep 5, 2026
c39a289
fix: address PR #41's linux-64/linux-aarch64/win-64 CI failures
Tobias-Fischer Sep 5, 2026
969d915
fix: channel-priority in CI workflow, rmf_task cstdint, plotjuggler w…
Tobias-Fischer Sep 5, 2026
21853b1
fix: rmf_visualization_schedule websocketpp/asio, trim stale plotjugg…
Tobias-Fischer Sep 5, 2026
f72913e
fix: force fresh distro-mutex build, fix septentrio_gnss_driver tf2_r…
Tobias-Fischer Sep 5, 2026
e4bd46d
fix: webots_ros2_driver hardcoded Python 3.12 EXACT version match
Tobias-Fischer Sep 5, 2026
2cf7e6e
fix: septentrio_gnss_driver forced -std=c++17 breaks class_loader C++…
Tobias-Fischer Sep 5, 2026
247c5d2
fix: webots_ros2_control on_init override for hardware_interface >= 5.3
Tobias-Fischer Sep 5, 2026
97cbeb7
fix: mujoco_3d_lidar mjtnum.h renamed to mjtype.h upstream
Tobias-Fischer Sep 5, 2026
9226977
fix: mujoco_ros2_control_plugins missing libegl-devel host dependency
Tobias-Fischer Sep 5, 2026
c38da89
fix: force fresh libg2o build (stale mutex 0.19.* pin)
Tobias-Fischer Sep 5, 2026
f53f314
fix: drop unnecessary Qt5 host dep from libg2o, fixing rtabmap qt5/qt…
Tobias-Fischer Sep 5, 2026
1ede1f6
fix: libg2o win-64 link failure, csparse_extension exports no symbols
Tobias-Fischer Sep 5, 2026
2d68302
fix: bump libg2o build_number to 27, past stale cached qt-dependent b…
Tobias-Fischer Sep 5, 2026
10a5c12
fix: mavlink hardcoded /usr/bin/env and touch break on win-64
Tobias-Fischer Sep 5, 2026
97f5b7b
fix: libmavconn unconditional -Wcomment breaks MSVC
Tobias-Fischer Sep 5, 2026
e2d227b
fix: mavlink/libmavconn missing endian.h and pthread.h on win-64
Tobias-Fischer Sep 5, 2026
dd8712a
fix: force fresh cv_bridge build (stale mutex 0.19.* pin)
Tobias-Fischer Sep 5, 2026
9725a69
fix: skip rtabmap (OpenCV5 incompatible), bump mavlink past stale win…
Tobias-Fischer Sep 5, 2026
6bd12be
fix: mavlink msgmap.hpp also needs ssize_t on win-64, bump build_numb…
Tobias-Fischer Sep 5, 2026
6c8532a
fix: mavlink windows.h ERROR macro corrupts generated dialect enums
Tobias-Fischer Sep 5, 2026
d423631
fix: mavlink windows.h NO_ERROR macro also corrupts MAV_PARAM_ERROR enum
Tobias-Fischer Sep 5, 2026
dffc64d
fix: port rtabmap to OpenCV5, re-enable on rolling
Tobias-Fischer Sep 5, 2026
3c29603
fix: libmavconn Windows import-library export for downstream mavros
Tobias-Fischer Sep 5, 2026
5ba5388
fix: bump libmavconn build_number to bust stale win-64 CI cache
Tobias-Fischer Sep 5, 2026
6062502
fix: mavros/mavros_extras -Wcomment flag rejected by MSVC on win-64
Tobias-Fischer Sep 5, 2026
4334622
fix: undef Windows PASSTHROUGH macro before mavros's timesync_mode enum
Tobias-Fischer Sep 5, 2026
e893708
fix: mavros win-64 M_PI and fnmatch.h portability
Tobias-Fischer Sep 5, 2026
07a61d2
fix: mavros_plugins win-64 errno constants and clock_gettime
Tobias-Fischer Sep 5, 2026
c480dbb
fix: mavros win-64 missing mavros.lib import library
Tobias-Fischer Sep 5, 2026
375bcff
fix: scope mavros's win-64 WINDOWS_EXPORT_ALL_SYMBOLS to the mavros t…
Tobias-Fischer Sep 5, 2026
6f8af19
fix: explicitly export mavros's egm96_5 static data member on win-64
Tobias-Fischer Sep 5, 2026
23f0037
fix: mavros_extras win-64 M_PI undeclared identifier
Tobias-Fischer Sep 5, 2026
fd26978
fix: microstrain_inertial_driver win-64 min/max macro collision
Tobias-Fischer Sep 5, 2026
3830abf
fix: microstrain_inertial_driver win-64 unistd.h and localtime_r
Tobias-Fischer Sep 5, 2026
49877ed
fix: microstrain_inertial_driver win-64 -Wno-narrowing rejected by MSVC
Tobias-Fischer Sep 5, 2026
63aa028
fix: mocap4r2_control win-64 missing import library
Tobias-Fischer Sep 5, 2026
132cd94
fix: mocap4r2_dummy_driver win-64 missing import library
Tobias-Fischer Sep 5, 2026
aaf5540
fix: proactively export mocap4r2_marker_viz/mocap4r2_robot_gt librari…
Tobias-Fischer Sep 5, 2026
a1b8753
fix: motion_capture_tracking win-64 missing quat.lib import library
Tobias-Fischer Sep 5, 2026
f6cabe4
fix: proactively export vrpn/libmotioncapture/librigidbodytracker on …
Tobias-Fischer Sep 5, 2026
6c2c4ac
fix: qualisys_cpp_sdk win-64 missing import library
Tobias-Fischer Sep 5, 2026
83f1ea7
fix: libmotioncapture win-64 missing boost_filesystem.lib
Tobias-Fischer Sep 5, 2026
e486b17
fix: moveit_hybrid_planning win-64 __builtin_unreachable undeclared
Tobias-Fischer Sep 5, 2026
3421f83
fix: moveit_ros_perception win-64 __cplusplus and OpenCV5 pointPolygo…
Tobias-Fischer Sep 5, 2026
962b398
fix: py_binding_tools win-64 missing import library
Tobias-Fischer Sep 5, 2026
b3790fc
fix: moveit_task_constructor_core win-64 non-standard uint typedef
Tobias-Fischer Sep 5, 2026
8d87180
fix: moveit_task_constructor_core win-64 GCC-only __attribute__ usages
Tobias-Fischer Sep 5, 2026
691849b
fix: moveit_task_constructor_core win-64 missing fmt.lib
Tobias-Fischer Sep 5, 2026
70e18fb
fix: moveit_task_constructor_core win-64 missing ws2_32 for gethostname
Tobias-Fischer Sep 5, 2026
453146f
fix: moveit_task_constructor_core win-64 pybind11 header collision
Tobias-Fischer Sep 5, 2026
bcddef3
fix: moveit_task_constructor_capabilities win-64 missing fmt.lib
Tobias-Fischer Sep 5, 2026
b7e6642
fix: moveit_task_constructor_visualization win-64 missing import libs
Tobias-Fischer Sep 5, 2026
5881651
fix: mujoco_3d_lidar win-64 missing C++17 standard and M_PI
Tobias-Fischer Sep 5, 2026
5d09fcc
fix: exclude mujoco_ros2_control_plugins (and dependents) from win-64
Tobias-Fischer Sep 5, 2026
80383d0
fix: nlohmann_json_schema_validator_vendor win-64 git clone path length
Tobias-Fischer Sep 5, 2026
10cb636
fix: shorten nlohmann_json_schema_validator_vendor's ExternalProject …
Tobias-Fischer Sep 5, 2026
f687773
fix: nlohmann_json_schema_validator_vendor win-64 PATCH_COMMAND redirect
Tobias-Fischer Sep 5, 2026
50981f2
fix: nlohmann_json_schema_validator_vendor win-64 BUILD_COMMAND keywo…
Tobias-Fischer Sep 5, 2026
452aa0a
fix: nlohmann_json_schema_validator_vendor win-64 BUILD_COMMAND doubl…
Tobias-Fischer Sep 5, 2026
694edd1
fix: nlohmann_json_schema_validator_vendor win-64 BUILD_TESTS option …
Tobias-Fischer Sep 5, 2026
be78cdb
fix: ouster_ros win-64 libzip detection
Tobias-Fischer Sep 5, 2026
7cbe742
fix: ouster_ros win-64 spdlog bundled/external fmt collision
Tobias-Fischer Sep 5, 2026
36b27f6
fix: ouster_ros win-64 spdlog fmt collision via #undef, not CMake
Tobias-Fischer Sep 5, 2026
fcc99dd
fix: ouster_ros win-64 spdlog include-path priority, not macro
Tobias-Fischer Sep 5, 2026
6c2655c
fix: exclude ouster_ros from win-64 (pcl_io.lib not found)
Tobias-Fischer Sep 5, 2026
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
7 changes: 6 additions & 1 deletion .github/workflows/testpr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ jobs:
with:
frozen: true

- name: Disable git auto-maintenance in source caches
shell: bash -l {0}
run: |
git config --global maintenance.auto false

- name: Long paths workarounds for win-64
shell: bash -l {0}
if: matrix.platform == 'win-64'
Expand Down Expand Up @@ -138,7 +143,7 @@ jobs:
id: build-recipes
shell: bash -l {0}
run: |
pixi run rattler-build build --recipe-dir recipes --target-platform ${{ matrix.platform }} -m ./conda_build_config.yaml -c https://prefix.dev/conda-forge -c https://prefix.dev/robostack-rolling --skip-existing
pixi run rattler-build build --recipe-dir recipes --target-platform ${{ matrix.platform }} -m ./conda_build_config.yaml -c https://prefix.dev/conda-forge -c https://prefix.dev/robostack-rolling --skip-existing --channel-priority disabled

- name: See packages that will be saved in cache
shell: bash -l {0}
Expand Down
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ Rules:
- Run parallel lanes only for packages that do not depend on each other.
- If unsure, serialize the builds.

## Cross-distribution sync

- Work from the clean checked-out heads of rolling, lyrical, kilted, jazzy, and humble; create `codex/cross-distro-sync` in each repo and never merge their independent histories.
- Classify every candidate before editing: portable shared tooling/CI/metadata, conditional package fix requiring a compatible source and refreshed patch, or excluded distro-owned state.
- Keep rosdistro snapshots, mutex/build numbers, ABI/compiler/Python pins, channels/upload targets, package selection, generated recipes, and temporary rebuild controls distro-owned.
- Port patches only for an existing compatible package, using `patch/ros-$DISTRO-<pkg>.patch` and matching recipe wiring; do not copy a patch solely because its filename exists elsewhere.
- Validate changed patch metadata with `pixi run check-patches` and each changed package with `pixi run build-one ros-$DISTRO-<pkg>`; inspect final diffs for protected state.

## Inspect a built conda package

```bash
Expand Down
246 changes: 231 additions & 15 deletions build_gap_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,43 @@
"""Report gaps between generated recipes and built conda artifacts.

Default behavior is platform-agnostic: it inspects all output/<platform> folders that
contain conda artifacts and reports gaps per platform.
contain conda artifacts and reports gaps per platform. Only artifacts built with the
CURRENT build_number (and, for the mutex package, its own build_number) are counted —
older-build_number leftovers from a previous full rebuild are ignored, since counting
them makes the report claim far more packages are done than the current build actually
has.
"""

from __future__ import annotations

import argparse
import re
from pathlib import Path
from typing import Iterable, Set

CONDA_SUFFIX = ".conda"
TARBZ2_SUFFIX = ".tar.bz2"

# Matches known conda platform directory names (osx-arm64, linux-64, win-64, …)
_PLATFORM_RE = re.compile(r'^(osx|linux|win|emscripten)-')

# Strips distro prefix so ros-jazzy-rclcpp, ros2-rclcpp, ros-kilted-rclcpp all
# normalise to "rclcpp" for cross-naming-style comparison.
# Handles two forms: ros-<word>-<base> and ros<digits>-<base>
_DISTRO_PREFIX_RE = re.compile(r'^(?:ros-[a-z]+-|ros\d+-)')

# check_patches_clean_apply.py builds throwaway "<pkg>-check-patches[-<platform>]"
# packages into this same output/<platform> folder to verify patches apply (the
# platform suffix was added later; older leftover artifacts may lack it). They
# never have a matching recipes/ directory and would otherwise show up as false
# "built but no recipe" gaps.
_CHECK_PATCHES_RE = re.compile(r'-check-patches(?:-(?:linux|osx|win|emscripten|any))?$')

_TOP_LEVEL_BUILD_NUMBER_RE = re.compile(r'^build_number:\s*(\d+)\s*$')
_MUTEX_HEADER_RE = re.compile(r'^mutex_package:\s*$')
_MUTEX_NAME_RE = re.compile(r'^\s+name:\s*"?([\w.-]+)"?\s*$')
_MUTEX_BUILD_NUMBER_RE = re.compile(r'^\s+build_number:\s*(\d+)\s*$')


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -41,9 +66,38 @@ def parse_args() -> argparse.Namespace:
"If omitted, all detected platform folders are inspected."
),
)
parser.add_argument(
"--vinca-yaml",
default="vinca.yaml",
help="vinca.yaml to read the current build_number/mutex from (default: vinca.yaml)",
)
parser.add_argument(
"--build-number",
type=int,
default=None,
help="Override the build_number to filter artifacts by (default: parsed from --vinca-yaml)",
)
parser.add_argument(
"--any-build-number",
action="store_true",
help="Don't filter by build_number at all (count every artifact regardless of age)",
)
parser.add_argument(
"--pkg-additional-info",
default="pkg_additional_info.yaml",
help=(
"pkg_additional_info.yaml to read per-package build_number overrides from "
"(default: pkg_additional_info.yaml)"
),
)
return parser.parse_args()


def normalize_name(name: str) -> str:
"""Strip ros-<distro>- / ros2- prefix for cross-naming-style comparison."""
return _DISTRO_PREFIX_RE.sub("", name)


def is_conda_artifact(filename: str) -> bool:
return filename.endswith(CONDA_SUFFIX) or filename.endswith(TARBZ2_SUFFIX)

Expand All @@ -60,7 +114,109 @@ def package_name_from_artifact(filename: str) -> str | None:
parts = stem.rsplit("-", 2)
if len(parts) != 3:
return None
return parts[0]
name = parts[0]
if _CHECK_PATCHES_RE.search(name):
return None
return name


def build_number_from_artifact(filename: str) -> int | None:
"""Extract the trailing _<N> build number from a conda artifact's build string."""
stem = filename
if stem.endswith(CONDA_SUFFIX):
stem = stem[: -len(CONDA_SUFFIX)]
elif stem.endswith(TARBZ2_SUFFIX):
stem = stem[: -len(TARBZ2_SUFFIX)]
else:
return None

parts = stem.rsplit("-", 2)
if len(parts) != 3:
return None
build_string = parts[2]
suffix = build_string.rsplit("_", 1)[-1]
return int(suffix) if suffix.isdigit() else None


def read_vinca_config(vinca_yaml: Path) -> tuple[int | None, str | None, int | None]:
"""Parse (build_number, mutex_package_name, mutex_build_number) out of vinca.yaml
without requiring a YAML library, since this script has no other dependencies."""
if not vinca_yaml.is_file():
return None, None, None

build_number: int | None = None
mutex_name: str | None = None
mutex_build_number: int | None = None
in_mutex_block = False

for line in vinca_yaml.read_text().splitlines():
if in_mutex_block:
if line.startswith((" ", "\t")):
m = _MUTEX_NAME_RE.match(line)
if m:
mutex_name = m.group(1)
m = _MUTEX_BUILD_NUMBER_RE.match(line)
if m:
mutex_build_number = int(m.group(1))
continue
in_mutex_block = False # fall through: this line starts the next top-level key

m = _TOP_LEVEL_BUILD_NUMBER_RE.match(line)
if m:
build_number = int(m.group(1))
continue
if _MUTEX_HEADER_RE.match(line):
in_mutex_block = True

return build_number, mutex_name, mutex_build_number


_PKG_INFO_TOP_LEVEL_KEY_RE = re.compile(r'^([A-Za-z0-9_.]+):\s*(?:#.*)?$')
_PKG_INFO_BUILD_NUMBER_RE = re.compile(r'^\s+build_number:\s*(\d+)\s*$')


def read_pkg_build_number_overrides(pkg_info_yaml: Path) -> dict[str, int]:
"""Parse per-package `build_number:` overrides out of pkg_additional_info.yaml —
a surgical way to force a rebuild of just one package without bumping vinca.yaml's
global build_number for everything. Keyed by the ROS package name as written there
(underscores), same convention as normalize_name(...).replace('-', '_')."""
overrides: dict[str, int] = {}
if not pkg_info_yaml.is_file():
return overrides

current_key: str | None = None
for line in pkg_info_yaml.read_text().splitlines():
if not line.strip() or line.lstrip().startswith("#"):
continue
if not line[0].isspace():
m = _PKG_INFO_TOP_LEVEL_KEY_RE.match(line)
current_key = m.group(1) if m else None
continue
if current_key is None:
continue
m = _PKG_INFO_BUILD_NUMBER_RE.match(line)
if m:
overrides[current_key] = int(m.group(1))

return overrides


def expected_build_number(
norm_name: str,
build_number: int | None,
mutex_norm_name: str | None,
mutex_build_number: int | None,
pkg_build_number_overrides: dict[str, int],
) -> int | None:
"""The build_number a package's artifact must carry to count as "current":
the mutex's own build_number for the mutex package, a per-package override from
pkg_additional_info.yaml if one exists for it, otherwise the global build_number."""
if mutex_norm_name is not None and norm_name == mutex_norm_name and mutex_build_number is not None:
return mutex_build_number
override = pkg_build_number_overrides.get(norm_name.replace("-", "_"))
if override is not None:
return override
return build_number


def discover_platform_dirs(output_root: Path) -> list[str]:
Expand All @@ -71,6 +227,8 @@ def discover_platform_dirs(output_root: Path) -> list[str]:
for child in sorted(output_root.iterdir()):
if not child.is_dir():
continue
if not _PLATFORM_RE.match(child.name):
continue
try:
has_artifact = any(
entry.is_file() and is_conda_artifact(entry.name)
Expand All @@ -84,7 +242,14 @@ def discover_platform_dirs(output_root: Path) -> list[str]:
return platforms


def built_packages_for_platform(output_root: Path, platform: str) -> Set[str]:
def built_packages_for_platform(
output_root: Path,
platform: str,
build_number: int | None,
mutex_norm_name: str | None,
mutex_build_number: int | None,
pkg_build_number_overrides: dict[str, int],
) -> Set[str]:
platform_dir = output_root / platform
packages: Set[str] = set()
if not platform_dir.exists() or not platform_dir.is_dir():
Expand All @@ -94,8 +259,19 @@ def built_packages_for_platform(output_root: Path, platform: str) -> Set[str]:
if not artifact.is_file() or not is_conda_artifact(artifact.name):
continue
package_name = package_name_from_artifact(artifact.name)
if package_name:
packages.add(package_name)
if not package_name:
continue
norm_name = normalize_name(package_name)

if build_number is not None:
artifact_build_number = build_number_from_artifact(artifact.name)
expected = expected_build_number(
norm_name, build_number, mutex_norm_name, mutex_build_number, pkg_build_number_overrides
)
if artifact_build_number != expected:
continue

packages.add(norm_name)

return packages

Expand Down Expand Up @@ -132,22 +308,62 @@ def main() -> int:
)
return 1

if args.any_build_number:
build_number, mutex_name, mutex_build_number = None, None, None
pkg_build_number_overrides: dict[str, int] = {}
else:
build_number, mutex_name, mutex_build_number = read_vinca_config(Path(args.vinca_yaml))
if args.build_number is not None:
build_number = args.build_number
if build_number is None:
print(
f"Warning: could not read build_number from {args.vinca_yaml} "
"(pass --build-number or --any-build-number) — counting artifacts "
"from every build_number, including stale ones from earlier rebuilds.\n"
)
pkg_build_number_overrides = read_pkg_build_number_overrides(Path(args.pkg_additional_info))
mutex_norm_name = normalize_name(mutex_name) if mutex_name else None

if build_number is not None:
mutex_note = (
f", mutex build_number {mutex_build_number}" if mutex_build_number is not None else ""
)
override_note = (
f", {len(pkg_build_number_overrides)} per-package override(s) from {args.pkg_additional_info}"
if pkg_build_number_overrides
else ""
)
print(f"Filtering to build_number {build_number}{mutex_note}{override_note}\n")

for idx, platform in enumerate(selected_platforms):
built = built_packages_for_platform(output_root, platform)
built = built_packages_for_platform(
output_root, platform, build_number, mutex_norm_name, mutex_build_number, pkg_build_number_overrides
)

# Normalize recipe names for comparison so ros-jazzy-X and ros2-X match.
# Iterate in sorted (not set-hash) order so the displayed name for a
# dual-named package is deterministic across runs, not whichever of the
# two happens to come last per Python's randomized set iteration order —
# "ros2-X" sorts after "ros-<distro>-X" (- < digit in ASCII) so the
# shared ros2- convention consistently wins when both exist.
norm_to_recipe: dict[str, str] = {normalize_name(r): r for r in sorted(recipes)}
norm_recipes = set(norm_to_recipe)

print(f"Platform: {platform}")
print_list(
"Built package artifacts without matching recipe directory",
built - recipes,
)
extra_norm = built - norm_recipes
extra_display = sorted(extra_norm)
print(f"Built package artifacts without matching recipe directory: {len(extra_display)}")
for name in extra_display:
print(f" - {name}")
print()
missing = recipes - built
missing_norm = norm_recipes - built
missing_display = sorted(norm_to_recipe[n] for n in missing_norm)
print(
f"Recipe directories without built artifact on this platform: "
f"{len(missing)} out of {len(recipes)}"
f"Recipe directories without built artifact on {platform} platform: "
f"{len(missing_display)} out of {len(norm_recipes)}"
)
if missing:
for recipe in sorted(missing):
if missing_display:
for recipe in missing_display:
print(f" - {recipe}")

if idx != len(selected_platforms) - 1:
Expand Down
Loading