auto merge of #6904 : catamorphism/rust/rustpkg_version_vcs, r=catamorphism

r? @brson
This commit is contained in:
bors 2013-06-09 13:16:27 -07:00
commit 94f72dd736
9 changed files with 670 additions and 352 deletions

58
src/librustpkg/crate.rs Normal file
View File

@ -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 <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.
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
}
}
}

View File

@ -9,12 +9,8 @@
// except according to those terms.
pub use package_path::{RemotePath, LocalPath, normalize, hash};
use extra::semver;
use core::prelude::*;
use core::result;
/// Placeholder
pub fn default_version() -> Version { ExactRevision(0.1) }
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
@ -39,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,11 +60,20 @@ impl 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));
let version = match given_version {
Some(v) => v,
None => match try_getting_version(&remote_path) {
Some(v) => v,
None => NoVersion
}
};
PkgId {
local_path: local_path,
remote_path: remote_path,
short_name: short_name,
version: default_version()
version: version
}
}
@ -64,69 +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())
}
}
/// A version is either an exact revision,
/// or a semantic version
pub enum Version {
ExactRevision(float),
SemVersion(semver::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,
_ => 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,
_ => 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,
_ => 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,
_ => false // incomparable, really
}
}
}
impl ToStr for Version {
fn to_str(&self) -> ~str {
match *self {
ExactRevision(ref n) => n.to_str(),
SemVersion(ref v) => v.to_str()
}
}
}
pub fn parse_vers(vers: ~str) -> result::Result<semver::Version, ~str> {
match semver::parse(vers) {
Some(vers) => result::Ok(vers),
None => result::Err(~"could not parse version: invalid")
fmt!("%s%s%s", self.local_path.to_str(), maybe_dash, self.version.to_str())
}
}

View File

@ -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 <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.
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<Path> {
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<Path> {
let maybe_path = cwd.push("pkg.rs");
if os::path_exists(&maybe_path) {
Some(maybe_path)
}
else {
None
}
}
/// True if the given path's stem is self's pkg ID's stem
/// or if the pkg ID's stem is <rust-foo> and the given path's
/// stem is foo
/// Requires that dashes in p have already been normalized to
/// underscores
fn stem_matches(&self, p: &Path) -> bool {
let self_id = 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);
}
}

View File

@ -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 <pkgid>
/// in <workspace>
/// Returns the installed path for <built_library> in <workspace>
/// 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 <pkgid>

View File

@ -18,6 +18,7 @@
#[license = "MIT/ASL2"];
#[crate_type = "lib"];
#[no_core];
#[no_std];
extern mod core(name = "std");
@ -35,24 +36,28 @@ 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;
#[cfg(test)]
mod tests;
mod util;
mod version;
mod workspace;
pub mod usage;
@ -335,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| {
@ -350,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)) {
@ -430,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
@ -493,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<Path> {
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<Path> {
let maybe_path = cwd.push("pkg.rs");
if os::path_exists(&maybe_path) {
Some(maybe_path)
}
else {
None
}
}
/// True if the given path's stem is self's pkg ID's stem
/// or if the pkg ID's stem is <rust-foo> and the given path's
/// stem is foo
/// Requires that dashes in p have already been normalized to
/// underscores
fn stem_matches(&self, p: &Path) -> bool {
let self_id = 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);
}
}

View File

@ -18,11 +18,15 @@ use core::prelude::*;
use core::result;
use extra::tempfile::mkdtemp;
use package_path::*;
use package_id::{PkgId, default_version};
use package_id::PkgId;
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 {
@ -39,7 +43,7 @@ fn fake_pkg() -> PkgId {
local_path: normalize(copy remote),
remote_path: remote,
short_name: sn,
version: default_version()
version: NoVersion
}
}
@ -49,7 +53,7 @@ fn remote_pkg() -> PkgId {
local_path: normalize(copy remote),
remote_path: remote,
short_name: ~"test_pkg",
version: default_version()
version: NoVersion
}
}
@ -60,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
@ -76,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 {
@ -120,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
@ -178,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));
@ -212,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());
@ -229,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)| {
@ -238,7 +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 temp_pkg_id = PkgId::new("github.com/catamorphism/test_pkg_version");
match temp_pkg_id.version {
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))
}
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"));
}

View File

@ -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);

215
src/librustpkg/version.rs Normal file
View File

@ -0,0 +1,215 @@
// Copyright 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.
/// A version is either an exact revision,
/// or a semantic version
extern mod std;
use extra::semver;
use core::prelude::*;
use core::{char, os, result, run, str};
use package_path::RemotePath;
use extra::tempfile::mkdtemp;
#[deriving(Eq)]
pub enum 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(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(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(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(ref f1), &ExactRevision(ref f2)) => f1 >= f2,
(&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => v1 >= v2,
_ => false // incomparable, really
}
}
}
impl ToStr for Version {
fn to_str(&self) -> ~str {
match *self {
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<semver::Version, ~str> {
match semver::parse(vers) {
Some(vers) => result::Ok(vers),
None => result::Err(~"could not parse version: invalid")
}
}
/// If `remote_path` refers to a git repo that can be downloaded,
/// and the most recent tag in that repo denotes a version, return it;
/// otherwise, `None`
pub fn try_getting_version(remote_path: &RemotePath) -> Option<Version> {
debug!("try_getting_version: %s", remote_path.to_str());
if is_url_like(remote_path) {
debug!("Trying to fetch its sources..");
let tmp_dir = mkdtemp(&os::tmpdir(),
"test").expect("try_getting_version: couldn't create temp dir");
debug!("executing {git clone https://%s %s}", remote_path.to_str(), tmp_dir.to_str());
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));
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 output_text = str::from_bytes(outp.output);
debug!("Full output: ( %s ) [%?]", output_text, outp.status);
for output_text.each_split_char('\n') |l| {
debug!("A line of output: %s", l);
if !l.is_whitespace() {
output = Some(l);
}
}
output.chain(try_parsing_version)
}
else {
None
}
}
else {
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<Version> {
let s = s.trim();
debug!("Attempting to parse: %s", s);
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;
}
else if c == '.' && parse_state == SawDigit {
parse_state = SawDot;
}
else {
return None;
}
}
match parse_state {
SawDigit => Some(ExactRevision(s.to_owned())),
_ => None
}
}
/// Just an approximation
fn is_url_like(p: &RemotePath) -> bool {
let mut n = 0;
for p.to_str().each_split_char('/') |_| {
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);
}

View File

@ -47,7 +47,7 @@ pub static neg_infinity: float = -1.0/0.0;
/* Module: consts */
pub mod consts {
// FIXME (requires Issue #1433 to fix): replace with mathematical
// staticants from cmath.
// constants from cmath.
/// Archimedes' constant
pub static pi: float = 3.14159265358979323846264338327950288;