auto merge of #6418 : catamorphism/rust/rustpkg, r=catamorphism

r? @brson This patch implements package IDs like
github.com/catamorphism/test-pkg.

To support such package IDs, I changed the PkgId struct to contain
a LocalPath and a RemotePath field, where the RemotePath reflects
the actual URL and the LocalPath reflects the file name of the cached
copy. Right now, the only difference is that the local path doesn't
contain dashes, but this will change when we implement #6407.

Also, PkgIds now have a short_name field -- though the short name
can be derived from the LocalPath, I thought it was cleaner not to
call option::get() wantonly.
This commit is contained in:
bors 2013-05-15 13:28:49 -07:00
commit 6a9c3bd86e
7 changed files with 296 additions and 431 deletions

View file

@ -28,3 +28,7 @@ condition! {
condition! {
missing_pkg_files: (super::PkgId) -> ();
}
condition! {
bad_pkg_id: (super::Path, ~str) -> ::util::PkgId;
}

View file

@ -10,12 +10,10 @@
// rustpkg utilities having to do with paths and directories
use util::PkgId;
pub use util::{PkgId, RemotePath, LocalPath};
use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
use core::os::mkdir_recursive;
#[deriving(Eq)]
pub enum OutputType { Main, Lib, Bench, Test }
pub use util::{normalize, OutputType, Main, Lib, Bench, Test};
/// Returns the value of RUST_PATH, as a list
/// of Paths. In general this should be read from the
@ -31,24 +29,6 @@ pub static u_rwx: i32 = (S_IRUSR | S_IWUSR | S_IXUSR) as i32;
/// succeeded.
pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, u_rwx) }
/// 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: ~Path) -> ~Path {
match p.filestem() {
None => p,
Some(st) => {
let replaced = str::replace(st, "-", "_");
if replaced != st {
~p.with_filestem(replaced)
}
else {
p
}
}
}
}
// n.b. So far this only handles local workspaces
// n.b. The next three functions ignore the package version right
// now. Should fix that.
@ -56,7 +36,7 @@ pub fn normalize(p: ~Path) -> ~Path {
/// 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());
let pkgpath = workspace.push("src").push(pkgid.local_path.to_str());
os::path_is_dir(&pkgpath)
}
@ -64,23 +44,50 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
/// 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())
result.push(pkgid.local_path.to_str())
}
/// Figure out what the executable name for <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
let mut result = workspace.push("build");
result = result.push_rel(&pkgid.path);
// should use a target-specific subdirectory
result = mk_output_path(Main, fmt!("%s-%s", pkgid.path.to_str(), pkgid.version.to_str()),
result);
result = mk_output_path(Main, pkgid, &result);
debug!("built_executable_in_workspace: checking whether %s exists",
result.to_str());
if os::path_exists(&result) {
Some(result)
}
else {
// This is not an error, but it's worth logging it
error!(fmt!("built_executable_in_workspace: %s does not exist", result.to_str()));
None
}
}
/// Figure out what the test name for <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
output_in_workspace(pkgid, workspace, Test)
}
/// Figure out what the test name for <pkgid> in <workspace>'s build
/// directory is, and if the file exists, return it.
pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
output_in_workspace(pkgid, workspace, Bench)
}
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);
debug!("output_in_workspace: checking whether %s exists",
result.to_str());
if os::path_exists(&result) {
Some(result)
}
else {
error!(fmt!("output_in_workspace: %s does not exist", result.to_str()));
None
}
}
@ -88,10 +95,7 @@ pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<
/// 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 mut result = workspace.push("build");
result = result.push_rel(&pkgid.path);
// should use a target-specific subdirectory
result = mk_output_path(Lib, pkgid.path.to_str(), result);
let result = mk_output_path(Lib, pkgid, &workspace.push("build"));
debug!("built_library_in_workspace: checking whether %s exists",
result.to_str());
@ -100,8 +104,7 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Pat
let dir_contents = os::list_dir(&result.pop());
debug!("dir has %? entries", dir_contents.len());
// n.b. This code assumes the pkgid's path only has one element
let lib_prefix = fmt!("%s%s", os::consts::DLL_PREFIX, pkgid.path.to_str());
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);
debug!("lib_prefix = %s and lib_filetype = %s", lib_prefix, lib_filetype);
@ -173,12 +176,14 @@ pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
/// 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)
}
/// 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)
}
@ -187,18 +192,14 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path,
what: OutputType) -> Path {
use conditions::bad_path::cond;
let (subdir, create_dir) = match what {
Main => ("bin", true), Lib => ("lib", true), Test | Bench => ("build", false)
let subdir = match what {
Lib => "lib", Main | Test | Bench => "bin"
};
let result = workspace.push(subdir);
if create_dir {
if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) {
cond.raise((copy result,
fmt!("I couldn't create the %s dir", subdir)));
}
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.path.to_str(), result)
mk_output_path(what, pkgid, &result)
}
/// Return the directory for <pkgid>'s build artifacts in <workspace>.
@ -209,7 +210,7 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
let mut result = workspace.push("build");
// n.b. Should actually use a target-specific
// subdirectory of build/
result = result.push(normalize(~copy pkgid.path).to_str());
result = result.push_rel(&*pkgid.local_path);
if os::path_exists(&result) || os::mkdir_recursive(&result, u_rwx) {
result
}
@ -220,15 +221,26 @@ 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, short_name: ~str, dir: Path) -> Path {
match what {
Lib => dir.push(os::dll_filename(short_name)),
_ => dir.push(fmt!("%s%s%s", short_name,
pub fn mk_output_path(what: OutputType, 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",
if what == Lib { copy short_name_with_version } else { copy pkg_id.short_name },
dir.to_str());
let 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
_ => dir.push(fmt!("%s%s%s", copy pkg_id.short_name,
match what {
Test => "test",
Bench => "bench",
_ => ""
}
os::EXE_SUFFIX))
}
};
debug!("mk_output_path: returning %s", output_path.to_str());
output_path
}

View file

@ -30,12 +30,10 @@ use rustc::metadata::filesearch;
use std::{getopts};
use syntax::{ast, diagnostic};
use util::*;
use path_util::normalize;
use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace};
use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, 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 rustc::driver::session::{lib_crate, bin_crate, crate_type};
use context::Ctx;
mod conditions;
@ -159,27 +157,6 @@ impl<'self> PkgScript<'self> {
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 {
@ -229,9 +206,7 @@ impl Ctx {
return usage::uninstall();
}
let (name, vers) = sep_name_vers(copy args[0]);
self.prefer(name.get(), vers);
self.prefer(args[0], None);
}
~"test" => {
self.test();
@ -241,20 +216,16 @@ impl Ctx {
return usage::uninstall();
}
let (name, vers) = sep_name_vers(copy args[0]);
self.uninstall(name.get(), vers);
self.uninstall(args[0], None);
}
~"unprefer" => {
if args.len() < 1 {
return usage::uninstall();
}
let (name, vers) = sep_name_vers(copy args[0]);
self.unprefer(name.get(), vers);
self.unprefer(args[0], None);
}
_ => fail!("reached an unhandled command")
_ => fail!(fmt!("I don't know the command `%s`", cmd))
}
}
@ -269,7 +240,7 @@ impl Ctx {
debug!("Destination dir = %s", build_dir.to_str());
// Create the package source
let mut src = PkgSrc::new(&workspace.push("src"), &build_dir, pkgid);
let mut src = PkgSrc::new(workspace, &build_dir, pkgid);
debug!("Package src = %?", src);
// Is there custom build logic? If so, use it
@ -307,7 +278,6 @@ impl Ctx {
// Build it!
src.build(&build_dir, cfgs, self.sysroot_opt);
}
}
fn clean(&self, workspace: &Path, id: &PkgId) {
@ -319,7 +289,7 @@ impl Ctx {
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);
os::remove_dir_recursive(&dir);
util::note(fmt!("Removed directory %s", dir.to_str()));
}
@ -337,6 +307,8 @@ impl Ctx {
// Should use RUST_PATH in the future.
// Also should use workcache to not build if not necessary.
self.build(workspace, id);
debug!("install: workspace = %s, id = %s", workspace.to_str(),
id.to_str());
// Now copy stuff into the install dirs
let maybe_executable = built_executable_in_workspace(id, workspace);
@ -344,104 +316,29 @@ impl Ctx {
let target_exec = target_executable_in_workspace(id, workspace);
let target_lib = target_library_in_workspace(id, workspace);
debug!("target_exec = %s target_lib = %s \
maybe_executable = %? maybe_library = %?",
target_exec.to_str(), target_lib.to_str(),
maybe_executable, maybe_library);
for maybe_executable.each |exec| {
debug!("Copying: %s -> %s", exec.to_str(), target_exec.to_str());
if !os::copy_file(exec, &target_exec) {
if !(os::mkdir_recursive(&target_exec.dir_path(), u_rwx) &&
os::copy_file(exec, &target_exec)) {
cond.raise((copy *exec, copy target_exec));
}
}
for maybe_library.each |lib| {
debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str());
if !os::copy_file(lib, &target_lib) {
if !(os::mkdir_recursive(&target_lib.dir_path(), u_rwx) &&
os::copy_file(lib, &target_lib)) {
cond.raise((copy *lib, copy target_lib));
}
}
}
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, mut 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.swap_unwrap()]).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 prefer(&self, _id: &str, _vers: Option<~str>) {
fail!(~"prefer not yet implemented");
}
fn test(&self) {
@ -449,15 +346,16 @@ impl Ctx {
fail!("test not yet implemented");
}
fn uninstall(&self, _id: ~str, _vers: Option<~str>) {
fn uninstall(&self, _id: &str, _vers: Option<~str>) {
fail!("uninstall not yet implemented");
}
fn unprefer(&self, _id: ~str, _vers: Option<~str>) {
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");
@ -518,32 +416,6 @@ pub struct Crate {
cfgs: ~[~str]
}
pub struct Listener {
cmds: ~[~str],
cb: ~fn()
}
pub fn run(listeners: ~[Listener]) {
let rcmd = copy 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 {
@ -602,10 +474,6 @@ 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 {
@ -641,17 +509,19 @@ impl PkgSrc {
fn check_dir(&self) -> Path {
use conditions::nonexistent_package::cond;
debug!("Pushing onto root: %s | %s", self.id.path.to_str(),
debug!("Pushing onto root: %s | %s", self.id.to_str(),
self.root.to_str());
let dir = self.root.push_rel(&self.id.path).normalize();
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());
// tjc: Rather than erroring out, need to try downloading the
// contents of the path to a local directory (#5679)
if !os::path_exists(&dir) {
cond.raise((copy self.id, ~"missing package 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"));
}
}
if !os::path_is_dir(&dir) {
@ -662,6 +532,28 @@ impl PkgSrc {
dir
}
/// 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.
/// (right now we only support git)
fn fetch_git(&self) -> bool {
let mut local = self.root.push("src");
local = local.push(self.id.to_str());
// Git can't clone into a non-empty directory
os::remove_dir_recursive(&local);
let url = fmt!("https://%s", self.id.remote_path.to_str());
util::note(fmt!("git clone %s %s", url, local.to_str()));
if run::program_output(~"git", ~[~"clone", copy url, local.to_str()]).status != 0 {
util::note(fmt!("fetching %s failed: can't clone repository", url));
return false;
}
true
}
// 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> {
@ -680,7 +572,7 @@ impl PkgSrc {
/// Requires that dashes in p have already been normalized to
/// underscores
fn stem_matches(&self, p: &Path) -> bool {
let self_id = normalize(~copy self.id.path).filestem();
let self_id = self.id.local_path.filestem();
if self_id == p.filestem() {
return true;
}
@ -715,7 +607,7 @@ impl PkgSrc {
let dir = self.check_dir();
let prefix = dir.components.len();
debug!("Matching against %?", self.id.path.filestem());
debug!("Matching against %?", self.id.local_path.filestem());
for os::walk_dir(&dir) |pth| {
match pth.filename() {
Some(~"lib.rs") => push_crate(&mut self.libs,
@ -752,8 +644,7 @@ impl PkgSrc {
src_dir: &Path,
crates: &[Crate],
cfgs: &[~str],
test: bool, crate_type: crate_type) {
what: OutputType) {
for crates.each |&crate| {
let path = &src_dir.push_rel(&crate.file).normalize();
util::note(fmt!("build_crates: compiling %s", path.to_str()));
@ -763,7 +654,7 @@ impl PkgSrc {
dst_dir,
crate.flags,
crate.cfgs + cfgs,
false, test, crate_type);
false, what);
if !result {
build_err::cond.raise(fmt!("build failure on %s",
path.to_str()));
@ -776,12 +667,13 @@ impl PkgSrc {
fn build(&self, dst_dir: &Path, cfgs: ~[~str], maybe_sysroot: Option<@Path>) {
let dir = self.check_dir();
debug!("Building libs");
self.build_crates(maybe_sysroot, dst_dir, &dir, self.libs, cfgs, false, lib_crate);
self.build_crates(maybe_sysroot, dst_dir, &dir, self.libs, cfgs, Lib);
debug!("Building mains");
self.build_crates(maybe_sysroot, dst_dir, &dir, self.mains, cfgs, false, bin_crate);
self.build_crates(maybe_sysroot, dst_dir, &dir, self.mains, cfgs, Main);
debug!("Building tests");
self.build_crates(maybe_sysroot, dst_dir, &dir, self.tests, cfgs, true, bin_crate);
self.build_crates(maybe_sysroot, dst_dir, &dir, self.tests, cfgs, Test);
debug!("Building benches");
self.build_crates(maybe_sysroot, dst_dir, &dir, self.benchs, cfgs, true, bin_crate);
self.build_crates(maybe_sysroot, dst_dir, &dir, self.benchs, cfgs, Bench);
}
}

View file

@ -17,8 +17,8 @@ use std::tempfile::mkdtemp;
use util::{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};
use core::os::mkdir_recursive;
make_dir_rwx, u_rwx, RemotePath, LocalPath, normalize,
built_bench_in_workspace, built_test_in_workspace};
fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
Ctx {
@ -29,15 +29,22 @@ fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx {
}
fn fake_pkg() -> PkgId {
let sn = ~"bogus";
let remote = RemotePath(Path(sn));
PkgId {
path: Path(~"bogus"),
local_path: normalize(copy remote),
remote_path: remote,
short_name: sn,
version: default_version()
}
}
fn remote_pkg() -> PkgId {
let remote = RemotePath(Path(~"github.com/catamorphism/test-pkg"));
PkgId {
path: Path(~"github.com/catamorphism/test-pkg"),
local_path: normalize(copy remote),
remote_path: remote,
short_name: ~"test_pkg",
version: default_version()
}
}
@ -49,10 +56,11 @@ fn writeFile(file_path: &Path, contents: ~str) {
out.write_line(contents);
}
fn mk_temp_workspace(short_name: &Path) -> Path {
fn mk_temp_workspace(short_name: &LocalPath) -> Path {
let workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
let package_dir = workspace.push(~"src").push_rel(short_name);
assert!(mkdir_recursive(&package_dir, u_rwx));
// 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));
// Create main, lib, test, and bench files
writeFile(&package_dir.push(~"main.rs"),
~"fn main() { let _x = (); }");
@ -104,7 +112,7 @@ fn test_install_valid() {
debug!("sysroot = %s", sysroot.to_str());
let ctxt = fake_ctxt(Some(@sysroot));
let temp_pkg_id = fake_pkg();
let temp_workspace = mk_temp_workspace(&temp_pkg_id.path);
let temp_workspace = mk_temp_workspace(&temp_pkg_id.local_path);
// should have test, bench, lib, and main
ctxt.install(&temp_workspace, &temp_pkg_id);
// Check that all files exist
@ -146,27 +154,79 @@ fn test_install_invalid() {
}
#[test]
#[ignore(reason = "install from URL-fragment not yet implemented")]
fn test_install_url() {
let workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir");
let sysroot = test_sysroot();
debug!("sysroot = %s", sysroot.to_str());
let ctxt = fake_ctxt(Some(@sysroot));
let temp_pkg_id = remote_pkg();
let temp_workspace = mk_temp_workspace(&temp_pkg_id.path);
// should have test, bench, lib, and main
ctxt.install(&temp_workspace, &temp_pkg_id);
ctxt.install(&workspace, &temp_pkg_id);
// Check that all files exist
let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace);
let exec = target_executable_in_workspace(&temp_pkg_id, &workspace);
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);
let lib = target_library_in_workspace(&temp_pkg_id, &workspace);
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");
assert!(os::path_exists(&built_test));
let built_bench = built_bench_in_workspace(&temp_pkg_id,
&workspace).expect(~"test_install_url");
assert!(os::path_exists(&built_bench));
// 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);
let test = target_test_in_workspace(&temp_pkg_id, &workspace);
assert!(!os::path_exists(&test));
debug!("test = %s", test.to_str());
let bench = target_bench_in_workspace(&temp_pkg_id, &workspace);
debug!("bench = %s", bench.to_str());
assert!(!os::path_exists(&bench));
}
#[test]
fn test_package_ids_must_be_relative_path_like() {
use conditions::bad_pkg_id::cond;
/*
Okay:
- One identifier, with no slashes
- Several slash-delimited things, with no / at the root
Not okay:
- Empty string
- Absolute path (as per os::is_absolute)
*/
let default_version_str = "0.1";
let addversion = |s| {
fmt!("%s-%s", s, default_version_str)
};
let whatever = PkgId::new("foo");
assert!(addversion("foo") == whatever.to_str());
assert!(addversion("github.com/mozilla/rust") ==
PkgId::new("github.com/mozilla/rust").to_str());
do cond.trap(|(p, e)| {
assert!("" == p.to_str());
assert!("0-length pkgid" == e);
copy whatever
}).in {
let x = PkgId::new("");
assert!(addversion("foo") == x.to_str());
}
do cond.trap(|(p, e)| {
assert!(p.to_str() == os::make_absolute(&Path("foo/bar/quux")).to_str());
assert!("absolute pkgid" == e);
copy whatever
}).in {
let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str());
assert!(addversion("foo") == z.to_str());
}
}

View file

@ -13,20 +13,18 @@ use core::cmp::Ord;
use core::hash::Streaming;
use core::rt::io::Writer;
use rustc::driver::{driver, session};
use rustc::driver::session::{lib_crate, unknown_crate};
use rustc::metadata::filesearch;
use std::getopts::groups::getopts;
use std::semver;
use std::{json, term, getopts};
use std::{term, getopts};
use syntax::ast_util::*;
use syntax::codemap::{dummy_sp, spanned, dummy_spanned};
use syntax::ext::base::{mk_ctxt, ext_ctxt};
use syntax::ext::build;
use syntax::{ast, attr, codemap, diagnostic, fold};
use syntax::ast::{meta_name_value, meta_list, attribute};
use syntax::ast::{meta_name_value, meta_list};
use syntax::attr::{mk_attr};
use rustc::back::link::output_type_exe;
use rustc::driver::session::{lib_crate, unknown_crate, crate_type};
use rustc::driver::session::{lib_crate, bin_crate};
static Commands: &'static [&'static str] =
&["build", "clean", "do", "info", "install", "prefer", "test", "uninstall",
@ -83,20 +81,34 @@ impl ToStr for Version {
}
}
#[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.
/// Path-fragment identifier of a package such as
/// 'github.com/graydon/test'; path must be a relative
/// path with >=1 component.
pub struct PkgId {
path: Path,
/// 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 bad_pkg_id::cond;
use conditions::bad_pkg_id::cond;
let p = Path(s);
if p.is_absolute {
@ -105,31 +117,32 @@ pub impl 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 {
path: p,
local_path: local_path,
remote_path: remote_path,
short_name: short_name,
version: default_version()
}
}
fn hash(&self) -> ~str {
fmt!("%s-%s-%s", self.path.to_str(),
hash(self.path.to_str() + self.version.to_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.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(), ".", "-"))
fmt!("%s-%s", self.local_path.to_str(), self.version.to_str())
}
}
@ -156,26 +169,6 @@ pub fn is_cmd(cmd: &str) -> bool {
Commands.any(|&c| c == cmd)
}
pub fn parse_name(id: ~str) -> result::Result<~str, ~str> {
let mut last_part = None;
for str::each_split_char(id, '.') |part| {
for str::each_char(part) |char| {
if char::is_whitespace(char) {
return result::Err(
~"could not parse id: contains whitespace");
} else if char::is_uppercase(char) {
return result::Err(
~"could not parse id: should be all lowercase");
}
}
last_part = Some(part.to_owned());
}
if last_part.is_none() { return result::Err(~"could not parse id: is empty"); }
result::Ok(last_part.unwrap())
}
struct ListenerFn {
cmds: ~[~str],
span: codemap::span,
@ -248,52 +241,6 @@ fn fold_item(ctx: @mut ReadyCtx,
res
}
fn add_pkg_module(ctx: @mut ReadyCtx, m: ast::_mod) -> ast::_mod {
let listeners = mk_listener_vec(ctx);
let ext_cx = ctx.ext_cx;
let item = quote_item! (
mod __pkg {
extern mod rustpkg (vers="0.7-pre");
static listeners : &[rustpkg::Listener] = $listeners;
#[main]
fn main() {
rustpkg::run(listeners);
}
}
);
ast::_mod {
items: vec::append_one(/*bad*/copy m.items, item.get()),
.. m
}
}
fn mk_listener_vec(ctx: @mut ReadyCtx) -> @ast::expr {
let descs = do ctx.fns.map |listener| {
mk_listener_rec(ctx, listener)
};
let ext_cx = ctx.ext_cx;
build::mk_slice_vec_e(ext_cx, dummy_sp(), descs)
}
fn mk_listener_rec(ctx: @mut ReadyCtx, listener: &ListenerFn) -> @ast::expr {
let span = listener.span;
let cmds = do listener.cmds.map |&cmd| {
let ext_cx = ctx.ext_cx;
build::mk_base_str(ext_cx, span, cmd)
};
let ext_cx = ctx.ext_cx;
let cmds_expr = build::mk_slice_vec_e(ext_cx, span, cmds);
let cb_expr = build::mk_path(ext_cx, span, copy listener.path);
quote_expr!(
Listener {
cmds: $cmds_expr,
cb: $cb_expr
}
)
}
/// Generate/filter main function, add the list of commands, etc.
pub fn ready_crate(sess: session::Session,
crate: @ast::crate) -> @ast::crate {
@ -375,67 +322,6 @@ pub fn hash(data: ~str) -> ~str {
hasher.result_str()
}
pub fn temp_change_dir<T>(dir: &Path, cb: &fn() -> T) {
let cwd = os::getcwd();
os::change_dir(dir);
cb();
os::change_dir(&cwd);
}
pub fn touch(path: &Path) {
match io::mk_file_writer(path, ~[io::Create]) {
result::Ok(writer) => writer.write_line(~""),
_ => {}
}
}
pub fn remove_dir_r(path: &Path) {
for os::walk_dir(path) |&file| {
let mut cdir = file;
loop {
if os::path_is_dir(&cdir) {
os::remove_dir(&cdir);
} else {
os::remove_file(&cdir);
}
cdir = cdir.dir_path();
if cdir == *path { break; }
}
}
os::remove_dir(path);
}
pub fn wait_for_lock(path: &Path) {
if os::path_exists(path) {
warn(fmt!("the database appears locked, please wait (or rm %s)",
path.to_str()));
loop {
if !os::path_exists(path) { break; }
}
}
}
pub fn load_pkgs() -> result::Result<~[json::Json], ~str> {
fail!("load_pkg not implemented");
}
pub fn get_pkg(_id: ~str,
_vers: Option<~str>) -> result::Result<Pkg, ~str> {
fail!("get_pkg not implemented");
}
pub fn add_pkg(pkg: &Pkg) -> bool {
note(fmt!("Would be adding package, but add_pkg is not yet implemented %s",
pkg.to_str()));
false
}
// FIXME (#4432): Use workcache to only compile when needed
pub fn compile_input(sysroot: Option<@Path>,
pkg_id: &PkgId,
@ -444,31 +330,24 @@ pub fn compile_input(sysroot: Option<@Path>,
flags: &[~str],
cfgs: &[~str],
opt: bool,
test: bool,
crate_type: session::crate_type) -> bool {
// 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());
what: OutputType) -> bool {
assert!(in_file.components.len() > 1);
let input = driver::file_input(copy *in_file);
debug!("compile_input: %s / %?", in_file.to_str(), crate_type);
debug!("compile_input: %s / %?", in_file.to_str(), what);
// tjc: by default, use the package ID name as the link name
// not sure if we should support anything else
let binary = @copy os::args()[0];
let building_library = match crate_type {
lib_crate | unknown_crate => true,
_ => false
};
let binary = @(copy os::args()[0]);
let building_library = what == Lib;
let out_file = if building_library {
out_dir.push(os::dll_filename(short_name))
out_dir.push(os::dll_filename(pkg_id.short_name))
}
else {
out_dir.push(short_name + if test { ~"test" } else { ~"" }
+ os::EXE_SUFFIX)
out_dir.push(pkg_id.short_name + match what {
Test => ~"test", Bench => ~"bench", Main | Lib => ~""
} + os::EXE_SUFFIX)
};
debug!("compiling %s into %s",
@ -478,18 +357,24 @@ pub fn compile_input(sysroot: Option<@Path>,
debug!("cfgs: %s", str::connect(cfgs, ~" "));
debug!("compile_input's sysroot = %?", sysroot);
let crate_type = match what {
Lib => lib_crate,
Test | Bench | Main => bin_crate
};
let matches = getopts(~[~"-Z", ~"time-passes"]
+ if building_library { ~[~"--lib"] }
else if test { ~[~"--test"] }
// bench?
else { ~[] }
+ match what {
Lib => ~[~"--lib"],
// --test compiles both #[test] and #[bench] fns
Test | Bench => ~[~"--test"],
Main => ~[]
}
+ flags
+ cfgs.flat_map(|&c| { ~[~"--cfg", c] }),
driver::optgroups()).get();
let mut options = session::options {
crate_type: crate_type,
optimize: if opt { session::Aggressive } else { session::No },
test: test,
test: what == Test || what == Bench,
maybe_sysroot: sysroot,
addl_lib_search_paths: ~[copy *out_dir],
// output_type should be conditional
@ -540,26 +425,27 @@ pub fn compile_crate_from_input(input: &driver::input,
debug!("Calling compile_upto, outputs = %?", outputs);
let (crate, _) = driver::compile_upto(sess, copy cfg, input,
driver::cu_parse, Some(outputs));
let mut crate = crate;
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)
let mut crate_to_use = crate;
debug!("How many attrs? %?", attr::find_linkage_metas(crate.node.attrs).len());
if attr::find_linkage_metas(crate.node.attrs).is_empty() {
crate_to_use = add_attrs(copy *crate,
~[mk_attr(@dummy_spanned(meta_list(@~"link",
// change PkgId to have a <shortname> field?
~[@dummy_spanned(meta_name_value(@~"name",
mk_string_lit(@pkg_id.path.filestem().get()))),
@dummy_spanned(meta_name_value(@~"vers",
mk_string_lit(@pkg_id.version.to_str())))])))]);
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});
}
driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate_to_use));
crate_to_use
driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate));
crate
}
}
}
@ -573,29 +459,40 @@ pub fn exe_suffix() -> ~str { ~".exe" }
#[cfg(target_os = "macos")]
pub fn exe_suffix() -> ~str { ~"" }
/// Returns a copy of crate `c` with attributes `attrs` added to its
/// attributes
fn add_attrs(mut c: ast::crate, new_attrs: ~[attribute]) -> @ast::crate {
c.node.attrs += new_attrs;
@c
}
// Called by build_crates
// FIXME (#4432): Use workcache to only compile when needed
pub fn compile_crate(sysroot: Option<@Path>, pkg_id: &PkgId,
crate: &Path, dir: &Path,
flags: &[~str], cfgs: &[~str], opt: bool,
test: bool, crate_type: crate_type) -> bool {
what: OutputType) -> bool {
debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str());
debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
for flags.each |&fl| {
debug!("+++ %s", fl);
}
compile_input(sysroot, pkg_id,
crate, dir, flags, cfgs, opt, test, crate_type)
compile_input(sysroot, 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)
}
}
}
}
#[cfg(windows)]
pub fn link_exe(_src: &Path, _dest: &Path) -> bool {
@ -628,9 +525,13 @@ 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, parse_name};
use super::is_cmd;
#[test]
fn test_is_cmd() {
@ -645,9 +546,4 @@ mod test {
assert!(is_cmd(~"unprefer"));
}
#[test]
fn test_parse_name() {
assert!(parse_name(~"org.mozilla.servo").get() == ~"servo");
assert!(parse_name(~"org. mozilla.servo 2131").is_err());
}
}

View file

@ -21,9 +21,10 @@ pub fn pkg_parent_workspaces(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool
workspace_contains_package_id(pkgid, ws));
if workspaces.is_empty() {
// tjc: make this a condition
fail!("Package %s not found in any of the following workspaces: %s",
pkgid.path.to_str(),
rust_path().to_str());
fail!("Package %s not found in any of \
the following workspaces: %s",
pkgid.remote_path.to_str(),
rust_path().to_str());
}
for workspaces.each |ws| {
if action(ws) {

@ -1 +1 @@
Subproject commit 97ac7c087a0caf6b0f611b80e14f7fe3cb18bb27
Subproject commit 218ab86721eefd7b7e97fa6d9f95a80a1fa8686c