diff --git a/src/bin/bench.rs b/src/bin/bench.rs index ec5e9272299..dd5bb5ee564 100644 --- a/src/bin/bench.rs +++ b/src/bin/bench.rs @@ -75,6 +75,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustc_args: None, }, }; diff --git a/src/bin/build.rs b/src/bin/build.rs index cb925510abb..3af175ec1b4 100644 --- a/src/bin/build.rs +++ b/src/bin/build.rs @@ -76,6 +76,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustc_args: None, }; ops::compile(&root, &opts).map(|_| None).map_err(|err| { diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index a0490a96dcc..eb5ed1b6580 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -75,6 +75,7 @@ macro_rules! each_subcommand{ ($mac:ident) => ({ $mac!(publish); $mac!(read_manifest); $mac!(run); + $mac!(rustc); $mac!(search); $mac!(test); $mac!(update); diff --git a/src/bin/doc.rs b/src/bin/doc.rs index ed05f9f55b3..bd1127e2fe5 100644 --- a/src/bin/doc.rs +++ b/src/bin/doc.rs @@ -62,6 +62,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { mode: ops::CompileMode::Doc { deps: !options.flag_no_deps, }, + target_rustc_args: None, }, }; diff --git a/src/bin/run.rs b/src/bin/run.rs index 9ff8728fa3a..c34c36be1b4 100644 --- a/src/bin/run.rs +++ b/src/bin/run.rs @@ -72,6 +72,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { bins: &bins, examples: &examples, } }, + target_rustc_args: None, }; let err = try!(ops::run(&root, diff --git a/src/bin/rustc.rs b/src/bin/rustc.rs new file mode 100644 index 00000000000..f91c7d89c13 --- /dev/null +++ b/src/bin/rustc.rs @@ -0,0 +1,90 @@ +use std::env; + +use cargo::ops::CompileOptions; +use cargo::ops; +use cargo::util::important_paths::{find_root_manifest_for_cwd}; +use cargo::util::{CliResult, CliError, Config}; + +#[derive(RustcDecodable)] +struct Options { + arg_opts: Option>, + flag_package: Option, + flag_jobs: Option, + flag_features: Vec, + flag_no_default_features: bool, + flag_target: Option, + flag_manifest_path: Option, + flag_verbose: bool, + flag_release: bool, + flag_lib: bool, + flag_bin: Vec, + flag_example: Vec, + flag_test: Vec, + flag_bench: Vec, +} + +pub const USAGE: &'static str = " +Compile a package and all of its dependencies + +Usage: + cargo rustc [options] [--] [...] + +Options: + -h, --help Print this message + -p SPEC, --package SPEC The profile to compile for + -j N, --jobs N The number of jobs to run in parallel + --lib Build only this package's library + --bin NAME Build only the specified binary + --example NAME Build only the specified example + --test NAME Build only the specified test + --bench NAME Build only the specified benchmark + --release Build artifacts in release mode, with optimizations + --features FEATURES Features to compile for the package + --no-default-features Do not compile default features for the package + --target TRIPLE Target triple which compiles will be for + --manifest-path PATH Path to the manifest to fetch depednencies for + -v, --verbose Use verbose output + +The specified target for the current package (or package specified by SPEC if +provided) will be compiled along with all of its dependencies. The specified +... will all be passed to the final compiler invocation, not any of the +dependencies. Note that the compiler will still unconditionally receive +arguments such as -L, --extern, and --crate-type, and the specified ... +will simply be added to the compiler invocation. + +This command requires that only one target is being compiled. If more than one +target is available for the current package the filters of --lib, --bin, etc, +must be used to select which target is compiled. +"; + +pub fn execute(options: Options, config: &Config) -> CliResult> { + debug!("executing; cmd=cargo-rustc; args={:?}", + env::args().collect::>()); + config.shell().set_verbose(options.flag_verbose); + + let root = try!(find_root_manifest_for_cwd(options.flag_manifest_path)); + + let opts = CompileOptions { + config: config, + jobs: options.flag_jobs, + target: options.flag_target.as_ref().map(|t| &t[..]), + features: &options.flag_features, + no_default_features: options.flag_no_default_features, + spec: options.flag_package.as_ref().map(|s| &s[..]), + exec_engine: None, + mode: ops::CompileMode::Build, + release: options.flag_release, + filter: ops::CompileFilter::new(options.flag_lib, + &options.flag_bin, + &options.flag_test, + &options.flag_example, + &options.flag_bench), + target_rustc_args: options.arg_opts.as_ref().map(|a| &a[..]), + }; + + ops::compile(&root, &opts).map(|_| None).map_err(|err| { + CliError::from_boxed(err, 101) + }) +} + + diff --git a/src/bin/test.rs b/src/bin/test.rs index f7a7ba55572..6199f917be4 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -79,6 +79,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult> { &options.flag_test, &options.flag_example, &options.flag_bench), + target_rustc_args: None, }, }; diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index e9b9e154a7f..6c2c5edfc28 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -116,6 +116,7 @@ pub struct Profile { pub opt_level: u32, pub lto: bool, pub codegen_units: Option, // None = use rustc default + pub rustc_args: Option>, pub debuginfo: bool, pub debug_assertions: bool, pub rpath: bool, @@ -464,6 +465,7 @@ impl Default for Profile { opt_level: 0, lto: false, codegen_units: None, + rustc_args: None, debuginfo: false, debug_assertions: false, rpath: false, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 6231dcb1d8b..f2b276cf7ee 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -58,6 +58,9 @@ pub struct CompileOptions<'a, 'b: 'a> { pub release: bool, /// Mode for this compile. pub mode: CompileMode, + /// The specified target will be compiled with all the available arguments, + /// note that this only accounts for the *final* invocation of rustc + pub target_rustc_args: Option<&'a [String]>, } #[derive(Clone, Copy, PartialEq)] @@ -102,7 +105,8 @@ pub fn compile_pkg(package: &Package, options: &CompileOptions) -> CargoResult { let CompileOptions { config, jobs, target, spec, features, no_default_features, release, mode, - ref filter, ref exec_engine } = *options; + ref filter, ref exec_engine, + ref target_rustc_args } = *options; let target = target.map(|s| s.to_string()); let features = features.iter().flat_map(|s| { @@ -163,6 +167,23 @@ pub fn compile_pkg(package: &Package, options: &CompileOptions) let to_build = packages.iter().find(|p| p.package_id() == pkgid).unwrap(); let targets = try!(generate_targets(to_build, mode, filter, release)); + let target_with_args = match *target_rustc_args { + Some(args) if targets.len() == 1 => { + let (target, profile) = targets[0]; + let mut profile = profile.clone(); + profile.rustc_args = Some(args.to_vec()); + Some((target, profile)) + } + Some(_) => + return Err(human("extra arguments to `rustc` can only be passed to one target, \ + consider filtering\nthe package by passing e.g. `--lib` or \ + `--bin NAME` to specify a single target")), + None => None, + }; + + let targets = target_with_args.as_ref().map(|&(t, ref p)| vec![(t, p)]) + .unwrap_or(targets); + let ret = { let _p = profile::start("compiling"); let mut build_config = try!(scrape_build_config(config, jobs, target)); diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 0392580a8ca..892512d543a 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -189,6 +189,7 @@ fn run_verify(config: &Config, pkg: &Package, tar: &Path) exec_engine: None, release: false, mode: ops::CompileMode::Build, + target_rustc_args: None, })); Ok(()) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index e1535bb8f3e..a06f7a6e1cd 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -620,8 +620,8 @@ fn build_base_args(cx: &Context, profile: &Profile, crate_types: &[&str]) { let Profile { - opt_level, lto, codegen_units, debuginfo, debug_assertions, rpath, test, - doc: _doc, + opt_level, lto, codegen_units, ref rustc_args, debuginfo, debug_assertions, + rpath, test, doc: _doc, } = *profile; // Move to cwd so the root_path() passed below is actually correct @@ -664,6 +664,10 @@ fn build_base_args(cx: &Context, cmd.arg("-g"); } + if let Some(ref args) = *rustc_args { + cmd.args(args); + } + if debug_assertions && opt_level > 0 { cmd.args(&["-C", "debug-assertions=on"]); } else if !debug_assertions && opt_level == 0 { diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 3a50195ee65..35450030ebd 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -814,6 +814,7 @@ fn build_profiles(profiles: &Option) -> Profiles { opt_level: opt_level.unwrap_or(profile.opt_level), lto: lto.unwrap_or(profile.lto), codegen_units: codegen_units, + rustc_args: None, debuginfo: debug.unwrap_or(profile.debuginfo), debug_assertions: debug_assertions.unwrap_or(profile.debug_assertions), rpath: rpath.unwrap_or(profile.rpath), diff --git a/tests/test_cargo_rustc.rs b/tests/test_cargo_rustc.rs new file mode 100644 index 00000000000..8000517932c --- /dev/null +++ b/tests/test_cargo_rustc.rs @@ -0,0 +1,298 @@ +use std::path::MAIN_SEPARATOR as SEP; +use support::{execs, project}; +use support::{COMPILING, RUNNING}; +use hamcrest::{assert_that}; + +fn setup() { +} + +fn cargo_rustc_error() -> &'static str { + "extra arguments to `rustc` can only be passed to one target, consider filtering\n\ + the package by passing e.g. `--lib` or `--bin NAME` to specify a single target" +} + +test!(build_lib_for_foo { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("--lib").arg("-v"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.1 ({url}) +{running} `rustc src{sep}lib.rs --crate-name foo --crate-type lib -g \ + --out-dir {dir}{sep}target{sep}debug \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +", + running = RUNNING, compiling = COMPILING, sep = SEP, + dir = p.root().display(), url = p.url()))); +}); + +test!(build_lib_and_allow_unstable_options { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("--lib").arg("-v") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.1 ({url}) +{running} `rustc src{sep}lib.rs --crate-name foo --crate-type lib -g \ + -Z unstable-options \ + --out-dir {dir}{sep}target{sep}debug \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +", + running = RUNNING, compiling = COMPILING, sep = SEP, + dir = p.root().display(), url = p.url()))) +}); + +test!(build_main_and_allow_unstable_options { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("-v").arg("--bin").arg("foo") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(&format!("\ +{compiling} {name} v{version} ({url}) +{running} `rustc src{sep}lib.rs --crate-name {name} --crate-type lib -g \ + --out-dir {dir}{sep}target{sep}debug \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps` +{running} `rustc src{sep}main.rs --crate-name {name} --crate-type bin -g \ + -Z unstable-options \ + --out-dir {dir}{sep}target{sep}debug \ + --emit=dep-info,link \ + -L dependency={dir}{sep}target{sep}debug \ + -L dependency={dir}{sep}target{sep}debug{sep}deps \ + --extern {name}={dir}{sep}target{sep}debug{sep}lib{name}.rlib` +", + running = RUNNING, compiling = COMPILING, sep = SEP, + dir = p.root().display(), url = p.url(), + name = "foo", version = "0.0.1"))); +}); + +test!(fails_when_trying_to_build_main_and_lib_with_args { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("-v") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(101) + .with_stderr(cargo_rustc_error())); +}); + +test!(build_with_args_to_one_of_multiple_binaries { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/foo.rs", r#" + fn main() {} + "#) + .file("src/bin/bar.rs", r#" + fn main() {} + "#) + .file("src/bin/baz.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("-v").arg("--bin").arg("bar") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.1 ({url}) +{running} `rustc src{sep}lib.rs --crate-name foo --crate-type lib -g \ + --out-dir {dir}{sep}target{sep}debug [..]` +{running} `rustc src{sep}bin{sep}bar.rs --crate-name bar --crate-type bin -g \ + -Z unstable-options [..]` +", + compiling = COMPILING, running = RUNNING, sep = SEP, + dir = p.root().display(), url = p.url()))); +}); + +test!(fails_with_args_to_all_binaries { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/foo.rs", r#" + fn main() {} + "#) + .file("src/bin/bar.rs", r#" + fn main() {} + "#) + .file("src/bin/baz.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("-v") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(101) + .with_stderr(cargo_rustc_error())); +}); + +test!(build_with_args_to_one_of_multiple_tests { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("tests/foo.rs", r#" "#) + .file("tests/bar.rs", r#" "#) + .file("tests/baz.rs", r#" "#) + .file("src/lib.rs", r#" "#); + + assert_that(p.cargo_process("rustc").arg("-v").arg("--test").arg("bar") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} foo v0.0.1 ({url}) +{running} `rustc src{sep}lib.rs --crate-name foo --crate-type lib -g \ + --out-dir {dir}{sep}target{sep}debug [..]` +{running} `rustc tests{sep}bar.rs --crate-name bar --crate-type bin -g \ + -Z unstable-options [..]--test[..]` +", + compiling = COMPILING, running = RUNNING, sep = SEP, + dir = p.root().display(), url = p.url()))); +}); + +test!(build_foo_with_bar_dependency { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#) + .file("src/main.rs", r#" + extern crate bar; + fn main() { + bar::baz() + } + "#); + let bar = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn baz() {} + "#); + bar.build(); + + assert_that(foo.cargo_process("rustc").arg("-v").arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} bar v0.1.0 ({url}) +{running} `[..] -g -C [..]` +{compiling} foo v0.0.1 ({url}) +{running} `[..] -g -Z unstable-options [..]` +", + compiling = COMPILING, running = RUNNING, + url = foo.url()))); +}); + +test!(build_only_bar_dependency { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + "#) + .file("src/main.rs", r#" + extern crate bar; + fn main() { + bar::baz() + } + "#); + let bar = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn baz() {} + "#); + bar.build(); + + assert_that(foo.cargo_process("rustc").arg("-v").arg("-p").arg("bar") + .arg("--").arg("-Z").arg("unstable-options"), + execs() + .with_status(0) + .with_stdout(format!("\ +{compiling} bar v0.1.0 ({url}) +{running} `[..]--crate-name bar --crate-type lib [..] -Z unstable-options [..]` +", + compiling = COMPILING, running = RUNNING, + url = foo.url()))); +}); diff --git a/tests/tests.rs b/tests/tests.rs index 8be26db87c6..4d3ff6d771a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -48,6 +48,7 @@ mod test_cargo_profiles; mod test_cargo_publish; mod test_cargo_registry; mod test_cargo_run; +mod test_cargo_rustc; mod test_cargo_search; mod test_cargo_test; mod test_cargo_version;