rustpkg: Add package script parsing
This commit is contained in:
parent
71d34a8872
commit
226b61ba5f
@ -169,6 +169,7 @@ pub mod reflect;
|
||||
pub mod condition;
|
||||
pub mod logging;
|
||||
pub mod util;
|
||||
pub mod semver;
|
||||
|
||||
|
||||
/* Reexported core operators */
|
||||
|
227
src/libcore/semver.rs
Normal file
227
src/libcore/semver.rs
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
//! Semver parsing and logic
|
||||
|
||||
use io;
|
||||
use io::{ReaderUtil};
|
||||
use option::{Option, Some, None};
|
||||
use uint;
|
||||
use str;
|
||||
use to_str::ToStr;
|
||||
use char;
|
||||
|
||||
pub struct Version {
|
||||
major: uint,
|
||||
minor: uint,
|
||||
patch: uint,
|
||||
tag: Option<~str>,
|
||||
}
|
||||
|
||||
impl Version: ToStr {
|
||||
#[inline(always)]
|
||||
pure fn to_str() -> ~str {
|
||||
let suffix = match copy self.tag {
|
||||
Some(tag) => ~"-" + tag,
|
||||
None => ~""
|
||||
};
|
||||
|
||||
fmt!("%u.%u.%u%s", self.major, self.minor, self.patch, suffix)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_whitespace(rdr: io::Reader, ch: char) -> char {
|
||||
let mut nch = ch;
|
||||
|
||||
while char::is_whitespace(nch) {
|
||||
nch = rdr.read_char();
|
||||
}
|
||||
|
||||
nch
|
||||
}
|
||||
|
||||
fn parse_reader(rdr: io::Reader) -> Option<(Version, char)> {
|
||||
fn read_digits(rdr: io::Reader, ch: char) -> Option<(uint, char)> {
|
||||
let mut buf = ~"";
|
||||
let mut nch = ch;
|
||||
|
||||
while nch != -1 as char {
|
||||
match nch {
|
||||
'0' .. '9' => buf += str::from_char(nch),
|
||||
_ => break
|
||||
}
|
||||
|
||||
nch = rdr.read_char();
|
||||
}
|
||||
|
||||
do uint::from_str(buf).chain_ref |&i| {
|
||||
Some((i, nch))
|
||||
}
|
||||
}
|
||||
|
||||
fn read_tag(rdr: io::Reader) -> Option<(~str, char)> {
|
||||
let mut ch = rdr.read_char();
|
||||
let mut buf = ~"";
|
||||
|
||||
while ch != -1 as char {
|
||||
match ch {
|
||||
'0' .. '9' | 'A' .. 'Z' | 'a' .. 'z' | '-' => {
|
||||
buf += str::from_char(ch);
|
||||
}
|
||||
_ => break
|
||||
}
|
||||
|
||||
ch = rdr.read_char();
|
||||
}
|
||||
|
||||
if buf == ~"" { return None; }
|
||||
else { Some((buf, ch)) }
|
||||
}
|
||||
|
||||
let ch = read_whitespace(rdr, rdr.read_char());
|
||||
let (major, ch) = match read_digits(rdr, ch) {
|
||||
None => return None,
|
||||
Some(item) => item
|
||||
};
|
||||
|
||||
if ch != '.' { return None; }
|
||||
|
||||
let (minor, ch) = match read_digits(rdr, rdr.read_char()) {
|
||||
None => return None,
|
||||
Some(item) => item
|
||||
};
|
||||
|
||||
if ch != '.' { return None; }
|
||||
|
||||
let (patch, ch) = match read_digits(rdr, rdr.read_char()) {
|
||||
None => return None,
|
||||
Some(item) => item
|
||||
};
|
||||
let (tag, ch) = if ch == '-' {
|
||||
match read_tag(rdr) {
|
||||
None => return None,
|
||||
Some((tag, ch)) => (Some(tag), ch)
|
||||
}
|
||||
} else {
|
||||
(None, ch)
|
||||
};
|
||||
|
||||
Some((Version { major: major, minor: minor, patch: patch, tag: tag },
|
||||
ch))
|
||||
}
|
||||
|
||||
pub fn parse(s: ~str) -> Option<Version> {
|
||||
do io::with_str_reader(s) |rdr| {
|
||||
do parse_reader(rdr).chain_ref |&item| {
|
||||
let (version, ch) = item;
|
||||
|
||||
if read_whitespace(rdr, ch) != -1 as char {
|
||||
None
|
||||
} else {
|
||||
Some(version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
assert parse("") == None;
|
||||
assert parse(" ") == None;
|
||||
assert parse("1") == None;
|
||||
assert parse("1.2") == None;
|
||||
assert parse("1.2") == None;
|
||||
assert parse("1") == None;
|
||||
assert parse("1.2") == None;
|
||||
assert parse("1.2.3-") == None;
|
||||
assert parse("a.b.c") == None;
|
||||
assert parse("1.2.3 abc") == None;
|
||||
|
||||
assert parse("1.2.3") == Some({
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
tag: None,
|
||||
});
|
||||
assert parse(" 1.2.3 ") == Some({
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
tag: None,
|
||||
});
|
||||
assert parse("1.2.3-alpha1") == Some({
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
tag: Some("alpha1")
|
||||
});
|
||||
assert parse(" 1.2.3-alpha1 ") == Some({
|
||||
major: 1u,
|
||||
minor: 2u,
|
||||
patch: 3u,
|
||||
tag: Some("alpha1")
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
assert parse("1.2.3") == parse("1.2.3");
|
||||
assert parse("1.2.3-alpha1") == parse("1.2.3-alpha1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ne() {
|
||||
assert parse("0.0.0") != parse("0.0.1");
|
||||
assert parse("0.0.0") != parse("0.1.0");
|
||||
assert parse("0.0.0") != parse("1.0.0");
|
||||
assert parse("1.2.3-alpha") != parse("1.2.3-beta");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lt() {
|
||||
assert parse("0.0.0") < parse("1.2.3-alpha2");
|
||||
assert parse("1.0.0") < parse("1.2.3-alpha2");
|
||||
assert parse("1.2.0") < parse("1.2.3-alpha2");
|
||||
assert parse("1.2.3") < parse("1.2.3-alpha2");
|
||||
assert parse("1.2.3-alpha1") < parse("1.2.3-alpha2");
|
||||
|
||||
assert !(parse("1.2.3-alpha2") < parse("1.2.3-alpha2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_le() {
|
||||
assert parse("0.0.0") <= parse("1.2.3-alpha2");
|
||||
assert parse("1.0.0") <= parse("1.2.3-alpha2");
|
||||
assert parse("1.2.0") <= parse("1.2.3-alpha2");
|
||||
assert parse("1.2.3") <= parse("1.2.3-alpha2");
|
||||
assert parse("1.2.3-alpha1") <= parse("1.2.3-alpha2");
|
||||
assert parse("1.2.3-alpha2") <= parse("1.2.3-alpha2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gt() {
|
||||
assert parse("1.2.3-alpha2") > parse("0.0.0");
|
||||
assert parse("1.2.3-alpha2") > parse("1.0.0");
|
||||
assert parse("1.2.3-alpha2") > parse("1.2.0");
|
||||
assert parse("1.2.3-alpha2") > parse("1.2.3");
|
||||
assert parse("1.2.3-alpha2") > parse("1.2.3-alpha1");
|
||||
|
||||
assert !(parse("1.2.3-alpha2") > parse("1.2.3-alpha2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ge() {
|
||||
assert parse("1.2.3-alpha2") >= parse("0.0.0");
|
||||
assert parse("1.2.3-alpha2") >= parse("1.0.0");
|
||||
assert parse("1.2.3-alpha2") >= parse("1.2.0");
|
||||
assert parse("1.2.3-alpha2") >= parse("1.2.3");
|
||||
assert parse("1.2.3-alpha2") >= parse("1.2.3-alpha1");
|
||||
assert parse("1.2.3-alpha2") >= parse("1.2.3-alpha2");
|
||||
}
|
@ -9,7 +9,7 @@ pub struct Crate {
|
||||
pub impl Crate {
|
||||
fn flag(flag: ~str) -> Crate {
|
||||
Crate {
|
||||
flags: vec::append(self.flags, flag),
|
||||
flags: vec::append(copy self.flags, ~[flag]),
|
||||
.. copy self
|
||||
}
|
||||
}
|
||||
@ -18,3 +18,7 @@ pub impl Crate {
|
||||
pub fn build(_targets: ~[Crate]) {
|
||||
// TODO: magic
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
#[crate_type = "lib"];
|
||||
#[no_core];
|
||||
#[allow(vecs_implicitly_copyable,
|
||||
non_implicitly_copyable_typarams)];
|
||||
|
||||
extern mod core(vers = "0.6");
|
||||
extern mod std(vers = "0.6");
|
||||
@ -24,26 +26,171 @@ extern mod rustc(vers = "0.6");
|
||||
extern mod syntax(vers = "0.6");
|
||||
|
||||
use core::*;
|
||||
use io::{ReaderUtil, WriterUtil};
|
||||
use std::getopts;
|
||||
use getopts::{optflag, optopt, opt_present};
|
||||
use rustc::metadata::{filesearch};
|
||||
use syntax::{ast, codemap, parse, visit, attr};
|
||||
use semver::Version;
|
||||
|
||||
mod api;
|
||||
mod usage;
|
||||
mod util;
|
||||
|
||||
use util::*;
|
||||
struct PackageScript {
|
||||
id: ~str,
|
||||
name: ~str,
|
||||
vers: Version
|
||||
}
|
||||
|
||||
impl PackageScript {
|
||||
static fn parse(parent: Path) -> PackageScript {
|
||||
let script = parent.push(~"package.rs");
|
||||
|
||||
if !os::path_exists(&script) {
|
||||
fail ~"no package.rs file";
|
||||
}
|
||||
|
||||
let sess = parse::new_parse_sess(None);
|
||||
let crate = parse::parse_crate_from_file(&script, ~[], sess);
|
||||
let mut id = None;
|
||||
let mut vers = None;
|
||||
|
||||
fn load_pkg_attr(mis: ~[@ast::meta_item]) -> (Option<~str>,
|
||||
Option<~str>) {
|
||||
let mut id = None;
|
||||
let mut vers = None;
|
||||
|
||||
for mis.each |a| {
|
||||
match a.node {
|
||||
ast::meta_name_value(v, ast::spanned {
|
||||
node: ast::lit_str(s),
|
||||
span: _}) => {
|
||||
match v {
|
||||
~"id" => id = Some(*s),
|
||||
~"vers" => vers = Some(*s),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
(id, vers)
|
||||
}
|
||||
|
||||
for crate.node.attrs.each |a| {
|
||||
match a.node.value.node {
|
||||
ast::meta_list(v, mis) => {
|
||||
match v {
|
||||
~"pkg" => {
|
||||
let (i, v) = load_pkg_attr(mis);
|
||||
|
||||
id = i;
|
||||
vers = v;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if id.is_none() || vers.is_none() {
|
||||
fail ~"id or vers isn't defined in a pkg attribute in package.rs";
|
||||
}
|
||||
|
||||
let id = id.get();
|
||||
let vers = vers.get();
|
||||
|
||||
PackageScript {
|
||||
id: id,
|
||||
name: util::parse_id(id),
|
||||
vers: util::parse_vers(vers)
|
||||
}
|
||||
}
|
||||
|
||||
fn hash() -> ~str {
|
||||
let hasher = hash::default_state();
|
||||
|
||||
hasher.write_str(self.id + self.vers.to_str());
|
||||
|
||||
self.name + hasher.result_str() + self.vers.to_str()
|
||||
}
|
||||
|
||||
fn work_dir() -> Path {
|
||||
util::root().push(self.hash())
|
||||
}
|
||||
}
|
||||
|
||||
struct Ctx {
|
||||
cmd: ~str,
|
||||
args: ~[~str],
|
||||
cfgs: ~[~str],
|
||||
prefer: bool
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
fn run() {
|
||||
match self.cmd {
|
||||
~"build" => self.build(),
|
||||
~"clean" => self.clean(),
|
||||
~"install" => self.install(),
|
||||
~"prefer" => self.prefer(),
|
||||
~"test" => self.test(),
|
||||
~"uninstall" => self.uninstall(),
|
||||
~"unprefer" => self.unprefer(),
|
||||
_ => fail ~"reached an unhandled command"
|
||||
}
|
||||
}
|
||||
|
||||
fn build() {
|
||||
let script = PackageScript::parse(os::getcwd());
|
||||
|
||||
io::println(fmt!("build: %s (v%s)", script.id, script.vers.to_str()));
|
||||
}
|
||||
|
||||
fn clean() {
|
||||
|
||||
}
|
||||
|
||||
fn install() {
|
||||
|
||||
}
|
||||
|
||||
fn prefer() {
|
||||
|
||||
}
|
||||
|
||||
fn test() {
|
||||
|
||||
}
|
||||
|
||||
fn uninstall() {
|
||||
|
||||
}
|
||||
|
||||
fn unprefer() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let args = os::args();
|
||||
let opts = ~[optflag(~"h"), optflag(~"help")];
|
||||
let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"),
|
||||
getopts::optmulti(~"c"), getopts::optmulti(~"cfg"),
|
||||
getopts::optmulti(~"p"), getopts::optmulti(~"prefer")];
|
||||
let matches = &match getopts::getopts(args, opts) {
|
||||
result::Ok(m) => m,
|
||||
result::Err(f) => {
|
||||
fail fmt!("%s", getopts::fail_str(f));
|
||||
}
|
||||
};
|
||||
let help = opt_present(matches, ~"h") || opt_present(matches, ~"help");
|
||||
let help = getopts::opt_present(matches, ~"h") ||
|
||||
getopts::opt_present(matches, ~"help");
|
||||
let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"),
|
||||
getopts::opt_strs(matches, ~"c"));
|
||||
let prefer = getopts::opt_present(matches, ~"p") ||
|
||||
getopts::opt_present(matches, ~"prefer");
|
||||
let mut args = copy matches.free;
|
||||
|
||||
args.shift();
|
||||
@ -52,12 +199,12 @@ pub fn main() {
|
||||
return usage::general();
|
||||
}
|
||||
|
||||
let cmd = copy args[0];
|
||||
let cmd = args.shift();
|
||||
|
||||
if !is_cmd(cmd) {
|
||||
if !util::is_cmd(cmd) {
|
||||
return usage::general();
|
||||
} else if help {
|
||||
match cmd {
|
||||
return match cmd {
|
||||
~"build" => usage::build(),
|
||||
~"clean" => usage::clean(),
|
||||
~"install" => usage::install(),
|
||||
@ -66,10 +213,15 @@ pub fn main() {
|
||||
~"uninstall" => usage::uninstall(),
|
||||
~"unprefer" => usage::unprefer(),
|
||||
_ => usage::general()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ctx { cmd: cmd, args: args }
|
||||
Ctx {
|
||||
cmd: cmd,
|
||||
args: args,
|
||||
cfgs: cfgs,
|
||||
prefer: prefer
|
||||
}.run();
|
||||
}
|
||||
|
||||
pub use Crate = api::Crate;
|
||||
|
@ -1,6 +1,58 @@
|
||||
use core::*;
|
||||
use rustc::metadata::filesearch;
|
||||
use semver::Version;
|
||||
use std::net::url;
|
||||
|
||||
pub fn root() -> Path {
|
||||
match filesearch::get_rustpkg_root() {
|
||||
result::Ok(path) => path,
|
||||
result::Err(err) => fail err
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_cmd(cmd: ~str) -> bool {
|
||||
let cmds = &[~"build", ~"clean", ~"install", ~"prefer", ~"test",
|
||||
~"uninstall", ~"unprefer"];
|
||||
|
||||
vec::contains(cmds, &cmd)
|
||||
}
|
||||
|
||||
pub fn parse_id(id: ~str) -> ~str {
|
||||
let parts = str::split_char(id, '.');
|
||||
|
||||
for parts.each |&part| {
|
||||
for str::chars(part).each |&char| {
|
||||
if char::is_whitespace(char) {
|
||||
fail ~"could not parse id: contains whitespace";
|
||||
} else if char::is_uppercase(char) {
|
||||
fail ~"could not parse id: should be all lowercase";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.last()
|
||||
}
|
||||
|
||||
pub fn parse_vers(vers: ~str) -> Version {
|
||||
match semver::parse(vers) {
|
||||
Some(vers) => vers,
|
||||
None => fail ~"could not parse version: invalid"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_cmd() {
|
||||
assert is_cmd(~"build");
|
||||
assert is_cmd(~"clean");
|
||||
assert is_cmd(~"install");
|
||||
assert is_cmd(~"prefer");
|
||||
assert is_cmd(~"test");
|
||||
assert is_cmd(~"uninstall");
|
||||
assert is_cmd(~"unprefer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_id() {
|
||||
assert parse_id(~"org.mozilla.servo").get() == ~"servo";
|
||||
assert parse_id(~"org. mozilla.servo 2131").is_err();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user