800 lines
26 KiB
Plaintext
800 lines
26 KiB
Plaintext
// Copyright 2012-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 - a package manager and build system for Rust
|
|
|
|
#[link(name = "rustpkg",
|
|
vers = "0.7-pre",
|
|
uuid = "25de5e6e-279e-4a20-845c-4cabae92daaf",
|
|
url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")];
|
|
|
|
#[license = "MIT/ASL2"];
|
|
#[crate_type = "lib"];
|
|
#[no_core];
|
|
#[allow(vecs_implicitly_copyable,
|
|
non_implicitly_copyable_typarams)];
|
|
|
|
extern mod core(vers = "0.7-pre");
|
|
extern mod std(vers = "0.7-pre");
|
|
extern mod rustc(vers = "0.7-pre");
|
|
extern mod syntax(vers = "0.7-pre");
|
|
|
|
use core::*;
|
|
pub use core::path::Path;
|
|
use core::container::Map;
|
|
use core::hashmap::HashMap;
|
|
use core::io::WriterUtil;
|
|
use rustc::driver::{driver, session};
|
|
use rustc::metadata::filesearch;
|
|
use std::{getopts};
|
|
use syntax::{ast, diagnostic};
|
|
use util::*;
|
|
use path_util::{dest_dir, normalize};
|
|
use rustc::driver::session::{lib_crate, bin_crate, crate_type};
|
|
|
|
mod conditions;
|
|
mod usage;
|
|
mod path_util;
|
|
mod util;
|
|
|
|
/// A PkgScript represents user-supplied custom logic for
|
|
/// special build hooks. This only exists for packages with
|
|
/// an explicit package script.
|
|
struct PkgScript {
|
|
/// Uniquely identifies this package
|
|
id: PkgId,
|
|
// Used to have this field: deps: ~[(~str, Option<~str>)]
|
|
// but I think it shouldn't be stored here
|
|
/// The contents of the package script: either a file path,
|
|
/// or a string containing the text of the input
|
|
input: driver::input,
|
|
/// The session to use *only* for compiling the custom
|
|
/// build script
|
|
sess: session::Session,
|
|
/// The config for compiling the custom build script
|
|
cfg: ast::crate_cfg,
|
|
/// The crate for the custom build script
|
|
crate: @ast::crate,
|
|
/// Directory in which to store build output
|
|
build_dir: Path
|
|
}
|
|
|
|
impl PkgScript {
|
|
/// Given the path name for a package script
|
|
/// and a package ID, parse the package script into
|
|
/// a PkgScript that we can then execute
|
|
fn parse(script: Path, id: PkgId) -> PkgScript {
|
|
// Get the executable name that was invoked
|
|
let binary = os::args()[0];
|
|
// Build the rustc session data structures to pass
|
|
// to the compiler
|
|
let options = @session::options {
|
|
binary: @binary,
|
|
crate_type: session::bin_crate,
|
|
.. *session::basic_options()
|
|
};
|
|
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, cfg, &input,
|
|
driver::cu_parse, None);
|
|
let work_dir = dest_dir(id);
|
|
|
|
debug!("Returning package script with id %?", id);
|
|
|
|
PkgScript {
|
|
id: id,
|
|
input: input,
|
|
sess: sess,
|
|
cfg: cfg,
|
|
crate: crate,
|
|
build_dir: work_dir
|
|
}
|
|
}
|
|
|
|
/// Run the contents of this package script, where <what>
|
|
/// is the command to pass to it (e.g., "build", "clean", "install")
|
|
/// Returns a pair of an exit code and list of configs (obtained by
|
|
/// calling the package script's configs() function if it exists
|
|
// FIXME (#4432): Use workcache to only compile the script when changed
|
|
fn run_custom(&self, what: ~str) -> (~[~str], ExitCode) {
|
|
debug!("run_custom: %s", what);
|
|
let sess = self.sess;
|
|
|
|
debug!("Working directory = %s", self.build_dir.to_str());
|
|
// Collect together any user-defined commands in the package script
|
|
let crate = util::ready_crate(sess, self.crate);
|
|
debug!("Building output filenames with script name %s",
|
|
driver::source_name(&self.input));
|
|
match filesearch::get_rustpkg_sysroot() {
|
|
Ok(r) => {
|
|
let root = r.pop().pop().pop().pop(); // :-\
|
|
debug!("Root is %s, calling compile_rest", root.to_str());
|
|
let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
|
|
util::compile_crate_from_input(self.input, self.id,
|
|
Some(self.build_dir),
|
|
sess, Some(crate),
|
|
exe, os::args()[0],
|
|
driver::cu_everything);
|
|
debug!("Running program: %s %s %s", exe.to_str(), root.to_str(), what);
|
|
let status = run::run_program(exe.to_str(), ~[root.to_str(), what]);
|
|
if status != 0 {
|
|
return (~[], status);
|
|
}
|
|
else {
|
|
debug!("Running program (configs): %s %s %s",
|
|
exe.to_str(), root.to_str(), ~"configs");
|
|
let output = run::program_output(exe.to_str(), ~[root.to_str(), ~"configs"]);
|
|
// Run the configs() function to get the configs
|
|
let mut cfgs = ~[];
|
|
for str::each_word(output.out) |w| {
|
|
cfgs.push(w.to_owned());
|
|
}
|
|
(cfgs, output.status)
|
|
}
|
|
}
|
|
Err(e) => {
|
|
fail!(fmt!("Running package script, couldn't find rustpkg sysroot (%s)",
|
|
e))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn hash(&self) -> ~str {
|
|
self.id.hash()
|
|
}
|
|
|
|
}
|
|
|
|
struct Ctx {
|
|
// I'm not sure what this is for
|
|
json: bool,
|
|
// Cache of hashes of things already installed
|
|
// though I'm not sure why the value is a bool
|
|
dep_cache: @mut HashMap<~str, bool>,
|
|
}
|
|
|
|
impl Ctx {
|
|
|
|
fn run(&self, cmd: ~str, args: ~[~str]) {
|
|
let root = util::root();
|
|
|
|
util::need_dir(&root);
|
|
util::need_dir(&root.push(~"work"));
|
|
util::need_dir(&root.push(~"lib"));
|
|
util::need_dir(&root.push(~"bin"));
|
|
util::need_dir(&root.push(~"tmp"));
|
|
|
|
fn sep_name_vers(in: ~str) -> (Option<~str>, Option<~str>) {
|
|
let mut name = None;
|
|
let mut vers = None;
|
|
|
|
for str::each_split_char(in, '@') |s| {
|
|
if name.is_none() { name = Some(s.to_owned()); }
|
|
else if vers.is_none() { vers = Some(s.to_owned()); }
|
|
else { break; }
|
|
}
|
|
|
|
(name, vers)
|
|
}
|
|
|
|
match cmd {
|
|
~"build" => {
|
|
if args.len() < 1 {
|
|
return usage::build();
|
|
}
|
|
// The package id is presumed to be the first command-line
|
|
// argument
|
|
let pkgid = PkgId::new(args[0]);
|
|
// Should allow the build directory to be configured.
|
|
// Right now it's always the "build" subdirectory in
|
|
// the package directory
|
|
let dst_dir = dest_dir(pkgid);
|
|
debug!("Destination dir = %s", dst_dir.to_str());
|
|
// Right now, we assume the pkgid path is a valid dir
|
|
// relative to the CWD. In the future, we should search
|
|
// paths
|
|
let cwd = os::getcwd().normalize();
|
|
debug!("Current working directory = %s", cwd.to_str());
|
|
|
|
// Create the package source
|
|
let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid);
|
|
debug!("Package src = %?", src);
|
|
|
|
// Is there custom build logic? If so, use it
|
|
let pkg_src_dir = cwd.push_rel(&pkgid.path);
|
|
let mut custom = false;;
|
|
debug!("Package source directory = %s", pkg_src_dir.to_str());
|
|
let cfgs = match src.package_script_option(&pkg_src_dir) {
|
|
Some(package_script_path) => {
|
|
let pscript = PkgScript::parse(package_script_path,
|
|
pkgid);
|
|
// Limited right now -- we're only running the post_build
|
|
// hook and probably fail otherwise
|
|
// also post_build should be called pre_build
|
|
let (cfgs, hook_result) = pscript.run_custom(~"post_build");
|
|
debug!("Command return code = %?", hook_result);
|
|
if hook_result != 0 {
|
|
fail!(fmt!("Error running custom build command"))
|
|
}
|
|
custom = true;
|
|
// otherwise, the package script succeeded
|
|
cfgs
|
|
}
|
|
None => {
|
|
debug!("No package script, continuing");
|
|
~[]
|
|
}
|
|
};
|
|
|
|
// If there was a package script, it should have finished
|
|
// the build already. Otherwise...
|
|
if !custom {
|
|
// Find crates inside the workspace
|
|
src.find_crates();
|
|
// Build it!
|
|
src.build(&dst_dir, cfgs);
|
|
}
|
|
}
|
|
~"clean" => {
|
|
if args.len() < 1 {
|
|
return usage::build();
|
|
}
|
|
// The package id is presumed to be the first command-line
|
|
// argument
|
|
let pkgid = PkgId::new(args[0]);
|
|
|
|
self.clean(pkgid);
|
|
}
|
|
~"do" => {
|
|
if args.len() < 2 {
|
|
return usage::do_cmd();
|
|
}
|
|
|
|
self.do_cmd(args[0], args[1]);
|
|
}
|
|
~"info" => {
|
|
self.info();
|
|
}
|
|
~"install" => {
|
|
self.install(if args.len() >= 1 { Some(args[0]) }
|
|
else { None },
|
|
if args.len() >= 2 { Some(args[1]) }
|
|
else { None }, false);
|
|
}
|
|
~"prefer" => {
|
|
if args.len() < 1 {
|
|
return usage::uninstall();
|
|
}
|
|
|
|
let (name, vers) = sep_name_vers(args[0]);
|
|
|
|
self.prefer(name.get(), vers);
|
|
}
|
|
~"test" => {
|
|
self.test();
|
|
}
|
|
~"uninstall" => {
|
|
if args.len() < 1 {
|
|
return usage::uninstall();
|
|
}
|
|
|
|
let (name, vers) = sep_name_vers(args[0]);
|
|
|
|
self.uninstall(name.get(), vers);
|
|
}
|
|
~"unprefer" => {
|
|
if args.len() < 1 {
|
|
return usage::uninstall();
|
|
}
|
|
|
|
let (name, vers) = sep_name_vers(args[0]);
|
|
|
|
self.unprefer(name.get(), vers);
|
|
}
|
|
_ => fail!(~"reached an unhandled command")
|
|
}
|
|
}
|
|
|
|
fn do_cmd(&self, cmd: ~str, pkgname: ~str) {
|
|
match cmd {
|
|
~"build" | ~"test" => {
|
|
util::error(~"that command cannot be manually called");
|
|
fail!(~"do_cmd");
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let cwd = &os::getcwd();
|
|
let pkgid = PkgId::new(pkgname);
|
|
// Always use the "build" subdirectory of the package dir,
|
|
// but we should allow this to be configured
|
|
let dst_dir = dest_dir(pkgid);
|
|
|
|
let mut src = PkgSrc::new(cwd, &dst_dir, &pkgid);
|
|
match src.package_script_option(cwd) {
|
|
Some(script_path) => {
|
|
let script = PkgScript::parse(script_path, pkgid);
|
|
let (_, status) = script.run_custom(cmd); // Ignore cfgs?
|
|
if status == 42 {
|
|
util::error(~"no fns are listening for that cmd");
|
|
fail!(~"do_cmd");
|
|
}
|
|
}
|
|
None => {
|
|
util::error(fmt!("invoked `do`, but there is no package script in %s",
|
|
cwd.to_str()));
|
|
fail!(~"do_cmd");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn build(&self, _dir: &Path, _verbose: bool, _opt: bool,
|
|
_test: bool) -> Option<PkgScript> {
|
|
// either not needed anymore,
|
|
// or needed only when we don't have a package script. Not sure which one.
|
|
fail!();
|
|
}
|
|
|
|
fn compile(&self, _crate: &Path, _dir: &Path, _flags: ~[~str],
|
|
_cfgs: ~[~str], _opt: bool, _test: bool) {
|
|
// What's the difference between build and compile?
|
|
fail!(~"compile not yet implemented");
|
|
}
|
|
|
|
fn clean(&self, id: PkgId) {
|
|
// Could also support a custom build hook in the pkg
|
|
// script for cleaning files rustpkg doesn't know about.
|
|
// Do something reasonable for now
|
|
|
|
let dir = dest_dir(id);
|
|
util::note(fmt!("Cleaning package %s (removing directory %s)",
|
|
id.to_str(), dir.to_str()));
|
|
if os::path_exists(&dir) {
|
|
util::remove_dir_r(&dir);
|
|
util::note(fmt!("Removed directory %s", dir.to_str()));
|
|
}
|
|
|
|
util::note(fmt!("Cleaned package %s", id.to_str()));
|
|
}
|
|
|
|
fn info(&self) {
|
|
// stub
|
|
fail!(~"info not yet implemented");
|
|
}
|
|
|
|
fn install(&self, _url: Option<~str>,
|
|
_target: Option<~str>, _cache: bool) {
|
|
// stub
|
|
fail!(~"install not yet implemented");
|
|
}
|
|
|
|
fn fetch(&self, _dir: &Path, _url: ~str, _target: Option<~str>) {
|
|
// stub
|
|
fail!(~"fetch not yet implemented");
|
|
}
|
|
|
|
fn fetch_curl(&self, dir: &Path, url: ~str) {
|
|
util::note(fmt!("fetching from %s using curl", url));
|
|
|
|
let tar = dir.dir_path().push(&dir.file_path().to_str() + ~".tar");
|
|
|
|
if run::program_output(~"curl", ~[~"-f", ~"-s",
|
|
~"-o", tar.to_str(),
|
|
url]).status != 0 {
|
|
util::error(~"fetching failed: downloading using curl failed");
|
|
|
|
fail!();
|
|
}
|
|
|
|
if run::program_output(~"tar", ~[~"-x", ~"--strip-components=1",
|
|
~"-C", dir.to_str(), ~"-f",
|
|
tar.to_str()]).status != 0 {
|
|
util::error(~"fetching failed: extracting using tar failed" +
|
|
~"(is it a valid tar archive?)");
|
|
|
|
fail!();
|
|
}
|
|
}
|
|
|
|
fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>) {
|
|
util::note(fmt!("fetching from %s using git", url));
|
|
|
|
// Git can't clone into a non-empty directory
|
|
util::remove_dir_r(dir);
|
|
|
|
if run::program_output(~"git", ~[~"clone", url,
|
|
dir.to_str()]).status != 0 {
|
|
util::error(~"fetching failed: can't clone repository");
|
|
fail!();
|
|
}
|
|
|
|
if !target.is_none() {
|
|
let mut success = true;
|
|
|
|
do util::temp_change_dir(dir) {
|
|
success = run::program_output(~"git",
|
|
~[~"checkout",
|
|
target.get()]).status != 0
|
|
}
|
|
|
|
if !success {
|
|
util::error(~"fetching failed: can't checkout target");
|
|
fail!();
|
|
}
|
|
}
|
|
}
|
|
|
|
fn prefer(&self, id: ~str, vers: Option<~str>) {
|
|
let package = match util::get_pkg(id, vers) {
|
|
result::Ok(package) => package,
|
|
result::Err(err) => {
|
|
util::error(err);
|
|
fail!(); // Condition?
|
|
}
|
|
};
|
|
let name = package.id.path.to_str(); // ???
|
|
|
|
util::note(fmt!("preferring %s v%s", name, package.id.version.to_str()));
|
|
|
|
let bin_dir = util::root().push(~"bin");
|
|
|
|
for package.bins.each |&bin| {
|
|
let path = Path(bin);
|
|
let mut name = None;
|
|
for str::each_split_char(path.file_path().to_str(), '-') |s| {
|
|
name = Some(s.to_owned());
|
|
break;
|
|
}
|
|
let out = bin_dir.push(name.unwrap());
|
|
|
|
util::link_exe(&path, &out);
|
|
util::note(fmt!("linked %s", out.to_str()));
|
|
}
|
|
|
|
util::note(fmt!("preferred %s v%s", name, package.id.version.to_str()));
|
|
}
|
|
|
|
fn test(&self) {
|
|
// stub
|
|
fail!(~"test not yet implemented");
|
|
}
|
|
|
|
fn uninstall(&self, _id: ~str, _vers: Option<~str>) {
|
|
fail!(~"uninstall not yet implemented");
|
|
}
|
|
|
|
fn unprefer(&self, _id: ~str, _vers: Option<~str>) {
|
|
fail!(~"unprefer not yet implemented");
|
|
}
|
|
}
|
|
|
|
pub fn main() {
|
|
io::println("WARNING: The Rust package manager is experimental and may be unstable");
|
|
|
|
let args = os::args();
|
|
let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"),
|
|
getopts::optflag(~"j"), getopts::optflag(~"json"),
|
|
getopts::optmulti(~"c"), getopts::optmulti(~"cfg")];
|
|
let matches = &match getopts::getopts(args, opts) {
|
|
result::Ok(m) => m,
|
|
result::Err(f) => {
|
|
util::error(fmt!("%s", getopts::fail_str(f)));
|
|
|
|
return;
|
|
}
|
|
};
|
|
let help = getopts::opt_present(matches, ~"h") ||
|
|
getopts::opt_present(matches, ~"help");
|
|
let json = getopts::opt_present(matches, ~"j") ||
|
|
getopts::opt_present(matches, ~"json");
|
|
let mut args = copy matches.free;
|
|
|
|
args.shift();
|
|
|
|
if (args.len() < 1) {
|
|
return usage::general();
|
|
}
|
|
|
|
let cmd = args.shift();
|
|
|
|
if !util::is_cmd(cmd) {
|
|
return usage::general();
|
|
} else if help {
|
|
return match cmd {
|
|
~"build" => usage::build(),
|
|
~"clean" => usage::clean(),
|
|
~"do" => usage::do_cmd(),
|
|
~"info" => usage::info(),
|
|
~"install" => usage::install(),
|
|
~"prefer" => usage::prefer(),
|
|
~"test" => usage::test(),
|
|
~"uninstall" => usage::uninstall(),
|
|
~"unprefer" => usage::unprefer(),
|
|
_ => usage::general()
|
|
};
|
|
}
|
|
|
|
Ctx {
|
|
json: json,
|
|
dep_cache: @mut HashMap::new()
|
|
}.run(cmd, args);
|
|
}
|
|
|
|
/// A crate is a unit of Rust code to be compiled into a binary or library
|
|
pub struct Crate {
|
|
file: Path,
|
|
flags: ~[~str],
|
|
cfgs: ~[~str]
|
|
}
|
|
|
|
pub struct Listener {
|
|
cmds: ~[~str],
|
|
cb: ~fn()
|
|
}
|
|
|
|
pub fn run(listeners: ~[Listener]) {
|
|
let rcmd = os::args()[2];
|
|
let mut found = false;
|
|
|
|
for listeners.each |listener| {
|
|
for listener.cmds.each |&cmd| {
|
|
if cmd == rcmd {
|
|
(listener.cb)();
|
|
|
|
found = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
os::set_exit_status(42);
|
|
}
|
|
}
|
|
|
|
pub impl Crate {
|
|
|
|
fn new(p: &Path) -> Crate {
|
|
Crate {
|
|
file: copy *p,
|
|
flags: ~[],
|
|
cfgs: ~[]
|
|
}
|
|
}
|
|
|
|
fn flag(&self, flag: ~str) -> Crate {
|
|
Crate {
|
|
flags: vec::append(copy self.flags, ~[flag]),
|
|
.. copy *self
|
|
}
|
|
}
|
|
|
|
fn flags(&self, flags: ~[~str]) -> Crate {
|
|
Crate {
|
|
flags: vec::append(copy self.flags, flags),
|
|
.. copy *self
|
|
}
|
|
}
|
|
|
|
fn cfg(&self, cfg: ~str) -> Crate {
|
|
Crate {
|
|
cfgs: vec::append(copy self.cfgs, ~[cfg]),
|
|
.. copy *self
|
|
}
|
|
}
|
|
|
|
fn cfgs(&self, cfgs: ~[~str]) -> Crate {
|
|
Crate {
|
|
cfgs: vec::append(copy self.cfgs, cfgs),
|
|
.. copy *self
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the working directory of the package script.
|
|
* Assumes that the package script has been compiled
|
|
* in is the working directory.
|
|
*/
|
|
pub fn work_dir() -> Path {
|
|
os::self_exe_path().get()
|
|
}
|
|
|
|
/**
|
|
* Get the source directory of the package (i.e.
|
|
* where the crates are located). Assumes
|
|
* that the cwd is changed to it before
|
|
* running this executable.
|
|
*/
|
|
pub fn src_dir() -> Path {
|
|
os::getcwd()
|
|
}
|
|
|
|
condition! {
|
|
bad_pkg_id: (super::Path, ~str) -> ::util::PkgId;
|
|
}
|
|
|
|
// An enumeration of the unpacked source of a package workspace.
|
|
// This contains a list of files found in the source workspace.
|
|
pub struct PkgSrc {
|
|
root: Path, // root of where the package source code lives
|
|
dst_dir: Path, // directory where we will put the compiled output
|
|
id: PkgId,
|
|
libs: ~[Crate],
|
|
mains: ~[Crate],
|
|
tests: ~[Crate],
|
|
benchs: ~[Crate],
|
|
}
|
|
|
|
condition! {
|
|
build_err: (~str) -> ();
|
|
}
|
|
|
|
impl PkgSrc {
|
|
|
|
fn new(src_dir: &Path, dst_dir: &Path,
|
|
id: &PkgId) -> PkgSrc {
|
|
PkgSrc {
|
|
root: copy *src_dir,
|
|
dst_dir: copy *dst_dir,
|
|
id: copy *id,
|
|
libs: ~[],
|
|
mains: ~[],
|
|
tests: ~[],
|
|
benchs: ~[]
|
|
}
|
|
}
|
|
|
|
|
|
fn check_dir(&self) -> Path {
|
|
use conditions::bad_path::cond;
|
|
|
|
debug!("Pushing onto root: %s | %s", self.id.path.to_str(),
|
|
self.root.to_str());
|
|
|
|
let dir = self.root.push_rel(&self.id.path).normalize();
|
|
|
|
debug!("Checking dir: %s", dir.to_str());
|
|
|
|
if !os::path_exists(&dir) {
|
|
return cond.raise((dir, ~"missing package dir"));
|
|
}
|
|
|
|
if !os::path_is_dir(&dir) {
|
|
return cond.raise((dir, ~"supplied path for package dir is a \
|
|
non-directory"));
|
|
}
|
|
|
|
dir
|
|
}
|
|
|
|
// If a file named "pkg.rs" in the current directory exists,
|
|
// return the path for it. Otherwise, None
|
|
fn package_script_option(&self, cwd: &Path) -> Option<Path> {
|
|
let maybe_path = cwd.push("pkg.rs");
|
|
if os::path_exists(&maybe_path) {
|
|
Some(maybe_path)
|
|
}
|
|
else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// True if the given path's stem is self's pkg ID's stem
|
|
/// or if the pkg ID's stem is <rust-foo> and the given path's
|
|
/// stem is foo
|
|
/// Requires that dashes in p have already been normalized to
|
|
/// underscores
|
|
fn stem_matches(&self, p: &Path) -> bool {
|
|
let self_id = normalize(~self.id.path).filestem();
|
|
if self_id == p.filestem() {
|
|
return true;
|
|
}
|
|
else {
|
|
for self_id.each |pth| {
|
|
if pth.starts_with("rust_") // because p is already normalized
|
|
&& match p.filestem() {
|
|
Some(s) => str::eq_slice(s, pth.slice(5, pth.len())),
|
|
None => false
|
|
} { return true; }
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
|
|
assert!(p.components.len() > prefix);
|
|
let mut sub = Path("");
|
|
for vec::slice(p.components, prefix,
|
|
p.components.len()).each |c| {
|
|
sub = sub.push(*c);
|
|
}
|
|
debug!("found crate %s", sub.to_str());
|
|
cs.push(Crate::new(&sub));
|
|
}
|
|
|
|
/// Infers crates to build. Called only in the case where there
|
|
/// is no custom build logic
|
|
fn find_crates(&mut self) {
|
|
use PkgSrc::push_crate;
|
|
|
|
let dir = self.check_dir();
|
|
let prefix = dir.components.len();
|
|
debug!("Matching against %?", self.id.path.filestem());
|
|
for os::walk_dir(&dir) |pth| {
|
|
match pth.filename() {
|
|
Some(~"lib.rs") => push_crate(&mut self.libs,
|
|
prefix, pth),
|
|
Some(~"main.rs") => push_crate(&mut self.mains,
|
|
prefix, pth),
|
|
Some(~"test.rs") => push_crate(&mut self.tests,
|
|
prefix, pth),
|
|
Some(~"bench.rs") => push_crate(&mut self.benchs,
|
|
prefix, pth),
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
if self.libs.is_empty() && self.mains.is_empty()
|
|
&& self.tests.is_empty() && self.benchs.is_empty() {
|
|
|
|
util::note(~"Couldn't infer any crates to build.\n\
|
|
Try naming a crate `main.rs`, `lib.rs`, \
|
|
`test.rs`, or `bench.rs`.");
|
|
fail!(~"Failed to infer crates to build");
|
|
}
|
|
|
|
debug!("found %u libs, %u mains, %u tests, %u benchs",
|
|
self.libs.len(),
|
|
self.mains.len(),
|
|
self.tests.len(),
|
|
self.benchs.len())
|
|
}
|
|
|
|
fn build_crates(&self, dst_dir: &Path,
|
|
src_dir: &Path,
|
|
crates: &[Crate],
|
|
cfgs: ~[~str],
|
|
test: bool, crate_type: crate_type) {
|
|
|
|
for crates.each |&crate| {
|
|
let path = &src_dir.push_rel(&crate.file).normalize();
|
|
util::note(fmt!("build_crates: compiling %s", path.to_str()));
|
|
util::note(fmt!("build_crates: destination dir is %s", dst_dir.to_str()));
|
|
|
|
let result = util::compile_crate(None, self.id, path,
|
|
dst_dir,
|
|
crate.flags,
|
|
crate.cfgs + cfgs,
|
|
false, test, crate_type);
|
|
if !result {
|
|
build_err::cond.raise(fmt!("build failure on %s",
|
|
path.to_str()));
|
|
}
|
|
debug!("Result of compiling %s was %?",
|
|
path.to_str(), result);
|
|
}
|
|
}
|
|
|
|
fn build(&self, dst_dir: &Path, cfgs: ~[~str]) {
|
|
let dir = self.check_dir();
|
|
debug!("Building libs");
|
|
self.build_crates(dst_dir, &dir, self.libs, cfgs, false, lib_crate);
|
|
debug!("Building mains");
|
|
self.build_crates(dst_dir, &dir, self.mains, cfgs, false, bin_crate);
|
|
debug!("Building tests");
|
|
self.build_crates(dst_dir, &dir, self.tests, cfgs, true, bin_crate);
|
|
debug!("Building benches");
|
|
self.build_crates(dst_dir, &dir, self.benchs, cfgs, true, bin_crate);
|
|
}
|
|
}
|