auto merge of #9107 : catamorphism/rust/rustpkg-command-line-flags, r=brson

r? @brson rustpkg now accepts most of rustc's command-line arguments and passes
them along to rustc when building or installing.

A few rarely-used arguments aren't implemented yet.

rustpkg doesn't support flags that don't make sense with rustpkg
(for example, --bin and --lib, which get inferred from crate file names).

Closes #8522
This commit is contained in:
bors 2013-09-11 05:16:04 -07:00
commit d14bd0879d
6 changed files with 704 additions and 65 deletions

View File

@ -29,6 +29,8 @@ pub fn default_context(p: Path) -> BuildContext {
pub fn new_default_context(c: workcache::Context, p: Path) -> BuildContext {
BuildContext {
context: Context {
cfgs: ~[],
rustc_flags: RustcFlags::default(),
use_rust_path_hack: false,
sysroot: p
},
@ -44,7 +46,6 @@ fn binary_is_fresh(path: &str, in_hash: &str) -> bool {
in_hash == digest_only_date(&Path(path))
}
pub fn new_workcache_context(p: &Path) -> workcache::Context {
let db_file = p.push("rustpkg_db.json"); // ??? probably wrong
debug!("Workcache database file: %s", db_file.to_str());

View File

@ -10,11 +10,16 @@
// Context data structure used by rustpkg
use std::os;
use std::{io, os};
use extra::workcache;
use rustc::driver::session::{OptLevel, No};
#[deriving(Clone)]
pub struct Context {
// Config strings that the user passed in with --cfg
cfgs: ~[~str],
// Flags to pass to rustc
rustc_flags: RustcFlags,
// If use_rust_path_hack is true, rustpkg searches for sources
// in *package* directories that are in the RUST_PATH (for example,
// FOO/src/bar-0.1 instead of FOO). The flag doesn't affect where
@ -40,15 +45,82 @@ impl BuildContext {
pub fn sysroot_to_use(&self) -> Path {
self.context.sysroot_to_use()
}
/// Returns the flags to pass to rustc, as a vector of strings
pub fn flag_strs(&self) -> ~[~str] {
self.context.flag_strs()
}
pub fn compile_upto(&self) -> StopBefore {
self.context.compile_upto()
}
}
/*
Deliberately unsupported rustc flags:
--bin, --lib inferred from crate file names
-L inferred from extern mods
--out-dir inferred from RUST_PATH
--test use `rustpkg test`
-v -h --ls don't make sense with rustpkg
-W -A -D -F - use pragmas instead
rustc flags that aren't implemented yet:
--passes
--llvm-arg
--target-feature
--android-cross-path
*/
pub struct RustcFlags {
compile_upto: StopBefore,
// Linker to use with the --linker flag
linker: Option<~str>,
// Extra arguments to pass to rustc with the --link-args flag
link_args: Option<~str>,
// Optimization level. 0 = default. -O = 2.
optimization_level: OptLevel,
// True if the user passed in --save-temps
save_temps: bool,
// Target (defaults to rustc's default target)
target: Option<~str>,
// Target CPU (defaults to rustc's default target CPU)
target_cpu: Option<~str>,
// Any -Z features
experimental_features: Option<~[~str]>
}
impl Clone for RustcFlags {
fn clone(&self) -> RustcFlags {
RustcFlags {
compile_upto: self.compile_upto,
linker: self.linker.clone(),
link_args: self.link_args.clone(),
optimization_level: self.optimization_level,
save_temps: self.save_temps,
target: self.target.clone(),
target_cpu: self.target_cpu.clone(),
experimental_features: self.experimental_features.clone()
}
}
}
#[deriving(Eq)]
pub enum StopBefore {
Nothing, // compile everything
Link, // --no-link
LLVMCompileBitcode, // --emit-llvm without -S
LLVMAssemble, // -S --emit-llvm
Assemble, // -S without --emit-llvm
Trans, // --no-trans
Pretty, // --pretty
Analysis, // --parse-only
}
impl Context {
pub fn sysroot(&self) -> Path {
self.sysroot.clone()
}
}
impl Context {
/// Debugging
pub fn sysroot_str(&self) -> ~str {
self.sysroot.to_str()
@ -63,6 +135,15 @@ impl Context {
self.sysroot.pop().pop().pop()
}
}
/// Returns the flags to pass to rustc, as a vector of strings
pub fn flag_strs(&self) -> ~[~str] {
self.rustc_flags.flag_strs()
}
pub fn compile_upto(&self) -> StopBefore {
self.rustc_flags.compile_upto
}
}
/// We assume that if ../../rustc exists, then we're running
@ -72,3 +153,141 @@ pub fn in_target(sysroot: &Path) -> bool {
debug!("Checking whether %s is in target", sysroot.to_str());
os::path_is_dir(&sysroot.pop().pop().push("rustc"))
}
impl RustcFlags {
fn flag_strs(&self) -> ~[~str] {
let linker_flag = match self.linker {
Some(ref l) => ~[~"--linker", l.clone()],
None => ~[]
};
let link_args_flag = match self.link_args {
Some(ref l) => ~[~"--link-args", l.clone()],
None => ~[]
};
let save_temps_flag = if self.save_temps { ~[~"--save-temps"] } else { ~[] };
let target_flag = match self.target {
Some(ref l) => ~[~"--target", l.clone()],
None => ~[]
};
let target_cpu_flag = match self.target_cpu {
Some(ref l) => ~[~"--target-cpu", l.clone()],
None => ~[]
};
let z_flags = match self.experimental_features {
Some(ref ls) => ls.flat_map(|s| ~[~"-Z", s.clone()]),
None => ~[]
};
linker_flag
+ link_args_flag
+ save_temps_flag
+ target_flag
+ target_cpu_flag
+ z_flags + (match self.compile_upto {
LLVMCompileBitcode => ~[~"--emit-llvm"],
LLVMAssemble => ~[~"--emit-llvm", ~"-S"],
Link => ~[~"-c"],
Trans => ~[~"--no-trans"],
Assemble => ~[~"-S"],
// n.b. Doesn't support all flavors of --pretty (yet)
Pretty => ~[~"--pretty"],
Analysis => ~[~"--parse-only"],
Nothing => ~[]
})
}
pub fn default() -> RustcFlags {
RustcFlags {
linker: None,
link_args: None,
compile_upto: Nothing,
optimization_level: No,
save_temps: false,
target: None,
target_cpu: None,
experimental_features: None
}
}
}
/// Returns true if any of the flags given are incompatible with the cmd
pub fn flags_ok_for_cmd(flags: &RustcFlags,
cfgs: &[~str],
cmd: &str, user_supplied_opt_level: bool) -> bool {
let complain = |s| {
io::println(fmt!("The %s option can only be used with the build command:
rustpkg [options..] build %s [package-ID]", s, s));
};
if flags.linker.is_some() && cmd != "build" && cmd != "install" {
io::println("The --linker option can only be used with the build or install commands.");
return true;
}
if flags.link_args.is_some() && cmd != "build" && cmd != "install" {
io::println("The --link-args option can only be used with the build or install commands.");
return true;
}
if !cfgs.is_empty() && cmd != "build" && cmd != "install" {
io::println("The --cfg option can only be used with the build or install commands.");
return true;
}
if user_supplied_opt_level && cmd != "build" && cmd != "install" {
io::println("The -O and --opt-level options can only be used with the build \
or install commands.");
return true;
}
if flags.save_temps && cmd != "build" && cmd != "install" {
io::println("The --save-temps option can only be used with the build \
or install commands.");
return true;
}
if flags.target.is_some() && cmd != "build" && cmd != "install" {
io::println("The --target option can only be used with the build \
or install commands.");
return true;
}
if flags.target_cpu.is_some() && cmd != "build" && cmd != "install" {
io::println("The --target-cpu option can only be used with the build \
or install commands.");
return true;
}
if flags.experimental_features.is_some() && cmd != "build" && cmd != "install" {
io::println("The -Z option can only be used with the build or install commands.");
return true;
}
match flags.compile_upto {
Link if cmd != "build" => {
complain("--no-link");
true
}
Trans if cmd != "build" => {
complain("--no-trans");
true
}
Assemble if cmd != "build" => {
complain("-S");
true
}
Pretty if cmd != "build" => {
complain("--pretty");
true
}
Analysis if cmd != "build" => {
complain("--parse-only");
true
}
LLVMCompileBitcode if cmd != "build" => {
complain("--emit-llvm");
true
}
LLVMAssemble if cmd != "build" => {
complain("--emit-llvm");
true
}
_ => false
}
}

View File

@ -40,7 +40,9 @@ use path_util::{built_executable_in_workspace, built_library_in_workspace, defau
use path_util::{target_executable_in_workspace, target_library_in_workspace};
use source_control::is_git_dir;
use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
use context::{BuildContext, Context};
use context::{Context, BuildContext,
RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
LLVMAssemble, LLVMCompileBitcode};
use package_id::PkgId;
use package_source::PkgSrc;
use workcache_support::{discover_outputs, digest_only_date};
@ -138,6 +140,7 @@ impl<'self> PkgScript<'self> {
let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
util::compile_crate_from_input(&self.input,
exec,
Nothing,
&self.build_dir,
sess,
crate);
@ -400,7 +403,7 @@ impl CtxMethods for BuildContext {
debug!("No package script, continuing");
~[]
}
};
} + self.context.cfgs;
// If there was a package script, it should have finished
// the build already. Otherwise...
@ -539,9 +542,25 @@ pub fn main() {
pub fn main_args(args: &[~str]) {
let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
getopts::optflag("no-link"),
getopts::optflag("no-trans"),
// n.b. Ignores different --pretty options for now
getopts::optflag("pretty"),
getopts::optflag("parse-only"),
getopts::optflag("S"), getopts::optflag("assembly"),
getopts::optmulti("c"), getopts::optmulti("cfg"),
getopts::optflag("v"), getopts::optflag("version"),
getopts::optflag("r"), getopts::optflag("rust-path-hack")];
getopts::optflag("r"), getopts::optflag("rust-path-hack"),
getopts::optopt("sysroot"),
getopts::optflag("emit-llvm"),
getopts::optopt("linker"),
getopts::optopt("link-args"),
getopts::optopt("opt-level"),
getopts::optflag("O"),
getopts::optflag("save-temps"),
getopts::optopt("target"),
getopts::optopt("target-cpu"),
getopts::optmulti("Z") ];
let matches = &match getopts::getopts(args, opts) {
result::Ok(m) => m,
result::Err(f) => {
@ -550,8 +569,16 @@ pub fn main_args(args: &[~str]) {
return;
}
};
let help = getopts::opt_present(matches, "h") ||
getopts::opt_present(matches, "help");
let mut help = getopts::opt_present(matches, "h") ||
getopts::opt_present(matches, "help");
let no_link = getopts::opt_present(matches, "no-link");
let no_trans = getopts::opt_present(matches, "no-trans");
let supplied_sysroot = getopts::opt_val(matches, "sysroot");
let generate_asm = getopts::opt_present(matches, "S") ||
getopts::opt_present(matches, "assembly");
let parse_only = getopts::opt_present(matches, "parse-only");
let pretty = getopts::opt_present(matches, "pretty");
let emit_llvm = getopts::opt_present(matches, "emit-llvm");
if getopts::opt_present(matches, "v") ||
getopts::opt_present(matches, "version") {
@ -562,6 +589,35 @@ pub fn main_args(args: &[~str]) {
let use_rust_path_hack = getopts::opt_present(matches, "r") ||
getopts::opt_present(matches, "rust-path-hack");
let linker = getopts::opt_maybe_str(matches, "linker");
let link_args = getopts::opt_maybe_str(matches, "link-args");
let cfgs = getopts::opt_strs(matches, "cfg") + getopts::opt_strs(matches, "c");
let mut user_supplied_opt_level = true;
let opt_level = match getopts::opt_maybe_str(matches, "opt-level") {
Some(~"0") => session::No,
Some(~"1") => session::Less,
Some(~"2") => session::Default,
Some(~"3") => session::Aggressive,
_ if getopts::opt_present(matches, "O") => session::Default,
_ => {
user_supplied_opt_level = false;
session::No
}
};
let save_temps = getopts::opt_present(matches, "save-temps");
let target = getopts::opt_maybe_str(matches, "target");
let target_cpu = getopts::opt_maybe_str(matches, "target-cpu");
let experimental_features = {
let strs = getopts::opt_strs(matches, "Z");
if getopts::opt_present(matches, "Z") {
Some(strs)
}
else {
None
}
};
let mut args = matches.free.clone();
args.shift();
@ -569,6 +625,33 @@ pub fn main_args(args: &[~str]) {
return usage::general();
}
let rustc_flags = RustcFlags {
linker: linker,
link_args: link_args,
optimization_level: opt_level,
compile_upto: if no_trans {
Trans
} else if no_link {
Link
} else if pretty {
Pretty
} else if parse_only {
Analysis
} else if emit_llvm && generate_asm {
LLVMAssemble
} else if generate_asm {
Assemble
} else if emit_llvm {
LLVMCompileBitcode
} else {
Nothing
},
save_temps: save_temps,
target: target,
target_cpu: target_cpu,
experimental_features: experimental_features
};
let mut cmd_opt = None;
for a in args.iter() {
if util::is_cmd(*a) {
@ -578,23 +661,25 @@ pub fn main_args(args: &[~str]) {
}
let cmd = match cmd_opt {
None => return usage::general(),
Some(cmd) => if help {
return match *cmd {
~"build" => usage::build(),
~"clean" => usage::clean(),
~"do" => usage::do_cmd(),
~"info" => usage::info(),
~"install" => usage::install(),
~"list" => usage::list(),
~"prefer" => usage::prefer(),
~"test" => usage::test(),
~"uninstall" => usage::uninstall(),
~"unprefer" => usage::unprefer(),
_ => usage::general()
};
}
else {
cmd
Some(cmd) => {
help |= context::flags_ok_for_cmd(&rustc_flags, cfgs, *cmd, user_supplied_opt_level);
if help {
return match *cmd {
~"build" => usage::build(),
~"clean" => usage::clean(),
~"do" => usage::do_cmd(),
~"info" => usage::info(),
~"install" => usage::install(),
~"list" => usage::list(),
~"prefer" => usage::prefer(),
~"test" => usage::test(),
~"uninstall" => usage::uninstall(),
~"unprefer" => usage::unprefer(),
_ => usage::general()
};
} else {
cmd
}
}
};
@ -603,14 +688,20 @@ pub fn main_args(args: &[~str]) {
// I had to add this type annotation to get the code to typecheck
let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
remaining_args.shift();
let sroot = filesearch::get_or_default_sysroot();
let sroot = match supplied_sysroot {
Some(getopts::Val(s)) => Path(s),
_ => filesearch::get_or_default_sysroot()
};
debug!("Using sysroot: %s", sroot.to_str());
debug!("Will store workcache in %s", default_workspace().to_str());
BuildContext {
context: Context {
use_rust_path_hack: use_rust_path_hack,
sysroot: sroot, // Currently, only tests override this
},
cfgs: cfgs,
rustc_flags: rustc_flags,
use_rust_path_hack: use_rust_path_hack,
sysroot: sroot, // Currently, only tests override this
},
workcache_context: api::default_context(default_workspace()).workcache_context
}.run(*cmd, remaining_args)
}

View File

@ -10,7 +10,7 @@
// rustpkg unit tests
use context::{BuildContext, Context};
use context::{BuildContext, Context, RustcFlags};
use std::{io, libc, os, run, str, task};
use extra::arc::Arc;
use extra::arc::RWArc;
@ -18,6 +18,7 @@ use extra::tempfile::mkdtemp;
use extra::workcache;
use extra::workcache::{Database, Logger};
use extra::treemap::TreeMap;
use extra::getopts::groups::getopts;
use std::run::ProcessOutput;
use installed_packages::list_installed_packages;
use package_id::{PkgId};
@ -27,8 +28,10 @@ use path_util::{target_executable_in_workspace, target_test_in_workspace,
library_in_workspace, installed_library_in_workspace,
built_bench_in_workspace, built_test_in_workspace,
built_library_in_workspace, built_executable_in_workspace};
use rustc::back::link::get_cc_prog;
use rustc::metadata::filesearch::rust_path;
use rustc::driver::driver::host_triple;
use rustc::driver::driver::{build_session, build_session_options, host_triple, optgroups};
use syntax::diagnostic;
use target::*;
use package_source::PkgSrc;
@ -45,6 +48,9 @@ fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext {
BuildContext {
workcache_context: context,
context: Context {
cfgs: ~[],
rustc_flags: RustcFlags::default(),
use_rust_path_hack: false,
sysroot: sysroot
}
@ -218,6 +224,10 @@ fn rustpkg_exec() -> Path {
}
fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
command_line_test_with_env(args, cwd, None).expect("Command line test failed")
}
fn command_line_test_partial(args: &[~str], cwd: &Path) -> Option<ProcessOutput> {
command_line_test_with_env(args, cwd, None)
}
@ -225,7 +235,7 @@ fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
/// invoked from) with the given arguments, in the given working directory.
/// Returns the process's output.
fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>)
-> ProcessOutput {
-> Option<ProcessOutput> {
let cmd = rustpkg_exec().to_str();
debug!("cd %s; %s %s",
cwd.to_str(), cmd, args.connect(" "));
@ -250,11 +260,14 @@ So tests that use this need to check the existence of a file
to make sure the command succeeded
*/
if output.status != 0 {
fail!("Command %s %? failed with exit code %?; its output was {{{ %s }}}",
debug!("Command %s %? failed with exit code %?; its output was {{{ %s }}}",
cmd, args, output.status,
str::from_utf8(output.output) + str::from_utf8(output.error));
None
}
else {
Some(output)
}
output
}
fn create_local_package(pkgid: &PkgId) -> Path {
@ -352,6 +365,27 @@ fn built_executable_exists(repo: &Path, short_name: &str) -> bool {
}
}
fn object_file_exists(repo: &Path, short_name: &str) -> bool {
file_exists(repo, short_name, "o")
}
fn assembly_file_exists(repo: &Path, short_name: &str) -> bool {
file_exists(repo, short_name, "s")
}
fn llvm_assembly_file_exists(repo: &Path, short_name: &str) -> bool {
file_exists(repo, short_name, "ll")
}
fn llvm_bitcode_file_exists(repo: &Path, short_name: &str) -> bool {
file_exists(repo, short_name, "bc")
}
fn file_exists(repo: &Path, short_name: &str, extension: &str) -> bool {
os::path_exists(&repo.push_many([~"build", short_name.to_owned(),
fmt!("%s.%s", short_name, extension)]))
}
fn assert_built_library_exists(repo: &Path, short_name: &str) {
assert!(built_library_exists(repo, short_name));
}
@ -377,7 +411,8 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] {
fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] {
let mut result = ~[];
let p_output = command_line_test_with_env(args, &os::getcwd(), Some(env));
let p_output = command_line_test_with_env(args,
&os::getcwd(), Some(env)).expect("Command-line test failed");
let test_output = str::from_utf8(p_output.output);
for s in test_output.split_iter('\n') {
result.push(s.to_owned());
@ -1264,6 +1299,256 @@ fn rust_path_install_target() {
}
#[test]
fn sysroot_flag() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
// no-op sysroot setting; I'm not sure how else to test this
command_line_test([~"--sysroot",
test_sysroot().to_str(),
~"build",
~"foo"],
&workspace);
assert_built_executable_exists(&workspace, "foo");
}
#[test]
fn compile_flag_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"build",
~"--no-link",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(object_file_exists(&workspace, "foo"));
}
#[test]
fn compile_flag_fail() {
// --no-link shouldn't be accepted for install
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"install",
~"--no-link",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
}
#[test]
fn notrans_flag_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
let flags_to_test = [~"--no-trans", ~"--parse-only",
~"--pretty", ~"-S"];
for flag in flags_to_test.iter() {
command_line_test([test_sysroot().to_str(),
~"build",
flag.clone(),
~"foo"],
&workspace);
// Ideally we'd test that rustpkg actually succeeds, but
// since task failure doesn't set the exit code properly,
// we can't tell
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
}
}
#[test]
fn notrans_flag_fail() {
// --no-trans shouldn't be accepted for install
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
let flags_to_test = [~"--no-trans", ~"--parse-only",
~"--pretty", ~"-S"];
for flag in flags_to_test.iter() {
command_line_test([test_sysroot().to_str(),
~"install",
flag.clone(),
~"foo"],
&workspace);
// Ideally we'd test that rustpkg actually fails, but
// since task failure doesn't set the exit code properly,
// we can't tell
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(!lib_exists(&workspace, "foo", NoVersion));
}
}
#[test]
fn dash_S() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"build",
~"-S",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(assembly_file_exists(&workspace, "foo"));
}
#[test]
fn dash_S_fail() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"install",
~"-S",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(!assembly_file_exists(&workspace, "foo"));
}
#[test]
fn test_cfg_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
// If the cfg flag gets messed up, this won't compile
writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]),
"#[cfg(quux)] fn main() {}");
command_line_test([test_sysroot().to_str(),
~"build",
~"--cfg",
~"quux",
~"foo"],
&workspace);
assert_built_executable_exists(&workspace, "foo");
}
#[test]
fn test_cfg_fail() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]),
"#[cfg(quux)] fn main() {}");
assert!(command_line_test_partial([test_sysroot().to_str(),
~"build",
~"foo"],
&workspace).is_none());
}
#[test]
fn test_emit_llvm_S_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"build",
~"-S", ~"--emit-llvm",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(llvm_assembly_file_exists(&workspace, "foo"));
assert!(!assembly_file_exists(&workspace, "foo"));
}
#[test]
fn test_emit_llvm_S_fail() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"install",
~"-S", ~"--emit-llvm",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(!llvm_assembly_file_exists(&workspace, "foo"));
assert!(!assembly_file_exists(&workspace, "foo"));
}
#[test]
fn test_emit_llvm_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"build",
~"--emit-llvm",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(llvm_bitcode_file_exists(&workspace, "foo"));
assert!(!assembly_file_exists(&workspace, "foo"));
assert!(!llvm_assembly_file_exists(&workspace, "foo"));
}
#[test]
fn test_emit_llvm_fail() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"install",
~"--emit-llvm",
~"foo"],
&workspace);
assert!(!built_executable_exists(&workspace, "foo"));
assert!(!object_file_exists(&workspace, "foo"));
assert!(!llvm_bitcode_file_exists(&workspace, "foo"));
assert!(!llvm_assembly_file_exists(&workspace, "foo"));
assert!(!assembly_file_exists(&workspace, "foo"));
}
#[test]
fn test_linker_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
let matches = getopts([], optgroups());
let options = build_session_options(@"rustpkg",
matches.get_ref(),
diagnostic::emit);
let sess = build_session(options, diagnostic::emit);
command_line_test([test_sysroot().to_str(),
~"install",
~"--linker",
get_cc_prog(sess),
~"foo"],
&workspace);
assert_executable_exists(&workspace, "foo");
}
#[test]
fn test_build_install_flags_fail() {
// The following flags can only be used with build or install:
let forbidden = [~[~"--linker", ~"ld"],
~[~"--link-args", ~"quux"],
~[~"-O"],
~[~"--opt-level", ~"2"],
~[~"--save-temps"],
~[~"--target", host_triple()],
~[~"--target-cpu", ~"generic"],
~[~"-Z", ~"--time-passes"]];
for flag in forbidden.iter() {
let output = command_line_test_output([test_sysroot().to_str(),
~"list"] + *flag);
assert!(output.len() > 1);
assert!(output[1].find_str("can only be used with").is_some());
}
}
#[test]
fn test_optimized_build() {
let p_id = PkgId::new("foo");
let workspace = create_local_package(&p_id);
command_line_test([test_sysroot().to_str(),
~"build",
~"-O",
~"foo"],
&workspace);
assert!(built_executable_exists(&workspace, "foo"));
}
/// Returns true if p exists and is executable
fn is_executable(p: &Path) -> bool {

View File

@ -19,18 +19,34 @@ Where <cmd> is one of:
Options:
-h, --help Display this message
--sysroot PATH Override the system root
<cmd> -h, <cmd> --help Display help for <cmd>");
}
pub fn build() {
io::println("rustpkg [options..] build [package-ID]
io::println("rustpkg build [options..] [package-ID]
Build the given package ID if specified. With no package ID argument,
build the package in the current directory. In that case, the current
directory must be a direct child of an `src` directory in a workspace.
Options:
-c, --cfg Pass a cfg flag to the package script");
-c, --cfg Pass a cfg flag to the package script
--no-link Compile and assemble, but don't link (like -c in rustc)
--no-trans Parse and translate, but don't generate any code
--pretty Pretty-print the code, but don't generate output
--parse-only Parse the code, but don't typecheck or generate code
-S Generate assembly code, but don't assemble or link it
-S --emit-llvm Generate LLVM assembly code
--emit-llvm Generate LLVM bitcode
--linker PATH Use a linker other than the system linker
--link-args [ARG..] Extra arguments to pass to the linker
--opt-level=n Set the optimization level (0 <= n <= 3)
-O Equivalent to --opt-level=2
--save-temps Don't delete temporary files
--target TRIPLE Set the target triple
--target-cpu CPU Set the target CPU
-Z FLAG Enable an experimental rustc feature (see `rustc --help`)");
}
pub fn clean() {
@ -63,7 +79,7 @@ List all installed packages.");
}
pub fn install() {
io::println("rustpkg [options..] install [package-ID]
io::println("rustpkg install [options..] [package-ID]
Install the given package ID if specified. With no package ID
argument, install the package in the current directory.
@ -76,7 +92,16 @@ Examples:
rustpkg install github.com/mozilla/servo#0.1.2
Options:
-c, --cfg Pass a cfg flag to the package script");
-c, --cfg Pass a cfg flag to the package script
--emit-llvm Generate LLVM bitcode
--linker PATH Use a linker other than the system linker
--link-args [ARG..] Extra arguments to pass to the linker
--opt-level=n Set the optimization level (0 <= n <= 3)
-O Equivalent to --opt-level=2
--save-temps Don't delete temporary files
--target TRIPLE Set the target triple
--target-cpu CPU Set the target CPU
-Z FLAG Enable an experimental rustc feature (see `rustc --help`)");
}
pub fn uninstall() {

View File

@ -17,9 +17,9 @@ use syntax::codemap::{dummy_sp, Spanned};
use syntax::ext::base::ExtCtxt;
use syntax::{ast, attr, codemap, diagnostic, fold};
use syntax::attr::AttrMetaMethods;
use rustc::back::link::output_type_exe;
use rustc::back::link;
use rustc::driver::session::{lib_crate, bin_crate};
use context::{in_target, BuildContext};
use context::{in_target, StopBefore, Link, Assemble, BuildContext};
use package_id::PkgId;
use package_source::PkgSrc;
use path_util::{installed_library_in_workspace, U_RWX};
@ -153,7 +153,7 @@ pub fn ready_crate(sess: session::Session,
@fold.fold_crate(crate)
}
pub fn compile_input(ctxt: &BuildContext,
pub fn compile_input(context: &BuildContext,
exec: &mut workcache::Exec,
pkg_id: &PkgId,
in_file: &Path,
@ -161,7 +161,7 @@ pub fn compile_input(ctxt: &BuildContext,
flags: &[~str],
cfgs: &[~str],
opt: bool,
what: OutputType) -> Path {
what: OutputType) -> Option<Path> {
assert!(in_file.components.len() > 1);
let input = driver::file_input((*in_file).clone());
debug!("compile_input: %s / %?", in_file.to_str(), what);
@ -174,7 +174,7 @@ pub fn compile_input(ctxt: &BuildContext,
debug!("flags: %s", flags.connect(" "));
debug!("cfgs: %s", cfgs.connect(" "));
debug!("compile_input's sysroot = %s", ctxt.sysroot().to_str());
debug!("compile_input's sysroot = %s", context.sysroot().to_str());
let crate_type = match what {
Lib => lib_crate,
@ -188,26 +188,38 @@ pub fn compile_input(ctxt: &BuildContext,
Main => ~[]
}
+ flags
+ context.flag_strs()
+ cfgs.flat_map(|c| { ~[~"--cfg", (*c).clone()] }),
driver::optgroups()).unwrap();
debug!("rustc flags: %?", matches);
// Hack so that rustpkg can run either out of a rustc target dir,
// or the host dir
let sysroot_to_use = @if !in_target(&ctxt.sysroot()) {
ctxt.sysroot()
let sysroot_to_use = @if !in_target(&context.sysroot()) {
context.sysroot()
}
else {
ctxt.sysroot().pop().pop().pop()
context.sysroot().pop().pop().pop()
};
debug!("compile_input's sysroot = %s", ctxt.sysroot().to_str());
debug!("compile_input's sysroot = %s", context.sysroot().to_str());
debug!("sysroot_to_use = %s", sysroot_to_use.to_str());
let output_type = match context.compile_upto() {
Assemble => link::output_type_assembly,
Link => link::output_type_object,
Pretty | Trans | Analysis => link::output_type_none,
LLVMAssemble => link::output_type_llvm_assembly,
LLVMCompileBitcode => link::output_type_bitcode,
Nothing => link::output_type_exe
};
let options = @session::options {
crate_type: crate_type,
optimize: if opt { session::Aggressive } else { session::No },
test: what == Test || what == Bench,
maybe_sysroot: Some(sysroot_to_use),
addl_lib_search_paths: @mut (~[out_dir.clone()]),
// output_type should be conditional
output_type: output_type_exe, // Use this to get a library? That's weird
output_type: output_type,
.. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone()
};
@ -233,7 +245,7 @@ pub fn compile_input(ctxt: &BuildContext,
// Not really right. Should search other workspaces too, and the installed
// database (which doesn't exist yet)
find_and_install_dependencies(ctxt, sess, exec, workspace, crate,
find_and_install_dependencies(context, sess, exec, workspace, crate,
|p| {
debug!("a dependency: %s", p.to_str());
// Pass the directory containing a dependency
@ -270,7 +282,7 @@ pub fn compile_input(ctxt: &BuildContext,
debug!("calling compile_crate_from_input, workspace = %s,
building_library = %?", out_dir.to_str(), sess.building_library);
compile_crate_from_input(in_file, exec, &out_dir, sess, crate)
compile_crate_from_input(in_file, exec, context.compile_upto(), &out_dir, sess, crate)
}
// Should use workcache to avoid recompiling when not necessary
@ -280,10 +292,13 @@ pub fn compile_input(ctxt: &BuildContext,
// also, too many arguments
pub fn compile_crate_from_input(input: &Path,
exec: &mut workcache::Exec,
stop_before: StopBefore,
// should be of the form <workspace>/build/<pkg id's path>
out_dir: &Path,
sess: session::Session,
crate: @ast::Crate) -> Path {
// Returns None if one of the flags that suppresses compilation output was
// given
crate: @ast::Crate) -> Option<Path> {
debug!("Calling build_output_filenames with %s, building library? %?",
out_dir.to_str(), sess.building_library);
@ -302,17 +317,21 @@ pub fn compile_crate_from_input(input: &Path,
debug!("an additional library: %s", lib.to_str());
}
let analysis = driver::phase_3_run_analysis_passes(sess, crate);
if driver::stop_after_phase_3(sess) { return None; }
let translation = driver::phase_4_translate_to_llvm(sess, crate,
&analysis,
outputs);
driver::phase_5_run_llvm_passes(sess, &translation, outputs);
if driver::stop_after_phase_5(sess) { return outputs.out_filename; }
// The second check shouldn't be necessary, but rustc seems to ignore
// -c
if driver::stop_after_phase_5(sess)
|| stop_before == Link || stop_before == Assemble { return Some(outputs.out_filename); }
driver::phase_6_link_output(sess, &translation, outputs);
// Register dependency on the source file
exec.discover_input("file", input.to_str(), digest_file_with_date(input));
outputs.out_filename
Some(outputs.out_filename)
}
#[cfg(windows)]
@ -330,7 +349,7 @@ pub fn compile_crate(ctxt: &BuildContext,
pkg_id: &PkgId,
crate: &Path, workspace: &Path,
flags: &[~str], cfgs: &[~str], opt: bool,
what: OutputType) -> Path {
what: OutputType) -> Option<Path> {
debug!("compile_crate: crate=%s, workspace=%s", crate.to_str(), workspace.to_str());
debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
for fl in flags.iter() {
@ -344,14 +363,13 @@ pub fn compile_crate(ctxt: &BuildContext,
/// try to install their targets, failing if any target
/// can't be found.
pub fn find_and_install_dependencies(ctxt: &BuildContext,
sess: session::Session,
exec: &mut workcache::Exec,
workspace: &Path,
c: &ast::Crate,
save: @fn(Path)
) {
debug!("Finding and installing dependencies...");
do c.each_view_item |vi| {
sess: session::Session,
exec: &mut workcache::Exec,
workspace: &Path,
c: &ast::Crate,
save: @fn(Path)
) {
do c.each_view_item() |vi: &ast::view_item| {
debug!("A view item!");
match vi.node {
// ignore metadata, I guess