diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5e33a8f78fea81f0023d9cdf3bd3a2aec662ca76..9e23c7df212d81abc3460b95cb7e27e8576cd006 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -161,8 +161,15 @@ pub fn parse_input(sess: Session, cfg: ast::crate_cfg, input: &input) } } +/// First phase to do, last phase to do #[deriving(Eq)] -pub enum compile_upto { +pub struct compile_upto { + from: compile_phase, + to: compile_phase +} + +#[deriving(Eq)] +pub enum compile_phase { cu_parse, cu_expand, cu_typeck, @@ -177,138 +184,147 @@ pub enum compile_upto { #[fixed_stack_segment] pub fn compile_rest(sess: Session, cfg: ast::crate_cfg, - upto: compile_upto, + phases: compile_upto, outputs: Option<@OutputFilenames>, curr: Option<@ast::crate>) -> (Option<@ast::crate>, Option) { let time_passes = sess.time_passes(); - let (llmod, link_meta) = { + let mut crate_opt = curr; - let mut crate = curr.unwrap(); + if phases.from == cu_parse || phases.from == cu_everything { *sess.building_library = session::building_library( - sess.opts.crate_type, crate, sess.opts.test); + sess.opts.crate_type, crate_opt.unwrap(), sess.opts.test); - crate = time(time_passes, ~"expansion", || + crate_opt = Some(time(time_passes, ~"expansion", || syntax::ext::expand::expand_crate(sess.parse_sess, copy cfg, - crate)); + crate_opt.unwrap()))); + + crate_opt = Some(time(time_passes, ~"configuration", || + front::config::strip_unconfigured_items(crate_opt.unwrap()))); + + crate_opt = Some(time(time_passes, ~"maybe building test harness", || + front::test::modify_for_testing(sess, crate_opt.unwrap()))); + } - crate = time(time_passes, ~"configuration", || - front::config::strip_unconfigured_items(crate)); + if phases.to == cu_expand { return (crate_opt, None); } - crate = time(time_passes, ~"maybe building test harness", || - front::test::modify_for_testing(sess, crate)); + assert!(phases.from != cu_no_trans); - if upto == cu_expand { return (Some(crate), None); } + let mut crate = crate_opt.unwrap(); - crate = time(time_passes, ~"intrinsic injection", || - front::intrinsic_inject::inject_intrinsic(sess, crate)); + let (llmod, link_meta) = { + crate = time(time_passes, ~"intrinsic injection", || + front::intrinsic_inject::inject_intrinsic(sess, crate)); - crate = time(time_passes, ~"extra injection", || - front::std_inject::maybe_inject_libstd_ref(sess, crate)); + crate = time(time_passes, ~"extra injection", || + front::std_inject::maybe_inject_libstd_ref(sess, crate)); - let ast_map = time(time_passes, ~"ast indexing", || - syntax::ast_map::map_crate(sess.diagnostic(), crate)); + let ast_map = time(time_passes, ~"ast indexing", || + syntax::ast_map::map_crate(sess.diagnostic(), crate)); - time(time_passes, ~"external crate/lib resolution", || - creader::read_crates(sess.diagnostic(), crate, sess.cstore, - sess.filesearch, - session::sess_os_to_meta_os(sess.targ_cfg.os), - sess.opts.is_static, - sess.parse_sess.interner)); + time(time_passes, ~"external crate/lib resolution", || + creader::read_crates(sess.diagnostic(), crate, sess.cstore, + sess.filesearch, + session::sess_os_to_meta_os(sess.targ_cfg.os), + sess.opts.is_static, + sess.parse_sess.interner)); - let lang_items = time(time_passes, ~"language item collection", || - middle::lang_items::collect_language_items(crate, sess)); + let lang_items = time(time_passes, ~"language item collection", || + middle::lang_items::collect_language_items(crate, sess)); - let middle::resolve::CrateMap { - def_map: def_map, - exp_map2: exp_map2, - trait_map: trait_map - } = - time(time_passes, ~"resolution", || - middle::resolve::resolve_crate(sess, lang_items, crate)); + let middle::resolve::CrateMap { + def_map: def_map, + exp_map2: exp_map2, + trait_map: trait_map + } = + time(time_passes, ~"resolution", || + middle::resolve::resolve_crate(sess, lang_items, crate)); - time(time_passes, ~"looking for entry point", - || middle::entry::find_entry_point(sess, crate, ast_map)); + time(time_passes, ~"looking for entry point", + || middle::entry::find_entry_point(sess, crate, ast_map)); - let freevars = time(time_passes, ~"freevar finding", || - freevars::annotate_freevars(def_map, crate)); + let freevars = time(time_passes, ~"freevar finding", || + freevars::annotate_freevars(def_map, crate)); - let region_map = time(time_passes, ~"region resolution", || - middle::region::resolve_crate(sess, def_map, crate)); + let freevars = time(time_passes, ~"freevar finding", || + freevars::annotate_freevars(def_map, crate)); - let rp_set = time(time_passes, ~"region parameterization inference", || - middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate)); + let region_map = time(time_passes, ~"region resolution", || + middle::region::resolve_crate(sess, def_map, crate)); - let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars, - region_map, rp_set, lang_items); + let rp_set = time(time_passes, ~"region parameterization inference", || + middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate)); - // passes are timed inside typeck - let (method_map, vtable_map) = typeck::check_crate( - ty_cx, trait_map, crate); + let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars, + region_map, rp_set, lang_items); - // These next two const passes can probably be merged - time(time_passes, ~"const marking", || - middle::const_eval::process_crate(crate, ty_cx)); + // passes are timed inside typeck + let (method_map, vtable_map) = typeck::check_crate( + ty_cx, trait_map, crate); - time(time_passes, ~"const checking", || - middle::check_const::check_crate(sess, crate, ast_map, def_map, - method_map, ty_cx)); + // These next two const passes can probably be merged + time(time_passes, ~"const marking", || + middle::const_eval::process_crate(crate, ty_cx)); - if upto == cu_typeck { return (Some(crate), Some(ty_cx)); } + time(time_passes, ~"const checking", || + middle::check_const::check_crate(sess, crate, ast_map, def_map, + method_map, ty_cx)); - time(time_passes, ~"privacy checking", || - middle::privacy::check_crate(ty_cx, &method_map, crate)); + if phases.to == cu_typeck { return (Some(crate), Some(ty_cx)); } - time(time_passes, ~"effect checking", || - middle::effect::check_crate(ty_cx, method_map, crate)); + time(time_passes, ~"privacy checking", || + middle::privacy::check_crate(ty_cx, &method_map, crate)); - time(time_passes, ~"loop checking", || - middle::check_loop::check_crate(ty_cx, crate)); + time(time_passes, ~"effect checking", || + middle::effect::check_crate(ty_cx, method_map, crate)); - let middle::moves::MoveMaps {moves_map, moved_variables_set, - capture_map} = - time(time_passes, ~"compute moves", || - middle::moves::compute_moves(ty_cx, method_map, crate)); + time(time_passes, ~"loop checking", || + middle::check_loop::check_crate(ty_cx, crate)); - time(time_passes, ~"match checking", || - middle::check_match::check_crate(ty_cx, method_map, - moves_map, crate)); + let middle::moves::MoveMaps {moves_map, moved_variables_set, + capture_map} = + time(time_passes, ~"compute moves", || + middle::moves::compute_moves(ty_cx, method_map, crate)); - time(time_passes, ~"liveness checking", || - middle::liveness::check_crate(ty_cx, method_map, - capture_map, crate)); + time(time_passes, ~"match checking", || + middle::check_match::check_crate(ty_cx, method_map, + moves_map, crate)); - let (root_map, write_guard_map) = - time(time_passes, ~"borrow checking", || - middle::borrowck::check_crate(ty_cx, method_map, - moves_map, moved_variables_set, - capture_map, crate)); + time(time_passes, ~"liveness checking", || + middle::liveness::check_crate(ty_cx, method_map, + capture_map, crate)); - time(time_passes, ~"kind checking", || - kind::check_crate(ty_cx, method_map, crate)); + let (root_map, write_guard_map) = + time(time_passes, ~"borrow checking", || + middle::borrowck::check_crate(ty_cx, method_map, + moves_map, moved_variables_set, + capture_map, crate)); - time(time_passes, ~"lint checking", || - lint::check_crate(ty_cx, crate)); + time(time_passes, ~"kind checking", || + kind::check_crate(ty_cx, method_map, crate)); - if upto == cu_no_trans { return (Some(crate), Some(ty_cx)); } + time(time_passes, ~"lint checking", || + lint::check_crate(ty_cx, crate)); - let maps = astencode::Maps { - root_map: root_map, - method_map: method_map, - vtable_map: vtable_map, - write_guard_map: write_guard_map, - moves_map: moves_map, - capture_map: capture_map - }; + if phases.to == cu_no_trans { return (Some(crate), Some(ty_cx)); } + + let maps = astencode::Maps { + root_map: root_map, + method_map: method_map, + vtable_map: vtable_map, + write_guard_map: write_guard_map, + moves_map: moves_map, + capture_map: capture_map + }; - let outputs = outputs.get_ref(); - time(time_passes, ~"translation", || - trans::base::trans_crate(sess, crate, ty_cx, - &outputs.obj_filename, - exp_map2, maps)) + let outputs = outputs.get_ref(); + time(time_passes, ~"translation", || + trans::base::trans_crate(sess, crate, ty_cx, + &outputs.obj_filename, + exp_map2, maps)) }; let outputs = outputs.get_ref(); @@ -351,7 +367,7 @@ pub fn compile_rest(sess: Session, } pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, - input: &input, upto: compile_upto, + input: &input, upto: compile_phase, outputs: Option<@OutputFilenames>) -> (Option<@ast::crate>, Option) { let time_passes = sess.time_passes(); @@ -359,7 +375,8 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, || parse_input(sess, copy cfg, input) ); if upto == cu_parse { return (Some(crate), None); } - compile_rest(sess, cfg, upto, outputs, Some(crate)) + compile_rest(sess, cfg, compile_upto { from: cu_parse, to: upto }, + outputs, Some(crate)) } pub fn compile_input(sess: Session, cfg: ast::crate_cfg, input: &input, @@ -367,7 +384,7 @@ pub fn compile_input(sess: Session, cfg: ast::crate_cfg, input: &input, let upto = if sess.opts.parse_only { cu_parse } else if sess.opts.no_trans { cu_no_trans } else { cu_everything }; - let outputs = build_output_filenames(input, outdir, output, sess); + let outputs = build_output_filenames(input, outdir, output, [], sess); // ??? compile_upto(sess, cfg, input, upto, Some(outputs)); } @@ -645,8 +662,7 @@ pub fn build_session_options(binary: @~str, ~"2" => Default, ~"3" => Aggressive, _ => { - early_error(demitter, ~"optimization level needs " + - "to be between 0-3") + early_error(demitter, ~"optimization level needs to be between 0-3") } } } else { No } @@ -706,7 +722,7 @@ pub fn build_session_options(binary: @~str, save_temps: save_temps, jit: jit, output_type: output_type, - addl_lib_search_paths: addl_lib_search_paths, + addl_lib_search_paths: @mut addl_lib_search_paths, linker: linker, linker_args: linker_args, maybe_sysroot: sysroot_opt, @@ -745,7 +761,7 @@ pub fn build_session_(sopts: @session::options, let filesearch = filesearch::mk_filesearch( &sopts.maybe_sysroot, sopts.target_triple, - /*bad*/copy sopts.addl_lib_search_paths); + sopts.addl_lib_search_paths); @Session_ { targ_cfg: target_cfg, opts: sopts, @@ -854,6 +870,7 @@ pub struct OutputFilenames { pub fn build_output_filenames(input: &input, odir: &Option, ofile: &Option, + attrs: &[ast::attribute], sess: Session) -> @OutputFilenames { let obj_path; @@ -863,7 +880,6 @@ pub fn build_output_filenames(input: &input, sopts.output_type != link::output_type_exe || sopts.is_static && *sess.building_library; - let obj_suffix = match sopts.output_type { link::output_type_none => ~"none", @@ -876,29 +892,44 @@ pub fn build_output_filenames(input: &input, match *ofile { None => { - // "-" as input file will cause the parser to read from stdin so we - // have to make up a name - // We want to toss everything after the final '.' - let dirpath = match *odir { - Some(ref d) => (/*bad*/copy *d), - None => match *input { - str_input(_) => os::getcwd(), - file_input(ref ifile) => (*ifile).dir_path() + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirpath = match *odir { + Some(ref d) => (/*bad*/copy *d), + None => match *input { + str_input(_) => os::getcwd(), + file_input(ref ifile) => (*ifile).dir_path() + } + }; + + let mut stem = match *input { + file_input(ref ifile) => (*ifile).filestem().get(), + str_input(_) => ~"rust_out" + }; + + // If a linkage name meta is present, we use it as the link name + let linkage_metas = attr::find_linkage_metas(attrs); + if !linkage_metas.is_empty() { + // But if a linkage meta is present, that overrides + let maybe_matches = attr::find_meta_items_by_name(linkage_metas, "name"); + if !maybe_matches.is_empty() { + match attr::get_meta_item_value_str(maybe_matches[0]) { + Some(s) => stem = copy *s, + _ => () + } + } + // If the name is missing, we just default to the filename + // version } - }; - let stem = match *input { - file_input(ref ifile) => (*ifile).filestem().get(), - str_input(_) => ~"rust_out" - }; - - if *sess.building_library { - out_path = dirpath.push(os::dll_filename(stem)); - obj_path = dirpath.push(stem).with_filetype(obj_suffix); - } else { - out_path = dirpath.push(stem); - obj_path = dirpath.push(stem).with_filetype(obj_suffix); - } + if *sess.building_library { + out_path = dirpath.push(os::dll_filename(stem)); + obj_path = dirpath.push(stem).with_filetype(obj_suffix); + } else { + out_path = dirpath.push(stem); + obj_path = dirpath.push(stem).with_filetype(obj_suffix); + } } Some(ref out_file) => { diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index ab895221de1337939bf7e3db31d43de3d065dcc9..d6a99b3edc9510ca0ca7b8138411323e1e049283 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -136,7 +136,9 @@ pub struct options { save_temps: bool, jit: bool, output_type: back::link::output_type, - addl_lib_search_paths: ~[Path], + addl_lib_search_paths: @mut ~[Path], // This is mutable for rustpkg, which + // updates search paths based on the + // parsed code linker: Option<~str>, linker_args: ~[~str], maybe_sysroot: Option<@Path>, @@ -316,7 +318,7 @@ pub fn basic_options() -> @options { save_temps: false, jit: false, output_type: link::output_type_exe, - addl_lib_search_paths: ~[], + addl_lib_search_paths: @mut ~[], linker: None, linker_args: ~[], maybe_sysroot: None, diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index 4dbcd21e8f3d5db594ddc85bf5460ca1e427d770..f83d33dcb3c0609e5ad8cb8c35fb30da9454d454 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -35,17 +35,18 @@ pub trait FileSearch { pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, target_triple: &str, - addl_lib_search_paths: ~[Path]) + addl_lib_search_paths: @mut ~[Path]) -> @FileSearch { struct FileSearchImpl { sysroot: @Path, - addl_lib_search_paths: ~[Path], + addl_lib_search_paths: @mut ~[Path], target_triple: ~str } impl FileSearch for FileSearchImpl { fn sysroot(&self) -> @Path { self.sysroot } fn for_each_lib_search_path(&self, f: &fn(&Path) -> bool) -> bool { - debug!("filesearch: searching additional lib search paths"); + debug!("filesearch: searching additional lib search paths [%?]", + self.addl_lib_search_paths.len()); // a little weird self.addl_lib_search_paths.each(f); diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc index 07056a10f3a94dfb83e59afa6aa294327173315d..baf920c04acbea3cc9851c2aae831dd424637cd1 100644 --- a/src/librustc/rustc.rc +++ b/src/librustc/rustc.rc @@ -183,11 +183,11 @@ pub fn version(argv0: &str) { pub fn usage(argv0: &str) { let message = fmt!("Usage: %s [OPTIONS] INPUT", argv0); - io::println(groups::usage(message, optgroups()) + - "Additional help: - -W help Print 'lint' options and default settings - -Z help Print internal options for debugging rustc -"); + io::println(fmt!("%s \ + Additional help: \ + -W help Print 'lint' options and default settings \ + -Z help Print internal options for debugging rustc", + groups::usage(message, optgroups()))); } pub fn describe_warnings() { diff --git a/src/librusti/rusti.rc b/src/librusti/rusti.rc index ff8b4092fabba27349ae4a30c2dcf0c04f204ebd..08325ffb2a3ec73d7d4d28b7d33b8e6f919d6cfb 100644 --- a/src/librusti/rusti.rc +++ b/src/librusti/rusti.rc @@ -121,7 +121,7 @@ fn run(repl: Repl, input: ~str) -> Repl { let options = @session::options { crate_type: session::unknown_crate, binary: binary, - addl_lib_search_paths: repl.lib_search_paths.map(|p| Path(*p)), + addl_lib_search_paths: @mut repl.lib_search_paths.map(|p| Path(*p)), jit: true, .. copy *session::basic_options() }; @@ -142,12 +142,13 @@ fn run(repl: Repl, input: ~str) -> Repl { binary, &wrapped); - let outputs = driver::build_output_filenames(&wrapped, &None, &None, sess); + let outputs = driver::build_output_filenames(&wrapped, &None, &None, [], sess); debug!("calling compile_upto"); let crate = driver::parse_input(sess, copy cfg, &wrapped); - driver::compile_rest(sess, cfg, driver::cu_everything, - Some(outputs), Some(crate)); + driver::compile_rest(sess, cfg, driver::compile_upto { from: driver::cu_parse, + to: driver::cu_everything }, + Some(outputs), Some(crate)); let mut opt = None; @@ -188,7 +189,7 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option { let binary = @copy binary; let options = @session::options { binary: binary, - addl_lib_search_paths: ~[os::getcwd()], + addl_lib_search_paths: @mut ~[os::getcwd()], .. copy *session::basic_options() }; let input = driver::file_input(copy src_path); @@ -196,7 +197,7 @@ fn compile_crate(src_filename: ~str, binary: ~str) -> Option { *sess.building_library = true; let cfg = driver::build_configuration(sess, binary, &input); let outputs = driver::build_output_filenames( - &input, &None, &None, sess); + &input, &None, &None, [], sess); // If the library already exists and is newer than the source // file, skip compilation and return None. let mut should_compile = true; diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index 680e0924d798c033fbf072e7fd1286481cce7882..caab16cd2917d88972eab952e0c3019a30d662a5 100644 --- a/src/librustpkg/conditions.rs +++ b/src/librustpkg/conditions.rs @@ -11,14 +11,14 @@ // Useful conditions pub use core::path::Path; -pub use util::PkgId; +pub use package_id::PkgId; condition! { bad_path: (super::Path, ~str) -> super::Path; } condition! { - nonexistent_package: (super::PkgId, ~str) -> (); + nonexistent_package: (super::PkgId, ~str) -> super::Path; } condition! { @@ -30,5 +30,5 @@ } condition! { - bad_pkg_id: (super::Path, ~str) -> ::util::PkgId; + bad_pkg_id: (super::Path, ~str) -> super::PkgId; } diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca0210f469b1c8e4853f08c44f7b70b0b5e7e7bc --- /dev/null +++ b/src/librustpkg/package_id.rs @@ -0,0 +1,132 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use package_path::{RemotePath, LocalPath, normalize, hash}; +use extra::semver; +use core::prelude::*; +use core::result; + +/// Placeholder +pub 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 { + /// Remote path: for example, github.com/mozilla/quux-whatever + remote_path: RemotePath, + /// Local path: for example, /home/quux/github.com/mozilla/quux_whatever + /// Note that '-' normalizes to '_' when mapping a remote path + /// onto a local path + /// Also, this will change when we implement #6407, though we'll still + /// need to keep track of separate local and remote paths + local_path: LocalPath, + /// Short name. This is the local path's filestem, but we store it + /// redundantly so as to not call get() everywhere (filestem() returns an + /// option) + short_name: ~str, + version: Version +} + +pub impl PkgId { + fn new(s: &str) -> PkgId { + use conditions::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")); + } + let remote_path = RemotePath(p); + let local_path = normalize(copy remote_path); + let short_name = (copy local_path).filestem().expect(fmt!("Strange path! %s", s)); + PkgId { + local_path: local_path, + remote_path: remote_path, + short_name: short_name, + version: default_version() + } + } + + fn hash(&self) -> ~str { + fmt!("%s-%s-%s", self.remote_path.to_str(), + hash(self.remote_path.to_str() + self.version.to_str()), + self.version.to_str()) + } + + fn short_name_with_version(&self) -> ~str { + fmt!("%s-%s", self.short_name, 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-%s", self.local_path.to_str(), self.version.to_str()) + } +} + +/// 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(ref v1), &SemVersion(ref v2)) => v1 < v2, + _ => false // incomparable, really + } + } + fn le(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 <= f2, + (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 <= v2, + _ => false // incomparable, really + } + } + fn ge(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 > f2, + (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 > v2, + _ => false // incomparable, really + } + } + fn gt(&self, other: &Version) -> bool { + match (self, other) { + (&ExactRevision(f1), &ExactRevision(f2)) => f1 >= f2, + (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 >= v2, + _ => false // incomparable, really + } + } + +} + +impl ToStr for Version { + fn to_str(&self) -> ~str { + match *self { + ExactRevision(ref n) => n.to_str(), + SemVersion(ref v) => v.to_str() + } + } +} + +pub fn parse_vers(vers: ~str) -> result::Result { + match semver::parse(vers) { + Some(vers) => result::Ok(vers), + None => result::Err(~"could not parse version: invalid") + } +} diff --git a/src/librustpkg/package_path.rs b/src/librustpkg/package_path.rs new file mode 100644 index 0000000000000000000000000000000000000000..a54f9ad152f48c9038081b527b3b99eeb6ee315b --- /dev/null +++ b/src/librustpkg/package_path.rs @@ -0,0 +1,55 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustpkg utilities having to do with local and remote paths + +use core::path::Path; +use core::option::Some; +use core::{hash, str}; +use core::rt::io::Writer; +use core::hash::Streaming; + +/// Wrappers to prevent local and remote paths from getting confused +/// (These will go away after #6407) +pub struct RemotePath (Path); +pub struct LocalPath (Path); + + +// normalize should be the only way to construct a LocalPath +// (though this isn't enforced) +/// Replace all occurrences of '-' in the stem part of path with '_' +/// This is because we treat rust-foo-bar-quux and rust_foo_bar_quux +/// as the same name +pub fn normalize(p_: RemotePath) -> LocalPath { + let RemotePath(p) = p_; + match p.filestem() { + None => LocalPath(p), + Some(st) => { + let replaced = str::replace(st, "-", "_"); + if replaced != st { + LocalPath(p.with_filestem(replaced)) + } + else { + LocalPath(p) + } + } + } +} + +pub fn write(writer: &mut W, string: &str) { + let buffer = str::as_bytes_slice(string); + writer.write(buffer); +} + +pub fn hash(data: ~str) -> ~str { + let hasher = &mut hash::default_state(); + write(hasher, data); + hasher.result_str() +} diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 2fca04196293647de3ad99087e3f3d2243bdffdd..7b4a9a63a4ec07e4cfae0e45a77bd471b36549cf 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -11,9 +11,9 @@ // rustpkg utilities having to do with paths and directories use core::prelude::*; - -pub use util::{PkgId, RemotePath, LocalPath}; -pub use util::{normalize, OutputType, Main, Lib, Bench, Test}; +pub use package_path::{RemotePath, LocalPath}; +pub use package_id::{PkgId, Version}; +pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install}; use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; use core::os::mkdir_recursive; use core::os; @@ -32,22 +32,38 @@ pub fn rust_path() -> ~[Path] { /// succeeded. pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, u_rwx) } -// n.b. So far this only handles local workspaces // n.b. The next three functions ignore the package version right // now. Should fix that. /// True if there's a directory in with /// pkgid's short name pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { - let pkgpath = workspace.push("src").push(pkgid.local_path.to_str()); + let pkgpath = workspace.push("src").push(pkgid.remote_path.to_str()); os::path_is_dir(&pkgpath) } -/// Return the directory for 's source files in . -/// Doesn't check that it exists. -pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - let result = workspace.push("src"); - result.push(pkgid.local_path.to_str()) +/// Returns a list of possible directories +/// for 's source files in . +/// Doesn't check that any of them exist. +/// (for example, try both with and without the version) +pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] { + let mut results = ~[]; + let result = workspace.push("src").push(fmt!("%s-%s", + pkgid.local_path.to_str(), pkgid.version.to_str())); + results.push(result); + results.push(workspace.push("src").push_rel(&*pkgid.remote_path)); + results +} + +/// Returns a src for pkgid that does exist -- None if none of them do +pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { + let rs = pkgid_src_in_workspace(pkgid, workspace); + for rs.each |p| { + if os::path_exists(p) { + return Some(copy *p); + } + } + None } /// Figure out what the executable name for in 's build @@ -55,7 +71,7 @@ pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { let mut result = workspace.push("build"); // should use a target-specific subdirectory - result = mk_output_path(Main, pkgid, &result); + result = mk_output_path(Main, Build, pkgid, &result); debug!("built_executable_in_workspace: checking whether %s exists", result.to_str()); if os::path_exists(&result) { @@ -83,7 +99,7 @@ pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option { let mut result = workspace.push("build"); // should use a target-specific subdirectory - result = mk_output_path(what, pkgid, &result); + result = mk_output_path(what, Build, pkgid, &result); debug!("output_in_workspace: checking whether %s exists", result.to_str()); if os::path_exists(&result) { @@ -98,17 +114,39 @@ fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Opt /// Figure out what the library name for in 's build /// directory is, and if the file exists, return it. pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { - let result = mk_output_path(Lib, pkgid, &workspace.push("build")); - debug!("built_library_in_workspace: checking whether %s exists", - result.to_str()); + // passing in local_path here sounds fishy + library_in_workspace(pkgid.local_path.to_str(), pkgid.short_name, Build, + workspace, "build") +} + +/// Does the actual searching stuff +pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option { + library_in_workspace(short_name, short_name, Install, workspace, "lib") +} + + +/// This doesn't take a PkgId, so we can use it for `extern mod` inference, where we +/// don't know the entire package ID. +/// `full_name` is used to figure out the directory to search. +/// `short_name` is taken as the link name of the library. +fn library_in_workspace(full_name: &str, short_name: &str, where: Target, + workspace: &Path, prefix: &str) -> Option { + debug!("library_in_workspace: checking whether a library named %s exists", + short_name); // We don't know what the hash is, so we have to search through the directory // contents - let dir_contents = os::list_dir(&result.pop()); + + let dir_to_search = match where { + Build => workspace.push(prefix).push(full_name), + Install => workspace.push(prefix) + }; + debug!("Listing directory %s", dir_to_search.to_str()); + let dir_contents = os::list_dir(&dir_to_search); debug!("dir has %? entries", dir_contents.len()); - let lib_prefix = fmt!("%s%s", os::consts::DLL_PREFIX, pkgid.short_name); - let lib_filetype = fmt!("%s%s", pkgid.version.to_str(), os::consts::DLL_SUFFIX); + let lib_prefix = fmt!("%s%s", os::consts::DLL_PREFIX, short_name); + let lib_filetype = os::consts::DLL_SUFFIX; debug!("lib_prefix = %s and lib_filetype = %s", lib_prefix, lib_filetype); @@ -116,9 +154,19 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option (), + _ => loop + } // Find a filename that matches the pattern: (lib_prefix)-hash-(version)(lib_suffix) // and remember what the hash was - for p.each_split_char('-') |piece| { + let f_name = match p_path.filename() { + Some(s) => s, None => loop + }; + for f_name.each_split_char('-') |piece| { debug!("a piece = %s", piece); if which == 0 && piece != lib_prefix { break; @@ -128,13 +176,6 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option Option None, Some(result_filename) => { - let result_filename = result.with_filename(result_filename); - debug!("result_filename = %s", result_filename.to_str()); - Some(result_filename) + let absolute_path = dir_to_search.push_rel(&result_filename); + debug!("result_filename = %s", absolute_path.to_str()); + Some(absolute_path) } } } @@ -166,33 +206,36 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option /// As a side effect, creates the bin-dir if it doesn't exist pub fn target_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - target_file_in_workspace(pkgid, workspace, Main) + target_file_in_workspace(pkgid, workspace, Main, Install) } /// Returns the executable that would be installed for /// in -/// As a side effect, creates the bin-dir if it doesn't exist +/// As a side effect, creates the lib-dir if it doesn't exist pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - target_file_in_workspace(pkgid, workspace, Lib) + target_file_in_workspace(pkgid, workspace, Lib, Install) } /// Returns the test executable that would be installed for /// in /// note that we *don't* install test executables, so this is just for unit testing pub fn target_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - target_file_in_workspace(pkgid, workspace, Test) + target_file_in_workspace(pkgid, workspace, Test, Install) } /// Returns the bench executable that would be installed for /// in /// note that we *don't* install bench executables, so this is just for unit testing pub fn target_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - target_file_in_workspace(pkgid, workspace, Bench) + target_file_in_workspace(pkgid, workspace, Bench, Install) } + +/// Returns the path that pkgid `pkgid` would have if placed `where` +/// in `workspace` fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, - what: OutputType) -> Path { + what: OutputType, where: Target) -> Path { use conditions::bad_path::cond; let subdir = match what { @@ -202,7 +245,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) { cond.raise((copy result, fmt!("I couldn't create the %s dir", subdir))); } - mk_output_path(what, pkgid, &result) + mk_output_path(what, where, pkgid, &result) } /// Return the directory for 's build artifacts in . @@ -224,15 +267,21 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { /// Return the output file for a given directory name, /// given whether we're building a library and whether we're building tests -pub fn mk_output_path(what: OutputType, pkg_id: &PkgId, workspace: &Path) -> Path { +pub fn mk_output_path(what: OutputType, where: Target, + pkg_id: &PkgId, workspace: &Path) -> Path { let short_name_with_version = pkg_id.short_name_with_version(); // Not local_path.dir_path()! For package foo/bar/blat/, we want // the executable blat-0.5 to live under blat/ - let dir = workspace.push_rel(&*pkg_id.local_path); - debug!("mk_output_path: short_name = %s, path = %s", + let dir = match where { + // If we're installing, it just goes under ... + Install => copy *workspace, // bad copy, but I just couldn't make the borrow checker happy + // and if we're just building, it goes in a package-specific subdir + Build => workspace.push_rel(&*pkg_id.local_path) + }; + debug!("[%?:%?] mk_output_path: short_name = %s, path = %s", what, where, if what == Lib { copy short_name_with_version } else { copy pkg_id.short_name }, dir.to_str()); - let output_path = match what { + let mut output_path = match what { // this code is duplicated from elsewhere; fix this Lib => dir.push(os::dll_filename(short_name_with_version)), // executable names *aren't* versioned @@ -244,6 +293,9 @@ pub fn mk_output_path(what: OutputType, pkg_id: &PkgId, workspace: &Path) -> Pat } os::EXE_SUFFIX)) }; + if !output_path.is_absolute() { + output_path = os::getcwd().push_rel(&output_path).normalize(); + } debug!("mk_output_path: returning %s", output_path.to_str()); output_path } diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index ada03e777ca63fa641b4b148588e9427b84713f1..005f2de3f4276fb87bf6904c6434254ebde97927 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -35,15 +35,21 @@ use rustc::metadata::filesearch; use extra::{getopts}; use syntax::{ast, diagnostic}; use util::*; -use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, u_rwx}; +use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, first_pkgid_src_in_workspace}; +use path_util::u_rwx; use path_util::{built_executable_in_workspace, built_library_in_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; use workspace::pkg_parent_workspaces; use context::Ctx; +use package_id::PkgId; mod conditions; mod context; +mod package_id; +mod package_path; mod path_util; +mod search; +mod target; #[cfg(test)] mod tests; mod util; @@ -99,8 +105,7 @@ impl<'self> PkgScript<'self> { let input = driver::file_input(script); let sess = driver::build_session(options, diagnostic::emit); let cfg = driver::build_configuration(sess, binary, &input); - let (crate, _) = driver::compile_upto(sess, copy cfg, &input, - driver::cu_parse, None); + let (crate, _) = driver::compile_upto(sess, copy cfg, &input, driver::cu_parse, None); let work_dir = build_pkg_id_in_workspace(id, workspace); debug!("Returning package script with id %?", id); @@ -134,11 +139,13 @@ impl<'self> PkgScript<'self> { let root = r.pop().pop().pop().pop(); // :-\ debug!("Root is %s, calling compile_rest", root.to_str()); let exe = self.build_dir.push(~"pkg" + util::exe_suffix()); - util::compile_crate_from_input(&self.input, self.id, - Some(copy self.build_dir), - sess, Some(crate), - &exe, @copy os::args()[0], - driver::cu_everything); + let binary = @copy os::args()[0]; + util::compile_crate_from_input(&self.input, + &self.build_dir, + sess, + crate, + driver::build_configuration(sess, + binary, &self.input)); debug!("Running program: %s %s %s", exe.to_str(), root.to_str(), what); let status = run::process_status(exe.to_str(), [root.to_str(), what]); if status != 0 { @@ -170,9 +177,9 @@ impl<'self> PkgScript<'self> { impl Ctx { - fn run(&self, cmd: ~str, args: ~[~str]) { + fn run(&self, cmd: &str, args: ~[~str]) { match cmd { - ~"build" => { + "build" => { if args.len() < 1 { return usage::build(); } @@ -183,7 +190,7 @@ impl Ctx { self.build(workspace, &pkgid); } } - ~"clean" => { + "clean" => { if args.len() < 1 { return usage::build(); } @@ -193,17 +200,17 @@ impl Ctx { let cwd = os::getcwd(); self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd } - ~"do" => { + "do" => { if args.len() < 2 { return usage::do_cmd(); } self.do_cmd(copy args[0], copy args[1]); } - ~"info" => { + "info" => { self.info(); } - ~"install" => { + "install" => { if args.len() < 1 { return usage::install(); } @@ -215,24 +222,24 @@ impl Ctx { self.install(workspace, &pkgid); } } - ~"prefer" => { + "prefer" => { if args.len() < 1 { return usage::uninstall(); } self.prefer(args[0], None); } - ~"test" => { + "test" => { self.test(); } - ~"uninstall" => { + "uninstall" => { if args.len() < 1 { return usage::uninstall(); } self.uninstall(args[0], None); } - ~"unprefer" => { + "unprefer" => { if args.len() < 1 { return usage::uninstall(); } @@ -249,7 +256,7 @@ impl Ctx { } fn build(&self, workspace: &Path, pkgid: &PkgId) { - let src_dir = pkgid_src_in_workspace(pkgid, workspace); + let src_dir = first_pkgid_src_in_workspace(pkgid, workspace); let build_dir = build_pkg_id_in_workspace(pkgid, workspace); debug!("Destination dir = %s", build_dir.to_str()); @@ -260,8 +267,8 @@ impl Ctx { // Is there custom build logic? If so, use it let pkg_src_dir = src_dir; let mut custom = false; - debug!("Package source directory = %s", pkg_src_dir.to_str()); - let cfgs = match src.package_script_option(&pkg_src_dir) { + debug!("Package source directory = %?", pkg_src_dir); + let cfgs = match pkg_src_dir.chain_ref(|p| src.package_script_option(p)) { Some(package_script_path) => { let pscript = PkgScript::parse(package_script_path, workspace, @@ -290,7 +297,7 @@ impl Ctx { // Find crates inside the workspace src.find_crates(); // Build it! - src.build(&build_dir, cfgs, self.sysroot_opt); + src.build(self, build_dir, cfgs); } } @@ -352,7 +359,7 @@ impl Ctx { } fn prefer(&self, _id: &str, _vers: Option<~str>) { - fail!(~"prefer not yet implemented"); + fail!("prefer not yet implemented"); } fn test(&self) { @@ -522,21 +529,20 @@ impl PkgSrc { fn check_dir(&self) -> Path { use conditions::nonexistent_package::cond; - debug!("Pushing onto root: %s | %s", self.id.to_str(), + debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), self.root.to_str()); - - let mut dir = self.root.push("src"); - dir = dir.push(self.id.to_str()); // ?? Should this use the version number? - - debug!("Checking dir: %s", dir.to_str()); - - if !os::path_exists(&dir) { - if !self.fetch_git() { - cond.raise((copy self.id, ~"supplied path for package dir does not \ - exist, and couldn't interpret it as a URL fragment")); + let dir; + let dirs = pkgid_src_in_workspace(&self.id, &self.root); + debug!("Checking dirs: %?", dirs); + let path = dirs.find(|d| os::path_exists(d)); + match path { + Some(d) => dir = d, + None => dir = match self.fetch_git() { + None => cond.raise((copy self.id, ~"supplied path for package dir does not \ + exist, and couldn't interpret it as a URL fragment")), + Some(d) => d } } - if !os::path_is_dir(&dir) { cond.raise((copy self.id, ~"supplied path for package dir is a \ non-directory")); @@ -546,10 +552,10 @@ impl PkgSrc { } /// Try interpreting self's package id as a remote package, and try - /// fetching it and caching it in a local directory. If that didn't - /// work, return false. + /// fetching it and caching it in a local directory. Return the cached directory + /// if this was successful, None otherwise /// (right now we only support git) - fn fetch_git(&self) -> bool { + fn fetch_git(&self) -> Option { let mut local = self.root.push("src"); local = local.push(self.id.to_str()); @@ -561,9 +567,11 @@ impl PkgSrc { if run::process_output("git", [~"clone", copy url, local.to_str()]).status != 0 { util::note(fmt!("fetching %s failed: can't clone repository", url)); - return false; + None + } + else { + Some(local) } - true } @@ -655,7 +663,7 @@ impl PkgSrc { } fn build_crates(&self, - maybe_sysroot: Option<@Path>, + ctx: &Ctx, dst_dir: &Path, src_dir: &Path, crates: &[Crate], @@ -666,11 +674,14 @@ impl PkgSrc { 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(maybe_sysroot, &self.id, path, - dst_dir, - crate.flags, - crate.cfgs + cfgs, - false, what); + let result = util::compile_crate(ctx, + &self.id, + path, + dst_dir, + crate.flags, + crate.cfgs + cfgs, + false, + what); if !result { build_err::cond.raise(fmt!("build failure on %s", path.to_str())); @@ -680,15 +691,15 @@ impl PkgSrc { } } - fn build(&self, dst_dir: &Path, cfgs: ~[~str], maybe_sysroot: Option<@Path>) { + fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { let dir = self.check_dir(); debug!("Building libs"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.libs, cfgs, Lib); + self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); debug!("Building mains"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.mains, cfgs, Main); + self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); debug!("Building tests"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.tests, cfgs, Test); + self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); debug!("Building benches"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.benchs, cfgs, Bench); + self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); } } diff --git a/src/librustpkg/search.rs b/src/librustpkg/search.rs new file mode 100644 index 0000000000000000000000000000000000000000..987e01009fc8b8580da1832e86ff78bce0f855c3 --- /dev/null +++ b/src/librustpkg/search.rs @@ -0,0 +1,25 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path_util::installed_library_in_workspace; +use core::prelude::*; + +/// If a library with path `p` matching pkg_id's name exists under sroot_opt, +/// return Some(p). Return None if there's no such path or if sroot_opt is None. +pub fn find_library_in_search_path(sroot_opt: Option<@Path>, short_name: &str) -> Option { + match sroot_opt { + Some(sroot) => { + debug!("Will search for a library with short name %s in \ + %s", short_name, (sroot.push("lib")).to_str()); + installed_library_in_workspace(short_name, sroot) + } + None => None + } +} \ No newline at end of file diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs new file mode 100644 index 0000000000000000000000000000000000000000..03c2f5a4fe42afa5ae5c2514cd461333aaae63ac --- /dev/null +++ b/src/librustpkg/target.rs @@ -0,0 +1,23 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// Data types that express build artifacts + +#[deriving(Eq)] +pub enum OutputType { Main, Lib, Bench, Test } + +#[deriving(Eq)] +pub enum Target { + // In-place build + Build, + // Install to bin/ or lib/ dir + Install +} diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 9499430ef4c20a6ca87aa035f9240a378fd5104c..a96a7a0a5fc9a7b601f193b2a14148b40ecd5637 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -17,10 +17,11 @@ use core::prelude::*; use core::result; use extra::tempfile::mkdtemp; -use util::{PkgId, default_version}; +use package_path::*; +use package_id::{PkgId, default_version}; use path_util::{target_executable_in_workspace, target_library_in_workspace, target_test_in_workspace, target_bench_in_workspace, - make_dir_rwx, u_rwx, RemotePath, LocalPath, normalize, + make_dir_rwx, u_rwx, built_bench_in_workspace, built_test_in_workspace}; fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { @@ -52,7 +53,7 @@ fn remote_pkg() -> PkgId { } } -fn writeFile(file_path: &Path, contents: ~str) { +fn writeFile(file_path: &Path, contents: &str) { let out: @io::Writer = result::get(&io::file_writer(file_path, [io::Create, io::Truncate])); @@ -64,15 +65,17 @@ fn mk_temp_workspace(short_name: &LocalPath) -> Path { // include version number in directory name let package_dir = workspace.push("src").push(fmt!("%s-0.1", short_name.to_str())); assert!(os::mkdir_recursive(&package_dir, u_rwx)); + debug!("Created %s and does it exist? %?", package_dir.to_str(), + os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files writeFile(&package_dir.push("main.rs"), - ~"fn main() { let _x = (); }"); + "fn main() { let _x = (); }"); writeFile(&package_dir.push("lib.rs"), - ~"pub fn f() { let _x = (); }"); + "pub fn f() { let _x = (); }"); writeFile(&package_dir.push("test.rs"), - ~"#[test] pub fn f() { (); }"); + "#[test] pub fn f() { (); }"); writeFile(&package_dir.push("bench.rs"), - ~"#[bench] pub fn f() { (); }"); + "#[bench] pub fn f() { (); }"); workspace } @@ -111,6 +114,8 @@ fn test_make_dir_rwx() { #[test] fn test_install_valid() { + use path_util::installed_library_in_workspace; + let sysroot = test_sysroot(); debug!("sysroot = %s", sysroot.to_str()); let ctxt = fake_ctxt(Some(@sysroot)); @@ -123,10 +128,12 @@ fn test_install_valid() { debug!("exec = %s", exec.to_str()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); - let lib = target_library_in_workspace(&temp_pkg_id, &temp_workspace); - debug!("lib = %s", lib.to_str()); - assert!(os::path_exists(&lib)); - assert!(is_rwx(&lib)); + + let lib = installed_library_in_workspace(temp_pkg_id.short_name, &temp_workspace); + debug!("lib = %?", lib); + assert!(lib.map_default(false, |l| os::path_exists(l))); + assert!(lib.map_default(false, |l| is_rwx(l))); + // And that the test and bench executables aren't installed assert!(!os::path_exists(&target_test_in_workspace(&temp_pkg_id, &temp_workspace))); let bench = target_bench_in_workspace(&temp_pkg_id, &temp_workspace); @@ -149,6 +156,7 @@ fn test_install_invalid() { }).in { do cond.trap(|_| { error_occurred = true; + copy temp_workspace }).in { ctxt.install(&temp_workspace, &pkgid); } @@ -174,10 +182,11 @@ fn test_install_url() { debug!("lib = %s", lib.to_str()); assert!(os::path_exists(&lib)); assert!(is_rwx(&lib)); - let built_test = built_test_in_workspace(&temp_pkg_id, &workspace).expect("test_install_url"); + let built_test = built_test_in_workspace(&temp_pkg_id, + &workspace).expect("test_install_url: built test should exist"); assert!(os::path_exists(&built_test)); let built_bench = built_bench_in_workspace(&temp_pkg_id, - &workspace).expect("test_install_url"); + &workspace).expect("test_install_url: built bench should exist"); assert!(os::path_exists(&built_bench)); // And that the test and bench executables aren't installed let test = target_test_in_workspace(&temp_pkg_id, &workspace); diff --git a/src/librustpkg/testsuite/pass/src/external-crate/main.rs b/src/librustpkg/testsuite/pass/src/external-crate/main.rs index d094bcd6bba62766dbfb6207f26a1d1b9fd8fe24..1e5c1d5e627d18a9c4f8ee57d5bd6bd574ace710 100644 --- a/src/librustpkg/testsuite/pass/src/external-crate/main.rs +++ b/src/librustpkg/testsuite/pass/src/external-crate/main.rs @@ -13,9 +13,12 @@ with RUST_PATH undefined in the environment and with `rustpkg install deeply/nested/path/foo` already executed: - * ./.rust/external_crate exists and is an executable + * ../bin/external_crate exists and is an executable + + tjc: Also want a test like this where foo is an external URL, + which requires the `extern mod` changes */ -extern mod foo; // refers to deeply/nested/path/foo +extern mod foo; fn main() {} diff --git a/src/librustpkg/testsuite/pass/src/foo/lib.rs b/src/librustpkg/testsuite/pass/src/foo/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..91fc466f8c65b075eef43d88604ebc07678fbe78 --- /dev/null +++ b/src/librustpkg/testsuite/pass/src/foo/lib.rs @@ -0,0 +1,11 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn f() {} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 34fd719fc4c6b1bc179c143731ddcf74abc760b5..c5a5aaea178ade81f1236a0fed65ceead4d934ba 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -9,23 +9,28 @@ // except according to those terms. use core::prelude::*; -use core::*; -use core::cmp::Ord; -use core::hash::Streaming; -use core::rt::io::Writer; +use core::{io, libc, os, result, str}; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use extra::getopts::groups::getopts; -use extra::semver; use extra::term; +#[cfg(not(test))] +use extra::getopts; use syntax::ast_util::*; -use syntax::codemap::{dummy_sp, spanned, dummy_spanned}; +use syntax::codemap::{dummy_sp, spanned}; +use syntax::codemap::dummy_spanned; use syntax::ext::base::ExtCtxt; use syntax::{ast, attr, codemap, diagnostic, fold}; use syntax::ast::{meta_name_value, meta_list}; use syntax::attr::{mk_attr}; use rustc::back::link::output_type_exe; +use rustc::driver::driver::compile_upto; use rustc::driver::session::{lib_crate, bin_crate}; +use context::Ctx; +use package_id::PkgId; +use path_util::target_library_in_workspace; +use search::find_library_in_search_path; +pub use target::{OutputType, Main, Lib, Bench, Test}; static Commands: &'static [&'static str] = &["build", "clean", "do", "info", "install", "prefer", "test", "uninstall", @@ -34,119 +39,6 @@ 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(ref v1), &SemVersion(ref v2)) => v1 < v2, - _ => false // incomparable, really - } - } - fn le(&self, other: &Version) -> bool { - match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 <= f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 <= v2, - _ => false // incomparable, really - } - } - fn ge(&self, other: &Version) -> bool { - match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 > f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 > v2, - _ => false // incomparable, really - } - } - fn gt(&self, other: &Version) -> bool { - match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 >= f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 >= v2, - _ => false // incomparable, really - } - } - -} - -impl ToStr for Version { - fn to_str(&self) -> ~str { - match *self { - ExactRevision(ref n) => n.to_str(), - SemVersion(ref v) => v.to_str() - } - } -} - -#[deriving(Eq)] -pub enum OutputType { Main, Lib, Bench, Test } - -/// Placeholder -pub 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 { - /// Remote path: for example, github.com/mozilla/quux-whatever - remote_path: RemotePath, - /// Local path: for example, /home/quux/github.com/mozilla/quux_whatever - /// Note that '-' normalizes to '_' when mapping a remote path - /// onto a local path - /// Also, this will change when we implement #6407, though we'll still - /// need to keep track of separate local and remote paths - local_path: LocalPath, - /// Short name. This is the local path's filestem, but we store it - /// redundantly so as to not call get() everywhere (filestem() returns an - /// option) - short_name: ~str, - version: Version -} - -impl PkgId { - pub fn new(s: &str) -> PkgId { - use conditions::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")); - } - let remote_path = RemotePath(p); - let local_path = normalize(copy remote_path); - let short_name = (copy local_path).filestem().expect(fmt!("Strange path! %s", s)); - PkgId { - local_path: local_path, - remote_path: remote_path, - short_name: short_name, - version: default_version() - } - } - - pub fn hash(&self) -> ~str { - fmt!("%s-%s-%s", self.remote_path.to_str(), - hash(self.remote_path.to_str() + self.version.to_str()), - self.version.to_str()) - } - - pub fn short_name_with_version(&self) -> ~str { - fmt!("%s-%s", self.short_name, 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-%s", self.local_path.to_str(), self.version.to_str()) - } -} - pub struct Pkg { id: PkgId, bins: ~[~str], @@ -264,13 +156,6 @@ pub fn ready_crate(sess: session::Session, @fold.fold_crate(crate) } -pub fn parse_vers(vers: ~str) -> result::Result { - match semver::parse(vers) { - Some(vers) => result::Ok(vers), - None => result::Err(~"could not parse version: invalid") - } -} - pub fn need_dir(s: &Path) { if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) { fail!("can't create dir: %s", s.to_str()); @@ -316,15 +201,8 @@ pub fn error(msg: ~str) { } } -pub fn hash(data: ~str) -> ~str { - let mut hasher = hash::default_state(); - let buffer = str::as_bytes_slice(data); - hasher.write(buffer); - hasher.result_str() -} - // FIXME (#4432): Use workcache to only compile when needed -pub fn compile_input(sysroot: Option<@Path>, +pub fn compile_input(ctxt: &Ctx, pkg_id: &PkgId, in_file: &Path, out_dir: &Path, @@ -333,6 +211,8 @@ pub fn compile_input(sysroot: Option<@Path>, opt: bool, what: OutputType) -> bool { + let workspace = out_dir.pop().pop(); + assert!(in_file.components.len() > 1); let input = driver::file_input(copy *in_file); debug!("compile_input: %s / %?", in_file.to_str(), what); @@ -340,23 +220,10 @@ pub fn compile_input(sysroot: Option<@Path>, // not sure if we should support anything else let binary = @(copy os::args()[0]); - let building_library = what == Lib; - - let out_file = if building_library { - out_dir.push(os::dll_filename(pkg_id.short_name)) - } - else { - out_dir.push(pkg_id.short_name + match what { - Test => ~"test", Bench => ~"bench", Main | Lib => ~"" - } + os::EXE_SUFFIX) - }; - debug!("compiling %s into %s", - in_file.to_str(), - out_file.to_str()); debug!("flags: %s", str::connect(flags, " ")); debug!("cfgs: %s", str::connect(cfgs, " ")); - debug!("compile_input's sysroot = %?", sysroot); + debug!("compile_input's sysroot = %?", ctxt.sysroot_opt); let crate_type = match what { Lib => lib_crate, @@ -372,28 +239,62 @@ pub fn compile_input(sysroot: Option<@Path>, + flags + cfgs.flat_map(|&c| { ~[~"--cfg", c] }), driver::optgroups()).get(); - let mut options = session::options { + let options = @session::options { crate_type: crate_type, optimize: if opt { session::Aggressive } else { session::No }, test: what == Test || what == Bench, - maybe_sysroot: sysroot, - addl_lib_search_paths: ~[copy *out_dir], + maybe_sysroot: ctxt.sysroot_opt, + addl_lib_search_paths: @mut ~[copy *out_dir], // output_type should be conditional output_type: output_type_exe, // Use this to get a library? That's weird .. copy *driver::build_session_options(binary, &matches, diagnostic::emit) }; - for cfgs.each |&cfg| { - options.cfg.push(attr::mk_word_item(@cfg)); - } + let addl_lib_search_paths = @mut options.addl_lib_search_paths; - let sess = driver::build_session(@options, diagnostic::emit); + let sess = driver::build_session(options, diagnostic::emit); + + // Infer dependencies that rustpkg needs to build, by scanning for + // `extern mod` directives. + let cfg = driver::build_configuration(sess, binary, &input); + let (crate_opt, _) = driver::compile_upto(sess, copy cfg, &input, driver::cu_expand, None); + + let mut crate = match crate_opt { + Some(c) => c, + None => fail!("compile_input expected...") + }; + + // Not really right. Should search other workspaces too, and the installed + // database (which doesn't exist yet) + find_and_install_dependencies(ctxt, sess, &workspace, crate, + |p| { + debug!("a dependency: %s", p.to_str()); + // Pass the directory containing a dependency + // as an additional lib search path + addl_lib_search_paths.push(p); + }); + + // Inject the link attributes so we get the right package name and version + if attr::find_linkage_metas(crate.node.attrs).is_empty() { + let short_name_to_use = match what { + Test => fmt!("%stest", pkg_id.short_name), + Bench => fmt!("%sbench", pkg_id.short_name), + _ => copy pkg_id.short_name + }; + debug!("Injecting link name: %s", short_name_to_use); + crate = @codemap::respan(crate.span, ast::crate_ { + attrs: ~[mk_attr(@dummy_spanned( + meta_list(@~"link", + ~[@dummy_spanned(meta_name_value(@~"name", + mk_string_lit(@short_name_to_use))), + @dummy_spanned(meta_name_value(@~"vers", + mk_string_lit(@(copy pkg_id.version.to_str()))))])))], + ..copy crate.node}); + } debug!("calling compile_crate_from_input, out_dir = %s, building_library = %?", out_dir.to_str(), sess.building_library); - let _ = compile_crate_from_input(&input, pkg_id, Some(copy *out_dir), sess, - None, &out_file, binary, - driver::cu_everything); + compile_crate_from_input(&input, out_dir, sess, crate, copy cfg); true } @@ -403,52 +304,31 @@ pub fn compile_input(sysroot: Option<@Path>, // call compile_upto and return the crate // also, too many arguments pub fn compile_crate_from_input(input: &driver::input, - pkg_id: &PkgId, - build_dir_opt: Option, + build_dir: &Path, sess: session::Session, - crate_opt: Option<@ast::crate>, - out_file: &Path, - binary: @~str, - what: driver::compile_upto) -> @ast::crate { - debug!("Calling build_output_filenames with %? and %s", build_dir_opt, out_file.to_str()); - let outputs = driver::build_output_filenames(input, &build_dir_opt, - &Some(copy *out_file), sess); - debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type); - let cfg = driver::build_configuration(sess, binary, input); - match crate_opt { - Some(c) => { - debug!("Calling compile_rest, outputs = %?", outputs); - assert_eq!(what, driver::cu_everything); - driver::compile_rest(sess, cfg, driver::cu_everything, Some(outputs), Some(c)); - c - } - None => { - debug!("Calling compile_upto, outputs = %?", outputs); - let (crate, _) = driver::compile_upto(sess, copy cfg, input, - driver::cu_parse, Some(outputs)); - let mut crate = crate.unwrap(); - - debug!("About to inject link_meta info..."); - // Inject the inferred link_meta info if it's not already there - // (assumes that name and vers are the only linkage metas) - - debug!("How many attrs? %?", attr::find_linkage_metas(crate.node.attrs).len()); - - if attr::find_linkage_metas(crate.node.attrs).is_empty() { - crate = @codemap::respan(crate.span, ast::crate_ { - attrs: ~[mk_attr(@dummy_spanned( - meta_list(@~"link", - ~[@dummy_spanned(meta_name_value(@~"name", - mk_string_lit(@(copy pkg_id.short_name)))), - @dummy_spanned(meta_name_value(@~"vers", - mk_string_lit(@(copy pkg_id.version.to_str()))))])))], - ..copy crate.node}); - } + crate: @ast::crate, + cfg: ast::crate_cfg) { + debug!("Calling build_output_filenames with %s, building library? %?", + build_dir.to_str(), sess.building_library); - driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate)); - crate - } + // bad copy + let outputs = driver::build_output_filenames(input, &Some(copy *build_dir), &None, + crate.node.attrs, sess); + + debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type); + debug!("additional libraries:"); + for sess.opts.addl_lib_search_paths.each |lib| { + debug!("an additional library: %s", lib.to_str()); } + + driver::compile_rest(sess, + cfg, + compile_upto { + from: driver::cu_expand, + to: driver::cu_everything + }, + Some(outputs), + Some(crate)); } #[cfg(windows)] @@ -462,7 +342,7 @@ pub fn exe_suffix() -> ~str { ~"" } // Called by build_crates // FIXME (#4432): Use workcache to only compile when needed -pub fn compile_crate(sysroot: Option<@Path>, pkg_id: &PkgId, +pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId, crate: &Path, dir: &Path, flags: &[~str], cfgs: &[~str], opt: bool, what: OutputType) -> bool { @@ -471,26 +351,51 @@ pub fn compile_crate(sysroot: Option<@Path>, pkg_id: &PkgId, for flags.each |&fl| { debug!("+++ %s", fl); } - compile_input(sysroot, pkg_id, crate, dir, flags, cfgs, opt, what) + compile_input(ctxt, pkg_id, crate, dir, flags, cfgs, opt, what) } -// normalize should be the only way to construct a LocalPath -// (though this isn't enforced) -/// Replace all occurrences of '-' in the stem part of path with '_' -/// This is because we treat rust-foo-bar-quux and rust_foo_bar_quux -/// as the same name -pub fn normalize(p_: RemotePath) -> LocalPath { - let RemotePath(p) = p_; - match p.filestem() { - None => LocalPath(p), - Some(st) => { - let replaced = str::replace(st, "-", "_"); - if replaced != st { - LocalPath(p.with_filestem(replaced)) - } - else { - LocalPath(p) +/// Collect all `extern mod` directives in `c`, then +/// try to install their targets, failing if any target +/// can't be found. +fn find_and_install_dependencies(ctxt: &Ctx, + sess: session::Session, + workspace: &Path, + c: &ast::crate, + save: @fn(Path) + ) { + // :-( + debug!("In find_and_install_dependencies..."); + let my_workspace = copy *workspace; + let my_ctxt = copy *ctxt; + for c.each_view_item() |vi: @ast::view_item| { + debug!("A view item!"); + match vi.node { + // ignore metadata, I guess + ast::view_item_extern_mod(lib_ident, _, _) => { + match my_ctxt.sysroot_opt { + Some(ref x) => debug!("sysroot: %s", x.to_str()), + None => () + }; + let lib_name = sess.str_of(lib_ident); + match find_library_in_search_path(my_ctxt.sysroot_opt, *lib_name) { + Some(installed_path) => { + debug!("It exists: %s", installed_path.to_str()); + } + None => { + // Try to install it + let pkg_id = PkgId::new(*lib_name); + my_ctxt.install(&my_workspace, &pkg_id); + // Also, add an additional search path + let installed_path = target_library_in_workspace(&pkg_id, + &my_workspace).pop(); + debug!("Great, I installed %s, and it's in %s", + *lib_name, installed_path.to_str()); + save(installed_path); + } + } } + // Ignore `use`s + _ => () } } } @@ -526,10 +431,6 @@ pub fn mk_string_lit(s: @~str) -> ast::lit { } } -/// Wrappers to prevent local and remote paths from getting confused -pub struct RemotePath (Path); -pub struct LocalPath (Path); - #[cfg(test)] mod test { use super::is_cmd; diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index cb9f735bce8d4cd8a4aec215b65e6d5969fe34a4..3010e27385fa0d79a10b8436946f7b944288a3db 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -11,7 +11,7 @@ // rustpkg utilities having to do with workspaces use path_util::{rust_path, workspace_contains_package_id}; -use util::PkgId; +use package_id::PkgId; use core::path::Path; pub fn pkg_parent_workspaces(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool {