Skip to content

Commit 571a542

Browse files
committedOct 15, 2018
Auto merge of #6170 - ehuss:panic-in-unit, r=alexcrichton
Track `panic` in Unit early. This is an alternate solution for #5445. It ensures that `panic` is cleared in the Profile for "for_host" targets (proc-macro/plugin/build-scripts) and dependencies. This helps avoid unnecessary recompiles in some situations (though does add extra units in some situations, see below). Some examples where extra rebuilds are now avoided: * A workspace with a dependency shared with normal and build-deps. `build --all` should build everything, and then `build -p foo` was causing a recompile because the shared dep was no longer in the `used_in_plugin` set. Now it should not recompile. * `panic=abort`, with a shared dependency in build and dev, `build` would cause that shared dependency to be built twice (exactly the same without panic set). Now it should only build once. Some examples where `panic` is now set correctly: * `panic=abort`, with a binary with a shared dependency in build and normal, `test` would cause that shared dependency to be built twice (exactly the same without panic set). Now it is still built twice, but the one for the normal (bin) dependency will correctly have `panic` set. Some examples where new units are now generated: * `panic=abort`, with shared dependency between normal and proc-macro or build. Previously the shared dependency was built once with `panic=unwind`. Now it is built separately (once with `panic`, once without). I feel like that this is more correct behavior — that now the normal dependency avoids adding landing pads. For `panic=abort` cross-compiling, this makes no difference in compile time since it was already built twice. Additional notes: - I left build-scripts with the old behavior where `panic` is cleared for it and all its dependencies. There could be arguments made to change that (#5436), but it doesn't seem important to me. - Renamed and refactored `ProfileFor` to `UnitFor`. I expect this API to continue to evolve in the future. Closes #6143, closes #6154.
2 parents 6d4be78 + 8648994 commit 571a542

File tree

10 files changed

+421
-262
lines changed

10 files changed

+421
-262
lines changed
 

‎src/cargo/core/compiler/context/compilation_files.rs

