Handle target specific outputs such as .wasm or .dll.lib
authorTatsuya Kawano <tatsuya@hibaridb.org>
Tue, 3 Oct 2017 11:44:35 +0000 (19:44 +0800)
committerTatsuya Kawano <tatsuya@hibaridb.org>
Tue, 3 Oct 2017 11:44:35 +0000 (19:44 +0800)
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 [changed mode: 0755->0644]
tests/build.rs

old mode 100755 (executable)
new mode 100644 (file)
index b4435f2..0ee0614
@@ -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
+}
index 3bc34ca3a2b0299a47a8ec59b0c1a717e009e940..8105b913bc82b1b3ed931bf9a286c4261d99e336 100644 (file)
@@ -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::<Vec<_>>();
+    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::<Vec<_>>();
+    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