1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // rustpkg - a package manager and build system for Rust
13 #[link(name = "rustpkg",
14 package_id = "rustpkg",
16 uuid = "25de5e6e-279e-4a20-845c-4cabae92daaf",
17 url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")];
19 #[license = "MIT/ASL2"];
20 #[crate_type = "dylib"];
22 #[feature(globs, managed_boxes)];
28 use std::{os, result, run, str, task};
30 use std::hashmap::HashSet;
33 pub use std::path::Path;
36 use rustc::driver::{driver, session};
37 use rustc::metadata::filesearch;
38 use rustc::metadata::filesearch::rust_path;
40 use syntax::{ast, diagnostic};
41 use messages::{error, warn, note};
42 use path_util::{build_pkg_id_in_workspace, built_test_in_workspace};
43 use path_util::in_rust_path;
44 use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace};
45 use path_util::{target_executable_in_workspace, target_library_in_workspace, dir_has_crate_file};
46 use source_control::{CheckedOutSources, is_git_dir, make_read_only};
47 use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
48 use workspace::determine_destination;
49 use context::{Context, BuildContext,
50 RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
51 LLVMAssemble, LLVMCompileBitcode};
52 use package_id::PkgId;
53 use package_source::PkgSrc;
54 use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench};
55 use target::{Tests, MaybeCustom, Inferred, JustOne};
56 use workcache_support::digest_only_date;
57 use exit_codes::{COPY_FAILED_CODE, BAD_FLAG_CODE};
64 mod installed_packages;
73 #[cfg(not(windows), test)] // FIXME test failure on windows: #10471
77 pub mod workcache_support;
82 /// A PkgScript represents user-supplied custom logic for
83 /// special build hooks. This only exists for packages with
84 /// an explicit package script.
85 struct PkgScript<'self> {
86 /// Uniquely identifies this package
88 /// File path for the package script
90 /// The session to use *only* for compiling the custom
92 sess: session::Session,
93 /// The config for compiling the custom build script
94 cfg: ast::CrateConfig,
95 /// The crate for the custom build script
96 crate: Option<ast::Crate>,
97 /// Directory in which to store build output
101 impl<'self> PkgScript<'self> {
102 /// Given the path name for a package script
103 /// and a package ID, parse the package script into
104 /// a PkgScript that we can then execute
105 fn parse<'a>(sysroot: Path,
108 id: &'a PkgId) -> PkgScript<'a> {
109 // Get the executable name that was invoked
110 let binary = os::args()[0].to_managed();
111 // Build the rustc session data structures to pass
113 debug!("pkgscript parse: {}", sysroot.display());
114 let options = @session::options {
116 maybe_sysroot: Some(@sysroot),
117 outputs: ~[session::OutputExecutable],
118 .. (*session::basic_options()).clone()
120 let input = driver::file_input(script.clone());
121 let sess = driver::build_session(options,
122 @diagnostic::DefaultEmitter as
123 @diagnostic::Emitter);
124 let cfg = driver::build_configuration(sess);
125 let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
126 let crate = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate);
127 let work_dir = build_pkg_id_in_workspace(id, workspace);
129 debug!("Returning package script with id {}", id.to_str());
141 fn build_custom(&mut self, exec: &mut workcache::Exec) -> ~str {
142 let sess = self.sess;
144 debug!("Working directory = {}", self.build_dir.display());
145 // Collect together any user-defined commands in the package script
146 let crate = util::ready_crate(sess, self.crate.take_unwrap());
147 debug!("Building output filenames with script name {}",
148 driver::source_name(&driver::file_input(self.input.clone())));
149 let exe = self.build_dir.join("pkg" + util::exe_suffix());
150 util::compile_crate_from_input(&self.input,
156 // Discover the output
157 // FIXME (#9639): This needs to handle non-utf8 paths
158 // Discover the output
159 exec.discover_output("binary", exe.as_str().unwrap().to_owned(), digest_only_date(&exe));
160 exe.as_str().unwrap().to_owned()
164 /// Run the contents of this package script, where <what>
165 /// is the command to pass to it (e.g., "build", "clean", "install")
166 /// Returns a pair of an exit code and list of configs (obtained by
167 /// calling the package script's configs() function if it exists
168 fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], process::ProcessExit) {
169 debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(),
170 sysroot.display(), "install");
171 // FIXME #7401 should support commands besides `install`
172 // FIXME (#9639): This needs to handle non-utf8 paths
173 let status = run::process_status(exe.as_str().unwrap(),
174 [sysroot.as_str().unwrap().to_owned(), ~"install"]);
175 if !status.success() {
176 debug!("run_custom: first pkg command failed with {:?}", status);
180 debug!("Running program (configs): {} {} {}",
181 exe.display(), sysroot.display(), "configs");
182 // FIXME (#9639): This needs to handle non-utf8 paths
183 let output = run::process_output(exe.as_str().unwrap(),
184 [sysroot.as_str().unwrap().to_owned(), ~"configs"]);
185 debug!("run_custom: second pkg command did {:?}", output.status);
186 // Run the configs() function to get the configs
187 let cfgs = str::from_utf8_slice(output.output).words()
188 .map(|w| w.to_owned()).collect();
189 (cfgs, output.status)
193 fn hash(&self) -> ~str {
198 pub trait CtxMethods {
199 fn run(&self, cmd: &str, args: ~[~str]);
200 fn do_cmd(&self, _cmd: &str, _pkgname: &str);
201 /// Returns a pair of the selected package ID, and the destination workspace
202 fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)>;
203 /// Returns the destination workspace
204 fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild);
205 fn clean(&self, workspace: &Path, id: &PkgId);
207 /// Returns a pair. First component is a list of installed paths,
208 /// second is a list of declared and discovered inputs
209 fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]);
210 /// Returns a list of installed files
211 fn install_no_build(&self,
212 build_workspace: &Path,
213 build_inputs: &[Path],
214 target_workspace: &Path,
215 id: &PkgId) -> ~[~str];
216 fn prefer(&self, _id: &str, _vers: Option<~str>);
217 fn test(&self, id: &PkgId, workspace: &Path);
218 fn uninstall(&self, _id: &str, _vers: Option<~str>);
219 fn unprefer(&self, _id: &str, _vers: Option<~str>);
223 impl CtxMethods for BuildContext {
224 fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)> {
225 let cwd = os::getcwd();
228 match cwd_to_workspace() {
229 None if dir_has_crate_file(&cwd) => {
230 // FIXME (#9639): This needs to handle non-utf8 paths
231 let pkgid = PkgId::new(cwd.filename_str().unwrap());
232 let mut pkg_src = PkgSrc::new(cwd, default_workspace(), true, pkgid);
233 self.build(&mut pkg_src, what);
235 PkgSrc { destination_workspace: ws,
241 None => { usage::build(); None }
242 Some((ws, pkgid)) => {
243 let mut pkg_src = PkgSrc::new(ws.clone(), ws, false, pkgid);
244 self.build(&mut pkg_src, what);
246 PkgSrc { destination_workspace: ws,
254 // The package id is presumed to be the first command-line
256 let pkgid = PkgId::new(args[0].clone());
257 let mut dest_ws = default_workspace();
258 each_pkg_parent_workspace(&self.context, &pkgid, |workspace| {
259 debug!("found pkg {} in workspace {}, trying to build",
260 pkgid.to_str(), workspace.display());
261 dest_ws = determine_destination(os::getcwd(),
262 self.context.use_rust_path_hack,
264 let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(),
265 false, pkgid.clone());
266 self.build(&mut pkg_src, what);
269 // n.b. If this builds multiple packages, it only returns the workspace for
270 // the last one. The whole building-multiple-packages-with-the-same-ID is weird
271 // anyway and there are no tests for it, so maybe take it out
272 Some((pkgid, dest_ws))
275 fn run(&self, cmd: &str, args: ~[~str]) {
276 let cwd = os::getcwd();
279 self.build_args(args, &WhatToBuild::new(MaybeCustom, Everything));
283 match cwd_to_workspace() {
284 None => { usage::clean(); return }
285 // tjc: Maybe clean should clean all the packages in the
286 // current workspace, though?
287 Some((ws, pkgid)) => self.clean(&ws, &pkgid)
292 // The package id is presumed to be the first command-line
294 let pkgid = PkgId::new(args[0].clone());
295 self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd
300 return usage::do_cmd();
303 self.do_cmd(args[0].clone(), args[1].clone());
310 match cwd_to_workspace() {
311 None if dir_has_crate_file(&cwd) => {
312 // FIXME (#9639): This needs to handle non-utf8 paths
315 PkgId::new(cwd.filename_str().unwrap());
316 self.install(PkgSrc::new(cwd, default_workspace(),
317 true, inferred_pkgid),
318 &WhatToBuild::new(MaybeCustom, Everything));
320 None => { usage::install(); return; }
321 Some((ws, pkgid)) => {
322 let pkg_src = PkgSrc::new(ws.clone(), ws.clone(), false, pkgid);
323 self.install(pkg_src, &WhatToBuild::new(MaybeCustom,
329 // The package id is presumed to be the first command-line
331 let pkgid = PkgId::new(args[0]);
332 let workspaces = pkg_parent_workspaces(&self.context, &pkgid);
333 debug!("package ID = {}, found it in {:?} workspaces",
334 pkgid.to_str(), workspaces.len());
335 if workspaces.is_empty() {
336 let d = default_workspace();
337 let src = PkgSrc::new(d.clone(), d, false, pkgid.clone());
338 self.install(src, &WhatToBuild::new(MaybeCustom, Everything));
341 for workspace in workspaces.iter() {
342 let dest = determine_destination(os::getcwd(),
343 self.context.use_rust_path_hack,
345 let src = PkgSrc::new(workspace.clone(),
347 self.context.use_rust_path_hack,
349 self.install(src, &WhatToBuild::new(MaybeCustom, Everything));
355 println("Installed packages:");
356 installed_packages::list_installed_packages(|pkg_id| {
357 pkg_id.path.display().with_str(|s| println(s));
363 return usage::uninstall();
366 self.prefer(args[0], None);
369 // Build the test executable
370 let maybe_id_and_workspace = self.build_args(args,
371 &WhatToBuild::new(MaybeCustom, Tests));
372 match maybe_id_and_workspace {
373 Some((pkg_id, workspace)) => {
374 // Assuming it's built, run the tests
375 self.test(&pkg_id, &workspace);
378 error("Testing failed because building the specified package failed.");
384 return usage::init();
391 return usage::uninstall();
394 let pkgid = PkgId::new(args[0]);
395 if !installed_packages::package_is_installed(&pkgid) {
396 warn(format!("Package {} doesn't seem to be installed! \
397 Doing nothing.", args[0]));
401 let rp = rust_path();
402 assert!(!rp.is_empty());
403 each_pkg_parent_workspace(&self.context, &pkgid, |workspace| {
404 path_util::uninstall_package_from(workspace, &pkgid);
405 note(format!("Uninstalled package {} (was installed in {})",
406 pkgid.to_str(), workspace.display()));
413 return usage::unprefer();
416 self.unprefer(args[0], None);
418 _ => fail!("I don't know the command `{}`", cmd)
422 fn do_cmd(&self, _cmd: &str, _pkgname: &str) {
424 fail!("`do` not yet implemented");
427 fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
428 use conditions::git_checkout_failed::cond;
430 let workspace = pkg_src.source_workspace.clone();
431 let pkgid = pkg_src.id.clone();
433 debug!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \
434 pkgid = {} pkgsrc start_dir = {}", workspace.display(),
435 in_rust_path(&workspace), is_git_dir(&workspace.join(&pkgid.path)),
436 pkgid.to_str(), pkg_src.start_dir.display());
437 debug!("build: what to build = {:?}", what_to_build);
439 // If workspace isn't in the RUST_PATH, and it's a git repo,
440 // then clone it into the first entry in RUST_PATH, and repeat
441 if !in_rust_path(&workspace) && is_git_dir(&workspace.join(&pkgid.path)) {
442 let mut out_dir = default_workspace().join("src");
443 out_dir.push(&pkgid.path);
444 let git_result = source_control::safe_git_clone(&workspace.join(&pkgid.path),
448 CheckedOutSources => make_read_only(&out_dir),
449 // FIXME (#9639): This needs to handle non-utf8 paths
450 _ => cond.raise((pkgid.path.as_str().unwrap().to_owned(), out_dir.clone()))
452 let default_ws = default_workspace();
453 debug!("Calling build recursively with {:?} and {:?}", default_ws.display(),
455 return self.build(&mut PkgSrc::new(default_ws.clone(),
458 pkgid.clone()), what_to_build);
461 // Is there custom build logic? If so, use it
462 let mut custom = false;
463 debug!("Package source directory = {}", pkg_src.to_str());
464 let opt = pkg_src.package_script_option();
465 debug!("Calling pkg_script_option on {:?}", opt);
466 let cfgs = match (pkg_src.package_script_option(), what_to_build.build_type) {
467 (Some(package_script_path), MaybeCustom) => {
468 let sysroot = self.sysroot_to_use();
469 // Build the package script if needed
470 let script_build = format!("build_package_script({})",
471 package_script_path.display());
472 let pkg_exe = self.workcache_context.with_prep(script_build, |prep| {
473 let subsysroot = sysroot.clone();
474 let psp = package_script_path.clone();
475 let ws = workspace.clone();
476 let pid = pkgid.clone();
477 prep.exec(proc(exec) {
478 let mut pscript = PkgScript::parse(subsysroot.clone(),
482 pscript.build_custom(exec)
485 // We always *run* the package script
486 let (cfgs, hook_result) = PkgScript::run_custom(&Path::init(pkg_exe), &sysroot);
487 debug!("Command return code = {:?}", hook_result);
488 if !hook_result.success() {
489 fail!("Error running custom build command")
492 // otherwise, the package script succeeded
495 (Some(_), Inferred) => {
496 debug!("There is a package script, but we're ignoring it");
500 debug!("No package script, continuing");
503 } + self.context.cfgs;
505 // If there was a package script, it should have finished
506 // the build already. Otherwise...
508 match what_to_build.sources {
509 // Find crates inside the workspace
510 Everything => pkg_src.find_crates(),
512 Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::init(s)) }),
513 // Don't infer any crates -- just build the one that was requested
515 // We expect that p is relative to the package source's start directory,
516 // so check that assumption
517 debug!("JustOne: p = {}", p.display());
518 assert!(pkg_src.start_dir.join(p).exists());
520 PkgSrc::push_crate(&mut pkg_src.libs, 0, p);
521 } else if is_main(p) {
522 PkgSrc::push_crate(&mut pkg_src.mains, 0, p);
523 } else if is_test(p) {
524 PkgSrc::push_crate(&mut pkg_src.tests, 0, p);
525 } else if is_bench(p) {
526 PkgSrc::push_crate(&mut pkg_src.benchs, 0, p);
528 warn(format!("Not building any crates for dependency {}", p.display()));
534 pkg_src.build(self, cfgs, []);
538 fn clean(&self, workspace: &Path, id: &PkgId) {
539 // Could also support a custom build hook in the pkg
540 // script for cleaning files rustpkg doesn't know about.
541 // Do something reasonable for now
543 let dir = build_pkg_id_in_workspace(id, workspace);
544 note(format!("Cleaning package {} (removing directory {})",
545 id.to_str(), dir.display()));
547 fs::rmdir_recursive(&dir);
548 note(format!("Removed directory {}", dir.display()));
551 note(format!("Cleaned package {}", id.to_str()));
556 fail!("info not yet implemented");
559 fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) {
561 let id = pkg_src.id.clone();
563 let mut installed_files = ~[];
564 let mut inputs = ~[];
565 let mut build_inputs = ~[];
567 debug!("Installing package source: {}", pkg_src.to_str());
569 // workcache only knows about *crates*. Building a package
570 // just means inferring all the crates in it, then building each one.
571 self.build(&mut pkg_src, what);
573 debug!("Done building package source {}", pkg_src.to_str());
575 let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(),
576 pkg_src.tests.clone(), pkg_src.benchs.clone()];
577 debug!("In declare inputs for {}", id.to_str());
578 for cs in to_do.iter() {
580 let path = pkg_src.start_dir.join(&c.file);
581 debug!("Recording input: {}", path.display());
582 // FIXME (#9639): This needs to handle non-utf8 paths
583 inputs.push((~"file", path.as_str().unwrap().to_owned()));
584 build_inputs.push(path);
588 let result = self.install_no_build(pkg_src.build_workspace(),
590 &pkg_src.destination_workspace,
591 &id).map(|s| Path::init(s.as_slice()));
592 installed_files = installed_files + result;
593 note(format!("Installed package {} to {}",
595 pkg_src.destination_workspace.display()));
596 (installed_files, inputs)
599 // again, working around lack of Encodable for Path
600 fn install_no_build(&self,
601 build_workspace: &Path,
602 build_inputs: &[Path],
603 target_workspace: &Path,
604 id: &PkgId) -> ~[~str] {
606 debug!("install_no_build: assuming {} comes from {} with target {}",
607 id.to_str(), build_workspace.display(), target_workspace.display());
609 // Now copy stuff into the install dirs
610 let maybe_executable = built_executable_in_workspace(id, build_workspace);
611 let maybe_library = built_library_in_workspace(id, build_workspace);
612 let target_exec = target_executable_in_workspace(id, target_workspace);
613 let target_lib = maybe_library.as_ref()
614 .map(|_| target_library_in_workspace(id, target_workspace));
616 debug!("target_exec = {} target_lib = {:?} \
617 maybe_executable = {:?} maybe_library = {:?}",
618 target_exec.display(), target_lib,
619 maybe_executable, maybe_library);
621 self.workcache_context.with_prep(id.install_tag(), |prep| {
622 for ee in maybe_executable.iter() {
623 // FIXME (#9639): This needs to handle non-utf8 paths
624 prep.declare_input("binary",
625 ee.as_str().unwrap(),
626 workcache_support::digest_only_date(ee));
628 for ll in maybe_library.iter() {
629 // FIXME (#9639): This needs to handle non-utf8 paths
630 prep.declare_input("binary",
631 ll.as_str().unwrap(),
632 workcache_support::digest_only_date(ll));
634 let subex = maybe_executable.clone();
635 let sublib = maybe_library.clone();
636 let sub_target_ex = target_exec.clone();
637 let sub_target_lib = target_lib.clone();
638 let sub_build_inputs = build_inputs.to_owned();
639 prep.exec(proc(exe_thing) {
640 let mut outputs = ~[];
641 // Declare all the *inputs* to the declared input too, as inputs
642 for executable in subex.iter() {
643 exe_thing.discover_input("binary",
644 executable.as_str().unwrap().to_owned(),
645 workcache_support::digest_only_date(executable));
647 for library in sublib.iter() {
648 exe_thing.discover_input("binary",
649 library.as_str().unwrap().to_owned(),
650 workcache_support::digest_only_date(library));
653 for transitive_dependency in sub_build_inputs.iter() {
654 exe_thing.discover_input(
656 transitive_dependency.as_str().unwrap().to_owned(),
657 workcache_support::digest_file_with_date(transitive_dependency));
661 for exec in subex.iter() {
662 debug!("Copying: {} -> {}", exec.display(), sub_target_ex.display());
663 fs::mkdir_recursive(&sub_target_ex.dir_path(), io::UserRWX);
664 fs::copy(exec, &sub_target_ex);
665 // FIXME (#9639): This needs to handle non-utf8 paths
666 exe_thing.discover_output("binary",
667 sub_target_ex.as_str().unwrap(),
668 workcache_support::digest_only_date(&sub_target_ex));
669 outputs.push(sub_target_ex.as_str().unwrap().to_owned());
671 for lib in sublib.iter() {
672 let mut target_lib = sub_target_lib
673 .clone().expect(format!("I built {} but apparently \
674 didn't install it!", lib.display()));
675 target_lib.set_filename(lib.filename().expect("weird target lib"));
676 fs::mkdir_recursive(&target_lib.dir_path(), io::UserRWX);
677 fs::copy(lib, &target_lib);
678 debug!("3. discovering output {}", target_lib.display());
679 exe_thing.discover_output("binary",
680 target_lib.as_str().unwrap(),
681 workcache_support::digest_only_date(&target_lib));
682 outputs.push(target_lib.as_str().unwrap().to_owned());
689 fn prefer(&self, _id: &str, _vers: Option<~str>) {
690 fail!("prefer not yet implemented");
693 fn test(&self, pkgid: &PkgId, workspace: &Path) {
694 match built_test_in_workspace(pkgid, workspace) {
696 debug!("test: test_exec = {}", test_exec.display());
697 // FIXME (#9639): This needs to handle non-utf8 paths
698 let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
699 if !status.success() {
700 fail!("Some tests failed");
704 error(format!("Internal error: test executable for package ID {} in workspace {} \
705 wasn't built! Please report this as a bug.",
706 pkgid.to_str(), workspace.display()));
712 fs::mkdir_recursive(&Path::init("src"), io::UserRWX);
713 fs::mkdir_recursive(&Path::init("bin"), io::UserRWX);
714 fs::mkdir_recursive(&Path::init("lib"), io::UserRWX);
715 fs::mkdir_recursive(&Path::init("build"), io::UserRWX);
718 fn uninstall(&self, _id: &str, _vers: Option<~str>) {
719 fail!("uninstall not yet implemented");
722 fn unprefer(&self, _id: &str, _vers: Option<~str>) {
723 fail!("unprefer not yet implemented");
728 println("WARNING: The Rust package manager is experimental and may be unstable");
729 os::set_exit_status(main_args(os::args()));
732 pub fn main_args(args: &[~str]) -> int {
733 let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
734 getopts::optflag("no-link"),
735 getopts::optflag("no-trans"),
736 // n.b. Ignores different --pretty options for now
737 getopts::optflag("pretty"),
738 getopts::optflag("parse-only"),
739 getopts::optflag("S"), getopts::optflag("assembly"),
740 getopts::optmulti("c"), getopts::optmulti("cfg"),
741 getopts::optflag("v"), getopts::optflag("version"),
742 getopts::optflag("r"), getopts::optflag("rust-path-hack"),
743 getopts::optopt("sysroot"),
744 getopts::optflag("emit-llvm"),
745 getopts::optopt("linker"),
746 getopts::optopt("link-args"),
747 getopts::optopt("opt-level"),
748 getopts::optflag("O"),
749 getopts::optflag("save-temps"),
750 getopts::optopt("target"),
751 getopts::optopt("target-cpu"),
752 getopts::optmulti("Z") ];
753 let matches = &match getopts::getopts(args, opts) {
756 error(format!("{}", f.to_err_msg()));
761 let help = matches.opt_present("h") ||
762 matches.opt_present("help");
763 let no_link = matches.opt_present("no-link");
764 let no_trans = matches.opt_present("no-trans");
765 let supplied_sysroot = matches.opt_str("sysroot");
766 let generate_asm = matches.opt_present("S") ||
767 matches.opt_present("assembly");
768 let parse_only = matches.opt_present("parse-only");
769 let pretty = matches.opt_present("pretty");
770 let emit_llvm = matches.opt_present("emit-llvm");
772 if matches.opt_present("v") ||
773 matches.opt_present("version") {
774 rustc::version(args[0]);
778 let use_rust_path_hack = matches.opt_present("r") ||
779 matches.opt_present("rust-path-hack");
781 let linker = matches.opt_str("linker");
782 let link_args = matches.opt_str("link-args");
783 let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c");
784 let mut user_supplied_opt_level = true;
785 let opt_level = match matches.opt_str("opt-level") {
786 Some(~"0") => session::No,
787 Some(~"1") => session::Less,
788 Some(~"2") => session::Default,
789 Some(~"3") => session::Aggressive,
790 _ if matches.opt_present("O") => session::Default,
792 user_supplied_opt_level = false;
797 let save_temps = matches.opt_present("save-temps");
798 let target = matches.opt_str("target");
799 let target_cpu = matches.opt_str("target-cpu");
800 let experimental_features = {
801 let strs = matches.opt_strs("Z");
802 if matches.opt_present("Z") {
810 let mut args = matches.free.clone();
813 if (args.len() < 1) {
818 let rustc_flags = RustcFlags {
820 link_args: link_args,
821 optimization_level: opt_level,
822 compile_upto: if no_trans {
828 } else if parse_only {
830 } else if emit_llvm && generate_asm {
832 } else if generate_asm {
834 } else if emit_llvm {
839 save_temps: save_temps,
841 target_cpu: target_cpu,
842 additional_library_paths:
843 HashSet::new(), // No way to set this from the rustpkg command line
844 experimental_features: experimental_features
847 let mut cmd_opt = None;
848 for a in args.iter() {
849 if util::is_cmd(*a) {
854 let cmd = match cmd_opt {
860 let bad_option = context::flags_forbidden_for_cmd(&rustc_flags,
863 user_supplied_opt_level);
864 if help || bad_option {
866 ~"build" => usage::build(),
867 ~"clean" => usage::clean(),
868 ~"do" => usage::do_cmd(),
869 ~"info" => usage::info(),
870 ~"install" => usage::install(),
871 ~"list" => usage::list(),
872 ~"prefer" => usage::prefer(),
873 ~"test" => usage::test(),
874 ~"init" => usage::init(),
875 ~"uninstall" => usage::uninstall(),
876 ~"unprefer" => usage::unprefer(),
877 _ => usage::general()
880 return BAD_FLAG_CODE;
891 // Pop off all flags, plus the command
892 let remaining_args = args.iter().skip_while(|s| !util::is_cmd(**s));
893 // I had to add this type annotation to get the code to typecheck
894 let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
895 remaining_args.shift();
896 let sroot = match supplied_sysroot {
897 Some(s) => Path::init(s),
898 _ => filesearch::get_or_default_sysroot()
901 debug!("Using sysroot: {}", sroot.display());
902 let ws = default_workspace();
903 debug!("Will store workcache in {}", ws.display());
905 let rm_args = remaining_args.clone();
906 let sub_cmd = cmd.clone();
907 // Wrap the rest in task::try in case of a condition failure in a task
908 let result = do task::try {
912 rustc_flags: rustc_flags.clone(),
913 use_rust_path_hack: use_rust_path_hack,
914 sysroot: sroot.clone(), // Currently, only tests override this
916 workcache_context: api::default_context(sroot.clone(),
917 default_workspace()).workcache_context
918 }.run(sub_cmd, rm_args.clone())
920 // FIXME #9262: This is using the same error code for all errors,
921 // and at least one test case succeeds if rustpkg returns COPY_FAILED_CODE,
922 // when actually, it might set the exit code for that even if a different
923 // unhandled condition got raised.
924 if result.is_err() { return COPY_FAILED_CODE; }
928 fn declare_package_script_dependency(prep: &mut workcache::Prep, pkg_src: &PkgSrc) {
929 match pkg_src.package_script_option() {
930 // FIXME (#9639): This needs to handle non-utf8 paths
931 Some(ref p) => prep.declare_input("file", p.as_str().unwrap(),
932 workcache_support::digest_file_with_date(p)),