Skip to content

Commit eac3b66

Browse files
committed
Rework how Cargo computes the rustc file outputs.
1 parent 7a06be1 commit eac3b66

18 files changed

+516
-377
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::collections::HashMap;
1010
use std::path::PathBuf;
1111

1212
mod target_info;
13-
pub use self::target_info::{FileFlavor, RustcTargetData, TargetInfo};
13+
pub use self::target_info::{FileFlavor, FileType, RustcTargetData, TargetInfo};
1414

1515
/// The build context, containing all information about a build task.
1616
///

src/cargo/core/compiler/build_context/target_info.rs

Lines changed: 184 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::core::compiler::{BuildOutput, CompileKind, CompileTarget, CrateType};
2-
use crate::core::{Dependency, TargetKind, Workspace};
1+
use crate::core::compiler::{BuildOutput, CompileKind, CompileMode, CompileTarget, CrateType};
2+
use crate::core::{Dependency, Target, TargetKind, Workspace};
33
use crate::util::config::{Config, StringList, TargetConfig};
44
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc};
55
use cargo_platform::{Cfg, CfgExpr};
@@ -49,41 +49,73 @@ pub struct TargetInfo {
4949
pub enum FileFlavor {
5050
/// Not a special file type.
5151
Normal,
52-
/// Like `Normal`, but not directly executable
52+
/// Like `Normal`, but not directly executable.
53+
/// For example, a `.wasm` file paired with the "normal" `.js` file.
5354
Auxiliary,
5455
/// Something you can link against (e.g., a library).
55-
Linkable { rmeta: bool },
56+
Linkable,
57+
/// An `.rmeta` Rust metadata file.
58+
Rmeta,
5659
/// Piece of external debug information (e.g., `.dSYM`/`.pdb` file).
5760
DebugInfo,
5861
}
5962

6063
/// Type of each file generated by a Unit.
64+
#[derive(Debug)]
6165
pub struct FileType {
6266
/// The kind of file.
6367
pub flavor: FileFlavor,
68+
/// The crate-type that generates this file.
69+
///
70+
/// `None` for things that aren't associated with a specific crate type,
71+
/// for example `rmeta` files.
72+
pub crate_type: Option<CrateType>,
6473
/// The suffix for the file (for example, `.rlib`).
6574
/// This is an empty string for executables on Unix-like platforms.
6675
suffix: String,
6776
/// The prefix for the file (for example, `lib`).
6877
/// This is an empty string for things like executables.
6978
prefix: String,
70-
/// Flag to convert hyphen to underscore.
71-
///
72-
/// wasm bin targets will generate two files in deps such as
73-
/// "web-stuff.js" and "web_stuff.wasm". Note the different usages of "-"
74-
/// and "_". This flag indicates that the stem "web-stuff" should be
75-
/// converted to "web_stuff".
79+
/// Flag to convert hyphen to underscore when uplifting.
7680
should_replace_hyphens: bool,
7781
}
7882

7983
impl FileType {
80-
pub fn filename(&self, stem: &str) -> String {
81-
let stem = if self.should_replace_hyphens {
82-
stem.replace("-", "_")
84+
/// The filename for this FileType crated by rustc.
85+
pub fn output_filename(&self, target: &Target, metadata: Option<&str>) -> String {
86+
match metadata {
87+
Some(metadata) => format!(
88+
"{}{}-{}{}",
89+
self.prefix,
90+
target.crate_name(),
91+
metadata,
92+
self.suffix
93+
),
94+
None => format!("{}{}{}", self.prefix, target.crate_name(), self.suffix),
95+
}
96+
}
97+
98+
/// The filename for this FileType that Cargo should use when "uplifting"
99+
/// it to the destination directory.
100+
pub fn uplift_filename(&self, target: &Target) -> String {
101+
let name = if self.should_replace_hyphens {
102+
target.crate_name()
83103
} else {
84-
stem.to_string()
104+
target.name().to_string()
85105
};
86-
format!("{}{}{}", self.prefix, stem, self.suffix)
106+
format!("{}{}{}", self.prefix, name, self.suffix)
107+
}
108+
109+
/// Creates a new instance representing a `.rmeta` file.
110+
pub fn new_rmeta() -> FileType {
111+
// Note that even binaries use the `lib` prefix.
112+
FileType {
113+
flavor: FileFlavor::Rmeta,
114+
crate_type: None,
115+
suffix: ".rmeta".to_string(),
116+
prefix: "lib".to_string(),
117+
should_replace_hyphens: true,
118+
}
87119
}
88120
}
89121

@@ -232,11 +264,10 @@ impl TargetInfo {
232264
/// Returns the list of file types generated by the given crate type.
233265
///
234266
/// Returns `None` if the target does not support the given crate type.
235-
pub fn file_types(
267+
fn file_types(
236268
&self,
237269
crate_type: &CrateType,
238270
flavor: FileFlavor,
239-
kind: &TargetKind,
240271
target_triple: &str,
241272
) -> CargoResult<Option<Vec<FileType>>> {
242273
let crate_type = if *crate_type == CrateType::Lib {
@@ -262,67 +293,109 @@ impl TargetInfo {
262293
suffix: suffix.clone(),
263294
prefix: prefix.clone(),
264295
flavor,
265-
should_replace_hyphens: false,
296+
crate_type: Some(crate_type.clone()),
297+
should_replace_hyphens: crate_type != CrateType::Bin,
266298
}];
267299

268-
// See rust-lang/cargo#4500.
269-
if target_triple.ends_with("-windows-msvc")
270-
&& (crate_type == CrateType::Dylib || crate_type == CrateType::Cdylib)
271-
&& suffix == ".dll"
272-
{
273-
ret.push(FileType {
274-
suffix: ".dll.lib".to_string(),
275-
prefix: prefix.clone(),
276-
flavor: FileFlavor::Normal,
277-
should_replace_hyphens: false,
278-
})
279-
} else if target_triple.ends_with("windows-gnu")
280-
&& (crate_type == CrateType::Dylib || crate_type == CrateType::Cdylib)
281-
&& suffix == ".dll"
282-
{
283-
// LD can link DLL directly, but LLD requires the import library.
284-
ret.push(FileType {
285-
suffix: ".dll.a".to_string(),
286-
prefix: "lib".to_string(),
287-
flavor: FileFlavor::Normal,
288-
should_replace_hyphens: false,
289-
})
300+
// Window shared library import/export files.
301+
if crate_type.is_dynamic() {
302+
if target_triple.ends_with("-windows-msvc") {
303+
assert!(suffix == ".dll");
304+
// See https://docs.microsoft.com/en-us/cpp/build/reference/working-with-import-libraries-and-export-files
305+
// for more information about DLL import/export files.
306+
ret.push(FileType {
307+
suffix: ".dll.lib".to_string(),
308+
prefix: prefix.clone(),
309+
flavor: FileFlavor::Auxiliary,
310+
crate_type: Some(crate_type.clone()),
311+
should_replace_hyphens: true,
312+
});
313+
// NOTE: lld does not produce these
314+
ret.push(FileType {
315+
suffix: ".dll.exp".to_string(),
316+
prefix: prefix.clone(),
317+
flavor: FileFlavor::Auxiliary,
318+
crate_type: Some(crate_type.clone()),
319+
should_replace_hyphens: true,
320+
});
321+
} else if target_triple.ends_with("windows-gnu") {
322+
assert!(suffix == ".dll");
323+
// See https://cygwin.com/cygwin-ug-net/dll.html for more
324+
// information about GNU import libraries.
325+
// LD can link DLL directly, but LLD requires the import library.
326+
ret.push(FileType {
327+
suffix: ".dll.a".to_string(),
328+
prefix: "lib".to_string(),
329+
flavor: FileFlavor::Auxiliary,
330+
crate_type: Some(crate_type.clone()),
331+
should_replace_hyphens: true,
332+
})
333+
}
290334
}
291335

292-
// See rust-lang/cargo#4535.
293336
if target_triple.starts_with("wasm32-") && crate_type == CrateType::Bin && suffix == ".js" {
337+
// emscripten binaries generate a .js file, which loads a .wasm
338+
// file.
294339
ret.push(FileType {
295340
suffix: ".wasm".to_string(),
296341
prefix: prefix.clone(),
297342
flavor: FileFlavor::Auxiliary,
343+
crate_type: Some(crate_type.clone()),
344+
// Name `foo-bar` will generate a `foo_bar.js` and
345+
// `foo_bar.wasm`. Cargo will translate the underscore and
346+
// copy `foo_bar.js` to `foo-bar.js`. However, the wasm
347+
// filename is embedded in the .js file with an underscore, so
348+
// it should not contain hyphens.
298349
should_replace_hyphens: true,
299-
})
350+
});
351+
// And a map file for debugging. This is only emitted with debug=2
352+
// (-g4 for emcc).
353+
ret.push(FileType {
354+
suffix: ".wasm.map".to_string(),
355+
prefix: prefix.clone(),
356+
flavor: FileFlavor::DebugInfo,
357+
crate_type: Some(crate_type.clone()),
358+
should_replace_hyphens: true,
359+
});
300360
}
301361

302-
// See rust-lang/cargo#4490, rust-lang/cargo#4960.
303-
// Only uplift debuginfo for binaries.
304-
// - Tests are run directly from `target/debug/deps/` with the
305-
// metadata hash still in the filename.
306-
// - Examples are only uplifted for apple because the symbol file
307-
// needs to match the executable file name to be found (i.e., it
308-
// needs to remove the hash in the filename). On Windows, the path
309-
// to the .pdb with the hash is embedded in the executable.
362+
// Handle separate debug files.
310363
let is_apple = target_triple.contains("-apple-");
311-
if *kind == TargetKind::Bin || (*kind == TargetKind::ExampleBin && is_apple) {
364+
if matches!(
365+
crate_type,
366+
CrateType::Bin | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro
367+
) {
312368
if is_apple {
369+
let suffix = if crate_type == CrateType::Bin {
370+
".dSYM".to_string()
371+
} else {
372+
".dylib.dSYM".to_string()
373+
};
313374
ret.push(FileType {
314-
suffix: ".dSYM".to_string(),
375+
suffix,
315376
prefix: prefix.clone(),
316377
flavor: FileFlavor::DebugInfo,
378+
crate_type: Some(crate_type.clone()),
379+
// macOS tools like lldb use all sorts of magic to locate
380+
// dSYM files. See https://lldb.llvm.org/use/symbols.html
381+
// for some details. It seems like a `.dSYM` located next
382+
// to the executable with the same name is one method. The
383+
// dSYM should have the same hyphens as the executable for
384+
// the names to match.
317385
should_replace_hyphens: false,
318386
})
319387
} else if target_triple.ends_with("-msvc") {
320388
ret.push(FileType {
321389
suffix: ".pdb".to_string(),
322390
prefix: prefix.clone(),
323391
flavor: FileFlavor::DebugInfo,
324-
// rustc calls the linker with underscores, and the
325-
// filename is embedded in the executable.
392+
crate_type: Some(crate_type.clone()),
393+
// The absolute path to the pdb file is embedded in the
394+
// executable. If the exe/pdb pair is moved to another
395+
// machine, then debuggers will look in the same directory
396+
// of the exe with the original pdb filename. Since the
397+
// original name contains underscores, they need to be
398+
// preserved.
326399
should_replace_hyphens: true,
327400
})
328401
}
@@ -353,6 +426,62 @@ impl TargetInfo {
353426
&mut output.lines(),
354427
)?)
355428
}
429+
430+
/// Returns all the file types generated by rustc for the given mode/target_kind.
431+
///
432+
/// The first value is a Vec of file types generated, the second value is
433+
/// a list of CrateTypes that are not supported by the given target.
434+
pub fn rustc_outputs(
435+
&self,
436+
mode: CompileMode,
437+
target_kind: &TargetKind,
438+
target_triple: &str,
439+
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
440+
match mode {
441+
CompileMode::Build => self.calc_rustc_outputs(target_kind, target_triple),
442+
CompileMode::Test | CompileMode::Bench => {
443+
match self.file_types(&CrateType::Bin, FileFlavor::Normal, target_triple)? {
444+
Some(fts) => Ok((fts, Vec::new())),
445+
None => Ok((Vec::new(), vec![CrateType::Bin])),
446+
}
447+
}
448+
CompileMode::Check { .. } => Ok((vec![FileType::new_rmeta()], Vec::new())),
449+
CompileMode::Doc { .. } | CompileMode::Doctest | CompileMode::RunCustomBuild => {
450+
panic!("asked for rustc output for non-rustc mode")
451+
}
452+
}
453+
}
454+
455+
fn calc_rustc_outputs(
456+
&self,
457+
target_kind: &TargetKind,
458+
target_triple: &str,
459+
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
460+
let mut unsupported = Vec::new();
461+
let mut result = Vec::new();
462+
let crate_types = target_kind.rustc_crate_types();
463+
for crate_type in &crate_types {
464+
let flavor = if crate_type.is_linkable() {
465+
FileFlavor::Linkable
466+
} else {
467+
FileFlavor::Normal
468+
};
469+
let file_types = self.file_types(&crate_type, flavor, target_triple)?;
470+
match file_types {
471+
Some(types) => {
472+
result.extend(types);
473+
}
474+
None => {
475+
unsupported.push(crate_type.clone());
476+
}
477+
}
478+
}
479+
if !result.is_empty() && !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
480+
// Only add rmeta if pipelining.
481+
result.push(FileType::new_rmeta());
482+
}
483+
Ok((result, unsupported))
484+
}
356485
}
357486

358487
/// Takes rustc output (using specialized command line args), and calculates the file prefix and
@@ -537,9 +666,7 @@ pub struct RustcTargetData {
537666
host_info: TargetInfo,
538667

539668
/// Build information for targets that we're building for. This will be
540-
/// empty if the `--target` flag is not passed, and currently also only ever
541-
/// has at most one entry, but eventually we'd like to support multi-target
542-
/// builds with Cargo.
669+
/// empty if the `--target` flag is not passed.
543670
target_config: HashMap<CompileTarget, TargetConfig>,
544671
target_info: HashMap<CompileTarget, TargetInfo>,
545672
}

0 commit comments

Comments
 (0)