Skip to content

Commit

Permalink
Rework how Cargo computes the rustc file outputs.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed May 5, 2020
1 parent 7a06be1 commit eac3b66
Show file tree
Hide file tree
Showing 18 changed files with 516 additions and 377 deletions.
2 changes: 1 addition & 1 deletion src/cargo/core/compiler/build_context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::collections::HashMap;
use std::path::PathBuf;

mod target_info;
pub use self::target_info::{FileFlavor, RustcTargetData, TargetInfo};
pub use self::target_info::{FileFlavor, FileType, RustcTargetData, TargetInfo};

/// The build context, containing all information about a build task.
///
Expand Down
241 changes: 184 additions & 57 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::core::compiler::{BuildOutput, CompileKind, CompileTarget, CrateType};
use crate::core::{Dependency, TargetKind, Workspace};
use crate::core::compiler::{BuildOutput, CompileKind, CompileMode, CompileTarget, CrateType};
use crate::core::{Dependency, Target, TargetKind, Workspace};
use crate::util::config::{Config, StringList, TargetConfig};
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc};
use cargo_platform::{Cfg, CfgExpr};
Expand Down Expand Up @@ -49,41 +49,73 @@ pub struct TargetInfo {
pub enum FileFlavor {
/// Not a special file type.
Normal,
/// Like `Normal`, but not directly executable
/// Like `Normal`, but not directly executable.
/// For example, a `.wasm` file paired with the "normal" `.js` file.
Auxiliary,
/// Something you can link against (e.g., a library).
Linkable { rmeta: bool },
Linkable,
/// An `.rmeta` Rust metadata file.
Rmeta,
/// Piece of external debug information (e.g., `.dSYM`/`.pdb` file).
DebugInfo,
}

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

impl FileType {
pub fn filename(&self, stem: &str) -> String {
let stem = if self.should_replace_hyphens {
stem.replace("-", "_")
/// The filename for this FileType crated by rustc.
pub fn output_filename(&self, target: &Target, metadata: Option<&str>) -> String {
match metadata {
Some(metadata) => format!(
"{}{}-{}{}",
self.prefix,
target.crate_name(),
metadata,
self.suffix
),
None => format!("{}{}{}", self.prefix, target.crate_name(), self.suffix),
}
}

/// The filename for this FileType that Cargo should use when "uplifting"
/// it to the destination directory.
pub fn uplift_filename(&self, target: &Target) -> String {
let name = if self.should_replace_hyphens {
target.crate_name()
} else {
stem.to_string()
target.name().to_string()
};
format!("{}{}{}", self.prefix, stem, self.suffix)
format!("{}{}{}", self.prefix, name, self.suffix)
}

/// Creates a new instance representing a `.rmeta` file.
pub fn new_rmeta() -> FileType {
// Note that even binaries use the `lib` prefix.
FileType {
flavor: FileFlavor::Rmeta,
crate_type: None,
suffix: ".rmeta".to_string(),
prefix: "lib".to_string(),
should_replace_hyphens: true,
}
}
}

Expand Down Expand Up @@ -232,11 +264,10 @@ impl TargetInfo {
/// Returns the list of file types generated by the given crate type.
///
/// Returns `None` if the target does not support the given crate type.
pub fn file_types(
fn file_types(
&self,
crate_type: &CrateType,
flavor: FileFlavor,
kind: &TargetKind,
target_triple: &str,
) -> CargoResult<Option<Vec<FileType>>> {
let crate_type = if *crate_type == CrateType::Lib {
Expand All @@ -262,67 +293,109 @@ impl TargetInfo {
suffix: suffix.clone(),
prefix: prefix.clone(),
flavor,
should_replace_hyphens: false,
crate_type: Some(crate_type.clone()),
should_replace_hyphens: crate_type != CrateType::Bin,
}];

// See rust-lang/cargo#4500.
if target_triple.ends_with("-windows-msvc")
&& (crate_type == CrateType::Dylib || crate_type == CrateType::Cdylib)
&& suffix == ".dll"
{
ret.push(FileType {
suffix: ".dll.lib".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::Normal,
should_replace_hyphens: false,
})
} else if target_triple.ends_with("windows-gnu")
&& (crate_type == CrateType::Dylib || crate_type == CrateType::Cdylib)
&& suffix == ".dll"
{
// LD can link DLL directly, but LLD requires the import library.
ret.push(FileType {
suffix: ".dll.a".to_string(),
prefix: "lib".to_string(),
flavor: FileFlavor::Normal,
should_replace_hyphens: false,
})
// Window shared library import/export files.
if crate_type.is_dynamic() {
if target_triple.ends_with("-windows-msvc") {
assert!(suffix == ".dll");
// See https://docs.microsoft.com/en-us/cpp/build/reference/working-with-import-libraries-and-export-files
// for more information about DLL import/export files.
ret.push(FileType {
suffix: ".dll.lib".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::Auxiliary,
crate_type: Some(crate_type.clone()),
should_replace_hyphens: true,
});
// NOTE: lld does not produce these
ret.push(FileType {
suffix: ".dll.exp".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::Auxiliary,
crate_type: Some(crate_type.clone()),
should_replace_hyphens: true,
});
} else if target_triple.ends_with("windows-gnu") {
assert!(suffix == ".dll");
// See https://cygwin.com/cygwin-ug-net/dll.html for more
// information about GNU import libraries.
// LD can link DLL directly, but LLD requires the import library.
ret.push(FileType {
suffix: ".dll.a".to_string(),
prefix: "lib".to_string(),
flavor: FileFlavor::Auxiliary,
crate_type: Some(crate_type.clone()),
should_replace_hyphens: true,
})
}
}

// See rust-lang/cargo#4535.
if target_triple.starts_with("wasm32-") && crate_type == CrateType::Bin && suffix == ".js" {
// emscripten binaries generate a .js file, which loads a .wasm
// file.
ret.push(FileType {
suffix: ".wasm".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::Auxiliary,
crate_type: Some(crate_type.clone()),
// Name `foo-bar` will generate a `foo_bar.js` and
// `foo_bar.wasm`. Cargo will translate the underscore and
// copy `foo_bar.js` to `foo-bar.js`. However, the wasm
// filename is embedded in the .js file with an underscore, so
// it should not contain hyphens.
should_replace_hyphens: true,
})
});
// And a map file for debugging. This is only emitted with debug=2
// (-g4 for emcc).
ret.push(FileType {
suffix: ".wasm.map".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::DebugInfo,
crate_type: Some(crate_type.clone()),
should_replace_hyphens: true,
});
}

// See rust-lang/cargo#4490, rust-lang/cargo#4960.
// Only uplift debuginfo for binaries.
// - Tests are run directly from `target/debug/deps/` with the
// metadata hash still in the filename.
// - Examples are only uplifted for apple because the symbol file
// needs to match the executable file name to be found (i.e., it
// needs to remove the hash in the filename). On Windows, the path
// to the .pdb with the hash is embedded in the executable.
// Handle separate debug files.
let is_apple = target_triple.contains("-apple-");
if *kind == TargetKind::Bin || (*kind == TargetKind::ExampleBin && is_apple) {
if matches!(
crate_type,
CrateType::Bin | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro
) {
if is_apple {
let suffix = if crate_type == CrateType::Bin {
".dSYM".to_string()
} else {
".dylib.dSYM".to_string()
};
ret.push(FileType {
suffix: ".dSYM".to_string(),
suffix,
prefix: prefix.clone(),
flavor: FileFlavor::DebugInfo,
crate_type: Some(crate_type.clone()),
// macOS tools like lldb use all sorts of magic to locate
// dSYM files. See https://lldb.llvm.org/use/symbols.html
// for some details. It seems like a `.dSYM` located next
// to the executable with the same name is one method. The
// dSYM should have the same hyphens as the executable for
// the names to match.
should_replace_hyphens: false,
})
} else if target_triple.ends_with("-msvc") {
ret.push(FileType {
suffix: ".pdb".to_string(),
prefix: prefix.clone(),
flavor: FileFlavor::DebugInfo,
// rustc calls the linker with underscores, and the
// filename is embedded in the executable.
crate_type: Some(crate_type.clone()),
// The absolute path to the pdb file is embedded in the
// executable. If the exe/pdb pair is moved to another
// machine, then debuggers will look in the same directory
// of the exe with the original pdb filename. Since the
// original name contains underscores, they need to be
// preserved.
should_replace_hyphens: true,
})
}
Expand Down Expand Up @@ -353,6 +426,62 @@ impl TargetInfo {
&mut output.lines(),
)?)
}

/// Returns all the file types generated by rustc for the given mode/target_kind.
///
/// The first value is a Vec of file types generated, the second value is
/// a list of CrateTypes that are not supported by the given target.
pub fn rustc_outputs(
&self,
mode: CompileMode,
target_kind: &TargetKind,
target_triple: &str,
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
match mode {
CompileMode::Build => self.calc_rustc_outputs(target_kind, target_triple),
CompileMode::Test | CompileMode::Bench => {
match self.file_types(&CrateType::Bin, FileFlavor::Normal, target_triple)? {
Some(fts) => Ok((fts, Vec::new())),
None => Ok((Vec::new(), vec![CrateType::Bin])),
}
}
CompileMode::Check { .. } => Ok((vec![FileType::new_rmeta()], Vec::new())),
CompileMode::Doc { .. } | CompileMode::Doctest | CompileMode::RunCustomBuild => {
panic!("asked for rustc output for non-rustc mode")
}
}
}

fn calc_rustc_outputs(
&self,
target_kind: &TargetKind,
target_triple: &str,
) -> CargoResult<(Vec<FileType>, Vec<CrateType>)> {
let mut unsupported = Vec::new();
let mut result = Vec::new();
let crate_types = target_kind.rustc_crate_types();
for crate_type in &crate_types {
let flavor = if crate_type.is_linkable() {
FileFlavor::Linkable
} else {
FileFlavor::Normal
};
let file_types = self.file_types(&crate_type, flavor, target_triple)?;
match file_types {
Some(types) => {
result.extend(types);
}
None => {
unsupported.push(crate_type.clone());
}
}
}
if !result.is_empty() && !crate_types.iter().any(|ct| ct.requires_upstream_objects()) {
// Only add rmeta if pipelining.
result.push(FileType::new_rmeta());
}
Ok((result, unsupported))
}
}

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

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

0 comments on commit eac3b66

Please sign in to comment.