Skip to content

Commit

Permalink
Support cross compiling to triples without dylibs
Browse files Browse the repository at this point in the history
Discovering the prefix/suffix needs to understand that it's not actually
available.

Closes rust-lang#442
  • Loading branch information
alexcrichton committed Sep 2, 2014
1 parent 70446f9 commit aa9ec52
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 40 deletions.
50 changes: 33 additions & 17 deletions src/cargo/ops/cargo_rustc/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::str;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target};
use util::{mod, CargoResult, ChainError, internal, Config, profile, Require};
use util::human;

use super::{Kind, KindPlugin, KindTarget, Compilation};
use super::layout::{Layout, LayoutProxy};
Expand All @@ -26,9 +27,10 @@ pub struct Context<'a, 'b> {
host: Layout,
target: Option<Layout>,
target_triple: String,
host_dylib: (String, String),
host_triple: String,
host_dylib: Option<(String, String)>,
package_set: &'a PackageSet,
target_dylib: (String, String),
target_dylib: Option<(String, String)>,
target_exe: String,
requirements: HashMap<(&'a PackageId, &'a str), PlatformRequirement>,
}
Expand All @@ -48,10 +50,11 @@ impl<'a, 'b> Context<'a, 'b> {
};
let (rustc_version, rustc_host) = try!(Context::rustc_version());
let target_triple = config.target().map(|s| s.to_string());
let target_triple = target_triple.unwrap_or(rustc_host);
let target_triple = target_triple.unwrap_or(rustc_host.clone());
Ok(Context {
rustc_version: rustc_version,
target_triple: target_triple,
host_triple: rustc_host,
env: env,
host: host,
target: target,
Expand Down Expand Up @@ -93,7 +96,7 @@ impl<'a, 'b> Context<'a, 'b> {
/// Run `rustc` to discover the dylib prefix/suffix for the target
/// specified as well as the exe suffix
fn filename_parts(target: Option<&str>)
-> CargoResult<((String, String), String)> {
-> CargoResult<(Option<(String, String)>, String)> {
let process = util::process("rustc")
.arg("-")
.arg("--crate-name").arg("-")
Expand All @@ -106,17 +109,22 @@ impl<'a, 'b> Context<'a, 'b> {
};
let output = try!(process.exec_with_output());

let error = str::from_utf8(output.error.as_slice()).unwrap();
let output = str::from_utf8(output.output.as_slice()).unwrap();
let mut lines = output.lines();
let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
.split('-').collect();
assert!(dylib_parts.len() == 2,
"rustc --print-file-name output has changed");
let dylib = if error.contains("dropping unsupported crate type `dylib`") {

This comment has been minimized.

Copy link
@brson

brson Sep 2, 2014

Parsing the error text makes me cringe. I wonder how much of rustc's incidental bits of interface cargo is depending on and making de-facto stable.

None
} else {
let dylib_parts: Vec<&str> = lines.next().unwrap().trim()
.split('-').collect();
assert!(dylib_parts.len() == 2,
"rustc --print-file-name output has changed");
Some((dylib_parts[0].to_string(), dylib_parts[1].to_string()))
};

let exe_suffix = lines.next().unwrap().trim()
.split('-').skip(1).next().unwrap().to_string();

Ok(((dylib_parts[0].to_string(), dylib_parts[1].to_string()),
exe_suffix.to_string()))
Ok((dylib, exe_suffix.to_string()))
}

/// Prepare this context, ensuring that all filesystem directories are in
Expand Down Expand Up @@ -193,9 +201,17 @@ impl<'a, 'b> Context<'a, 'b> {
///
/// If `plugin` is true, the pair corresponds to the host platform,
/// otherwise it corresponds to the target platform.
fn dylib(&self, kind: Kind) -> (&str, &str) {
let pair = if kind == KindPlugin {&self.host_dylib} else {&self.target_dylib};
(pair.ref0().as_slice(), pair.ref1().as_slice())
fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
let (triple, pair) = if kind == KindPlugin {
(&self.host_triple, &self.host_dylib)
} else {
(&self.target_triple, &self.target_dylib)
};
match *pair {
None => return Err(human(format!("dylib outputs are not supported \
for {}", triple))),
Some((ref s1, ref s2)) => Ok((s1.as_slice(), s2.as_slice())),
}
}

/// Return the target triple which this context is targeting.
Expand All @@ -204,7 +220,7 @@ impl<'a, 'b> Context<'a, 'b> {
}

/// Return the exact filename of the target.
pub fn target_filenames(&self, target: &Target) -> Vec<String> {
pub fn target_filenames(&self, target: &Target) -> CargoResult<Vec<String>> {
let stem = target.file_stem();

let mut ret = Vec::new();
Expand All @@ -214,7 +230,7 @@ impl<'a, 'b> Context<'a, 'b> {
if target.is_dylib() {
let plugin = target.get_profile().is_plugin();
let kind = if plugin {KindPlugin} else {KindTarget};
let (prefix, suffix) = self.dylib(kind);
let (prefix, suffix) = try!(self.dylib(kind));
ret.push(format!("{}{}{}", prefix, stem, suffix));
}
if target.is_rlib() {
Expand All @@ -225,7 +241,7 @@ impl<'a, 'b> Context<'a, 'b> {
}
}
assert!(ret.len() > 0);
return ret;
return Ok(ret);
}

/// For a package, return all targets which are registered as dependencies
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/ops/cargo_rustc/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub fn prepare_target(cx: &mut Context, pkg: &Package, target: &Target,
if !target.get_profile().is_doc() {
pairs.push((old_dep_info, new_dep_info));

for filename in cx.target_filenames(target).iter() {
for filename in try!(cx.target_filenames(target)).iter() {
let filename = filename.as_slice();
let dst = root.join(filename);
pairs.push((old_root.join(filename), root.join(filename)));
Expand Down
50 changes: 28 additions & 22 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ fn compile<'a, 'b>(targets: &[&'a Target], pkg: &'a Package,
let (mut libs, mut bins) = (Vec::new(), Vec::new());
for &target in targets.iter() {
let work = if target.get_profile().is_doc() {
vec![(rustdoc(pkg, target, cx), KindTarget)]
vec![(try!(rustdoc(pkg, target, cx)), KindTarget)]
} else {
let req = cx.get_requirement(pkg, target);
rustc(pkg, target, cx, req)
try!(rustc(pkg, target, cx, req))
};

let dst = if target.is_lib() {&mut libs} else {&mut bins};
Expand Down Expand Up @@ -188,15 +188,16 @@ fn compile_custom(pkg: &Package, cmd: &str,
}

fn rustc(package: &Package, target: &Target,
cx: &mut Context, req: PlatformRequirement) -> Vec<(Work, Kind)> {
cx: &mut Context, req: PlatformRequirement)
-> CargoResult<Vec<(Work, Kind)> >{
let crate_types = target.rustc_crate_types();
let root = package.get_root();

log!(5, "root={}; target={}; crate_types={}; verbose={}; req={}",
root.display(), target, crate_types, cx.primary, req);

let primary = cx.primary;
let rustcs = prepare_rustc(package, target, crate_types, cx, req);
let rustcs = try!(prepare_rustc(package, target, crate_types, cx, req));

let _ = cx.config.shell().verbose(|shell| {
for &(ref rustc, _) in rustcs.iter() {
Expand All @@ -205,7 +206,7 @@ fn rustc(package: &Package, target: &Target,
Ok(())
});

rustcs.move_iter().map(|(rustc, kind)| {
Ok(rustcs.move_iter().map(|(rustc, kind)| {
let name = package.get_name().to_string();

(proc() {
Expand All @@ -223,40 +224,43 @@ fn rustc(package: &Package, target: &Target,
}
Ok(())
}, kind)
}).collect()
}).collect())
}

fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
cx: &Context, req: PlatformRequirement)
-> Vec<(ProcessBuilder, Kind)> {
-> CargoResult<Vec<(ProcessBuilder, Kind)>> {
let base = process("rustc", package, cx);
let base = build_base_args(base, target, crate_types.as_slice());

let target_cmd = build_plugin_args(base.clone(), cx, package, target, KindTarget);
let plugin_cmd = build_plugin_args(base, cx, package, target, KindPlugin);
let target_cmd = build_deps_args(target_cmd, target, package, cx, KindTarget);
let plugin_cmd = build_deps_args(plugin_cmd, target, package, cx, KindPlugin);
let target_cmd = try!(build_deps_args(target_cmd, target, package, cx,
KindTarget));
let plugin_cmd = try!(build_deps_args(plugin_cmd, target, package, cx,
KindPlugin));

match req {
Ok(match req {
Target => vec![(target_cmd, KindTarget)],
Plugin => vec![(plugin_cmd, KindPlugin)],
PluginAndTarget if cx.config.target().is_none() =>
vec![(target_cmd, KindTarget)],
PluginAndTarget => vec![(target_cmd, KindTarget),
(plugin_cmd, KindPlugin)],
}
})
}


fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
fn rustdoc(package: &Package, target: &Target,
cx: &mut Context) -> CargoResult<Work> {
let kind = KindTarget;
let pkg_root = package.get_root();
let cx_root = cx.layout(kind).proxy().dest().join("doc");
let rustdoc = process("rustdoc", package, cx).cwd(pkg_root.clone());
let rustdoc = rustdoc.arg(target.get_src_path())
.arg("-o").arg(cx_root)
.arg("--crate-name").arg(target.get_name());
let rustdoc = build_deps_args(rustdoc, target, package, cx, kind);
let rustdoc = try!(build_deps_args(rustdoc, target, package, cx, kind));

log!(5, "commands={}", rustdoc);

Expand All @@ -266,7 +270,7 @@ fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {

let primary = cx.primary;
let name = package.get_name().to_string();
proc() {
Ok(proc() {
if primary {
try!(rustdoc.exec().chain_error(|| {
human(format!("Could not document `{}`.", name))
Expand All @@ -278,7 +282,7 @@ fn rustdoc(package: &Package, target: &Target, cx: &mut Context) -> Work {
}))
}
Ok(())
}
})
}

fn build_base_args(mut cmd: ProcessBuilder,
Expand Down Expand Up @@ -355,7 +359,8 @@ fn build_plugin_args(mut cmd: ProcessBuilder, cx: &Context, pkg: &Package,
}

fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
cx: &Context, kind: Kind) -> ProcessBuilder {
cx: &Context,
kind: Kind) -> CargoResult<ProcessBuilder> {
enum LinkReason { Dependency, LocalLib }

let layout = cx.layout(kind);
Expand All @@ -367,7 +372,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
cmd = push_native_dirs(cmd, &layout, package, cx, &mut HashSet::new());

for &(_, target) in cx.dep_targets(package).iter() {
cmd = link_to(cmd, target, cx, kind, Dependency);
cmd = try!(link_to(cmd, target, cx, kind, Dependency));
}

let mut targets = package.get_targets().iter().filter(|target| {
Expand All @@ -376,14 +381,15 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,

if target.is_bin() {
for target in targets {
cmd = link_to(cmd, target, cx, kind, LocalLib);
cmd = try!(link_to(cmd, target, cx, kind, LocalLib));
}
}

return cmd;
return Ok(cmd);

fn link_to(mut cmd: ProcessBuilder, target: &Target,
cx: &Context, kind: Kind, reason: LinkReason) -> ProcessBuilder {
cx: &Context, kind: Kind,
reason: LinkReason) -> CargoResult<ProcessBuilder> {
// If this target is itself a plugin *or* if it's being linked to a
// plugin, then we want the plugin directory. Otherwise we want the
// target directory (hence the || here).
Expand All @@ -393,7 +399,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
KindTarget => KindTarget,
});

for filename in cx.target_filenames(target).iter() {
for filename in try!(cx.target_filenames(target)).iter() {
let mut v = Vec::new();
v.push_all(target.get_name().as_bytes());
v.push(b'=');
Expand All @@ -405,7 +411,7 @@ fn build_deps_args(mut cmd: ProcessBuilder, target: &Target, package: &Package,
v.push_all(filename.as_bytes());
cmd = cmd.arg("--extern").arg(v.as_slice());
}
return cmd;
return Ok(cmd);
}

fn push_native_dirs(mut cmd: ProcessBuilder, layout: &layout::LayoutProxy,
Expand Down
19 changes: 19 additions & 0 deletions tests/test_cargo_cross_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,22 @@ test!(simple_cargo_run {
assert_that(p.cargo_process("run").arg("--target").arg(target),
execs().with_status(0));
})

test!(cross_but_no_dylibs {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.0.0"
authors = []
[lib]
name = "foo"
crate-type = ["dylib"]
"#)
.file("src/lib.rs", "");
assert_that(p.cargo_process("build").arg("--target").arg("arm-apple-ios"),
execs().with_status(101)
.with_stderr("dylib outputs are not supported for \
arm-apple-ios"));
})

2 comments on commit aa9ec52

@brson
Copy link

@brson brson commented on aa9ec52 Sep 2, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r+

@alexcrichton
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors: retry

Please sign in to comment.