From c4b56e2a34662bc93012af1d3bc2e7c1c6d46406 Mon Sep 17 00:00:00 2001 From: Tatsuya Kawano Date: Tue, 3 Oct 2017 19:44:35 +0800 Subject: [PATCH] Handle target specific outputs such as .wasm or .dll.lib Fixes #4500, #4535 Until now, Cargo does not have any knowledge about target-specific output files. It relies solely on rustc for this sort of information (`rustc --print=file-names ...`). As a result, Cargo does not place some build artifacts (files) to target/{debug,release} directory. These files include *.wasm for wasm32-unknown-emscripten target and *.dll.lib for *-pc-windows-msvc cdylib target. This commit will add such knowledge to Cargo so that *.wasm and *.dll.lib will be placed in target/{debug,release} directory. --- src/cargo/ops/cargo_rustc/context.rs | 44 ++++++++- tests/build.rs | 131 +++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 5 deletions(-) mode change 100755 => 100644 src/cargo/ops/cargo_rustc/context.rs diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs old mode 100755 new mode 100644 index b4435f213..0ee0614a7 --- a/src/cargo/ops/cargo_rustc/context.rs +++ b/src/cargo/ops/cargo_rustc/context.rs @@ -646,11 +646,20 @@ impl<'a, 'cfg> Context<'a, 'cfg> { }; match *crate_type_info { Some((ref prefix, ref suffix)) => { - let filename = out_dir.join(format!("{}{}{}", prefix, stem, suffix)); - let link_dst = link_stem.clone().map(|(ld, ls)| { - ld.join(format!("{}{}{}", prefix, ls, suffix)) - }); - ret.push((filename, link_dst, linkable)); + let suffixes = add_target_specific_suffixes( + &self.target_triple(), + &crate_type, + suffix, + linkable, + ); + for (suffix, linkable) in suffixes { + let filename = + out_dir.join(format!("{}{}{}", prefix, stem, suffix)); + let link_dst = link_stem.clone().map(|(ld, ls)| { + ld.join(format!("{}{}{}", prefix, ls, suffix)) + }); + ret.push((filename, link_dst, linkable)); + } Ok(()) } // not supported, don't worry about it @@ -1164,3 +1173,28 @@ fn parse_crate_type( Ok(Some((prefix.to_string(), suffix.to_string()))) } + +fn add_target_specific_suffixes( + target_triple: &str, + crate_type: &str, + suffix: &str, + linkable: bool, +) -> Vec<(String, bool)> { + let mut suffixes = vec![(suffix.to_string(), linkable)]; + + // rust-lang/cargo#4500 + if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib") && + suffix == ".dll" + { + suffixes.push((format!("{}.lib", suffix), false)); + } + + // rust-lang/cargo#4535 + if target_triple == "wasm32-unknown-emscripten" && crate_type == "bin" && + suffix == ".js" + { + suffixes.push((".wasm".to_string(), false)); + } + + suffixes +} diff --git a/tests/build.rs b/tests/build.rs index 3bc34ca3a..8105b913b 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -3366,6 +3366,137 @@ fn cdylib_not_lifted() { } } +#[test] +fn cdylib_final_outputs() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + authors = [] + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#) + .file("src/lib.rs", ""); + + assert_that(p.cargo_process("build"), execs().with_status(0)); + + let files = if cfg!(windows) { + vec!["foo.dll.lib", "foo.dll"] + } else if cfg!(target_os = "macos") { + vec!["libfoo.dylib"] + } else { + vec!["libfoo.so"] + }; + + for file in files { + println!("checking: {}", file); + assert_that(&p.root().join("target/debug").join(&file), existing_file()); + } +} + +#[test] +fn wasm32_final_outputs() { + use cargo::core::{Shell, Target, Workspace}; + use cargo::ops::{self, BuildConfig, Context, CompileMode, CompileOptions, Kind, Unit}; + use cargo::util::Config; + use cargo::util::important_paths::find_root_manifest_for_wd; + + let target_triple = "wasm32-unknown-emscripten"; + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + authors = [] + version = "0.1.0" + "#) + .file("src/main.rs", "fn main() {}"); + p.build(); + + // We can't cross-compile the project to wasm target unless we have emscripten installed. + // So here we will not run `cargo build`, but just create cargo_rustc::Context and ask it + // what the target file names would be. + + // Create various stuff required to build cargo_rustc::Context. + let shell = Shell::new(); + let config = Config::new(shell, p.root(), p.root()); + let root = find_root_manifest_for_wd(None, config.cwd()).expect("Can't find the root manifest"); + let ws = Workspace::new(&root, &config).expect("Can't create workspace"); + + let opts = CompileOptions { + target: Some(target_triple), + .. CompileOptions::default(&config, CompileMode::Build) + }; + + let specs = opts.spec.into_package_id_specs(&ws).expect("Can't create specs"); + + let (packages, resolve) = ops::resolve_ws_precisely( + &ws, + None, + opts.features, + opts.all_features, + opts.no_default_features, + &specs, + ).expect("Can't create resolve"); + + let build_config = BuildConfig { + requested_target: Some(target_triple.to_string()), + jobs: 1, + .. BuildConfig::default() + }; + + let pkgid = packages + .package_ids() + .filter(|id| id.name() == "foo") + .collect::>(); + let pkg = packages.get(pkgid[0]).expect("Can't get package"); + + let target = Target::bin_target("foo", p.root().join("src/main.rs"), None); + + let unit = Unit { + pkg: &pkg, + target: &target, + profile: &ws.profiles().dev, + kind: Kind::Target, + }; + let units = vec![unit]; + + // Finally, create the cargo_rustc::Context. + let mut ctx = Context::new( + &ws, + &resolve, + &packages, + &config, + build_config, + ws.profiles(), + ).expect("Can't create context"); + + // Ask the context to resolve target file names. + ctx.probe_target_info(&units).expect("Can't probe target info"); + let target_filenames = ctx.target_filenames(&unit).expect("Can't get target file names"); + + // Verify the result. + let mut expected = vec!["debug/foo.js", "debug/foo.wasm"]; + + assert_eq!(target_filenames.len(), expected.len()); + + let mut target_filenames = target_filenames + .iter() + .map(|&(_, ref link_dst, _)| link_dst.clone().unwrap()) + .collect::>(); + target_filenames.sort(); + expected.sort(); + + for (expected, actual) in expected.iter().zip(target_filenames.iter()) { + assert!( + actual.ends_with(expected), + format!("{:?} does not end with {}", actual, expected) + ); + } +} + #[test] fn deterministic_cfg_flags() { // This bug is non-deterministic -- 2.30.2