-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,6 @@ fn compute_metadata<'a, 'cfg>(
427427
// panic=abort and panic=unwind artifacts, additionally with various
428428
// settings like debuginfo and whatnot.
429429
unit.profile.hash(&mut hasher);
430-
cx.used_in_plugin.contains(unit).hash(&mut hasher);
431430
unit.mode.hash(&mut hasher);
432431
if let Some(ref args) = bcx.extra_args_for(unit) {
433432
args.hash(&mut hasher);

‎src/cargo/core/compiler/context/mod.rs

-34
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ pub struct Context<'a, 'cfg: 'a> {
9494
pub compiled: HashSet<Unit<'a>>,
9595
pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
9696
pub links: Links<'a>,
97-
pub used_in_plugin: HashSet<Unit<'a>>,
9897
pub jobserver: Client,
9998
primary_packages: HashSet<&'a PackageId>,
10099
unit_dependencies: HashMap<Unit<'a>, Vec<Unit<'a>>>,
@@ -127,7 +126,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
127126
build_scripts: HashMap::new(),
128127
build_explicit_deps: HashMap::new(),
129128
links: Links::new(),
130-
used_in_plugin: HashSet::new(),
131129
jobserver,
132130
build_script_overridden: HashSet::new(),
133131

@@ -334,7 +332,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
334332
&mut self.unit_dependencies,
335333
&mut self.package_cache,
336334
)?;
337-
self.build_used_in_plugin_map(units)?;
338335
let files = CompilationFiles::new(
339336
units,
340337
host_layout,
@@ -371,37 +368,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
371368
Ok(())
372369
}
373370

374-
/// Builds up the `used_in_plugin` internal to this context from the list of
375-
/// top-level units.
376-
///
377-
/// This will recursively walk `units` and all of their dependencies to
378-
/// determine which crate are going to be used in plugins or not.
379-
fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>]) -> CargoResult<()> {
380-
let mut visited = HashSet::new();
381-
for unit in units {
382-
self.walk_used_in_plugin_map(unit, unit.target.for_host(), &mut visited)?;
383-
}
384-
Ok(())
385-
}
386-
387-
fn walk_used_in_plugin_map(
388-
&mut self,
389-
unit: &Unit<'a>,
390-
is_plugin: bool,
391-
visited: &mut HashSet<(Unit<'a>, bool)>,
392-
) -> CargoResult<()> {
393-
if !visited.insert((*unit, is_plugin)) {
394-
return Ok(());
395-
}
396-
if is_plugin {
397-
self.used_in_plugin.insert(*unit);
398-
}
399-
for unit in self.dep_targets(unit) {
400-
self.walk_used_in_plugin_map(&unit, is_plugin || unit.target.for_host(), visited)?;
401-
}
402-
Ok(())
403-
}
404-
405371
pub fn files(&self) -> &CompilationFiles<'a, 'cfg> {
406372
self.files.as_ref().unwrap()
407373
}

‎src/cargo/core/compiler/context/unit_dependencies.rs

+44-35
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::collections::{HashMap, HashSet};
2020

2121
use CargoResult;
2222
use core::dependency::Kind as DepKind;
23-
use core::profiles::ProfileFor;
23+
use core::profiles::UnitFor;
2424
use core::{Package, Target, PackageId};
2525
use core::package::Downloads;
2626
use super::{BuildContext, CompileMode, Kind, Unit};
@@ -59,12 +59,19 @@ pub fn build_unit_dependencies<'a, 'cfg>(
5959
// cleared, and avoid building the lib thrice (once with `panic`, once
6060
// without, once for --test). In particular, the lib included for
6161
// doctests and examples are `Build` mode here.
62-
let profile_for = if unit.mode.is_any_test() || bcx.build_config.test() {
63-
ProfileFor::TestDependency
62+
let unit_for = if unit.mode.is_any_test() || bcx.build_config.test() {
63+
UnitFor::new_test()
64+
} else if unit.target.is_custom_build() {
65+
// This normally doesn't happen, except `clean` aggressively
66+
// generates all units.
67+
UnitFor::new_build()
68+
} else if unit.target.for_host() {
69+
// proc-macro/plugin should never have panic set.
70+
UnitFor::new_compiler()
6471
} else {
65-
ProfileFor::Any
72+
UnitFor::new_normal()
6673
};
67-
deps_of(unit, &mut state, profile_for)?;
74+
deps_of(unit, &mut state, unit_for)?;
6875
}
6976

7077
if state.waiting_on_download.len() > 0 {
@@ -84,34 +91,34 @@ pub fn build_unit_dependencies<'a, 'cfg>(
8491
fn deps_of<'a, 'cfg, 'tmp>(
8592
unit: &Unit<'a>,
8693
state: &mut State<'a, 'cfg, 'tmp>,
87-
profile_for: ProfileFor,
94+
unit_for: UnitFor,
8895
) -> CargoResult<()> {
89-
// Currently the `deps` map does not include `profile_for`. This should
96+
// Currently the `deps` map does not include `unit_for`. This should
9097
// be safe for now. `TestDependency` only exists to clear the `panic`
9198
// flag, and you'll never ask for a `unit` with `panic` set as a
9299
// `TestDependency`. `CustomBuild` should also be fine since if the
93100
// requested unit's settings are the same as `Any`, `CustomBuild` can't
94101
// affect anything else in the hierarchy.
95102
if !state.deps.contains_key(unit) {
96-
let unit_deps = compute_deps(unit, state, profile_for)?;
103+
let unit_deps = compute_deps(unit, state, unit_for)?;
97104
let to_insert: Vec<_> = unit_deps.iter().map(|&(unit, _)| unit).collect();
98105
state.deps.insert(*unit, to_insert);
99-
for (unit, profile_for) in unit_deps {
100-
deps_of(&unit, state, profile_for)?;
106+
for (unit, unit_for) in unit_deps {
107+
deps_of(&unit, state, unit_for)?;
101108
}
102109
}
103110
Ok(())
104111
}
105112

106113
/// For a package, return all targets which are registered as dependencies
107114
/// for that package.
108-
/// This returns a vec of `(Unit, ProfileFor)` pairs. The `ProfileFor`
115+
/// This returns a vec of `(Unit, UnitFor)` pairs. The `UnitFor`
109116
/// is the profile type that should be used for dependencies of the unit.
110117
fn compute_deps<'a, 'cfg, 'tmp>(
111118
unit: &Unit<'a>,
112119
state: &mut State<'a, 'cfg, 'tmp>,
113-
profile_for: ProfileFor,
114-
) -> CargoResult<Vec<(Unit<'a>, ProfileFor)>> {
120+
unit_for: UnitFor,
121+
) -> CargoResult<Vec<(Unit<'a>, UnitFor)>> {
115122
if unit.mode.is_run_custom_build() {
116123
return compute_deps_custom_build(unit, state.bcx);
117124
} else if unit.mode.is_doc() && !unit.mode.is_any_test() {
@@ -173,15 +180,16 @@ fn compute_deps<'a, 'cfg, 'tmp>(
173180
None => continue,
174181
};
175182
let mode = check_or_build_mode(unit.mode, lib);
183+
let dep_unit_for = unit_for.with_for_host(lib.for_host());
176184
let unit = new_unit(
177185
bcx,
178186
pkg,
179187
lib,
180-
profile_for,
188+
dep_unit_for,
181189
unit.kind.for_target(lib),
182190
mode,
183191
);
184-
ret.push((unit, profile_for));
192+
ret.push((unit, dep_unit_for));
185193
}
186194

187195
// If this target is a build script, then what we've collected so far is
@@ -199,7 +207,7 @@ fn compute_deps<'a, 'cfg, 'tmp>(
199207
if unit.target.is_lib() && unit.mode != CompileMode::Doctest {
200208
return Ok(ret);
201209
}
202-
ret.extend(maybe_lib(unit, bcx, profile_for));
210+
ret.extend(maybe_lib(unit, bcx, unit_for));
203211

204212
// If any integration tests/benches are being run, make sure that
205213
// binaries are built as well.
@@ -225,11 +233,11 @@ fn compute_deps<'a, 'cfg, 'tmp>(
225233
bcx,
226234
unit.pkg,
227235
t,
228-
ProfileFor::Any,
236+
UnitFor::new_normal(),
229237
unit.kind.for_target(t),
230238
CompileMode::Build,
231239
),
232-
ProfileFor::Any,
240+
UnitFor::new_normal(),
233241
)
234242
}),
235243
);
@@ -245,7 +253,7 @@ fn compute_deps<'a, 'cfg, 'tmp>(
245253
fn compute_deps_custom_build<'a, 'cfg>(
246254
unit: &Unit<'a>,
247255
bcx: &BuildContext<'a, 'cfg>,
248-
) -> CargoResult<Vec<(Unit<'a>, ProfileFor)>> {
256+
) -> CargoResult<Vec<(Unit<'a>, UnitFor)>> {
249257
// When not overridden, then the dependencies to run a build script are:
250258
//
251259
// 1. Compiling the build script itself
@@ -259,20 +267,20 @@ fn compute_deps_custom_build<'a, 'cfg>(
259267
bcx,
260268
unit.pkg,
261269
unit.target,
262-
ProfileFor::CustomBuild,
270+
UnitFor::new_build(),
263271
Kind::Host, // build scripts always compiled for the host
264272
CompileMode::Build,
265273
);
266274
// All dependencies of this unit should use profiles for custom
267275
// builds.
268-
Ok(vec![(unit, ProfileFor::CustomBuild)])
276+
Ok(vec![(unit, UnitFor::new_build())])
269277
}
270278

