diff --git a/AUTHORS.txt b/AUTHORS.txt index 628fa4c86da00..92903a26093f9 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -161,4 +161,4 @@ Wade Mealing William Ting Yasuhiro Fujii Youngsoo Son -Zack Corr +Zack Corr diff --git a/Makefile.in b/Makefile.in index e476ac1dba2d1..cf6fcd42021ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -131,7 +131,7 @@ CFG_STDLIB :=$(call CFG_LIB_NAME,std) CFG_LIBRUSTC :=$(call CFG_LIB_NAME,rustc) CFG_LIBSYNTAX :=$(call CFG_LIB_NAME,syntax) CFG_LIBFUZZER :=$(call CFG_LIB_NAME,fuzzer) -CFG_LIBCARGO :=$(call CFG_LIB_NAME,cargo) +CFG_LIBRUSTPKG :=$(call CFG_LIB_NAME,rustpkg) CFG_LIBRUSTDOC :=$(call CFG_LIB_NAME,rustdoc) CFG_LIBRUSTI :=$(call CFG_LIB_NAME,rusti) @@ -140,7 +140,7 @@ CORELIB_GLOB :=$(call CFG_LIB_GLOB,core) LIBRUSTC_GLOB :=$(call CFG_LIB_GLOB,rustc) LIBSYNTAX_GLOB :=$(call CFG_LIB_GLOB,syntax) LIBFUZZER_GLOB :=$(call CFG_LIB_GLOB,fuzzer) -LIBCARGO_GLOB :=$(call CFG_LIB_GLOB,cargo) +LIBRUSTPKG_GLOB :=$(call CFG_LIB_GLOB,rustpkg) LIBRUSTDOC_GLOB :=$(call CFG_LIB_GLOB,rustdoc) LIBRUSTI_GLOB :=$(call CFG_LIB_GLOB,rusti) STDLIB_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,std) @@ -148,7 +148,7 @@ CORELIB_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,core) LIBRUSTC_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustc) LIBSYNTAX_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,syntax) LIBFUZZER_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,fuzzer) -LIBCARGO_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,cargo) +LIBRUSTPKG_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustpkg) LIBRUSTDOC_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustdoc) LIBRUSTI_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rusti) @@ -371,11 +371,11 @@ SREQ$(1)_T_$(2)_H_$(3) = \ CSREQ$(1)_T_$(2)_H_$(3) = \ $$(TSREQ$(1)_T_$(2)_H_$(3)) \ $$(HBIN$(1)_H_$(3))/fuzzer$$(X) \ - $$(HBIN$(1)_H_$(3))/cargo$$(X) \ + $$(HBIN$(1)_H_$(3))/rustpkg$$(X) \ $$(HBIN$(1)_H_$(3))/rustdoc$$(X) \ $$(HBIN$(1)_H_$(3))/rusti$$(X) \ $$(HLIB$(1)_H_$(3))/$$(CFG_LIBFUZZER) \ - $$(HLIB$(1)_H_$(3))/$$(CFG_LIBCARGO) \ + $$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTPKG) \ $$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTDOC) \ $$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTI) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_CORELIB) \ @@ -383,7 +383,7 @@ CSREQ$(1)_T_$(2)_H_$(3) = \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBFUZZER) \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBCARGO) \ + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTPKG) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTDOC) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTI) diff --git a/README.md b/README.md index 27b63c1080bd4..a34f8814f020d 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ for more information on them. When complete, `make install` will place several programs into `/usr/local/bin`: `rustc`, the Rust compiler; `rustdoc`, the -API-documentation tool, and `cargo`, the Rust package manager. +API-documentation tool, and `rustpkg`, the Rust package manager and build system. [wiki-start]: https://github.com/mozilla/rust/wiki/Note-getting-started-developing-Rust [tarball]: http://static.rust-lang.org/dist/rust-0.5.tar.gz diff --git a/mk/clean.mk b/mk/clean.mk index 57c14b0afc8a2..3b35071c818e9 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -64,12 +64,12 @@ define CLEAN_HOST_STAGE_N clean$(1)_H_$(2): $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustc$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/fuzzer$(X) - $(Q)rm -f $$(HBIN$(1)_H_$(2))/cargo$(X) + $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustpkg$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/serializer$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc$(X) $(Q)rm -f $$(HBIN$(1)_H_$(2))/rusti$(X) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBFUZZER) - $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBCARGO) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTPKG) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOC) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUNTIME) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_CORELIB) @@ -82,7 +82,7 @@ clean$(1)_H_$(2): $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTC_GLOB) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBSYNTAX_GLOB) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBFUZZER_GLOB) - $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBCARGO_GLOB) + $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTI_GLOB) $(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM) @@ -99,11 +99,11 @@ define CLEAN_TARGET_STAGE_N clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/fuzzer$(X) - $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/cargo$(X) + $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/serializer$(X) $(Q)rm -f $$(TBIN$(1)_T_$(2)_H_$(3))/rustdoc$(X) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBFUZZER) - $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBCARGO) + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTPKG) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTDOC) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUNTIME) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_CORELIB) @@ -115,7 +115,7 @@ clean$(1)_T_$(2)_H_$(3): $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTC_GLOB) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBSYNTAX_GLOB) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBFUZZER_GLOB) - $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBCARGO_GLOB) + $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTPKG_GLOB) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(LIBRUSTDOC_GLOB) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_RUSTLLVM) $(Q)rm -f $$(TLIB$(1)_T_$(2)_H_$(3))/libstd.rlib diff --git a/mk/dist.mk b/mk/dist.mk index dd9b201170781..5b6740461e6e5 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -25,7 +25,7 @@ PKG_FILES := \ $(addprefix $(S)src/, \ README.txt \ driver \ - libcargo \ + librustpkg \ librusti \ librustc \ compiletest \ diff --git a/mk/install.mk b/mk/install.mk index 555b28e96976b..7f3ec816e0206 100644 --- a/mk/install.mk +++ b/mk/install.mk @@ -55,7 +55,7 @@ install-target-$(1)-host-$(2): $$(CSREQ$$(ISTAGE)_T_$(1)_H_$(2)) $$(Q)$$(call INSTALL_LIB, \ $$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBSYNTAX_GLOB)) $$(Q)$$(call INSTALL_LIB, \ - $$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBCARGO_GLOB)) + $$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBRUSTPKG_GLOB)) $$(Q)$$(call INSTALL_LIB, \ $$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBRUSTDOC_GLOB)) $$(Q)$$(call INSTALL_LIB, \ @@ -87,11 +87,11 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_HOST_TRIPLE)_H_$(CFG_HOST_TRIPLE)) $(Q)mkdir -p $(PREFIX_LIB) $(Q)mkdir -p $(PREFIX_ROOT)/share/man/man1 $(Q)$(call INSTALL,$(HB2),$(PHB),rustc$(X)) - $(Q)$(call INSTALL,$(HB2),$(PHB),cargo$(X)) + $(Q)$(call INSTALL,$(HB2),$(PHB),rustpkg$(X)) $(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X)) $(Q)$(call INSTALL,$(HB2),$(PHB),rusti$(X)) $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTC)) - $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBCARGO)) + $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTPKG)) $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTDOC)) $(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTI)) $(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(CORELIB_GLOB)) @@ -112,11 +112,11 @@ HOST_LIB_FROM_HL_GLOB = \ uninstall: $(Q)rm -f $(PHB)/rustc$(X) - $(Q)rm -f $(PHB)/cargo$(X) + $(Q)rm -f $(PHB)/rustpkg$(X) $(Q)rm -f $(PHB)/rusti$(X) $(Q)rm -f $(PHB)/rustdoc$(X) $(Q)rm -f $(PHL)/$(CFG_RUSTLLVM) - $(Q)rm -f $(PHL)/$(CFG_LIBCARGO) + $(Q)rm -f $(PHL)/$(CFG_LIBRUSTPKG) $(Q)rm -f $(PHL)/$(CFG_LIBRUSTC) $(Q)rm -f $(PHL)/$(CFG_LIBRUSTDOC) $(Q)rm -f $(PHL)/$(CFG_LIBRUSTI) @@ -126,7 +126,7 @@ uninstall: $(call HOST_LIB_FROM_HL_GLOB,$(STDLIB_GLOB)) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTC_GLOB)) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBSYNTAX_GLOB)) \ - $(call HOST_LIB_FROM_HL_GLOB,$(LIBCARGO_GLOB)) \ + $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTPKG_GLOB)) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTDOC_GLOB)) \ $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTI_GLOB)) \ ; \ diff --git a/mk/pp.mk b/mk/pp.mk index 4d8baa2ea32c2..772365b105819 100644 --- a/mk/pp.mk +++ b/mk/pp.mk @@ -18,7 +18,7 @@ else $(wildcard $(S)src/test/*/*.rs \ $(S)src/test/*/*/*.rs) \ $(wildcard $(S)src/fuzzer/*.rs) \ - $(wildcard $(S)src/cargo/*.rs) \ + $(wildcard $(S)src/rustpkg/*.rs) \ $(wildcard $(S)src/rusti/*.rs) PP_INPUTS_FILTERED = $(shell echo $(PP_INPUTS) | xargs grep -L \ diff --git a/mk/tests.mk b/mk/tests.mk index abe9ba60ecda4..8bcf5d5ad10ca 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -14,7 +14,7 @@ ###################################################################### # The names of crates that must be tested -TEST_CRATES = core std syntax rustc rustdoc rusti cargo +TEST_CRATES = core std syntax rustc rustdoc rusti rustpkg # Markdown files under doc/ that should have their code extracted and run DOC_TEST_NAMES = tutorial tutorial-ffi tutorial-macros tutorial-borrowed-ptr tutorial-tasks rust @@ -229,8 +229,8 @@ $(3)/test/rustctest.stage$(1)-$(2)$$(X): \ @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test -$(3)/test/cargotest.stage$(1)-$(2)$$(X): \ - $$(CARGO_LIB) $$(CARGO_INPUTS) \ +$(3)/test/rustpkgtest.stage$(1)-$(2)$$(X): \ + $$(RUSTPKG_LIB) $$(RUSTPKG_INPUTS) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test diff --git a/mk/tools.mk b/mk/tools.mk index 31956eda24686..1554d760a49cc 100644 --- a/mk/tools.mk +++ b/mk/tools.mk @@ -14,13 +14,13 @@ FUZZER_LIB := $(S)src/libfuzzer/fuzzer.rc FUZZER_INPUTS := $(wildcard $(addprefix $(S)src/libfuzzer/, *.rs)) -# The test runner that runs the cfail/rfail/rpass and bench tests +# The test runner that runs the cfail/rfail/rpass and bxench tests COMPILETEST_CRATE := $(S)src/compiletest/compiletest.rc COMPILETEST_INPUTS := $(wildcard $(S)src/compiletest/*rs) -# Cargo, the package manager -CARGO_LIB := $(S)src/libcargo/cargo.rc -CARGO_INPUTS := $(wildcard $(S)src/libcargo/*rs) +# Rustpkg, the package manager and build system +RUSTPKG_LIB := $(S)src/librustpkg/rustpkg.rc +RUSTPKG_INPUTS := $(wildcard $(S)src/librustpkg/*rs) # Rustdoc, the documentation tool RUSTDOC_LIB := $(S)src/librustdoc/rustdoc.rc @@ -57,8 +57,8 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/compiletest$$(X): \ @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< -$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBCARGO): \ - $$(CARGO_LIB) $$(CARGO_INPUTS) \ +$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTPKG): \ + $$(RUSTPKG_LIB) $$(RUSTPKG_INPUTS) \ $$(TSREQ$(1)_T_$(4)_H_$(3)) \ $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_CORELIB) \ $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_STDLIB) \ @@ -66,11 +66,11 @@ $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBCARGO): \ @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< && touch $$@ -$$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X): \ +$$(TBIN$(1)_T_$(4)_H_$(3))/rustpkg$$(X): \ $$(DRIVER_CRATE) \ - $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBCARGO) + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTPKG) @$$(call E, compile_and_link: $$@) - $$(STAGE$(1)_T_$(4)_H_$(3)) --cfg cargo -o $$@ $$< + $$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rustpkg -o $$@ $$< $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTDOC): \ $$(RUSTDOC_LIB) $$(RUSTDOC_INPUTS) \ @@ -134,19 +134,19 @@ $$(HBIN$(2)_H_$(4))/compiletest$$(X): \ $$(Q)cp $$< $$@ -$$(HLIB$(2)_H_$(4))/$$(CFG_LIBCARGO): \ - $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBCARGO) \ +$$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUSTPKG): \ + $$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTPKG) \ $$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUSTC) \ $$(HSREQ$(2)_H_$(4)) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ - $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBCARGO_GLOB) \ - $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBCARGO_DSYM_GLOB)) \ + $$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTPKG_GLOB) \ + $$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUSTPKG_DSYM_GLOB)) \ $$(HLIB$(2)_H_$(4)) -$$(HBIN$(2)_H_$(4))/cargo$$(X): \ - $$(TBIN$(1)_T_$(4)_H_$(3))/cargo$$(X) \ - $$(HLIB$(2)_H_$(4))/$$(CFG_LIBCARGO) \ +$$(HBIN$(2)_H_$(4))/rustpkg$$(X): \ + $$(TBIN$(1)_T_$(4)_H_$(3))/rustpkg$$(X) \ + $$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUSTPKG) \ $$(HSREQ$(2)_H_$(4)) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ diff --git a/src/README.txt b/src/README.txt index c8029098cfaf4..1b06c4259fc6f 100644 --- a/src/README.txt +++ b/src/README.txt @@ -29,7 +29,7 @@ test/auxiliary - Dependencies of tests compiletest/ The test runner -libcargo/ The package manager +librustpkg/ The package manager and build system librusti/ The JIT REPL diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 0c1cc566fe2ec..b2c4f69d3029b 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -11,8 +11,8 @@ #[no_core]; extern mod core(vers = "0.6"); -#[cfg(cargo)] -extern mod this(name = "cargo", vers = "0.6"); +#[cfg(rustpkg)] +extern mod this(name = "rustpkg", vers = "0.6"); #[cfg(fuzzer)] extern mod this(name = "fuzzer", vers = "0.6"); diff --git a/src/libcargo/cargo.rc b/src/libcargo/cargo.rc deleted file mode 100644 index dceeb96800fd1..0000000000000 --- a/src/libcargo/cargo.rc +++ /dev/null @@ -1,1981 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -// cargo.rs - Rust package manager - -// Local Variables: -// fill-column: 78; -// indent-tabs-mode: nil -// c-basic-offset: 4 -// buffer-file-coding-system: utf-8-unix -// End: - -#[link(name = "cargo", - vers = "0.6", - uuid = "9ff87a04-8fed-4295-9ff8-f99bb802650b", - url = "https://github.com/mozilla/rust/tree/master/src/cargo")]; - -#[crate_type = "lib"]; - -#[no_core]; - -#[legacy_modes]; - -#[allow(vecs_implicitly_copyable, - non_implicitly_copyable_typarams)]; -#[allow(non_camel_case_types)]; -#[allow(deprecated_mode)]; -#[allow(deprecated_pattern)]; -#[allow(deprecated_self)]; - -extern mod core(vers = "0.6"); -extern mod std(vers = "0.6"); -extern mod rustc(vers = "0.6"); -extern mod syntax(vers = "0.6"); - -mod pgp; - -use rustc::metadata::filesearch::{get_cargo_root, get_cargo_root_nearest}; -use rustc::metadata::filesearch::{get_cargo_sysroot, libdir}; - -use core::*; - -use core::dvec::DVec; -use core::io::WriterUtil; -use core::result::{Ok, Err}; -use core::hashmap::linear::LinearMap; -use std::getopts::{optflag, optopt, opt_present}; -use std::oldmap::HashMap; -use std::{oldmap, json, tempfile, term, sort, getopts}; -use syntax::codemap::span; -use syntax::diagnostic::span_handler; -use syntax::diagnostic; -use syntax::{ast, codemap, parse, visit, attr}; - -pub struct Package { - name: ~str, - uuid: ~str, - url: ~str, - method: ~str, - description: ~str, - reference: Option<~str>, - tags: ~[~str], - versions: ~[(~str, ~str)] -} - -pub impl cmp::Ord for Package { - pure fn lt(&self, other: &Package) -> bool { - if (*self).name.lt(&(*other).name) { return true; } - if (*other).name.lt(&(*self).name) { return false; } - if (*self).uuid.lt(&(*other).uuid) { return true; } - if (*other).uuid.lt(&(*self).uuid) { return false; } - if (*self).url.lt(&(*other).url) { return true; } - if (*other).url.lt(&(*self).url) { return false; } - if (*self).method.lt(&(*other).method) { return true; } - if (*other).method.lt(&(*self).method) { return false; } - if (*self).description.lt(&(*other).description) { return true; } - if (*other).description.lt(&(*self).description) { return false; } - if (*self).tags.lt(&(*other).tags) { return true; } - if (*other).tags.lt(&(*self).tags) { return false; } - if (*self).versions.lt(&(*other).versions) { return true; } - return false; - } - pure fn le(&self, other: &Package) -> bool { !(*other).lt(&(*self)) } - pure fn ge(&self, other: &Package) -> bool { !(*self).lt(other) } - pure fn gt(&self, other: &Package) -> bool { (*other).lt(&(*self)) } -} - -pub struct Source { - name: ~str, - mut url: ~str, - mut method: ~str, - mut key: Option<~str>, - mut keyfp: Option<~str>, - packages: DVec -} - -pub struct Cargo { - pgp: bool, - root: Path, - installdir: Path, - bindir: Path, - libdir: Path, - workdir: Path, - sourcedir: Path, - sources: oldmap::HashMap<~str, @Source>, - mut current_install: ~str, - dep_cache: oldmap::HashMap<~str, bool>, - opts: Options -} - -pub struct Crate { - name: ~str, - vers: ~str, - uuid: ~str, - desc: Option<~str>, - sigs: Option<~str>, - crate_type: Option<~str>, - deps: ~[~str] -} - -pub struct Options { - test: bool, - mode: Mode, - free: ~[~str], - help: bool, -} - -#[deriving_eq] -pub enum Mode { SystemMode, UserMode, LocalMode } - -pub fn opts() -> ~[getopts::Opt] { - ~[optflag(~"g"), optflag(~"G"), optflag(~"test"), - optflag(~"h"), optflag(~"help")] -} - -pub fn info(msg: ~str) { - let out = io::stdout(); - - if term::color_supported() { - term::fg(out, term::color_green); - out.write_str(~"info: "); - term::reset(out); - out.write_line(msg); - } else { out.write_line(~"info: " + msg); } -} - -pub fn warn(msg: ~str) { - let out = io::stdout(); - - if term::color_supported() { - term::fg(out, term::color_yellow); - out.write_str(~"warning: "); - term::reset(out); - out.write_line(msg); - }else { out.write_line(~"warning: " + msg); } -} - -pub fn error(msg: ~str) { - let out = io::stdout(); - - if term::color_supported() { - term::fg(out, term::color_red); - out.write_str(~"error: "); - term::reset(out); - out.write_line(msg); - } - else { out.write_line(~"error: " + msg); } -} - -pub fn is_uuid(id: ~str) -> bool { - let parts = str::split_str(id, ~"-"); - if vec::len(parts) == 5u { - let mut correct = 0u; - for vec::eachi(parts) |i, part| { - fn is_hex_digit(+ch: char) -> bool { - ('0' <= ch && ch <= '9') || - ('a' <= ch && ch <= 'f') || - ('A' <= ch && ch <= 'F') - } - - if !part.all(is_hex_digit) { - return false; - } - - match i { - 0u => { - if part.len() == 8u { - correct += 1u; - } - } - 1u | 2u | 3u => { - if part.len() == 4u { - correct += 1u; - } - } - 4u => { - if part.len() == 12u { - correct += 1u; - } - } - _ => { } - } - } - if correct >= 5u { - return true; - } - } - return false; -} - -#[test] -pub fn test_is_uuid() { - assert is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaafAF09"); - assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa"); - assert !is_uuid(~""); - assert !is_uuid(~"aaaaaaaa-aaa -aaaa-aaaa-aaaaaaaaaaaa"); - assert !is_uuid(~"aaaaaaaa-aaa!-aaaa-aaaa-aaaaaaaaaaaa"); - assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-a"); - assert !is_uuid(~"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaป"); -} - -// FIXME (#2661): implement url/URL parsing so we don't have to resort -// to weak checks - -pub fn has_archive_extension(p: ~str) -> bool { - str::ends_with(p, ~".tar") || - str::ends_with(p, ~".tar.gz") || - str::ends_with(p, ~".tar.bz2") || - str::ends_with(p, ~".tar.Z") || - str::ends_with(p, ~".tar.lz") || - str::ends_with(p, ~".tar.xz") || - str::ends_with(p, ~".tgz") || - str::ends_with(p, ~".tbz") || - str::ends_with(p, ~".tbz2") || - str::ends_with(p, ~".tb2") || - str::ends_with(p, ~".taz") || - str::ends_with(p, ~".tlz") || - str::ends_with(p, ~".txz") -} - -pub fn is_archive_path(u: ~str) -> bool { - has_archive_extension(u) && os::path_exists(&Path(u)) -} - -pub fn is_archive_url(u: ~str) -> bool { - // FIXME (#2661): this requires the protocol bit - if we had proper - // url parsing, we wouldn't need it - - match str::find_str(u, ~"://") { - option::Some(_) => has_archive_extension(u), - _ => false - } -} - -pub fn is_git_url(url: ~str) -> bool { - if str::ends_with(url, ~"/") { str::ends_with(url, ~".git/") } - else { - str::starts_with(url, ~"git://") || str::ends_with(url, ~".git") - } -} - -pub fn assume_source_method(url: ~str) -> ~str { - if is_git_url(url) { - return ~"git"; - } - if str::starts_with(url, ~"file://") || os::path_exists(&Path(url)) { - return ~"file"; - } - - ~"curl" -} - -pub fn load_link(mis: ~[@ast::meta_item]) -> (Option<~str>, - Option<~str>, - Option<~str>) { - let mut name = None; - let mut vers = None; - let mut uuid = None; - for mis.each |a| { - match a.node { - ast::meta_name_value(v, codemap::spanned { node: ast::lit_str(s), - _ }) => { - match v { - ~"name" => name = Some(*s), - ~"vers" => vers = Some(*s), - ~"uuid" => uuid = Some(*s), - _ => { } - } - } - _ => fail!(~"load_link: meta items must be name-values") - } - } - (name, vers, uuid) -} - -pub fn load_crate(filename: &Path) -> Option { - let sess = parse::new_parse_sess(None); - let c = parse::parse_crate_from_file(filename, ~[], sess); - - let mut name = None; - let mut vers = None; - let mut uuid = None; - let mut desc = None; - let mut sigs = None; - let mut crate_type = None; - - for c.node.attrs.each |a| { - match a.node.value.node { - ast::meta_name_value(v, codemap::spanned { node: ast::lit_str(_), - _ }) => { - match v { - ~"desc" => desc = Some(v), - ~"sigs" => sigs = Some(v), - ~"crate_type" => crate_type = Some(v), - _ => { } - } - } - ast::meta_list(v, mis) => { - if v == ~"link" { - let (n, v, u) = load_link(mis); - name = n; - vers = v; - uuid = u; - } - } - _ => { - fail!(~"crate attributes may not contain " + - ~"meta_words"); - } - } - } - - struct Env { - mut deps: ~[~str] - } - - fn goto_view_item(ps: syntax::parse::parse_sess, e: @Env, - i: @ast::view_item) { - match i.node { - ast::view_item_use(ident, metas, _) => { - let name_items = - attr::find_meta_items_by_name(metas, ~"name"); - let m = if name_items.is_empty() { - metas + ~[attr::mk_name_value_item_str( - ~"name", *ps.interner.get(ident))] - } else { - metas - }; - let mut attr_name = ident; - let mut attr_vers = ~""; - let mut attr_from = ~""; - - for m.each |item| { - match attr::get_meta_item_value_str(*item) { - Some(value) => { - let name = attr::get_meta_item_name(*item); - - match name { - ~"vers" => attr_vers = value, - ~"from" => attr_from = value, - _ => () - } - } - None => () - } - } - - let query = if !str::is_empty(attr_from) { - attr_from - } else { - if !str::is_empty(attr_vers) { - ps.interner.get(attr_name) + ~"@" + attr_vers - } else { *ps.interner.get(attr_name) } - }; - - match *ps.interner.get(attr_name) { - ~"std" | ~"core" => (), - _ => e.deps.push(query) - } - } - _ => () - } - } - fn goto_item(_e: @Env, _i: @ast::item) { - } - - let e = @Env { - mut deps: ~[] - }; - let v = visit::mk_simple_visitor(@visit::SimpleVisitor { - visit_view_item: |a| goto_view_item(sess, e, a), - visit_item: |a| goto_item(e, a), - .. *visit::default_simple_visitor() - }); - - visit::visit_crate(*c, (), v); - - let deps = copy e.deps; - - match (name, vers, uuid) { - (Some(name0), Some(vers0), Some(uuid0)) => { - Some(Crate { - name: name0, - vers: vers0, - uuid: uuid0, - desc: desc, - sigs: sigs, - crate_type: crate_type, - deps: deps }) - } - _ => return None - } -} - -pub fn print(s: ~str) { - io::stdout().write_line(s); -} - -pub fn rest(s: ~str, start: uint) -> ~str { - if (start >= str::len(s)) { - ~"" - } else { - str::slice(s, start, str::len(s)) - } -} - -pub fn need_dir(s: &Path) { - if os::path_is_dir(s) { return; } - if !os::make_dir(s, 493_i32 /* oct: 755 */) { - fail!(fmt!("can't make_dir %s", s.to_str())); - } -} - -pub fn valid_pkg_name(s: &str) -> bool { - fn is_valid_digit(+c: char) -> bool { - ('0' <= c && c <= '9') || - ('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || - c == '-' || - c == '_' - } - - s.all(is_valid_digit) -} - -pub fn parse_source(name: ~str, j: &json::Json) -> @Source { - if !valid_pkg_name(name) { - fail!(fmt!("'%s' is an invalid source name", name)); - } - - match *j { - json::Object(ref j) => { - let mut url = match j.find(&~"url") { - Some(&json::String(u)) => copy u, - _ => fail!(~"needed 'url' field in source") - }; - let method = match j.find(&~"method") { - Some(&json::String(u)) => copy u, - _ => assume_source_method(url) - }; - let key = match j.find(&~"key") { - Some(&json::String(u)) => Some(copy u), - _ => None - }; - let keyfp = match j.find(&~"keyfp") { - Some(&json::String(u)) => Some(copy u), - _ => None - }; - if method == ~"file" { - url = os::make_absolute(&Path(url)).to_str(); - } - return @Source { - name: name, - mut url: url, - mut method: method, - mut key: key, - mut keyfp: keyfp, - packages: DVec() }; - } - _ => fail!(~"needed dict value in source") - }; -} - -pub fn try_parse_sources(filename: &Path, - sources: oldmap::HashMap<~str, @Source>) { - if !os::path_exists(filename) { return; } - let c = io::read_whole_file_str(filename); - match json::from_str(c.get()) { - Ok(json::Object(j)) => { - for j.each |&(k, v)| { - sources.insert(copy *k, parse_source(*k, v)); - debug!("source: %s", *k); - } - } - Ok(_) => fail!(~"malformed sources.json"), - Err(e) => fail!(fmt!("%s:%s", filename.to_str(), e.to_str())) - } -} - -pub fn load_one_source_package(src: @Source, p: &json::Object) { - let name = match p.find(&~"name") { - Some(&json::String(n)) => { - if !valid_pkg_name(n) { - warn(~"malformed source json: " - + src.name + ~", '" + n + ~"'"+ - ~" is an invalid name (alphanumeric, underscores and" + - ~" dashes only)"); - return; - } - n - } - _ => { - warn(~"malformed source json: " + src.name + ~" (missing name)"); - return; - } - }; - - let uuid = match p.find(&~"uuid") { - Some(&json::String(n)) => { - if !is_uuid(n) { - warn(~"malformed source json: " - + src.name + ~", '" + n + ~"'"+ - ~" is an invalid uuid"); - return; - } - copy n - } - _ => { - warn(~"malformed source json: " + src.name + ~" (missing uuid)"); - return; - } - }; - - let url = match p.find(&~"url") { - Some(&json::String(n)) => copy n, - _ => { - warn(~"malformed source json: " + src.name + ~" (missing url)"); - return; - } - }; - - let method = match p.find(&~"method") { - Some(&json::String(n)) => copy n, - _ => { - warn(~"malformed source json: " - + src.name + ~" (missing method)"); - return; - } - }; - - let reference = match p.find(&~"ref") { - Some(&json::String(n)) => Some(copy n), - _ => None - }; - - let mut tags = ~[]; - match p.find(&~"tags") { - Some(&json::List(ref js)) => { - for js.each |j| { - match *j { - json::String(ref j) => tags.grow(1u, j), - _ => () - } - } - } - _ => () - } - - let description = match p.find(&~"description") { - Some(&json::String(n)) => copy n, - _ => { - warn(~"malformed source json: " + src.name - + ~" (missing description)"); - return; - } - }; - - let newpkg = Package { - name: name, - uuid: uuid, - url: url, - method: method, - description: description, - reference: reference, - tags: tags, - versions: ~[] - }; - - match src.packages.position(|pkg| pkg.uuid == uuid) { - Some(idx) => { - src.packages.set_elt(idx, newpkg); - log(debug, ~" updated package: " + src.name + ~"/" + name); - } - None => { - src.packages.push(newpkg); - } - } - - log(debug, ~" loaded package: " + src.name + ~"/" + name); -} - -pub fn load_source_info(c: &Cargo, src: @Source) { - let dir = c.sourcedir.push(src.name); - let srcfile = dir.push("source.json"); - if !os::path_exists(&srcfile) { return; } - let srcstr = io::read_whole_file_str(&srcfile); - match json::from_str(srcstr.get()) { - Ok(ref json @ json::Object(_)) => { - let o = parse_source(src.name, json); - - src.key = o.key; - src.keyfp = o.keyfp; - } - Ok(_) => { - warn(~"malformed source.json: " + src.name + - ~"(source info is not a dict)"); - } - Err(e) => { - warn(fmt!("%s:%s", src.name, e.to_str())); - } - }; -} -pub fn load_source_packages(c: &Cargo, src: @Source) { - log(debug, ~"loading source: " + src.name); - let dir = c.sourcedir.push(src.name); - let pkgfile = dir.push("packages.json"); - if !os::path_exists(&pkgfile) { return; } - let pkgstr = io::read_whole_file_str(&pkgfile); - match json::from_str(pkgstr.get()) { - Ok(json::List(ref js)) => { - for js.each |j| { - match *j { - json::Object(ref p) => { - load_one_source_package(src, *p); - } - _ => { - warn(~"malformed source json: " + src.name + - ~" (non-dict pkg)"); - } - } - } - } - Ok(_) => { - warn(~"malformed packages.json: " + src.name + - ~"(packages is not a list)"); - } - Err(e) => { - warn(fmt!("%s:%s", src.name, e.to_str())); - } - }; -} - -pub fn build_cargo_options(argv: ~[~str]) -> Options { - let matches = &match getopts::getopts(argv, opts()) { - result::Ok(m) => m, - result::Err(f) => { - fail!(fmt!("%s", getopts::fail_str(f))); - } - }; - - let test = opt_present(matches, ~"test"); - let G = opt_present(matches, ~"G"); - let g = opt_present(matches, ~"g"); - let help = opt_present(matches, ~"h") || opt_present(matches, ~"help"); - let len = vec::len(matches.free); - - let is_install = len > 1u && matches.free[1] == ~"install"; - let is_uninstall = len > 1u && matches.free[1] == ~"uninstall"; - - if G && g { fail!(~"-G and -g both provided"); } - - if !is_install && !is_uninstall && (g || G) { - fail!(~"-g and -G are only valid for `install` and `uninstall|rm`"); - } - - let mode = - if (!is_install && !is_uninstall) || g { UserMode } - else if G { SystemMode } - else { LocalMode }; - - Options {test: test, mode: mode, free: matches.free, help: help} -} - -pub fn configure(opts: Options) -> Cargo { - let home = match get_cargo_root() { - Ok(home) => home, - Err(_err) => get_cargo_sysroot().get() - }; - - let get_cargo_dir = match opts.mode { - SystemMode => get_cargo_sysroot, - UserMode => get_cargo_root, - LocalMode => get_cargo_root_nearest - }; - - let p = get_cargo_dir().get(); - - let sources = HashMap(); - try_parse_sources(&home.push("sources.json"), sources); - try_parse_sources(&home.push("local-sources.json"), sources); - - let dep_cache = HashMap(); - - let mut c = Cargo { - pgp: pgp::supported(), - root: home, - installdir: p, - bindir: p.push("bin"), - libdir: p.push("lib"), - workdir: p.push("work"), - sourcedir: home.push("sources"), - sources: sources, - mut current_install: ~"", - dep_cache: dep_cache, - opts: opts - }; - - need_dir(&c.root); - need_dir(&c.installdir); - need_dir(&c.sourcedir); - need_dir(&c.workdir); - need_dir(&c.libdir); - need_dir(&c.bindir); - - for sources.each_key |&k| { - let mut s = sources.get(&k); - load_source_packages(&c, s); - sources.insert(k, s); - } - - if c.pgp { - pgp::init(&c.root); - } else { - warn(~"command `gpg` was not found"); - warn(~"you have to install gpg from source " + - ~" or package manager to get it to work correctly"); - } - - c -} - -pub fn for_each_package(c: &Cargo, b: fn(s: @Source, p: &Package)) { - for c.sources.each_value |&v| { - for v.packages.each |p| { - b(v, p); - } - } -} - -// Runs all programs in directory -pub fn run_programs(buildpath: &Path) { - let newv = os::list_dir_path(buildpath); - for newv.each |ct| { - run::run_program(ct.to_str(), ~[]); - } -} - -// Runs rustc in with the given flags -// and returns -pub fn run_in_buildpath(what: &str, path: &Path, subdir: &Path, cf: &Path, - extra_flags: ~[~str]) -> Option { - let buildpath = path.push_rel(subdir); - need_dir(&buildpath); - debug!("%s: %s -> %s", what, cf.to_str(), buildpath.to_str()); - let p = run::program_output(rustc_sysroot(), - ~[~"--out-dir", - buildpath.to_str(), - cf.to_str()] + extra_flags); - if p.status != 0 { - error(fmt!("rustc failed: %d\n%s\n%s", p.status, p.err, p.out)); - return None; - } - Some(buildpath) -} - -pub fn test_one_crate(_c: &Cargo, path: &Path, cf: &Path) { - let buildpath = match run_in_buildpath(~"testing", path, - &Path("test"), - cf, - ~[ ~"--test"]) { - None => return, - Some(bp) => bp - }; - run_programs(&buildpath); -} - -pub fn install_one_crate(c: &Cargo, path: &Path, cf: &Path) { - let buildpath = match run_in_buildpath(~"installing", path, - &Path("build"), - cf, ~[]) { - None => return, - Some(bp) => bp - }; - let newv = os::list_dir_path(&buildpath); - let exec_suffix = str::from_slice(os::EXE_SUFFIX); - for newv.each |ct| { - if (exec_suffix != ~"" && str::ends_with(ct.to_str(), - exec_suffix)) || - (exec_suffix == ~"" && - !str::starts_with(ct.filename().get(), - ~"lib")) { - debug!(" bin: %s", ct.to_str()); - install_to_dir(*ct, &c.bindir); - if c.opts.mode == SystemMode { - // FIXME (#2662): Put this file in PATH / symlink it so it can - // be used as a generic executable - // `cargo install -G rustray` and `rustray file.obj` - } - } else { - debug!(" lib: %s", ct.to_str()); - install_to_dir(*ct, &c.libdir); - } - } -} - - -pub fn rustc_sysroot() -> ~str { - match os::self_exe_path() { - Some(path) => { - let rustc = path.push_many([~"..", ~"bin", ~"rustc"]); - debug!(" rustc: %s", rustc.to_str()); - rustc.to_str() - } - None => ~"rustc" - } -} - -pub fn install_source(c: &mut Cargo, path: &Path) { - debug!("source: %s", path.to_str()); - os::change_dir(path); - - let mut cratefiles = ~[]; - for os::walk_dir(&Path(".")) |p| { - if p.filetype() == Some(~".rc") { - cratefiles.push(*p); - } - } - - if vec::is_empty(cratefiles) { - fail!(~"this doesn't look like a rust package (no .rc files)"); - } - - for cratefiles.each |cf| { - match load_crate(cf) { - None => loop, - Some(crate) => { - for crate.deps.each |query| { - // FIXME (#1356): handle cyclic dependencies - // (n.b. #1356 says "Cyclic dependency is an error - // condition") - - let wd = get_temp_workdir(c); - install_query(c, &wd, *query); - } - - os::change_dir(path); - - if c.opts.test { - test_one_crate(c, path, cf); - } - install_one_crate(c, path, cf); - } - } - } -} - -pub fn install_git(c: &mut Cargo, wd: &Path, url: ~str, - reference: Option<~str>) { - run::program_output(~"git", ~[~"clone", url, wd.to_str()]); - if reference.is_some() { - let r = reference.get(); - os::change_dir(wd); - run::run_program(~"git", ~[~"checkout", r]); - } - - install_source(c, wd); -} - -pub fn install_curl(c: &mut Cargo, wd: &Path, url: ~str) { - let tarpath = wd.push("pkg.tar"); - let p = run::program_output(~"curl", ~[~"-f", ~"-s", ~"-o", - tarpath.to_str(), url]); - if p.status != 0 { - fail!(fmt!("fetch of %s failed: %s", url, p.err)); - } - run::run_program(~"tar", ~[~"-x", ~"--strip-components=1", - ~"-C", wd.to_str(), - ~"-f", tarpath.to_str()]); - install_source(c, wd); -} - -pub fn install_file(c: &mut Cargo, wd: &Path, path: &Path) { - run::program_output(~"tar", ~[~"-x", ~"--strip-components=1", - ~"-C", wd.to_str(), - ~"-f", path.to_str()]); - install_source(c, wd); -} - -pub fn install_package(c: &mut Cargo, src: ~str, wd: &Path, pkg: Package) { - let url = copy pkg.url; - let method = match pkg.method { - ~"git" => ~"git", - ~"file" => ~"file", - _ => ~"curl" - }; - - info(fmt!("installing %s/%s via %s...", src, pkg.name, method)); - - match method { - ~"git" => install_git(c, wd, url, copy pkg.reference), - ~"file" => install_file(c, wd, &Path(url)), - ~"curl" => install_curl(c, wd, url), - _ => () - } -} - -pub fn cargo_suggestion(c: &Cargo, fallback: fn()) { - if c.sources.is_empty() { - error(~"no sources defined - you may wish to run " + - ~"`cargo init`"); - return; - } - fallback(); -} - -pub fn install_uuid(c: &mut Cargo, wd: &Path, uuid: ~str) { - let mut ps = ~[]; - for_each_package(c, |s, p| { - if p.uuid == uuid { - vec::push(&mut ps, (s.name, copy *p)); - } - }); - if vec::len(ps) == 1u { - let (sname, p) = copy ps[0]; - install_package(c, sname, wd, p); - return; - } else if vec::len(ps) == 0u { - cargo_suggestion(c, || { - error(~"can't find package: " + uuid); - }); - return; - } - error(~"found multiple packages:"); - for ps.each |elt| { - let (sname,p) = copy *elt; - info(~" " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")"); - } -} - -pub fn install_named(c: &mut Cargo, wd: &Path, name: ~str) { - let mut ps = ~[]; - for_each_package(c, |s, p| { - if p.name == name { - vec::push(&mut ps, (s.name, copy *p)); - } - }); - if vec::len(ps) == 1u { - let (sname, p) = copy ps[0]; - install_package(c, sname, wd, p); - return; - } else if vec::len(ps) == 0u { - cargo_suggestion(c, || { - error(~"can't find package: " + name); - }); - return; - } - error(~"found multiple packages:"); - for ps.each |elt| { - let (sname,p) = copy *elt; - info(~" " + sname + ~"/" + p.uuid + ~" (" + p.name + ~")"); - } -} - -pub fn install_uuid_specific(c: &mut Cargo, wd: &Path, src: ~str, - uuid: ~str) { - match c.sources.find(&src) { - Some(s) => { - for s.packages.each |p| { - if p.uuid == uuid { - install_package(c, src, wd, *p); - return; - } - } - } - _ => () - } - error(~"can't find package: " + src + ~"/" + uuid); -} - -pub fn install_named_specific(c: &mut Cargo, wd: &Path, src: ~str, - name: ~str) { - match c.sources.find(&src) { - Some(s) => { - for s.packages.each |p| { - if p.name == name { - install_package(c, src, wd, *p); - return; - } - } - } - _ => () - } - error(~"can't find package: " + src + ~"/" + name); -} - -pub fn cmd_uninstall(c: &Cargo) { - if vec::len(c.opts.free) < 3u { - cmd_usage(); - return; - } - - let lib = &c.libdir; - let bin = &c.bindir; - let target = c.opts.free[2u]; - - // FIXME (#2662): needs stronger pattern matching - // FIXME (#2662): needs to uninstall from a specified location in a - // cache instead of looking for it (binaries can be uninstalled by - // name only) - - fn try_uninstall(p: &Path) -> bool { - if os::remove_file(p) { - info(~"uninstalled: '" + p.to_str() + ~"'"); - true - } else { - error(~"could not uninstall: '" + - p.to_str() + ~"'"); - false - } - } - - if is_uuid(target) { - for os::list_dir(lib).each |file| { - match str::find_str(*file, ~"-" + target + ~"-") { - Some(_) => if !try_uninstall(&lib.push(*file)) { return }, - None => () - } - } - error(~"can't find package with uuid: " + target); - } else { - for os::list_dir(lib).each |file| { - match str::find_str(*file, ~"lib" + target + ~"-") { - Some(_) => if !try_uninstall(&lib.push(*file)) { return }, - None => () - } - } - for os::list_dir(bin).each |file| { - match str::find_str(*file, target) { - Some(_) => if !try_uninstall(&lib.push(*file)) { return }, - None => () - } - } - - error(~"can't find package with name: " + target); - } -} - -pub fn install_query(c: &mut Cargo, wd: &Path, target: ~str) { - match c.dep_cache.find(&target) { - Some(inst) => { - if inst { - return; - } - } - None => () - } - - c.dep_cache.insert(target, true); - - if is_archive_path(target) { - install_file(c, wd, &Path(target)); - return; - } else if is_git_url(target) { - let reference = if c.opts.free.len() >= 4u { - Some(c.opts.free[3u]) - } else { - None - }; - install_git(c, wd, target, reference); - } else if !valid_pkg_name(target) && has_archive_extension(target) { - install_curl(c, wd, target); - return; - } else { - let mut ps = copy target; - - match str::find_char(ps, '/') { - option::Some(idx) => { - let source = str::slice(ps, 0u, idx); - ps = str::slice(ps, idx + 1u, str::len(ps)); - if is_uuid(ps) { - install_uuid_specific(c, wd, source, ps); - } else { - install_named_specific(c, wd, source, ps); - } - } - option::None => { - if is_uuid(ps) { - install_uuid(c, wd, ps); - } else { - install_named(c, wd, ps); - } - } - } - } - - // FIXME (#2662): This whole dep_cache and current_install thing is - // a bit of a hack. It should be cleaned up in the future. - - if target == c.current_install { - c.dep_cache.clear(); - c.current_install = ~""; - } -} - -pub fn get_temp_workdir(c: &Cargo) -> Path { - match tempfile::mkdtemp(&c.workdir, "cargo") { - Some(wd) => wd, - None => fail!(fmt!("needed temp dir: %s", - c.workdir.to_str())) - } -} - -pub fn cmd_install(c: &mut Cargo) { - unsafe { - let wd = get_temp_workdir(c); - - if vec::len(c.opts.free) == 2u { - let cwd = os::getcwd(); - let status = run::run_program(~"cp", ~[~"-R", cwd.to_str(), - wd.to_str()]); - - if status != 0 { - fail!(fmt!("could not copy directory: %s", cwd.to_str())); - } - - install_source(c, &wd); - return; - } - - sync(c); - - let query = c.opts.free[2]; - c.current_install = query.to_str(); - - install_query(c, &wd, query); - } -} - -pub fn sync(c: &Cargo) { - for c.sources.each_key |&k| { - let mut s = c.sources.get(&k); - sync_one(c, s); - c.sources.insert(k, s); - } -} - -pub fn sync_one_file(c: &Cargo, dir: &Path, src: @Source) -> bool { - let name = src.name; - let srcfile = dir.push("source.json.new"); - let destsrcfile = dir.push("source.json"); - let pkgfile = dir.push("packages.json.new"); - let destpkgfile = dir.push("packages.json"); - let keyfile = dir.push("key.gpg"); - let srcsigfile = dir.push("source.json.sig"); - let sigfile = dir.push("packages.json.sig"); - let url = Path(src.url); - let mut has_src_file = false; - - if !os::copy_file(&url.push("packages.json"), &pkgfile) { - error(fmt!("fetch for source %s (url %s) failed", - name, url.to_str())); - return false; - } - - if os::copy_file(&url.push("source.json"), &srcfile) { - has_src_file = false; - } - - os::copy_file(&url.push("source.json.sig"), &srcsigfile); - os::copy_file(&url.push("packages.json.sig"), &sigfile); - - match copy src.key { - Some(u) => { - let p = run::program_output(~"curl", - ~[~"-f", ~"-s", - ~"-o", keyfile.to_str(), u]); - if p.status != 0 { - error(fmt!("fetch for source %s (key %s) failed", name, u)); - return false; - } - pgp::add(&c.root, &keyfile); - } - _ => () - } - match (src.key, src.keyfp) { - (Some(_), Some(f)) => { - let r = pgp::verify(&c.root, &pkgfile, &sigfile); - - if !r { - error(fmt!("signature verification failed for source %s with \ - key %s", name, f)); - return false; - } - - if has_src_file { - let e = pgp::verify(&c.root, &srcfile, &srcsigfile); - - if !e { - error(fmt!("signature verification failed for source %s \ - with key %s", name, f)); - return false; - } - } - } - _ => () - } - - copy_warn(&pkgfile, &destpkgfile); - - if has_src_file { - copy_warn(&srcfile, &destsrcfile); - } - - os::remove_file(&keyfile); - os::remove_file(&srcfile); - os::remove_file(&srcsigfile); - os::remove_file(&pkgfile); - os::remove_file(&sigfile); - - info(fmt!("synced source: %s", name)); - - return true; -} - -pub fn sync_one_git(c: &Cargo, dir: &Path, src: @Source) -> bool { - let name = src.name; - let srcfile = dir.push("source.json"); - let pkgfile = dir.push("packages.json"); - let keyfile = dir.push("key.gpg"); - let srcsigfile = dir.push("source.json.sig"); - let sigfile = dir.push("packages.json.sig"); - let url = src.url; - - fn rollback(name: ~str, dir: &Path, insecure: bool) { - fn msg(name: ~str, insecure: bool) { - error(fmt!("could not rollback source: %s", name)); - - if insecure { - warn(~"a past security check failed on source " + - name + ~" and rolling back the source failed -" - + ~" this source may be compromised"); - } - } - - if !os::change_dir(dir) { - msg(name, insecure); - } - else { - let p = run::program_output(~"git", ~[~"reset", ~"--hard", - ~"HEAD@{1}"]); - - if p.status != 0 { - msg(name, insecure); - } - } - } - - if !os::path_exists(&dir.push(".git")) { - let p = run::program_output(~"git", ~[~"clone", url, dir.to_str()]); - - if p.status != 0 { - error(fmt!("fetch for source %s (url %s) failed", name, url)); - return false; - } - } - else { - if !os::change_dir(dir) { - error(fmt!("fetch for source %s (url %s) failed", name, url)); - return false; - } - - let p = run::program_output(~"git", ~[~"pull"]); - - if p.status != 0 { - error(fmt!("fetch for source %s (url %s) failed", name, url)); - return false; - } - } - - let has_src_file = os::path_exists(&srcfile); - - match copy src.key { - Some(u) => { - let p = run::program_output(~"curl", - ~[~"-f", ~"-s", - ~"-o", keyfile.to_str(), u]); - if p.status != 0 { - error(fmt!("fetch for source %s (key %s) failed", name, u)); - rollback(name, dir, false); - return false; - } - pgp::add(&c.root, &keyfile); - } - _ => () - } - match (src.key, src.keyfp) { - (Some(_), Some(f)) => { - let r = pgp::verify(&c.root, &pkgfile, &sigfile); - - if !r { - error(fmt!("signature verification failed for source %s with \ - key %s", name, f)); - rollback(name, dir, false); - return false; - } - - if has_src_file { - let e = pgp::verify(&c.root, &srcfile, &srcsigfile); - - if !e { - error(fmt!("signature verification failed for source %s \ - with key %s", name, f)); - rollback(name, dir, false); - return false; - } - } - } - _ => () - } - - os::remove_file(&keyfile); - - info(fmt!("synced source: %s", name)); - - return true; -} - -pub fn sync_one_curl(c: &Cargo, dir: &Path, src: @Source) -> bool { - let name = src.name; - let srcfile = dir.push("source.json.new"); - let destsrcfile = dir.push("source.json"); - let pkgfile = dir.push("packages.json.new"); - let destpkgfile = dir.push("packages.json"); - let keyfile = dir.push("key.gpg"); - let srcsigfile = dir.push("source.json.sig"); - let sigfile = dir.push("packages.json.sig"); - let mut url = src.url; - let smart = !str::ends_with(src.url, ~"packages.json"); - let mut has_src_file = false; - - if smart { - url += ~"/packages.json"; - } - - let p = run::program_output(~"curl", - ~[~"-f", ~"-s", - ~"-o", pkgfile.to_str(), url]); - - if p.status != 0 { - error(fmt!("fetch for source %s (url %s) failed", name, url)); - return false; - } - if smart { - url = src.url + ~"/source.json"; - let p = - run::program_output(~"curl", - ~[~"-f", ~"-s", - ~"-o", srcfile.to_str(), url]); - - if p.status == 0 { - has_src_file = true; - } - } - - match copy src.key { - Some(u) => { - let p = run::program_output(~"curl", - ~[~"-f", ~"-s", - ~"-o", keyfile.to_str(), u]); - if p.status != 0 { - error(fmt!("fetch for source %s (key %s) failed", name, u)); - return false; - } - pgp::add(&c.root, &keyfile); - } - _ => () - } - match (src.key, src.keyfp) { - (Some(_), Some(f)) => { - if smart { - url = src.url + ~"/packages.json.sig"; - } - else { - url = src.url + ~".sig"; - } - - let mut p = run::program_output(~"curl", - ~[~"-f", ~"-s", ~"-o", - sigfile.to_str(), url]); - if p.status != 0 { - error(fmt!("fetch for source %s (sig %s) failed", name, url)); - return false; - } - - let r = pgp::verify(&c.root, &pkgfile, &sigfile); - - if !r { - error(fmt!("signature verification failed for source %s with \ - key %s", name, f)); - return false; - } - - if smart && has_src_file { - url = src.url + ~"/source.json.sig"; - - p = run::program_output(~"curl", - ~[~"-f", ~"-s", ~"-o", - srcsigfile.to_str(), url]); - if p.status != 0 { - error(fmt!("fetch for source %s (sig %s) failed", - name, url)); - return false; - } - - let e = pgp::verify(&c.root, &srcfile, &srcsigfile); - - if !e { - error(~"signature verification failed for " + - ~"source " + name + ~" with key " + f); - return false; - } - } - } - _ => () - } - - copy_warn(&pkgfile, &destpkgfile); - - if smart && has_src_file { - copy_warn(&srcfile, &destsrcfile); - } - - os::remove_file(&keyfile); - os::remove_file(&srcfile); - os::remove_file(&srcsigfile); - os::remove_file(&pkgfile); - os::remove_file(&sigfile); - - info(fmt!("synced source: %s", name)); - - return true; -} - -pub fn sync_one(c: &Cargo, src: @Source) { - let name = src.name; - let dir = c.sourcedir.push(name); - - info(fmt!("syncing source: %s...", name)); - - need_dir(&dir); - - let result = match src.method { - ~"git" => sync_one_git(c, &dir, src), - ~"file" => sync_one_file(c, &dir, src), - _ => sync_one_curl(c, &dir, src) - }; - - if result { - load_source_info(c, src); - load_source_packages(c, src); - } -} - -pub fn cmd_init(c: &Cargo) { - let srcurl = ~"http://www.rust-lang.org/cargo/sources.json"; - let sigurl = ~"http://www.rust-lang.org/cargo/sources.json.sig"; - - let srcfile = c.root.push("sources.json.new"); - let sigfile = c.root.push("sources.json.sig"); - let destsrcfile = c.root.push("sources.json"); - - let p = - run::program_output(~"curl", ~[~"-f", ~"-s", - ~"-o", srcfile.to_str(), srcurl]); - if p.status != 0 { - error(fmt!("fetch of sources.json failed: %s", p.out)); - return; - } - - let p = - run::program_output(~"curl", ~[~"-f", ~"-s", - ~"-o", sigfile.to_str(), sigurl]); - if p.status != 0 { - error(fmt!("fetch of sources.json.sig failed: %s", p.out)); - return; - } - - let r = pgp::verify(&c.root, &srcfile, &sigfile); - if !r { - error(fmt!("signature verification failed for '%s'", - srcfile.to_str())); - return; - } - - copy_warn(&srcfile, &destsrcfile); - os::remove_file(&srcfile); - os::remove_file(&sigfile); - - info(fmt!("initialized .cargo in %s", c.root.to_str())); -} - -pub fn print_pkg(s: @Source, p: &Package) { - let mut m = s.name + ~"/" + p.name + ~" (" + p.uuid + ~")"; - if vec::len(p.tags) > 0u { - m = m + ~" [" + str::connect(p.tags, ~", ") + ~"]"; - } - info(m); - if p.description != ~"" { - print(~" >> " + p.description + ~"\n") - } -} - -pub fn print_source(s: @Source) { - info(s.name + ~" (" + s.url + ~")"); - - let pks = sort::merge_sort(s.packages.get(), sys::shape_lt); - let l = vec::len(pks); - - print(io::with_str_writer(|writer| { - let mut list = ~" >> "; - - for vec::eachi(pks) |i, pk| { - if str::len(list) > 78u { - writer.write_line(list); - list = ~" >> "; - } - list += pk.name + (if l - 1u == i { ~"" } else { ~", " }); - } - - writer.write_line(list); - })); -} - -pub fn cmd_list(c: &Cargo) { - sync(c); - - if vec::len(c.opts.free) >= 3u { - let v = vec::slice(c.opts.free, 2u, vec::len(c.opts.free)); - for vec::each(v) |name| { - if !valid_pkg_name(*name) { - error(fmt!("'%s' is an invalid source name", *name)); - } else { - match c.sources.find(name) { - Some(source) => { - print_source(source); - } - None => { - error(fmt!("no such source: %s", *name)); - } - } - } - } - } else { - for c.sources.each_value |&v| { - print_source(v); - } - } -} - -pub fn cmd_search(c: &Cargo) { - if vec::len(c.opts.free) < 3u { - cmd_usage(); - return; - } - - sync(c); - - let mut n = 0; - let name = c.opts.free[2]; - let tags = vec::slice(c.opts.free, 3u, vec::len(c.opts.free)); - for_each_package(c, |s, p| { - if (str::contains(p.name, name) || name == ~"*") && - vec::all(tags, |t| vec::contains(p.tags, t) ) { - print_pkg(s, p); - n += 1; - } - }); - info(fmt!("found %d packages", n)); -} - -pub fn install_to_dir(srcfile: &Path, destdir: &Path) { - let newfile = destdir.push(srcfile.filename().get()); - - let status = run::run_program(~"cp", ~[~"-r", srcfile.to_str(), - newfile.to_str()]); - if status == 0 { - info(fmt!("installed: '%s'", newfile.to_str())); - } else { - error(fmt!("could not install: '%s'", newfile.to_str())); - } -} - -pub fn dump_cache(c: &Cargo) { - need_dir(&c.root); - - let out = c.root.push("cache.json"); - let _root = json::Object(~LinearMap::new()); - - if os::path_exists(&out) { - copy_warn(&out, &c.root.push("cache.json.old")); - } -} - -pub fn dump_sources(c: &Cargo) { - if c.sources.is_empty() { - return; - } - - need_dir(&c.root); - - let out = c.root.push("sources.json"); - - if os::path_exists(&out) { - copy_warn(&out, &c.root.push("sources.json.old")); - } - - match io::buffered_file_writer(&out) { - result::Ok(writer) => { - let mut hash = ~LinearMap::new(); - - for c.sources.each |&k, &v| { - let mut chash = ~LinearMap::new(); - - chash.insert(~"url", json::String(v.url)); - chash.insert(~"method", json::String(v.method)); - - match copy v.key { - Some(key) => { - chash.insert(~"key", json::String(copy key)); - } - _ => () - } - match copy v.keyfp { - Some(keyfp) => { - chash.insert(~"keyfp", json::String(copy keyfp)); - } - _ => () - } - - hash.insert(copy k, json::Object(chash)); - } - - json::to_writer(writer, &json::Object(hash)) - } - result::Err(e) => { - error(fmt!("could not dump sources: %s", e)); - } - } -} - -pub fn copy_warn(srcfile: &Path, destfile: &Path) { - if !os::copy_file(srcfile, destfile) { - warn(fmt!("copying %s to %s failed", - srcfile.to_str(), destfile.to_str())); - } -} - -pub fn cmd_sources(c: &Cargo) { - if vec::len(c.opts.free) < 3u { - for c.sources.each_value |&v| { - info(fmt!("%s (%s) via %s", - v.name, v.url, v.method)); - } - return; - } - - let action = c.opts.free[2u]; - - match action { - ~"clear" => { - for c.sources.each_key |&k| { - c.sources.remove(&k); - } - - info(~"cleared sources"); - } - ~"add" => { - if vec::len(c.opts.free) < 5u { - cmd_usage(); - return; - } - - let name = c.opts.free[3u]; - let url = c.opts.free[4u]; - - if !valid_pkg_name(name) { - error(fmt!("'%s' is an invalid source name", name)); - return; - } - - if c.sources.contains_key(&name) { - error(fmt!("source already exists: %s", name)); - } else { - c.sources.insert(name, @Source { - name: name, - mut url: url, - mut method: assume_source_method(url), - mut key: None, - mut keyfp: None, - packages: DVec() - }); - info(fmt!("added source: %s", name)); - } - } - ~"remove" => { - if vec::len(c.opts.free) < 4u { - cmd_usage(); - return; - } - - let name = c.opts.free[3u]; - - if !valid_pkg_name(name) { - error(fmt!("'%s' is an invalid source name", name)); - return; - } - - if c.sources.contains_key(&name) { - c.sources.remove(&name); - info(fmt!("removed source: %s", name)); - } else { - error(fmt!("no such source: %s", name)); - } - } - ~"set-url" => { - if vec::len(c.opts.free) < 5u { - cmd_usage(); - return; - } - - let name = c.opts.free[3u]; - let url = c.opts.free[4u]; - - if !valid_pkg_name(name) { - error(fmt!("'%s' is an invalid source name", name)); - return; - } - - match c.sources.find(&name) { - Some(source) => { - let old = copy source.url; - let method = assume_source_method(url); - - source.url = url; - source.method = method; - - c.sources.insert(name, source); - - info(fmt!("changed source url: '%s' to '%s'", old, url)); - } - None => { - error(fmt!("no such source: %s", name)); - } - } - } - ~"set-method" => { - if vec::len(c.opts.free) < 5u { - cmd_usage(); - return; - } - - let name = c.opts.free[3u]; - let method = c.opts.free[4u]; - - if !valid_pkg_name(name) { - error(fmt!("'%s' is an invalid source name", name)); - return; - } - - match c.sources.find(&name) { - Some(source) => { - let old = copy source.method; - - source.method = match method { - ~"git" => ~"git", - ~"file" => ~"file", - _ => ~"curl" - }; - - c.sources.insert(name, source); - - info(fmt!("changed source method: '%s' to '%s'", old, - method)); - } - None => { - error(fmt!("no such source: %s", name)); - } - } - } - ~"rename" => { - if vec::len(c.opts.free) < 5u { - cmd_usage(); - return; - } - - let name = c.opts.free[3u]; - let newn = c.opts.free[4u]; - - if !valid_pkg_name(name) { - error(fmt!("'%s' is an invalid source name", name)); - return; - } - if !valid_pkg_name(newn) { - error(fmt!("'%s' is an invalid source name", newn)); - return; - } - - match c.sources.find(&name) { - Some(source) => { - c.sources.remove(&name); - c.sources.insert(newn, source); - info(fmt!("renamed source: %s to %s", name, newn)); - } - None => { - error(fmt!("no such source: %s", name)); - } - } - } - _ => cmd_usage() - } -} - -pub fn cmd_usage() { - print(~"Usage: cargo [options] [args..] -e.g. cargo install - -Where is one of: - init, install, list, search, sources, - uninstall, usage - -Options: - - -h, --help Display this message - -h, --help Display help for -"); -} - -pub fn cmd_usage_init() { - print(~"cargo init - -Re-initialize cargo in ~/.cargo. Clears all sources and then adds the -default sources from ."); -} - -pub fn cmd_usage_install() { - print(~"cargo install -cargo install [source/][@version] -cargo install [source/][@version] -cargo install [ref] -cargo install -cargo install - -Options: - --test Run crate tests before installing - -g Install to the user level (~/.cargo/bin/ instead of - locally in ./.cargo/bin/ by default) - -G Install to the system level (/usr/local/lib/cargo/bin/) - -Install a crate. If no arguments are supplied, it installs from -the current working directory. If a source is provided, only install -from that source, otherwise it installs from any source."); -} - -pub fn cmd_usage_uninstall() { - print(~"cargo uninstall [source/][@version] -cargo uninstall [source/][@version] -cargo uninstall [@version] -cargo uninstall [@version] - -Options: - -g Remove from the user level (~/.cargo/bin/ instead of - locally in ./.cargo/bin/ by default) - -G Remove from the system level (/usr/local/lib/cargo/bin/) - -Remove a crate. If a source is provided, only remove -from that source, otherwise it removes from any source. -If a crate was installed directly (git, tarball, etc.), you can remove -it by metadata."); -} - -pub fn cmd_usage_list() { - print(~"cargo list [sources..] - -If no arguments are provided, list all sources and their packages. -If source names are provided, list those sources and their packages. -"); -} - -pub fn cmd_usage_search() { - print(~"cargo search [tags..] - -Search packages."); -} - -pub fn cmd_usage_sources() { - print(~"cargo sources -cargo sources add -cargo sources remove -cargo sources rename -cargo sources set-url -cargo sources set-method - -If no arguments are supplied, list all sources (but not their packages). - -Commands: - add Add a source. The source method will be guessed - from the URL. - remove Remove a source. - rename Rename a source. - set-url Change the URL for a source. - set-method Change the method for a source."); -} - -pub fn main() { - let argv = os::args(); - let o = build_cargo_options(argv); - - if vec::len(o.free) < 2u { - cmd_usage(); - return; - } - if o.help { - match o.free[1] { - ~"init" => cmd_usage_init(), - ~"install" => cmd_usage_install(), - ~"uninstall" => cmd_usage_uninstall(), - ~"list" => cmd_usage_list(), - ~"search" => cmd_usage_search(), - ~"sources" => cmd_usage_sources(), - _ => cmd_usage() - } - return; - } - if o.free[1] == ~"usage" { - cmd_usage(); - return; - } - - let mut c = configure(o); - let home = c.root; - let first_time = os::path_exists(&home.push("sources.json")); - - if !first_time && o.free[1] != ~"init" { - cmd_init(&c); - - // FIXME (#2662): shouldn't need to reconfigure - c = configure(o); - } - - match o.free[1] { - ~"init" => cmd_init(&c), - ~"install" => cmd_install(&mut c), - ~"uninstall" => cmd_uninstall(&c), - ~"list" => cmd_list(&c), - ~"search" => cmd_search(&c), - ~"sources" => cmd_sources(&c), - _ => cmd_usage() - } - - dump_cache(&c); - dump_sources(&c); -} - diff --git a/src/libcargo/pgp.rs b/src/libcargo/pgp.rs deleted file mode 100644 index 364effcd32f48..0000000000000 --- a/src/libcargo/pgp.rs +++ /dev/null @@ -1,115 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::os; -use core::path::Path; -use core::run; - -pub fn gpgv(args: ~[~str]) -> run::ProgramOutput { - return run::program_output(~"gpgv", args); -} - -pub fn signing_key() -> ~str { - ~" ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: SKS 1.1.0 - -mQINBE7dQY0BEADYs5pHqXQugXjmgRTj0AzE3F4HAEJAiUBechVOmCgNcnW4dyb6bgj7Ctqs -Td/ZDSZkFwmsIqpwfGxMr+s9VA3PW+sEMDZPY+p8w3kvFPo/L2eRjSnQ+cPffdUPo+IXl96d -N/49iXs6/d7PHw+pYszdgCfpPAAo4TtLJLVCWRs1ETSbZBIUOFywgE5P71egYVMgYKndRM5K -cY0ZUsGUX9InpItuD3R7vFwDL9cUHBonOJoax+rYeM7eLQvNncl4YAwJsUKOVDBy28QK2wmz -R6MsBTX8+vRkj3ZTCnP1+RBNllViYnq6absnAgHFdQ6OL4T2wKhAaYhukE1foFTNNI1wAm4s -iYAI20Me+54xMQZa3QvrokL/Wf9+qeajEDOTZWs1T3Sn+H3Dg3T25b8WOH3ULZE7R4FPr0Id -5u95nxKG2D2fkMXDwc0BeG+VWh3lCdjOBn2kyT+6TwM9d+/VQmY4vZdZFhI6nCUlxeKEg4wk -HW6kad5QPcUlS/3flNHM0bVLPrmNDb61bm+2sYPpgw0iy7JA5m8MceG57jS7q6Mo001cIya8 -EqrfBLZ0/0eLyIH81/RjFYwEoI54+QWe0ovdsqNTVnQsCcZnIRFTbMQqdInuCqrROIn+00xe -L0KNMh0iQO4zRaG0XhQaUxt2mIbkA0PuntsM8+I9DUIAqXgttwARAQABtERSdXN0IExhbmd1 -YWdlIChUYWcgYW5kIFJlbGVhc2UgU2lnbmluZyBLZXkpIDxydXN0LWtleUBydXN0LWxhbmcu -b3JnPokCPgQTAQIAKAUCTt1BjQIbAwUJAeEzgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA -CgkQCy1qKDAzY3azFg//V+IoiCurdYyS4nckMbr9gTn5SKaAtQUqMWAoJty3/lZ2jLq/9zO0 -TO9Zw0rcoVUORpl4VsGsUu0QIA53KJJLOto4hHGvDBsASm4x1o06Ftsp37YrMozRN+4capIR -Kx5uM3whSUTGponOQplj9ED3zw/FkFWF4ni2KAZMfRJQy6berIBBHNWbMtY/vneTwv0YZOah -sS23AQ958mVhOfDYYnmpEzHza9kl6le9RjmxuFX0bOOB+bHE4T3X0OmB2q4RJetwd18qRGGY -dy/e5xON13Y708gV2v4t3ZC3X+XT/+dwHHjoa6nWIxI5OU59AfnjBJIs09pHq2VYUCfdZiHL -YRTrMQkUyapjOwWV5tbCtYnCufjILk2vk1YBqj1vjco0tMH7llsEoQ4seg8NrwkZYZ8jccN9 -Aymb0ObZZgSVJCFN3akUESfh9wPDAQjmLjqWAOMNDSpnElIVAxLX1O/HNgRv7tl0Te14Goul -lhrWzTg5vPpOhSe+1SVUAUVcBwHcZl1opXCHQHfW2vkfe9w1hRBqEMOmr54TBXufxneNc/te -NuV+ZA4l9QvirmGtmQee4LQwz7d//IFGVxidsbOTVOU9hbijm/USJCK1BPqF36I2rB/8ve7h -qTwTVbvMRb8qWS2YhwRHsYrngXbun1vwwFouiW2KV5NEFNMt3pj+Rcu5Ag0ETt1BjQEQAMOf -6oCHj5ASMHCdKzSGF+ofIG3OWH7SUVRDKtJck75LyjbW/14SxNQCF6UvyjwhVWnnGmXiCED6 -cCOo9UdMhF46ojWe//mszSJRZTc0OvUpq9AIe3UA7mLHve4A+8fXBd1mpgciG8qD4vifdO4T -yvkb4dwxW+hpsenKHaM4hvQJFB1c33loEeGdfE/9svZyCO9T4FA6tdj5niLdtGtcJ6eC/6rp -53kcg4RLz9hOH39ouitqIHVqO/j+TW2M8kYgh1niBCGQm2kV5jeh7QUMe7TA3KHksAVqAKcJ -4TO538KswbC8MLz4+cdHpXf+kSUNnRzyndazjIF31XSyT8cDZHdfFHFkCA/4Xr7ebp+gub6R -qbCeCbds/UQ8L7NOqze9/qGuRBLTarXmvZ0AgELu/z4bPF6GyKcJjFYkMZQoAzYZfFc2pNW+ -WhWCusAz0aw+6NoZVI6bYhfY2w+kf3vebpzuKdD0Qublk5cKFCU9bV6BYqI9PbgBkErUgrgp -Zrjkc2c2u6uje0sKRxihdczr75Kikhb3M4BKQx3V5GyKdvo+61MhYurwWtyTylgMvlyL+3Bn -r0bg/vFbdwO4wgdNjR9UkjjABjuTExdnAqvf2+eBnYkuzxG60TH5At3CRTBshNUO9N0q1SGH -tGJkDOOxEZwAnUmE9jAG9CdeWxJNaUa5ABEBAAGJAiUEGAECAA8FAk7dQY0CGwwFCQHhM4AA -CgkQCy1qKDAzY3a9NBAAqpQKlFBCJV2h8GJU68OzFdxYIelhzH0KcInm6QREiUtU2+WAAyli -IbvsEL3c0hH0xykhwZx0wPmj7QQW7h5geOTvfLhNe/XMLsnlIRXBCSZKmlsZ8HfOVAXZTY61 -LM0v11eI6w0lCUC6GqWfzpph+uxUQjJ6YrGomj7nDrvj8Dp4S4UYaJc+1pcVPjO/XmZrZkb1 -6KnTm4RJcIW0iO61g7SDn8JZCmrDf9Ur+9NmRdynEeiWn9DUkbAXTKj09NiRyV+8mVmSGw4F -Jylqtk+X4WTu7qCm9C0S3ROuSSJOkCQGcE552GaS5RN9wdL/cG1PfqQjSaY0HMQzpBzV+nXa -2eFk3Bg2/qi4OghjR00Y3SQftDWI4K3opwVdsF7u9YH6PQoX4jl5DJIvtdIwwQJVaHLjVF4r -koV3ryFlL4Oq70TLwBSUlUhYoii5pokr3GdzloUWuuBa8AK5sM0RG/pybUPWK1PQnDlJJg6H -JyEC4EFfBWv2+nwt1K+vIRuCX9ZSd5YP9F4RbQjsnz7dimo5ooy3Wj7Fv7lQnQGkaUev0+hs -t9H7RfQEyREukTMxzXjKEW9EO4lJ20cif3l7Be+bw6OzKaEkVE3reZRnKxO6SejUYA7reye1 -HI1jilzwKSXuV2EmyBk3tKh9NwscT/A78pr30FxxPUg3v72raNgusTo= -=2z6P ------END PGP PUBLIC KEY BLOCK----- -" -} - -pub fn signing_key_fp() -> ~str { - ~"FE79 EDB0 3DEF B0D8 27D2 6C41 0B2D 6A28 3033 6376" -} - -pub fn supported() -> bool { - let r = gpgv(~[~"--version"]); - r.status == 0 -} - -pub fn init(root: &Path) { - let p = root.push("gpg"); - if !os::path_is_dir(&p) { - os::make_dir(&p, 0x1c0i32); - let mut p = run::start_program(~"gpg", ~[~"--homedir", - p.to_str(), - ~"--import"]); - p.input().write_str(signing_key()); - let s = p.finish(); - if s != 0 { - fail!(~"pgp init failed"); - } - } -} - -pub fn add(root: &Path, key: &Path) { - let path = root.push("gpg"); - let p = - run::program_output(~"gpg", ~[~"--homedir", path.to_str(), - ~"--import", key.to_str()]); - if p.status != 0 { - fail!(~"pgp add failed: " + p.out); - } -} - -pub fn verify(root: &Path, data: &Path, sig: &Path) -> bool { - let path = root.push("gpg"); - let res = gpgv(~[~"--homedir", path.to_str(), - ~"--keyring", ~"pubring.gpg", - ~"--verbose", - sig.to_str(), data.to_str()]); - if res.status != 0 { - return false; - } - return true; -} diff --git a/src/libcargo/sources.json b/src/libcargo/sources.json deleted file mode 100644 index 66e14f9eff7d4..0000000000000 --- a/src/libcargo/sources.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "central": { - "url": "https://raw.github.com/mozilla/cargo-central/master/packages.json" - }, - "elly": { - "url": "https://raw.github.com/elly/rust-packages/master/packages.json", - "sig": "https://raw.github.com/elly/rust-packages/master/packages.json.sig", - "key": "https://raw.github.com/elly/rust-packages/master/signing-key.gpg", - "keyfp": "4107 21C0 FF32 858F 61FF 33F6 E595 8E36 FDC8 EA00" - }, - "erickt": { - "url": "https://raw.github.com/erickt/rust-packages/master/packages.json" - } -} diff --git a/src/libcore/core.rc b/src/libcore/core.rc index 5b6c40e09ef07..da13145b9f60f 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -170,7 +170,6 @@ pub mod condition; pub mod logging; pub mod util; - /* Reexported core operators */ pub use kinds::{Const, Copy, Owned, Durable}; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 59c4ce6b9dc57..2189f109eff69 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -14,6 +14,7 @@ use cmath; use cmp; use libc::{c_float, c_int}; use num::NumCast; +use num::strconv; use num; use ops; use option::Option; @@ -376,8 +377,8 @@ impl num::Round for f32 { */ #[inline(always)] pub pure fn to_str(num: f32) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -390,8 +391,8 @@ pub pure fn to_str(num: f32) -> ~str { */ #[inline(always)] pub pure fn to_str_hex(num: f32) -> ~str { - let (r, _) = num::to_str_common( - &num, 16u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -411,8 +412,8 @@ pub pure fn to_str_hex(num: f32) -> ~str { */ #[inline(always)] pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str { - let (r, special) = num::to_str_common( - &num, rdx, true, true, num::SignNeg, num::DigAll); + let (r, special) = strconv::to_str_common( + &num, rdx, true, strconv::SignNeg, strconv::DigAll); if special { fail!(~"number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -429,7 +430,8 @@ pub pure fn to_str_radix(num: f32, rdx: uint) -> ~str { */ #[inline(always)] pub pure fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { - num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll) + strconv::to_str_common(&num, rdx, true, + strconv::SignNeg, strconv::DigAll) } /** @@ -443,8 +445,8 @@ pub pure fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { */ #[inline(always)] pub pure fn to_str_exact(num: f32, dig: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigExact(dig)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); r } @@ -459,8 +461,8 @@ pub pure fn to_str_exact(num: f32, dig: uint) -> ~str { */ #[inline(always)] pub pure fn to_str_digits(num: f32, dig: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigMax(dig)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); r } @@ -505,7 +507,8 @@ impl num::ToStrRadix for f32 { */ #[inline(always)] pub pure fn from_str(num: &str) -> Option { - num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false) } /** @@ -537,7 +540,8 @@ pub pure fn from_str(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_hex(num: &str) -> Option { - num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false) } /** @@ -561,7 +565,8 @@ pub pure fn from_str_hex(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_radix(num: &str, rdx: uint) -> Option { - num::from_str_common(num, rdx, true, true, false, num::ExpNone, false) + strconv::from_str_common(num, rdx, true, true, false, + strconv::ExpNone, false) } impl from_str::FromStr for f32 { diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index b24c76a5e0a95..df68b6153cb42 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -15,6 +15,7 @@ use cmp; use libc::{c_double, c_int}; use libc; use num::NumCast; +use num::strconv; use num; use ops; use option::Option; @@ -401,8 +402,8 @@ impl num::Round for f64 { */ #[inline(always)] pub pure fn to_str(num: f64) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -415,8 +416,8 @@ pub pure fn to_str(num: f64) -> ~str { */ #[inline(always)] pub pure fn to_str_hex(num: f64) -> ~str { - let (r, _) = num::to_str_common( - &num, 16u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -436,8 +437,8 @@ pub pure fn to_str_hex(num: f64) -> ~str { */ #[inline(always)] pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str { - let (r, special) = num::to_str_common( - &num, rdx, true, true, num::SignNeg, num::DigAll); + let (r, special) = strconv::to_str_common( + &num, rdx, true, strconv::SignNeg, strconv::DigAll); if special { fail!(~"number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -454,7 +455,8 @@ pub pure fn to_str_radix(num: f64, rdx: uint) -> ~str { */ #[inline(always)] pub pure fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { - num::to_str_common(&num, rdx, true, true, num::SignNeg, num::DigAll) + strconv::to_str_common(&num, rdx, true, + strconv::SignNeg, strconv::DigAll) } /** @@ -468,8 +470,8 @@ pub pure fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { */ #[inline(always)] pub pure fn to_str_exact(num: f64, dig: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigExact(dig)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); r } @@ -484,8 +486,8 @@ pub pure fn to_str_exact(num: f64, dig: uint) -> ~str { */ #[inline(always)] pub pure fn to_str_digits(num: f64, dig: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigMax(dig)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); r } @@ -530,7 +532,8 @@ impl num::ToStrRadix for f64 { */ #[inline(always)] pub pure fn from_str(num: &str) -> Option { - num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false) } /** @@ -562,7 +565,8 @@ pub pure fn from_str(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_hex(num: &str) -> Option { - num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false) } /** @@ -586,7 +590,8 @@ pub pure fn from_str_hex(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_radix(num: &str, rdx: uint) -> Option { - num::from_str_common(num, rdx, true, true, false, num::ExpNone, false) + strconv::from_str_common(num, rdx, true, true, false, + strconv::ExpNone, false) } impl from_str::FromStr for f64 { diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs index 51b115e84c3ec..b857f76570ff5 100644 --- a/src/libcore/num/float.rs +++ b/src/libcore/num/float.rs @@ -26,6 +26,7 @@ use cmp::{Eq, Ord}; use cmp; use f64; use num::NumCast; +use num::strconv; use num; use ops; use option::{None, Option, Some}; @@ -107,8 +108,8 @@ pub mod consts { */ #[inline(always)] pub pure fn to_str(num: float) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -121,8 +122,8 @@ pub pure fn to_str(num: float) -> ~str { */ #[inline(always)] pub pure fn to_str_hex(num: float) -> ~str { - let (r, _) = num::to_str_common( - &num, 16u, true, true, num::SignNeg, num::DigAll); + let (r, _) = strconv::to_str_common( + &num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -142,8 +143,8 @@ pub pure fn to_str_hex(num: float) -> ~str { */ #[inline(always)] pub pure fn to_str_radix(num: float, radix: uint) -> ~str { - let (r, special) = num::to_str_common( - &num, radix, true, true, num::SignNeg, num::DigAll); + let (r, special) = strconv::to_str_common( + &num, radix, true, strconv::SignNeg, strconv::DigAll); if special { fail!(~"number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -160,7 +161,8 @@ pub pure fn to_str_radix(num: float, radix: uint) -> ~str { */ #[inline(always)] pub pure fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { - num::to_str_common(&num, radix, true, true, num::SignNeg, num::DigAll) + strconv::to_str_common(&num, radix, true, + strconv::SignNeg, strconv::DigAll) } /** @@ -174,8 +176,8 @@ pub pure fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { */ #[inline(always)] pub pure fn to_str_exact(num: float, digits: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigExact(digits)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigExact(digits)); r } @@ -196,8 +198,8 @@ pub fn test_to_str_exact_do_decimal() { */ #[inline(always)] pub pure fn to_str_digits(num: float, digits: uint) -> ~str { - let (r, _) = num::to_str_common( - &num, 10u, true, true, num::SignNeg, num::DigMax(digits)); + let (r, _) = strconv::to_str_common( + &num, 10u, true, strconv::SignNeg, strconv::DigMax(digits)); r } @@ -242,7 +244,8 @@ impl num::ToStrRadix for float { */ #[inline(always)] pub pure fn from_str(num: &str) -> Option { - num::from_str_common(num, 10u, true, true, true, num::ExpDec, false) + strconv::from_str_common(num, 10u, true, true, true, + strconv::ExpDec, false) } /** @@ -274,7 +277,8 @@ pub pure fn from_str(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_hex(num: &str) -> Option { - num::from_str_common(num, 16u, true, true, true, num::ExpBin, false) + strconv::from_str_common(num, 16u, true, true, true, + strconv::ExpBin, false) } /** @@ -298,7 +302,8 @@ pub pure fn from_str_hex(num: &str) -> Option { */ #[inline(always)] pub pure fn from_str_radix(num: &str, radix: uint) -> Option { - num::from_str_common(num, radix, true, true, false, num::ExpNone, false) + strconv::from_str_common(num, radix, true, true, false, + strconv::ExpNone, false) } impl from_str::FromStr for float { diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs index eaaa78b84f837..8d72878ef6acb 100644 --- a/src/libcore/num/int-template.rs +++ b/src/libcore/num/int-template.rs @@ -16,6 +16,7 @@ use cmp; use to_str::ToStr; use from_str::FromStr; use num::{ToStrRadix, FromStrRadix}; +use num::strconv; use num; use prelude::*; use str; @@ -176,18 +177,6 @@ impl num::One for T { static pure fn one() -> T { 1 } } -impl num::Round for T { - #[inline(always)] - pure fn round(&self, _: num::RoundMode) -> T { *self } - - #[inline(always)] - pure fn floor(&self) -> T { *self } - #[inline(always)] - pure fn ceil(&self) -> T { *self } - #[inline(always)] - pure fn fract(&self) -> T { 0 } -} - #[cfg(notest)] impl ops::Add for T { pure fn add(&self, other: &T) -> T { *self + *other } @@ -218,22 +207,22 @@ impl ops::Neg for T { /// Parse a string as a number in base 10. #[inline(always)] pub pure fn from_str(s: &str) -> Option { - num::from_str_common(s, 10u, true, false, false, - num::ExpNone, false) + strconv::from_str_common(s, 10u, true, false, false, + strconv::ExpNone, false) } /// Parse a string as a number in the given base. #[inline(always)] pub pure fn from_str_radix(s: &str, radix: uint) -> Option { - num::from_str_common(s, radix, true, false, false, - num::ExpNone, false) + strconv::from_str_common(s, radix, true, false, false, + strconv::ExpNone, false) } /// Parse a byte slice as a number in the given base. #[inline(always)] pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option { - num::from_str_bytes_common(buf, radix, true, false, false, - num::ExpNone, false) + strconv::from_str_bytes_common(buf, radix, true, false, false, + strconv::ExpNone, false) } impl FromStr for T { @@ -255,24 +244,24 @@ impl FromStrRadix for T { /// Convert to a string as a byte slice in a given base. #[inline(always)] pub pure fn to_str_bytes(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U { - let (buf, _) = num::to_str_bytes_common(&n, radix, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, + strconv::SignNeg, strconv::DigAll); f(buf) } /// Convert to a string in base 10. #[inline(always)] pub pure fn to_str(num: T) -> ~str { - let (buf, _) = num::to_str_common(&num, 10u, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_common(&num, 10u, false, + strconv::SignNeg, strconv::DigAll); buf } /// Convert to a string in a given base. #[inline(always)] pub pure fn to_str_radix(num: T, radix: uint) -> ~str { - let (buf, _) = num::to_str_common(&num, radix, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_common(&num, radix, false, + strconv::SignNeg, strconv::DigAll); buf } diff --git a/src/libcore/num/num.rs b/src/libcore/num/num.rs index 44cd66363fb28..e720c2fa108ae 100644 --- a/src/libcore/num/num.rs +++ b/src/libcore/num/num.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -17,6 +17,8 @@ use str; use kinds::Copy; use vec; +pub mod strconv; + pub trait IntConvertible { pure fn to_int(&self) -> int; static pure fn from_int(n: int) -> Self; @@ -42,6 +44,13 @@ pub trait Round { pure fn fract(&self) -> Self; } +pub enum RoundMode { + RoundDown, + RoundUp, + RoundToZero, + RoundFromZero +} + /** * Cast a number the the enclosing type * @@ -80,13 +89,6 @@ pub trait NumCast { pure fn to_float(&self) -> float; } -pub enum RoundMode { - RoundDown, - RoundUp, - RoundToZero, - RoundFromZero -} - pub trait ToStrRadix { pub pure fn to_str_radix(&self, radix: uint) -> ~str; } @@ -97,62 +99,6 @@ pub trait FromStrRadix { // Generic math functions: -/// Dynamically calculates the value `inf` (`1/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn infinity>() -> T { - let _0: T = Zero::zero(); - let _1: T = One::one(); - _1 / _0 -} - -/// Dynamically calculates the value `-inf` (`-1/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn neg_infinity+Neg>() -> T { - let _0: T = Zero::zero(); - let _1: T = One::one(); - - _1 / _0 -} - -/// Dynamically calculates the value `NaN` (`0/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn NaN>() -> T { - let _0: T = Zero::zero(); - _0 / _0 -} - -/// Returns `true` if `num` has the value `inf` (`1/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn is_infinity>(num: &T) -> bool { - (*num) == (infinity::()) -} - -/// Returns `true` if `num` has the value `-inf` (`-1/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn is_neg_infinity+Neg>(num: &T) - -> bool { - (*num) == (neg_infinity::()) -} - -/// Returns `true` if `num` has the value `NaN` (is not equal to itself). -#[inline(always)] -pub pure fn is_NaN(num: &T) -> bool { - (*num) != (*num) -} - -/// Returns `true` if `num` has the value `-0` (`1/num == -1/0`). -/// Can fail on integer types. -#[inline(always)] -pub pure fn is_neg_zero+Neg>(num: &T) -> bool { - let _1: T = One::one(); - let _0: T = Zero::zero(); - *num == _0 && is_neg_infinity(&(_1 / *num)) -} - /** * Calculates a power to a given radix, optimized for uint `pow` and `radix`. * @@ -186,540 +132,3 @@ pub pure fn pow_with_uint+Mul>( total } -pub enum ExponentFormat { - ExpNone, - ExpDec, - ExpBin -} - -pub enum SignificantDigits { - DigAll, - DigMax(uint), - DigExact(uint) -} - -pub enum SignFormat { - SignNone, - SignNeg, - SignAll -} - -/** - * Converts a number to its string representation as a byte vector. - * This is meant to be a common base implementation for all numeric string - * conversion functions like `to_str()` or `to_str_radix()`. - * - * # Arguments - * - `num` - The number to convert. Accepts any number that - * implements the numeric traits. - * - `radix` - Base to use. Accepts only the values 2-36. - * - `special` - Whether to attempt to compare to special values like - * `inf` or `NaN`. Also needed to detect negative 0. - * Can fail if it doesn't match `num`s type - * (see safety note). - * - `negative_zero` - Whether to treat the special value `-0` as - * `-0` or as `+0`. - * - `sign` - How to emit the sign. Options are: - * - `SignNone`: No sign at all. Basically emits `abs(num)`. - * - `SignNeg`: Only `-` on negative values. - * - `SignAll`: Both `+` on positive, and `-` on negative numbers. - * - `digits` - The amount of digits to use for emitting the - * fractional part, if any. Options are: - * - `DigAll`: All calculatable digits. Beware of bignums or - * fractions! - * - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros. - * - `DigExact(uint)`: Exactly N digits. - * - * # Return value - * A tuple containing the byte vector, and a boolean flag indicating - * whether it represents a special value like `inf`, `-inf`, `NaN` or not. - * It returns a tuple because there can be ambiguity between a special value - * and a number representation at higher bases. - * - * # Failure - * - Fails if `radix` < 2 or `radix` > 36. - * - Fails on wrong value for `special` (see safety note). - * - * # Safety note - * The function detects the special values `inf`, `-inf` and `NaN` by - * dynamically comparing `num` to `1 / 0`, `-1 / 0` and `0 / 0` - * (each of type T) if `special` is `true`. This will fail on integer types - * with a 'divide by zero'. Likewise, it will fail if `num` **is** one of - * those special values, and `special` is `false`, because then the - * algorithm just does normal calculations on them. - */ -pub pure fn to_str_bytes_common+ - Neg+Modulo+Mul>( - num: &T, radix: uint, special: bool, negative_zero: bool, - sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { - if radix as int < 2 { - fail!(fmt!("to_str_bytes_common: radix %? to low, \ - must lie in the range [2, 36]", radix)); - } else if radix as int > 36 { - fail!(fmt!("to_str_bytes_common: radix %? to high, \ - must lie in the range [2, 36]", radix)); - } - - let _0: T = Zero::zero(); - let _1: T = One::one(); - - if special { - if is_NaN(num) { - return (str::to_bytes("NaN"), true); - } else if is_infinity(num){ - return match sign { - SignAll => (str::to_bytes("+inf"), true), - _ => (str::to_bytes("inf"), true) - } - } else if is_neg_infinity(num) { - return match sign { - SignNone => (str::to_bytes("inf"), true), - _ => (str::to_bytes("-inf"), true), - } - } - } - - let neg = *num < _0 || (negative_zero && *num == _0 - && special && is_neg_zero(num)); - let mut buf: ~[u8] = ~[]; - let radix_gen: T = cast(radix as int); - - let mut deccum; - - // First emit the non-fractional part, looping at least once to make - // sure at least a `0` gets emitted. - deccum = num.round(RoundToZero); - loop { - // Calculate the absolute value of each digit instead of only - // doing it once for the whole number because a - // representable negative number doesn't necessary have an - // representable additive inverse of the same type - // (See twos complement). But we assume that for the - // numbers [-35 .. 0] we always have [0 .. 35]. - let current_digit_signed = deccum % radix_gen; - let current_digit = if current_digit_signed < _0 { - -current_digit_signed - } else { - current_digit_signed - }; - - // Decrease the deccumulator one digit at a time - deccum /= radix_gen; - deccum = deccum.round(RoundToZero); - - unsafe { // FIXME: Pureness workaround (#4568) - buf.push(char::from_digit(current_digit.to_int() as uint, radix) - .unwrap() as u8); - } - - // No more digits to calculate for the non-fractional part -> break - if deccum == _0 { break; } - } - - // If limited digits, calculate one digit more for rounding. - let (limit_digits, digit_count, exact) = match digits { - DigAll => (false, 0u, false), - DigMax(count) => (true, count+1, false), - DigExact(count) => (true, count+1, true) - }; - - // Decide what sign to put in front - match sign { - SignNeg | SignAll if neg => { - unsafe { // FIXME: Pureness workaround (#4568) - buf.push('-' as u8); - } - } - SignAll => { - unsafe { // FIXME: Pureness workaround (#4568) - buf.push('+' as u8); - } - } - _ => () - } - - unsafe { // FIXME: Pureness workaround (#4568) - vec::reverse(buf); - } - - // Remember start of the fractional digits. - // Points one beyond end of buf if none get generated, - // or at the '.' otherwise. - let start_fractional_digits = buf.len(); - - // Now emit the fractional part, if any - deccum = num.fract(); - if deccum != _0 || (limit_digits && exact && digit_count > 0) { - unsafe { // FIXME: Pureness workaround (#4568) - buf.push('.' as u8); - } - let mut dig = 0u; - - // calculate new digits while - // - there is no limit and there are digits left - // - or there is a limit, it's not reached yet and - // - it's exact - // - or it's a maximum, and there are still digits left - while (!limit_digits && deccum != _0) - || (limit_digits && dig < digit_count && ( - exact - || (!exact && deccum != _0) - ) - ) { - // Shift first fractional digit into the integer part - deccum *= radix_gen; - - // Calculate the absolute value of each digit. - // See note in first loop. - let current_digit_signed = deccum.round(RoundToZero); - let current_digit = if current_digit_signed < _0 { - -current_digit_signed - } else { - current_digit_signed - }; - - unsafe { // FIXME: Pureness workaround (#4568) - buf.push(char::from_digit( - current_digit.to_int() as uint, radix).unwrap() as u8); - } - - // Decrease the deccumulator one fractional digit at a time - deccum = deccum.fract(); - dig += 1u; - } - - // If digits are limited, and that limit has been reached, - // cut off the one extra digit, and depending on its value - // round the remaining ones. - if limit_digits && dig == digit_count { - let ascii2value = |chr: u8| { - char::to_digit(chr as char, radix).unwrap() as uint - }; - let value2ascii = |val: uint| { - char::from_digit(val, radix).unwrap() as u8 - }; - - unsafe { // FIXME: Pureness workaround (#4568) - let extra_digit = ascii2value(buf.pop()); - if extra_digit >= radix / 2 { // -> need to round - let mut i: int = buf.len() as int - 1; - loop { - // If reached left end of number, have to - // insert additional digit: - if i < 0 - || buf[i] == '-' as u8 - || buf[i] == '+' as u8 { - buf.insert((i + 1) as uint, value2ascii(1)); - break; - } - - // Skip the '.' - if buf[i] == '.' as u8 { i -= 1; loop; } - - // Either increment the digit, - // or set to 0 if max and carry the 1. - let current_digit = ascii2value(buf[i]); - if current_digit < (radix - 1) { - buf[i] = value2ascii(current_digit+1); - break; - } else { - buf[i] = value2ascii(0); - i -= 1; - } - } - } - } - } - } - - // if number of digits is not exact, remove all trailing '0's up to - // and including the '.' - if !exact { - let buf_max_i = buf.len() - 1; - - // index to truncate from - let mut i = buf_max_i; - - // discover trailing zeros of fractional part - while i > start_fractional_digits && buf[i] == '0' as u8 { - i -= 1; - } - - // Only attempt to truncate digits if buf has fractional digits - if i >= start_fractional_digits { - // If buf ends with '.', cut that too. - if buf[i] == '.' as u8 { i -= 1 } - - // only resize buf if we actually remove digits - if i < buf_max_i { - buf = buf.slice(0, i + 1); - } - } - } // If exact and trailing '.', just cut that - else { - let max_i = buf.len() - 1; - if buf[max_i] == '.' as u8 { - buf = buf.slice(0, max_i); - } - } - - (buf, false) -} - -/** - * Converts a number to its string representation. This is a wrapper for - * `to_str_bytes_common()`, for details see there. - */ -#[inline(always)] -pub pure fn to_str_common+Neg - +Modulo+Mul>( - num: &T, radix: uint, special: bool, negative_zero: bool, - sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { - let (bytes, special) = to_str_bytes_common(num, radix, special, - negative_zero, sign, digits); - (str::from_bytes(bytes), special) -} - -// Some constants for from_str_bytes_common's input validation, -// they define minimum radix values for which the character is a valid digit. -priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; -priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; -priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; - -/** - * Parses a byte slice as a number. This is meant to - * be a common base implementation for all numeric string conversion - * functions like `from_str()` or `from_str_radix()`. - * - * # Arguments - * - `buf` - The byte slice to parse. - * - `radix` - Which base to parse the number as. Accepts 2-36. - * - `negative` - Whether to accept negative numbers. - * - `fractional` - Whether to accept numbers with fractional parts. - * - `special` - Whether to accept special values like `inf` - * and `NaN`. Can conflict with `radix`, see Failure. - * - `exponent` - Which exponent format to accept. Options are: - * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or - * `-8.2`. - * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or - * `8.2E-2`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or - * `FFp128`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - `empty_zero` - Whether to accept a empty `buf` as a 0 or not. - * - * # Return value - * Returns `Some(n)` if `buf` parses to a number n without overflowing, and - * `None` otherwise, depending on the constraints set by the remaining - * arguments. - * - * # Failure - * - Fails if `radix` < 2 or `radix` > 36. - * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict - * between digit and exponent sign `'e'`. - * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict - * between digit and exponent sign `'p'`. - * - Fails if `radix` > 18 and `special == true` due to conflict - * between digit and lowest first character in `inf` and `NaN`, the `'i'`. - * - * # Possible improvements - * - Could accept option to allow ignoring underscores, allowing for numbers - * formated like `FF_AE_FF_FF`. - */ -pub pure fn from_str_bytes_common+ - Mul+Sub+Neg+Add>( - buf: &[u8], radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool - ) -> Option { - match exponent { - ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' - => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ - use of 'e' as decimal exponent", radix)), - ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' - => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ - use of 'p' as binary exponent", radix)), - _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' - => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ - special values 'inf' and 'NaN'", radix)), - _ if radix as int < 2 - => fail!(fmt!("from_str_bytes_common: radix %? to low, \ - must lie in the range [2, 36]", radix)), - _ if radix as int > 36 - => fail!(fmt!("from_str_bytes_common: radix %? to high, \ - must lie in the range [2, 36]", radix)), - _ => () - } - - let _0: T = Zero::zero(); - let _1: T = One::one(); - let radix_gen: T = cast(radix as int); - - let len = buf.len(); - - if len == 0 { - if empty_zero { - return Some(_0); - } else { - return None; - } - } - - if special { - if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") { - return Some(infinity()); - } else if buf == str::to_bytes("-inf") { - if negative { - return Some(neg_infinity()); - } else { - return None; - } - } else if buf == str::to_bytes("NaN") { - return Some(NaN()); - } - } - - let (start, accum_positive) = match buf[0] { - '-' as u8 if !negative => return None, - '-' as u8 => (1u, false), - '+' as u8 => (1u, true), - _ => (0u, true) - }; - - // Initialize accumulator with signed zero for floating point parsing to - // work - let mut accum = if accum_positive { _0 } else { -_1 * _0}; - let mut last_accum = accum; // Necessary to detect overflow - let mut i = start; - let mut exp_found = false; - - // Parse integer part of number - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { - Some(digit) => { - // shift accum one digit left - accum *= radix_gen; - - // add/subtract current digit depending on sign - if accum_positive { - accum += cast(digit as int); - } else { - accum -= cast(digit as int); - } - - // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return None; } - if !accum_positive && accum > last_accum { return None; } - last_accum = accum; - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent - } - '.' if fractional => { - i += 1u; // skip the '.' - break; // start of fractional part - } - _ => return None // invalid number - } - } - - i += 1u; - } - - // Parse fractional part of number - // Skip if already reached start of exponent - if !exp_found { - let mut power = _1; - - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { - Some(digit) => { - // Decrease power one order of magnitude - power /= radix_gen; - - let digit_t: T = cast(digit); - - // add/subtract current digit depending on sign - if accum_positive { - accum += digit_t * power; - } else { - accum -= digit_t * power; - } - - // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return None; } - if !accum_positive && accum > last_accum { return None; } - last_accum = accum; - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent - } - _ => return None // invalid number - } - } - - i += 1u; - } - } - - // Special case: buf not empty, but does not contain any digit in front - // of the exponent sign -> number is empty string - if i == start { - if empty_zero { - return Some(_0); - } else { - return None; - } - } - - let mut multiplier = _1; - - if exp_found { - let c = buf[i] as char; - let base = match (c, exponent) { - ('e', ExpDec) | ('E', ExpDec) => 10u, - ('p', ExpBin) | ('P', ExpBin) => 2u, - _ => return None // char doesn't fit given exponent format - }; - - // parse remaining bytes as decimal integer, - // skipping the exponent char - let exp: Option = from_str_bytes_common( - buf.view(i+1, len), 10, true, false, false, ExpNone, false); - - match exp { - Some(exp_pow) => { - multiplier = if exp_pow < 0 { - _1 / pow_with_uint::(base, (-exp_pow.to_int()) as uint) - } else { - pow_with_uint::(base, exp_pow.to_int() as uint) - } - } - None => return None // invalid exponent -> invalid number - } - } - - Some(accum * multiplier) -} - -/** - * Parses a string as a number. This is a wrapper for - * `from_str_bytes_common()`, for details see there. - */ -#[inline(always)] -pub pure fn from_str_common+Mul+ - Sub+Neg+Add>( - buf: &str, radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool - ) -> Option { - from_str_bytes_common(str::to_bytes(buf), radix, negative, - fractional, special, exponent, empty_zero) -} diff --git a/src/libcore/num/strconv.rs b/src/libcore/num/strconv.rs new file mode 100644 index 0000000000000..4322ea40428fb --- /dev/null +++ b/src/libcore/num/strconv.rs @@ -0,0 +1,639 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp::{Ord, Eq}; +use ops::{Add, Div, Modulo, Mul, Neg, Sub}; +use option::{None, Option, Some}; +use char; +use str; +use kinds::Copy; +use vec; +use num::{NumCast, Zero, One, cast, pow_with_uint}; +use f64; + +pub enum ExponentFormat { + ExpNone, + ExpDec, + ExpBin +} + +pub enum SignificantDigits { + DigAll, + DigMax(uint), + DigExact(uint) +} + +pub enum SignFormat { + SignNone, + SignNeg, + SignAll +} + +#[inline(always)] +pure fn is_NaN(num: &T) -> bool { + *num != *num +} + +#[inline(always)] +pure fn is_inf(num: &T) -> bool { + match NumStrConv::inf() { + None => false, + Some(n) => *num == n + } +} + +#[inline(always)] +pure fn is_neg_inf(num: &T) -> bool { + match NumStrConv::neg_inf() { + None => false, + Some(n) => *num == n + } +} + +#[inline(always)] +pure fn is_neg_zero>(num: &T) -> bool { + let _0: T = Zero::zero(); + let _1: T = One::one(); + + *num == _0 && is_neg_inf(&(_1 / *num)) +} + +pub trait NumStrConv { + static pure fn NaN() -> Option; + static pure fn inf() -> Option; + static pure fn neg_inf() -> Option; + static pure fn neg_zero() -> Option; + + pure fn round_to_zero(&self) -> Self; + pure fn fractional_part(&self) -> Self; +} + +macro_rules! impl_NumStrConv_Floating (($t:ty) => ( + impl NumStrConv for $t { + #[inline(always)] + static pure fn NaN() -> Option<$t> { Some( 0.0 / 0.0) } + #[inline(always)] + static pure fn inf() -> Option<$t> { Some( 1.0 / 0.0) } + #[inline(always)] + static pure fn neg_inf() -> Option<$t> { Some(-1.0 / 0.0) } + #[inline(always)] + static pure fn neg_zero() -> Option<$t> { Some(-0.0 ) } + + #[inline(always)] + pure fn round_to_zero(&self) -> $t { + ( if *self < 0.0 { f64::ceil(*self as f64) } + else { f64::floor(*self as f64) } + ) as $t + } + + #[inline(always)] + pure fn fractional_part(&self) -> $t { + *self - self.round_to_zero() + } + } +)) + +macro_rules! impl_NumStrConv_Integer (($t:ty) => ( + impl NumStrConv for $t { + #[inline(always)] static pure fn NaN() -> Option<$t> { None } + #[inline(always)] static pure fn inf() -> Option<$t> { None } + #[inline(always)] static pure fn neg_inf() -> Option<$t> { None } + #[inline(always)] static pure fn neg_zero() -> Option<$t> { None } + + #[inline(always)] pure fn round_to_zero(&self) -> $t { *self } + #[inline(always)] pure fn fractional_part(&self) -> $t { 0 } + } +)) + +// FIXME: #4955 +// Replace by two generic impls for traits 'Integral' and 'Floating' +impl_NumStrConv_Floating!(float) +impl_NumStrConv_Floating!(f32) +impl_NumStrConv_Floating!(f64) + +impl_NumStrConv_Integer!(int) +impl_NumStrConv_Integer!(i8) +impl_NumStrConv_Integer!(i16) +impl_NumStrConv_Integer!(i32) +impl_NumStrConv_Integer!(i64) + +impl_NumStrConv_Integer!(uint) +impl_NumStrConv_Integer!(u8) +impl_NumStrConv_Integer!(u16) +impl_NumStrConv_Integer!(u32) +impl_NumStrConv_Integer!(u64) + +/** + * Converts a number to its string representation as a byte vector. + * This is meant to be a common base implementation for all numeric string + * conversion functions like `to_str()` or `to_str_radix()`. + * + * # Arguments + * - `num` - The number to convert. Accepts any number that + * implements the numeric traits. + * - `radix` - Base to use. Accepts only the values 2-36. + * - `negative_zero` - Whether to treat the special value `-0` as + * `-0` or as `+0`. + * - `sign` - How to emit the sign. Options are: + * - `SignNone`: No sign at all. Basically emits `abs(num)`. + * - `SignNeg`: Only `-` on negative values. + * - `SignAll`: Both `+` on positive, and `-` on negative numbers. + * - `digits` - The amount of digits to use for emitting the + * fractional part, if any. Options are: + * - `DigAll`: All calculatable digits. Beware of bignums or + * fractions! + * - `DigMax(uint)`: Maximum N digits, truncating any trailing zeros. + * - `DigExact(uint)`: Exactly N digits. + * + * # Return value + * A tuple containing the byte vector, and a boolean flag indicating + * whether it represents a special value like `inf`, `-inf`, `NaN` or not. + * It returns a tuple because there can be ambiguity between a special value + * and a number representation at higher bases. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + */ +pub pure fn to_str_bytes_common+Neg+Modulo+Mul>( + num: &T, radix: uint, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { + if radix as int < 2 { + fail!(fmt!("to_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix)); + } else if radix as int > 36 { + fail!(fmt!("to_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix)); + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + + if is_NaN(num) { + return (str::to_bytes("NaN"), true); + } + else if is_inf(num){ + return match sign { + SignAll => (str::to_bytes("+inf"), true), + _ => (str::to_bytes("inf"), true) + } + } + else if is_neg_inf(num) { + return match sign { + SignNone => (str::to_bytes("inf"), true), + _ => (str::to_bytes("-inf"), true), + } + } + + let neg = *num < _0 || (negative_zero && is_neg_zero(num)); + let mut buf: ~[u8] = ~[]; + let radix_gen: T = cast(radix as int); + + let mut deccum; + + // First emit the non-fractional part, looping at least once to make + // sure at least a `0` gets emitted. + deccum = num.round_to_zero(); + loop { + // Calculate the absolute value of each digit instead of only + // doing it once for the whole number because a + // representable negative number doesn't necessary have an + // representable additive inverse of the same type + // (See twos complement). But we assume that for the + // numbers [-35 .. 0] we always have [0 .. 35]. + let current_digit_signed = deccum % radix_gen; + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + // Decrease the deccumulator one digit at a time + deccum /= radix_gen; + deccum = deccum.round_to_zero(); + + unsafe { // FIXME: Pureness workaround (#4568) + buf.push(char::from_digit(current_digit.to_int() as uint, radix) + .unwrap() as u8); + } + + // No more digits to calculate for the non-fractional part -> break + if deccum == _0 { break; } + } + + // If limited digits, calculate one digit more for rounding. + let (limit_digits, digit_count, exact) = match digits { + DigAll => (false, 0u, false), + DigMax(count) => (true, count+1, false), + DigExact(count) => (true, count+1, true) + }; + + // Decide what sign to put in front + match sign { + SignNeg | SignAll if neg => { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('-' as u8); + } + } + SignAll => { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('+' as u8); + } + } + _ => () + } + + unsafe { // FIXME: Pureness workaround (#4568) + vec::reverse(buf); + } + + // Remember start of the fractional digits. + // Points one beyond end of buf if none get generated, + // or at the '.' otherwise. + let start_fractional_digits = buf.len(); + + // Now emit the fractional part, if any + deccum = num.fractional_part(); + if deccum != _0 || (limit_digits && exact && digit_count > 0) { + unsafe { // FIXME: Pureness workaround (#4568) + buf.push('.' as u8); + } + let mut dig = 0u; + + // calculate new digits while + // - there is no limit and there are digits left + // - or there is a limit, it's not reached yet and + // - it's exact + // - or it's a maximum, and there are still digits left + while (!limit_digits && deccum != _0) + || (limit_digits && dig < digit_count && ( + exact + || (!exact && deccum != _0) + ) + ) { + // Shift first fractional digit into the integer part + deccum *= radix_gen; + + // Calculate the absolute value of each digit. + // See note in first loop. + let current_digit_signed = deccum.round_to_zero(); + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + + unsafe { // FIXME: Pureness workaround (#4568) + buf.push(char::from_digit( + current_digit.to_int() as uint, radix).unwrap() as u8); + } + + // Decrease the deccumulator one fractional digit at a time + deccum = deccum.fractional_part(); + dig += 1u; + } + + // If digits are limited, and that limit has been reached, + // cut off the one extra digit, and depending on its value + // round the remaining ones. + if limit_digits && dig == digit_count { + let ascii2value = |chr: u8| { + char::to_digit(chr as char, radix).unwrap() as uint + }; + let value2ascii = |val: uint| { + char::from_digit(val, radix).unwrap() as u8 + }; + + unsafe { // FIXME: Pureness workaround (#4568) + let extra_digit = ascii2value(buf.pop()); + if extra_digit >= radix / 2 { // -> need to round + let mut i: int = buf.len() as int - 1; + loop { + // If reached left end of number, have to + // insert additional digit: + if i < 0 + || buf[i] == '-' as u8 + || buf[i] == '+' as u8 { + buf.insert((i + 1) as uint, value2ascii(1)); + break; + } + + // Skip the '.' + if buf[i] == '.' as u8 { i -= 1; loop; } + + // Either increment the digit, + // or set to 0 if max and carry the 1. + let current_digit = ascii2value(buf[i]); + if current_digit < (radix - 1) { + buf[i] = value2ascii(current_digit+1); + break; + } else { + buf[i] = value2ascii(0); + i -= 1; + } + } + } + } + } + } + + // if number of digits is not exact, remove all trailing '0's up to + // and including the '.' + if !exact { + let buf_max_i = buf.len() - 1; + + // index to truncate from + let mut i = buf_max_i; + + // discover trailing zeros of fractional part + while i > start_fractional_digits && buf[i] == '0' as u8 { + i -= 1; + } + + // Only attempt to truncate digits if buf has fractional digits + if i >= start_fractional_digits { + // If buf ends with '.', cut that too. + if buf[i] == '.' as u8 { i -= 1 } + + // only resize buf if we actually remove digits + if i < buf_max_i { + buf = buf.slice(0, i + 1); + } + } + } // If exact and trailing '.', just cut that + else { + let max_i = buf.len() - 1; + if buf[max_i] == '.' as u8 { + buf = buf.slice(0, max_i); + } + } + + (buf, false) +} + +/** + * Converts a number to its string representation. This is a wrapper for + * `to_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub pure fn to_str_common+Neg+Modulo+Mul>( + num: &T, radix: uint, negative_zero: bool, + sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { + let (bytes, special) = to_str_bytes_common(num, radix, + negative_zero, sign, digits); + (str::from_bytes(bytes), special) +} + +// Some constants for from_str_bytes_common's input validation, +// they define minimum radix values for which the character is a valid digit. +priv const DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; +priv const DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; +priv const DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; + +/** + * Parses a byte slice as a number. This is meant to + * be a common base implementation for all numeric string conversion + * functions like `from_str()` or `from_str_radix()`. + * + * # Arguments + * - `buf` - The byte slice to parse. + * - `radix` - Which base to parse the number as. Accepts 2-36. + * - `negative` - Whether to accept negative numbers. + * - `fractional` - Whether to accept numbers with fractional parts. + * - `special` - Whether to accept special values like `inf` + * and `NaN`. Can conflict with `radix`, see Failure. + * - `exponent` - Which exponent format to accept. Options are: + * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or + * `-8.2`. + * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or + * `8.2E-2`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or + * `FFp128`. The exponent string itself is always base 10. + * Can conflict with `radix`, see Failure. + * - `empty_zero` - Whether to accept a empty `buf` as a 0 or not. + * + * # Return value + * Returns `Some(n)` if `buf` parses to a number n without overflowing, and + * `None` otherwise, depending on the constraints set by the remaining + * arguments. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict + * between digit and exponent sign `'e'`. + * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict + * between digit and exponent sign `'p'`. + * - Fails if `radix` > 18 and `special == true` due to conflict + * between digit and lowest first character in `inf` and `NaN`, the `'i'`. + * + * # Possible improvements + * - Could accept option to allow ignoring underscores, allowing for numbers + * formated like `FF_AE_FF_FF`. + */ +pub pure fn from_str_bytes_common+ + Mul+Sub+Neg+Add+ + NumStrConv>( + buf: &[u8], radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool + ) -> Option { + match exponent { + ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' + => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'e' as decimal exponent", radix)), + ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' + => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ + use of 'p' as binary exponent", radix)), + _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' + => fail!(fmt!("from_str_bytes_common: radix %? incompatible with \ + special values 'inf' and 'NaN'", radix)), + _ if radix as int < 2 + => fail!(fmt!("from_str_bytes_common: radix %? to low, \ + must lie in the range [2, 36]", radix)), + _ if radix as int > 36 + => fail!(fmt!("from_str_bytes_common: radix %? to high, \ + must lie in the range [2, 36]", radix)), + _ => () + } + + let _0: T = Zero::zero(); + let _1: T = One::one(); + let radix_gen: T = cast(radix as int); + + let len = buf.len(); + + if len == 0 { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + // XXX: Bytevector constant from str + if special { + if buf == str::to_bytes("inf") || buf == str::to_bytes("+inf") { + return NumStrConv::inf(); + } else if buf == str::to_bytes("-inf") { + if negative { + return NumStrConv::neg_inf(); + } else { + return None; + } + } else if buf == str::to_bytes("NaN") { + return NumStrConv::NaN(); + } + } + + let (start, accum_positive) = match buf[0] { + '-' as u8 if !negative => return None, + '-' as u8 => (1u, false), + '+' as u8 => (1u, true), + _ => (0u, true) + }; + + // Initialize accumulator with signed zero for floating point parsing to + // work + let mut accum = if accum_positive { _0 } else { -_1 * _0}; + let mut last_accum = accum; // Necessary to detect overflow + let mut i = start; + let mut exp_found = false; + + // Parse integer part of number + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // shift accum one digit left + accum *= radix_gen; + + // add/subtract current digit depending on sign + if accum_positive { + accum += cast(digit as int); + } else { + accum -= cast(digit as int); + } + + // Detect overflow by comparing to last value + if accum_positive && accum < last_accum { return None; } + if !accum_positive && accum > last_accum { return None; } + last_accum = accum; + } + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + '.' if fractional => { + i += 1u; // skip the '.' + break; // start of fractional part + } + _ => return None // invalid number + } + } + + i += 1u; + } + + // Parse fractional part of number + // Skip if already reached start of exponent + if !exp_found { + let mut power = _1; + + while i < len { + let c = buf[i] as char; + + match char::to_digit(c, radix) { + Some(digit) => { + // Decrease power one order of magnitude + power /= radix_gen; + + let digit_t: T = cast(digit); + + // add/subtract current digit depending on sign + if accum_positive { + accum += digit_t * power; + } else { + accum -= digit_t * power; + } + + // Detect overflow by comparing to last value + if accum_positive && accum < last_accum { return None; } + if !accum_positive && accum > last_accum { return None; } + last_accum = accum; + } + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_found = true; + break; // start of exponent + } + _ => return None // invalid number + } + } + + i += 1u; + } + } + + // Special case: buf not empty, but does not contain any digit in front + // of the exponent sign -> number is empty string + if i == start { + if empty_zero { + return Some(_0); + } else { + return None; + } + } + + let mut multiplier = _1; + + if exp_found { + let c = buf[i] as char; + let base = match (c, exponent) { + ('e', ExpDec) | ('E', ExpDec) => 10u, + ('p', ExpBin) | ('P', ExpBin) => 2u, + _ => return None // char doesn't fit given exponent format + }; + + // parse remaining bytes as decimal integer, + // skipping the exponent char + let exp: Option = from_str_bytes_common( + buf.view(i+1, len), 10, true, false, false, ExpNone, false); + + match exp { + Some(exp_pow) => { + multiplier = if exp_pow < 0 { + _1 / pow_with_uint::(base, (-exp_pow.to_int()) as uint) + } else { + pow_with_uint::(base, exp_pow.to_int() as uint) + } + } + None => return None // invalid exponent -> invalid number + } + } + + Some(accum * multiplier) +} + +/** + * Parses a string as a number. This is a wrapper for + * `from_str_bytes_common()`, for details see there. + */ +#[inline(always)] +pub pure fn from_str_common+Mul+ + Sub+Neg+Add+NumStrConv>( + buf: &str, radix: uint, negative: bool, fractional: bool, + special: bool, exponent: ExponentFormat, empty_zero: bool + ) -> Option { + from_str_bytes_common(str::to_bytes(buf), radix, negative, + fractional, special, exponent, empty_zero) +} diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs index b1ef3f11fa4e1..005f0f2b5a4df 100644 --- a/src/libcore/num/uint-template.rs +++ b/src/libcore/num/uint-template.rs @@ -17,6 +17,7 @@ use cmp; use to_str::ToStr; use from_str::FromStr; use num::{ToStrRadix, FromStrRadix}; +use num::strconv; use num; use option::{None, Option, Some}; use prelude::*; @@ -140,18 +141,6 @@ impl num::One for T { static pure fn one() -> T { 1 } } -impl num::Round for T { - #[inline(always)] - pure fn round(&self, _: num::RoundMode) -> T { *self } - - #[inline(always)] - pure fn floor(&self) -> T { *self } - #[inline(always)] - pure fn ceil(&self) -> T { *self } - #[inline(always)] - pure fn fract(&self) -> T { 0 } -} - #[cfg(notest)] impl ops::Add for T { pure fn add(&self, other: &T) -> T { *self + *other } @@ -182,22 +171,22 @@ impl ops::Neg for T { /// Parse a string as a number in base 10. #[inline(always)] pub pure fn from_str(s: &str) -> Option { - num::from_str_common(s, 10u, false, false, false, - num::ExpNone, false) + strconv::from_str_common(s, 10u, false, false, false, + strconv::ExpNone, false) } /// Parse a string as a number in the given base. #[inline(always)] pub pure fn from_str_radix(s: &str, radix: uint) -> Option { - num::from_str_common(s, radix, false, false, false, - num::ExpNone, false) + strconv::from_str_common(s, radix, false, false, false, + strconv::ExpNone, false) } /// Parse a byte slice as a number in the given base. #[inline(always)] pub pure fn parse_bytes(buf: &[u8], radix: uint) -> Option { - num::from_str_bytes_common(buf, radix, false, false, false, - num::ExpNone, false) + strconv::from_str_bytes_common(buf, radix, false, false, false, + strconv::ExpNone, false) } impl FromStr for T { @@ -219,24 +208,24 @@ impl FromStrRadix for T { /// Convert to a string as a byte slice in a given base. #[inline(always)] pub pure fn to_str_bytes(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U { - let (buf, _) = num::to_str_bytes_common(&n, radix, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, + strconv::SignNeg, strconv::DigAll); f(buf) } /// Convert to a string in base 10. #[inline(always)] pub pure fn to_str(num: T) -> ~str { - let (buf, _) = num::to_str_common(&num, 10u, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_common(&num, 10u, false, + strconv::SignNeg, strconv::DigAll); buf } /// Convert to a string in a given base. #[inline(always)] pub pure fn to_str_radix(num: T, radix: uint) -> ~str { - let (buf, _) = num::to_str_common(&num, radix, false, false, - num::SignNeg, num::DigAll); + let (buf, _) = strconv::to_str_common(&num, radix, false, + strconv::SignNeg, strconv::DigAll); buf } diff --git a/src/libcore/option.rs b/src/libcore/option.rs index a90364c7d8ced..13d4ec6e10df6 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -41,7 +41,7 @@ let unwrapped_msg = match msg { */ -use cmp::Eq; +use cmp::{Eq,Ord}; use kinds::Copy; use option; use ptr; @@ -56,6 +56,34 @@ pub enum Option { Some(T), } +pub impl Ord for Option { + pure fn lt(&self, other: &Option) -> bool { + match (self, other) { + (&None, &None) => false, + (&None, &Some(_)) => true, + (&Some(_), &None) => false, + (&Some(ref a), &Some(ref b)) => *a < *b + } + } + + pure fn le(&self, other: &Option) -> bool { + match (self, other) { + (&None, &None) => true, + (&None, &Some(_)) => true, + (&Some(_), &None) => false, + (&Some(ref a), &Some(ref b)) => *a <= *b + } + } + + pure fn ge(&self, other: &Option) -> bool { + ! (self < other) + } + + pure fn gt(&self, other: &Option) -> bool { + ! (self <= other) + } +} + #[inline(always)] pub pure fn get(opt: Option) -> T { /*! diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index f1712d05562d5..5fc284bc9fc49 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -184,14 +184,14 @@ pub enum compile_upto { cu_everything, } -pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, - input: input, upto: compile_upto, - outputs: Option) - -> {crate: @ast::crate, tcx: Option} { +// For continuing compilation after a parsed crate has been +// modified +pub fn compile_rest(sess: Session, cfg: ast::crate_cfg, + upto: compile_upto, outputs: Option, + curr: Option<@ast::crate>) + -> {crate: @ast::crate, tcx: Option} { let time_passes = sess.time_passes(); - let mut crate = time(time_passes, ~"parsing", - || parse_input(sess, copy cfg, input) ); - if upto == cu_parse { return {crate: crate, tcx: None}; } + let mut crate = curr.get(); *sess.building_library = session::building_library( sess.opts.crate_type, crate, sess.opts.test); @@ -322,7 +322,6 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, }; - time(time_passes, ~"LLVM passes", || link::write::run_passes(sess, llmod, &outputs.obj_filename)); @@ -342,9 +341,20 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg, return {crate: crate, tcx: None}; } +pub fn compile_upto(sess: Session, +cfg: ast::crate_cfg, + input: input, upto: compile_upto, + outputs: Option) + -> {crate: @ast::crate, tcx: Option} { + let time_passes = sess.time_passes(); + let mut crate = time(time_passes, ~"parsing", + || parse_input(sess, copy cfg, input) ); + if upto == cu_parse { return {crate: crate, tcx: None}; } + + compile_rest(sess, cfg, upto, outputs, Some(crate)) +} + pub fn compile_input(sess: Session, +cfg: ast::crate_cfg, input: input, outdir: &Option, output: &Option) { - let upto = if sess.opts.parse_only { cu_parse } else if sess.opts.no_trans { cu_no_trans } else { cu_everything }; diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index ed4ea665aafcd..f5cc44867a5d1 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -49,11 +49,11 @@ pub fn mk_filesearch(maybe_sysroot: Option, paths.push( make_target_lib_path(&self.sysroot, self.target_triple)); - match get_cargo_lib_path_nearest() { + match get_rustpkg_lib_path_nearest() { result::Ok(ref p) => paths.push((/*bad*/copy *p)), result::Err(_) => () } - match get_cargo_lib_path() { + match get_rustpkg_lib_path() { result::Ok(ref p) => paths.push((/*bad*/copy *p)), result::Err(_) => () } @@ -119,54 +119,54 @@ fn get_sysroot(maybe_sysroot: Option) -> Path { } } -pub fn get_cargo_sysroot() -> Result { - result::Ok(get_or_default_sysroot().push_many([libdir(), ~"cargo"])) +pub fn get_rustpkg_sysroot() -> Result { + result::Ok(get_or_default_sysroot().push_many([libdir(), ~"rustpkg"])) } -pub fn get_cargo_root() -> Result { - match os::getenv(~"CARGO_ROOT") { +pub fn get_rustpkg_root() -> Result { + match os::getenv(~"RUSTPKG_ROOT") { Some(ref _p) => result::Ok(Path((*_p))), None => match os::homedir() { - Some(ref _q) => result::Ok((*_q).push(".cargo")), - None => result::Err(~"no CARGO_ROOT or home directory") + Some(ref _q) => result::Ok((*_q).push(".rustpkg")), + None => result::Err(~"no RUSTPKG_ROOT or home directory") } } } -pub fn get_cargo_root_nearest() -> Result { - do result::chain(get_cargo_root()) |p| { +pub fn get_rustpkg_root_nearest() -> Result { + do result::chain(get_rustpkg_root()) |p| { let cwd = os::getcwd(); - let cwd_cargo = cwd.push(".cargo"); - let cargo_is_non_root_file = - !os::path_is_dir(&cwd_cargo) && cwd_cargo != p; - let mut par_cargo = cwd.pop().push(".cargo"); - let mut rslt = result::Ok(cwd_cargo); - - if cargo_is_non_root_file { - while par_cargo != p { - if os::path_is_dir(&par_cargo) { - rslt = result::Ok(par_cargo); + let cwd_rustpkg = cwd.push(".rustpkg"); + let rustpkg_is_non_root_file = + !os::path_is_dir(&cwd_rustpkg) && cwd_rustpkg != p; + let mut par_rustpkg = cwd.pop().push(".rustpkg"); + let mut rslt = result::Ok(cwd_rustpkg); + + if rustpkg_is_non_root_file { + while par_rustpkg != p { + if os::path_is_dir(&par_rustpkg) { + rslt = result::Ok(par_rustpkg); break; } - if par_cargo.components.len() == 1 { - // We just checked /.cargo, stop now. + if par_rustpkg.components.len() == 1 { + // We just checked /.rustpkg, stop now. break; } - par_cargo = par_cargo.pop().pop().push(".cargo"); + par_rustpkg = par_rustpkg.pop().pop().push(".rustpkg"); } } rslt } } -fn get_cargo_lib_path() -> Result { - do result::chain(get_cargo_root()) |p| { +fn get_rustpkg_lib_path() -> Result { + do result::chain(get_rustpkg_root()) |p| { result::Ok(p.push(libdir())) } } -fn get_cargo_lib_path_nearest() -> Result { - do result::chain(get_cargo_root_nearest()) |p| { +fn get_rustpkg_lib_path_nearest() -> Result { + do result::chain(get_rustpkg_root_nearest()) |p| { result::Ok(p.push(libdir())) } } diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc new file mode 100644 index 0000000000000..5b610bbb1f8ea --- /dev/null +++ b/src/librustpkg/rustpkg.rc @@ -0,0 +1,1038 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// rustpkg - a purely function package manager and build system + +#[link(name = "rustpkg", + vers = "0.6", + uuid = "25de5e6e-279e-4a20-845c-4cabae92daaf", + url = "https://github.com/mozilla/rust/tree/master/src/librustpkg")]; + +#[crate_type = "lib"]; +#[no_core]; +#[allow(vecs_implicitly_copyable, + non_implicitly_copyable_typarams)]; + +#[legacy_records]; + +extern mod core(vers = "0.6"); +extern mod std(vers = "0.6"); +extern mod rustc(vers = "0.6"); +extern mod syntax(vers = "0.6"); + +use core::*; +use io::{ReaderUtil, WriterUtil}; +use std::{json, semver, getopts}; +use std::net::url; +use hashmap::linear::LinearMap; +use rustc::metadata::filesearch; +use rustc::driver::{driver, session}; +use syntax::{ast, attr, codemap, diagnostic, parse, visit}; +use syntax::codemap::spanned; + +mod usage; +mod util; + +use util::Package; + +struct PackageScript { + id: ~str, + name: ~str, + vers: semver::Version, + crates: ~[~str], + deps: ~[(~str, Option<~str>)], + input: driver::input, + sess: session::Session, + cfg: ast::crate_cfg, + crate: @ast::crate, + custom: bool +} + +impl PackageScript { + static fn parse(parent: &Path) -> Result { + let script = parent.push(~"pkg.rs"); + + if !os::path_exists(&script) { + return result::Err(~"no pkg.rs file"); + } + + let binary = os::args()[0]; + let options: @session::options = @{ + binary: binary, + crate_type: session::bin_crate, + .. *session::basic_options() + }; + let input = driver::file_input(script); + let sess = driver::build_session(options, diagnostic::emit); + let cfg = driver::build_configuration(sess, binary, input); + let {crate, _} = driver::compile_upto(sess, cfg, input, + driver::cu_parse, None); + let mut id = None; + let mut vers = None; + let mut crates = ~[]; + let mut deps = ~[]; + + 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, spanned { + node: ast::lit_str(s), + span: _}) => { + match v { + ~"id" => id = Some(*s), + ~"vers" => vers = Some(*s), + _ => () + } + } + _ => {} + } + } + + (id, vers) + } + + fn load_pkg_dep_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, + Option<~str>) { + let mut url = None; + let mut target = None; + + for mis.each |a| { + match a.node { + ast::meta_name_value(v, spanned { + node: ast::lit_str(s), + span: _}) => { + match v { + ~"url" => url = Some(*s), + ~"target" => target = Some(*s), + _ => () + } + } + _ => {} + } + } + + (url, target) + } + + fn load_pkg_crate_attr(mis: ~[@ast::meta_item]) -> Option<~str> { + let mut file = None; + + for mis.each |a| { + match a.node { + ast::meta_name_value(v, spanned { + node: ast::lit_str(s), + span: _}) => { + match v { + ~"file" => file = Some(*s), + _ => () + } + } + _ => {} + } + } + + file + } + + 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; + } + ~"pkg_dep" => { + let (u, t) = load_pkg_dep_attr(mis); + + if u.is_none() { + fail!(~"pkg_dep attr without a url value"); + } + + deps.push((u.get(), t)); + } + ~"pkg_crate" => { + let f = load_pkg_crate_attr(mis); + + if f.is_none() { + fail!(~"pkg_file attr without a file value"); + } + + crates.push(f.get()); + } + _ => {} + } + } + _ => {} + } + } + + let mut custom = false; + + // If we hit a function, we assume they want to use + // the build API. + for crate.node.module.items.each |i| { + match i.node { + ast::item_fn(_, _, _, _) => { + custom = true; + + break; + } + _ => {} + } + } + + if id.is_none() || vers.is_none() { + return result::Err(~"pkg attr without (id, vers) values"); + } + + let id = id.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: name, + vers: vers, + crates: crates, + deps: deps, + input: input, + sess: sess, + cfg: cfg, + crate: crate, + custom: custom + }) + } + + // Build the bootstrap and run a command + // FIXME (#4432): Use workcache to only compile the script when changed + fn run(&self, cmd: ~str, test: bool) -> int { + let work_dir = self.work_dir(); + let input = self.input; + let sess = self.sess; + let cfg = self.cfg; + let crate = util::ready_crate(sess, self.crate); + let outputs = driver::build_output_filenames(input, &Some(work_dir), + &None, sess); + let exe = work_dir.push(~"pkg" + util::exe_suffix()); + let root = filesearch::get_rustpkg_sysroot().get().pop().pop(); + + driver::compile_rest(sess, cfg, driver::cu_parse, + Some(outputs), Some(crate)); + run::run_program(exe.to_str(), ~[root.to_str(), cmd, test.to_str()]) + } + + fn hash(&self) -> ~str { + fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()), + self.vers.to_str()) + } + + fn work_dir(&self) -> Path { + util::root().push(~"work").push(self.hash()) + } +} + +struct Ctx { + cfgs: ~[~str], + json: bool, + mut dep_cache: LinearMap<~str, bool> +} + +impl Ctx { + fn run(&self, cmd: ~str, args: ~[~str]) { + let root = util::root(); + + util::need_dir(&root); + util::need_dir(&root.push(~"work")); + util::need_dir(&root.push(~"lib")); + util::need_dir(&root.push(~"bin")); + util::need_dir(&root.push(~"tmp")); + + fn sep_name_vers(in: ~str) -> (Option<~str>, Option<~str>) { + let mut name = None; + let mut vers = None; + let parts = str::split_char(in, '@'); + + if parts.len() >= 1 { + name = Some(parts[0]); + + if parts.len() >= 2 { + vers = Some(parts[1]); + } + } + + (name, vers) + } + + match cmd { + ~"build" => { + self.build(&os::getcwd(), true, false, false); + } + ~"clean" => { + self.clean(); + } + ~"do" => { + if args.len() < 1 { + return usage::do_cmd(); + } + + self.do_cmd(args[0]); + } + ~"info" => { + self.info(); + } + ~"install" => { + self.install(if args.len() >= 1 { Some(args[0]) } + else { None }, + if args.len() >= 2 { Some(args[1]) } + else { None }, false); + } + ~"prefer" => { + if args.len() < 1 { + return usage::uninstall(); + } + + let (name, vers) = sep_name_vers(args[0]); + + self.prefer(name.get(), vers); + } + ~"test" => { + self.test(); + } + ~"uninstall" => { + if args.len() < 1 { + return usage::uninstall(); + } + + let (name, vers) = sep_name_vers(args[0]); + + self.uninstall(name.get(), vers); + } + ~"unprefer" => { + if args.len() < 1 { + return usage::uninstall(); + } + + let (name, vers) = sep_name_vers(args[0]); + + self.unprefer(name.get(), vers); + } + _ => fail!(~"reached an unhandled command") + } + } + + fn do_cmd(&self, cmd: ~str) -> bool { + match cmd { + ~"build" | ~"test" => { + util::error(~"that command cannot be manually called"); + + return false; + } + _ => {} + } + + let cwd = &os::getcwd(); + let script = match PackageScript::parse(cwd) { + result::Ok(script) => script, + result::Err(err) => { + util::error(err); + + return false; + } + }; + let status = script.run(cmd, false); + + if status == 42 { + util::error(~"no fns are listening for that cmd"); + + return false; + } + + status == 0 + } + + fn build(&self, dir: &Path, verbose: bool, opt: bool, + test: bool) -> Option { + let cwd = &os::getcwd(); + let script = match PackageScript::parse(dir) { + result::Ok(script) => script, + result::Err(err) => { + util::error(err); + + return None; + } + }; + let work_dir = script.work_dir(); + let mut success = true; + + util::need_dir(&work_dir); + + 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!("building %s v%s failed: a dep wasn't installed", + script.name, script.vers.to_str())); + + return None; + } + + util::note(~"installed dependencies"); + } + + // Build imperative crates + os::change_dir(dir); + + if script.custom { + let status = script.run(~"build", test); + + if status != 0 && status != 42 { + util::error( + fmt!("building %s v%s failed: custom logic failed (%d)", + script.name, script.vers.to_str(), status)); + + return None; + } + } + + os::change_dir(cwd); + + for script.crates.each |&crate| { + let crate = &dir.push_rel(&Path(crate)).normalize(); + + util::note(fmt!("compiling %s", crate.to_str())); + + success = self.compile(crate, &work_dir, ~[], + ~[], opt, test); + + if !success { break; } + } + + if !success { + util::error( + fmt!("building %s v%s failed: a crate failed to compile", + script.name, script.vers.to_str())); + + return None; + } + + if verbose { + util::note(fmt!("built %s v%s", script.name, + script.vers.to_str())); + } + + Some(script) + } + + fn compile(&self, crate: &Path, dir: &Path, flags: ~[~str], + cfgs: ~[~str], opt: bool, test: bool) -> bool { + util::compile_crate(None, crate, dir, flags, cfgs, opt, test) + } + + fn clean(&self) -> bool { + let script = match PackageScript::parse(&os::getcwd()) { + result::Ok(script) => script, + result::Err(err) => { + util::error(err); + + return false; + } + }; + let dir = script.work_dir(); + + util::note(fmt!("cleaning %s v%s (%s)", script.name, + script.vers.to_str(), script.id)); + + 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 + } + + fn info(&self) { + if self.json { + match PackageScript::parse(&os::getcwd()) { + result::Ok(script) => { + let mut map = ~LinearMap::new(); + + map.insert(~"id", json::String(script.id)); + map.insert(~"name", json::String(script.name)); + map.insert(~"vers", json::String(script.vers.to_str())); + map.insert(~"deps", json::List(do script.deps.map |&dep| { + let (url, target) = dep; + let mut inner = ~LinearMap::new(); + + inner.insert(~"url", json::String(url)); + + if !target.is_none() { + inner.insert(~"target", + json::String(target.get())); + } + + json::Object(inner) + })); + + io::println(json::to_pretty_str(&json::Object(map))); + } + result::Err(_) => io::println(~"{}") + } + } else { + let script = match PackageScript::parse(&os::getcwd()) { + result::Ok(script) => script, + result::Err(err) => { + util::error(err); + + return; + } + }; + + util::note(fmt!("id: %s", script.id)); + util::note(fmt!("name: %s", script.name)); + util::note(fmt!("vers: %s", script.vers.to_str())); + util::note(fmt!("deps: %s", + if script.deps.len() > 0 { + ~"" + } else { + ~"none" + })); + + for script.deps.each |&dep| { + let (url, target) = dep; + + util::note(fmt!(" <%s> (%s)", url, match target { + Some(target) => target, + None => ~"" + })); + } + } + } + + fn install(&self, url: Option<~str>, + target: Option<~str>, cache: bool) -> bool { + let mut success; + let mut dir; + + if url.is_none() { + util::note(~"installing from the cwd"); + + dir = os::getcwd(); + } else { + let url = url.get(); + let hash = util::hash(if !target.is_none() { url + target.get() } + else { url }); + + if self.dep_cache.contains_key(&hash) { + util::warn(~"already installed dep this run"); + + return true; + } + + self.dep_cache.insert(hash, true); + + dir = util::root().push(~"tmp").push(hash); + + if cache && os::path_exists(&dir) { + return true; + } + + success = self.fetch(&dir, url, target); + + if !success { + return false; + } + } + + let script = match self.build(&dir, false, true, false) { + Some(script) => script, + None => { + return false; + } + }; + let work_dir = script.work_dir(); + 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 + } + + fn fetch(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool { + let url = if str::find_str(url, "://").is_none() { + ~"http://" + url } + else { url }; + let url = match url::from_str(url) { + result::Ok(url) => url, + result::Err(err) => { + util::error(fmt!("failed parsing %s", err.to_lower())); + + return false; + } + }; + let str = url.to_str(); + + match Path(url.path).filetype() { + Some(ext) => { + if ext == ~".git" { + return self.fetch_git(dir, str, target); + } + } + None => {} + } + + match url.scheme { + ~"git" => self.fetch_git(dir, str, target), + ~"http" | ~"ftp" | ~"file" => self.fetch_curl(dir, str), + _ => { + util::warn(~"unknown url scheme to fetch, using curl"); + self.fetch_curl(dir, str) + } + } + } + + fn fetch_curl(&self, dir: &Path, url: ~str) -> bool { + util::note(fmt!("fetching from %s using curl", url)); + + let tar = dir.dir_path().push(&dir.file_path().to_str() + ~".tar"); + + if run::program_output(~"curl", ~[~"-f", ~"-s", + ~"-o", tar.to_str(), + url]).status != 0 { + util::error(~"fetching failed: downloading using curl failed"); + + return false; + } + + if run::program_output(~"tar", ~[~"-x", ~"--strip-components=1", + ~"-C", dir.to_str(), ~"-f", + tar.to_str()]).status != 0 { + util::error(~"fetching failed: extracting using tar failed" + + ~"(is it a valid tar archive?)"); + + return false; + } + + true + } + + fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool { + util::note(fmt!("fetching from %s using git", url)); + + // Git can't clone into a non-empty directory + util::remove_dir_r(dir); + + if run::program_output(~"git", ~[~"clone", url, + dir.to_str()]).status != 0 { + util::error(~"fetching failed: can't clone repository"); + + return false; + } + + if !target.is_none() { + let mut success = true; + + do util::temp_change_dir(dir) { + success = run::program_output(~"git", + ~[~"checkout", + target.get()]).status != 0 + } + + if !success { + util::error(~"fetching failed: can't checkout target"); + + return false; + } + } + + true + } + + fn prefer(&self, 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(&self) -> bool { + let script = match self.build(&os::getcwd(), false, false, true) { + Some(script) => script, + None => { + return false; + } + }; + let work_dir = script.work_dir(); + let test_dir = work_dir.push(~"test"); + + 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); + } + } + + // Run custom test listener + if script.custom { + let status = script.run(~"test", false); + + if status != 0 && status != 42 { + util::error( + fmt!("testing %s v%s failed: custom logic failed (%d)", + script.name, script.vers.to_str(), status)); + + os::set_exit_status(status); + } + } + + util::note(fmt!("tested %s v%s", script.name, script.vers.to_str())); + + true + } + + fn uninstall(&self, 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(&self, 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 + } +} + +pub fn main() { + let args = os::args(); + let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"), + getopts::optflag(~"j"), getopts::optflag(~"json"), + getopts::optmulti(~"c"), getopts::optmulti(~"cfg")]; + let matches = &match getopts::getopts(args, opts) { + result::Ok(m) => m, + result::Err(f) => { + util::error(fmt!("%s", getopts::fail_str(f))); + + return; + } + }; + let help = getopts::opt_present(matches, ~"h") || + getopts::opt_present(matches, ~"help"); + let json = getopts::opt_present(matches, ~"j") || + getopts::opt_present(matches, ~"json"); + let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"), + getopts::opt_strs(matches, ~"c")); + let mut args = copy matches.free; + + args.shift(); + + if (args.len() < 1) { + return usage::general(); + } + + let cmd = args.shift(); + + if !util::is_cmd(cmd) { + return usage::general(); + } else if help { + return match cmd { + ~"build" => usage::build(), + ~"clean" => usage::clean(), + ~"do" => usage::do_cmd(), + ~"info" => usage::info(), + ~"install" => usage::install(), + ~"prefer" => usage::prefer(), + ~"test" => usage::test(), + ~"uninstall" => usage::uninstall(), + ~"unprefer" => usage::unprefer(), + _ => usage::general() + }; + } + + Ctx { + cfgs: cfgs, + json: json, + mut dep_cache: LinearMap::new() + }.run(cmd, args); +} + +/// A crate is a unit of Rust code to be compiled into a binary or library +pub struct Crate { + file: ~str, + flags: ~[~str], + cfgs: ~[~str] +} + +pub struct Listener { + cmds: ~[~str], + cb: fn~() +} + +pub fn run(listeners: ~[Listener]) { + let rcmd = os::args()[2]; + let mut found = false; + + for listeners.each |listener| { + for listener.cmds.each |&cmd| { + if cmd == rcmd { + (listener.cb)(); + + found = true; + + break; + } + } + } + + if !found { + os::set_exit_status(42); + } +} + +pub impl Crate { + 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 + } + } +} + +/// Create a crate target from a source file +pub fn Crate(file: ~str) -> Crate { + Crate { + file: file, + flags: ~[], + cfgs: ~[] + } +} + +/** + * Get the working directory of the package script. + * Assumes that the package script has been compiled + * in is the working directory. + */ +pub fn work_dir() -> Path { + os::self_exe_path().get() +} + +/** + * Get the source directory of the package (i.e. + * where the crates are located). Assumes + * that the cwd is changed to it before + * running this executable. + */ +pub fn src_dir() -> Path { + os::getcwd() +} + +/// Build a set of crates, should be called once +pub fn build(crates: ~[Crate]) -> bool { + let args = os::args(); + let dir = src_dir(); + let work_dir = work_dir(); + let mut success = true; + let sysroot = Path(args[1]); + let test = args[3] == ~"true"; + + for crates.each |&crate| { + let path = &dir.push_rel(&Path(crate.file)).normalize(); + + util::note(fmt!("compiling %s", path.to_str())); + + success = util::compile_crate(Some(sysroot), path, &work_dir, + crate.flags, crate.cfgs, + false, test); + + if !success { break; } + } + + if !success { + os::set_exit_status(101); + } + + success +} diff --git a/src/librustpkg/usage.rs b/src/librustpkg/usage.rs new file mode 100644 index 0000000000000..cfda56f777ab2 --- /dev/null +++ b/src/librustpkg/usage.rs @@ -0,0 +1,121 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::io; + +pub fn general() { + io::println(~"Usage: rustpkg [options] [args..] + +Where is one of: + build, clean, do, info, install, prefer, test, uninstall, unprefer + +Options: + + -h, --help Display this message + -h, --help Display help for "); +} + +pub fn build() { + io::println(~"rustpkg [options..] build + +Build all targets described in the package script in the current +directory. + +Options: + -c, --cfg Pass a cfg flag to the package script"); +} + +pub fn clean() { + io::println(~"rustpkg clean + +Remove all build files in the work cache for the package in the current +directory."); +} + +pub fn do_cmd() { + io::println(~"rustpkg do + +Runs a command in the package script. You can listen to a command +by tagging a function with the attribute `#[pkg_do(cmd)]`."); +} + +pub fn info() { + io::println(~"rustpkg [options..] info + +Probe the package script in the current directory for information. + +Options: + -j, --json Output the result as JSON"); +} + +pub fn install() { + io::println(~"rustpkg [options..] install [url] [target] + +Install a package from a URL by Git or cURL (FTP, HTTP, etc.). +If target is provided, Git will checkout the branch or tag before +continuing. If the URL is a TAR file (with or without compression), +extract it before installing. If a URL isn't provided, the package will +be built and installed from the current directory (which is +functionally the same as `rustpkg build` and installing the result). + +Examples: + rustpkg install + rustpkg install git://github.com/mozilla/servo.git + rustpkg install git://github.com/mozilla/servo.git v0.1.2 + rustpkg install http://rust-lang.org/servo-0.1.2.tar.gz + +Options: + -c, --cfg Pass a cfg flag to the package script"); +} + +pub fn uninstall() { + io::println(~"rustpkg uninstall [@version] + +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 [@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. If version is not supplied, the +latest version of the package will be preferred. + +Example: + export PATH=$PATH:/home/user/.rustpkg/bin + rustpkg prefer machine@1.2.4 + machine -v + ==> v1.2.4 + rustpkg prefer machine@0.4.6 + machine -v + ==> v0.4.6"); +} + +pub fn unprefer() { + io::println(~"rustpkg [options..] unprefer [@version] + +Remove all symlinks from the store to the binary directory for a package +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() { + io::println(~"rustpkg [options..] test + +Build all targets described in the package script in the current directory +with the test flag. The test bootstraps will be run afterwards and the output +and exit code will be redirected. + +Options: + -c, --cfg Pass a cfg flag to the package script"); +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs new file mode 100644 index 0000000000000..1ad706742a8f6 --- /dev/null +++ b/src/librustpkg/util.rs @@ -0,0 +1,803 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::*; +use hashmap::linear::LinearMap; +use rustc::metadata::filesearch; +use rustc::driver::{driver, session}; +use syntax::ast_util::*; +use syntax::{ast, attr, codemap, diagnostic, fold, parse, visit}; +use codemap::{span, dummy_sp, spanned}; +use std::semver; +use std::{json, term, sort, getopts}; +use getopts::groups::getopts; +use Listener; + +use syntax::ext::base::{mk_ctxt, ext_ctxt}; +use syntax::ext::build; + +pub struct Package { + id: ~str, + vers: semver::Version, + bins: ~[~str], + libs: ~[~str], +} + +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", ~"do", ~"info", ~"install", ~"prefer", + ~"test", ~"uninstall", ~"unprefer"]; + + vec::contains(cmds, &cmd) +} + +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) { + return result::Err( + ~"could not parse id: contains whitespace"); + } else if char::is_uppercase(char) { + return result::Err( + ~"could not parse id: should be all lowercase"); + } + } + } + + result::Ok(parts.last()) +} + +struct ListenerFn { + cmds: ~[~str], + span: codemap::span, + path: ~[ast::ident] +} + +struct ReadyCtx { + sess: session::Session, + crate: @ast::crate, + ext_cx: ext_ctxt, + mut path: ~[ast::ident], + mut fns: ~[ListenerFn] +} + +fn fold_mod(_ctx: @ReadyCtx, m: ast::_mod, + fold: fold::ast_fold) -> ast::_mod { + fn strip_main(item: @ast::item) -> @ast::item { + @ast::item { + attrs: do item.attrs.filtered |attr| { + attr::get_attr_name(*attr) != ~"main" + }, + .. copy *item + } + } + + fold::noop_fold_mod(ast::_mod { + items: do vec::map(m.items) |item| { + strip_main(*item) + }, + .. m + }, fold) +} + +fn fold_item(ctx: @ReadyCtx, item: @ast::item, + fold: fold::ast_fold) -> Option<@ast::item> { + + ctx.path.push(item.ident); + + let attrs = attr::find_attrs_by_name(item.attrs, ~"pkg_do"); + + if attrs.len() > 0 { + let mut cmds = ~[]; + + for attrs.each |attr| { + match attr.node.value.node { + ast::meta_list(_, mis) => { + for mis.each |mi| { + match mi.node { + ast::meta_word(cmd) => cmds.push(cmd), + _ => {} + }; + } + } + _ => cmds.push(~"build") + }; + } + + ctx.fns.push(ListenerFn { + cmds: cmds, + span: item.span, + path: /*bad*/copy ctx.path + }); + } + + let res = fold::noop_fold_item(item, fold); + + ctx.path.pop(); + + res +} + +fn add_pkg_module(ctx: @ReadyCtx, m: ast::_mod) -> ast::_mod { + let listeners = mk_listener_vec(ctx); + let ext_cx = ctx.ext_cx; + let item = quote_item! ( + mod __pkg { + extern mod rustpkg (vers="0.6"); + const listeners : &[rustpkg::Listener] = $listeners; + #[main] + fn main() { + rustpkg::run(listeners); + } + } + ); + ast::_mod { + items: vec::append_one(/*bad*/copy m.items, item.get()), + .. m + } +} + +fn mk_listener_vec(ctx: @ReadyCtx) -> @ast::expr { + let fns = ctx.fns; + let descs = do fns.map |listener| { + mk_listener_rec(ctx, *listener) + }; + build::mk_slice_vec_e(ctx.ext_cx, dummy_sp(), descs) +} + +fn mk_listener_rec(ctx: @ReadyCtx, listener: ListenerFn) -> @ast::expr { + + let span = listener.span; + let cmds = do listener.cmds.map |&cmd| { + build::mk_base_str(ctx.ext_cx, span, cmd) + }; + + let cmds_expr = build::mk_slice_vec_e(ctx.ext_cx, span, cmds); + let cb_expr = build::mk_path(ctx.ext_cx, span, copy listener.path); + let ext_cx = ctx.ext_cx; + + quote_expr!( + Listener { + cmds: $cmds_expr, + cb: $cb_expr + } + ) +} + +/// Generate/filter main function, add the list of commands, etc. +pub fn ready_crate(sess: session::Session, + crate: @ast::crate) -> @ast::crate { + let ctx = @ReadyCtx { + sess: sess, + crate: crate, + ext_cx: mk_ctxt(sess.parse_sess, copy sess.opts.cfg), + mut path: ~[], + mut fns: ~[] + }; + let precursor = @fold::AstFoldFns { + // fold_crate: fold::wrap(|a, b| fold_crate(ctx, a, b)), + fold_item: |a, b| fold_item(ctx, a, b), + fold_mod: |a, b| fold_mod(ctx, a, b), + .. *fold::default_ast_fold() + }; + + let fold = fold::make_fold(precursor); + + @fold.fold_crate(*crate) +} + +pub fn parse_vers(vers: ~str) -> result::Result { + match semver::parse(vers) { + Some(vers) => result::Ok(vers), + None => result::Err(~"could not parse version: invalid") + } +} + +pub fn need_dir(s: &Path) { + if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) { + fail!(fmt!("can't create dir: %s", s.to_str())); + } +} + +pub fn note(msg: ~str) { + let out = io::stdout(); + + if term::color_supported() { + term::fg(out, term::color_green); + out.write_str(~"note: "); + term::reset(out); + out.write_line(msg); + } else { + out.write_line(~"note: " + msg); + } +} + +pub fn warn(msg: ~str) { + let out = io::stdout(); + + if term::color_supported() { + term::fg(out, term::color_yellow); + out.write_str(~"warning: "); + term::reset(out); + out.write_line(msg); + } else { + out.write_line(~"warning: " + msg); + } +} + +pub fn error(msg: ~str) { + let out = io::stdout(); + + if term::color_supported() { + term::fg(out, term::color_red); + out.write_str(~"error: "); + term::reset(out); + out.write_line(msg); + } 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(dir: &Path, cb: fn() -> T) { + let cwd = os::getcwd(); + + os::change_dir(dir); + cb(); + os::change_dir(&cwd); +} + +pub fn touch(path: &Path) { + match io::mk_file_writer(path, ~[io::Create]) { + result::Ok(writer) => writer.write_line(~""), + _ => {} + } +} + +pub fn remove_dir_r(path: &Path) { + for os::walk_dir(path) |&file| { + let mut cdir = file; + + loop { + if os::path_is_dir(&cdir) { + os::remove_dir(&cdir); + } else { + os::remove_file(&cdir); + } + + cdir = cdir.dir_path(); + + if cdir == *path { break; } + } + } + + os::remove_dir(path); +} + +pub fn wait_for_lock(path: &Path) { + if os::path_exists(path) { + warn(fmt!("the database appears locked, please wait (or rm %s)", + path.to_str())); + + loop { + if !os::path_exists(path) { break; } + } + } +} + +fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] { + for packages.each |&package| { + match &package { + &json::Object(ref map) => { + let mut has_id = false; + + match map.get(&~"id") { + &json::String(ref str) => { + if pkg.id == *str { + has_id = true; + } + } + _ => {} + } + + match map.get(&~"vers") { + &json::String(ref str) => { + if has_id && pkg.vers.to_str() == *str { + return copy packages; + } + } + _ => {} + } + } + _ => {} + } + } + + let mut map = ~LinearMap::new(); + + 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_mapped |&package| { + match &package { + &json::Object(ref 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(ref str) => { + if has_id && pkg.vers.to_str() == *str { + None + } else { + Some(copy package) + } + } + _ => { Some(copy package) } + } + } + _ => { Some(copy 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 { + 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(ref list) => { + do list.map |&bin| { + match bin { + json::String(str) => str, + _ => ~"" + } + } + } + _ => ~[] + }; + let libs = match map.get(&~"libs") { + &json::List(ref 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 +} + +pub fn compile_input(sysroot: Option, input: driver::input, dir: &Path, + flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool { + let lib_dir = dir.push(~"lib"); + let bin_dir = dir.push(~"bin"); + let test_dir = dir.push(~"test"); + let binary = os::args()[0]; + let matches = getopts(flags, driver::optgroups()).get(); + let options = @{ + crate_type: session::unknown_crate, + optimize: if opt { session::Aggressive } else { session::No }, + test: test, + maybe_sysroot: sysroot, + .. *driver::build_session_options(binary, &matches, diagnostic::emit) + }; + let mut crate_cfg = options.cfg; + + for cfgs.each |&cfg| { + crate_cfg.push(attr::mk_word_item(cfg)); + } + + let options = @{ + cfg: vec::append(options.cfg, crate_cfg), + .. *options + }; + let sess = driver::build_session(options, diagnostic::emit); + let cfg = driver::build_configuration(sess, binary, input); + let mut outputs = driver::build_output_filenames(input, &None, &None, + sess); + let {crate, _} = driver::compile_upto(sess, cfg, input, driver::cu_parse, + Some(outputs)); + + let mut name = None; + let mut vers = None; + let mut uuid = None; + let mut crate_type = None; + + fn load_link_attr(mis: ~[@ast::meta_item]) -> (Option<~str>, + Option<~str>, + Option<~str>) { + let mut name = None; + let mut vers = None; + let mut uuid = None; + + for mis.each |a| { + match a.node { + ast::meta_name_value(v, spanned {node: ast::lit_str(s), + span: _}) => { + match v { + ~"name" => name = Some(*s), + ~"vers" => vers = Some(*s), + ~"uuid" => uuid = Some(*s), + _ => { } + } + } + _ => {} + } + } + + (name, vers, uuid) + } + + for crate.node.attrs.each |a| { + match a.node.value.node { + ast::meta_name_value(v, spanned {node: ast::lit_str(s), + span: _}) => { + match v { + ~"crate_type" => crate_type = Some(*s), + _ => {} + } + } + ast::meta_list(v, mis) => { + match v { + ~"link" => { + let (n, v, u) = load_link_attr(mis); + + name = n; + vers = v; + uuid = u; + } + _ => {} + } + } + _ => {} + } + } + + if name.is_none() || vers.is_none() || uuid.is_none() { + error(~"link attr without (name, vers, uuid) values"); + + return false; + } + + let name = name.get(); + let vers = vers.get(); + let uuid = uuid.get(); + + let is_bin = match crate_type { + Some(crate_type) => { + match crate_type { + ~"bin" => true, + ~"lib" => false, + _ => { + warn(~"unknown crate_type, falling back to lib"); + + false + } + } + } + None => { + warn(~"missing crate_type attr, assuming lib"); + + false + } + }; + + if test { + need_dir(&test_dir); + + outputs = driver::build_output_filenames(input, &Some(test_dir), + &None, sess) + } + else if is_bin { + need_dir(&bin_dir); + + let path = bin_dir.push(fmt!("%s-%s-%s%s", name, + hash(name + uuid + vers), + vers, exe_suffix())); + outputs = driver::build_output_filenames(input, &None, &Some(path), + sess); + } else { + need_dir(&lib_dir); + + outputs = driver::build_output_filenames(input, &Some(lib_dir), + &None, sess) + } + + driver::compile_rest(sess, cfg, driver::cu_everything, + Some(outputs), Some(crate)); + + true +} + +#[cfg(windows)] +pub fn exe_suffix() -> ~str { ~".exe" } + +#[cfg(target_os = "linux")] +#[cfg(target_os = "android")] +#[cfg(target_os = "freebsd")] +#[cfg(target_os = "macos")] +pub fn exe_suffix() -> ~str { ~"" } + + +// FIXME (#4432): Use workcache to only compile when needed +pub fn compile_crate(sysroot: Option, crate: &Path, dir: &Path, + flags: ~[~str], cfgs: ~[~str], opt: bool, + test: bool) -> bool { + compile_input(sysroot, driver::file_input(*crate), dir, flags, cfgs, + opt, test) +} + +pub fn compile_str(sysroot: Option, code: ~str, dir: &Path, + flags: ~[~str], cfgs: ~[~str], opt: bool, + test: bool) -> bool { + compile_input(sysroot, driver::str_input(code), dir, flags, cfgs, + opt, test) +} + +#[cfg(windows)] +pub fn link_exe(_src: &Path, _dest: &Path) -> bool { + /* FIXME (#1768): 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"); + assert is_cmd(~"clean"); + assert is_cmd(~"do"); + assert is_cmd(~"info"); + assert is_cmd(~"install"); + assert is_cmd(~"prefer"); + assert is_cmd(~"test"); + assert is_cmd(~"uninstall"); + assert is_cmd(~"unprefer"); +} + +#[test] +fn test_parse_name() { + assert parse_name(~"org.mozilla.servo").get() == ~"servo"; + assert parse_name(~"org. mozilla.servo 2131").is_err(); +} diff --git a/src/libstd/semver.rs b/src/libstd/semver.rs new file mode 100644 index 0000000000000..97c60330a7e31 --- /dev/null +++ b/src/libstd/semver.rs @@ -0,0 +1,395 @@ +// 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 or the MIT license +// , 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; +use core::cmp; + +#[deriving_eq] +pub enum Identifier { + Numeric(uint), + AlphaNumeric(~str) +} + +impl cmp::Ord for Identifier { + #[inline(always)] + pure fn lt(&self, other: &Identifier) -> bool { + match (self, other) { + (&Numeric(a), &Numeric(b)) => a < b, + (&Numeric(_), _) => true, + (&AlphaNumeric(ref a), &AlphaNumeric(ref b)) => *a < *b, + (&AlphaNumeric(_), _) => false + } + } + #[inline(always)] + pure fn le(&self, other: &Identifier) -> bool { + ! (other < self) + } + #[inline(always)] + pure fn gt(&self, other: &Identifier) -> bool { + other < self + } + #[inline(always)] + pure fn ge(&self, other: &Identifier) -> bool { + ! (self < other) + } +} + +impl ToStr for Identifier { + #[inline(always)] + pure fn to_str(&self) -> ~str { + match self { + &Numeric(n) => n.to_str(), + &AlphaNumeric(ref s) => s.to_str() + } + } +} + + +#[deriving_eq] +pub struct Version { + major: uint, + minor: uint, + patch: uint, + pre: ~[Identifier], + build: ~[Identifier], +} + +impl ToStr for Version { + #[inline(always)] + pure fn to_str(&self) -> ~str { + let s = fmt!("%u.%u.%u", self.major, self.minor, self.patch); + let s = if self.pre.is_empty() { + s + } else { + s + "-" + str::connect(self.pre.map(|i| i.to_str()), ".") + }; + if self.build.is_empty() { + s + } else { + s + "+" + str::connect(self.build.map(|i| i.to_str()), ".") + } + } +} + +impl cmp::Ord for Version { + #[inline(always)] + pure fn lt(&self, other: &Version) -> bool { + + self.major < other.major || + + (self.major == other.major && + self.minor < other.minor) || + + (self.major == other.major && + self.minor == other.minor && + self.patch < other.patch) || + + (self.major == other.major && + self.minor == other.minor && + self.patch == other.patch && + // NB: semver spec says 0.0.0-pre < 0.0.0 + // but the version of ord defined for vec + // says that [] < [pre], so we alter it + // here. + (match (self.pre.len(), other.pre.len()) { + (0, 0) => false, + (0, _) => false, + (_, 0) => true, + (_, _) => self.pre < other.pre + })) || + + (self.major == other.major && + self.minor == other.minor && + self.patch == other.patch && + self.pre == other.pre && + self.build < other.build) + } + + #[inline(always)] + pure fn le(&self, other: &Version) -> bool { + ! (other < self) + } + #[inline(always)] + pure fn gt(&self, other: &Version) -> bool { + other < self + } + #[inline(always)] + pure fn ge(&self, other: &Version) -> bool { + ! (self < other) + } +} + +condition! { + bad_parse: () -> (); +} + +fn take_nonempty_prefix(rdr: io::Reader, + ch: char, + pred: fn(char) -> bool) -> (~str, char) { + let mut buf = ~""; + let mut ch = ch; + while pred(ch) { + str::push_char(&mut buf, ch); + ch = rdr.read_char(); + } + if buf.is_empty() { + bad_parse::cond.raise(()) + } + debug!("extracted nonempty prefix: %s", buf); + (buf, ch) +} + +fn take_num(rdr: io::Reader, ch: char) -> (uint, char) { + let (s, ch) = take_nonempty_prefix(rdr, ch, char::is_digit); + match uint::from_str(s) { + None => { bad_parse::cond.raise(()); (0, ch) }, + Some(i) => (i, ch) + } +} + +fn take_ident(rdr: io::Reader, ch: char) -> (Identifier, char) { + let (s,ch) = take_nonempty_prefix(rdr, ch, char::is_alphanumeric); + if s.all(char::is_digit) { + match uint::from_str(s) { + None => { bad_parse::cond.raise(()); (Numeric(0), ch) }, + Some(i) => (Numeric(i), ch) + } + } else { + (AlphaNumeric(s), ch) + } +} + +fn expect(ch: char, c: char) { + if ch != c { + bad_parse::cond.raise(()) + } +} + +fn parse_reader(rdr: io::Reader) -> Version { + + let (major, ch) = take_num(rdr, rdr.read_char()); + expect(ch, '.'); + let (minor, ch) = take_num(rdr, rdr.read_char()); + expect(ch, '.'); + let (patch, ch) = take_num(rdr, rdr.read_char()); + + let mut pre = ~[]; + let mut build = ~[]; + + let mut ch = ch; + if ch == '-' { + loop { + let (id, c) = take_ident(rdr, rdr.read_char()); + pre.push(id); + ch = c; + if ch != '.' { break; } + } + } + + if ch == '+' { + loop { + let (id, c) = take_ident(rdr, rdr.read_char()); + build.push(id); + ch = c; + if ch != '.' { break; } + } + } + + Version { + major: major, + minor: minor, + patch: patch, + pre: pre, + build: build, + } +} + + +pub fn parse(s: &str) -> Option { + if ! str::is_ascii(s) { + return None; + } + let s = s.trim(); + let mut bad = false; + do bad_parse::cond.trap(|_| { debug!("bad"); bad = true }).in { + do io::with_str_reader(s) |rdr| { + let v = parse_reader(rdr); + if bad || v.to_str() != s { + None + } else { + Some(v) + } + } + } +} + +#[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(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[], + build: ~[], + }); + assert parse(" 1.2.3 ") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[], + build: ~[], + }); + assert parse("1.2.3-alpha1") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[AlphaNumeric(~"alpha1")], + build: ~[] + }); + assert parse(" 1.2.3-alpha1 ") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[AlphaNumeric(~"alpha1")], + build: ~[] + }); + assert parse("1.2.3+build5") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[], + build: ~[AlphaNumeric(~"build5")] + }); + assert parse(" 1.2.3+build5 ") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[], + build: ~[AlphaNumeric(~"build5")] + }); + assert parse("1.2.3-alpha1+build5") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[AlphaNumeric(~"alpha1")], + build: ~[AlphaNumeric(~"build5")] + }); + assert parse(" 1.2.3-alpha1+build5 ") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[AlphaNumeric(~"alpha1")], + build: ~[AlphaNumeric(~"build5")] + }); + assert parse("1.2.3-1.alpha1.9+build5.7.3aedf ") == Some(Version { + major: 1u, + minor: 2u, + patch: 3u, + pre: ~[Numeric(1),AlphaNumeric(~"alpha1"),Numeric(9)], + build: ~[AlphaNumeric(~"build5"), + Numeric(7), + AlphaNumeric(~"3aedf")] + }); + +} + +#[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-alpha1") < parse("1.2.3"); + 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-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-alpha1"); + assert parse("1.2.3") > parse("1.2.3-alpha2"); + 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-alpha1"); + assert parse("1.2.3-alpha2") >= parse("1.2.3-alpha2"); +} + +#[test] +fn test_spec_order() { + + let vs = ["1.0.0-alpha", + "1.0.0-alpha.1", + "1.0.0-beta.2", + "1.0.0-beta.11", + "1.0.0-rc.1", + "1.0.0-rc.1+build.1", + "1.0.0", + "1.0.0+0.3.7", + "1.3.7+build", + "1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a"]; + let mut i = 1; + while i < vs.len() { + let a = parse(vs[i-1]).get(); + let b = parse(vs[i]).get(); + assert a < b; + i += 1; + } +} \ No newline at end of file diff --git a/src/libstd/std.rc b/src/libstd/std.rc index 3c2baae6d57c1..b0756104fe532 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -99,6 +99,7 @@ pub mod rl; pub mod workcache; pub mod bigint; pub mod stats; +pub mod semver; #[cfg(unicode)] mod unicode;