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 = "lib"]; // NOTE: remove after stage0 snapshot
21 #[crate_type = "dylib"];
23 #[feature(globs, managed_boxes)];
29 use std::{os, result, run, str, task};
31 use std::hashmap::HashSet;
34 pub use std::path::Path;
37 use rustc::driver::{driver, session};
38 use rustc::metadata::filesearch;
39 use rustc::metadata::filesearch::rust_path;
41 use syntax::{ast, diagnostic};
42 use messages::{error, warn, note};
43 use path_util::{build_pkg_id_in_workspace, built_test_in_workspace};
44 use path_util::in_rust_path;
45 use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace};
46 use path_util::{target_executable_in_workspace, target_library_in_workspace, dir_has_crate_file};
47 use source_control::{CheckedOutSources, is_git_dir, make_read_only};
48 use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace};
49 use workspace::determine_destination;
50 use context::{Context, BuildContext,
51 RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
52 LLVMAssemble, LLVMCompileBitcode};
53 use package_id::PkgId;
54 use package_source::PkgSrc;
55 use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench};
56 use target::{Tests, MaybeCustom, Inferred, JustOne};
57 use workcache_support::digest_only_date;
58 use exit_codes::{COPY_FAILED_CODE, BAD_FLAG_CODE};
65 mod installed_packages;
74 #[cfg(not(windows), test)] // FIXME test failure on windows: #10471
78 pub mod workcache_support;
83 /// A PkgScript represents user-supplied custom logic for
84 /// special build hooks. This only exists for packages with
85 /// an explicit package script.
86 struct PkgScript<'self> {
87 /// Uniquely identifies this package
89 /// File path for the package script
91 /// The session to use *only* for compiling the custom
93 sess: session::Session,
94 /// The config for compiling the custom build script
95 cfg: ast::CrateConfig,
96 /// The crate for the custom build script
97 crate: Option<ast::Crate>,
98 /// Directory in which to store build output
102 impl<'self> PkgScript<'self> {
103 /// Given the path name for a package script
104 /// and a package ID, parse the package script into
105 /// a PkgScript that we can then execute
106 fn parse<'a>(sysroot: Path,
109 id: &'a PkgId) -> PkgScript<'a> {
110 // Get the executable name that was invoked
111 let binary = os::args()[0].to_managed();
112 // Build the rustc session data structures to pass
114 debug!("pkgscript parse: {}", sysroot.display());
115 let options = @session::options {
117 maybe_sysroot: Some(@sysroot),
118 outputs: ~[session::OutputExecutable],
119 .. (*session::basic_options()).clone()
121 let input = driver::file_input(script.clone());
122 let sess = driver::build_session(options,
123 @diagnostic::DefaultEmitter as
124 @diagnostic::Emitter);
125 let cfg = driver::build_configuration(sess);
126 let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input);
127 let crate = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate);
128 let work_dir = build_pkg_id_in_workspace(id, workspace);
130 debug!("Returning package script with id {}", id.to_str());
142 fn build_custom(&mut self, exec: &mut workcache::Exec) -> ~str {
143 let sess = self.sess;
145 debug!("Working directory = {}", self.build_dir.display());
146 // Collect together any user-defined commands in the package script
147 let crate = util::ready_crate(sess, self.crate.take_unwrap());
148 debug!("Building output filenames with script name {}",
149 driver::source_name(&driver::file_input(self.input.clone())));
150 let exe = self.build_dir.join("pkg" + util::exe_suffix());
151 util::compile_crate_from_input(&self.input,
157 // Discover the output
158 // FIXME (#9639): This needs to handle non-utf8 paths
159 // Discover the output
160 exec.discover_output("binary", exe.as_str().unwrap().to_owned(), digest_only_date(&exe));
161 exe.as_str().unwrap().to_owned()
165 /// Run the contents of this package script, where <what>
166 /// is the command to pass to it (e.g., "build", "clean", "install")
167 /// Returns a pair of an exit code and list of configs (obtained by
168 /// calling the package script's configs() function if it exists
169 fn run_custom(exe: &Path, sysroot: &Path) -> (~[~str], process::ProcessExit) {
170 debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(),
171 sysroot.display(), "install");
172 // FIXME #7401 should support commands besides `install`
173 // FIXME (#9639): This needs to handle non-utf8 paths
174 let status = run::process_status(exe.as_str().unwrap(),
175 [sysroot.as_str().unwrap().to_owned(), ~"install"]);
176 if !status.success() {
177 debug!("run_custom: first pkg command failed with {:?}", status);
181 debug!("Running program (configs): {} {} {}",
182 exe.display(), sysroot.display(), "configs");
183 // FIXME (#9639): This needs to handle non-utf8 paths
184 let output = run::process_output(exe.as_str().unwrap(),
185 [sysroot.as_str().unwrap().to_owned(), ~"configs"]);
186 debug!("run_custom: second pkg command did {:?}", output.status);
187 // Run the configs() function to get the configs
188 let cfgs = str::from_utf8_slice(output.output).words()
189 .map(|w| w.to_owned()).collect();
190 (cfgs, output.status)
194 fn hash(&self) -> ~str {
199 pub trait CtxMethods {
200 fn run(&self, cmd: &str, args: ~[~str]);
201 fn do_cmd(&self, _cmd: &str, _pkgname: &str);
202 /// Returns a pair of the selected package ID, and the destination workspace
203 fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)>;
204 /// Returns the destination workspace
205 fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild);
206 fn clean(&self, workspace: &Path, id: &PkgId);
208 /// Returns a pair. First component is a list of installed paths,
209 /// second is a list of declared and discovered inputs
210 fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]);
211 /// Returns a list of installed files
212 fn install_no_build(&self,
213 build_workspace: &Path,
214 build_inputs: &[Path],
215 target_workspace: &Path,
216 id: &PkgId) -> ~[~str];
217 fn prefer(&self, _id: &str, _vers: Option<~str>);
218 fn test(&self, id: &PkgId, workspace: &Path);
219 fn uninstall(&self, _id: &str, _vers: Option<~str>);
220 fn unprefer(&self, _id: &str, _vers: Option<~str>);
224 impl CtxMethods for BuildContext {
225 fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(PkgId, Path)> {
226 let cwd = os::getcwd();
229 match cwd_to_workspace() {
230 None if dir_has_crate_file(&cwd) => {
231 // FIXME (#9639): This needs to handle non-utf8 paths
232 let pkgid = PkgId::new(cwd.filename_str().unwrap());
233 let mut pkg_src = PkgSrc::new(cwd, default_workspace(), true, pkgid);
234 self.build(&mut pkg_src, what);
236 PkgSrc { destination_workspace: ws,
242 None => { usage::build(); None }
243 Some((ws, pkgid)) => {
244 let mut pkg_src = PkgSrc::new(ws.clone(), ws, false, pkgid);
245 self.build(&mut pkg_src, what);
247 PkgSrc { destination_workspace: ws,
255 // The package id is presumed to be the first command-line
257 let pkgid = PkgId::new(args[0].clone());
258 let mut dest_ws = default_workspace();
259 each_pkg_parent_workspace(&self.context, &pkgid, |workspace| {
260 debug!("found pkg {} in workspace {}, trying to build",
261 pkgid.to_str(), workspace.display());
262 dest_ws = determine_destination(os::getcwd(),
263 self.context.use_rust_path_hack,
265 let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(),
266 false, pkgid.clone());
267 self.build(&mut pkg_src, what);
270 // n.b. If this builds multiple packages, it only returns the workspace for
271 // the last one. The whole building-multiple-packages-with-the-same-ID is weird
272 // anyway and there are no tests for it, so maybe take it out
273 Some((pkgid, dest_ws))
276 fn run(&self, cmd: &str, args: ~[~str]) {
277 let cwd = os::getcwd();
280 self.build_args(args, &WhatToBuild::new(MaybeCustom, Everything));
284 match cwd_to_workspace() {
285 None => { usage::clean(); return }
286 // tjc: Maybe clean should clean all the packages in the
287 // current workspace, though?
288 Some((ws, pkgid)) => self.clean(&ws, &pkgid)
293 // The package id is presumed to be the first command-line
295 let pkgid = PkgId::new(args[0].clone());
296 self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd
301 return usage::do_cmd();
304 self.do_cmd(args[0].clone(), args[1].clone());
311 match cwd_to_workspace() {
312 None if dir_has_crate_file(&cwd) => {
313 // FIXME (#9639): This needs to handle non-utf8 paths
316 PkgId::new(cwd.filename_str().unwrap());
317 self.install(PkgSrc::new(cwd, default_workspace(),
318 true, inferred_pkgid),
319 &WhatToBuild::new(MaybeCustom, Everything));
321 None => { usage::install(); return; }
322 Some((ws, pkgid)) => {
323 let pkg_src = PkgSrc::new(ws.clone(), ws.clone(), false, pkgid);
324 self.install(pkg_src, &WhatToBuild::new(MaybeCustom,
330 // The package id is presumed to be the first command-line
332 let pkgid = PkgId::new(args[0]);
333 let workspaces = pkg_parent_workspaces(&self.context, &pkgid);
334 debug!("package ID = {}, found it in {:?} workspaces",
335 pkgid.to_str(), workspaces.len());
336 if workspaces.is_empty() {
337 let d = default_workspace();
338 let src = PkgSrc::new(d.clone(), d, false, pkgid.clone());
339 self.install(src, &WhatToBuild::new(MaybeCustom, Everything));
342 for workspace in workspaces.iter() {
343 let dest = determine_destination(os::getcwd(),
344 self.context.use_rust_path_hack,
346 let src = PkgSrc::new(workspace.clone(),
348 self.context.use_rust_path_hack,
350 self.install(src, &WhatToBuild::new(MaybeCustom, Everything));
356 println("Installed packages:");
357 installed_packages::list_installed_packages(|pkg_id| {
358 pkg_id.path.display().with_str(|s| println(s));
364 return usage::uninstall();
367 self.prefer(args[0], None);
370 // Build the test executable
371 let maybe_id_and_workspace = self.build_args(args,
372 &WhatToBuild::new(MaybeCustom, Tests));
373 match maybe_id_and_workspace {
374 Some((pkg_id, workspace)) => {
375 // Assuming it's built, run the tests
376 self.test(&pkg_id, &workspace);
379 error("Testing failed because building the specified package failed.");
385 return usage::init();
392 return usage::uninstall();
395 let pkgid = PkgId::new(args[0]);
396 if !installed_packages::package_is_installed(&pkgid) {
397 warn(format!("Package {} doesn't seem to be installed! \
398 Doing nothing.", args[0]));
402 let rp = rust_path();
403 assert!(!rp.is_empty());
404 each_pkg_parent_workspace(&self.context, &pkgid, |workspace| {
405 path_util::uninstall_package_from(workspace, &pkgid);
406 note(format!("Uninstalled package {} (was installed in {})",
407 pkgid.to_str(), workspace.display()));
414 return usage::unprefer();
417 self.unprefer(args[0], None);
419 _ => fail!("I don't know the command `{}`", cmd)
423 fn do_cmd(&self, _cmd: &str, _pkgname: &str) {
425 fail!("`do` not yet implemented");
428 fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) {
429 use conditions::git_checkout_failed::cond;
431 let workspace = pkg_src.source_workspace.clone();
432 let pkgid = pkg_src.id.clone();
434 debug!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \
435 pkgid = {} pkgsrc start_dir = {}", workspace.display(),
436 in_rust_path(&workspace), is_git_dir(&workspace.join(&pkgid.path)),
437 pkgid.to_str(), pkg_src.start_dir.display());
438 debug!("build: what to build = {:?}", what_to_build);
440 // If workspace isn't in the RUST_PATH, and it's a git repo,
441 // then clone it into the first entry in RUST_PATH, and repeat
442 if !in_rust_path(&workspace) && is_git_dir(&workspace.join(&pkgid.path)) {
443 let mut out_dir = default_workspace().join("src");
444 out_dir.push(&pkgid.path);
445 let git_result = source_control::safe_git_clone(&workspace.join(&pkgid.path),
449 CheckedOutSources => make_read_only(&out_dir),
450 // FIXME (#9639): This needs to handle non-utf8 paths
451 _ => cond.raise((pkgid.path.as_str().unwrap().to_owned(), out_dir.clone()))
453 let default_ws = default_workspace();
454 debug!("Calling build recursively with {:?} and {:?}", default_ws.display(),
456 return self.build(&mut PkgSrc::new(default_ws.clone(),
459 pkgid.clone()), what_to_build);
462 // Is there custom build logic? If so, use it
463 let mut custom = false;
464 debug!("Package source directory = {}", pkg_src.to_str());
465 let opt = pkg_src.package_script_option();
466 debug!("Calling pkg_script_option on {:?}", opt);
467 let cfgs = match (pkg_src.package_script_option(), what_to_build.build_type) {
468 (Some(package_script_path), MaybeCustom) => {
469 let sysroot = self.sysroot_to_use();
470 // Build the package script if needed
471 let script_build = format!("build_package_script({})",
472 package_script_path.display());
473 let pkg_exe = self.workcache_context.with_prep(script_build, |prep| {
474 let subsysroot = sysroot.clone();
475 let psp = package_script_path.clone();
476 let ws = workspace.clone();
477 let pid = pkgid.clone();
478 prep.exec(proc(exec) {
479 let mut pscript = PkgScript::parse(subsysroot.clone(),
483 pscript.build_custom(exec)
486 // We always *run* the package script
487 let (cfgs, hook_result) = PkgScript::run_custom(&Path::init(pkg_exe), &sysroot);
488 debug!("Command return code = {:?}", hook_result);
489 if !hook_result.success() {
490 fail!("Error running custom build command")
493 // otherwise, the package script succeeded
496 (Some(_), Inferred) => {
497 debug!("There is a package script, but we're ignoring it");
501 debug!("No package script, continuing");
504 } + self.context.cfgs;
506 // If there was a package script, it should have finished
507 // the build already. Otherwise...
509 match what_to_build.sources {
510 // Find crates inside the workspace
511 Everything => pkg_src.find_crates(),
513 Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::init(s)) }),
514 // Don't infer any crates -- just build the one that was requested
516 // We expect that p is relative to the package source's start directory,
517 // so check that assumption
518 debug!("JustOne: p = {}", p.display());
519 assert!(pkg_src.start_dir.join(p).exists());
521 PkgSrc::push_crate(&mut pkg_src.libs, 0, p);
522 } else if is_main(p) {
523 PkgSrc::push_crate(&mut pkg_src.mains, 0, p);
524 } else if is_test(p) {
525 PkgSrc::push_crate(&mut pkg_src.tests, 0, p);
526 } else if is_bench(p) {
527 PkgSrc::push_crate(&mut pkg_src.benchs, 0, p);
529 warn(format!("Not building any crates for dependency {}", p.display()));
535 pkg_src.build(self, cfgs, []);
539 fn clean(&self, workspace: &Path, id: &PkgId) {
540 // Could also support a custom build hook in the pkg
541 // script for cleaning files rustpkg doesn't know about.
542 // Do something reasonable for now
544 let dir = build_pkg_id_in_workspace(id, workspace);
545 note(format!("Cleaning package {} (removing directory {})",
546 id.to_str(), dir.display()));
548 fs::rmdir_recursive(&dir);
549 note(format!("Removed directory {}", dir.display()));
552 note(format!("Cleaned package {}", id.to_str()));
557 fail!("info not yet implemented");
560 fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) {
562 let id = pkg_src.id.clone();
564 let mut installed_files = ~[];
565 let mut inputs = ~[];
566 let mut build_inputs = ~[];
568 debug!("Installing package source: {}", pkg_src.to_str());
570 // workcache only knows about *crates*. Building a package
571 // just means inferring all the crates in it, then building each one.
572 self.build(&mut pkg_src, what);
574 debug!("Done building package source {}", pkg_src.to_str());
576 let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(),
577 pkg_src.tests.clone(), pkg_src.benchs.clone()];
578 debug!("In declare inputs for {}", id.to_str());
579 for cs in to_do.iter() {
581 let path = pkg_src.start_dir.join(&c.file);
582 debug!("Recording input: {}", path.display());
583 // FIXME (#9639): This needs to handle non-utf8 paths
584 inputs.push((~"file", path.as_str().unwrap().to_owned()));
585 build_inputs.push(path);
589 let result = self.install_no_build(pkg_src.build_workspace(),
591 &pkg_src.destination_workspace,
592 &id).map(|s| Path::init(s.as_slice()));
593 installed_files = installed_files + result;
594 note(format!("Installed package {} to {}",
596 pkg_src.destination_workspace.display()));
597 (installed_files, inputs)
600 // again, working around lack of Encodable for Path
601 fn install_no_build(&self,
602 build_workspace: &Path,
603 build_inputs: &[Path],
604 target_workspace: &Path,
605 id: &PkgId) -> ~[~str] {
607 debug!("install_no_build: assuming {} comes from {} with target {}",
608 id.to_str(), build_workspace.display(), target_workspace.display());
610 // Now copy stuff into the install dirs
611 let maybe_executable = built_executable_in_workspace(id, build_workspace);
612 let maybe_library = built_library_in_workspace(id, build_workspace);
613 let target_exec = target_executable_in_workspace(id, target_workspace);
614 let target_lib = maybe_library.as_ref()
615 .map(|_| target_library_in_workspace(id, target_workspace));
617 debug!("target_exec = {} target_lib = {:?} \
618 maybe_executable = {:?} maybe_library = {:?}",
619 target_exec.display(), target_lib,
620 maybe_executable, maybe_library);
622 self.workcache_context.with_prep(id.install_tag(), |prep| {
623 for ee in maybe_executable.iter() {
624 // FIXME (#9639): This needs to handle non-utf8 paths
625 prep.declare_input("binary",
626 ee.as_str().unwrap(),
627 workcache_support::digest_only_date(ee));
629 for ll in maybe_library.iter() {
630 // FIXME (#9639): This needs to handle non-utf8 paths
631 prep.declare_input("binary",
632 ll.as_str().unwrap(),
633 workcache_support::digest_only_date(ll));
635 let subex = maybe_executable.clone();
636 let sublib = maybe_library.clone();
637 let sub_target_ex = target_exec.clone();
638 let sub_target_lib = target_lib.clone();
639 let sub_build_inputs = build_inputs.to_owned();
640 prep.exec(proc(exe_thing) {
641 let mut outputs = ~[];
642 // Declare all the *inputs* to the declared input too, as inputs
643 for executable in subex.iter() {
644 exe_thing.discover_input("binary",
645 executable.as_str().unwrap().to_owned(),
646 workcache_support::digest_only_date(executable));
648 for library in sublib.iter() {
649 exe_thing.discover_input("binary",
650 library.as_str().unwrap().to_owned(),
651 workcache_support::digest_only_date(library));
654 for transitive_dependency in sub_build_inputs.iter() {
655 exe_thing.discover_input(
657 transitive_dependency.as_str().unwrap().to_owned(),
658 workcache_support::digest_file_with_date(transitive_dependency));
662 for exec in subex.iter() {
663 debug!("Copying: {} -> {}", exec.display(), sub_target_ex.display());
664 fs::mkdir_recursive(&sub_target_ex.dir_path(), io::UserRWX);
665 fs::copy(exec, &sub_target_ex);
666 // FIXME (#9639): This needs to handle non-utf8 paths
667 exe_thing.discover_output("binary",
668 sub_target_ex.as_str().unwrap(),
669 workcache_support::digest_only_date(&sub_target_ex));
670 outputs.push(sub_target_ex.as_str().unwrap().to_owned());
672 for lib in sublib.iter() {
673 let mut target_lib = sub_target_lib
674 .clone().expect(format!("I built {} but apparently \
675 didn't install it!", lib.display()));
676 target_lib.set_filename(lib.filename().expect("weird target lib"));
677 fs::mkdir_recursive(&target_lib.dir_path(), io::UserRWX);
678 fs::copy(lib, &target_lib);
679 debug!("3. discovering output {}", target_lib.display());
680 exe_thing.discover_output("binary",
681 target_lib.as_str().unwrap(),
682 workcache_support::digest_only_date(&target_lib));
683 outputs.push(target_lib.as_str().unwrap().to_owned());
690 fn prefer(&self, _id: &str, _vers: Option<~str>) {
691 fail!("prefer not yet implemented");
694 fn test(&self, pkgid: &PkgId, workspace: &Path) {
695 match built_test_in_workspace(pkgid, workspace) {
697 debug!("test: test_exec = {}", test_exec.display());
698 // FIXME (#9639): This needs to handle non-utf8 paths
699 let status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]);
700 if !status.success() {
701 fail!("Some tests failed");
705 error(format!("Internal error: test executable for package ID {} in workspace {} \
706 wasn't built! Please report this as a bug.",
707 pkgid.to_str(), workspace.display()));
713 fs::mkdir_recursive(&Path::init("src"), io::UserRWX);
714 fs::mkdir_recursive(&Path::init("bin"), io::UserRWX);
715 fs::mkdir_recursive(&Path::init("lib"), io::UserRWX);
716 fs::mkdir_recursive(&Path::init("build"), io::UserRWX);
719 fn uninstall(&self, _id: &str, _vers: Option<~str>) {
720 fail!("uninstall not yet implemented");
723 fn unprefer(&self, _id: &str, _vers: Option<~str>) {
724 fail!("unprefer not yet implemented");
729 println("WARNING: The Rust package manager is experimental and may be unstable");
730 os::set_exit_status(main_args(os::args()));
733 pub fn main_args(args: &[~str]) -> int {
734 let opts = ~[getopts::optflag("h"), getopts::optflag("help"),
735 getopts::optflag("no-link"),
736 getopts::optflag("no-trans"),
737 // n.b. Ignores different --pretty options for now
738 getopts::optflag("pretty"),
739 getopts::optflag("parse-only"),
740 getopts::optflag("S"), getopts::optflag("assembly"),
741 getopts::optmulti("c"), getopts::optmulti("cfg"),
742 getopts::optflag("v"), getopts::optflag("version"),
743 getopts::optflag("r"), getopts::optflag("rust-path-hack"),
744 getopts::optopt("sysroot"),
745 getopts::optflag("emit-llvm"),
746 getopts::optopt("linker"),
747 getopts::optopt("link-args"),
748 getopts::optopt("opt-level"),
749 getopts::optflag("O"),
750 getopts::optflag("save-temps"),
751 getopts::optopt("target"),
752 getopts::optopt("target-cpu"),
753 getopts::optmulti("Z") ];
754 let matches = &match getopts::getopts(args, opts) {
757 error(format!("{}", f.to_err_msg()));
762 let help = matches.opt_present("h") ||
763 matches.opt_present("help");
764 let no_link = matches.opt_present("no-link");
765 let no_trans = matches.opt_present("no-trans");
766 let supplied_sysroot = matches.opt_str("sysroot");
767 let generate_asm = matches.opt_present("S") ||
768 matches.opt_present("assembly");
769 let parse_only = matches.opt_present("parse-only");
770 let pretty = matches.opt_present("pretty");
771 let emit_llvm = matches.opt_present("emit-llvm");
773 if matches.opt_present("v") ||
774 matches.opt_present("version") {
775 rustc::version(args[0]);
779 let use_rust_path_hack = matches.opt_present("r") ||
780 matches.opt_present("rust-path-hack");
782 let linker = matches.opt_str("linker");
783 let link_args = matches.opt_str("link-args");
784 let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c");
785 let mut user_supplied_opt_level = true;
786 let opt_level = match matches.opt_str("opt-level") {
787 Some(~"0") => session::No,
788 Some(~"1") => session::Less,
789 Some(~"2") => session::Default,
790 Some(~"3") => session::Aggressive,
791 _ if matches.opt_present("O") => session::Default,
793 user_supplied_opt_level = false;
798 let save_temps = matches.opt_present("save-temps");
799 let target = matches.opt_str("target");
800 let target_cpu = matches.opt_str("target-cpu");
801 let experimental_features = {
802 let strs = matches.opt_strs("Z");
803 if matches.opt_present("Z") {
811 let mut args = matches.free.clone();
814 if (args.len() < 1) {
819 let rustc_flags = RustcFlags {
821 link_args: link_args,
822 optimization_level: opt_level,
823 compile_upto: if no_trans {
829 } else if parse_only {
831 } else if emit_llvm && generate_asm {
833 } else if generate_asm {
835 } else if emit_llvm {
840 save_temps: save_temps,
842 target_cpu: target_cpu,
843 additional_library_paths:
844 HashSet::new(), // No way to set this from the rustpkg command line
845 experimental_features: experimental_features
848 let mut cmd_opt = None;
849 for a in args.iter() {
850 if util::is_cmd(*a) {
855 let cmd = match cmd_opt {
861 let bad_option = context::flags_forbidden_for_cmd(&rustc_flags,
864 user_supplied_opt_level);
865 if help || bad_option {
867 ~"build" => usage::build(),
868 ~"clean" => usage::clean(),
869 ~"do" => usage::do_cmd(),
870 ~"info" => usage::info(),
871 ~"install" => usage::install(),
872 ~"list" => usage::list(),
873 ~"prefer" => usage::prefer(),
874 ~"test" => usage::test(),
875 ~"init" => usage::init(),
876 ~"uninstall" => usage::uninstall(),
877 ~"unprefer" => usage::unprefer(),
878 _ => usage::general()
881 return BAD_FLAG_CODE;
892 // Pop off all flags, plus the command
893 let remaining_args = args.iter().skip_while(|s| !util::is_cmd(**s));
894 // I had to add this type annotation to get the code to typecheck
895 let mut remaining_args: ~[~str] = remaining_args.map(|s| (*s).clone()).collect();
896 remaining_args.shift();
897 let sroot = match supplied_sysroot {
898 Some(s) => Path::init(s),
899 _ => filesearch::get_or_default_sysroot()
902 debug!("Using sysroot: {}", sroot.display());
903 let ws = default_workspace();
904 debug!("Will store workcache in {}", ws.display());
906 let rm_args = remaining_args.clone();
907 let sub_cmd = cmd.clone();
908 // Wrap the rest in task::try in case of a condition failure in a task
909 let result = do task::try {
913 rustc_flags: rustc_flags.clone(),
914 use_rust_path_hack: use_rust_path_hack,
915 sysroot: sroot.clone(), // Currently, only tests override this
917 workcache_context: api::default_context(sroot.clone(),
918 default_workspace()).workcache_context
919 }.run(sub_cmd, rm_args.clone())
921 // FIXME #9262: This is using the same error code for all errors,
922 // and at least one test case succeeds if rustpkg returns COPY_FAILED_CODE,
923 // when actually, it might set the exit code for that even if a different
924 // unhandled condition got raised.
925 if result.is_err() { return COPY_FAILED_CODE; }
929 fn declare_package_script_dependency(prep: &mut workcache::Prep, pkg_src: &PkgSrc) {
930 match pkg_src.package_script_option() {
931 // FIXME (#9639): This needs to handle non-utf8 paths
932 Some(ref p) => prep.declare_input("file", p.as_str().unwrap(),
933 workcache_support::digest_file_with_date(p)),