271279
/// Returns the dependencies necessary to document a package
272280
fn compute_deps_doc<'a, 'cfg, 'tmp>(
273281
unit: &Unit<'a>,
274282
state: &mut State<'a, 'cfg, 'tmp>,
275-
) -> CargoResult<Vec<(Unit<'a>, ProfileFor)>> {
283+
) -> CargoResult<Vec<(Unit<'a>, UnitFor)>> {
276284
let bcx = state.bcx;
277285
let deps = bcx.resolve
278286
.deps(unit.pkg.package_id())
@@ -299,26 +307,27 @@ fn compute_deps_doc<'a, 'cfg, 'tmp>(
299307
// rustdoc only needs rmeta files for regular dependencies.
300308
// However, for plugins/proc-macros, deps should be built like normal.
301309
let mode = check_or_build_mode(unit.mode, lib);
310+
let dep_unit_for = UnitFor::new_normal().with_for_host(lib.for_host());
302311
let lib_unit = new_unit(
303312
bcx,
304313
dep,
305314
lib,
306-
ProfileFor::Any,
315+
dep_unit_for,
307316
unit.kind.for_target(lib),
308317
mode,
309318
);
310-
ret.push((lib_unit, ProfileFor::Any));
319+
ret.push((lib_unit, dep_unit_for));
311320
if let CompileMode::Doc { deps: true } = unit.mode {
312321
// Document this lib as well.
313322
let doc_unit = new_unit(
314323
bcx,
315324
dep,
316325
lib,
317-
ProfileFor::Any,
326+
dep_unit_for,
318327
unit.kind.for_target(lib),
319328
unit.mode,
320329
);
321-
ret.push((doc_unit, ProfileFor::Any));
330+
ret.push((doc_unit, dep_unit_for));
322331
}
323332
}
324333

