rustpkg: Finish all commands and declarative logic
This commit is contained in:
parent
321e3c4909
commit
bd28fa4af5
@ -29,6 +29,7 @@ use core::*;
|
||||
use io::{ReaderUtil, WriterUtil};
|
||||
use std::getopts;
|
||||
use std::net::url;
|
||||
use send_map::linear::LinearMap;
|
||||
use rustc::driver::{driver, session};
|
||||
use rustc::metadata::{filesearch};
|
||||
use syntax::{ast, attr, codemap, diagnostic, parse, visit};
|
||||
@ -38,6 +39,8 @@ mod api;
|
||||
mod usage;
|
||||
mod util;
|
||||
|
||||
use util::Package;
|
||||
|
||||
struct PackageScript {
|
||||
id: ~str,
|
||||
name: ~str,
|
||||
@ -167,23 +170,26 @@ impl PackageScript {
|
||||
}
|
||||
|
||||
let id = id.get();
|
||||
let vers = vers.get();
|
||||
let name = match util::parse_name(id) {
|
||||
result::Ok(name) => name,
|
||||
result::Err(err) => return result::Err(err)
|
||||
};
|
||||
let vers = match util::parse_vers(vers.get()) {
|
||||
result::Ok(vers) => vers,
|
||||
result::Err(err) => return result::Err(err)
|
||||
};
|
||||
|
||||
result::Ok(PackageScript {
|
||||
id: id,
|
||||
name: util::parse_id(id),
|
||||
vers: util::parse_vers(vers),
|
||||
name: name,
|
||||
vers: vers,
|
||||
crates: crates,
|
||||
deps: deps
|
||||
})
|
||||
}
|
||||
|
||||
fn hash() -> ~str {
|
||||
let hasher = hash::default_state();
|
||||
|
||||
hasher.write_str(self.id + self.vers.to_str());
|
||||
|
||||
fmt!("%s-%s-%s", self.name, hasher.result_str(), self.vers.to_str())
|
||||
fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()), self.vers.to_str())
|
||||
}
|
||||
|
||||
fn work_dir() -> Path {
|
||||
@ -192,8 +198,8 @@ impl PackageScript {
|
||||
}
|
||||
|
||||
struct Ctx {
|
||||
cfgs: ~[~str],
|
||||
prefer: bool
|
||||
cfg: ast::crate_cfg,
|
||||
mut dep_cache: LinearMap<~str, bool>
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
@ -213,8 +219,10 @@ impl Ctx {
|
||||
|
||||
if parts.len() >= 1 {
|
||||
name = Some(parts[0]);
|
||||
} else if parts.len() >= 2 {
|
||||
vers = Some(parts[1]);
|
||||
|
||||
if parts.len() >= 2 {
|
||||
vers = Some(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
(name, vers)
|
||||
@ -301,7 +309,8 @@ impl Ctx {
|
||||
}
|
||||
|
||||
for script.crates.each |&crate| {
|
||||
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[]);
|
||||
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
|
||||
false, false);
|
||||
|
||||
if !success { break; }
|
||||
}
|
||||
@ -318,15 +327,20 @@ impl Ctx {
|
||||
true
|
||||
}
|
||||
|
||||
fn compile(dir: &Path, crate: &Path, flags: ~[~str]) -> bool {
|
||||
fn compile(dir: &Path, crate: &Path, flags: ~[~str],
|
||||
opt: bool, test: bool) -> bool {
|
||||
util::note(~"compiling " + crate.to_str());
|
||||
|
||||
let lib_dir = dir.push(~"lib");
|
||||
let bin_dir = dir.push(~"bin");
|
||||
let test_dir = dir.push(~"test");
|
||||
let binary = os::args()[0];
|
||||
let options: @session::options = @{
|
||||
binary: binary,
|
||||
addl_lib_search_paths: ~[util::root().push(~"lib")],
|
||||
crate_type: session::unknown_crate,
|
||||
optimize: if opt { session::Aggressive } else { session::No },
|
||||
test: test,
|
||||
.. *session::basic_options()
|
||||
};
|
||||
let input = driver::file_input(*crate);
|
||||
@ -421,19 +435,33 @@ impl Ctx {
|
||||
}
|
||||
};
|
||||
|
||||
if is_bin {
|
||||
let hasher = hash::default_state();
|
||||
if test {
|
||||
util::need_dir(&test_dir);
|
||||
|
||||
outputs = driver::build_output_filenames(input, &Some(test_dir),
|
||||
&None, sess)
|
||||
}
|
||||
else if is_bin {
|
||||
util::need_dir(&bin_dir);
|
||||
hasher.write_str(name + uuid + vers);
|
||||
|
||||
let path = bin_dir.push(fmt!("%s-%s-%s", name, hasher.result_str(), vers));
|
||||
#[cfg(windows)]
|
||||
fn suffix() -> ~str { ~".exe" }
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn suffix() -> ~str { ~"" }
|
||||
|
||||
let path = bin_dir.push(fmt!("%s-%s-%s%s", name,
|
||||
util::hash(name + uuid + vers),
|
||||
vers, suffix()));
|
||||
outputs = driver::build_output_filenames(input, &None, &Some(path), sess);
|
||||
} else {
|
||||
util::need_dir(&lib_dir);
|
||||
|
||||
outputs = driver::build_output_filenames(input, &Some(lib_dir), &None,
|
||||
sess)
|
||||
outputs = driver::build_output_filenames(input, &Some(lib_dir),
|
||||
&None, sess)
|
||||
}
|
||||
|
||||
driver::compile_upto(sess, cfg, input, driver::cu_everything,
|
||||
@ -456,19 +484,14 @@ impl Ctx {
|
||||
util::note(fmt!("cleaning %s v%s (%s)", script.name, script.vers.to_str(),
|
||||
script.id));
|
||||
|
||||
if os::path_is_dir(&dir) {
|
||||
if os::remove_dir(&dir) {
|
||||
util::note(fmt!("cleaned %s v%s", script.name,
|
||||
script.vers.to_str()));
|
||||
} else {
|
||||
util::error(fmt!("cleaning %s v%s failed",
|
||||
script.name, script.vers.to_str()));
|
||||
}
|
||||
} else {
|
||||
util::note(fmt!("cleaned %s v%s", script.name,
|
||||
script.vers.to_str()));
|
||||
if os::path_exists(&dir) {
|
||||
util::remove_dir_r(&dir);
|
||||
util::note(fmt!("removed %s", dir.to_str()));
|
||||
}
|
||||
|
||||
util::note(fmt!("cleaned %s v%s", script.name,
|
||||
script.vers.to_str()));
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@ -482,15 +505,17 @@ impl Ctx {
|
||||
dir = os::getcwd();
|
||||
} else {
|
||||
let url = url.get();
|
||||
let hasher = hash::default_state();
|
||||
let hash = util::hash(if !target.is_none() { url + target.get() } else { url });
|
||||
|
||||
hasher.write_str(url);
|
||||
if self.dep_cache.contains_key(&hash) {
|
||||
util::warn(~"already installed dep this run");
|
||||
|
||||
if !target.is_none() {
|
||||
hasher.write_str(target.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
dir = util::root().push(~"tmp").push(hasher.result_str());
|
||||
self.dep_cache.insert(hash, true);
|
||||
|
||||
dir = util::root().push(~"tmp").push(hash);
|
||||
|
||||
if cache && os::path_exists(&dir) {
|
||||
return true;
|
||||
@ -538,7 +563,8 @@ impl Ctx {
|
||||
}
|
||||
|
||||
for script.crates.each |&crate| {
|
||||
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[]);
|
||||
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
|
||||
true, false);
|
||||
|
||||
if !success { break; }
|
||||
}
|
||||
@ -549,24 +575,37 @@ impl Ctx {
|
||||
return false;
|
||||
}
|
||||
|
||||
let from_bin_dir = dir.push(~"bin");
|
||||
let from_lib_dir = dir.push(~"lib");
|
||||
let from_bin_dir = work_dir.push(~"bin");
|
||||
let from_lib_dir = work_dir.push(~"lib");
|
||||
let to_bin_dir = util::root().push(~"bin");
|
||||
let to_lib_dir = util::root().push(~"lib");
|
||||
let mut bins = ~[];
|
||||
let mut libs = ~[];
|
||||
|
||||
for os::walk_dir(&from_bin_dir) |bin| {
|
||||
let to = to_bin_dir.push_rel(&bin.file_path());
|
||||
|
||||
os::copy_file(bin, &to);
|
||||
bins.push(to.to_str());
|
||||
}
|
||||
|
||||
for os::walk_dir(&from_lib_dir) |lib| {
|
||||
let to = to_lib_dir.push_rel(&lib.file_path());
|
||||
|
||||
os::copy_file(lib, &to);
|
||||
libs.push(to.to_str());
|
||||
}
|
||||
|
||||
let package = Package {
|
||||
id: script.id,
|
||||
vers: script.vers,
|
||||
bins: bins,
|
||||
libs: libs
|
||||
};
|
||||
|
||||
util::note(fmt!("installed %s v%s", script.name,
|
||||
script.vers.to_str()));
|
||||
util::add_pkg(&package);
|
||||
|
||||
true
|
||||
}
|
||||
@ -626,21 +665,7 @@ impl Ctx {
|
||||
util::note(fmt!("fetching from %s using git", url));
|
||||
|
||||
// Git can't clone into a non-empty directory
|
||||
for os::walk_dir(dir) |&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 == *dir { break; }
|
||||
}
|
||||
}
|
||||
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");
|
||||
@ -665,19 +690,190 @@ impl Ctx {
|
||||
true
|
||||
}
|
||||
|
||||
fn prefer(name: ~str, vers: Option<~str>) -> bool {
|
||||
fn prefer(id: ~str, vers: Option<~str>) -> bool {
|
||||
let package = match util::get_pkg(id, vers) {
|
||||
result::Ok(package) => package,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let name = match util::parse_name(package.id) {
|
||||
result::Ok(name) => name,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
util::note(fmt!("preferring %s v%s (%s)", name, package.vers.to_str(),
|
||||
package.id));
|
||||
|
||||
let bin_dir = util::root().push(~"bin");
|
||||
|
||||
for package.bins.each |&bin| {
|
||||
let path = Path(bin);
|
||||
let name = str::split_char(path.file_path().to_str(), '-')[0];
|
||||
let out = bin_dir.push(name);
|
||||
|
||||
util::link_exe(&path, &out);
|
||||
util::note(fmt!("linked %s", out.to_str()));
|
||||
}
|
||||
|
||||
util::note(fmt!("preferred %s v%s", name, package.vers.to_str()));
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn test() -> bool {
|
||||
let dir = os::getcwd();
|
||||
let script = match PackageScript::parse(dir) {
|
||||
result::Ok(script) => script,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let work_dir = script.work_dir();
|
||||
let test_dir = work_dir.push(~"test");
|
||||
let mut success = true;
|
||||
|
||||
util::need_dir(&work_dir);
|
||||
util::note(fmt!("testing %s v%s (%s)", script.name, script.vers.to_str(),
|
||||
script.id));
|
||||
|
||||
if script.deps.len() >= 1 {
|
||||
util::note(~"installing dependencies");
|
||||
|
||||
for script.deps.each |&dep| {
|
||||
let (url, target) = dep;
|
||||
|
||||
success = self.install(Some(url), target, true);
|
||||
|
||||
if !success { break; }
|
||||
}
|
||||
|
||||
|
||||
if !success {
|
||||
util::error(fmt!("testing %s v%s failed: a dep wasn't installed",
|
||||
script.name, script.vers.to_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
util::note(~"installed dependencies");
|
||||
}
|
||||
|
||||
for script.crates.each |&crate| {
|
||||
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
|
||||
false, true);
|
||||
|
||||
if !success { break; }
|
||||
}
|
||||
|
||||
if !success {
|
||||
util::error(fmt!("testing %s v%s failed: a crate failed to compile",
|
||||
script.name, script.vers.to_str()));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for os::walk_dir(&test_dir) |test| {
|
||||
util::note(fmt!("running %s", test.to_str()));
|
||||
|
||||
let status = run::run_program(test.to_str(), ~[]);
|
||||
|
||||
if status != 0 {
|
||||
os::set_exit_status(status);
|
||||
}
|
||||
}
|
||||
|
||||
util::note(fmt!("tested %s v%s", script.name, script.vers.to_str()));
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn uninstall(name: ~str, vers: Option<~str>) -> bool {
|
||||
fn uninstall(id: ~str, vers: Option<~str>) -> bool {
|
||||
let package = match util::get_pkg(id, vers) {
|
||||
result::Ok(package) => package,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let name = match util::parse_name(package.id) {
|
||||
result::Ok(name) => name,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
util::note(fmt!("uninstalling %s v%s (%s)", name, package.vers.to_str(),
|
||||
package.id));
|
||||
|
||||
for vec::append(package.bins, package.libs).each |&file| {
|
||||
let path = Path(file);
|
||||
|
||||
if os::path_exists(&path) {
|
||||
if os::remove_file(&path) {
|
||||
util::note(fmt!("removed %s", path.to_str()));
|
||||
} else {
|
||||
util::error(fmt!("could not remove %s", path.to_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::note(fmt!("uninstalled %s v%s", name, package.vers.to_str()));
|
||||
util::remove_pkg(&package);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn unprefer(name: ~str, vers: Option<~str>) -> bool {
|
||||
fn unprefer(id: ~str, vers: Option<~str>) -> bool {
|
||||
let package = match util::get_pkg(id, vers) {
|
||||
result::Ok(package) => package,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let name = match util::parse_name(package.id) {
|
||||
result::Ok(name) => name,
|
||||
result::Err(err) => {
|
||||
util::error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
util::note(fmt!("unpreferring %s v%s (%s)", name, package.vers.to_str(),
|
||||
package.id));
|
||||
|
||||
let bin_dir = util::root().push(~"bin");
|
||||
|
||||
for package.bins.each |&bin| {
|
||||
let path = Path(bin);
|
||||
let name = str::split_char(path.file_path().to_str(), '-')[0];
|
||||
let out = bin_dir.push(name);
|
||||
|
||||
if os::path_exists(&out) {
|
||||
if os::remove_file(&out) {
|
||||
util::note(fmt!("unlinked %s", out.to_str()));
|
||||
} else {
|
||||
util::error(fmt!("could not unlink %s", out.to_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
util::note(fmt!("unpreferred %s v%s", name, package.vers.to_str()));
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@ -685,20 +881,19 @@ impl Ctx {
|
||||
pub fn main() {
|
||||
let args = os::args();
|
||||
let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"),
|
||||
getopts::optmulti(~"c"), getopts::optmulti(~"cfg"),
|
||||
getopts::optmulti(~"p"), getopts::optmulti(~"prefer")];
|
||||
getopts::optmulti(~"c"), getopts::optmulti(~"cfg")];
|
||||
let matches = &match getopts::getopts(args, opts) {
|
||||
result::Ok(m) => m,
|
||||
result::Err(f) => {
|
||||
fail fmt!("%s", getopts::fail_str(f));
|
||||
util::error(fmt!("%s", getopts::fail_str(f)));
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
let help = getopts::opt_present(matches, ~"h") ||
|
||||
getopts::opt_present(matches, ~"help");
|
||||
let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"),
|
||||
let cfg = 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();
|
||||
@ -724,9 +919,15 @@ pub fn main() {
|
||||
};
|
||||
}
|
||||
|
||||
let mut cfg_specs = ~[];
|
||||
|
||||
for cfg.each |s| {
|
||||
cfg_specs.push(attr::mk_word_item(/*bad*/copy *s));
|
||||
}
|
||||
|
||||
Ctx {
|
||||
cfgs: cfgs,
|
||||
prefer: prefer
|
||||
cfg: cfg_specs,
|
||||
mut dep_cache: LinearMap()
|
||||
}.run(cmd, args);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use core::io;
|
||||
|
||||
|
||||
pub fn general() {
|
||||
io::println(~"Usage: rustpkg [options] <cmd> [args..]
|
||||
|
||||
@ -46,29 +46,22 @@ Examples:
|
||||
rustpkg install http://rust-lang.org/servo-0.1.2.tar.gz
|
||||
|
||||
Options:
|
||||
-c, --cfg Pass a cfg flag to the package script
|
||||
-p, --prefer Prefer the package after installing
|
||||
(see `rustpkg prefer -h`)");
|
||||
-c, --cfg Pass a cfg flag to the package script");
|
||||
}
|
||||
|
||||
pub fn uninstall() {
|
||||
io::println(~"rustpkg uninstall <name>[@version]
|
||||
io::println(~"rustpkg uninstall <id|name>[@version]
|
||||
|
||||
Remove a package by name and/or version. If version is omitted then all
|
||||
versions of the package will be removed. If the package[s] is/are depended
|
||||
on by another package then they cannot be removed. If the package is preferred
|
||||
(see `rustpkg prefer -h`), it will attempt to prefer the next latest
|
||||
version of the package if another version is installed, otherwise it'll remove
|
||||
the symlink.");
|
||||
Remove a package by id or name and optionally version. If the package(s) is/are depended
|
||||
on by another package then they cannot be removed.");
|
||||
}
|
||||
|
||||
pub fn prefer() {
|
||||
io::println(~"rustpkg [options..] prefer <name>[@version]
|
||||
io::println(~"rustpkg [options..] prefer <id|name>[@version]
|
||||
|
||||
By default all binaries are given a unique name so that multiple versions can
|
||||
coexist. The prefer command will symlink the uniquely named binary to
|
||||
the binary directory under its bare name. The user will need to confirm
|
||||
if the symlink will overwrite another. If version is not supplied, the latest
|
||||
the binary directory under its bare name. If version is not supplied, the latest
|
||||
version of the package will be preferred.
|
||||
|
||||
Example:
|
||||
@ -82,10 +75,12 @@ Example:
|
||||
}
|
||||
|
||||
pub fn unprefer() {
|
||||
io::println(~"rustpkg [options..] unprefer <name>
|
||||
io::println(~"rustpkg [options..] unprefer <id|name>[@version]
|
||||
|
||||
Remove all symlinks from the store to the binary directory for a package
|
||||
name. See `rustpkg prefer -h` for more information.");
|
||||
name and optionally version. If version is not supplied, the latest version
|
||||
of the package will be unpreferred. See `rustpkg prefer -h` for more
|
||||
information.");
|
||||
}
|
||||
|
||||
pub fn test() {
|
||||
|
@ -1,7 +1,15 @@
|
||||
use core::*;
|
||||
use send_map::linear::LinearMap;
|
||||
use rustc::metadata::filesearch;
|
||||
use semver::Version;
|
||||
use std::term;
|
||||
use std::{json, term, sort};
|
||||
|
||||
pub struct Package {
|
||||
id: ~str,
|
||||
vers: Version,
|
||||
bins: ~[~str],
|
||||
libs: ~[~str],
|
||||
}
|
||||
|
||||
pub fn root() -> Path {
|
||||
match filesearch::get_rustpkg_root() {
|
||||
@ -17,26 +25,26 @@ pub fn is_cmd(cmd: ~str) -> bool {
|
||||
vec::contains(cmds, &cmd)
|
||||
}
|
||||
|
||||
pub fn parse_id(id: ~str) -> ~str {
|
||||
pub fn parse_name(id: ~str) -> result::Result<~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";
|
||||
return result::Err(~"could not parse id: contains whitespace");
|
||||
} else if char::is_uppercase(char) {
|
||||
fail ~"could not parse id: should be all lowercase";
|
||||
return result::Err(~"could not parse id: should be all lowercase");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parts.last()
|
||||
result::Ok(parts.last())
|
||||
}
|
||||
|
||||
pub fn parse_vers(vers: ~str) -> Version {
|
||||
pub fn parse_vers(vers: ~str) -> result::Result<Version, ~str> {
|
||||
match semver::parse(vers) {
|
||||
Some(vers) => vers,
|
||||
None => fail ~"could not parse version: invalid"
|
||||
Some(vers) => result::Ok(vers),
|
||||
None => result::Err(~"could not parse version: invalid")
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +88,13 @@ pub fn error(msg: ~str) {
|
||||
else { out.write_line(~"error: " + msg); }
|
||||
}
|
||||
|
||||
pub fn hash(data: ~str) -> ~str {
|
||||
let hasher = hash::default_state();
|
||||
|
||||
hasher.write_str(data);
|
||||
hasher.result_str()
|
||||
}
|
||||
|
||||
pub fn temp_change_dir<T>(dir: &Path, cb: fn() -> T) {
|
||||
let cwd = os::getcwd();
|
||||
|
||||
@ -88,6 +103,342 @@ pub fn temp_change_dir<T>(dir: &Path, cb: fn() -> T) {
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
|
||||
for packages.each |&package| {
|
||||
match package {
|
||||
json::Object(map) => {
|
||||
let mut has_id = false;
|
||||
|
||||
match map.get(&~"id") {
|
||||
json::String(str) => {
|
||||
if pkg.id == str {
|
||||
has_id = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match map.get(&~"vers") {
|
||||
json::String(str) => {
|
||||
if pkg.vers.to_str() == str {
|
||||
return packages;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut map = ~LinearMap();
|
||||
|
||||
map.insert(~"id", json::String(pkg.id));
|
||||
map.insert(~"vers", json::String(pkg.vers.to_str()));
|
||||
map.insert(~"bins", json::List(do pkg.bins.map |&bin| {
|
||||
json::String(bin)
|
||||
}));
|
||||
map.insert(~"libs", json::List(do pkg.libs.map |&lib| {
|
||||
json::String(lib)
|
||||
}));
|
||||
|
||||
vec::append(packages, ~[json::Object(map)])
|
||||
}
|
||||
|
||||
fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
|
||||
do packages.filter_map |&package| {
|
||||
match package {
|
||||
json::Object(map) => {
|
||||
let mut has_id = false;
|
||||
|
||||
match map.get(&~"id") {
|
||||
json::String(str) => {
|
||||
if pkg.id == str {
|
||||
has_id = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match map.get(&~"vers") {
|
||||
json::String(str) => {
|
||||
if pkg.vers.to_str() == str { None }
|
||||
else { Some(package) }
|
||||
}
|
||||
_ => { Some(package) }
|
||||
}
|
||||
}
|
||||
_ => { Some(package) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_pkgs() -> result::Result<~[json::Json], ~str> {
|
||||
let root = root();
|
||||
let db = root.push(~"db.json");
|
||||
let db_lock = root.push(~"db.json.lck");
|
||||
|
||||
wait_for_lock(&db_lock);
|
||||
touch(&db_lock);
|
||||
|
||||
let packages = if os::path_exists(&db) {
|
||||
match io::read_whole_file_str(&db) {
|
||||
result::Ok(str) => {
|
||||
match json::from_str(str) {
|
||||
result::Ok(json) => {
|
||||
match json {
|
||||
json::List(list) => list,
|
||||
_ => {
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
return result::Err(~"package db's json is not a list");
|
||||
}
|
||||
}
|
||||
}
|
||||
result::Err(err) => {
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
return result::Err(fmt!("failed to parse package db: %s", err.to_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
result::Err(err) => {
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
return result::Err(fmt!("failed to read package db: %s", err));
|
||||
}
|
||||
}
|
||||
} else { ~[] };
|
||||
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
result::Ok(packages)
|
||||
}
|
||||
|
||||
pub fn get_pkg(id: ~str, vers: Option<~str>) -> result::Result<Package, ~str> {
|
||||
let name = match parse_name(id) {
|
||||
result::Ok(name) => name,
|
||||
result::Err(err) => return result::Err(err)
|
||||
};
|
||||
let packages = match load_pkgs() {
|
||||
result::Ok(packages) => packages,
|
||||
result::Err(err) => return result::Err(err)
|
||||
};
|
||||
let mut sel = None;
|
||||
let mut possibs = ~[];
|
||||
let mut err = None;
|
||||
|
||||
for packages.each |&package| {
|
||||
match package {
|
||||
json::Object(map) => {
|
||||
let pid = match map.get(&~"id") {
|
||||
json::String(str) => str,
|
||||
_ => loop
|
||||
};
|
||||
let pname = match parse_name(pid) {
|
||||
result::Ok(pname) => pname,
|
||||
result::Err(perr) => {
|
||||
err = Some(perr);
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
let pvers = match map.get(&~"vers") {
|
||||
json::String(str) => str,
|
||||
_ => loop
|
||||
};
|
||||
if pid == id || pname == name {
|
||||
let bins = match map.get(&~"bins") {
|
||||
json::List(list) => {
|
||||
do list.map |&bin| {
|
||||
match bin {
|
||||
json::String(str) => str,
|
||||
_ => ~""
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => ~[]
|
||||
};
|
||||
let libs = match map.get(&~"libs") {
|
||||
json::List(list) => {
|
||||
do list.map |&lib| {
|
||||
match lib {
|
||||
json::String(str) => str,
|
||||
_ => ~""
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => ~[]
|
||||
};
|
||||
let package = Package {
|
||||
id: pid,
|
||||
vers: match parse_vers(pvers) {
|
||||
result::Ok(vers) => vers,
|
||||
result::Err(verr) => {
|
||||
err = Some(verr);
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
bins: bins,
|
||||
libs: libs
|
||||
};
|
||||
|
||||
if !vers.is_none() && vers.get() == pvers {
|
||||
sel = Some(package);
|
||||
}
|
||||
else {
|
||||
possibs.push(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !err.is_none() {
|
||||
return result::Err(err.get());
|
||||
}
|
||||
if !sel.is_none() {
|
||||
return result::Ok(sel.get());
|
||||
}
|
||||
if !vers.is_none() || possibs.len() < 1 {
|
||||
return result::Err(~"package not found");
|
||||
}
|
||||
|
||||
result::Ok(sort::merge_sort(possibs, |v1, v2| {
|
||||
v1.vers <= v2.vers
|
||||
}).last())
|
||||
}
|
||||
|
||||
pub fn add_pkg(pkg: &Package) -> bool {
|
||||
let root = root();
|
||||
let db = root.push(~"db.json");
|
||||
let db_lock = root.push(~"db.json.lck");
|
||||
let packages = match load_pkgs() {
|
||||
result::Ok(packages) => packages,
|
||||
result::Err(err) => {
|
||||
error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
wait_for_lock(&db_lock);
|
||||
touch(&db_lock);
|
||||
os::remove_file(&db);
|
||||
|
||||
match io::mk_file_writer(&db, ~[io::Create]) {
|
||||
result::Ok(writer) => {
|
||||
writer.write_line(json::to_pretty_str(&json::List(_add_pkg(packages, pkg))));
|
||||
}
|
||||
result::Err(err) => {
|
||||
error(fmt!("failed to dump package db: %s", err));
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn remove_pkg(pkg: &Package) -> bool {
|
||||
let root = root();
|
||||
let db = root.push(~"db.json");
|
||||
let db_lock = root.push(~"db.json.lck");
|
||||
let packages = match load_pkgs() {
|
||||
result::Ok(packages) => packages,
|
||||
result::Err(err) => {
|
||||
error(err);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
wait_for_lock(&db_lock);
|
||||
touch(&db_lock);
|
||||
os::remove_file(&db);
|
||||
|
||||
match io::mk_file_writer(&db, ~[io::Create]) {
|
||||
result::Ok(writer) => {
|
||||
writer.write_line(json::to_pretty_str(&json::List(_rm_pkg(packages, pkg))));
|
||||
}
|
||||
result::Err(err) => {
|
||||
error(fmt!("failed to dump package db: %s", err));
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
os::remove_file(&db_lock);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn link_exe(_src: &Path, _dest: &Path) -> bool{
|
||||
/* FIXME: Investigate how to do this on win32
|
||||
Node wraps symlinks by having a .bat,
|
||||
but that won't work with minGW. */
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn link_exe(src: &Path, dest: &Path) -> bool unsafe {
|
||||
do str::as_c_str(src.to_str()) |src_buf| {
|
||||
do str::as_c_str(dest.to_str()) |dest_buf| {
|
||||
libc::link(src_buf, dest_buf) == 0 as libc::c_int &&
|
||||
libc::chmod(dest_buf, 755) == 0 as libc::c_int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_cmd() {
|
||||
assert is_cmd(~"build");
|
||||
@ -100,7 +451,7 @@ fn test_is_cmd() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_id() {
|
||||
assert parse_id(~"org.mozilla.servo").get() == ~"servo";
|
||||
assert parse_id(~"org. mozilla.servo 2131").is_err();
|
||||
fn test_parse_name() {
|
||||
assert parse_name(~"org.mozilla.servo").get() == ~"servo";
|
||||
assert parse_name(~"org. mozilla.servo 2131").is_err();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user