From e0f72e22985d0d39707f7741c0b63002889c457a Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 7 Mar 2013 14:54:23 -0800 Subject: [PATCH 1/3] checkpoint --- src/librustpkg/rustpkg.rc | 190 ++++++++++++++++++++++++++++++++++---- src/librustpkg/util.rs | 78 ++++++---------- 2 files changed, 200 insertions(+), 68 deletions(-) diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index bf1301868af5..86802c657814 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -281,7 +281,13 @@ impl Ctx { match cmd { ~"build" => { - self.build(&os::getcwd(), true, false, false); + if args.len() < 1 { + return usage::build(); + } + let pkgid = PkgId::new(args[0]); + let mut src = PkgSrc::new(&Path("."), &pkgid); + src.find_crates(); + src.build(&Path(".")); } ~"clean" => { self.clean(); @@ -927,7 +933,7 @@ pub fn main() { /// A crate is a unit of Rust code to be compiled into a binary or library pub struct Crate { - file: ~str, + file: Path, flags: ~[~str], cfgs: ~[~str] } @@ -959,28 +965,37 @@ pub fn run(listeners: ~[Listener]) { } pub impl Crate { - pub fn flag(&self, flag: ~str) -> Crate { + + static fn new(p: &Path) -> Crate { + Crate { + file: copy *p, + flags: ~[], + cfgs: ~[] + } + } + + fn flag(&self, flag: ~str) -> Crate { Crate { flags: vec::append(copy self.flags, ~[flag]), .. copy *self } } - pub fn flags(&self, flags: ~[~str]) -> Crate { + fn flags(&self, flags: ~[~str]) -> Crate { Crate { flags: vec::append(copy self.flags, flags), .. copy *self } } - pub fn cfg(&self, cfg: ~str) -> Crate { + fn cfg(&self, cfg: ~str) -> Crate { Crate { cfgs: vec::append(copy self.cfgs, ~[cfg]), .. copy *self } } - pub fn cfgs(&self, cfgs: ~[~str]) -> Crate { + fn cfgs(&self, cfgs: ~[~str]) -> Crate { Crate { cfgs: vec::append(copy self.cfgs, cfgs), .. copy *self @@ -988,15 +1003,6 @@ pub impl Crate { } } -/// Create a crate target from a source file -pub fn Crate(file: ~str) -> Crate { - Crate { - file: file, - flags: ~[], - cfgs: ~[] - } -} - /** * Get the working directory of the package script. * Assumes that the package script has been compiled @@ -1026,7 +1032,7 @@ pub fn build(crates: ~[Crate]) -> bool { let test = args[3] == ~"true"; for crates.each |&crate| { - let path = &dir.push_rel(&Path(crate.file)).normalize(); + let path = &dir.push_rel(&crate.file).normalize(); util::note(fmt!("compiling %s", path.to_str())); @@ -1043,3 +1049,155 @@ pub fn build(crates: ~[Crate]) -> bool { success } + + +// Path-fragment identifier of a package such as +// 'github.com/graydon/test'; must be a relative +// path with >=1 component. +struct PkgId { + path: Path +} + +condition! { + bad_pkg_id: (::core::path::Path, ~str) -> ::PkgId; +} + +impl PkgId { + static fn new(s: &str) -> PkgId { + use bad_pkg_id::cond; + + let p = Path(s); + if p.is_absolute { + return cond.raise((p, ~"absolute pkgid")); + } + if p.components.len() < 1 { + return cond.raise((p, ~"0-length pkgid")); + } + PkgId { + path: p + } + } +} + + +// An enumeration of the unpacked source of a package workspace. +// This contains a list of files found in the source workspace. +pub struct PkgSrc { + root: Path, + id: PkgId, + libs: ~[Crate], + mains: ~[Crate], + tests: ~[Crate], + benchs: ~[Crate], +} + +condition! { + bad_path: (::core::path::Path, ~str) -> ::core::path::Path; +} + +condition! { + build_err: (~str) -> (); +} + +impl PkgSrc { + + + static fn new(fs_root: &Path, id: &PkgId) -> PkgSrc { + PkgSrc { + root: copy *fs_root, + id: copy *id, + libs: ~[], + mains: ~[], + tests: ~[], + benchs: ~[] + } + } + + + fn check_dir(&self) -> Path { + use bad_path::cond; + + let dir = self.root.push_rel(&self.id.path).normalize(); + + if ! os::path_exists(&dir) { + return cond.raise((dir, ~"missing package dir")); + } + + if ! os::path_is_dir(&dir) { + return cond.raise((dir, ~"missing package dir")); + } + + dir + } + + + fn has_pkg_file(&self) -> bool { + let dir = self.check_dir(); + dir.push("pkg.rs").exists() + } + + + static fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { + assert p.components.len() > prefix; + let mut sub = Path(""); + for vec::slice(p.components, prefix, + p.components.len()).each |c| { + sub = sub.push(*c); + } + debug!("found crate %s", sub.to_str()); + cs.push(Crate::new(&sub)); + } + + fn find_crates(&mut self) { + use PkgSrc::push_crate; + assert ! self.has_pkg_file(); + let dir = self.check_dir(); + let prefix = dir.components.len(); + for os::walk_dir(&dir) |pth| { + match pth.filename() { + Some(~"lib.rs") => push_crate(&mut self.libs, + prefix, pth), + Some(~"main.rs") => push_crate(&mut self.mains, + prefix, pth), + Some(~"test.rs") => push_crate(&mut self.tests, + prefix, pth), + Some(~"bench.rs") => push_crate(&mut self.benchs, + prefix, pth), + _ => () + } + } + debug!("found %u libs, %u mains, %u tests, %u benchs", + self.libs.len(), + self.mains.len(), + self.tests.len(), + self.benchs.len()) + } + + + static fn build_crates(dst_dir: &Path, + src_dir: &Path, + crates: &[Crate], + test: bool) { + + for crates.each |&crate| { + let path = &src_dir.push_rel(&crate.file).normalize(); + util::note(fmt!("compiling %s", path.to_str())); + if ! util::compile_crate(None, path, + dst_dir, + crate.flags, + crate.cfgs, + false, test) { + build_err::cond.raise(fmt!("build failure on %s", + path.to_str())); + } + } + } + + fn build(&self, dst_dir: &Path) { + let dir = self.check_dir(); + PkgSrc::build_crates(dst_dir, &dir, self.libs, false); + PkgSrc::build_crates(dst_dir, &dir, self.mains, false); + PkgSrc::build_crates(dst_dir, &dir, self.tests, true); + PkgSrc::build_crates(dst_dir, &dir, self.benchs, true); + } +} \ No newline at end of file diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 9d3751c3de29..776ca3982683 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -597,11 +597,23 @@ pub fn remove_pkg(pkg: &Package) -> bool { true } -pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, - flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool { - let lib_dir = dir.push(~"lib"); - let bin_dir = dir.push(~"bin"); - let test_dir = dir.push(~"test"); +pub fn compile_input(sysroot: Option, + in_file: &Path, + out_dir: &Path, + flags: ~[~str], + cfgs: ~[~str], + opt: bool, + test: bool) -> bool { + + assert in_file.components.len() > 1; + let input = driver::file_input(copy *in_file); + let short_name = in_file.pop().filename().get(); + let out_file = out_dir.push(os::dll_filename(short_name)); + + debug!("compiling %s into %s", + in_file.to_str(), + out_file.to_str()); + let binary = os::args()[0]; let matches = getopts(flags, driver::optgroups()).get(); let options = @session::options { @@ -630,15 +642,12 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, let mut name = None; let mut vers = None; - let mut uuid = None; let mut crate_type = None; fn load_link_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, - Option<~str>, Option<~str>) { let mut name = None; let mut vers = None; - let mut uuid = None; for mis.each |a| { match a.node { @@ -647,7 +656,6 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, match *v { ~"name" => name = Some(*s), ~"vers" => vers = Some(*s), - ~"uuid" => uuid = Some(*s), _ => { } } } @@ -655,7 +663,7 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, } } - (name, vers, uuid) + (name, vers) } for crate.node.attrs.each |a| { @@ -670,11 +678,10 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, ast::meta_list(v, mis) => { match *v { ~"link" => { - let (n, v, u) = load_link_attr(mis); + let (n, v) = load_link_attr(mis); name = n; vers = v; - uuid = u; } _ => {} } @@ -683,16 +690,6 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, } } - if name.is_none() || vers.is_none() || uuid.is_none() { - error(~"link attr without (name, vers, uuid) values"); - - return false; - } - - let name = name.get(); - let vers = vers.get(); - let uuid = uuid.get(); - let is_bin = match crate_type { Some(crate_type) => { match crate_type { @@ -712,29 +709,14 @@ pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, } }; - if test { - need_dir(&test_dir); - - outputs = driver::build_output_filenames(input, &Some(test_dir), - &None, sess) - } - else if is_bin { - need_dir(&bin_dir); - - let path = bin_dir.push(fmt!("%s-%s-%s%s", name, - hash(name + uuid + vers), - vers, exe_suffix())); - outputs = driver::build_output_filenames(input, &None, &Some(path), - sess); - } else { - need_dir(&lib_dir); - - outputs = driver::build_output_filenames(input, &Some(lib_dir), - &None, sess) - } + outputs = driver::build_output_filenames(input, + &Some(copy *out_dir), + &Some(out_file), + sess); driver::compile_rest(sess, cfg, driver::cu_everything, - Some(outputs), Some(crate)); + Some(outputs), + Some(crate)); true } @@ -753,15 +735,7 @@ pub fn exe_suffix() -> ~str { ~"" } pub fn compile_crate(sysroot: Option, crate: &Path, dir: &Path, flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool { - compile_input(sysroot, driver::file_input(*crate), dir, flags, cfgs, - opt, test) -} - -pub fn compile_str(sysroot: Option, code: ~str, dir: &Path, - flags: ~[~str], cfgs: ~[~str], opt: bool, - test: bool) -> bool { - compile_input(sysroot, driver::str_input(code), dir, flags, cfgs, - opt, test) + compile_input(sysroot, crate, dir, flags, cfgs, opt, test) } #[cfg(windows)] From 8158dd7e9a3469b8c0946035b457a953faf29d99 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Thu, 11 Apr 2013 17:43:02 -0700 Subject: [PATCH 2/3] rustpkg: Use pkg IDs, remove old code for now that required packages to declare IDs explicitly This is preliminary work on bringing rustpkg up to conformance with #5679 and related issues. This change makes rustpkg infer a package ID from its containing directory, instead of requiring name and vers attributes in the code. Many aspects of it are incomplete; I've only tested one package (see README.txt) and one command, "build". So far it only works for local packages. I also removed code for several of the package commands other than "build", replacing them with stubs that fail, since they used package IDs in ways that didn't jibe well with the new scheme. I will re-implement the commands one at a time. --- src/librustpkg/README.txt | 4 + src/librustpkg/rustpkg.rc | 940 ++++++++++++++------------------------ src/librustpkg/util.rs | 545 +++++++--------------- 3 files changed, 522 insertions(+), 967 deletions(-) create mode 100644 src/librustpkg/README.txt diff --git a/src/librustpkg/README.txt b/src/librustpkg/README.txt new file mode 100644 index 000000000000..eacf07e01ea5 --- /dev/null +++ b/src/librustpkg/README.txt @@ -0,0 +1,4 @@ +Right now (2013-04-11), only one package works, the branch of rust-sdl at: +https://github.com/catamorphism/rust-sdl/tree/new-rustpkg + +and only one command works, "build". diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index 86802c657814..24c37d255895 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -27,42 +27,51 @@ extern mod rustc(vers = "0.6"); extern mod syntax(vers = "0.6"); use core::*; +pub use core::path::Path; use core::container::Map; use core::hashmap::HashMap; use core::io::WriterUtil; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use std::net::url; -use std::{json, semver, getopts}; -use syntax::codemap::spanned; +use std::{getopts}; use syntax::{ast, diagnostic}; -use util::Package; +use util::{ExitCode, Pkg, PkgId}; mod usage; mod util; -struct PackageScript { - id: ~str, - name: ~str, - vers: semver::Version, - crates: ~[~str], - deps: ~[(~str, Option<~str>)], +/// A PkgScript represents user-supplied custom logic for +/// special build hooks. This only exists for packages with +/// an explicit package script. +struct PkgScript { + /// Uniquely identifies this package + id: PkgId, + // Used to have this field: deps: ~[(~str, Option<~str>)] + // but I think it shouldn't be stored here + /// The contents of the package script: either a file path, + /// or a string containing the text of the input input: driver::input, + /// The session to use *only* for compiling the custom + /// build script sess: session::Session, + /// The config for compiling the custom build script cfg: ast::crate_cfg, + /// The crate for the custom build script crate: @ast::crate, - custom: bool + /// Directory in which to store build output + build_dir: Path } -impl PackageScript { - fn parse(parent: &Path) -> Result { - let script = parent.push(~"pkg.rs"); - - if !os::path_exists(&script) { - return result::Err(~"no pkg.rs file"); - } - +impl PkgScript { + /// Given the path name for a package script + /// and a package ID, parse the package script into + /// a PkgScript that we can then execute + fn parse(script: Path, id: PkgId) -> PkgScript { + // Get the executable name that was invoked let binary = os::args()[0]; + // Build the rustc session data structures to pass + // to the compiler let options = @session::options { binary: binary, crate_type: session::bin_crate, @@ -73,190 +82,122 @@ impl PackageScript { let cfg = driver::build_configuration(sess, binary, input); let (crate, _) = driver::compile_upto(sess, cfg, input, driver::cu_parse, None); - let mut id = None; - let mut vers = None; - let mut crates = ~[]; - let mut deps = ~[]; + let work_dir = dest_dir(id); - fn load_pkg_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, - Option<~str>) { - let mut id = None; - let mut vers = None; + debug!("Returning package script with id %?", id); - for mis.each |a| { - match a.node { - ast::meta_name_value(v, spanned { - node: ast::lit_str(s), - span: _}) => { - match *v { - ~"id" => id = Some(*s), - ~"vers" => vers = Some(*s), - _ => () - } - } - _ => {} - } - } - - (id, vers) - } - - fn load_pkg_dep_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, - Option<~str>) { - let mut url = None; - let mut target = None; - - for mis.each |a| { - match a.node { - ast::meta_name_value(v, spanned { - node: ast::lit_str(s), - span: _}) => { - match *v { - ~"url" => url = Some(*s), - ~"target" => target = Some(*s), - _ => () - } - } - _ => {} - } - } - - (url, target) - } - - fn load_pkg_crate_attr(mis: ~[@ast::meta_item]) -> Option<~str> { - let mut file = None; - - for mis.each |a| { - match a.node { - ast::meta_name_value(v, spanned { - node: ast::lit_str(s), - span: _}) => { - match *v { - ~"file" => file = Some(*s), - _ => () - } - } - _ => {} - } - } - - file - } - - for crate.node.attrs.each |a| { - match a.node.value.node { - ast::meta_list(v, mis) => { - match *v { - ~"pkg" => { - let (i, v) = load_pkg_attr(mis); - - id = i; - vers = v; - } - ~"pkg_dep" => { - let (u, t) = load_pkg_dep_attr(mis); - - if u.is_none() { - fail!(~"pkg_dep attr without a url value"); - } - - deps.push((u.get(), t)); - } - ~"pkg_crate" => { - let f = load_pkg_crate_attr(mis); - - if f.is_none() { - fail!(~"pkg_file attr without a file value"); - } - - crates.push(f.get()); - } - _ => {} - } - } - _ => {} - } - } - - let mut custom = false; - - // If we hit a function, we assume they want to use - // the build API. - for crate.node.module.items.each |i| { - match i.node { - ast::item_fn(*) => { - custom = true; - - break; - } - _ => {} - } - } - - if id.is_none() || vers.is_none() { - return result::Err(~"pkg attr without (id, vers) values"); - } - - let id = id.get(); - let name = match util::parse_name(id) { - result::Ok(name) => name, - result::Err(err) => return result::Err(err) - }; - let vers = match util::parse_vers(vers.get()) { - result::Ok(vers) => vers, - result::Err(err) => return result::Err(err) - }; - - result::Ok(PackageScript { + PkgScript { id: id, - name: name, - vers: vers, - crates: crates, - deps: deps, input: input, sess: sess, cfg: cfg, crate: crate, - custom: custom - }) + build_dir: work_dir + } } - // Build the bootstrap and run a command + /// Run the contents of this package script, where + /// is the command to pass to it (e.g., "build", "clean", "install") + /// Returns a pair of an exit code and list of configs (obtained by + /// calling the package script's configs() function if it exists // FIXME (#4432): Use workcache to only compile the script when changed - fn run(&self, cmd: ~str, test: bool) -> int { - let work_dir = self.work_dir(); - let input = self.input; + fn run_custom(&self, what: ~str) -> (~[~str], ExitCode) { + debug!("run_custom: %s", what); let sess = self.sess; - let cfg = self.cfg; - let crate = util::ready_crate(sess, self.crate); - let outputs = driver::build_output_filenames(input, &Some(work_dir), - &None, sess); - let exe = work_dir.push(~"pkg" + util::exe_suffix()); - let root = filesearch::get_rustpkg_sysroot().get().pop().pop(); - driver::compile_rest(sess, cfg, driver::cu_parse, - Some(outputs), Some(crate)); - run::run_program(exe.to_str(), ~[root.to_str(), cmd, test.to_str()]) + debug!("Working directory = %s", self.build_dir.to_str()); + // Collect together any user-defined commands in the package script + let crate = util::ready_crate(sess, self.crate); + debug!("Building output filenames with script name %s", + driver::source_name(self.input)); + match filesearch::get_rustpkg_sysroot() { + Ok(r) => { + let root = r.pop().pop().pop().pop(); // :-\ + debug!("Root is %s, calling compile_rest", root.to_str()); + util::compile_crate_from_input(self.input, Some(self.build_dir), + sess, Some(crate), os::args()[0]); + let exe = self.build_dir.push(~"pkg" + util::exe_suffix()); + debug!("Running program: %s %s %s", exe.to_str(), root.to_str(), what); + let status = run::run_program(exe.to_str(), ~[root.to_str(), what]); + if status != 0 { + return (~[], status); + } + else { + debug!("Running program (configs): %s %s %s", + exe.to_str(), root.to_str(), ~"configs"); + let output = run::program_output(exe.to_str(), ~[root.to_str(), ~"configs"]); + // Run the configs() function to get the configs + let mut cfgs = ~[]; + for str::each_word(output.out) |w| { + cfgs.push(w.to_owned()); + } + (cfgs, output.status) + } + } + Err(e) => { + fail!(fmt!("Running package script, couldn't find rustpkg sysroot (%s)", + e)) + } + } } fn hash(&self) -> ~str { - fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()), - self.vers.to_str()) + self.id.hash() } - fn work_dir(&self) -> Path { - util::root().push(~"work").push(self.hash()) - } } struct Ctx { - cfgs: ~[~str], + // I'm not sure what this is for json: bool, - dep_cache: @mut HashMap<~str, bool> + // Cache of hashes of things already installed + // though I'm not sure why the value is a bool + dep_cache: @mut HashMap<~str, bool>, +} + + +/// Returns the output directory to use. +/// Right now is always the default, should +/// support changing it. +fn dest_dir(pkgid: PkgId) -> Path { + default_dest_dir(&pkgid.path).expect( + ~"couldn't make default dir?!") + +} + +/// Returns the default output directory for compilation. +/// Creates that directory if it doesn't exist. +fn default_dest_dir(pkg_dir: &Path) -> Option { + use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; + + // For now: assumes that pkg_dir exists and is relative + // to the CWD. Change this later when we do path searching. + let rslt = pkg_dir.push("build"); + let is_dir = os::path_is_dir(&rslt); + if os::path_exists(&rslt) { + if is_dir { + Some(rslt) + } + else { + util::error(fmt!("%s is not a directory", rslt.to_str())); + None + } + } + else { + // Create it + if os::make_dir(&rslt, (S_IRUSR | S_IWUSR | S_IXUSR) as i32) { + Some(rslt) + } + else { + util::error(fmt!("Could not create directory %s", + rslt.to_str())); + None // ??? should probably use conditions + } + } } impl Ctx { + fn run(&self, cmd: ~str, args: ~[~str]) { let root = util::root(); @@ -284,20 +225,61 @@ impl Ctx { if args.len() < 1 { return usage::build(); } + // The package id is presumed to be the first command-line + // argument let pkgid = PkgId::new(args[0]); - let mut src = PkgSrc::new(&Path("."), &pkgid); + // Should allow the build directory to be configured. + // Right now it's always the "build" subdirectory in + // the package directory + let dst_dir = dest_dir(pkgid); + debug!("Destination dir = %s", dst_dir.to_str()); + // Right now, we assume the pkgid path is a valid dir + // relative to the CWD. In the future, we should search + // paths + let cwd = os::getcwd().normalize(); + debug!("Current working directory = %?", cwd); + + // Find crates inside the workspace + let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid); + debug!("Package src = %?", src); src.find_crates(); - src.build(&Path(".")); + + // Is there custom build logic? If so, use it + let pkg_src_dir = cwd.push_rel(&pkgid.path); + debug!("Package source directory = %s", pkg_src_dir.to_str()); + let cfgs = match src.package_script_option(&pkg_src_dir) { + Some(package_script_path) => { + let pscript = PkgScript::parse(package_script_path, + pkgid); + // Limited right now -- we're only running the post_build + // hook and probably fail otherwise + // also post_build should be called pre_build + let (cfgs, hook_result) = pscript.run_custom(~"post_build"); + debug!("Command return code = %?", hook_result); + if hook_result != 0 { + fail!(fmt!("Error running custom build command")) + } + // otherwise, the package script succeeded + cfgs + } + None => { + debug!("No package script, continuing"); + ~[] + } + }; + src.build(&dst_dir, cfgs); } ~"clean" => { self.clean(); } ~"do" => { - if args.len() < 1 { + if args.len() < 2 { return usage::do_cmd(); } - self.do_cmd(args[0]); + if !self.do_cmd(args[0], args[1]) { + fail!(~"a command failed!"); + } } ~"info" => { self.info(); @@ -342,7 +324,7 @@ impl Ctx { } } - fn do_cmd(&self, cmd: ~str) -> bool { + fn do_cmd(&self, cmd: ~str, pkgname: ~str) -> bool { match cmd { ~"build" | ~"test" => { util::error(~"that command cannot be manually called"); @@ -353,230 +335,87 @@ impl Ctx { } let cwd = &os::getcwd(); - let script = match PackageScript::parse(cwd) { - result::Ok(script) => script, - result::Err(err) => { - util::error(err); + let pkgid = PkgId::new(pkgname); + // Always use the "build" subdirectory of the package dir, + // but we should allow this to be configured + let dst_dir = dest_dir(pkgid); + + let mut src = PkgSrc::new(cwd, &dst_dir, &pkgid); + + match src.package_script_option(cwd) { + Some(script_path) => { + let script = PkgScript::parse(script_path, pkgid); + let (_, status) = script.run_custom(cmd); // Ignore cfgs? + if status == 42 { // ??? + util::error(~"no fns are listening for that cmd"); + + return false; + } - return false; + status == 0 + } + None => { + util::error(fmt!("invoked `do`, but there is no package script in %s", cwd.to_str())); + false } - }; - let status = script.run(cmd, false); - - if status == 42 { - util::error(~"no fns are listening for that cmd"); - - return false; } - - status == 0 } - fn build(&self, dir: &Path, verbose: bool, opt: bool, - test: bool) -> Option { - let cwd = &os::getcwd(); - let script = match PackageScript::parse(dir) { - result::Ok(script) => script, - result::Err(err) => { - util::error(err); - - return None; - } - }; - let work_dir = script.work_dir(); - let mut success = true; - - util::need_dir(&work_dir); - - if script.deps.len() >= 1 { - util::note(~"installing dependencies"); - - for script.deps.each |&dep| { - let (url, target) = dep; - - success = self.install(Some(url), target, true); - - if !success { break; } - } - - - if !success { - util::error( - fmt!("building %s v%s failed: a dep wasn't installed", - script.name, script.vers.to_str())); - - return None; - } - - util::note(~"installed dependencies"); - } - - // Build imperative crates - os::change_dir(dir); - - if script.custom { - let status = script.run(~"build", test); - - if status != 0 && status != 42 { - util::error( - fmt!("building %s v%s failed: custom logic failed (%d)", - script.name, script.vers.to_str(), status)); - - return None; - } - } - - os::change_dir(cwd); - - for script.crates.each |&crate| { - let crate = &dir.push_rel(&Path(crate)).normalize(); - - util::note(fmt!("compiling %s", crate.to_str())); - - success = self.compile(crate, &work_dir, ~[], - ~[], opt, test); - - if !success { break; } - } - - if !success { - util::error( - fmt!("building %s v%s failed: a crate failed to compile", - script.name, script.vers.to_str())); - - return None; - } - - if verbose { - util::note(fmt!("built %s v%s", script.name, - script.vers.to_str())); - } - - Some(script) + fn build(&self, _dir: &Path, _verbose: bool, _opt: bool, + _test: bool) -> Option { + // either not needed anymore, + // or needed only when we don't have a package script. Not sure which one. + fail!(); } - fn compile(&self, crate: &Path, dir: &Path, flags: ~[~str], - cfgs: ~[~str], opt: bool, test: bool) -> bool { - util::compile_crate(None, crate, dir, flags, cfgs, opt, test) + fn compile(&self, _crate: &Path, _dir: &Path, _flags: ~[~str], + _cfgs: ~[~str], _opt: bool, _test: bool) -> bool { + // What's the difference between build and compile? + fail!(~"compile not yet implemented"); } fn clean(&self) -> bool { - let script = match PackageScript::parse(&os::getcwd()) { - result::Ok(script) => script, - result::Err(err) => { - util::error(err); - - return false; - } - }; - let dir = script.work_dir(); - - util::note(fmt!("cleaning %s v%s (%s)", script.name, - script.vers.to_str(), script.id)); - - if os::path_exists(&dir) { - util::remove_dir_r(&dir); - util::note(fmt!("removed %s", dir.to_str())); - } - - util::note(fmt!("cleaned %s v%s", script.name, - script.vers.to_str())); - - true + // stub + fail!(); } fn info(&self) { - if self.json { - match PackageScript::parse(&os::getcwd()) { - result::Ok(script) => { - let mut map = ~HashMap::new(); - - map.insert(~"id", json::String(script.id)); - map.insert(~"name", json::String(script.name)); - map.insert(~"vers", json::String(script.vers.to_str())); - map.insert(~"deps", json::List(do script.deps.map |&dep| { - let (url, target) = dep; - let mut inner = ~HashMap::new(); - - inner.insert(~"url", json::String(url)); - - if !target.is_none() { - inner.insert(~"target", - json::String(target.get())); - } - - json::Object(inner) - })); - - io::println(json::to_pretty_str(&json::Object(map))); - } - result::Err(_) => io::println(~"{}") - } - } else { - let script = match PackageScript::parse(&os::getcwd()) { - result::Ok(script) => script, - result::Err(err) => { - util::error(err); - - return; - } - }; - - util::note(fmt!("id: %s", script.id)); - util::note(fmt!("name: %s", script.name)); - util::note(fmt!("vers: %s", script.vers.to_str())); - util::note(fmt!("deps: %s", - if script.deps.len() > 0 { - ~"" - } else { - ~"none" - })); - - for script.deps.each |&dep| { - let (url, target) = dep; - - util::note(fmt!(" <%s> (%s)", url, match target { - Some(target) => target, - None => ~"" - })); - } - } + // stub + fail!(); } fn install(&self, url: Option<~str>, target: Option<~str>, cache: bool) -> bool { - let mut success; - let mut dir; - - if url.is_none() { - util::note(~"installing from the cwd"); - - dir = os::getcwd(); - } else { - let url = url.get(); - let hash = util::hash(if !target.is_none() { url + target.get() } - else { url }); - - if self.dep_cache.contains_key(&hash) { - util::warn(~"already installed dep this run"); - - return true; + let dir = match url { + None => { + util::note(~"installing from the cwd"); + os::getcwd() } + Some(url) => { + let hash = util::hash(if !target.is_none() { + url + target.get() + } + else { url }); - self.dep_cache.insert(hash, true); + if self.dep_cache.contains_key(&hash) { + util::warn(~"already installed dep this run"); + return true; + } - dir = util::root().push(~"tmp").push(hash); + self.dep_cache.insert(hash, true); - if cache && os::path_exists(&dir) { - return true; + let dir = util::root().push(~"tmp").push(hash); + + if cache && os::path_exists(&dir) { + return true; + } + + if !self.fetch(&dir, url, target) { + return false; + } + dir } - - success = self.fetch(&dir, url, target); - - if !success { - return false; - } - } + }; let script = match self.build(&dir, false, true, false) { Some(script) => script, @@ -584,7 +423,7 @@ impl Ctx { return false; } }; - let work_dir = script.work_dir(); + let work_dir = script.build_dir; let from_bin_dir = work_dir.push(~"bin"); let from_lib_dir = work_dir.push(~"lib"); let to_bin_dir = util::root().push(~"bin"); @@ -606,15 +445,13 @@ impl Ctx { libs.push(to.to_str()); } - let package = Package { + let package = Pkg { id: script.id, - vers: script.vers, bins: bins, libs: libs }; - util::note(fmt!("installed %s v%s", script.name, - script.vers.to_str())); + util::note(fmt!("installed %s", script.id.to_str())); util::add_pkg(&package); true @@ -719,17 +556,9 @@ impl Ctx { return false; } }; - let name = match util::parse_name(package.id) { - result::Ok(name) => name, - result::Err(err) => { - util::error(err); + let name = package.id.path.to_str(); // ??? - return false; - } - }; - - util::note(fmt!("preferring %s v%s (%s)", name, package.vers.to_str(), - package.id)); + util::note(fmt!("preferring %s v%s", name, package.id.version.to_str())); let bin_dir = util::root().push(~"bin"); @@ -746,7 +575,7 @@ impl Ctx { util::note(fmt!("linked %s", out.to_str())); } - util::note(fmt!("preferred %s v%s", name, package.vers.to_str())); + util::note(fmt!("preferred %s v%s", name, package.id.version.to_str())); true } @@ -758,126 +587,24 @@ impl Ctx { return false; } }; - let work_dir = script.work_dir(); - let test_dir = work_dir.push(~"test"); - for os::walk_dir(&test_dir) |test| { - util::note(fmt!("running %s", test.to_str())); - - let status = run::run_program(test.to_str(), ~[]); - - if status != 0 { - os::set_exit_status(status); - } - } - - // Run custom test listener - if script.custom { - let status = script.run(~"test", false); - - if status != 0 && status != 42 { - util::error( - fmt!("testing %s v%s failed: custom logic failed (%d)", - script.name, script.vers.to_str(), status)); - - os::set_exit_status(status); - } - } - - util::note(fmt!("tested %s v%s", script.name, script.vers.to_str())); - - true + // To do + util::note(fmt!("Would test %s, but this is a dry run", + script.id.to_str())); + false } - fn uninstall(&self, id: ~str, vers: Option<~str>) -> bool { - let package = match util::get_pkg(id, vers) { - result::Ok(package) => package, - result::Err(err) => { - util::error(err); - - return false; - } - }; - let name = match util::parse_name(package.id) { - result::Ok(name) => name, - result::Err(err) => { - util::error(err); - - return false; - } - }; - - util::note(fmt!("uninstalling %s v%s (%s)", name, - package.vers.to_str(), package.id)); - - for vec::append(package.bins, package.libs).each |&file| { - let path = Path(file); - - if os::path_exists(&path) { - if os::remove_file(&path) { - util::note(fmt!("removed %s", path.to_str())); - } else { - util::error(fmt!("could not remove %s", path.to_str())); - } - } - } - - util::note(fmt!("uninstalled %s v%s", name, package.vers.to_str())); - util::remove_pkg(&package); - - true + fn uninstall(&self, _id: ~str, _vers: Option<~str>) -> bool { + fail!(~"uninstall not yet implemented"); } - fn unprefer(&self, id: ~str, vers: Option<~str>) -> bool { - let package = match util::get_pkg(id, vers) { - result::Ok(package) => package, - result::Err(err) => { - util::error(err); - - return false; - } - }; - let name = match util::parse_name(package.id) { - result::Ok(name) => name, - result::Err(err) => { - util::error(err); - - return false; - } - }; - - util::note(fmt!("unpreferring %s v%s (%s)", name, - package.vers.to_str(), package.id)); - - let bin_dir = util::root().push(~"bin"); - - for package.bins.each |&bin| { - let path = Path(bin); - let mut name = None; - for str::each_split_char(path.file_path().to_str(), '-') |s| { - name = Some(s.to_owned()); - break; - } - let out = bin_dir.push(name.unwrap()); - - if os::path_exists(&out) { - if os::remove_file(&out) { - util::note(fmt!("unlinked %s", out.to_str())); - } else { - util::error(fmt!("could not unlink %s", out.to_str())); - } - } - } - - util::note(fmt!("unpreferred %s v%s", name, package.vers.to_str())); - - true + fn unprefer(&self, _id: ~str, _vers: Option<~str>) -> bool { + fail!(~"unprefer not yet implemented"); } } pub fn main() { - io::println("WARNING: The Rust package manager is experimental and may"); - io::println("be unstable."); + io::println("WARNING: The Rust package manager is experimental and may be unstable"); let args = os::args(); let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"), @@ -895,8 +622,6 @@ pub fn main() { getopts::opt_present(matches, ~"help"); let json = getopts::opt_present(matches, ~"j") || getopts::opt_present(matches, ~"json"); - let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"), - getopts::opt_strs(matches, ~"c")); let mut args = copy matches.free; args.shift(); @@ -925,7 +650,6 @@ pub fn main() { } Ctx { - cfgs: cfgs, json: json, dep_cache: @mut HashMap::new() }.run(cmd, args); @@ -966,7 +690,7 @@ pub fn run(listeners: ~[Listener]) { pub impl Crate { - static fn new(p: &Path) -> Crate { + fn new(p: &Path) -> Crate { Crate { file: copy *p, flags: ~[], @@ -1022,68 +746,15 @@ pub fn src_dir() -> Path { os::getcwd() } -/// Build a set of crates, should be called once -pub fn build(crates: ~[Crate]) -> bool { - let args = os::args(); - let dir = src_dir(); - let work_dir = work_dir(); - let mut success = true; - let sysroot = Path(args[1]); - let test = args[3] == ~"true"; - - for crates.each |&crate| { - let path = &dir.push_rel(&crate.file).normalize(); - - util::note(fmt!("compiling %s", path.to_str())); - - success = util::compile_crate(Some(sysroot), path, &work_dir, - crate.flags, crate.cfgs, - false, test); - - if !success { break; } - } - - if !success { - os::set_exit_status(101); - } - - success -} - - -// Path-fragment identifier of a package such as -// 'github.com/graydon/test'; must be a relative -// path with >=1 component. -struct PkgId { - path: Path -} - condition! { - bad_pkg_id: (::core::path::Path, ~str) -> ::PkgId; + bad_pkg_id: (super::Path, ~str) -> ::util::PkgId; } -impl PkgId { - static fn new(s: &str) -> PkgId { - use bad_pkg_id::cond; - - let p = Path(s); - if p.is_absolute { - return cond.raise((p, ~"absolute pkgid")); - } - if p.components.len() < 1 { - return cond.raise((p, ~"0-length pkgid")); - } - PkgId { - path: p - } - } -} - - // An enumeration of the unpacked source of a package workspace. // This contains a list of files found in the source workspace. pub struct PkgSrc { - root: Path, + root: Path, // root of where the package source code lives + dst_dir: Path, // directory where we will put the compiled output id: PkgId, libs: ~[Crate], mains: ~[Crate], @@ -1092,7 +763,7 @@ pub struct PkgSrc { } condition! { - bad_path: (::core::path::Path, ~str) -> ::core::path::Path; + bad_path: (super::Path, ~str) -> super::Path; } condition! { @@ -1102,9 +773,11 @@ condition! { impl PkgSrc { - static fn new(fs_root: &Path, id: &PkgId) -> PkgSrc { + fn new(src_dir: &Path, dst_dir: &Path, + id: &PkgId) -> PkgSrc { PkgSrc { - root: copy *fs_root, + root: copy *src_dir, + dst_dir: copy *dst_dir, id: copy *id, libs: ~[], mains: ~[], @@ -1117,14 +790,20 @@ impl PkgSrc { fn check_dir(&self) -> Path { use bad_path::cond; + debug!("Pushing onto root: %s | %s", self.id.path.to_str(), + self.root.to_str()); + let dir = self.root.push_rel(&self.id.path).normalize(); - if ! os::path_exists(&dir) { + debug!("Checking dir: %s", dir.to_str()); + + if !os::path_exists(&dir) { return cond.raise((dir, ~"missing package dir")); } - - if ! os::path_is_dir(&dir) { - return cond.raise((dir, ~"missing package dir")); + + if !os::path_is_dir(&dir) { + return cond.raise((dir, ~"supplied path for package dir is a \ + non-directory")); } dir @@ -1136,9 +815,40 @@ impl PkgSrc { dir.push("pkg.rs").exists() } + // If a file named "pkg.rs" in the current directory exists, + // return the path for it. Otherwise, None + fn package_script_option(&self, cwd: &Path) -> Option { + let maybe_path = cwd.push("pkg.rs"); + if os::path_exists(&maybe_path) { + Some(maybe_path) + } + else { + None + } + } - static fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { - assert p.components.len() > prefix; + /// True if the given path's stem is self's pkg ID's stem + /// or if the pkg ID's stem is and the given path's + /// stem is foo + fn stem_matches(&self, p: &Path) -> bool { + let self_id = self.id.path.filestem(); + if self_id == p.filestem() { + return true; + } + else { + for self_id.each |pth| { + if pth.starts_with("rust-") + && match p.filestem() { + Some(s) => str::eq_slice(s, pth.slice(5, pth.len())), + None => false + } { return true; } + } + } + false + } + + fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { + assert!(p.components.len() > prefix); let mut sub = Path(""); for vec::slice(p.components, prefix, p.components.len()).each |c| { @@ -1150,9 +860,15 @@ impl PkgSrc { fn find_crates(&mut self) { use PkgSrc::push_crate; - assert ! self.has_pkg_file(); + let dir = self.check_dir(); let prefix = dir.components.len(); + // This is ugly, but can go away once we get rid + // of .rc files + let mut saw_rs = false; + let mut saw_rc = false; + debug!("Matching against %?", + self.id.path.filestem()); for os::walk_dir(&dir) |pth| { match pth.filename() { Some(~"lib.rs") => push_crate(&mut self.libs, @@ -1163,7 +879,32 @@ impl PkgSrc { prefix, pth), Some(~"bench.rs") => push_crate(&mut self.benchs, prefix, pth), - _ => () + _ => { + // If the file stem is the same as the + // package ID, with an .rs or .rc extension, + // consider it to be a crate + let ext = pth.filetype(); + let matches = |p: &Path| { + self.stem_matches(p) && (ext == Some(~".rc") + || ext == Some(~".rs")) + }; + debug!("Checking %? which %s and ext = %? %? %?", pth.filestem(), + if matches(pth) { "matches" } else { "does not match" }, + ext, saw_rs, saw_rc); + if matches(pth) && + // Avoid pushing foo.rc *and* foo.rs + !((ext == Some(~".rc") && saw_rs) || + (ext == Some(~".rs") && saw_rc)) { + push_crate(&mut self.libs, // ???? + prefix, pth); + if ext == Some(~".rc") { + saw_rc = true; + } + else if ext == Some(~".rs") { + saw_rs = true; + } + } + } } } debug!("found %u libs, %u mains, %u tests, %u benchs", @@ -1173,31 +914,40 @@ impl PkgSrc { self.benchs.len()) } - - static fn build_crates(dst_dir: &Path, + fn build_crates(dst_dir: &Path, src_dir: &Path, crates: &[Crate], + cfgs: ~[~str], test: bool) { for crates.each |&crate| { let path = &src_dir.push_rel(&crate.file).normalize(); - util::note(fmt!("compiling %s", path.to_str())); - if ! util::compile_crate(None, path, + util::note(fmt!("build_crates: compiling %s", path.to_str())); + util::note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); + + let result = util::compile_crate(None, path, dst_dir, crate.flags, - crate.cfgs, - false, test) { + crate.cfgs + cfgs, + false, test); + if !result { build_err::cond.raise(fmt!("build failure on %s", path.to_str())); } + debug!("Result of compiling %s was %?", + path.to_str(), result); } } - fn build(&self, dst_dir: &Path) { + fn build(&self, dst_dir: &Path, cfgs: ~[~str]) { let dir = self.check_dir(); - PkgSrc::build_crates(dst_dir, &dir, self.libs, false); - PkgSrc::build_crates(dst_dir, &dir, self.mains, false); - PkgSrc::build_crates(dst_dir, &dir, self.tests, true); - PkgSrc::build_crates(dst_dir, &dir, self.benchs, true); + debug!("Building libs"); + PkgSrc::build_crates(dst_dir, &dir, self.libs, cfgs, false); + debug!("Building mains"); + PkgSrc::build_crates(dst_dir, &dir, self.mains, cfgs, false); + debug!("Building tests"); + PkgSrc::build_crates(dst_dir, &dir, self.tests, cfgs, true); + debug!("Building benches"); + PkgSrc::build_crates(dst_dir, &dir, self.benchs, cfgs, true); } -} \ No newline at end of file +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 776ca3982683..819823e87447 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -9,26 +9,125 @@ // except according to those terms. use core::*; +use core::cmp::Ord; use core::hash::Streaming; -use core::hashmap::HashMap; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use std::getopts::groups::getopts; use std::semver; -use std::{json, term, sort, getopts}; +use std::{json, term, getopts}; use syntax::ast_util::*; -use syntax::codemap::{dummy_sp, spanned}; +use syntax::codemap::{dummy_sp}; use syntax::ext::base::{mk_ctxt, ext_ctxt}; use syntax::ext::build; use syntax::{ast, attr, codemap, diagnostic, fold}; +use rustc::back::link::output_type_exe; -pub struct Package { - id: ~str, - vers: semver::Version, +pub type ExitCode = int; // For now + +/// A version is either an exact revision, +/// or a semantic version +pub enum Version { + ExactRevision(float), + SemVersion(semver::Version) +} + +impl Ord for Version { + fn lt(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 < f2, + (&SemVersion(v1), &SemVersion(v2)) => v1 < v2, + _ => false // incomparable, really + } + } + fn le(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 <= f2, + (&SemVersion(v1), &SemVersion(v2)) => v1 <= v2, + _ => false // incomparable, really + } + } + fn ge(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 > f2, + (&SemVersion(v1), &SemVersion(v2)) => v1 > v2, + _ => false // incomparable, really + } + } + fn gt(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 >= f2, + (&SemVersion(v1), &SemVersion(v2)) => v1 >= v2, + _ => false // incomparable, really + } + } + +} + +impl ToStr for Version { + fn to_str(&self) -> ~str { + match *self { + ExactRevision(n) => n.to_str(), + SemVersion(v) => v.to_str() + } + } +} + +/// Placeholder +fn default_version() -> Version { ExactRevision(0.1) } + +// Path-fragment identifier of a package such as +// 'github.com/graydon/test'; path must be a relative +// path with >=1 component. +pub struct PkgId { + path: Path, + version: Version +} + +pub impl PkgId { + fn new(s: &str) -> PkgId { + use bad_pkg_id::cond; + + let p = Path(s); + if p.is_absolute { + return cond.raise((p, ~"absolute pkgid")); + } + if p.components.len() < 1 { + return cond.raise((p, ~"0-length pkgid")); + } + PkgId { + path: p, + version: default_version() + } + } + + fn hash(&self) -> ~str { + fmt!("%s-%s-%s", self.path.to_str(), + hash(self.path.to_str() + self.version.to_str()), + self.version.to_str()) + } + +} + +impl ToStr for PkgId { + fn to_str(&self) -> ~str { + // should probably use the filestem and not the whole path + fmt!("%s-v%s", self.path.to_str(), self.version.to_str()) + } +} + +pub struct Pkg { + id: PkgId, bins: ~[~str], libs: ~[~str], } +impl ToStr for Pkg { + fn to_str(&self) -> ~str { + self.id.to_str() + } +} + pub fn root() -> Path { match filesearch::get_rustpkg_root() { result::Ok(path) => path, @@ -309,294 +408,22 @@ pub fn wait_for_lock(path: &Path) { } } -fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] { - for packages.each |&package| { - match &package { - &json::Object(ref map) => { - let mut has_id = false; - - match map.get(&~"id") { - &json::String(ref str) => { - if pkg.id == *str { - has_id = true; - } - } - _ => {} - } - - match map.get(&~"vers") { - &json::String(ref str) => { - if has_id && pkg.vers.to_str() == *str { - return copy packages; - } - } - _ => {} - } - } - _ => {} - } - } - - let mut map = ~HashMap::new(); - - map.insert(~"id", json::String(pkg.id)); - map.insert(~"vers", json::String(pkg.vers.to_str())); - map.insert(~"bins", json::List(do pkg.bins.map |&bin| { - json::String(bin) - })); - map.insert(~"libs", json::List(do pkg.libs.map |&lib| { - json::String(lib) - })); - - vec::append(packages, ~[json::Object(map)]) -} - -fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] { - do packages.filter_mapped |&package| { - match &package { - &json::Object(ref map) => { - let mut has_id = false; - - match map.get(&~"id") { - &json::String(str) => { - if pkg.id == str { - has_id = true; - } - } - _ => {} - } - - match map.get(&~"vers") { - &json::String(ref str) => { - if has_id && pkg.vers.to_str() == *str { - None - } else { - Some(copy package) - } - } - _ => { Some(copy package) } - } - } - _ => { Some(copy package) } - } - } -} - pub fn load_pkgs() -> result::Result<~[json::Json], ~str> { - let root = root(); - let db = root.push(~"db.json"); - let db_lock = root.push(~"db.json.lck"); - - wait_for_lock(&db_lock); - touch(&db_lock); - - let packages = if os::path_exists(&db) { - match io::read_whole_file_str(&db) { - result::Ok(str) => { - match json::from_str(str) { - result::Ok(json) => { - match json { - json::List(list) => list, - _ => { - os::remove_file(&db_lock); - - return result::Err( - ~"package db's json is not a list"); - } - } - } - result::Err(err) => { - os::remove_file(&db_lock); - - return result::Err( - fmt!("failed to parse package db: %s", - err.to_str())); - } - } - } - result::Err(err) => { - os::remove_file(&db_lock); - - return result::Err(fmt!("failed to read package db: %s", - err)); - } - } - } else { ~[] }; - - os::remove_file(&db_lock); - - result::Ok(packages) + fail!(~"load_pkg not implemented"); } -pub fn get_pkg(id: ~str, - vers: Option<~str>) -> result::Result { - let name = match parse_name(id) { - result::Ok(name) => name, - result::Err(err) => return result::Err(err) - }; - let packages = match load_pkgs() { - result::Ok(packages) => packages, - result::Err(err) => return result::Err(err) - }; - let mut sel = None; - let mut possibs = ~[]; - let mut err = None; - - for packages.each |&package| { - match package { - json::Object(map) => { - let pid = match map.get(&~"id") { - &json::String(str) => str, - _ => loop - }; - let pname = match parse_name(pid) { - result::Ok(pname) => pname, - result::Err(perr) => { - err = Some(perr); - - break; - } - }; - let pvers = match map.get(&~"vers") { - &json::String(str) => str, - _ => loop - }; - if pid == id || pname == name { - let bins = match map.get(&~"bins") { - &json::List(ref list) => { - do list.map |&bin| { - match bin { - json::String(str) => str, - _ => ~"" - } - } - } - _ => ~[] - }; - let libs = match map.get(&~"libs") { - &json::List(ref list) => { - do list.map |&lib| { - match lib { - json::String(str) => str, - _ => ~"" - } - } - } - _ => ~[] - }; - let package = Package { - id: pid, - vers: match parse_vers(pvers) { - result::Ok(vers) => vers, - result::Err(verr) => { - err = Some(verr); - - break; - } - }, - bins: bins, - libs: libs - }; - - if !vers.is_none() && vers.get() == pvers { - sel = Some(package); - } - else { - possibs.push(package); - } - } - } - _ => {} - } - } - - if !err.is_none() { - return result::Err(err.get()); - } - if !sel.is_none() { - return result::Ok(sel.get()); - } - if !vers.is_none() || possibs.len() < 1 { - return result::Err(~"package not found"); - } - - let possibs = sort::merge_sort(possibs, |v1, v2| { - v1.vers <= v2.vers - }); - - result::Ok(copy *possibs.last()) +pub fn get_pkg(_id: ~str, + _vers: Option<~str>) -> result::Result { + fail!(~"get_pkg not implemented"); } -pub fn add_pkg(pkg: &Package) -> bool { - let root = root(); - let db = root.push(~"db.json"); - let db_lock = root.push(~"db.json.lck"); - let packages = match load_pkgs() { - result::Ok(packages) => packages, - result::Err(err) => { - error(err); - - return false; - } - }; - - wait_for_lock(&db_lock); - touch(&db_lock); - os::remove_file(&db); - - match io::mk_file_writer(&db, ~[io::Create]) { - result::Ok(writer) => { - writer.write_line(json::to_pretty_str(&json::List( - _add_pkg(packages, pkg)))); - } - result::Err(err) => { - error(fmt!("failed to dump package db: %s", err)); - os::remove_file(&db_lock); - - return false; - } - } - - os::remove_file(&db_lock); - - true -} - -pub fn remove_pkg(pkg: &Package) -> bool { - let root = root(); - let db = root.push(~"db.json"); - let db_lock = root.push(~"db.json.lck"); - let packages = match load_pkgs() { - result::Ok(packages) => packages, - result::Err(err) => { - error(err); - - return false; - } - }; - - wait_for_lock(&db_lock); - touch(&db_lock); - os::remove_file(&db); - - match io::mk_file_writer(&db, ~[io::Create]) { - result::Ok(writer) => { - writer.write_line(json::to_pretty_str(&json::List( - _rm_pkg(packages, pkg)))); - } - result::Err(err) => { - error(fmt!("failed to dump package db: %s", err)); - os::remove_file(&db_lock); - - return false; - } - } - - os::remove_file(&db_lock); - - true +pub fn add_pkg(pkg: &Pkg) -> bool { + note(fmt!("Would be adding package, but add_pkg is not yet implemented %s", + pkg.to_str())); + false } +// FIXME (#4432): Use workcache to only compile when needed pub fn compile_input(sysroot: Option, in_file: &Path, out_dir: &Path, @@ -605,22 +432,41 @@ pub fn compile_input(sysroot: Option, opt: bool, test: bool) -> bool { - assert in_file.components.len() > 1; + assert!(in_file.components.len() > 1); let input = driver::file_input(copy *in_file); - let short_name = in_file.pop().filename().get(); + debug!("compile_input: %s", in_file.to_str()); + // tjc: by default, use the package ID name as the link name + // not sure if we should support anything else + let short_name = in_file.filestem().expect("Can't compile a directory!"); + debug!("short_name = %s", short_name.to_str()); + +// Right now we're always assuming that we're building a library. +// What we should do is parse the crate and infer whether it's a library +// from the absence or presence of a main fn let out_file = out_dir.push(os::dll_filename(short_name)); + let building_library = true; debug!("compiling %s into %s", in_file.to_str(), out_file.to_str()); let binary = os::args()[0]; - let matches = getopts(flags, driver::optgroups()).get(); + + debug!("flags: %s", str::connect(flags, ~" ")); + debug!("cfgs: %s", str::connect(cfgs, ~" ")); +// Again, we assume we're building a library + let matches = getopts(~[~"-Z", ~"time-passes"] + + if building_library { ~[~"--lib"] } else { ~[] } + + flags + + cfgs.flat_map(|&c| { ~[~"--cfg", c] }), + driver::optgroups()).get(); let options = @session::options { - crate_type: session::unknown_crate, + crate_type: if building_library { session::lib_crate } + else { session::bin_crate }, optimize: if opt { session::Aggressive } else { session::No }, test: test, maybe_sysroot: sysroot, + addl_lib_search_paths: ~[copy *out_dir], .. *driver::build_session_options(binary, &matches, diagnostic::emit) }; let mut crate_cfg = options.cfg; @@ -631,94 +477,42 @@ pub fn compile_input(sysroot: Option, let options = @session::options { cfg: vec::append(options.cfg, crate_cfg), + // output_type should be conditional + output_type: output_type_exe, // Use this to get a library? That's weird .. *options }; let sess = driver::build_session(options, diagnostic::emit); + + debug!("calling compile_crate_from_input, out_dir = %s, + building_library = %?", out_dir.to_str(), sess.building_library); + compile_crate_from_input(input, Some(*out_dir), sess, None, binary); + true +} + +// Should use workcache to avoid recompiling when not necessary +// Should also rename this to something better +// If crate_opt is present, then finish compilation. If it's None, then +// call compile_upto and return the crate +pub fn compile_crate_from_input(input: driver::input, build_dir_opt: Option, + sess: session::Session, crate_opt: Option<@ast::crate>, + binary: ~str) -> @ast::crate { + debug!("Calling build_output_filenames with %?", build_dir_opt); + let outputs = driver::build_output_filenames(input, &build_dir_opt, &None, sess); + debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type); let cfg = driver::build_configuration(sess, binary, input); - let mut outputs = driver::build_output_filenames(input, &None, &None, - sess); - let (crate, _) = driver::compile_upto(sess, cfg, input, driver::cu_parse, - Some(outputs)); - - let mut name = None; - let mut vers = None; - let mut crate_type = None; - - fn load_link_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, - Option<~str>) { - let mut name = None; - let mut vers = None; - - for mis.each |a| { - match a.node { - ast::meta_name_value(v, spanned {node: ast::lit_str(s), - span: _}) => { - match *v { - ~"name" => name = Some(*s), - ~"vers" => vers = Some(*s), - _ => { } - } - } - _ => {} - } - } - - (name, vers) - } - - for crate.node.attrs.each |a| { - match a.node.value.node { - ast::meta_name_value(v, spanned {node: ast::lit_str(s), - span: _}) => { - match *v { - ~"crate_type" => crate_type = Some(*s), - _ => {} - } - } - ast::meta_list(v, mis) => { - match *v { - ~"link" => { - let (n, v) = load_link_attr(mis); - - name = n; - vers = v; - } - _ => {} - } - } - _ => {} - } - } - - let is_bin = match crate_type { - Some(crate_type) => { - match crate_type { - ~"bin" => true, - ~"lib" => false, - _ => { - warn(~"unknown crate_type, falling back to lib"); - - false - } - } + match crate_opt { + Some(c) => { + debug!("Calling compile_rest, outputs = %?", outputs); + driver::compile_rest(sess, cfg, driver::cu_everything, Some(outputs), Some(c)); + c } None => { - warn(~"missing crate_type attr, assuming lib"); - - false + debug!("Calling compile_upto, outputs = %?", outputs); + let (crate, _) = driver::compile_upto(sess, cfg, input, driver::cu_parse, + Some(outputs)); + crate } - }; - - outputs = driver::build_output_filenames(input, - &Some(copy *out_dir), - &Some(out_file), - sess); - - driver::compile_rest(sess, cfg, driver::cu_everything, - Some(outputs), - Some(crate)); - - true + } } #[cfg(windows)] @@ -731,13 +525,20 @@ pub fn exe_suffix() -> ~str { ~".exe" } pub fn exe_suffix() -> ~str { ~"" } +// Called by build_crates // FIXME (#4432): Use workcache to only compile when needed pub fn compile_crate(sysroot: Option, crate: &Path, dir: &Path, flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool { + debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str()); + debug!("compile_crate: flags =..."); + for flags.each |&fl| { + debug!("+++ %s", fl); + } compile_input(sysroot, crate, dir, flags, cfgs, opt, test) } + #[cfg(windows)] pub fn link_exe(_src: &Path, _dest: &Path) -> bool { /* FIXME (#1768): Investigate how to do this on win32 From 74fee15bc1b6c3c558bab72a644b2600c91d0d2d Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Fri, 12 Apr 2013 12:49:11 -0700 Subject: [PATCH 3/3] Tidy --- src/librustpkg/rustpkg.rc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index 24c37d255895..399fcec68040 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -162,7 +162,6 @@ struct Ctx { fn dest_dir(pkgid: PkgId) -> Path { default_dest_dir(&pkgid.path).expect( ~"couldn't make default dir?!") - } /// Returns the default output directory for compilation. @@ -339,23 +338,21 @@ impl Ctx { // Always use the "build" subdirectory of the package dir, // but we should allow this to be configured let dst_dir = dest_dir(pkgid); - + let mut src = PkgSrc::new(cwd, &dst_dir, &pkgid); - match src.package_script_option(cwd) { Some(script_path) => { let script = PkgScript::parse(script_path, pkgid); let (_, status) = script.run_custom(cmd); // Ignore cfgs? if status == 42 { // ??? util::error(~"no fns are listening for that cmd"); - return false; } - status == 0 } None => { - util::error(fmt!("invoked `do`, but there is no package script in %s", cwd.to_str())); + util::error(fmt!("invoked `do`, but there is no package script in %s", + cwd.to_str())); false } } @@ -929,8 +926,8 @@ impl PkgSrc { dst_dir, crate.flags, crate.cfgs + cfgs, - false, test); - if !result { + false, test); + if !result { build_err::cond.raise(fmt!("build failure on %s", path.to_str())); }