@@ -327,27 +336,27 @@ fn compute_deps_doc<'a, 'cfg, 'tmp>(
327336

328337
// If we document a binary, we need the library available
329338
if unit.target.is_bin() {
330-
ret.extend(maybe_lib(unit, bcx, ProfileFor::Any));
339+
ret.extend(maybe_lib(unit, bcx, UnitFor::new_normal()));
331340
}
332341
Ok(ret)
333342
}
334343

335344
fn maybe_lib<'a>(
336345
unit: &Unit<'a>,
337346
bcx: &BuildContext,
338-
profile_for: ProfileFor,
339-
) -> Option<(Unit<'a>, ProfileFor)> {
347+
unit_for: UnitFor,
348+
) -> Option<(Unit<'a>, UnitFor)> {
340349
unit.pkg.targets().iter().find(|t| t.linkable()).map(|t| {
341350
let mode = check_or_build_mode(unit.mode, t);
342351
let unit = new_unit(
343352
bcx,
344353
unit.pkg,
345354
t,
346-
profile_for,
355+
unit_for,
347356
unit.kind.for_target(t),
348357
mode,
349358
);
350-
(unit, profile_for)
359+
(unit, unit_for)
351360
})
352361
}
353362

@@ -358,7 +367,7 @@ fn maybe_lib<'a>(
358367
/// script itself doesn't have any dependencies, so even in that case a unit
359368
/// of work is still returned. `None` is only returned if the package has no
360369
/// build script.
361-
fn dep_build_script<'a>(unit: &Unit<'a>, bcx: &BuildContext) -> Option<(Unit<'a>, ProfileFor)> {
370+
fn dep_build_script<'a>(unit: &Unit<'a>, bcx: &BuildContext) -> Option<(Unit<'a>, UnitFor)> {
362371
unit.pkg
363372
.targets()
364373
.iter()
@@ -374,7 +383,7 @@ fn dep_build_script<'a>(unit: &Unit<'a>, bcx: &BuildContext) -> Option<(Unit<'a>
374383
kind: unit.kind,
375384
mode: CompileMode::RunCustomBuild,
376385
},
377-
ProfileFor::CustomBuild,
386+
UnitFor::new_build(),
378387
)
379388
})
380389
}
@@ -401,14 +410,14 @@ fn new_unit<'a>(
401410
bcx: &BuildContext,
402411
pkg: &'a Package,
403412
target: &'a Target,
404-
profile_for: ProfileFor,
413+
unit_for: UnitFor,
405414
kind: Kind,
406415
mode: CompileMode,
407416
) -> Unit<'a> {
408417
let profile = bcx.profiles.get_profile(
409418
&pkg.package_id(),
410419
bcx.ws.is_member(pkg),
411-
profile_for,
420+
unit_for,
412421
mode,
413422
bcx.build_config.release,
414423
);

‎src/cargo/core/compiler/fingerprint.rs

-1
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,6 @@ fn calculate<'a, 'cfg>(
466466
unit.mode,
467467
bcx.extra_args_for(unit),
468468
cx.incremental_args(unit)?,
469-
cx.used_in_plugin.contains(unit), // used when passing panic=abort
470469
));
471470
let fingerprint = Arc::new(Fingerprint {
472471
rustc: util::hash_u64(&bcx.rustc.verbose_version),

‎src/cargo/core/compiler/mod.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -765,20 +765,8 @@ fn build_base_args<'a, 'cfg>(
765765
cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
766766
}
767767

768-
// If a panic mode was configured *and* we're not ever going to be used in a
769-
// plugin, then we can compile with that panic mode.
770-
//
771-
// If we're used in a plugin then we'll eventually be linked to libsyntax
772-
// most likely which isn't compiled with a custom panic mode, so we'll just
773-
// get an error if we actually compile with that. This fixes `panic=abort`
774-
// crates which have plugin dependencies, but unfortunately means that
775-
// dependencies shared between the main application and plugins must be
776-
// compiled without `panic=abort`. This isn't so bad, though, as the main
777-
// application will still be compiled with `panic=abort`.
778768
if let Some(panic) = panic.as_ref() {
779-
if !cx.used_in_plugin.contains(unit) {
780-
cmd.arg("-C").arg(format!("panic={}", panic));
781-
}
769+
cmd.arg("-C").arg(format!("panic={}", panic));
782770
}
783771

784772
// Disable LTO for host builds as prefer_dynamic and it are mutually

0 commit comments

Comments
 (0)