diff --git a/packages/cli/src/build/builder.rs b/packages/cli/src/build/builder.rs index 538ef48a7..b0a10072d 100644 --- a/packages/cli/src/build/builder.rs +++ b/packages/cli/src/build/builder.rs @@ -408,7 +408,9 @@ impl AppBuilder { // patch (starting from changed_crates + cascade). That serves a different purpose — it only // compiles what changed now, not everything ever modified. Both use workspace_dependents_of // for the BFS, so they stay in sync automatically. - let tip_crate_name = self.build.main_target.replace('-', "_"); + // Track the tip by *package* name — that's the identity used by the file→crate + // mapping and the workspace dependents graph (the bin target name can differ). + let tip_crate_name = self.build.tip_package_name(); self.modified_crates.insert(tip_crate_name.clone()); // Add changed crates and their transitive workspace dependents (cascade). diff --git a/packages/cli/src/build/link.rs b/packages/cli/src/build/link.rs index 44cdd0281..e3edd5ce2 100644 --- a/packages/cli/src/build/link.rs +++ b/packages/cli/src/build/link.rs @@ -614,7 +614,9 @@ impl BuildRequest { modified_crates: &HashSet, ) -> Result> { // Exclude the tip crate — it's compiled separately via cargo_build after replay. - let tip = self.tip_crate_name(); + // Modified crates are tracked by *package* name, which can differ from the bin + // target name (`tip_crate_name()`), e.g. package `browser` with `[[bin]] name = "blitz"`. + let tip = self.tip_package_name(); let crates: HashSet<&String> = modified_crates .iter() .filter(|name| **name != tip) diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index 652ffaa14..2a4154594 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -2581,6 +2581,16 @@ impl BuildRequest { self.main_target.replace('-', "_") } + /// The workspace package name of the tip crate (hyphens replaced with underscores). + /// + /// This can differ from `tip_crate_name()` when the binary target is named differently + /// than its package (e.g. package `browser` with `[[bin]] name = "blitz"`). Use this for + /// workspace-graph lookups (which are keyed by package name) and `tip_crate_name()` for + /// rustc `--crate-name` keys like `{name}.bin`. + pub(crate) fn tip_package_name(&self) -> String { + self.package().name.replace('-', "_") + } + /// Stderr captured from the linker during the last build. Written by the linker /// interception in `rustcwrapper` and read back to surface warnings/errors to the user. fn link_err_file(&self) -> PathBuf { @@ -3095,7 +3105,7 @@ impl BuildRequest { }) .collect(); - let tip = self.tip_crate_name(); + let tip = self.tip_package_name(); let Some(tip_nid) = krates.workspace_members().find_map(|m| match m { krates::Node::Krate { id, krate, .. } if krate.name.replace('-', "_") == tip => { krates.nid_for_kid(id) diff --git a/packages/cli/src/serve/runner.rs b/packages/cli/src/serve/runner.rs index 4436ee41e..82ca1b31e 100644 --- a/packages/cli/src/serve/runner.rs +++ b/packages/cli/src/serve/runner.rs @@ -1187,7 +1187,7 @@ impl AppServer { /// Uses BFS from the tip crate through its workspace dependencies to find the path. /// If the changed crate IS the tip crate, returns just `[tip]`. pub(crate) fn workspace_dep_chain(&self, changed_crate: &str) -> Vec { - let tip_name = self.client.build.main_target.replace('-', "_"); + let tip_name = self.client.build.tip_package_name(); // If the changed crate is the tip, no chain needed if changed_crate == tip_name {