rust/src/librustpkg/rustpkg.rc

954 lines
30 KiB
Plaintext
Raw Normal View History

2013-02-28 07:15:32 -06:00
// 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 purely function package manager and build system
#[link(name = "rustpkg",
vers = "0.6",
uuid = "25de5e6e-279e-4a20-845c-4cabae92daaf",
url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")];
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[no_core];
2013-01-16 05:59:37 -06:00
#[allow(vecs_implicitly_copyable,
non_implicitly_copyable_typarams)];
extern mod core(vers = "0.6");
extern mod std(vers = "0.6");
extern mod rustc(vers = "0.6");
extern mod syntax(vers = "0.6");
2013-01-15 07:57:03 -06:00
use core::*;
pub use core::path::Path;
use core::container::Map;
use core::hashmap::HashMap;
2013-03-26 15:38:07 -05:00
use core::io::WriterUtil;
use rustc::driver::{driver, session};
use rustc::metadata::filesearch;
use std::net::url;
use std::{getopts};
2013-03-26 15:38:07 -05:00
use syntax::{ast, diagnostic};
use util::{ExitCode, Pkg, PkgId};
2013-01-15 07:57:03 -06:00
mod usage;
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
2013-01-16 05:59:37 -06:00
}
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
2013-02-19 02:01:03 -06:00
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);
2013-02-19 02:01:03 -06:00
let (crate, _) = driver::compile_upto(sess, cfg, input,
driver::cu_parse, None);
let work_dir = dest_dir(id);
2013-01-16 05:59:37 -06:00
debug!("Returning package script with id %?", id);
PkgScript {
2013-01-16 05:59:37 -06:00
id: id,
input: input,
sess: sess,
cfg: cfg,
crate: crate,
build_dir: work_dir
}
2013-01-16 05:59:37 -06:00
}
/// 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
2013-01-23 03:25:03 -06:00
// 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());
util::compile_crate_from_input(self.input, Some(self.build_dir),
sess, Some(crate), os::args()[0]);
let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
debug!("Running program: %s %s %s", exe.to_str(), root.to_str(), what);
let status = run::run_program(exe.to_str(), ~[root.to_str(), what]);
if status != 0 {
return (~[], status);
}
else {
debug!("Running program (configs): %s %s %s",
exe.to_str(), root.to_str(), ~"configs");
let output = run::program_output(exe.to_str(), ~[root.to_str(), ~"configs"]);
// Run the configs() function to get the configs
let mut cfgs = ~[];
for str::each_word(output.out) |w| {
cfgs.push(w.to_owned());
}
(cfgs, output.status)
}
}
Err(e) => {
fail!(fmt!("Running package script, couldn't find rustpkg sysroot (%s)",
e))
}
}
}
2013-02-04 19:12:31 -06:00
fn hash(&self) -> ~str {
self.id.hash()
2013-01-16 05:59:37 -06:00
}
}
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>,
}
/// Returns the output directory to use.
/// Right now is always the default, should
/// support changing it.
fn dest_dir(pkgid: PkgId) -> Path {
default_dest_dir(&pkgid.path).expect(
~"couldn't make default dir?!")
}
/// Returns the default output directory for compilation.
/// Creates that directory if it doesn't exist.
fn default_dest_dir(pkg_dir: &Path) -> Option<Path> {
use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
// For now: assumes that pkg_dir exists and is relative
// to the CWD. Change this later when we do path searching.
let rslt = pkg_dir.push("build");
let is_dir = os::path_is_dir(&rslt);
if os::path_exists(&rslt) {
if is_dir {
Some(rslt)
}
else {
util::error(fmt!("%s is not a directory", rslt.to_str()));
None
}
}
else {
// Create it
if os::make_dir(&rslt, (S_IRUSR | S_IWUSR | S_IXUSR) as i32) {
Some(rslt)
}
else {
util::error(fmt!("Could not create directory %s",
rslt.to_str()));
None // ??? should probably use conditions
}
}
2013-01-16 05:59:37 -06:00
}
impl Ctx {
2013-02-04 19:12:31 -06:00
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;
2013-03-25 22:39:10 -05:00
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" => {
2013-03-07 16:54:23 -06:00
if args.len() < 1 {
return usage::build();
}
// The package id is presumed to be the first command-line
// argument
2013-03-07 16:54:23 -06:00
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 = %?", cwd);
// Find crates inside the workspace
let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid);
debug!("Package src = %?", src);
2013-03-07 16:54:23 -06:00
src.find_crates();
// Is there custom build logic? If so, use it
let pkg_src_dir = cwd.push_rel(&pkgid.path);
debug!("Package source directory = %s", pkg_src_dir.to_str());
let cfgs = match src.package_script_option(&pkg_src_dir) {
Some(package_script_path) => {
let pscript = PkgScript::parse(package_script_path,
pkgid);
// Limited right now -- we're only running the post_build
// hook and probably fail otherwise
// also post_build should be called pre_build
let (cfgs, hook_result) = pscript.run_custom(~"post_build");
debug!("Command return code = %?", hook_result);
if hook_result != 0 {
fail!(fmt!("Error running custom build command"))
}
// otherwise, the package script succeeded
cfgs
}
None => {
debug!("No package script, continuing");
~[]
}
};
src.build(&dst_dir, cfgs);
}
~"clean" => {
self.clean();
}
~"do" => {
if args.len() < 2 {
return usage::do_cmd();
}
if !self.do_cmd(args[0], args[1]) {
fail!(~"a command failed!");
}
}
~"info" => {
self.info();
}
~"install" => {
self.install(if args.len() >= 1 { Some(args[0]) }
2013-01-23 00:38:05 -06:00
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);
}
2013-02-04 19:12:31 -06:00
_ => fail!(~"reached an unhandled command")
}
2013-01-16 05:59:37 -06:00
}
fn do_cmd(&self, cmd: ~str, pkgname: ~str) -> bool {
match cmd {
~"build" | ~"test" => {
util::error(~"that command cannot be manually called");
return false;
}
_ => {}
}
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");
return false;
}
status == 0
}
None => {
util::error(fmt!("invoked `do`, but there is no package script in %s", cwd.to_str()));
false
}
}
}
2013-01-16 05:59:37 -06:00
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!();
}
2013-01-16 05:59:37 -06:00
fn compile(&self, _crate: &Path, _dir: &Path, _flags: ~[~str],
_cfgs: ~[~str], _opt: bool, _test: bool) -> bool {
// What's the difference between build and compile?
fail!(~"compile not yet implemented");
2013-01-16 05:59:37 -06:00
}
2013-02-04 19:12:31 -06:00
fn clean(&self) -> bool {
// stub
fail!();
2013-01-16 05:59:37 -06:00
}
2013-02-04 19:12:31 -06:00
fn info(&self) {
// stub
fail!();
}
2013-02-04 19:12:31 -06:00
fn install(&self, url: Option<~str>,
target: Option<~str>, cache: bool) -> bool {
let dir = match url {
None => {
util::note(~"installing from the cwd");
os::getcwd()
}
Some(url) => {
let hash = util::hash(if !target.is_none() {
url + target.get()
}
else { url });
if self.dep_cache.contains_key(&hash) {
util::warn(~"already installed dep this run");
return true;
}
self.dep_cache.insert(hash, true);
let dir = util::root().push(~"tmp").push(hash);
if cache && os::path_exists(&dir) {
return true;
}
2013-01-16 05:59:37 -06:00
if !self.fetch(&dir, url, target) {
return false;
}
dir
}
};
let script = match self.build(&dir, false, true, false) {
Some(script) => script,
None => {
return false;
}
};
let work_dir = script.build_dir;
let from_bin_dir = work_dir.push(~"bin");
let from_lib_dir = work_dir.push(~"lib");
let to_bin_dir = util::root().push(~"bin");
let to_lib_dir = util::root().push(~"lib");
let mut bins = ~[];
let mut libs = ~[];
for os::walk_dir(&from_bin_dir) |bin| {
let to = to_bin_dir.push_rel(&bin.file_path());
os::copy_file(bin, &to);
bins.push(to.to_str());
}
for os::walk_dir(&from_lib_dir) |lib| {
let to = to_lib_dir.push_rel(&lib.file_path());
os::copy_file(lib, &to);
libs.push(to.to_str());
}
let package = Pkg {
id: script.id,
bins: bins,
libs: libs
};
util::note(fmt!("installed %s", script.id.to_str()));
util::add_pkg(&package);
true
2013-01-16 05:59:37 -06:00
}
2013-02-04 19:12:31 -06:00
fn fetch(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
2013-01-23 00:38:05 -06:00
let url = if str::find_str(url, "://").is_none() {
~"http://" + url }
else { url };
let url = match url::from_str(url) {
result::Ok(url) => url,
result::Err(err) => {
util::error(fmt!("failed parsing %s", err.to_lower()));
return false;
}
};
let str = url.to_str();
match Path(url.path).filetype() {
Some(ext) => {
if ext == ~".git" {
return self.fetch_git(dir, str, target);
}
}
None => {}
}
match url.scheme {
~"git" => self.fetch_git(dir, str, target),
~"http" | ~"ftp" | ~"file" => self.fetch_curl(dir, str),
_ => {
util::warn(~"unknown url scheme to fetch, using curl");
self.fetch_curl(dir, str)
}
}
}
2013-02-04 19:12:31 -06:00
fn fetch_curl(&self, dir: &Path, url: ~str) -> bool {
util::note(fmt!("fetching from %s using curl", url));
let tar = dir.dir_path().push(&dir.file_path().to_str() + ~".tar");
2013-01-23 00:38:05 -06:00
if run::program_output(~"curl", ~[~"-f", ~"-s",
~"-o", tar.to_str(),
url]).status != 0 {
util::error(~"fetching failed: downloading using curl failed");
return false;
}
2013-01-23 00:38:05 -06:00
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?)");
return false;
}
true
}
2013-02-04 19:12:31 -06:00
fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
util::note(fmt!("fetching from %s using git", url));
// Git can't clone into a non-empty directory
util::remove_dir_r(dir);
2013-01-23 00:38:05 -06:00
if run::program_output(~"git", ~[~"clone", url,
dir.to_str()]).status != 0 {
util::error(~"fetching failed: can't clone repository");
return false;
}
if !target.is_none() {
let mut success = true;
do util::temp_change_dir(dir) {
2013-01-23 00:38:05 -06:00
success = run::program_output(~"git",
~[~"checkout",
target.get()]).status != 0
}
if !success {
util::error(~"fetching failed: can't checkout target");
return false;
}
}
2013-01-16 05:59:37 -06:00
true
2013-01-16 05:59:37 -06:00
}
2013-02-04 19:12:31 -06:00
fn prefer(&self, id: ~str, vers: Option<~str>) -> bool {
let package = match util::get_pkg(id, vers) {
result::Ok(package) => package,
result::Err(err) => {
util::error(err);
return false;
}
};
let name = 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);
2013-03-25 22:39:10 -05:00
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()));
true
}
2013-01-16 05:59:37 -06:00
2013-02-04 19:12:31 -06:00
fn test(&self) -> bool {
let script = match self.build(&os::getcwd(), false, false, true) {
Some(script) => script,
None => {
return false;
}
};
// To do
util::note(fmt!("Would test %s, but this is a dry run",
script.id.to_str()));
false
2013-01-16 05:59:37 -06:00
}
fn uninstall(&self, _id: ~str, _vers: Option<~str>) -> bool {
fail!(~"uninstall not yet implemented");
}
2013-01-16 05:59:37 -06:00
fn unprefer(&self, _id: ~str, _vers: Option<~str>) -> bool {
fail!(~"unprefer not yet implemented");
2013-01-16 05:59:37 -06:00
}
}
2013-01-15 07:57:03 -06:00
pub fn main() {
io::println("WARNING: The Rust package manager is experimental and may be unstable");
2013-01-15 07:57:03 -06:00
let args = os::args();
2013-01-16 05:59:37 -06:00
let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"),
getopts::optflag(~"j"), getopts::optflag(~"json"),
getopts::optmulti(~"c"), getopts::optmulti(~"cfg")];
2013-01-15 07:57:03 -06:00
let matches = &match getopts::getopts(args, opts) {
result::Ok(m) => m,
result::Err(f) => {
util::error(fmt!("%s", getopts::fail_str(f)));
return;
2013-01-15 07:57:03 -06:00
}
};
2013-01-16 05:59:37 -06:00
let help = getopts::opt_present(matches, ~"h") ||
getopts::opt_present(matches, ~"help");
let json = getopts::opt_present(matches, ~"j") ||
getopts::opt_present(matches, ~"json");
2013-01-15 07:57:03 -06:00
let mut args = copy matches.free;
args.shift();
2013-01-15 07:57:03 -06:00
if (args.len() < 1) {
return usage::general();
}
2013-01-16 05:59:37 -06:00
let cmd = args.shift();
2013-01-15 07:57:03 -06:00
2013-01-16 05:59:37 -06:00
if !util::is_cmd(cmd) {
2013-01-15 07:57:03 -06:00
return usage::general();
} else if help {
2013-01-16 05:59:37 -06:00
return match cmd {
2013-01-15 07:57:03 -06:00
~"build" => usage::build(),
~"clean" => usage::clean(),
~"do" => usage::do_cmd(),
~"info" => usage::info(),
2013-01-15 07:57:03 -06:00
~"install" => usage::install(),
~"prefer" => usage::prefer(),
~"test" => usage::test(),
~"uninstall" => usage::uninstall(),
~"unprefer" => usage::unprefer(),
_ => usage::general()
2013-01-16 05:59:37 -06:00
};
2013-01-15 07:57:03 -06:00
}
2013-01-16 05:59:37 -06:00
Ctx {
json: json,
dep_cache: @mut HashMap::new()
}.run(cmd, args);
}
2013-01-15 07:57:03 -06:00
2013-01-23 00:38:05 -06:00
/// A crate is a unit of Rust code to be compiled into a binary or library
pub struct Crate {
2013-03-07 16:54:23 -06:00
file: Path,
2013-01-23 00:38:05 -06:00
flags: ~[~str],
cfgs: ~[~str]
}
pub struct Listener {
cmds: ~[~str],
cb: ~fn()
2013-01-23 00:38:05 -06:00
}
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);
2013-01-23 00:38:05 -06:00
}
}
pub impl Crate {
2013-03-07 16:54:23 -06:00
fn new(p: &Path) -> Crate {
2013-03-07 16:54:23 -06:00
Crate {
file: copy *p,
flags: ~[],
cfgs: ~[]
}
}
fn flag(&self, flag: ~str) -> Crate {
2013-01-23 00:38:05 -06:00
Crate {
flags: vec::append(copy self.flags, ~[flag]),
2013-02-04 19:12:31 -06:00
.. copy *self
2013-01-23 00:38:05 -06:00
}
}
2013-03-07 16:54:23 -06:00
fn flags(&self, flags: ~[~str]) -> Crate {
2013-01-23 00:38:05 -06:00
Crate {
flags: vec::append(copy self.flags, flags),
2013-02-04 19:12:31 -06:00
.. copy *self
2013-01-23 00:38:05 -06:00
}
}
2013-03-07 16:54:23 -06:00
fn cfg(&self, cfg: ~str) -> Crate {
2013-01-23 00:38:05 -06:00
Crate {
cfgs: vec::append(copy self.cfgs, ~[cfg]),
2013-02-04 19:12:31 -06:00
.. copy *self
2013-01-23 00:38:05 -06:00
}
}
2013-03-07 16:54:23 -06:00
fn cfgs(&self, cfgs: ~[~str]) -> Crate {
2013-01-23 00:38:05 -06:00
Crate {
cfgs: vec::append(copy self.cfgs, cfgs),
2013-02-04 19:12:31 -06:00
.. copy *self
2013-01-23 00:38:05 -06:00
}
}
}
/**
* 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 {
2013-01-23 00:38:05 -06:00
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 {
2013-01-23 00:38:05 -06:00
os::getcwd()
}
2013-03-07 16:54:23 -06:00
condition! {
bad_pkg_id: (super::Path, ~str) -> ::util::PkgId;
2013-03-07 16:54:23 -06:00
}
// 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
2013-03-07 16:54:23 -06:00
id: PkgId,
libs: ~[Crate],
mains: ~[Crate],
tests: ~[Crate],
benchs: ~[Crate],
}
condition! {
bad_path: (super::Path, ~str) -> super::Path;
2013-03-07 16:54:23 -06:00
}
condition! {
build_err: (~str) -> ();
}
impl PkgSrc {
fn new(src_dir: &Path, dst_dir: &Path,
id: &PkgId) -> PkgSrc {
2013-03-07 16:54:23 -06:00
PkgSrc {
root: copy *src_dir,
dst_dir: copy *dst_dir,
2013-03-07 16:54:23 -06:00
id: copy *id,
libs: ~[],
mains: ~[],
tests: ~[],
benchs: ~[]
}
}
fn check_dir(&self) -> Path {
use bad_path::cond;
debug!("Pushing onto root: %s | %s", self.id.path.to_str(),
self.root.to_str());
2013-03-07 16:54:23 -06:00
let dir = self.root.push_rel(&self.id.path).normalize();
debug!("Checking dir: %s", dir.to_str());
if !os::path_exists(&dir) {
2013-03-07 16:54:23 -06:00
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"));
2013-03-07 16:54:23 -06:00
}
dir
}
fn has_pkg_file(&self) -> bool {
let dir = self.check_dir();
dir.push("pkg.rs").exists()
}
// If a file named "pkg.rs" in the current directory exists,
// return the path for it. Otherwise, None
fn package_script_option(&self, cwd: &Path) -> Option<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
fn stem_matches(&self, p: &Path) -> bool {
let self_id = self.id.path.filestem();
if self_id == p.filestem() {
return true;
}
else {
for self_id.each |pth| {
if pth.starts_with("rust-")
&& match p.filestem() {
Some(s) => str::eq_slice(s, pth.slice(5, pth.len())),
None => false
} { return true; }
}
}
false
}
2013-03-07 16:54:23 -06:00
fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
assert!(p.components.len() > prefix);
2013-03-07 16:54:23 -06:00
let mut sub = Path("");
for vec::slice(p.components, prefix,
p.components.len()).each |c| {
sub = sub.push(*c);
}
debug!("found crate %s", sub.to_str());
cs.push(Crate::new(&sub));
}
fn find_crates(&mut self) {
use PkgSrc::push_crate;
2013-03-07 16:54:23 -06:00
let dir = self.check_dir();
let prefix = dir.components.len();
// This is ugly, but can go away once we get rid
// of .rc files
let mut saw_rs = false;
let mut saw_rc = false;
debug!("Matching against %?",
self.id.path.filestem());
2013-03-07 16:54:23 -06:00
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 the file stem is the same as the
// package ID, with an .rs or .rc extension,
// consider it to be a crate
let ext = pth.filetype();
let matches = |p: &Path| {
self.stem_matches(p) && (ext == Some(~".rc")
|| ext == Some(~".rs"))
};
debug!("Checking %? which %s and ext = %? %? %?", pth.filestem(),
if matches(pth) { "matches" } else { "does not match" },
ext, saw_rs, saw_rc);
if matches(pth) &&
// Avoid pushing foo.rc *and* foo.rs
!((ext == Some(~".rc") && saw_rs) ||
(ext == Some(~".rs") && saw_rc)) {
push_crate(&mut self.libs, // ????
prefix, pth);
if ext == Some(~".rc") {
saw_rc = true;
}
else if ext == Some(~".rs") {
saw_rs = true;
}
}
}
2013-03-07 16:54:23 -06:00
}
}
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(dst_dir: &Path,
2013-03-07 16:54:23 -06:00
src_dir: &Path,
crates: &[Crate],
cfgs: ~[~str],
2013-03-07 16:54:23 -06:00
test: bool) {
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, path,
2013-03-07 16:54:23 -06:00
dst_dir,
crate.flags,
crate.cfgs + cfgs,
false, test);
if !result {
2013-03-07 16:54:23 -06:00
build_err::cond.raise(fmt!("build failure on %s",
path.to_str()));
}
debug!("Result of compiling %s was %?",
path.to_str(), result);
2013-03-07 16:54:23 -06:00
}
}
fn build(&self, dst_dir: &Path, cfgs: ~[~str]) {
2013-03-07 16:54:23 -06:00
let dir = self.check_dir();
debug!("Building libs");
PkgSrc::build_crates(dst_dir, &dir, self.libs, cfgs, false);
debug!("Building mains");
PkgSrc::build_crates(dst_dir, &dir, self.mains, cfgs, false);
debug!("Building tests");
PkgSrc::build_crates(dst_dir, &dir, self.tests, cfgs, true);
debug!("Building benches");
PkgSrc::build_crates(dst_dir, &dir, self.benchs, cfgs, true);
2013-03-07 16:54:23 -06:00
}
}