From 2ddce7e4a8a6a93c2e086a71d828f9e04b560e78 Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Mon, 31 Aug 2026 00:46:02 +0800 Subject: [PATCH 1/5] fix(hiroz-msgs): resolve msg assets via hiroz-codegen API, allow publish build.rs reached outside its own crate directory to find hiroz-codegen's .msg/.srv assets, which cargo package/publish cannot bundle. Use hiroz_codegen::bundled_assets_dir instead, which resolves against hiroz-codegen's own CARGO_MANIFEST_DIR and works whether hiroz-codegen is a path or crates.io dependency. Also add the crates.io metadata hiroz/hiroz-codegen already carry, pin versions on the path dependencies cargo publish requires, and remove publish = false so hiroz-msgs can be published. Update the custom-messages doc example to use version dependencies instead of git. --- crates/hiroz-msgs/Cargo.toml | 11 +++++-- crates/hiroz-msgs/build.rs | 48 +++++++++++++++--------------- docs/user-guide/custom-messages.md | 8 +++-- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/crates/hiroz-msgs/Cargo.toml b/crates/hiroz-msgs/Cargo.toml index 4432dbda4..2f8b089bd 100644 --- a/crates/hiroz-msgs/Cargo.toml +++ b/crates/hiroz-msgs/Cargo.toml @@ -2,10 +2,15 @@ name = "hiroz-msgs" version.workspace = true edition.workspace = true -publish = false +description = "ROS 2 message, service, and action types for hiroz, generated at build time" +license.workspace = true +repository.workspace = true +homepage.workspace = true +keywords = ["ros2", "zenoh", "robotics", "messages", "codegen"] +categories = ["network-programming", "science::robotics"] [dependencies] -hiroz = { path = "../hiroz", default-features = false, features = [ +hiroz = { version = "0.2", path = "../hiroz", default-features = false, features = [ "rmw-zenoh", ] } serde = { workspace = true } @@ -17,7 +22,7 @@ zenoh-buffers = { workspace = true } prost = { workspace = true, optional = true } pyo3 = { workspace = true, optional = true } -hiroz-derive = { path = "../hiroz-derive", optional = true } +hiroz-derive = { version = "0.2", path = "../hiroz-derive", optional = true } hiroz-cdr = { workspace = true } byteorder = { workspace = true } bytemuck = { version = "1", features = ["derive"] } diff --git a/crates/hiroz-msgs/build.rs b/crates/hiroz-msgs/build.rs index 66d1e4158..d8563c756 100644 --- a/crates/hiroz-msgs/build.rs +++ b/crates/hiroz-msgs/build.rs @@ -7,17 +7,6 @@ use hiroz_codegen::python_msgspec_generator; fn main() -> Result<()> { let out_dir = PathBuf::from(env::var("OUT_DIR")?); - // .msg/.srv definitions live in hiroz-codegen/assets/, outside this crate, - // so cargo won't rerun codegen on edits there unless we track them explicitly. - let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); - if let Some(codegen_assets) = manifest_dir - .parent() - .map(|p| p.join("hiroz-codegen/assets")) - && codegen_assets.exists() - { - println!("cargo:rerun-if-changed={}", codegen_assets.display()); - } - // Declare custom cfg for ROS version detection println!("cargo:rustc-check-cfg=cfg(ros_humble)"); @@ -40,6 +29,15 @@ fn main() -> Result<()> { // Detect ROS version and emit cfg let is_humble = detect_ros_version(); + // Message assets are vendored inside the published hiroz-codegen crate (not + // a sibling path in this crate's own directory), so ask hiroz-codegen for its + // own bundled-assets location. This resolves correctly whether hiroz-codegen + // is consumed via path (workspace) or from crates.io. + let codegen_assets = hiroz_codegen::bundled_assets_dir(is_humble); + if codegen_assets.exists() { + println!("cargo:rerun-if-changed={}", codegen_assets.display()); + } + // Discover ROS packages let ros_packages = discover_ros_packages(is_humble)?; @@ -171,8 +169,8 @@ fn discover_ros_packages(is_humble: bool) -> Result> { // This ensures our bundled message definitions are always used consistently, // avoiding issues with system packages that may have different versions or // hardcoded paths from Nix wrapProgram. - println!("cargo:info=Checking local bundled assets from hiroz-codegen/assets/jazzy"); - let local_asset_packages = discover_local_assets(&all_packages)?; + println!("cargo:info=Checking local bundled assets from hiroz-codegen's bundled assets"); + let local_asset_packages = discover_local_assets(&all_packages, is_humble)?; let local_count = local_asset_packages.len(); for pkg_path in local_asset_packages { if let Ok(name) = discover_package_name_from_path(&pkg_path) { @@ -229,7 +227,7 @@ fn discover_ros_packages(is_humble: bool) -> Result> { if !still_missing.is_empty() { println!("cargo:warning=Missing packages: {:?}", still_missing); - println!("cargo:warning=Consider installing ROS 2 or checking hiroz-codegen/assets/jazzy"); + println!("cargo:warning=Consider installing ROS 2 or checking hiroz-codegen's bundled assets"); } Ok(package_map.into_values().collect()) @@ -241,7 +239,8 @@ fn discover_package_name_from_path(package_path: &std::path::Path) -> Result Vec<&'static str> { let mut names = vec![ "builtin_interfaces", // Always required @@ -417,16 +416,17 @@ fn discover_system_packages(packages: &[&str]) -> Result> { Ok(found_packages) } -/// Discover packages from local bundled assets (hiroz-codegen/assets/jazzy/) -fn discover_local_assets(package_names: &[&str]) -> Result> { +/// Discover packages from hiroz-codegen's bundled assets directory. +/// +/// Only returns the subset named in `package_names` (i.e. whatever the enabled +/// Cargo features requested), not every package hiroz-codegen bundles. +fn discover_local_assets(package_names: &[&str], is_humble: bool) -> Result> { let mut found_packages = Vec::new(); - // Get the path to hiroz-codegen/assets/jazzy relative to this crate - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let assets_dir = manifest_dir - .parent() - .expect("Failed to get parent directory") - .join("hiroz-codegen/assets/jazzy"); + // Resolve via hiroz-codegen's own API, not a sibling-directory path — this + // works whether hiroz-codegen is a workspace path dependency or pulled from + // crates.io, since it resolves against hiroz-codegen's own CARGO_MANIFEST_DIR. + let assets_dir = hiroz_codegen::bundled_assets_dir(is_humble); if !assets_dir.exists() { println!( @@ -436,7 +436,7 @@ fn discover_local_assets(package_names: &[&str]) -> Result> { return Ok(Vec::new()); } - // Search for packages in jazzy assets directory + // Search for the requested packages in the bundled assets directory for package_name in package_names { let package_path = assets_dir.join(package_name); diff --git a/docs/user-guide/custom-messages.md b/docs/user-guide/custom-messages.md index bec1645ca..37413580a 100644 --- a/docs/user-guide/custom-messages.md +++ b/docs/user-guide/custom-messages.md @@ -194,17 +194,19 @@ edition = "2021" [workspace] [dependencies] -hiroz-msgs = { git = "https://github.com/ZettaScaleLabs/hiroz.git" } -hiroz = { git = "https://github.com/ZettaScaleLabs/hiroz.git", default-features = false } +hiroz-msgs = "0.2" +hiroz = { version = "0.2", default-features = false } serde = { version = "1", features = ["derive"] } smart-default = "0.7" zenoh-buffers = "1" [build-dependencies] -hiroz-codegen = { git = "https://github.com/ZettaScaleLabs/hiroz.git" } +hiroz-codegen = "0.2" anyhow = "1" ``` +`hiroz-msgs`, `hiroz`, and `hiroz-codegen` are published on crates.io, so a version dependency is enough — no `git` dependency is required. This matters for your own crate too: crates.io rejects any published crate that depends on a `git` source, so if you want `my-robot-msgs` itself to be publishable, its dependencies need to come from crates.io as well. + **build.rs:** ```rust From 721280c314b74704fdd55e0167ba9b121119a64a Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Mon, 31 Aug 2026 02:34:59 +0800 Subject: [PATCH 2/5] style(hiroz-msgs): wrap long println! per rustfmt --- crates/hiroz-msgs/build.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/hiroz-msgs/build.rs b/crates/hiroz-msgs/build.rs index d8563c756..6c54167d2 100644 --- a/crates/hiroz-msgs/build.rs +++ b/crates/hiroz-msgs/build.rs @@ -227,7 +227,9 @@ fn discover_ros_packages(is_humble: bool) -> Result> { if !still_missing.is_empty() { println!("cargo:warning=Missing packages: {:?}", still_missing); - println!("cargo:warning=Consider installing ROS 2 or checking hiroz-codegen's bundled assets"); + println!( + "cargo:warning=Consider installing ROS 2 or checking hiroz-codegen's bundled assets" + ); } Ok(package_map.into_values().collect()) From f55eedb420ff7456f8c82869468bdcd9ffa538a2 Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Mon, 31 Aug 2026 15:57:39 +0800 Subject: [PATCH 3/5] fix(hiroz-msgs): keep the jazzy asset corpus for the bundled fallback Passing is_humble to bundled_assets_dir resolved to assets/humble, which hiroz-codegen ships empty (only assets/jazzy is populated). A Humble build with no system ROS install would silently find zero bundled packages instead of falling back to the jazzy corpus, same as before this branch. --- crates/hiroz-msgs/build.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/hiroz-msgs/build.rs b/crates/hiroz-msgs/build.rs index 6c54167d2..d453c6cea 100644 --- a/crates/hiroz-msgs/build.rs +++ b/crates/hiroz-msgs/build.rs @@ -33,7 +33,12 @@ fn main() -> Result<()> { // a sibling path in this crate's own directory), so ask hiroz-codegen for its // own bundled-assets location. This resolves correctly whether hiroz-codegen // is consumed via path (workspace) or from crates.io. - let codegen_assets = hiroz_codegen::bundled_assets_dir(is_humble); + // + // Always the jazzy corpus, never `is_humble`: hiroz-codegen ships only the + // jazzy assets populated (assets/humble exists but is empty), and the + // package list below already excludes post-Humble-only interfaces, so the + // jazzy corpus is the correct bundled fallback for a Humble build too. + let codegen_assets = hiroz_codegen::bundled_assets_dir(false); if codegen_assets.exists() { println!("cargo:rerun-if-changed={}", codegen_assets.display()); } @@ -170,7 +175,7 @@ fn discover_ros_packages(is_humble: bool) -> Result> { // avoiding issues with system packages that may have different versions or // hardcoded paths from Nix wrapProgram. println!("cargo:info=Checking local bundled assets from hiroz-codegen's bundled assets"); - let local_asset_packages = discover_local_assets(&all_packages, is_humble)?; + let local_asset_packages = discover_local_assets(&all_packages)?; let local_count = local_asset_packages.len(); for pkg_path in local_asset_packages { if let Ok(name) = discover_package_name_from_path(&pkg_path) { @@ -422,13 +427,16 @@ fn discover_system_packages(packages: &[&str]) -> Result> { /// /// Only returns the subset named in `package_names` (i.e. whatever the enabled /// Cargo features requested), not every package hiroz-codegen bundles. -fn discover_local_assets(package_names: &[&str], is_humble: bool) -> Result> { +fn discover_local_assets(package_names: &[&str]) -> Result> { let mut found_packages = Vec::new(); // Resolve via hiroz-codegen's own API, not a sibling-directory path — this // works whether hiroz-codegen is a workspace path dependency or pulled from // crates.io, since it resolves against hiroz-codegen's own CARGO_MANIFEST_DIR. - let assets_dir = hiroz_codegen::bundled_assets_dir(is_humble); + // + // Always jazzy, never `is_humble`: hiroz-codegen only ships the jazzy + // corpus populated (see the call site in `main` for the full reasoning). + let assets_dir = hiroz_codegen::bundled_assets_dir(false); if !assets_dir.exists() { println!( From c79122ce420815c48ab6cd1ecd2264925eeb43e0 Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Mon, 31 Aug 2026 16:31:40 +0800 Subject: [PATCH 4/5] test(hiroz-msgs): pin down the Humble bundled-assets regression in CI check-bundled-msgs runs in the pureRust devshell, which has no system ROS install, so it's the one place a broken bundled-assets fallback can't be masked by discover_ros_packages' system-install fallback the way every ROS-container job masks it. Add the missing cell: hiroz-msgs built with --features humble and no ROS sourced. Verified both directions on this commit's parent: reverting the bundled_assets_dir(false) fix and running this check fails to compile (missing generated.rs, ros_packages len: 0); restoring the fix makes it pass (ros_packages len: 11). --- scripts/test-pure-rust.nu | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/test-pure-rust.nu b/scripts/test-pure-rust.nu index f58fe8950..951ffb5e2 100755 --- a/scripts/test-pure-rust.nu +++ b/scripts/test-pure-rust.nu @@ -46,6 +46,12 @@ def check-bundled-msgs [] { run-cmd "cargo check -p hiroz-msgs --no-default-features --features geometry_msgs" run-cmd "cargo check -p hiroz-msgs --no-default-features --features sensor_msgs" run-cmd "cargo check -p hiroz-msgs --no-default-features --features nav_msgs" + # This shell has no system ROS install (no AMENT_PREFIX_PATH), so + # detect_ros_version()'s system-install fallback can't mask a broken + # bundled-assets path the way every ROS-container CI job does. That gap + # let `hiroz-codegen::bundled_assets_dir(is_humble)` ship pointed at the + # empty `assets/humble` directory unnoticed — see ZettaScaleLabs/hiroz#332. + run-cmd "cargo check -p hiroz-msgs --features humble" } def check-hu [] { From 7eca11fa5b0718005cada3338faaa44351bd9328 Mon Sep 17 00:00:00 2001 From: yuanyuyuan Date: Mon, 31 Aug 2026 17:54:54 +0800 Subject: [PATCH 5/5] chore(hiroz-msgs): reference the workspace hiroz-derive dep, rewrite comments in STE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hiroz-derive is already declared in [workspace.dependencies], so use workspace = true instead of a second hardcoded version literal — one fewer place for the version pin to drift. hiroz itself can't take the same treatment: Cargo does not currently support overriding default-features on a workspace = true dependency (a known Cargo limitation), and hiroz-msgs needs default-features = false, features = ["rmw-zenoh"]. It keeps its explicit path + version form. Also rewrote this branch's own added comments in build.rs and scripts/test-pure-rust.nu to Simplified Technical English. --- crates/hiroz-msgs/Cargo.toml | 2 +- crates/hiroz-msgs/build.rs | 30 +++++++++++++++++------------- scripts/test-pure-rust.nu | 12 +++++++----- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/crates/hiroz-msgs/Cargo.toml b/crates/hiroz-msgs/Cargo.toml index 2f8b089bd..ca6632a3a 100644 --- a/crates/hiroz-msgs/Cargo.toml +++ b/crates/hiroz-msgs/Cargo.toml @@ -22,7 +22,7 @@ zenoh-buffers = { workspace = true } prost = { workspace = true, optional = true } pyo3 = { workspace = true, optional = true } -hiroz-derive = { version = "0.2", path = "../hiroz-derive", optional = true } +hiroz-derive = { workspace = true, optional = true } hiroz-cdr = { workspace = true } byteorder = { workspace = true } bytemuck = { version = "1", features = ["derive"] } diff --git a/crates/hiroz-msgs/build.rs b/crates/hiroz-msgs/build.rs index d453c6cea..e77a9d928 100644 --- a/crates/hiroz-msgs/build.rs +++ b/crates/hiroz-msgs/build.rs @@ -29,15 +29,17 @@ fn main() -> Result<()> { // Detect ROS version and emit cfg let is_humble = detect_ros_version(); - // Message assets are vendored inside the published hiroz-codegen crate (not - // a sibling path in this crate's own directory), so ask hiroz-codegen for its - // own bundled-assets location. This resolves correctly whether hiroz-codegen - // is consumed via path (workspace) or from crates.io. + // hiroz-codegen stores the message assets in its own published crate. + // They are not in a sibling path inside this crate's directory. Use + // hiroz_codegen::bundled_assets_dir to find them. This finds them + // correctly whether hiroz-codegen comes from a workspace path or + // from crates.io. // - // Always the jazzy corpus, never `is_humble`: hiroz-codegen ships only the - // jazzy assets populated (assets/humble exists but is empty), and the - // package list below already excludes post-Humble-only interfaces, so the - // jazzy corpus is the correct bundled fallback for a Humble build too. + // Always select the jazzy assets here. Do not pass `is_humble`. + // hiroz-codegen ships only the jazzy assets. The assets/humble + // directory exists but is empty. The package list below already + // excludes interfaces added after Humble. So the jazzy assets are + // correct for a Humble build too. let codegen_assets = hiroz_codegen::bundled_assets_dir(false); if codegen_assets.exists() { println!("cargo:rerun-if-changed={}", codegen_assets.display()); @@ -430,12 +432,14 @@ fn discover_system_packages(packages: &[&str]) -> Result> { fn discover_local_assets(package_names: &[&str]) -> Result> { let mut found_packages = Vec::new(); - // Resolve via hiroz-codegen's own API, not a sibling-directory path — this - // works whether hiroz-codegen is a workspace path dependency or pulled from - // crates.io, since it resolves against hiroz-codegen's own CARGO_MANIFEST_DIR. + // Resolve this through hiroz-codegen's own API, not a sibling-directory + // path. This works whether hiroz-codegen is a workspace path + // dependency or comes from crates.io, because it resolves against + // hiroz-codegen's own CARGO_MANIFEST_DIR. // - // Always jazzy, never `is_humble`: hiroz-codegen only ships the jazzy - // corpus populated (see the call site in `main` for the full reasoning). + // Always select jazzy here. Do not pass `is_humble`. hiroz-codegen + // ships only the jazzy assets. See the call site in `main` for the + // full reason. let assets_dir = hiroz_codegen::bundled_assets_dir(false); if !assets_dir.exists() { diff --git a/scripts/test-pure-rust.nu b/scripts/test-pure-rust.nu index 951ffb5e2..be7c3ac5e 100755 --- a/scripts/test-pure-rust.nu +++ b/scripts/test-pure-rust.nu @@ -46,11 +46,13 @@ def check-bundled-msgs [] { run-cmd "cargo check -p hiroz-msgs --no-default-features --features geometry_msgs" run-cmd "cargo check -p hiroz-msgs --no-default-features --features sensor_msgs" run-cmd "cargo check -p hiroz-msgs --no-default-features --features nav_msgs" - # This shell has no system ROS install (no AMENT_PREFIX_PATH), so - # detect_ros_version()'s system-install fallback can't mask a broken - # bundled-assets path the way every ROS-container CI job does. That gap - # let `hiroz-codegen::bundled_assets_dir(is_humble)` ship pointed at the - # empty `assets/humble` directory unnoticed — see ZettaScaleLabs/hiroz#332. + # This shell has no system ROS install. It has no AMENT_PREFIX_PATH. + # So detect_ros_version()'s system-install fallback cannot hide a + # broken bundled-assets path. Every ROS-container CI job hides this + # kind of bug, because it has a system ROS install. That gap let + # `hiroz_codegen::bundled_assets_dir(is_humble)` point at the empty + # `assets/humble` directory. Nobody noticed. See + # ZettaScaleLabs/hiroz#332 for the details. run-cmd "cargo check -p hiroz-msgs --features humble" }