From d92b4358d278afb6f0172f4cddb5a60ebcdff7ca Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Sat, 1 Jun 2013 15:59:12 -0700 Subject: [PATCH] rustpkg: Accept package IDs like github.com/foo/bar#0.3 If the package ID is of the form s#v, where v is a valid version string, fetch tag v of that package. --- src/librustpkg/crate.rs | 58 +++++++ src/librustpkg/package_id.rs | 35 +++- src/librustpkg/package_source.rs | 239 +++++++++++++++++++++++++++ src/librustpkg/path_util.rs | 16 +- src/librustpkg/rustpkg.rc | 268 ++----------------------------- src/librustpkg/tests.rs | 121 +++++++++----- src/librustpkg/util.rs | 13 +- src/librustpkg/version.rs | 133 +++++++++++---- 8 files changed, 542 insertions(+), 341 deletions(-) create mode 100644 src/librustpkg/crate.rs create mode 100644 src/librustpkg/package_source.rs diff --git a/src/librustpkg/crate.rs b/src/librustpkg/crate.rs new file mode 100644 index 0000000000000..5e8139063deca --- /dev/null +++ b/src/librustpkg/crate.rs @@ -0,0 +1,58 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::path::Path; +use core::vec; + +/// 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] +} + +impl Crate { + + pub 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 + } + } +} diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index 9b05c848b6e32..022acc29c8bca 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -9,11 +9,8 @@ // except according to those terms. pub use package_path::{RemotePath, LocalPath, normalize, hash}; -use extra::semver; use core::prelude::*; -use core::result; -use core::prelude::*; -use version::{default_version, try_getting_version, Version}; +use version::{try_getting_version, Version, NoVersion, split_version}; /// Path-fragment identifier of a package such as /// 'github.com/graydon/test'; path must be a relative @@ -38,6 +35,21 @@ impl PkgId { pub fn new(s: &str) -> PkgId { use conditions::bad_pkg_id::cond; + let mut given_version = None; + + // Did the user request a specific version? + let s = match split_version(s) { + Some((path, v)) => { + debug!("s = %s, path = %s, v = %s", s, path, v.to_str()); + given_version = Some(v); + path + } + None => { + debug!("%s has no explicit version", s); + s + } + }; + let p = Path(s); if p.is_absolute { return cond.raise((p, ~"absolute pkgid")); @@ -49,9 +61,12 @@ impl PkgId { let local_path = normalize(copy remote_path); let short_name = (copy local_path).filestem().expect(fmt!("Strange path! %s", s)); - let version = match try_getting_version(remote_path) { + let version = match given_version { Some(v) => v, - None => default_version() + None => match try_getting_version(&remote_path) { + Some(v) => v, + None => NoVersion + } }; PkgId { @@ -69,13 +84,17 @@ impl PkgId { } pub fn short_name_with_version(&self) -> ~str { - fmt!("%s-%s", self.short_name, self.version.to_str()) + fmt!("%s%s", self.short_name, self.version.to_str()) } } impl ToStr for PkgId { fn to_str(&self) -> ~str { + let maybe_dash = match self.version { + NoVersion => "", + _ => "-" + }; // should probably use the filestem and not the whole path - fmt!("%s-%s", self.local_path.to_str(), self.version.to_str()) + fmt!("%s%s%s", self.local_path.to_str(), maybe_dash, self.version.to_str()) } } diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs new file mode 100644 index 0000000000000..7c4c63781240b --- /dev/null +++ b/src/librustpkg/package_source.rs @@ -0,0 +1,239 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target::*; +use package_id::PkgId; +use core::path::Path; +use core::option::*; +use core::{os, run, str, vec}; +use context::*; +use crate::Crate; +use path_util::pkgid_src_in_workspace; +use util::{compile_crate, note}; +use version::{ExactRevision, SemanticVersion, NoVersion}; + +// 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 { + + pub 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::nonexistent_package::cond; + + debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), + self.root.to_str()); + let dir; + let dirs = pkgid_src_in_workspace(&self.id, &self.root); + debug!("Checking dirs: %?", dirs); + let path = dirs.find(|d| os::path_exists(d)); + match path { + Some(d) => dir = d, + None => dir = match self.fetch_git() { + None => cond.raise((copy self.id, ~"supplied path for package dir does not \ + exist, and couldn't interpret it as a URL fragment")), + Some(d) => d + } + } + if !os::path_is_dir(&dir) { + cond.raise((copy self.id, ~"supplied path for package dir is a \ + non-directory")); + } + + dir + } + + /// Try interpreting self's package id as a remote package, and try + /// fetching it and caching it in a local directory. Return the cached directory + /// if this was successful, None otherwise + /// (right now we only support git) + pub fn fetch_git(&self) -> Option { + + 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()); + let branch_args = match self.id.version { + NoVersion => ~[], + ExactRevision(ref s) => ~[~"--branch", copy *s], + SemanticVersion(ref s) => ~[~"--branch", s.to_str()] + }; + + + note(fmt!("git clone %s %s %?", url, local.to_str(), branch_args)); + + if run::process_output("git", + ~[~"clone", copy url, local.to_str()] + branch_args).status != 0 { + note(fmt!("fetching %s failed: can't clone repository", url)); + None + } + else { + Some(local) + } + } + + + // If a file named "pkg.rs" in the current directory exists, + // return the path for it. Otherwise, None + pub fn package_script_option(&self, cwd: &Path) -> Option { + 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 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 = self.id.local_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 + pub fn find_crates(&mut self) { + use conditions::missing_pkg_files::cond; + + let dir = self.check_dir(); + debug!("Called check_dir, I'm in %s", dir.to_str()); + let prefix = dir.components.len(); + debug!("Matching against %?", self.id.local_path.filestem()); + for os::walk_dir(&dir) |pth| { + match pth.filename() { + Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs, + prefix, + pth), + Some(~"main.rs") => PkgSrc::push_crate(&mut self.mains, + prefix, + pth), + Some(~"test.rs") => PkgSrc::push_crate(&mut self.tests, + prefix, + pth), + Some(~"bench.rs") => PkgSrc::push_crate(&mut self.benchs, + prefix, + pth), + _ => () + } + } + + if self.libs.is_empty() && self.mains.is_empty() + && self.tests.is_empty() && self.benchs.is_empty() { + + note(~"Couldn't infer any crates to build.\n\ + Try naming a crate `main.rs`, `lib.rs`, \ + `test.rs`, or `bench.rs`."); + cond.raise(copy self.id); + } + + 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, + ctx: &Ctx, + dst_dir: &Path, + src_dir: &Path, + crates: &[Crate], + cfgs: &[~str], + what: OutputType) { + for crates.each |&crate| { + let path = &src_dir.push_rel(&crate.file).normalize(); + note(fmt!("build_crates: compiling %s", path.to_str())); + note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); + + let result = compile_crate(ctx, + &self.id, + path, + dst_dir, + crate.flags, + crate.cfgs + cfgs, + false, + what); + if !result { + build_err::cond.raise(fmt!("build failure on %s", + path.to_str())); + } + debug!("Result of compiling %s was %?", + path.to_str(), result); + } + } + + pub fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { + let dir = self.check_dir(); + debug!("Building libs in %s", dir.to_str()); + self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); + debug!("Building mains"); + self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); + debug!("Building tests"); + self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); + debug!("Building benches"); + self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); + } +} diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 7b4a9a63a4ec0..af6b23ba75544 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -12,7 +12,7 @@ use core::prelude::*; pub use package_path::{RemotePath, LocalPath}; -pub use package_id::{PkgId, Version}; +pub use package_id::PkgId; pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install}; use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; use core::os::mkdir_recursive; @@ -210,11 +210,17 @@ pub fn target_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { } -/// Returns the executable that would be installed for -/// in +/// Returns the installed path for in /// As a side effect, creates the lib-dir if it doesn't exist -pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { - target_file_in_workspace(pkgid, workspace, Lib, Install) +pub fn target_library_in_workspace(workspace: &Path, + built_library: &Path) -> Path { + use conditions::bad_path::cond; + let result = workspace.push("lib"); + if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) { + cond.raise((copy result, ~"I couldn't create the library directory")); + } + result.push(built_library.filename().expect(fmt!("I don't know how to treat %s as a library", + built_library.to_str()))) } /// Returns the test executable that would be installed for diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index 8591251d4cd5f..37b8c2ad43384 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -36,18 +36,21 @@ use rustc::metadata::filesearch; use extra::{getopts}; use syntax::{ast, diagnostic}; use util::*; -use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, first_pkgid_src_in_workspace}; +use path_util::{build_pkg_id_in_workspace, first_pkgid_src_in_workspace}; use path_util::u_rwx; use path_util::{built_executable_in_workspace, built_library_in_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; use workspace::pkg_parent_workspaces; use context::Ctx; use package_id::PkgId; +use package_source::PkgSrc; mod conditions; mod context; +mod crate; mod package_id; mod package_path; +mod package_source; mod path_util; mod search; mod target; @@ -337,11 +340,11 @@ impl Ctx { let maybe_executable = built_executable_in_workspace(id, workspace); let maybe_library = built_library_in_workspace(id, workspace); let target_exec = target_executable_in_workspace(id, workspace); - let target_lib = target_library_in_workspace(id, workspace); + let target_lib = maybe_library.map(|p| target_library_in_workspace(workspace, p)); - debug!("target_exec = %s target_lib = %s \ + debug!("target_exec = %s target_lib = %? \ maybe_executable = %? maybe_library = %?", - target_exec.to_str(), target_lib.to_str(), + target_exec.to_str(), target_lib, maybe_executable, maybe_library); for maybe_executable.each |exec| { @@ -352,6 +355,8 @@ impl Ctx { } } for maybe_library.each |lib| { + let target_lib = (copy target_lib).expect(fmt!("I built %s but apparently \ + didn't install it!", lib.to_str())); debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str()); if !(os::mkdir_recursive(&target_lib.dir_path(), u_rwx) && os::copy_file(lib, &target_lib)) { @@ -432,51 +437,6 @@ pub fn main() { }.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] -} - -impl Crate { - pub fn new(p: &Path) -> Crate { - Crate { - file: copy *p, - flags: ~[], - cfgs: ~[] - } - } - - pub fn flag(&self, flag: ~str) -> Crate { - Crate { - flags: vec::append(copy self.flags, [flag]), - .. copy *self - } - } - - pub fn flags(&self, flags: ~[~str]) -> Crate { - Crate { - flags: vec::append(copy self.flags, flags), - .. copy *self - } - } - - pub fn cfg(&self, cfg: ~str) -> Crate { - Crate { - cfgs: vec::append(copy self.cfgs, [cfg]), - .. copy *self - } - } - - pub 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 @@ -495,213 +455,3 @@ pub fn work_dir() -> Path { pub fn src_dir() -> Path { os::getcwd() } - -// 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::nonexistent_package::cond; - - debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), - self.root.to_str()); - let dir; - let dirs = pkgid_src_in_workspace(&self.id, &self.root); - debug!("Checking dirs: %?", dirs); - let path = dirs.find(|d| os::path_exists(d)); - match path { - Some(d) => dir = d, - None => dir = match self.fetch_git() { - None => cond.raise((copy self.id, ~"supplied path for package dir does not \ - exist, and couldn't interpret it as a URL fragment")), - Some(d) => d - } - } - if !os::path_is_dir(&dir) { - cond.raise((copy self.id, ~"supplied path for package dir is a \ - non-directory")); - } - - dir - } - - /// Try interpreting self's package id as a remote package, and try - /// fetching it and caching it in a local directory. Return the cached directory - /// if this was successful, None otherwise - /// (right now we only support git) - fn fetch_git(&self) -> Option { - - 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::process_output("git", [~"clone", copy url, local.to_str()]).status != 0 { - util::note(fmt!("fetching %s failed: can't clone repository", url)); - None - } - else { - Some(local) - } - } - - - // 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 { - 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 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 = self.id.local_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 conditions::missing_pkg_files::cond; - - let dir = self.check_dir(); - let prefix = dir.components.len(); - debug!("Matching against %?", self.id.local_path.filestem()); - for os::walk_dir(&dir) |pth| { - match pth.filename() { - Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs, - prefix, - pth), - Some(~"main.rs") => PkgSrc::push_crate(&mut self.mains, - prefix, - pth), - Some(~"test.rs") => PkgSrc::push_crate(&mut self.tests, - prefix, - pth), - Some(~"bench.rs") => PkgSrc::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`."); - cond.raise(copy self.id); - } - - 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, - ctx: &Ctx, - dst_dir: &Path, - src_dir: &Path, - crates: &[Crate], - cfgs: &[~str], - 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())); - util::note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); - - let result = util::compile_crate(ctx, - &self.id, - path, - dst_dir, - crate.flags, - crate.cfgs + cfgs, - false, - what); - if !result { - build_err::cond.raise(fmt!("build failure on %s", - path.to_str())); - } - debug!("Result of compiling %s was %?", - path.to_str(), result); - } - } - - fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { - let dir = self.check_dir(); - debug!("Building libs"); - self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); - debug!("Building mains"); - self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); - debug!("Building tests"); - self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); - debug!("Building benches"); - self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); - } -} diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index e897f757ec229..8df84b27fccf4 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -19,11 +19,14 @@ use core::result; use extra::tempfile::mkdtemp; use package_path::*; use package_id::PkgId; -use version::{default_version, ExactRevision}; +use package_source::*; +use version::{ExactRevision, NoVersion, 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, - built_bench_in_workspace, built_test_in_workspace}; + built_bench_in_workspace, built_test_in_workspace, + built_library_in_workspace, built_executable_in_workspace, + installed_library_in_workspace}; fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { Ctx { @@ -40,7 +43,7 @@ fn fake_pkg() -> PkgId { local_path: normalize(copy remote), remote_path: remote, short_name: sn, - version: default_version() + version: NoVersion } } @@ -50,17 +53,7 @@ fn remote_pkg() -> PkgId { local_path: normalize(copy remote), remote_path: remote, short_name: ~"test_pkg", - version: default_version() - } -} - -fn remote_versioned_pkg() -> PkgId { - let remote = RemotePath(Path("github.com/catamorphism/test_pkg_version")); - PkgId { - local_path: normalize(copy remote), - remote_path: remote, - short_name: ~"test_pkg_version", - version: default_version() + version: NoVersion } } @@ -71,11 +64,21 @@ fn writeFile(file_path: &Path, contents: &str) { out.write_line(contents); } -fn mk_temp_workspace(short_name: &LocalPath) -> Path { +fn mk_empty_workspace(short_name: &LocalPath, version: &Version) -> Path { let workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); // include version number in directory name - let package_dir = workspace.push("src").push(fmt!("%s-0.1", short_name.to_str())); + let package_dir = workspace.push("src").push(fmt!("%s%s", + short_name.to_str(), version.to_str())); assert!(os::mkdir_recursive(&package_dir, u_rwx)); + package_dir.pop().pop() +} + +fn mk_temp_workspace(short_name: &LocalPath, version: &Version) -> Path { + let package_dir = mk_empty_workspace(short_name, + version).push("src").push(fmt!("%s%s", + short_name.to_str(), + version.to_str())); + debug!("Created %s and does it exist? %?", package_dir.to_str(), os::path_is_dir(&package_dir)); // Create main, lib, test, and bench files @@ -87,7 +90,7 @@ fn mk_temp_workspace(short_name: &LocalPath) -> Path { "#[test] pub fn f() { (); }"); writeFile(&package_dir.push("bench.rs"), "#[bench] pub fn f() { (); }"); - workspace + package_dir.pop().pop() } fn is_rwx(p: &Path) -> bool { @@ -131,7 +134,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.local_path); + let temp_workspace = mk_temp_workspace(&temp_pkg_id.local_path, &NoVersion); // should have test, bench, lib, and main ctxt.install(&temp_workspace, &temp_pkg_id); // Check that all files exist @@ -189,7 +192,10 @@ fn test_install_url() { debug!("exec = %s", exec.to_str()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); - let lib = target_library_in_workspace(&temp_pkg_id, &workspace); + let built_lib = + built_library_in_workspace(&temp_pkg_id, + &workspace).expect("test_install_url: built lib should exist"); + let lib = target_library_in_workspace(&workspace, &built_lib); debug!("lib = %s", lib.to_str()); assert!(os::path_exists(&lib)); assert!(is_rwx(&lib)); @@ -223,16 +229,11 @@ fn test_package_ids_must_be_relative_path_like() { */ - let default_version_str = "0.1"; - let addversion = |s| { - fmt!("%s-%s", s, default_version_str) - }; - let whatever = PkgId::new("foo"); - assert_eq!(addversion("foo"), whatever.to_str()); - assert!(addversion("github.com/mozilla/rust") == - PkgId::new("github.com/mozilla/rust").to_str()); + assert_eq!(~"foo", whatever.to_str()); + assert!("github.com/catamorphism/test_pkg" == + PkgId::new("github.com/catamorphism/test-pkg").to_str()); do cond.trap(|(p, e)| { assert!("" == p.to_str()); @@ -240,7 +241,7 @@ fn test_package_ids_must_be_relative_path_like() { copy whatever }).in { let x = PkgId::new(""); - assert_eq!(addversion("foo"), x.to_str()); + assert_eq!(~"foo", x.to_str()); } do cond.trap(|(p, e)| { @@ -249,22 +250,70 @@ fn test_package_ids_must_be_relative_path_like() { copy whatever }).in { let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str()); - assert_eq!(addversion("foo"), z.to_str()); + assert_eq!(~"foo", z.to_str()); } } #[test] fn test_package_version() { - 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 = PkgId::new("github.com/catamorphism/test_pkg_version"); match temp_pkg_id.version { - ExactRevision(0.2) => (), - _ => fail!(fmt!("test_package_version: package version was %?, expected Some(0.2)", + ExactRevision(~"0.4") => (), + _ => fail!(fmt!("test_package_version: package version was %?, expected Some(0.4)", + temp_pkg_id.version)) + } + let temp = mk_empty_workspace(&LocalPath(Path("test_pkg_version")), &temp_pkg_id.version); + let ctx = fake_ctxt(Some(@test_sysroot())); + ctx.build(&temp, &temp_pkg_id); + assert!(match built_library_in_workspace(&temp_pkg_id, &temp) { + Some(p) => p.to_str().ends_with(fmt!("0.4%s", os::consts::DLL_SUFFIX)), + None => false + }); + assert!(built_executable_in_workspace(&temp_pkg_id, &temp) + == Some(temp.push("build"). + push("github.com"). + push("catamorphism"). + push("test_pkg_version"). + push("test_pkg_version"))); +} + +// FIXME #7006: Fails on linux for some reason +#[test] +#[ignore(cfg(target_os = "linux"))] +fn test_package_request_version() { + let temp_pkg_id = PkgId::new("github.com/catamorphism/test_pkg_version#0.3"); + let temp = mk_empty_workspace(&LocalPath(Path("test_pkg_version")), &ExactRevision(~"0.3")); + let pkg_src = PkgSrc::new(&temp, &temp, &temp_pkg_id); + match temp_pkg_id.version { + ExactRevision(~"0.3") => { + match pkg_src.fetch_git() { + Some(p) => { + assert!(os::path_exists(&p.push("version-0.3-file.txt"))); + assert!(!os::path_exists(&p.push("version-0.4-file.txt"))); + + } + None => fail!("test_package_request_version: fetch_git failed") + } + } + ExactRevision(n) => { + fail!("n is %? and %? %s %?", n, n, if n == ~"0.3" { "==" } else { "!=" }, "0.3"); + } + _ => fail!(fmt!("test_package_version: package version was %?, expected ExactRevision(0.3)", temp_pkg_id.version)) } - // also check that file paths are right + let c = fake_ctxt(Some(@test_sysroot())); + c.install(&temp, &temp_pkg_id); + debug!("installed_library_in_workspace(%s, %s) = %?", temp_pkg_id.short_name, temp.to_str(), + installed_library_in_workspace(temp_pkg_id.short_name, &temp)); + assert!(match installed_library_in_workspace(temp_pkg_id.short_name, &temp) { + Some(p) => { + debug!("installed: %s", p.to_str()); + p.to_str().ends_with(fmt!("0.3%s", os::consts::DLL_SUFFIX)) + } + None => false + }); + assert!(target_executable_in_workspace(&temp_pkg_id, &temp) + == temp.push("bin").push("test_pkg_version")); + } diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 84af0ce509583..10d2d139a145b 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -26,7 +26,7 @@ use rustc::driver::driver::compile_upto; use rustc::driver::session::{lib_crate, bin_crate}; use context::Ctx; use package_id::PkgId; -use path_util::target_library_in_workspace; +use path_util::{target_library_in_workspace, built_library_in_workspace}; use search::find_library_in_search_path; pub use target::{OutputType, Main, Lib, Bench, Test}; @@ -274,7 +274,7 @@ pub fn compile_input(ctxt: &Ctx, ~[@dummy_spanned(meta_name_value(@~"name", mk_string_lit(@short_name_to_use))), @dummy_spanned(meta_name_value(@~"vers", - mk_string_lit(@(copy pkg_id.version.to_str()))))])))], + mk_string_lit(@pkg_id.version.to_str_nonempty())))])))], ..copy crate.node}); } @@ -371,9 +371,14 @@ fn find_and_install_dependencies(ctxt: &Ctx, // Try to install it let pkg_id = PkgId::new(*lib_name); my_ctxt.install(&my_workspace, &pkg_id); + let built_lib = + built_library_in_workspace(&pkg_id, + &my_workspace).expect(fmt!("find_and_install_dependencies: \ + I thought I already built %s, but the library doesn't seem \ + to exist", *lib_name)); // Also, add an additional search path - let installed_path = target_library_in_workspace(&pkg_id, - &my_workspace).pop(); + let installed_path = target_library_in_workspace(&my_workspace, + &built_lib).pop(); debug!("Great, I installed %s, and it's in %s", *lib_name, installed_path.to_str()); save(installed_path); diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs index 2de020970210d..92906f5af543d 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -13,44 +13,46 @@ extern mod std; -use std::semver; +use extra::semver; use core::prelude::*; -use core::run; +use core::{char, os, result, run, str}; use package_path::RemotePath; -use std::tempfile::mkdtemp; +use extra::tempfile::mkdtemp; +#[deriving(Eq)] pub enum Version { - ExactRevision(float), - SemVersion(semver::Version) + ExactRevision(~str), // Should look like a m.n.(...).x + SemanticVersion(semver::Version), + NoVersion // user didn't specify a version } impl Ord for Version { fn lt(&self, other: &Version) -> bool { match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 < f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 < v2, + (&ExactRevision(ref f1), &ExactRevision(ref f2)) => f1 < f2, + (&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => v1 < v2, _ => false // incomparable, really } } fn le(&self, other: &Version) -> bool { match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 <= f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 <= v2, + (&ExactRevision(ref f1), &ExactRevision(ref f2)) => f1 <= f2, + (&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => v1 <= v2, _ => false // incomparable, really } } fn ge(&self, other: &Version) -> bool { match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 > f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 > v2, + (&ExactRevision(ref f1), &ExactRevision(ref f2)) => f1 > f2, + (&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => v1 > v2, _ => false // incomparable, really } } fn gt(&self, other: &Version) -> bool { match (self, other) { - (&ExactRevision(f1), &ExactRevision(f2)) => f1 >= f2, - (&SemVersion(ref v1), &SemVersion(ref v2)) => v1 >= v2, + (&ExactRevision(ref f1), &ExactRevision(ref f2)) => f1 >= f2, + (&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => v1 >= v2, _ => false // incomparable, really } } @@ -60,12 +62,25 @@ impl Ord for Version { impl ToStr for Version { fn to_str(&self) -> ~str { match *self { - ExactRevision(ref n) => n.to_str(), - SemVersion(ref v) => v.to_str() + ExactRevision(ref n) => fmt!("%s", n.to_str()), + SemanticVersion(ref v) => fmt!("%s", v.to_str()), + NoVersion => ~"" } } } +impl Version { + /// Fills in a bogus default version for NoVersion -- for use when + /// injecting link_meta attributes + fn to_str_nonempty(&self) -> ~str { + match *self { + NoVersion => ~"0.1", + _ => self.to_str() + } + } +} + + pub fn parse_vers(vers: ~str) -> result::Result { match semver::parse(vers) { Some(vers) => result::Ok(vers), @@ -87,11 +102,14 @@ pub fn try_getting_version(remote_path: &RemotePath) -> Option { let outp = run::process_output("git", [~"clone", fmt!("https://%s", remote_path.to_str()), tmp_dir.to_str()]); if outp.status == 0 { - debug!("Cloned it... ( %s, %s )", str::from_bytes(outp.output), str::from_bytes(outp.error)); + debug!("Cloned it... ( %s, %s )", + str::from_bytes(outp.output), + str::from_bytes(outp.error)); let mut output = None; debug!("executing {git --git-dir=%s tag -l}", tmp_dir.push(".git").to_str()); - let outp = run::process_output("git", [fmt!("--git-dir=%s", tmp_dir.push(".git").to_str()), - ~"tag", ~"-l"]); + let outp = run::process_output("git", + [fmt!("--git-dir=%s", tmp_dir.push(".git").to_str()), + ~"tag", ~"-l"]); let output_text = str::from_bytes(outp.output); debug!("Full output: ( %s ) [%?]", output_text, outp.status); for output_text.each_split_char('\n') |l| { @@ -111,25 +129,37 @@ pub fn try_getting_version(remote_path: &RemotePath) -> Option { None } } - + +// Being lazy since we don't have a regexp library now +#[deriving(Eq)] +enum ParseState { + Start, + SawDigit, + SawDot +} + fn try_parsing_version(s: &str) -> Option { let s = s.trim(); debug!("Attempting to parse: %s", s); - match float::from_str(s) { - Some(f) => { - debug!("%s -> %f", s, f); - Some(ExactRevision(f)) // semver not handled yet + let mut parse_state = Start; + // I gave up on using external iterators (tjc) + for str::to_chars(s).each() |&c| { + if char::is_digit(c) { + parse_state = SawDigit; } - None => { - debug!("None!!"); - None + else if c == '.' && parse_state == SawDigit { + parse_state = SawDot; } + else { + return None; + } + } + match parse_state { + SawDigit => Some(ExactRevision(s.to_owned())), + _ => None } } -/// Placeholder -pub fn default_version() -> Version { ExactRevision(0.1) } - /// Just an approximation fn is_url_like(p: &RemotePath) -> bool { let mut n = 0; @@ -137,4 +167,49 @@ fn is_url_like(p: &RemotePath) -> bool { n += 1; } n > 2 +} + +/// If s is of the form foo#bar, where bar is a valid version +/// number, return the prefix before the # and the version. +/// Otherwise, return None. +pub fn split_version<'a>(s: &'a str) -> Option<(&'a str, Version)> { + // reject strings with multiple '#'s + if { let mut i: uint = 0; for str::to_chars(s).each |&c| { if c == '#' { i += 1; } }; i > 1 } { + return None; + } + match str::rfind_char(s, '#') { + Some(i) => { + debug!("in %s, i = %?", s, i); + let path = s.slice(0, i); + debug!("path = %s", path); + // n.b. for now, assuming an exact revision is intended, not a SemVer + Some((path, ExactRevision(s.slice(i + 1, s.len()).to_owned()))) + } + None => { + debug!("%s doesn't look like an explicit-version thing", s); + None + } + } +} + +#[test] +fn test_parse_version() { + assert!(try_parsing_version("1.2") == Some(ExactRevision(~"1.2"))); + assert!(try_parsing_version("1.0.17") == Some(ExactRevision(~"1.0.17"))); + assert!(try_parsing_version("you're_a_kitty") == None); + assert!(try_parsing_version("42..1") == None); + assert!(try_parsing_version("17") == Some(ExactRevision(~"17"))); + assert!(try_parsing_version(".1.2.3") == None); + assert!(try_parsing_version("2.3.") == None); +} + +#[test] +fn test_split_version() { + let s = "a/b/c#0.1"; + debug!("== %? ==", split_version(s)); + assert!(split_version(s) == Some((s.slice(0, 5), ExactRevision(~"0.1")))); + assert!(split_version("a/b/c") == None); + let s = "a#1.2"; + assert!(split_version(s) == Some((s.slice(0, 1), ExactRevision(~"1.2")))); + assert!(split_version("a#a#3.4") == None); } \ No newline at end of file