rustpkg: Make path searching work as described in the rustpkg doc
rustpkg now searches for package directories in ./src rather than in . . I also added a rudimentary RUST_PATH that's currently hard-wired to the current directory. rustpkg now uses src/, lib/, and build/ directories as described in the manual. Most of the existing test scenarios build now; the README file (in a separate commit) explains which ones.
This commit is contained in:
parent
c2af1de95b
commit
6a3e26aa40
@ -15,39 +15,43 @@ use core::{os, str};
|
||||
use core::option::*;
|
||||
use util::PkgId;
|
||||
|
||||
/// Returns the output directory to use.
|
||||
/// Right now is always the default, should
|
||||
/// support changing it.
|
||||
pub fn dest_dir(pkgid: PkgId) -> Path {
|
||||
default_dest_dir(&pkgid.path)
|
||||
#[deriving(Eq)]
|
||||
pub enum OutputType { Main, Lib, Bench, Test }
|
||||
|
||||
/// Returns the value of RUST_PATH, as a list
|
||||
/// of Paths. In general this should be read from the
|
||||
/// environment; for now, it's hard-wired to just be "."
|
||||
pub fn rust_path() -> ~[Path] {
|
||||
~[Path(".")]
|
||||
}
|
||||
|
||||
/// Returns the default output directory for compilation.
|
||||
/// Creates that directory if it doesn't exist.
|
||||
pub fn default_dest_dir(pkg_dir: &Path) -> Path {
|
||||
/// Creates a directory that is readable, writeable,
|
||||
/// and executable by the user. Returns true iff creation
|
||||
/// succeeded.
|
||||
pub fn make_dir_rwx(p: &Path) -> bool {
|
||||
use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
|
||||
use conditions::bad_path::cond;
|
||||
|
||||
// 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 {
|
||||
rslt
|
||||
}
|
||||
else {
|
||||
cond.raise((rslt, ~"Path names a file that isn't a directory"))
|
||||
}
|
||||
os::make_dir(p, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)
|
||||
}
|
||||
|
||||
/// Creates a directory that is readable, writeable,
|
||||
/// and executable by the user. Returns true iff creation
|
||||
/// succeeded. Also creates all intermediate subdirectories
|
||||
/// if they don't already exist.
|
||||
pub fn mkdir_recursive(p: &Path) -> bool {
|
||||
if os::path_is_dir(p) {
|
||||
return true;
|
||||
}
|
||||
let parent = p.dir_path();
|
||||
debug!("mkdir_recursive: parent = %s",
|
||||
parent.to_str());
|
||||
if parent.to_str() == ~"."
|
||||
|| parent.to_str() == ~"/" { // !!!
|
||||
// No parent directories to create
|
||||
os::path_is_dir(&parent) && make_dir_rwx(p)
|
||||
}
|
||||
else {
|
||||
// Create it
|
||||
if os::make_dir(&rslt, (S_IRUSR | S_IWUSR | S_IXUSR) as i32) {
|
||||
rslt
|
||||
}
|
||||
else {
|
||||
cond.raise((rslt, ~"Could not create directory"))
|
||||
}
|
||||
mkdir_recursive(&parent) && make_dir_rwx(p)
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,34 +73,94 @@ pub fn normalize(p: ~Path) -> ~Path {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::{os, rand};
|
||||
use core::path::Path;
|
||||
use path_util::*;
|
||||
use core::rand::RngUtil;
|
||||
// n.b. So far this only handles local workspaces
|
||||
// n.b. The next three functions ignore the package version right
|
||||
// now. Should fix that.
|
||||
|
||||
// Helper function to create a directory name that doesn't exist
|
||||
pub fn mk_nonexistent(tmpdir: &Path, suffix: &str) -> Path {
|
||||
let r = rand::rng();
|
||||
for 1000.times {
|
||||
let p = tmpdir.push(r.gen_str(16) + suffix);
|
||||
if !os::path_exists(&p) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
fail!(~"Couldn't compute a non-existent path name; this is worrisome")
|
||||
/// 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.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.path.to_str())
|
||||
}
|
||||
|
||||
/// Returns the executable that would be installed for <pkgid>
|
||||
/// in <workspace>
|
||||
pub fn target_executable_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
|
||||
let result = workspace.push("bin");
|
||||
// should use a target-specific subdirectory
|
||||
mk_output_path(Main, pkgid.path.to_str(), result)
|
||||
}
|
||||
|
||||
|
||||
/// Returns the executable that would be installed for <pkgid>
|
||||
/// in <workspace>
|
||||
pub fn target_library_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
|
||||
let result = workspace.push("lib");
|
||||
mk_output_path(Lib, pkgid.path.to_str(), result)
|
||||
}
|
||||
|
||||
/// Returns the test executable that would be installed for <pkgid>
|
||||
/// in <workspace>
|
||||
pub fn target_test_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
|
||||
let result = workspace.push("build");
|
||||
mk_output_path(Test, pkgid.path.to_str(), result)
|
||||
}
|
||||
|
||||
/// Returns the bench executable that would be installed for <pkgid>
|
||||
/// in <workspace>
|
||||
pub fn target_bench_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
|
||||
let result = workspace.push("build");
|
||||
mk_output_path(Bench, pkgid.path.to_str(), result)
|
||||
}
|
||||
|
||||
/// Return the directory for <pkgid>'s build artifacts in <workspace>.
|
||||
/// Creates it if it doesn't exist.
|
||||
pub fn build_pkg_id_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
|
||||
use conditions::bad_path::cond;
|
||||
|
||||
let mut result = workspace.push("build");
|
||||
// n.b. Should actually use a target-specific
|
||||
// subdirectory of build/
|
||||
result = result.push(normalize(~pkgid.path).to_str());
|
||||
if os::path_exists(&result) || mkdir_recursive(&result) {
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_dir_ok() {
|
||||
let the_path = os::tmpdir();
|
||||
let substitute_path = Path("xyzzy");
|
||||
assert!(default_dest_dir(&the_path) == the_path.push(~"build"));
|
||||
let nonexistent_path = mk_nonexistent(&the_path, "quux");
|
||||
let bogus = do ::conditions::bad_path::cond.trap(|_| {
|
||||
substitute_path
|
||||
}).in { default_dest_dir(&nonexistent_path) };
|
||||
assert!(bogus == substitute_path);
|
||||
else {
|
||||
cond.raise((result, fmt!("Could not create directory for package %s", pkgid.to_str())))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, short_name: ~str, dir: Path) -> Path {
|
||||
match what {
|
||||
Lib => dir.push(os::dll_filename(short_name)),
|
||||
_ => dir.push(fmt!("%s%s%s", short_name,
|
||||
if what == Test { ~"test" } else { ~"" },
|
||||
os::EXE_SUFFIX))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::os;
|
||||
|
||||
#[test]
|
||||
fn recursive_mkdir_ok() {
|
||||
let root = os::tmpdir();
|
||||
let path = "xy/z/zy";
|
||||
let nested = root.push(path);
|
||||
assert!(super::mkdir_recursive(&nested));
|
||||
assert!(os::path_is_dir(&root.push("xy")));
|
||||
assert!(os::path_is_dir(&root.push("xy/z")));
|
||||
assert!(os::path_is_dir(&nested));
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ use rustc::metadata::filesearch;
|
||||
use std::{getopts};
|
||||
use syntax::{ast, diagnostic};
|
||||
use util::*;
|
||||
use path_util::{dest_dir, normalize};
|
||||
use path_util::{normalize, workspace_contains_package_id};
|
||||
use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, rust_path};
|
||||
use rustc::driver::session::{lib_crate, bin_crate, crate_type};
|
||||
|
||||
mod conditions;
|
||||
@ -70,7 +71,7 @@ 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 {
|
||||
fn parse(script: Path, workspace: &Path, id: PkgId) -> PkgScript {
|
||||
// Get the executable name that was invoked
|
||||
let binary = os::args()[0];
|
||||
// Build the rustc session data structures to pass
|
||||
@ -85,7 +86,7 @@ impl PkgScript {
|
||||
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);
|
||||
let work_dir = build_pkg_id_in_workspace(id, workspace);
|
||||
|
||||
debug!("Returning package script with id %?", id);
|
||||
|
||||
@ -193,54 +194,60 @@ impl Ctx {
|
||||
// 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());
|
||||
// Using the RUST_PATH, find workspaces that contain
|
||||
// this package ID
|
||||
let workspaces = rust_path().filtered(|ws|
|
||||
workspace_contains_package_id(pkgid, ws));
|
||||
if workspaces.is_empty() {
|
||||
fail!(fmt!("Package %s not found in any of \
|
||||
the following workspaces: %s",
|
||||
pkgid.path.to_str(),
|
||||
rust_path().to_str()));
|
||||
}
|
||||
for workspaces.each |workspace| {
|
||||
let src_dir = pkgid_src_in_workspace(pkgid, workspace);
|
||||
let build_dir = build_pkg_id_in_workspace(pkgid, workspace);
|
||||
debug!("Destination dir = %s", build_dir.to_str());
|
||||
|
||||
// Create the package source
|
||||
let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid);
|
||||
debug!("Package src = %?", src);
|
||||
// Create the package source
|
||||
let mut src = PkgSrc::new(&workspace.push("src"), &build_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"))
|
||||
// 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) {
|
||||
Some(package_script_path) => {
|
||||
let pscript = PkgScript::parse(package_script_path,
|
||||
workspace,
|
||||
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
|
||||
}
|
||||
custom = true;
|
||||
// otherwise, the package script succeeded
|
||||
cfgs
|
||||
}
|
||||
None => {
|
||||
debug!("No package script, continuing");
|
||||
~[]
|
||||
}
|
||||
};
|
||||
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);
|
||||
// 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(&build_dir, cfgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
~"clean" => {
|
||||
@ -250,8 +257,8 @@ impl Ctx {
|
||||
// The package id is presumed to be the first command-line
|
||||
// argument
|
||||
let pkgid = PkgId::new(args[0]);
|
||||
|
||||
self.clean(pkgid);
|
||||
let cwd = os::getcwd();
|
||||
self.clean(&cwd, pkgid); // tjc: should use workspace, not cwd
|
||||
}
|
||||
~"do" => {
|
||||
if args.len() < 2 {
|
||||
@ -304,57 +311,16 @@ impl Ctx {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
// stub
|
||||
fail!("`do` not yet implemented");
|
||||
}
|
||||
|
||||
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) {
|
||||
fn clean(&self, workspace: &Path, 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);
|
||||
let dir = build_pkg_id_in_workspace(id, workspace);
|
||||
util::note(fmt!("Cleaning package %s (removing directory %s)",
|
||||
id.to_str(), dir.to_str()));
|
||||
if os::path_exists(&dir) {
|
||||
|
@ -116,7 +116,14 @@ pub impl PkgId {
|
||||
impl ToStr for PkgId {
|
||||
fn to_str(&self) -> ~str {
|
||||
// should probably use the filestem and not the whole path
|
||||
fmt!("%s-%s", self.path.to_str(), self.version.to_str())
|
||||
fmt!("%s-%s", self.path.to_str(),
|
||||
// Replace dots with -s in the version
|
||||
// this is because otherwise rustc will think
|
||||
// that foo-0.1 has .1 as its extension
|
||||
// (Temporary hack until I figure out how to
|
||||
// get rustc to not name the object file
|
||||
// foo-0.o if I pass in foo-0.1 to build_output_filenames)
|
||||
str::replace(self.version.to_str(), ".", "-"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +445,9 @@ pub fn compile_input(sysroot: Option<Path>,
|
||||
test: bool,
|
||||
crate_type: session::crate_type) -> bool {
|
||||
|
||||
let short_name = pkg_id.to_str();
|
||||
// Want just the directory component here
|
||||
let pkg_filename = pkg_id.path.filename().expect(~"Weird pkg id");
|
||||
let short_name = fmt!("%s-%s", pkg_filename, pkg_id.version.to_str());
|
||||
|
||||
assert!(in_file.components.len() > 1);
|
||||
let input = driver::file_input(copy *in_file);
|
||||
@ -515,7 +524,7 @@ pub fn compile_crate_from_input(input: driver::input,
|
||||
out_file: Path,
|
||||
binary: ~str,
|
||||
what: driver::compile_upto) -> @ast::crate {
|
||||
debug!("Calling build_output_filenames with %?", build_dir_opt);
|
||||
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(out_file), sess);
|
||||
debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type);
|
||||
let cfg = driver::build_configuration(sess, @binary, &input);
|
||||
|
Loading…
x
Reference in New Issue
Block a user