提交 c120464b 编写于 作者: T Tim Chevalier

rustc/rusti/rustpkg: Infer packages from `extern mod` directives

This commit won't be quite as useful until I implement RUST_PATH and
until we change `extern mod` to take a general string instead of
an identifier (#5682 and #6407).

With that said, now if you're using rustpkg and a program contains:

extern mod foo;

rustpkg will attempt to search for `foo`, so that you don't have to
provide a -L directory explicitly. In addition, rustpkg will
actually try to build and install `foo`, unless it's already
installed (specifically, I tested that `extern mod extra;` would
not cause it to try to find source for `extra` and compile it
again).

This is as per #5681.

Incidentally, I changed some driver code to infer the link name
from the crate link_meta attributes. If that change isn't ok, say
something. Also, I changed the addl_lib_search_paths field in the
session options to be an @mut ~[Path] so that it can be modified
after expansion but before later phases.
上级 341678b8
......@@ -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<ty::ctxt>) {
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<ty::ctxt>) {
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<Path>,
ofile: &Option<Path>,
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) => {
......
......@@ -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,
......
......@@ -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);
......
......@@ -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() {
......
......@@ -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<bool> {
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<bool> {
*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;
......
......@@ -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;
}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<semver::Version, ~str> {
match semver::parse(vers) {
Some(vers) => result::Ok(vers),
None => result::Err(~"could not parse version: invalid")
}
}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<W: Writer>(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()
}
......@@ -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 <workspace> 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 <pkgid>'s source files in <workspace>.
/// 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 <pkgid>'s source files in <workspace>.
/// 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<Path> {
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 <pkgid> in <workspace>'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<Path> {
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<Path>
fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option<Path> {
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 <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
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<Path> {
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<Path> {
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<Pat
for dir_contents.each |&p| {
let mut which = 0;
let mut hash = None;
let p_path = Path(p);
let extension = p_path.filetype();
debug!("p = %s, p's extension is %?", p.to_str(), extension);
match extension {
Some(ref s) if lib_filetype == *s => (),
_ => 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<Pat
}
else if which == 1 {
hash = Some(piece.to_owned());
which += 1;
}
else if which == 2 && piece != lib_filetype {
hash = None;
break;
}
else if which == 2 {
break;
}
else {
......@@ -144,20 +185,19 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Pat
}
}
if hash.is_some() {
result_filename = Some(p);
result_filename = Some(p_path);
break;
}
}
// Return the filename that matches, which we now know exists
// (if result_filename != None)
debug!("result_filename = %?", result_filename);
match result_filename {
None => 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<Pat
/// in <workspace>
/// 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 <pkgid>
/// in <workspace>
/// 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 <pkgid>
/// in <workspace>
/// 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 <pkgid>
/// in <workspace>
/// 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 <pkgid>'s build artifacts in <workspace>.
......@@ -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 <workspace>...
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
}
......@@ -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<Path> {
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);
}
}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Path> {
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
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
}
......@@ -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);
......
......@@ -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() {}
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn f() {}
......@@ -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<semver::Version, ~str> {
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<Path>,
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;
......
......@@ -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 {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册