Skip to content

Commit 463e850

Browse files
committed
Auto merge of #4570 - tatsuya6502:target-specific-artifacts, r=alexcrichton
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. **EDIT**: I added [a summary of changes](#4570 (comment)). Please read it for more details of changes. **IMPORTANT** Although I added test cases for both wasm32-unknown-emscripten and *-pc-windows-msvc cdylib targets, ~I did not do manual testing on wasm32-unknown-emscripten target as I do not have an environment with emscripten installed. It will be appreciated if anybody tests this change for wasm32-unknown-emscripten target.~ **EDIT**: Tested for both wasm32-unknown-emscripten and x86_64-pc-windows-msvc. Thanks @Herschel for the help.
2 parents 8031ef1 + f9a541b commit 463e850

File tree

2 files changed

+192
-7
lines changed

2 files changed

+192
-7
lines changed

src/cargo/ops/cargo_rustc/context.rs

100755100644
Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
507507
// - OSX encodes the dylib name in the executable
508508
// - Windows rustc multiple files of which we can't easily link all of them
509509
//
510+
// No metadata for bin because of an issue
511+
// - wasm32 rustc/emcc encodes the .wasm name in the .js (rust-lang/cargo#4535)
512+
//
510513
// Two exceptions
511514
// 1) Upstream dependencies (we aren't exporting + need to resolve name conflict)
512515
// 2) __CARGO_DEFAULT_LIB_METADATA env var
@@ -522,9 +525,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
522525
// doing this eventually.
523526
let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA");
524527
if !unit.profile.test &&
525-
(unit.target.is_dylib() || unit.target.is_cdylib()) &&
528+
(unit.target.is_dylib() || unit.target.is_cdylib() ||
529+
(unit.target.is_bin() && self.target_triple().starts_with("wasm32-"))) &&
526530
unit.pkg.package_id().source_id().is_path() &&
527-
!__cargo_default_lib_metadata.is_ok() {
531+
!__cargo_default_lib_metadata.is_ok()
532+
{
528533
return None;
529534
}
530535

@@ -690,11 +695,30 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
690695
};
691696
match *crate_type_info {
692697
Some((ref prefix, ref suffix)) => {
693-
let filename = out_dir.join(format!("{}{}{}", prefix, stem, suffix));
694-
let link_dst = link_stem.clone().map(|(ld, ls)| {
695-
ld.join(format!("{}{}{}", prefix, ls, suffix))
696-
});
697-
ret.push((filename, link_dst, linkable));
698+
let suffixes = add_target_specific_suffixes(
699+
&self.target_triple(),
700+
&crate_type,
701+
suffix,
702+
linkable,
703+
);
704+
for (suffix, linkable, should_replace_hyphens) in suffixes {
705+
// wasm bin target will generate two files in deps such as
706+
// "web-stuff.js" and "web_stuff.wasm". Note the different usages of
707+
// "-" and "_". should_replace_hyphens is a flag to indicate that
708+
// we need to convert the stem "web-stuff" to "web_stuff", so we
709+
// won't miss "web_stuff.wasm".
710+
let conv = |s: String| if should_replace_hyphens {
711+
s.replace("-", "_")
712+
} else {
713+
s
714+
};
715+
let filename =
716+
out_dir.join(format!("{}{}{}", prefix, conv(stem.clone()), suffix));
717+
let link_dst = link_stem.clone().map(|(ld, ls)| {
718+
ld.join(format!("{}{}{}", prefix, conv(ls), suffix))
719+
});
720+
ret.push((filename, link_dst, linkable));
721+
}
698722
Ok(())
699723
}
700724
// not supported, don't worry about it
@@ -1231,3 +1255,33 @@ fn parse_crate_type(
12311255

12321256
Ok(Some((prefix.to_string(), suffix.to_string())))
12331257
}
1258+
1259+
// (not a rustdoc)
1260+
// Return a list of 3-tuples (suffix, linkable, should_replace_hyphens).
1261+
//
1262+
// should_replace_hyphens will be used by the caller to replace "-" with "_"
1263+
// in a bin_stem. See the caller side (calc_target_filenames()) for details.
1264+
fn add_target_specific_suffixes(
1265+
target_triple: &str,
1266+
crate_type: &str,
1267+
suffix: &str,
1268+
linkable: bool,
1269+
) -> Vec<(String, bool, bool)> {
1270+
let mut ret = vec![(suffix.to_string(), linkable, false)];
1271+
1272+
// rust-lang/cargo#4500
1273+
if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib") &&
1274+
suffix == ".dll"
1275+
{
1276+
ret.push((".dll.lib".to_string(), false, false));
1277+
}
1278+
1279+
// rust-lang/cargo#4535
1280+
if target_triple.starts_with("wasm32-") && crate_type == "bin" &&
1281+
suffix == ".js"
1282+
{
1283+
ret.push((".wasm".to_string(), false, true));
1284+
}
1285+
1286+
ret
1287+
}

tests/build.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,137 @@ fn cdylib_not_lifted() {
33663366
}
33673367
}
33683368

3369+
#[test]
3370+
fn cdylib_final_outputs() {
3371+
let p = project("foo")
3372+
.file("Cargo.toml", r#"
3373+
[project]
3374+
name = "foo-bar"
3375+
authors = []
3376+
version = "0.1.0"
3377+
3378+
[lib]
3379+
crate-type = ["cdylib"]
3380+
"#)
3381+
.file("src/lib.rs", "");
3382+
3383+
assert_that(p.cargo_process("build"), execs().with_status(0));
3384+
3385+
let files = if cfg!(windows) {
3386+
vec!["foo_bar.dll.lib", "foo_bar.dll"]
3387+
} else if cfg!(target_os = "macos") {
3388+
vec!["libfoo_bar.dylib"]
3389+
} else {
3390+
vec!["libfoo_bar.so"]
3391+
};
3392+
3393+
for file in files {
3394+
println!("checking: {}", file);
3395+
assert_that(&p.root().join("target/debug").join(&file), existing_file());
3396+
}
3397+
}
3398+
3399+
#[test]
3400+
fn wasm32_final_outputs() {
3401+
use cargo::core::{Shell, Target, Workspace};
3402+
use cargo::ops::{self, BuildConfig, Context, CompileMode, CompileOptions, Kind, Unit};
3403+
use cargo::util::Config;
3404+
use cargo::util::important_paths::find_root_manifest_for_wd;
3405+
3406+
let target_triple = "wasm32-unknown-emscripten";
3407+
3408+
let p = project("foo")
3409+
.file("Cargo.toml", r#"
3410+
[project]
3411+
name = "foo-bar"
3412+
authors = []
3413+
version = "0.1.0"
3414+
"#)
3415+
.file("src/main.rs", "fn main() {}");
3416+
p.build();
3417+
3418+
// We can't cross-compile the project to wasm target unless we have emscripten installed.
3419+
// So here we will not run `cargo build`, but just create cargo_rustc::Context and ask it
3420+
// what the target file names would be.
3421+
3422+
// Create various stuff required to build cargo_rustc::Context.
3423+
let shell = Shell::new();
3424+
let config = Config::new(shell, p.root(), p.root());
3425+
let root = find_root_manifest_for_wd(None, config.cwd()).expect("Can't find the root manifest");
3426+
let ws = Workspace::new(&root, &config).expect("Can't create workspace");
3427+
3428+
let opts = CompileOptions {
3429+
target: Some(target_triple),
3430+
.. CompileOptions::default(&config, CompileMode::Build)
3431+
};
3432+
3433+
let specs = opts.spec.into_package_id_specs(&ws).expect("Can't create specs");
3434+
3435+
let (packages, resolve) = ops::resolve_ws_precisely(
3436+
&ws,
3437+
None,
3438+
opts.features,
3439+
opts.all_features,
3440+
opts.no_default_features,
3441+
&specs,
3442+
).expect("Can't create resolve");
3443+
3444+
let build_config = BuildConfig {
3445+
requested_target: Some(target_triple.to_string()),
3446+
jobs: 1,
3447+
.. BuildConfig::default()
3448+
};
3449+
3450+
let pkgid = packages
3451+
.package_ids()
3452+
.filter(|id| id.name() == "foo-bar")
3453+
.collect::<Vec<_>>();
3454+
let pkg = packages.get(pkgid[0]).expect("Can't get package");
3455+
3456+
let target = Target::bin_target("foo-bar", p.root().join("src/main.rs"), None);
3457+
3458+
let unit = Unit {
3459+
pkg: &pkg,
3460+
target: &target,
3461+
profile: &ws.profiles().dev,
3462+
kind: Kind::Target,
3463+
};
3464+
let units = vec![unit];
3465+
3466+
// Finally, create the cargo_rustc::Context.
3467+
let mut ctx = Context::new(
3468+
&ws,
3469+
&resolve,
3470+
&packages,
3471+
&config,
3472+
build_config,
3473+
ws.profiles(),
3474+
).expect("Can't create context");
3475+
3476+
// Ask the context to resolve target file names.
3477+
ctx.probe_target_info(&units).expect("Can't probe target info");
3478+
let target_filenames = ctx.target_filenames(&unit).expect("Can't get target file names");
3479+
3480+
// Verify the result.
3481+
let mut expected = vec!["debug/foo-bar.js", "debug/foo_bar.wasm"];
3482+
3483+
assert_eq!(target_filenames.len(), expected.len());
3484+
3485+
let mut target_filenames = target_filenames
3486+
.iter()
3487+
.map(|&(_, ref link_dst, _)| link_dst.clone().unwrap())
3488+
.collect::<Vec<_>>();
3489+
target_filenames.sort();
3490+
expected.sort();
3491+
3492+
for (expected, actual) in expected.iter().zip(target_filenames.iter()) {
3493+
assert!(
3494+
actual.ends_with(expected),
3495+
format!("{:?} does not end with {}", actual, expected)
3496+
);
3497+
}
3498+
}
3499+
33693500
#[test]
33703501
fn deterministic_cfg_flags() {
33713502
// This bug is non-deterministic

0 commit comments

Comments
 (0)