diff options
96 files changed, 2864 insertions, 3122 deletions
diff --git a/RELEASES.txt b/RELEASES.txt index 33f749d0010..379f64cce1a 100644 --- a/RELEASES.txt +++ b/RELEASES.txt @@ -1,3 +1,148 @@ +Version 0.8 (October 2013) +-------------------------- + + * ~2100 changes, numerous bugfixes + + * Language + * The `for` loop syntax has changed to work with the `Iterator` trait. + * At long last, unwinding works on Windows. + * Default methods definitely mostly work. + * Many trait inheritance bugs fixed. + * Owned and borrowed trait objects work more reliably. + * `copy` is no longer a keyword. It has been replaced by the `Clone` trait. + * rustc no longer emits code for the `debug!` macro unless it is passed + `--cfg debug` + * mod.rs is now "blessed". When loading `mod foo;`, rustc will now look + for foo.rs, then foo/mod.rs, and will generate an error when both are + present. + * Strings no longer contain trailing nulls. The new `std::c_str` module + provides new mechanisms for converting to C strings. + * The type of foreign functions is now `extern "C" fn` instead of `*u8'. + * The FFI has been overhauled such that foreign functions are called directly, + instead of through a stack-switching wrapper. + * Calling a foreign function must be done through a Rust function with the + `#[fixed_stack_segment]` attribute. + * The `externfn!` macro can be used to declare both a foreign function and + a `#[fixed_stack_segment]` wrapper at once. + * `pub` and `priv` modifiers on `extern` blocks are no longer parsed. + * `unsafe` is no longer allowed on extern fns - they are all unsafe. + * `priv` is disallowed everywhere except for struct fields and enum variants. + * `&T` (besides `&'static T`) is no longer allowed in `@T`. + * `ref` bindings in irrefutable patterns work correctly now. + * `char` is now prevented from containing invalid code points. + * Casting to `bool` is no longer allowed. + * `yield` is a reserved keyword. + * `typeof` is a reserved keyword. + * Crates may be imported by URL with `extern mod foo = "url";`. + * Explicit enum discriminants may be given as uints as in `enum E { V = 0u }` + * Static vectors can be initialized with repeating elements, + e.g. `static foo: [u8, .. 100]: [0, .. 100];`. + * Static structs can be initialized with functional record update, + e.g. `static foo: Foo = Foo { a: 5, .. bar };`. + * `cfg!` can be used to conditionally execute code based on the crate + configuration, similarly to `#[cfg(...)]`. + * The `unnecessary_qualification` lint detects unneeded module + prefixes (default: allow). + * Arithmetic operations have been implemented on the SIMD types in + `std::unstable::simd`. + * Exchange allocation headers were removed, reducing memory usage. + * `format!` implements a completely new, extensible, and higher-performance + string formatting system. It will replace `fmt!`. + * `print!` and `println!` write formatted strings (using the `format!` + extension) to stdout. + * `write!` and `writeln!` write formatted strings (using the `format!` + extension) to the new Writers in `std::rt::io`. + * The library section in which a function or static is placed may + be specified with `#[link_section = "..."]`. + * The `proto!` syntax extension for defining bounded message protocols + was removed. + * `macro_rules!` is hygenic for `let` declarations. + * The `#[export_name]` attribute specifies the name of a symbol. + * `unreachable!` can be used to indicate unreachable code, and fails + if executed. + + * Libraries + * std: Transitioned to the new runtime, written in Rust. + * std: Added an experimental I/O library, `rt::io`, based on the new + runtime. + * std: A new generic `range` function was added to the prelude, replacing + `uint::range` and friends. + * std: `range_rev` no longer exists. Since range is an iterator it can be + reversed with `range(lo, hi).invert()`. + * std: The `chain` method on option renamed to `and_then`; `unwrap_or_default` + renamed to `unwrap_or`. + * std: The `iterator` module was renamed to `iter`. + * std: Integral types now support the `checked_add`, `checked_sub`, and + `checked_mul` operations for detecting overflow. + * std: Many methods in `str`, `vec`, `option, `result` were renamed for + consistency. + * std: Methods are standardizing on conventions for casting methods: + `to_foo` for copying, `into_foo` for moving, `as_foo` for temporary + and cheap casts. + * std: The `CString` type in `c_str` provides new ways to convert to and + from C strings. + * std: `DoubleEndedIterator` can yield elements in two directions. + * std: The `mut_split` method on vectors partitions an `&mut [T]` into + two splices. + * std: `str::from_bytes` renamed to `str::from_utf8`. + * std: `pop_opt` and `shift_opt` methods added to vectors. + * std: The task-local data interface no longer uses @, and keys are + no longer function pointers. + * std: The `swap_unwrap` method of `Option` renamed to `take_unwrap`. + * std: Added `SharedPort` to `comm`. + * std: `Eq` has a default method for `ne`; only `eq` is required + in implementations. + * std: `Ord` has default methods for `le`, `gt` and `le`; only `lt` + is required in implementations. + * std: `is_utf8` performance is improved, impacting many string functions. + * std: `os::MemoryMap` provides cross-platform mmap. + * std: `ptr::offset` is now unsafe, but also more optimized. Offsets that + are not 'in-bounds' are considered undefined. + * std: Many freestanding functions in `vec` removed in favor of methods. + * std: Many freestanding functions on scalar types removed in favor of + methods. + * std: Many options to task builders were removed since they don't make + sense in the new scheduler design. + * std: More containers implement `FromIterator` so can be created by the + `collect` method. + * std: More complete atomic types in `unstable::atomics`. + * std: `comm::PortSet` removed. + * std: Mutating methods in the `Set` and `Map` traits have been moved into + the `MutableSet` and `MutableMap` traits. `Container::is_empty`, + `Map::contains_key`, `MutableMap::insert`, and `MutableMap::remove` have + default implementations. + * extra: `dlist`, the doubly-linked list was modernized. + * extra: Added a `hex` module with `ToHex` and `FromHex` traits. + * extra: Added `glob` module, replacing `std::os::glob`. + * extra: `rope` was removed. + * extra: `deque` was renamed to `ringbuf`. `RingBuf` implements `Deque`. + * extra: `net`, and `timer` were removed. The experimental replacements + are `std::rt::io::net` and `std::rt::io::timer`. + * extra: Iterators implemented for `SmallIntMap`. + * extra: Iterators implemented for `Bitv` and `BitvSet`. + * extra: `SmallIntSet` removed. Use `BitvSet`. + * extra: Performance of JSON parsing greatly improved. + * extra: `semver` updated to SemVer 2.0.0. + * extra: `term` handles more terminals correctly. + * extra: `dbg` module removed. + + * Other + * rustc's debug info generation (`-Z debug-info`) is greatly improved. + * rustc accepts `--target-cpu` to compile to a specific CPU architecture, + similarly to gcc's `--march` flag. + * rustpkg has received many improvements. + * rustpkg supports git tags as package IDs. + * rustpkg builds into target-specific directories so it can be used for + cross-compiling. + * The number of concurrent test tasks is controlled by the environment + variable RUST_TEST_TASKS. + * The test harness can now report metrics for benchmarks. + * All tools have man pages. + * Programs compiled with `--test` now support the `-h` and `--help` flags. + * The runtime uses jemalloc for allocations. + * Segmented stacks are temporarily disabled as part of the transition to + the new runtime. Stack overflows are possible! + Version 0.7 (July 2013) ----------------------- diff --git a/doc/rust.md b/doc/rust.md index 9ebb3384c61..d10238c1483 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -248,7 +248,7 @@ string_body : non_double_quote | '\x5c' [ '\x22' | common_escape ] ; common_escape : '\x5c' - | 'n' | 'r' | 't' + | 'n' | 'r' | 't' | '0' | 'x' hex_digit 2 | 'u' hex_digit 4 | 'U' hex_digit 8 ; diff --git a/mk/llvm.mk b/mk/llvm.mk index 9e024ffa9f4..9de04bed598 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -28,7 +28,7 @@ LLVM_STAMP_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-auto-clean-stamp $$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1)) @$$(call E, make: llvm) - $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV) + $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV_$(1)) $$(Q)touch $$(LLVM_CONFIG_$(1)) endif diff --git a/mk/platform.mk b/mk/platform.mk index ac6cf3c5451..2956c6cd251 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -343,6 +343,7 @@ CFG_PATH_MUNGE_mips-unknown-linux-gnu := true CFG_LDPATH_mips-unknown-linux-gnu := CFG_RUN_mips-unknown-linux-gnu= CFG_RUN_TARG_mips-unknown-linux-gnu= +RUSTC_FLAGS_mips-unknown-linux-gnu := --linker=$(CXX_mips-unknown-linux-gnu) --target-cpu mips32r2 --target-feature +mips32r2,+o32 # i686-pc-mingw32 configuration CC_i686-pc-mingw32=$(CC) @@ -352,7 +353,7 @@ AR_i686-pc-mingw32=$(AR) CFG_LIB_NAME_i686-pc-mingw32=$(1).dll CFG_LIB_GLOB_i686-pc-mingw32=$(1)-*.dll CFG_LIB_DSYM_GLOB_i686-pc-mingw32=$(1)-*.dylib.dSYM -CFG_GCCISH_CFLAGS_i686-pc-mingw32 := -Wall -Werror -g -m32 -march=i686 -D_WIN32_WINNT=0x0600 +CFG_GCCISH_CFLAGS_i686-pc-mingw32 := -Wall -Werror -g -m32 -march=i686 -D_WIN32_WINNT=0x0600 -I$(CFG_SRC_DIR)src/etc/mingw-fix-include CFG_GCCISH_CXXFLAGS_i686-pc-mingw32 := -fno-rtti CFG_GCCISH_LINK_FLAGS_i686-pc-mingw32 := -shared -fPIC -g -m32 CFG_GCCISH_DEF_FLAG_i686-pc-mingw32 := @@ -361,6 +362,7 @@ CFG_GCCISH_POST_LIB_FLAGS_i686-pc-mingw32 := CFG_DEF_SUFFIX_i686-pc-mingw32 := .mingw32.def CFG_INSTALL_NAME_i686-pc-mingw32 = CFG_LIBUV_LINK_FLAGS_i686-pc-mingw32 := -lWs2_32 -lpsapi -liphlpapi +CFG_LLVM_BUILD_ENV_i686-pc-mingw32 := CPATH=$(CFG_SRC_DIR)src/etc/mingw-fix-include CFG_EXE_SUFFIX_i686-pc-mingw32 := .exe CFG_WINDOWSY_i686-pc-mingw32 := 1 CFG_UNIXY_i686-pc-mingw32 := @@ -479,7 +481,7 @@ define CFG_MAKE_TOOLCHAIN $$(CFG_GCCISH_DEF_FLAG_$(1))$$(3) $$(2) \ $$(call CFG_INSTALL_NAME_$(1),$$(4)) - ifneq ($(HOST_$(1)),arm) + ifeq ($$(findstring $(HOST_$(1)),arm mips),) # We're using llvm-mc as our assembler because it supports # .cfi pseudo-ops on mac @@ -491,7 +493,7 @@ define CFG_MAKE_TOOLCHAIN -o=$$(1) else - # For the ARM crosses, use the toolchain assembler + # For the ARM and MIPS crosses, use the toolchain assembler # XXX: We should be able to use the LLVM assembler CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1) diff --git a/mk/rt.mk b/mk/rt.mk index 35ee42f9cb4..d1a4b2bc87a 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -24,7 +24,7 @@ # working under these assumptions). # Hack for passing flags into LIBUV, see below. -LIBUV_FLAGS_i386 = -m32 -fPIC +LIBUV_FLAGS_i386 = -m32 -fPIC -I$(S)src/etc/mingw-fix-include LIBUV_FLAGS_x86_64 = -m64 -fPIC ifeq ($(OSTYPE_$(1)), linux-androideabi) LIBUV_FLAGS_arm = -fPIC -DANDROID -std=gnu99 @@ -71,7 +71,6 @@ RUNTIME_CXXS_$(1)_$(2) := \ rt/sync/lock_and_signal.cpp \ rt/sync/rust_thread.cpp \ rt/rust_builtin.cpp \ - rt/rust_run_program.cpp \ rt/rust_rng.cpp \ rt/rust_upcall.cpp \ rt/rust_uv.cpp \ diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 166638bc359..93324007f98 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -17,7 +17,6 @@ extern mod extra; use std::os; use std::rt; -use std::f64; use extra::getopts; use extra::getopts::groups::{optopt, optflag, reqopt}; @@ -92,10 +91,10 @@ pub fn parse_config(args: ~[~str]) -> config { let matches = &match getopts::groups::getopts(args_, groups) { Ok(m) => m, - Err(f) => fail!(getopts::fail_str(f)) + Err(f) => fail!(f.to_err_msg()) }; - if getopts::opt_present(matches, "h") || getopts::opt_present(matches, "help") { + if matches.opt_present("h") || matches.opt_present("help") { let message = fmt!("Usage: %s [OPTIONS] [TESTNAME...]", argv0); println(getopts::groups::usage(message, groups)); println(""); @@ -103,53 +102,51 @@ pub fn parse_config(args: ~[~str]) -> config { } fn opt_path(m: &getopts::Matches, nm: &str) -> Path { - Path(getopts::opt_str(m, nm)) + Path(m.opt_str(nm).unwrap()) } config { - compile_lib_path: getopts::opt_str(matches, "compile-lib-path"), - run_lib_path: getopts::opt_str(matches, "run-lib-path"), + compile_lib_path: matches.opt_str("compile-lib-path").unwrap(), + run_lib_path: matches.opt_str("run-lib-path").unwrap(), rustc_path: opt_path(matches, "rustc-path"), - clang_path: getopts::opt_maybe_str(matches, "clang-path").map_move(|s| Path(s)), - llvm_bin_path: getopts::opt_maybe_str(matches, "llvm-bin-path").map_move(|s| Path(s)), + clang_path: matches.opt_str("clang-path").map_move(|s| Path(s)), + llvm_bin_path: matches.opt_str("llvm-bin-path").map_move(|s| Path(s)), src_base: opt_path(matches, "src-base"), build_base: opt_path(matches, "build-base"), aux_base: opt_path(matches, "aux-base"), - stage_id: getopts::opt_str(matches, "stage-id"), - mode: str_mode(getopts::opt_str(matches, "mode")), - run_ignored: getopts::opt_present(matches, "ignored"), + stage_id: matches.opt_str("stage-id").unwrap(), + mode: str_mode(matches.opt_str("mode").unwrap()), + run_ignored: matches.opt_present("ignored"), filter: if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None }, - logfile: getopts::opt_maybe_str(matches, "logfile").map_move(|s| Path(s)), - save_metrics: getopts::opt_maybe_str(matches, "save-metrics").map_move(|s| Path(s)), + logfile: matches.opt_str("logfile").map_move(|s| Path(s)), + save_metrics: matches.opt_str("save-metrics").map_move(|s| Path(s)), ratchet_metrics: - getopts::opt_maybe_str(matches, "ratchet-metrics").map_move(|s| Path(s)), + matches.opt_str("ratchet-metrics").map_move(|s| Path(s)), ratchet_noise_percent: - getopts::opt_maybe_str(matches, - "ratchet-noise-percent").map_move(|s| - f64::from_str(s).unwrap()), - runtool: getopts::opt_maybe_str(matches, "runtool"), - rustcflags: getopts::opt_maybe_str(matches, "rustcflags"), - jit: getopts::opt_present(matches, "jit"), - target: opt_str2(getopts::opt_maybe_str(matches, "target")).to_str(), - adb_path: opt_str2(getopts::opt_maybe_str(matches, "adb-path")).to_str(), + matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)), + runtool: matches.opt_str("runtool"), + rustcflags: matches.opt_str("rustcflags"), + jit: matches.opt_present("jit"), + target: opt_str2(matches.opt_str("target")).to_str(), + adb_path: opt_str2(matches.opt_str("adb-path")).to_str(), adb_test_dir: - opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")).to_str(), + opt_str2(matches.opt_str("adb-test-dir")).to_str(), adb_device_status: - if (opt_str2(getopts::opt_maybe_str(matches, "target")) == + if (opt_str2(matches.opt_str("target")) == ~"arm-linux-androideabi") { - if (opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) != + if (opt_str2(matches.opt_str("adb-test-dir")) != ~"(none)" && - opt_str2(getopts::opt_maybe_str(matches, "adb-test-dir")) != + opt_str2(matches.opt_str("adb-test-dir")) != ~"") { true } else { false } } else { false }, - test_shard: test::opt_shard(getopts::opt_maybe_str(matches, "test-shard")), - verbose: getopts::opt_present(matches, "verbose") + test_shard: test::opt_shard(matches.opt_str("test-shard")), + verbose: matches.opt_present("verbose") } } diff --git a/src/etc/mingw-fix-include/README.txt b/src/etc/mingw-fix-include/README.txt new file mode 100644 index 00000000000..876db17a248 --- /dev/null +++ b/src/etc/mingw-fix-include/README.txt @@ -0,0 +1,6 @@ +The purpose of these headers is to fix issues with mingw v4.0, as described in #9246. + +This works by adding this directory to GCC include search path before mingw system headers directories, +so we can intercept their inclusions and add missing definitions without having to modify files in mingw/include. + +Once mingw fixes all 3 issues mentioned in #9246, this directory and all references to it from rust/mk/* may be removed. diff --git a/src/etc/mingw-fix-include/bits/c++config.h b/src/etc/mingw-fix-include/bits/c++config.h new file mode 100644 index 00000000000..4520779e275 --- /dev/null +++ b/src/etc/mingw-fix-include/bits/c++config.h @@ -0,0 +1,8 @@ +#ifndef _FIX_CXXCONFIG_H +#define _FIX_CXXCONFIG_H 1 + +#define _GLIBCXX_HAVE_FENV_H 1 + +#include_next <bits/c++config.h> + +#endif diff --git a/src/etc/mingw-fix-include/winbase.h b/src/etc/mingw-fix-include/winbase.h new file mode 100644 index 00000000000..3be26d1cb34 --- /dev/null +++ b/src/etc/mingw-fix-include/winbase.h @@ -0,0 +1,8 @@ +#ifndef _FIX_WINBASE_H +#define _FIX_WINBASE_H 1 + +#define NTDDK_VERSION NTDDI_VERSION + +#include_next <winbase.h> + +#endif diff --git a/src/etc/mingw-fix-include/winsock2.h b/src/etc/mingw-fix-include/winsock2.h new file mode 100644 index 00000000000..36b58dcd1ee --- /dev/null +++ b/src/etc/mingw-fix-include/winsock2.h @@ -0,0 +1,12 @@ +#ifndef _FIX_WINSOCK2_H +#define _FIX_WINSOCK2_H 1 + +#include_next <winsock2.h> + +typedef struct pollfd { + SOCKET fd; + short events; + short revents; +} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; + +#endif diff --git a/src/libextra/getopts.rs b/src/libextra/getopts.rs index a21d9dc605f..0116c5a1f66 100644 --- a/src/libextra/getopts.rs +++ b/src/libextra/getopts.rs @@ -8,93 +8,89 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * Simple getopt alternative. - * - * Construct a vector of options, either by using reqopt, optopt, and optflag - * or by building them from components yourself, and pass them to getopts, - * along with a vector of actual arguments (not including argv[0]). You'll - * either get a failure code back, or a match. You'll have to verify whether - * the amount of 'free' arguments in the match is what you expect. Use opt_* - * accessors to get argument values out of the matches object. - * - * Single-character options are expected to appear on the command line with a - * single preceding dash; multiple-character options are expected to be - * proceeded by two dashes. Options that expect an argument accept their - * argument following either a space or an equals sign. Single-character - * options don't require the space. - * - * # Example - * - * The following example shows simple command line parsing for an application - * that requires an input file to be specified, accepts an optional output - * file name following -o, and accepts both -h and --help as optional flags. - * - * ``` - * extern mod extra; - * use extra::getopts::*; - * use std::os; - * - * fn do_work(in: &str, out: Option<~str>) { - * println(in); - * println(match out { - * Some(x) => x, - * None => ~"No Output" - * }); - * } - * - * fn print_usage(program: &str, _opts: &[Opt]) { - * printfln!("Usage: %s [options]", program); - * println("-o\t\tOutput"); - * println("-h --help\tUsage"); - * } - * - * fn main() { - * let args = os::args(); - * - * let program = args[0].clone(); - * - * let opts = ~[ - * optopt("o"), - * optflag("h"), - * optflag("help") - * ]; - * let matches = match getopts(args.tail(), opts) { - * Ok(m) => { m } - * Err(f) => { fail!(fail_str(f)) } - * }; - * if opt_present(&matches, "h") || opt_present(&matches, "help") { - * print_usage(program, opts); - * return; - * } - * let output = opt_maybe_str(&matches, "o"); - * let input: &str = if !matches.free.is_empty() { - * matches.free[0].clone() - * } else { - * print_usage(program, opts); - * return; - * }; - * do_work(input, output); - * } - * ``` - */ - -#[allow(missing_doc)]; - +//! Simple getopt alternative. +//! +//! Construct a vector of options, either by using reqopt, optopt, and optflag +//! or by building them from components yourself, and pass them to getopts, +//! along with a vector of actual arguments (not including argv[0]). You'll +//! either get a failure code back, or a match. You'll have to verify whether +//! the amount of 'free' arguments in the match is what you expect. Use opt_* +//! accessors to get argument values out of the matches object. +//! +//! Single-character options are expected to appear on the command line with a +//! single preceding dash; multiple-character options are expected to be +//! proceeded by two dashes. Options that expect an argument accept their +//! argument following either a space or an equals sign. Single-character +//! options don't require the space. +//! +//! # Example +//! +//! The following example shows simple command line parsing for an application +//! that requires an input file to be specified, accepts an optional output +//! file name following -o, and accepts both -h and --help as optional flags. +//! +//! ``` +//! exter mod extra; +//! use extra::getopts::*; +//! use std::os; +//! +//! fn do_work(inp: &str, out: Option<~str>) { +//! println(inp); +//! println(match out { +//! Some(x) => x, +//! None => ~"No Output" +//! }); +//! } +//! +//! fn print_usage(program: &str, _opts: &[Opt]) { +//! printfln!("Usage: %s [options]", program); +//! println("-o\t\tOutput"); +//! println("-h --help\tUsage"); +//! } +//! +//! fn main() { +//! let args = os::args(); +//! +//! let program = args[0].clone(); +//! +//! let opts = ~[ +//! optopt("o"), +//! optflag("h"), +//! optflag("help") +//! ]; +//! let matches = match getopts(args.tail(), opts) { +//! Ok(m) => { m } +//! Err(f) => { fail!(f.to_err_msg()) } +//! }; +//! if matches.opt_present("h") || matches.opt_present("help") { +//! print_usage(program, opts); +//! return; +//! } +//! let output = matches.opt_str("o"); +//! let input: &str = if !matches.free.is_empty() { +//! matches.free[0].clone() +//! } else { +//! print_usage(program, opts); +//! return; +//! }; +//! do_work(input, output); +//! } +//! ``` use std::cmp::Eq; use std::result::{Err, Ok}; use std::result; use std::option::{Some, None}; -use std::str; use std::vec; +/// Name of an option. Either a string or a single char. #[deriving(Clone, Eq)] pub enum Name { Long(~str), Short(char), } +/// Describes whether an option has an argument. #[deriving(Clone, Eq)] pub enum HasArg { Yes, @@ -102,6 +98,7 @@ pub enum HasArg { Maybe, } +/// Describes how often an option may occur. #[deriving(Clone, Eq)] pub enum Occur { Req, @@ -109,94 +106,189 @@ pub enum Occur { Multi, } -/// A description of a possible option +/// A description of a possible option. #[deriving(Clone, Eq)] pub struct Opt { + /// Name of the option name: Name, + /// Wheter it has an argument hasarg: HasArg, + /// How often it can occur occur: Occur, + /// Which options it aliases aliases: ~[Opt], } -fn mkname(nm: &str) -> Name { - if nm.len() == 1u { - Short(nm.char_at(0u)) - } else { - Long(nm.to_owned()) - } +/// Describes wether an option is given at all or has a value. +#[deriving(Clone, Eq)] +enum Optval { + Val(~str), + Given, } -/// Create an option that is required and takes an argument -pub fn reqopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Req, aliases: ~[]}; +/// The result of checking command line arguments. Contains a vector +/// of matches and a vector of free strings. +#[deriving(Clone, Eq)] +pub struct Matches { + /// Options that matched + opts: ~[Opt], + /// Values of the Options that matched + vals: ~[~[Optval]], + /// Free string fragments + free: ~[~str] } -/// Create an option that is optional and takes an argument -pub fn optopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Optional, aliases: ~[]}; +/// The type returned when the command line does not conform to the +/// expected format. Pass this value to <fail_str> to get an error message. +#[deriving(Clone, Eq, ToStr)] +pub enum Fail_ { + ArgumentMissing(~str), + UnrecognizedOption(~str), + OptionMissing(~str), + OptionDuplicated(~str), + UnexpectedArgument(~str), } -/// Create an option that is optional and does not take an argument -pub fn optflag(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: No, occur: Optional, aliases: ~[]}; +/// The type of failure that occured. +#[deriving(Eq)] +pub enum FailType { + ArgumentMissing_, + UnrecognizedOption_, + OptionMissing_, + OptionDuplicated_, + UnexpectedArgument_, } -/** Create an option that is optional, does not take an argument, - * and may occur multiple times. - */ -pub fn optflagmulti(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: No, occur: Multi, aliases: ~[]}; -} +/// The result of parsing a command line with a set of options. +pub type Result = result::Result<Matches, Fail_>; -/// Create an option that is optional and takes an optional argument -pub fn optflagopt(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Maybe, occur: Optional, aliases: ~[]}; -} +impl Name { + fn from_str(nm: &str) -> Name { + if nm.len() == 1u { + Short(nm.char_at(0u)) + } else { + Long(nm.to_owned()) + } + } -/** - * Create an option that is optional, takes an argument, and may occur - * multiple times - */ -pub fn optmulti(name: &str) -> Opt { - return Opt {name: mkname(name), hasarg: Yes, occur: Multi, aliases: ~[]}; + fn to_str(&self) -> ~str { + match *self { + Short(ch) => ch.to_str(), + Long(ref s) => s.to_owned() + } + } } -#[deriving(Clone, Eq)] -enum Optval { - Val(~str), - Given, -} +impl Matches { + /// FIXME: #9311 This used to be private, but rustpkg somehow managed to depend on it. + /// No idea what this does. + pub fn opt_vals(&self, nm: &str) -> ~[Optval] { + match find_opt(self.opts, Name::from_str(nm)) { + Some(id) => self.vals[id].clone(), + None => fail!("No option '%s' defined", nm) + } + } -/** - * The result of checking command line arguments. Contains a vector - * of matches and a vector of free strings. - */ -#[deriving(Clone, Eq)] -pub struct Matches { - opts: ~[Opt], - vals: ~[~[Optval]], - free: ~[~str] -} + /// FIXME: #9311 This used to be private, but rustpkg somehow managed to depend on it. + /// No idea what this does. + pub fn opt_val(&self, nm: &str) -> Option<Optval> { + let vals = self.opt_vals(nm); + if (vals.is_empty()) { + None + } else { + Some(vals[0].clone()) + } + } + + /// Returns true if an option was matched. + pub fn opt_present(&self, nm: &str) -> bool { + !self.opt_vals(nm).is_empty() + } + + /// Returns the number of times an option was matched. + pub fn opt_count(&self, nm: &str) -> uint { + self.opt_vals(nm).len() + } + + /// Returns true if any of several options were matched. + pub fn opts_present(&self, names: &[~str]) -> bool { + for nm in names.iter() { + match find_opt(self.opts, Name::from_str(*nm)) { + Some(id) if !self.vals[id].is_empty() => return true, + _ => (), + }; + } + false + } + + /// Returns the string argument supplied to one of several matching options or `None`. + pub fn opts_str(&self, names: &[~str]) -> Option<~str> { + for nm in names.iter() { + match self.opt_val(*nm) { + Some(Val(ref s)) => return Some(s.clone()), + _ => () + } + } + None + } + + /// Returns a vector of the arguments provided to all matches of the given + /// option. + /// + /// Used when an option accepts multiple values. + pub fn opt_strs(&self, nm: &str) -> ~[~str] { + let mut acc: ~[~str] = ~[]; + let r = self.opt_vals(nm); + for v in r.iter() { + match *v { + Val(ref s) => acc.push((*s).clone()), + _ => () + } + } + acc + } + + /// Returns the string argument supplied to a matching option or `None`. + pub fn opt_str(&self, nm: &str) -> Option<~str> { + let vals = self.opt_vals(nm); + if vals.is_empty() { + return None::<~str>; + } + match vals[0] { + Val(ref s) => Some((*s).clone()), + _ => None + } + } + + + /// Returns the matching string, a default, or none. + /// + /// Returns none if the option was not present, `def` if the option was + /// present but no argument was provided, and the argument if the option was + /// present and an argument was provided. + pub fn opt_default(&self, nm: &str, def: &str) -> Option<~str> { + let vals = self.opt_vals(nm); + if vals.is_empty() { return None; } + match vals[0] { + Val(ref s) => Some((*s).clone()), + _ => Some(def.to_owned()) + } + } -fn is_arg(arg: &str) -> bool { - return arg.len() > 1 && arg[0] == '-' as u8; } -fn name_str(nm: &Name) -> ~str { - return match *nm { - Short(ch) => str::from_char(ch), - Long(ref s) => (*s).clone() - }; +fn is_arg(arg: &str) -> bool { + arg.len() > 1 && arg[0] == '-' as u8 } fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> { - // search main options + // Search main options. let pos = opts.iter().position(|opt| opt.name == nm); if pos.is_some() { return pos } - // search in aliases + // Search in aliases. for candidate in opts.iter() { if candidate.aliases.iter().position(|opt| opt.name == nm).is_some() { return opts.iter().position(|opt| opt.name == candidate.name); @@ -206,56 +298,101 @@ fn find_opt(opts: &[Opt], nm: Name) -> Option<uint> { None } -/** - * The type returned when the command line does not conform to the - * expected format. Pass this value to <fail_str> to get an error message. - */ -#[deriving(Clone, Eq, ToStr)] -pub enum Fail_ { - ArgumentMissing(~str), - UnrecognizedOption(~str), - OptionMissing(~str), - OptionDuplicated(~str), - UnexpectedArgument(~str), +/// Create an option that is required and takes an argument. +pub fn reqopt(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: Yes, + occur: Req, + aliases: ~[] + } } -/// Convert a `fail_` enum into an error string -pub fn fail_str(f: Fail_) -> ~str { - return match f { - ArgumentMissing(ref nm) => { - fmt!("Argument to option '%s' missing.", *nm) - } - UnrecognizedOption(ref nm) => { - fmt!("Unrecognized option: '%s'.", *nm) - } - OptionMissing(ref nm) => { - fmt!("Required option '%s' missing.", *nm) - } - OptionDuplicated(ref nm) => { - fmt!("Option '%s' given more than once.", *nm) - } - UnexpectedArgument(ref nm) => { - fmt!("Option '%s' does not take an argument.", *nm) - } - }; +/// Create an option that is optional and takes an argument. +pub fn optopt(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: Yes, + occur: Optional, + aliases: ~[] + } } -/** - * The result of parsing a command line with a set of options - * (result::t<Matches, Fail_>) - */ -pub type Result = result::Result<Matches, Fail_>; +/// Create an option that is optional and does not take an argument. +pub fn optflag(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: No, + occur: Optional, + aliases: ~[] + } +} + +/// Create an option that is optional, does not take an argument, +/// and may occur multiple times. +pub fn optflagmulti(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: No, + occur: Multi, + aliases: ~[] + } +} + +/// Create an option that is optional and takes an optional argument. +pub fn optflagopt(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: Maybe, + occur: Optional, + aliases: ~[] + } +} + +/// Create an option that is optional, takes an argument, and may occur +/// multiple times. +pub fn optmulti(name: &str) -> Opt { + Opt { + name: Name::from_str(name), + hasarg: Yes, + occur: Multi, + aliases: ~[] + } +} -/** - * Parse command line arguments according to the provided options - * - * On success returns `ok(Opt)`. Use functions such as `opt_present` - * `opt_str`, etc. to interrogate results. Returns `err(Fail_)` on failure. - * Use <fail_str> to get an error message. - */ +impl Fail_ { + /// Convert a `Fail_` enum into an error string. + pub fn to_err_msg(self) -> ~str { + match self { + ArgumentMissing(ref nm) => { + fmt!("Argument to option '%s' missing.", *nm) + } + UnrecognizedOption(ref nm) => { + fmt!("Unrecognized option: '%s'.", *nm) + } + OptionMissing(ref nm) => { + fmt!("Required option '%s' missing.", *nm) + } + OptionDuplicated(ref nm) => { + fmt!("Option '%s' given more than once.", *nm) + } + UnexpectedArgument(ref nm) => { + fmt!("Option '%s' does not take an argument.", *nm) + } + } + } +} + +/// Parse command line arguments according to the provided options. +/// +/// On success returns `Ok(Opt)`. Use methods such as `opt_present` +/// `opt_str`, etc. to interrogate results. Returns `Err(Fail_)` on failure. +/// Use `to_err_msg` to get an error message. pub fn getopts(args: &[~str], opts: &[Opt]) -> Result { let n_opts = opts.len(); + fn f(_x: uint) -> ~[Optval] { return ~[]; } + let mut vals = vec::from_fn(n_opts, f); let mut free: ~[~str] = ~[]; let l = args.len(); @@ -325,12 +462,12 @@ pub fn getopts(args: &[~str], opts: &[Opt]) -> Result { name_pos += 1; let optid = match find_opt(opts, (*nm).clone()) { Some(id) => id, - None => return Err(UnrecognizedOption(name_str(nm))) + None => return Err(UnrecognizedOption(nm.to_str())) }; match opts[optid].hasarg { No => { if !i_arg.is_none() { - return Err(UnexpectedArgument(name_str(nm))); + return Err(UnexpectedArgument(nm.to_str())); } vals[optid].push(Given); } @@ -346,7 +483,7 @@ pub fn getopts(args: &[~str], opts: &[Opt]) -> Result { if !i_arg.is_none() { vals[optid].push(Val(i_arg.clone().unwrap())); } else if i + 1 == l { - return Err(ArgumentMissing(name_str(nm))); + return Err(ArgumentMissing(nm.to_str())); } else { i += 1; vals[optid].push(Val(args[i].clone())); } } } @@ -360,289 +497,183 @@ pub fn getopts(args: &[~str], opts: &[Opt]) -> Result { let occ = opts[i].occur; if occ == Req { if n == 0 { - return Err(OptionMissing(name_str(&(opts[i].name)))); + return Err(OptionMissing(opts[i].name.to_str())); } } if occ != Multi { if n > 1 { - return Err(OptionDuplicated(name_str(&(opts[i].name)))); + return Err(OptionDuplicated(opts[i].name.to_str())); } } i += 1; } - return Ok(Matches {opts: opts.to_owned(), - vals: vals, - free: free}); -} - -fn opt_vals(mm: &Matches, nm: &str) -> ~[Optval] { - return match find_opt(mm.opts, mkname(nm)) { - Some(id) => mm.vals[id].clone(), - None => { - error!("No option '%s' defined", nm); - fail!() - } - }; -} - -fn opt_val(mm: &Matches, nm: &str) -> Option<Optval> { - let vals = opt_vals(mm, nm); - if (vals.is_empty()) { - None - } else { - Some(opt_vals(mm, nm)[0].clone()) - } -} - -/// Returns true if an option was matched -pub fn opt_present(mm: &Matches, nm: &str) -> bool { - !opt_vals(mm, nm).is_empty() -} - -/// Returns the number of times an option was matched -pub fn opt_count(mm: &Matches, nm: &str) -> uint { - opt_vals(mm, nm).len() -} - -/// Returns true if any of several options were matched -pub fn opts_present(mm: &Matches, names: &[~str]) -> bool { - for nm in names.iter() { - match find_opt(mm.opts, mkname(*nm)) { - Some(id) if !mm.vals[id].is_empty() => return true, - _ => (), - }; - } - false -} - - -/** - * Returns the string argument supplied to a matching option - * - * Fails if the option was not matched or if the match did not take an - * argument - */ -pub fn opt_str(mm: &Matches, nm: &str) -> ~str { - return match opt_val(mm, nm) { - Some(Val(s)) => s, - _ => fail!() - }; -} - -/** - * Returns the string argument supplied to one of several matching options - * - * Fails if the no option was provided from the given list, or if the no such - * option took an argument - */ -pub fn opts_str(mm: &Matches, names: &[~str]) -> ~str { - for nm in names.iter() { - match opt_val(mm, *nm) { - Some(Val(ref s)) => return (*s).clone(), - _ => () - } - } - fail!(); + Ok(Matches { + opts: opts.to_owned(), + vals: vals, + free: free + }) } - -/** - * Returns a vector of the arguments provided to all matches of the given - * option. - * - * Used when an option accepts multiple values. - */ -pub fn opt_strs(mm: &Matches, nm: &str) -> ~[~str] { - let mut acc: ~[~str] = ~[]; - let r = opt_vals(mm, nm); - for v in r.iter() { - match *v { Val(ref s) => acc.push((*s).clone()), _ => () } - } - acc -} - -/// Returns the string argument supplied to a matching option or none -pub fn opt_maybe_str(mm: &Matches, nm: &str) -> Option<~str> { - let vals = opt_vals(mm, nm); - if vals.is_empty() { return None::<~str>; } - return match vals[0] { - Val(ref s) => Some((*s).clone()), - _ => None - }; -} - - -/** - * Returns the matching string, a default, or none - * - * Returns none if the option was not present, `def` if the option was - * present but no argument was provided, and the argument if the option was - * present and an argument was provided. - */ -pub fn opt_default(mm: &Matches, nm: &str, def: &str) -> Option<~str> { - let vals = opt_vals(mm, nm); - if vals.is_empty() { return None::<~str>; } - return match vals[0] { Val(ref s) => Some::<~str>((*s).clone()), - _ => Some::<~str>(def.to_owned()) } -} - -#[deriving(Eq)] -pub enum FailType { - ArgumentMissing_, - UnrecognizedOption_, - OptionMissing_, - OptionDuplicated_, - UnexpectedArgument_, -} - -/** A module which provides a way to specify descriptions and - * groups of short and long option names, together. - */ +/// A module which provides a way to specify descriptions and +/// groups of short and long option names, together. pub mod groups { use getopts::{HasArg, Long, Maybe, Multi, No, Occur, Opt, Optional, Req}; use getopts::{Short, Yes}; - /** one group of options, e.g., both -h and --help, along with - * their shared description and properties - */ + /// One group of options, e.g., both -h and --help, along with + /// their shared description and properties. #[deriving(Clone, Eq)] pub struct OptGroup { + /// Short Name of the `OptGroup` short_name: ~str, + /// Long Name of the `OptGroup` long_name: ~str, + /// Hint hint: ~str, + /// Description desc: ~str, + /// Whether it has an argument hasarg: HasArg, + /// How often it can occur occur: Occur } - /// Create a long option that is required and takes an argument - pub fn reqopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { + impl OptGroup { + /// Translate OptGroup into Opt. + /// (Both short and long names correspond to different Opts). + pub fn long_to_short(&self) -> Opt { + let OptGroup { + short_name: short_name, + long_name: long_name, + hasarg: hasarg, + occur: occur, + _ + } = (*self).clone(); + + match (short_name.len(), long_name.len()) { + (0,0) => fail!("this long-format option was given no name"), + (0,_) => Opt { + name: Long((long_name)), + hasarg: hasarg, + occur: occur, + aliases: ~[] + }, + (1,0) => Opt { + name: Short(short_name.char_at(0)), + hasarg: hasarg, + occur: occur, + aliases: ~[] + }, + (1,_) => Opt { + name: Long((long_name)), + hasarg: hasarg, + occur: occur, + aliases: ~[ + Opt { + name: Short(short_name.char_at(0)), + hasarg: hasarg, + occur: occur, + aliases: ~[] + } + ] + }, + (_,_) => fail!("something is wrong with the long-form opt") + } + } + } + + /// Create a long option that is required and takes an argument. + pub fn reqopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup { short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: hint.to_owned(), - desc: desc.to_owned(), - hasarg: Yes, - occur: Req}; + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: hint.to_owned(), + desc: desc.to_owned(), + hasarg: Yes, + occur: Req + } } - /// Create a long option that is optional and takes an argument - pub fn optopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { + /// Create a long option that is optional and takes an argument. + pub fn optopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup {short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: hint.to_owned(), - desc: desc.to_owned(), - hasarg: Yes, - occur: Optional}; + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: hint.to_owned(), + desc: desc.to_owned(), + hasarg: Yes, + occur: Optional + } } - /// Create a long option that is optional and does not take an argument - pub fn optflag(short_name: &str, long_name: &str, - desc: &str) -> OptGroup { + /// Create a long option that is optional and does not take an argument. + pub fn optflag(short_name: &str, long_name: &str, desc: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup {short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: ~"", - desc: desc.to_owned(), - hasarg: No, - occur: Optional}; + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: ~"", + desc: desc.to_owned(), + hasarg: No, + occur: Optional + } } /// Create a long option that can occur more than once and does not - /// take an argument - pub fn optflagmulti(short_name: &str, long_name: &str, - desc: &str) -> OptGroup { + /// take an argument. + pub fn optflagmulti(short_name: &str, long_name: &str, desc: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup {short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: ~"", - desc: desc.to_owned(), - hasarg: No, - occur: Multi}; + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: ~"", + desc: desc.to_owned(), + hasarg: No, + occur: Multi + } } - /// Create a long option that is optional and takes an optional argument - pub fn optflagopt(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { + /// Create a long option that is optional and takes an optional argument. + pub fn optflagopt(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup {short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: hint.to_owned(), - desc: desc.to_owned(), - hasarg: Maybe, - occur: Optional}; - } - - /** - * Create a long option that is optional, takes an argument, and may occur - * multiple times - */ - pub fn optmulti(short_name: &str, long_name: &str, - desc: &str, hint: &str) -> OptGroup { + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: hint.to_owned(), + desc: desc.to_owned(), + hasarg: Maybe, + occur: Optional + } + } + + /// Create a long option that is optional, takes an argument, and may occur + /// multiple times. + pub fn optmulti(short_name: &str, long_name: &str, desc: &str, hint: &str) -> OptGroup { let len = short_name.len(); assert!(len == 1 || len == 0); - return OptGroup {short_name: short_name.to_owned(), - long_name: long_name.to_owned(), - hint: hint.to_owned(), - desc: desc.to_owned(), - hasarg: Yes, - occur: Multi}; - } - - // translate OptGroup into Opt - // (both short and long names correspond to different Opts) - pub fn long_to_short(lopt: &OptGroup) -> Opt { - let OptGroup{short_name: short_name, - long_name: long_name, - hasarg: hasarg, - occur: occur, - _} = (*lopt).clone(); - - match (short_name.len(), long_name.len()) { - (0,0) => fail!("this long-format option was given no name"), - - (0,_) => Opt {name: Long((long_name)), - hasarg: hasarg, - occur: occur, - aliases: ~[]}, - - (1,0) => Opt {name: Short(short_name.char_at(0)), - hasarg: hasarg, - occur: occur, - aliases: ~[]}, - - (1,_) => Opt {name: Long((long_name)), - hasarg: hasarg, - occur: occur, - aliases: ~[Opt { - name: Short(short_name.char_at(0)), - hasarg: hasarg, - occur: occur, - aliases: ~[] - }]}, - - (_,_) => fail!("something is wrong with the long-form opt") - } - } - - /* - * Parse command line args with the provided long format options - */ + OptGroup { + short_name: short_name.to_owned(), + long_name: long_name.to_owned(), + hint: hint.to_owned(), + desc: desc.to_owned(), + hasarg: Yes, + occur: Multi + } + } + + /// Parse command line args with the provided long format options. pub fn getopts(args: &[~str], opts: &[OptGroup]) -> ::getopts::Result { - ::getopts::getopts(args, opts.map(long_to_short)) + ::getopts::getopts(args, opts.map(|x| x.long_to_short())) } - /** - * Derive a usage message from a set of long options - */ + /// Derive a usage message from a set of long options. pub fn usage(brief: &str, opts: &[OptGroup]) -> ~str { let desc_sep = "\n" + " ".repeat(24); @@ -721,28 +752,24 @@ pub mod groups { row }); - return brief.to_owned() + - "\n\nOptions:\n" + - rows.collect::<~[~str]>().connect("\n") + - "\n"; - } - - /** Splits a string into substrings with possibly internal whitespace, - * each of them at most `lim` bytes long. The substrings have leading and trailing - * whitespace removed, and are only cut at whitespace boundaries. - * - * Note: Function was moved here from `std::str` because this module is the only place that - * uses it, and because it was to specific for a general string function. - * - * #Failure: - * - * Fails during iteration if the string contains a non-whitespace - * sequence longer than the limit. - */ + fmt!("%s\n\nOptions:\n%s\n", brief, rows.collect::<~[~str]>().connect("\n")) + } + + /// Splits a string into substrings with possibly internal whitespace, + /// each of them at most `lim` bytes long. The substrings have leading and trailing + /// whitespace removed, and are only cut at whitespace boundaries. + /// + /// Note: Function was moved here from `std::str` because this module is the only place that + /// uses it, and because it was to specific for a general string function. + /// + /// #Failure: + /// + /// Fails during iteration if the string contains a non-whitespace + /// sequence longer than the limit. fn each_split_within<'a>(ss: &'a str, lim: uint, it: &fn(&'a str) -> bool) -> bool { - // Just for fun, let's write this as an state machine: + // Just for fun, let's write this as a state machine: enum SplitWithinState { A, // leading whitespace, initial state @@ -853,8 +880,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "test"))); - assert_eq!(opt_str(m, "test"), ~"20"); + assert!(m.opt_present("test")); + assert_eq!(m.opt_str("test").unwrap(), ~"20"); } _ => { fail!("test_reqopt_long failed"); } } @@ -900,8 +927,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "t"))); - assert_eq!(opt_str(m, "t"), ~"20"); + assert!(m.opt_present("t")); + assert_eq!(m.opt_str("t").unwrap(), ~"20"); } _ => fail!() } @@ -949,8 +976,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "test"))); - assert_eq!(opt_str(m, "test"), ~"20"); + assert!(m.opt_present("test")); + assert_eq!(m.opt_str("test").unwrap(), ~"20"); } _ => fail!() } @@ -962,7 +989,7 @@ mod tests { let opts = ~[optopt("test")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "test")), + Ok(ref m) => assert!(!m.opt_present("test")), _ => fail!() } } @@ -996,8 +1023,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "t"))); - assert_eq!(opt_str(m, "t"), ~"20"); + assert!((m.opt_present("t"))); + assert_eq!(m.opt_str("t").unwrap(), ~"20"); } _ => fail!() } @@ -1009,7 +1036,7 @@ mod tests { let opts = ~[optopt("t")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "t")), + Ok(ref m) => assert!(!m.opt_present("t")), _ => fail!() } } @@ -1044,7 +1071,7 @@ mod tests { let opts = ~[optflag("test")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(opt_present(m, "test")), + Ok(ref m) => assert!(m.opt_present("test")), _ => fail!() } } @@ -1055,7 +1082,7 @@ mod tests { let opts = ~[optflag("test")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "test")), + Ok(ref m) => assert!(!m.opt_present("test")), _ => fail!() } } @@ -1067,7 +1094,7 @@ mod tests { let rs = getopts(args, opts); match rs { Err(f) => { - error!(fail_str(f.clone())); + error!(f.clone().to_err_msg()); check_fail_type(f, UnexpectedArgument_); } _ => fail!() @@ -1091,7 +1118,7 @@ mod tests { let opts = ~[optflag("t")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(opt_present(m, "t")), + Ok(ref m) => assert!(m.opt_present("t")), _ => fail!() } } @@ -1102,7 +1129,7 @@ mod tests { let opts = ~[optflag("t")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "t")), + Ok(ref m) => assert!(!m.opt_present("t")), _ => fail!() } } @@ -1141,7 +1168,7 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert_eq!(opt_count(m, "v"), 1); + assert_eq!(m.opt_count("v"), 1); } _ => fail!() } @@ -1154,7 +1181,7 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert_eq!(opt_count(m, "v"), 2); + assert_eq!(m.opt_count("v"), 2); } _ => fail!() } @@ -1167,7 +1194,7 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert_eq!(opt_count(m, "v"), 2); + assert_eq!(m.opt_count("v"), 2); } _ => fail!() } @@ -1180,7 +1207,7 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert_eq!(opt_count(m, "verbose"), 1); + assert_eq!(m.opt_count("verbose"), 1); } _ => fail!() } @@ -1193,7 +1220,7 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert_eq!(opt_count(m, "verbose"), 2); + assert_eq!(m.opt_count("verbose"), 2); } _ => fail!() } @@ -1207,8 +1234,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "test"))); - assert_eq!(opt_str(m, "test"), ~"20"); + assert!((m.opt_present("test"))); + assert_eq!(m.opt_str("test").unwrap(), ~"20"); } _ => fail!() } @@ -1220,7 +1247,7 @@ mod tests { let opts = ~[optmulti("test")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "test")), + Ok(ref m) => assert!(!m.opt_present("test")), _ => fail!() } } @@ -1243,9 +1270,9 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!(opt_present(m, "test")); - assert_eq!(opt_str(m, "test"), ~"20"); - let pair = opt_strs(m, "test"); + assert!(m.opt_present("test")); + assert_eq!(m.opt_str("test").unwrap(), ~"20"); + let pair = m.opt_strs("test"); assert!(pair[0] == ~"20"); assert!(pair[1] == ~"30"); } @@ -1260,8 +1287,8 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "t"))); - assert_eq!(opt_str(m, "t"), ~"20"); + assert!((m.opt_present("t"))); + assert_eq!(m.opt_str("t").unwrap(), ~"20"); } _ => fail!() } @@ -1273,7 +1300,7 @@ mod tests { let opts = ~[optmulti("t")]; let rs = getopts(args, opts); match rs { - Ok(ref m) => assert!(!opt_present(m, "t")), + Ok(ref m) => assert!(!m.opt_present("t")), _ => fail!() } } @@ -1296,9 +1323,9 @@ mod tests { let rs = getopts(args, opts); match rs { Ok(ref m) => { - assert!((opt_present(m, "t"))); - assert_eq!(opt_str(m, "t"), ~"20"); - let pair = opt_strs(m, "t"); + assert!((m.opt_present("t"))); + assert_eq!(m.opt_str("t").unwrap(), ~"20"); + let pair = m.opt_strs("t"); assert!(pair[0] == ~"20"); assert!(pair[1] == ~"30"); } @@ -1343,18 +1370,18 @@ mod tests { Ok(ref m) => { assert!(m.free[0] == ~"prog"); assert!(m.free[1] == ~"free1"); - assert_eq!(opt_str(m, "s"), ~"20"); + assert_eq!(m.opt_str("s").unwrap(), ~"20"); assert!(m.free[2] == ~"free2"); - assert!((opt_present(m, "flag"))); - assert_eq!(opt_str(m, "long"), ~"30"); - assert!((opt_present(m, "f"))); - let pair = opt_strs(m, "m"); + assert!((m.opt_present("flag"))); + assert_eq!(m.opt_str("long").unwrap(), ~"30"); + assert!((m.opt_present("f"))); + let pair = m.opt_strs("m"); assert!(pair[0] == ~"40"); assert!(pair[1] == ~"50"); - let pair = opt_strs(m, "n"); + let pair = m.opt_strs("n"); assert!(pair[0] == ~"-A B"); assert!(pair[1] == ~"-60 70"); - assert!((!opt_present(m, "notpresent"))); + assert!((!m.opt_present("notpresent"))); } _ => fail!() } @@ -1369,34 +1396,34 @@ mod tests { result::Ok(m) => m, result::Err(_) => fail!() }; - assert!(opts_present(matches_single, [~"e"])); - assert!(opts_present(matches_single, [~"encrypt", ~"e"])); - assert!(opts_present(matches_single, [~"e", ~"encrypt"])); - assert!(!opts_present(matches_single, [~"encrypt"])); - assert!(!opts_present(matches_single, [~"thing"])); - assert!(!opts_present(matches_single, [])); + assert!(matches_single.opts_present([~"e"])); + assert!(matches_single.opts_present([~"encrypt", ~"e"])); + assert!(matches_single.opts_present([~"e", ~"encrypt"])); + assert!(!matches_single.opts_present([~"encrypt"])); + assert!(!matches_single.opts_present([~"thing"])); + assert!(!matches_single.opts_present([])); - assert_eq!(opts_str(matches_single, [~"e"]), ~"foo"); - assert_eq!(opts_str(matches_single, [~"e", ~"encrypt"]), ~"foo"); - assert_eq!(opts_str(matches_single, [~"encrypt", ~"e"]), ~"foo"); + assert_eq!(matches_single.opts_str([~"e"]).unwrap(), ~"foo"); + assert_eq!(matches_single.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo"); + assert_eq!(matches_single.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo"); let args_both = ~[~"-e", ~"foo", ~"--encrypt", ~"foo"]; let matches_both = &match getopts(args_both, opts) { result::Ok(m) => m, result::Err(_) => fail!() }; - assert!(opts_present(matches_both, [~"e"])); - assert!(opts_present(matches_both, [~"encrypt"])); - assert!(opts_present(matches_both, [~"encrypt", ~"e"])); - assert!(opts_present(matches_both, [~"e", ~"encrypt"])); - assert!(!opts_present(matches_both, [~"f"])); - assert!(!opts_present(matches_both, [~"thing"])); - assert!(!opts_present(matches_both, [])); + assert!(matches_both.opts_present([~"e"])); + assert!(matches_both.opts_present([~"encrypt"])); + assert!(matches_both.opts_present([~"encrypt", ~"e"])); + assert!(matches_both.opts_present([~"e", ~"encrypt"])); + assert!(!matches_both.opts_present([~"f"])); + assert!(!matches_both.opts_present([~"thing"])); + assert!(!matches_both.opts_present([])); - assert_eq!(opts_str(matches_both, [~"e"]), ~"foo"); - assert_eq!(opts_str(matches_both, [~"encrypt"]), ~"foo"); - assert_eq!(opts_str(matches_both, [~"e", ~"encrypt"]), ~"foo"); - assert_eq!(opts_str(matches_both, [~"encrypt", ~"e"]), ~"foo"); + assert_eq!(matches_both.opts_str([~"e"]).unwrap(), ~"foo"); + assert_eq!(matches_both.opts_str([~"encrypt"]).unwrap(), ~"foo"); + assert_eq!(matches_both.opts_str([~"e", ~"encrypt"]).unwrap(), ~"foo"); + assert_eq!(matches_both.opts_str([~"encrypt", ~"e"]).unwrap(), ~"foo"); } #[test] @@ -1407,10 +1434,10 @@ mod tests { result::Ok(m) => m, result::Err(_) => fail!() }; - assert!(opts_present(matches, [~"L"])); - assert_eq!(opts_str(matches, [~"L"]), ~"foo"); - assert!(opts_present(matches, [~"M"])); - assert_eq!(opts_str(matches, [~"M"]), ~"."); + assert!(matches.opts_present([~"L"])); + assert_eq!(matches.opts_str([~"L"]).unwrap(), ~"foo"); + assert!(matches.opts_present([~"M"])); + assert_eq!(matches.opts_str([~"M"]).unwrap(), ~"."); } @@ -1475,7 +1502,7 @@ mod tests { short.aliases = ~[reqopt("b")]; let verbose = groups::reqopt("b", "banana", "some bananas", "VAL"); - assert_eq!(groups::long_to_short(&verbose), short); + assert_eq!(verbose.long_to_short(), short); } #[test] @@ -1519,8 +1546,8 @@ mod tests { let args = ~[~"-a", ~"--apple", ~"-a"]; let matches = groups::getopts(args, opts).unwrap(); - assert_eq!(3, opt_count(&matches, "a")); - assert_eq!(3, opt_count(&matches, "apple")); + assert_eq!(3, matches.opt_count("a")); + assert_eq!(3, matches.opt_count("apple")); } #[test] diff --git a/src/libextra/glob.rs b/src/libextra/glob.rs index 39a4ac61846..43a4ecf5616 100644 --- a/src/libextra/glob.rs +++ b/src/libextra/glob.rs @@ -137,16 +137,6 @@ fn list_dir_sorted(path: &Path) -> ~[Path] { /** * A compiled Unix shell style pattern. */ -#[cfg(stage0)] -#[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes)] -pub struct Pattern { - priv tokens: ~[PatternToken] -} - -/** - * A compiled Unix shell style pattern. - */ -#[cfg(not(stage0))] #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes, Default)] pub struct Pattern { priv tokens: ~[PatternToken] @@ -465,39 +455,10 @@ fn is_sep(c: char) -> bool { } } -/** - * Configuration options to modify the behaviour of `Pattern::matches_with(..)` - */ -#[cfg(stage0)] -#[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes)] -pub struct MatchOptions { - - /** - * Whether or not patterns should be matched in a case-sensitive manner. This - * currently only considers upper/lower case relationships between ASCII characters, - * but in future this might be extended to work with Unicode. - */ - case_sensitive: bool, - - /** - * If this is true then path-component separator characters (e.g. `/` on Posix) - * must be matched by a literal `/`, rather than by `*` or `?` or `[...]` - */ - require_literal_separator: bool, - - /** - * If this is true then paths that contain components that start with a `.` will - * not match unless the `.` appears literally in the pattern: `*`, `?` or `[...]` - * will not match. This is useful because such files are conventionally considered - * hidden on Unix systems and it might be desirable to skip them when listing files. - */ - require_literal_leading_dot: bool -} /** * Configuration options to modify the behaviour of `Pattern::matches_with(..)` */ -#[cfg(not(stage0))] #[deriving(Clone, Eq, TotalEq, Ord, TotalOrd, IterBytes, Default)] pub struct MatchOptions { diff --git a/src/libextra/num/bigint.rs b/src/libextra/num/bigint.rs index 039694f5881..936efed94e4 100644 --- a/src/libextra/num/bigint.rs +++ b/src/libextra/num/bigint.rs @@ -115,8 +115,8 @@ impl TotalOrd for BigUint { if s_len > o_len { return Greater; } for (&self_i, &other_i) in self.data.rev_iter().zip(other.data.rev_iter()) { - cond!((self_i < other_i) { return Less; } - (self_i > other_i) { return Greater; }) + if self_i < other_i { return Less; } + if self_i > other_i { return Greater; } } return Equal; } diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs index 74b7aea9978..9476bcb8926 100644 --- a/src/libextra/rl.rs +++ b/src/libextra/rl.rs @@ -13,25 +13,6 @@ use std::libc::{c_char, c_int}; use std::{local_data, str, rt}; use std::unstable::finally::Finally; -#[cfg(stage0)] -pub mod rustrt { - use std::libc::{c_char, c_int}; - - extern { - fn linenoise(prompt: *c_char) -> *c_char; - fn linenoiseHistoryAdd(line: *c_char) -> c_int; - fn linenoiseHistorySetMaxLen(len: c_int) -> c_int; - fn linenoiseHistorySave(file: *c_char) -> c_int; - fn linenoiseHistoryLoad(file: *c_char) -> c_int; - fn linenoiseSetCompletionCallback(callback: *u8); - fn linenoiseAddCompletion(completions: *(), line: *c_char); - - fn rust_take_linenoise_lock(); - fn rust_drop_linenoise_lock(); - } -} - -#[cfg(not(stage0))] pub mod rustrt { use std::libc::{c_char, c_int}; @@ -109,7 +90,7 @@ pub fn read(prompt: &str) -> Option<~str> { pub type CompletionCb = @fn(~str, @fn(~str)); -static complete_key: local_data::Key<CompletionCb> = &local_data::Key; +local_data_key!(complete_key: CompletionCb) /// Bind to the main completion callback in the current task. /// diff --git a/src/libextra/test.rs b/src/libextra/test.rs index 4dcb48d2751..cc80da1506a 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -226,11 +226,11 @@ pub fn parse_opts(args: &[~str]) -> OptRes { let matches = match groups::getopts(args_, optgroups()) { Ok(m) => m, - Err(f) => return Err(getopts::fail_str(f)) + Err(f) => return Err(f.to_err_msg()) }; - if getopts::opt_present(&matches, "h") { usage(args[0], "h"); } - if getopts::opt_present(&matches, "help") { usage(args[0], "help"); } + if matches.opt_present("h") { usage(args[0], "h"); } + if matches.opt_present("help") { usage(args[0], "help"); } let filter = if matches.free.len() > 0 { @@ -239,25 +239,25 @@ pub fn parse_opts(args: &[~str]) -> OptRes { None }; - let run_ignored = getopts::opt_present(&matches, "ignored"); + let run_ignored = matches.opt_present("ignored"); - let logfile = getopts::opt_maybe_str(&matches, "logfile"); + let logfile = matches.opt_str("logfile"); let logfile = logfile.map_move(|s| Path(s)); - let run_benchmarks = getopts::opt_present(&matches, "bench"); + let run_benchmarks = matches.opt_present("bench"); let run_tests = ! run_benchmarks || - getopts::opt_present(&matches, "test"); + matches.opt_present("test"); - let ratchet_metrics = getopts::opt_maybe_str(&matches, "ratchet-metrics"); + let ratchet_metrics = matches.opt_str("ratchet-metrics"); let ratchet_metrics = ratchet_metrics.map_move(|s| Path(s)); - let ratchet_noise_percent = getopts::opt_maybe_str(&matches, "ratchet-noise-percent"); + let ratchet_noise_percent = matches.opt_str("ratchet-noise-percent"); let ratchet_noise_percent = ratchet_noise_percent.map_move(|s| from_str::<f64>(s).unwrap()); - let save_metrics = getopts::opt_maybe_str(&matches, "save-metrics"); + let save_metrics = matches.opt_str("save-metrics"); let save_metrics = save_metrics.map_move(|s| Path(s)); - let test_shard = getopts::opt_maybe_str(&matches, "test-shard"); + let test_shard = matches.opt_str("test-shard"); let test_shard = opt_shard(test_shard); let test_opts = TestOpts { diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 1fbbc1db05a..ee7fbed9e9f 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -190,7 +190,7 @@ pub mod jit { // The stage1 compiler won't work, but that doesn't really matter. TLS // changed only very recently to allow storage of owned values. - static engine_key: local_data::Key<~Engine> = &local_data::Key; + local_data_key!(engine_key: ~Engine) fn set_engine(engine: ~Engine) { local_data::set(engine_key, engine) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5078d0ded18..bd0462119bd 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -30,7 +30,6 @@ use std::io; use std::os; use std::vec; use extra::getopts::groups::{optopt, optmulti, optflag, optflagopt}; -use extra::getopts::{opt_present}; use extra::getopts; use syntax::ast; use syntax::abi; @@ -606,15 +605,15 @@ pub fn build_session_options(binary: @str, matches: &getopts::Matches, demitter: diagnostic::Emitter) -> @session::options { - let crate_type = if opt_present(matches, "lib") { + let crate_type = if matches.opt_present("lib") { session::lib_crate - } else if opt_present(matches, "bin") { + } else if matches.opt_present("bin") { session::bin_crate } else { session::unknown_crate }; - let parse_only = opt_present(matches, "parse-only"); - let no_trans = opt_present(matches, "no-trans"); + let parse_only = matches.opt_present("parse-only"); + let no_trans = matches.opt_present("no-trans"); let lint_levels = [lint::allow, lint::warn, lint::deny, lint::forbid]; @@ -627,8 +626,8 @@ pub fn build_session_options(binary: @str, // to_ascii_move and to_str_move to not do a unnecessary copy. let level_short = level_name.slice_chars(0, 1); let level_short = level_short.to_ascii().to_upper().to_str_ascii(); - let flags = vec::append(getopts::opt_strs(matches, level_short), - getopts::opt_strs(matches, level_name)); + let flags = vec::append(matches.opt_strs(level_short), + matches.opt_strs(level_name)); for lint_name in flags.iter() { let lint_name = lint_name.replace("-", "_"); match lint_dict.find_equiv(&lint_name) { @@ -644,7 +643,7 @@ pub fn build_session_options(binary: @str, } let mut debugging_opts = 0u; - let debug_flags = getopts::opt_strs(matches, "Z"); + let debug_flags = matches.opt_strs("Z"); let debug_map = session::debugging_opts_map(); for debug_flag in debug_flags.iter() { let mut this_bit = 0u; @@ -670,31 +669,31 @@ pub fn build_session_options(binary: @str, let output_type = if parse_only || no_trans { link::output_type_none - } else if opt_present(matches, "S") && - opt_present(matches, "emit-llvm") { + } else if matches.opt_present("S") && + matches.opt_present("emit-llvm") { link::output_type_llvm_assembly - } else if opt_present(matches, "S") { + } else if matches.opt_present("S") { link::output_type_assembly - } else if opt_present(matches, "c") { + } else if matches.opt_present("c") { link::output_type_object - } else if opt_present(matches, "emit-llvm") { + } else if matches.opt_present("emit-llvm") { link::output_type_bitcode } else { link::output_type_exe }; - let sysroot_opt = getopts::opt_maybe_str(matches, "sysroot").map_move(|m| @Path(m)); - let target = getopts::opt_maybe_str(matches, "target").unwrap_or(host_triple()); - let target_cpu = getopts::opt_maybe_str(matches, "target-cpu").unwrap_or(~"generic"); - let target_feature = getopts::opt_maybe_str(matches, "target-feature").unwrap_or(~""); - let save_temps = getopts::opt_present(matches, "save-temps"); + let sysroot_opt = matches.opt_str("sysroot").map_move(|m| @Path(m)); + let target = matches.opt_str("target").unwrap_or(host_triple()); + let target_cpu = matches.opt_str("target-cpu").unwrap_or(~"generic"); + let target_feature = matches.opt_str("target-feature").unwrap_or(~""); + let save_temps = matches.opt_present("save-temps"); let opt_level = { if (debugging_opts & session::no_opt) != 0 { No - } else if opt_present(matches, "O") { - if opt_present(matches, "opt-level") { + } else if matches.opt_present("O") { + if matches.opt_present("opt-level") { early_error(demitter, ~"-O and --opt-level both provided"); } Default - } else if opt_present(matches, "opt-level") { - match getopts::opt_str(matches, "opt-level") { + } else if matches.opt_present("opt-level") { + match matches.opt_str("opt-level").unwrap() { ~"0" => No, ~"1" => Less, ~"2" => Default, @@ -720,18 +719,17 @@ pub fn build_session_options(binary: @str, let statik = debugging_opts & session::statik != 0; - let addl_lib_search_paths = getopts::opt_strs(matches, "L").map(|s| Path(*s)); - let linker = getopts::opt_maybe_str(matches, "linker"); - let linker_args = getopts::opt_strs(matches, "link-args").flat_map( |a| { + let addl_lib_search_paths = matches.opt_strs("L").map(|s| Path(*s)); + let linker = matches.opt_str("linker"); + let linker_args = matches.opt_strs("link-args").flat_map( |a| { a.split_iter(' ').map(|arg| arg.to_owned()).collect() }); - let cfg = parse_cfgspecs(getopts::opt_strs(matches, "cfg"), demitter); - let test = opt_present(matches, "test"); - let android_cross_path = getopts::opt_maybe_str( - matches, "android-cross-path"); + let cfg = parse_cfgspecs(matches.opt_strs("cfg"), demitter); + let test = matches.opt_present("test"); + let android_cross_path = matches.opt_str("android-cross-path"); - let custom_passes = match getopts::opt_maybe_str(matches, "passes") { + let custom_passes = match matches.opt_str("passes") { None => ~[], Some(s) => { s.split_iter(|c: char| c == ' ' || c == ',').map(|s| { @@ -739,7 +737,7 @@ pub fn build_session_options(binary: @str, }).collect() } }; - let llvm_args = match getopts::opt_maybe_str(matches, "llvm-args") { + let llvm_args = match matches.opt_str("llvm-args") { None => ~[], Some(s) => { s.split_iter(|c: char| c == ' ' || c == ',').map(|s| { @@ -1020,7 +1018,6 @@ mod test { use driver::driver::{build_session_options, optgroups}; use extra::getopts::groups::getopts; - use extra::getopts; use syntax::attr; use syntax::diagnostic; @@ -1030,7 +1027,7 @@ mod test { let matches = &match getopts([~"--test"], optgroups()) { Ok(m) => m, - Err(f) => fail!("test_switch_implies_cfg_test: %s", getopts::fail_str(f)) + Err(f) => fail!("test_switch_implies_cfg_test: %s", f.to_err_msg()) }; let sessopts = build_session_options( @"rustc", matches, diagnostic::emit); @@ -1047,7 +1044,7 @@ mod test { &match getopts([~"--test", ~"--cfg=test"], optgroups()) { Ok(m) => m, Err(f) => { - fail!("test_switch_implies_cfg_test_unless_cfg_test: %s", getopts::fail_str(f)); + fail!("test_switch_implies_cfg_test_unless_cfg_test: %s", f.to_err_msg()); } }; let sessopts = build_session_options( diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index 9c788065133..49176c7bc17 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -293,50 +293,6 @@ fn mk_std(cx: &TestCtxt) -> ast::view_item { } } -#[cfg(stage0)] -fn mk_test_module(cx: &TestCtxt) -> @ast::item { - - // Link to extra - let view_items = ~[mk_std(cx)]; - - // A constant vector of test descriptors. - let tests = mk_tests(cx); - - // The synthesized main function which will call the console test runner - // with our list of tests - let ext_cx = cx.ext_cx; - let mainfn = (quote_item!( - pub fn main() { - #[main]; - extra::test::test_main_static(::std::os::args(), TESTS); - } - )).unwrap(); - - let testmod = ast::_mod { - view_items: view_items, - items: ~[mainfn, tests], - }; - let item_ = ast::item_mod(testmod); - - // This attribute tells resolve to let us call unexported functions - let resolve_unexported_attr = - attr::mk_attr(attr::mk_word_item(@"!resolve_unexported")); - - let item = ast::item { - ident: cx.sess.ident_of("__test"), - attrs: ~[resolve_unexported_attr], - id: ast::DUMMY_NODE_ID, - node: item_, - vis: ast::public, - span: dummy_sp(), - }; - - debug!("Synthetic test module:\n%s\n", - pprust::item_to_str(@item.clone(), cx.sess.intr())); - - return @item; -} -#[cfg(not(stage0))] fn mk_test_module(cx: &TestCtxt) -> @ast::item { // Link to extra @@ -407,21 +363,6 @@ fn path_node_global(ids: ~[ast::Ident]) -> ast::Path { } } -#[cfg(stage0)] -fn mk_tests(cx: &TestCtxt) -> @ast::item { - - let ext_cx = cx.ext_cx; - - // The vector of test_descs for this crate - let test_descs = mk_test_descs(cx); - - (quote_item!( - pub static TESTS : &'static [self::extra::test::TestDescAndFn] = - $test_descs - ; - )).unwrap() -} -#[cfg(not(stage0))] fn mk_tests(cx: &TestCtxt) -> @ast::item { // The vector of test_descs for this crate let test_descs = mk_test_descs(cx); @@ -461,63 +402,6 @@ fn mk_test_descs(cx: &TestCtxt) -> @ast::Expr { } } -#[cfg(stage0)] -fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> @ast::Expr { - let span = test.span; - let path = test.path.clone(); - - let ext_cx = cx.ext_cx; - - debug!("encoding %s", ast_util::path_name_i(path)); - - let name_lit: ast::lit = - nospan(ast::lit_str(ast_util::path_name_i(path).to_managed())); - - let name_expr = @ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprLit(@name_lit), - span: span - }; - - let fn_path = path_node_global(path); - - let fn_expr = @ast::Expr { - id: ast::DUMMY_NODE_ID, - node: ast::ExprPath(fn_path), - span: span, - }; - - let t_expr = if test.bench { - quote_expr!( self::extra::test::StaticBenchFn($fn_expr) ) - } else { - quote_expr!( self::extra::test::StaticTestFn($fn_expr) ) - }; - - let ignore_expr = if test.ignore { - quote_expr!( true ) - } else { - quote_expr!( false ) - }; - - let fail_expr = if test.should_fail { - quote_expr!( true ) - } else { - quote_expr!( false ) - }; - - let e = quote_expr!( - self::extra::test::TestDescAndFn { - desc: self::extra::test::TestDesc { - name: self::extra::test::StaticTestName($name_expr), - ignore: $ignore_expr, - should_fail: $fail_expr - }, - testfn: $t_expr, - } - ); - e -} -#[cfg(not(stage0))] fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> @ast::Expr { let span = test.span; let path = test.path.clone(); diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 2e5e87f225a..bba3ca9f212 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -19,7 +19,6 @@ use syntax::visit; use syntax::visit::Visitor; use syntax::ast::*; -use std::float; use std::hashmap::{HashMap, HashSet}; // @@ -476,9 +475,9 @@ pub fn lit_to_const(lit: &lit) -> const_val { lit_int(n, _) => const_int(n), lit_uint(n, _) => const_uint(n), lit_int_unsuffixed(n) => const_int(n), - lit_float(n, _) => const_float(float::from_str(n).unwrap() as f64), + lit_float(n, _) => const_float(from_str::<float>(n).unwrap() as f64), lit_float_unsuffixed(n) => - const_float(float::from_str(n).unwrap() as f64), + const_float(from_str::<float>(n).unwrap() as f64), lit_nil => const_int(0i64), lit_bool(b) => const_bool(b) } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 6f70d2e601d..f7fcd8f908d 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -92,7 +92,7 @@ use syntax::visit::Visitor; pub use middle::trans::context::task_llcx; -static task_local_insn_key: local_data::Key<@~[&'static str]> = &local_data::Key; +local_data_key!(task_local_insn_key: @~[&'static str]) pub fn with_insn_ctxt(blk: &fn(&[&'static str])) { let opt = local_data::get(task_local_insn_key, |k| k.map_move(|k| *k)); @@ -2388,38 +2388,12 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, let et = ccx.sess.entry_type.unwrap(); match et { session::EntryMain => { - let llfn = create_main(ccx, main_llfn); - create_entry_fn(ccx, llfn, true); + create_entry_fn(ccx, main_llfn, true); } session::EntryStart => create_entry_fn(ccx, main_llfn, false), session::EntryNone => {} // Do nothing. } - fn create_main(ccx: @mut CrateContext, main_llfn: ValueRef) -> ValueRef { - let nt = ty::mk_nil(); - let llfty = type_of_rust_fn(ccx, [], nt); - let llfdecl = decl_fn(ccx.llmod, "_rust_main", - lib::llvm::CCallConv, llfty); - - let fcx = new_fn_ctxt(ccx, ~[], llfdecl, nt, None); - - // the args vector built in create_entry_fn will need - // be updated if this assertion starts to fail. - assert!(!fcx.caller_expects_out_pointer); - - let bcx = fcx.entry_bcx.unwrap(); - // Call main. - let llenvarg = unsafe { - let env_arg = fcx.env_arg_pos(); - llvm::LLVMGetParam(llfdecl, env_arg as c_uint) - }; - let args = ~[llenvarg]; - Call(bcx, main_llfn, args, []); - - finish_fn(fcx, bcx); - return llfdecl; - } - fn create_entry_fn(ccx: @mut CrateContext, rust_main: ValueRef, use_start_lang_item: bool) { @@ -2660,7 +2634,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { foreign::register_foreign_item_fn(ccx, abis, &path, ni) } ast::foreign_item_static(*) => { - let ident = token::ident_to_str(&ni.ident); + let ident = foreign::link_name(ccx, ni); let g = do ident.with_c_str |buf| { unsafe { let ty = type_of(ccx, ty); diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 7e1c2c369b1..e342bcaf4fa 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -287,7 +287,7 @@ impl Drop for CrateContext { } } -static task_local_llcx_key: local_data::Key<@ContextRef> = &local_data::Key; +local_data_key!(task_local_llcx_key: @ContextRef) pub fn task_llcx() -> ContextRef { let opt = local_data::get(task_local_llcx_key, |k| k.map_move(|k| *k)); diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index bca2ceb2f84..8c3e198e5d6 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -40,7 +40,7 @@ use std::result; use std::str; use std::task; use std::vec; -use extra::getopts::{groups, opt_present}; +use extra::getopts::groups; use extra::getopts; use syntax::codemap; use syntax::diagnostic; @@ -134,7 +134,7 @@ pub fn version(argv0: &str) { pub fn usage(argv0: &str) { let message = fmt!("Usage: %s [OPTIONS] INPUT", argv0); - printfln!("%s\ + printfln!("%s\n\ Additional help: -W help Print 'lint' options and default settings -Z help Print internal options for debugging rustc\n", @@ -204,39 +204,39 @@ pub fn run_compiler(args: &[~str], demitter: diagnostic::Emitter) { &match getopts::groups::getopts(args, optgroups()) { Ok(m) => m, Err(f) => { - early_error(demitter, getopts::fail_str(f)); + early_error(demitter, f.to_err_msg()); } }; - if opt_present(matches, "h") || opt_present(matches, "help") { + if matches.opt_present("h") || matches.opt_present("help") { usage(binary); return; } // Display the available lint options if "-W help" or only "-W" is given. - let lint_flags = vec::append(getopts::opt_strs(matches, "W"), - getopts::opt_strs(matches, "warn")); + let lint_flags = vec::append(matches.opt_strs("W"), + matches.opt_strs("warn")); let show_lint_options = lint_flags.iter().any(|x| x == &~"help") || - (opt_present(matches, "W") && lint_flags.is_empty()); + (matches.opt_present("W") && lint_flags.is_empty()); if show_lint_options { describe_warnings(); return; } - let r = getopts::opt_strs(matches, "Z"); + let r = matches.opt_strs("Z"); if r.iter().any(|x| x == &~"help") { describe_debug_flags(); return; } - if getopts::opt_maybe_str(matches, "passes") == Some(~"list") { + if matches.opt_str("passes") == Some(~"list") { unsafe { lib::llvm::llvm::LLVMRustPrintPasses(); } return; } - if opt_present(matches, "v") || opt_present(matches, "version") { + if matches.opt_present("v") || matches.opt_present("version") { version(binary); return; } @@ -256,10 +256,10 @@ pub fn run_compiler(args: &[~str], demitter: diagnostic::Emitter) { let sopts = build_session_options(binary, matches, demitter); let sess = build_session(sopts, demitter); - let odir = getopts::opt_maybe_str(matches, "out-dir").map_move(|o| Path(o)); - let ofile = getopts::opt_maybe_str(matches, "o").map_move(|o| Path(o)); + let odir = matches.opt_str("out-dir").map_move(|o| Path(o)); + let ofile = matches.opt_str("o").map_move(|o| Path(o)); let cfg = build_configuration(sess); - let pretty = do getopts::opt_default(matches, "pretty", "normal").map_move |a| { + let pretty = do matches.opt_default("pretty", "normal").map_move |a| { parse_pretty(sess, a) }; match pretty { @@ -269,7 +269,7 @@ pub fn run_compiler(args: &[~str], demitter: diagnostic::Emitter) { } None::<PpMode> => {/* continue */ } } - let ls = opt_present(matches, "ls"); + let ls = matches.opt_present("ls"); if ls { match input { file_input(ref ifile) => { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index ff6401456b6..71ece178807 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -125,7 +125,7 @@ pub fn parse_config_( } } Err(f) => { - Err(getopts::fail_str(f)) + Err(f.to_err_msg()) } } } @@ -139,7 +139,7 @@ fn config_from_opts( let config = default_config(input_crate); let result = result::Ok(config); let result = do result.and_then |config| { - let output_dir = getopts::opt_maybe_str(matches, opt_output_dir()); + let output_dir = matches.opt_str(opt_output_dir()); let output_dir = output_dir.map_move(|s| Path(s)); result::Ok(Config { output_dir: output_dir.unwrap_or(config.output_dir.clone()), @@ -147,7 +147,7 @@ fn config_from_opts( }) }; let result = do result.and_then |config| { - let output_format = getopts::opt_maybe_str(matches, opt_output_format()); + let output_format = matches.opt_str(opt_output_format()); do output_format.map_move_default(result::Ok(config.clone())) |output_format| { do parse_output_format(output_format).and_then |output_format| { result::Ok(Config { @@ -159,7 +159,7 @@ fn config_from_opts( }; let result = do result.and_then |config| { let output_style = - getopts::opt_maybe_str(matches, opt_output_style()); + matches.opt_str(opt_output_style()); do output_style.map_move_default(result::Ok(config.clone())) |output_style| { do parse_output_style(output_style).and_then |output_style| { result::Ok(Config { @@ -171,7 +171,7 @@ fn config_from_opts( }; let process_output = Cell::new(process_output); let result = do result.and_then |config| { - let pandoc_cmd = getopts::opt_maybe_str(matches, opt_pandoc_cmd()); + let pandoc_cmd = matches.opt_str(opt_pandoc_cmd()); let pandoc_cmd = maybe_find_pandoc( &config, pandoc_cmd, process_output.take()); do pandoc_cmd.and_then |pandoc_cmd| { diff --git a/src/librusti/program.rs b/src/librusti/program.rs index 9208191e364..4deaa458f19 100644 --- a/src/librusti/program.rs +++ b/src/librusti/program.rs @@ -60,7 +60,7 @@ struct LocalVariable { } type LocalCache = @mut HashMap<~str, @~[u8]>; -static tls_key: local_data::Key<LocalCache> = &local_data::Key; +local_data_key!(tls_key: LocalCache) impl Program { pub fn new() -> Program { diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 727bbcb30b4..e1092458ffa 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -12,6 +12,7 @@ use context::*; use crate::*; use package_id::*; use package_source::*; +use target::*; use version::Version; use workcache_support::*; @@ -63,56 +64,40 @@ pub fn new_workcache_context(p: &Path) -> workcache::Context { pub fn build_lib(sysroot: Path, root: Path, name: ~str, version: Version, lib: Path) { let cx = default_context(sysroot); - let subroot = root.clone(); - let subversion = version.clone(); - let sublib = lib.clone(); - do cx.workcache_context.with_prep(name) |prep| { - let pkg_src = PkgSrc { - workspace: subroot.clone(), - start_dir: subroot.push("src").push(name), - id: PkgId{ version: subversion.clone(), ..PkgId::new(name)}, - libs: ~[mk_crate(sublib.clone())], + let pkg_src = PkgSrc { + workspace: root.clone(), + start_dir: root.push("src").push(name), + id: PkgId{ version: version, ..PkgId::new(name)}, + // n.b. This assumes the package only has one crate + libs: ~[mk_crate(lib)], mains: ~[], tests: ~[], benchs: ~[] }; - pkg_src.declare_inputs(prep); - let subcx = cx.clone(); - let subsrc = pkg_src.clone(); - do prep.exec |exec| { - subsrc.build(exec, &subcx.clone(), ~[]); - } - }; + pkg_src.build(&cx, ~[]); } pub fn build_exe(sysroot: Path, root: Path, name: ~str, version: Version, main: Path) { let cx = default_context(sysroot); - let subroot = root.clone(); - let submain = main.clone(); - do cx.workcache_context.with_prep(name) |prep| { - let pkg_src = PkgSrc { - workspace: subroot.clone(), - start_dir: subroot.push("src").push(name), - id: PkgId{ version: version.clone(), ..PkgId::new(name)}, - libs: ~[], - mains: ~[mk_crate(submain.clone())], - tests: ~[], - benchs: ~[] - }; - pkg_src.declare_inputs(prep); - let subsrc = pkg_src.clone(); - let subcx = cx.clone(); - do prep.exec |exec| { - subsrc.clone().build(exec, &subcx.clone(), ~[]); - } - } + let pkg_src = PkgSrc { + workspace: root.clone(), + start_dir: root.push("src").push(name), + id: PkgId{ version: version, ..PkgId::new(name)}, + libs: ~[], + // n.b. This assumes the package only has one crate + mains: ~[mk_crate(main)], + tests: ~[], + benchs: ~[] + }; + + pkg_src.build(&cx, ~[]); } pub fn install_pkg(sysroot: Path, workspace: Path, name: ~str, version: Version) { let cx = default_context(sysroot); let pkgid = PkgId{ version: version, ..PkgId::new(name)}; - cx.install(PkgSrc::new(workspace, false, pkgid)); + cx.install(PkgSrc::new(workspace, false, pkgid), &Everything); } fn mk_crate(p: Path) -> Crate { diff --git a/src/test/run-pass/cond-macro.rs b/src/librustpkg/exit_codes.rs index 61a51b67261..484f6bdcaec 100644 --- a/src/test/run-pass/cond-macro.rs +++ b/src/librustpkg/exit_codes.rs @@ -8,16 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn clamp<T:Ord + Signed>(x: T, mn: T, mx: T) -> T { - cond!( - (x > mx) { mx } - (x < mn) { mn } - _ { x } - ) -} - -fn main() { - assert_eq!(clamp(1, 2, 4), 2); - assert_eq!(clamp(8, 2, 4), 4); - assert_eq!(clamp(3, 2, 4), 3); -} +pub static copy_failed_code: int = 65; diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index bc2fcdd7fe9..52b986cb6e7 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -108,6 +108,12 @@ impl PkgId { } } + // This is the workcache function name for the *installed* + // binaries for this package (as opposed to the built ones, + // which are per-crate). + pub fn install_tag(&self) -> ~str { + fmt!("install(%s)", self.to_str()) + } } struct Prefixes { diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index b5ded6f3faf..4bf647b011d 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -22,6 +22,7 @@ use path_util::{find_dir_using_rust_path_hack, default_workspace, make_dir_rwx_r use util::compile_crate; use workspace::is_workspace; use workcache_support; +use workcache_support::crate_tag; use extra::workcache; // An enumeration of the unpacked source of a package workspace. @@ -231,7 +232,7 @@ impl PkgSrc { p.filestem().map_default(false, |p| { p == &self.id.short_name.as_slice() }) } - fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { + pub fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { assert!(p.components.len() > prefix); let mut sub = Path(""); for c in p.components.slice(prefix, p.components.len()).iter() { @@ -286,7 +287,6 @@ impl PkgSrc { fn build_crates(&self, ctx: &BuildContext, - exec: &mut workcache::Exec, destination_dir: &Path, crates: &[Crate], cfgs: &[~str], @@ -297,25 +297,40 @@ impl PkgSrc { let path_str = path.to_str(); let cfgs = crate.cfgs + cfgs; - let result = - // compile_crate should return the path of the output artifact - compile_crate(ctx, - exec, - &self.id, - &path, - destination_dir, - crate.flags, - cfgs, - false, - what).to_str(); - debug!("Result of compiling %s was %s", path_str, result); + do ctx.workcache_context.with_prep(crate_tag(&path)) |prep| { + debug!("Building crate %s, declaring it as an input", path.to_str()); + prep.declare_input("file", path.to_str(), + workcache_support::digest_file_with_date(&path)); + let subpath = path.clone(); + let subcfgs = cfgs.clone(); + let subpath_str = path_str.clone(); + let subcx = ctx.clone(); + let id = self.id.clone(); + let sub_dir = destination_dir.clone(); + let sub_flags = crate.flags.clone(); + do prep.exec |exec| { + let result = compile_crate(&subcx, + exec, + &id, + &subpath, + &sub_dir, + sub_flags, + subcfgs, + false, + what).to_str(); + debug!("Result of compiling %s was %s", subpath_str, result); + result + } + }; } } /// Declare all the crate files in the package source as inputs + /// (to the package) pub fn declare_inputs(&self, prep: &mut workcache::Prep) { let to_do = ~[self.libs.clone(), self.mains.clone(), self.tests.clone(), self.benchs.clone()]; + debug!("In declare inputs, self = %s", self.to_str()); for cs in to_do.iter() { for c in cs.iter() { let path = self.start_dir.push_rel(&c.file).normalize(); @@ -330,7 +345,6 @@ impl PkgSrc { // It would be better if build returned a Path, but then Path would have to derive // Encodable. pub fn build(&self, - exec: &mut workcache::Exec, build_context: &BuildContext, cfgs: ~[~str]) -> ~str { use conditions::not_a_workspace::cond; @@ -360,13 +374,23 @@ impl PkgSrc { let benchs = self.benchs.clone(); debug!("Building libs in %s, destination = %s", destination_workspace.to_str(), destination_workspace.to_str()); - self.build_crates(build_context, exec, &destination_workspace, libs, cfgs, Lib); + self.build_crates(build_context, &destination_workspace, libs, cfgs, Lib); debug!("Building mains"); - self.build_crates(build_context, exec, &destination_workspace, mains, cfgs, Main); + self.build_crates(build_context, &destination_workspace, mains, cfgs, Main); debug!("Building tests"); - self.build_crates(build_context, exec, &destination_workspace, tests, cfgs, Test); + self.build_crates(build_context, &destination_workspace, tests, cfgs, Test); debug!("Building benches"); - self.build_crates(build_context, exec, &destination_workspace, benchs, cfgs, Bench); + self.build_crates(build_context, &destination_workspace, benchs, cfgs, Bench); destination_workspace.to_str() } + + /// Debugging + pub fn dump_crates(&self) { + let crate_sets = [&self.libs, &self.mains, &self.tests, &self.benchs]; + for crate_set in crate_sets.iter() { + for c in crate_set.iter() { + debug!("Built crate: %s", c.file.to_str()) + } + } + } } diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index 2a0cf5fea34..0187a8d189c 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -22,11 +22,10 @@ extern mod extra; extern mod rustc; extern mod syntax; -use std::{io, os, result, run, str}; +use std::{io, os, result, run, str, task}; pub use std::path::Path; use extra::workcache; -use extra::arc::RWArc; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; use rustc::metadata::filesearch::rust_path; @@ -45,12 +44,16 @@ use context::{Context, BuildContext, LLVMAssemble, LLVMCompileBitcode}; use package_id::PkgId; use package_source::PkgSrc; -use workcache_support::{discover_outputs, digest_only_date}; +use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench}; +// use workcache_support::{discover_outputs, digest_only_date}; +use workcache_support::digest_only_date; +use exit_codes::copy_failed_code; pub mod api; mod conditions; mod context; mod crate; +mod exit_codes; mod installed_packages; mod messages; mod package_id; @@ -172,19 +175,18 @@ impl<'self> PkgScript<'self> { pub trait CtxMethods { fn run(&self, cmd: &str, args: ~[~str]); fn do_cmd(&self, _cmd: &str, _pkgname: &str); - fn build_from_src(&self, pkg_src: PkgSrc); /// Returns the destination workspace - fn build(&self, exec: &mut workcache::Exec, pkg_src: PkgSrc) -> Path; + fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild) -> Path; fn clean(&self, workspace: &Path, id: &PkgId); fn info(&self); /// Returns a pair. First component is a list of installed paths, /// second is a list of declared and discovered inputs - fn install(&self, src: PkgSrc) -> (~[Path], ~[(~str, ~str)]); + fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]); /// Returns a list of installed files fn install_no_build(&self, source_workspace: &Path, target_workspace: &Path, - id: &PkgId) -> ~[Path]; + id: &PkgId) -> ~[~str]; fn prefer(&self, _id: &str, _vers: Option<~str>); fn test(&self); fn uninstall(&self, _id: &str, _vers: Option<~str>); @@ -193,20 +195,6 @@ pub trait CtxMethods { } impl CtxMethods for BuildContext { - fn build_from_src(&self, pkg_src: PkgSrc) { - let tag = pkg_src.id.to_str(); - debug!("package source = %s", pkg_src.to_str()); - do self.workcache_context.with_prep(tag) |prep| { - let subsrc = pkg_src.clone(); - let subself = self.clone(); - declare_package_script_dependency(prep, &subsrc); - pkg_src.declare_inputs(prep); - do prep.exec |exec| { - subself.build(exec, subsrc.clone()); - } - } - } - fn run(&self, cmd: &str, args: ~[~str]) { match cmd { "build" => { @@ -215,11 +203,13 @@ impl CtxMethods for BuildContext { None if self.context.use_rust_path_hack => { let cwd = os::getcwd(); let pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]); - self.build_from_src(PkgSrc::new(cwd, true, pkgid)); + let mut pkg_src = PkgSrc::new(cwd, true, pkgid); + self.build(&mut pkg_src, &Everything); } None => { usage::build(); return; } Some((ws, pkgid)) => { - self.build_from_src(PkgSrc::new(ws, false, pkgid)); + let mut pkg_src = PkgSrc::new(ws, false, pkgid); + self.build(&mut pkg_src, &Everything); } } } @@ -230,8 +220,8 @@ impl CtxMethods for BuildContext { do each_pkg_parent_workspace(&self.context, &pkgid) |workspace| { debug!("found pkg %s in workspace %s, trying to build", pkgid.to_str(), workspace.to_str()); - let pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); - self.build_from_src(pkg_src); + let mut pkg_src = PkgSrc::new(workspace.clone(), false, pkgid.clone()); + self.build(&mut pkg_src, &Everything); true }; } @@ -271,12 +261,12 @@ impl CtxMethods for BuildContext { let cwd = os::getcwd(); let inferred_pkgid = PkgId::new(cwd.components[cwd.components.len() - 1]); - self.install(PkgSrc::new(cwd, true, inferred_pkgid)); + self.install(PkgSrc::new(cwd, true, inferred_pkgid), &Everything); } None => { usage::install(); return; } Some((ws, pkgid)) => { let pkg_src = PkgSrc::new(ws, false, pkgid); - self.install(pkg_src); + self.install(pkg_src, &Everything); } } } @@ -291,14 +281,14 @@ impl CtxMethods for BuildContext { let rp = rust_path(); assert!(!rp.is_empty()); let src = PkgSrc::new(rp[0].clone(), false, pkgid.clone()); - self.install(src); + self.install(src, &Everything); } else { for workspace in workspaces.iter() { let src = PkgSrc::new(workspace.clone(), self.context.use_rust_path_hack, pkgid.clone()); - self.install(src); + self.install(src, &Everything); }; } } @@ -366,7 +356,9 @@ impl CtxMethods for BuildContext { /// Returns the destination workspace /// In the case of a custom build, we don't know, so we just return the source workspace - fn build(&self, exec: &mut workcache::Exec, mut pkg_src: PkgSrc) -> Path { + /// what_to_build says: "Just build the lib.rs file in one subdirectory, + /// don't walk anything recursively." Or else, everything. + fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) -> Path { let workspace = pkg_src.workspace.clone(); let pkgid = pkg_src.id.clone(); @@ -384,7 +376,7 @@ impl CtxMethods for BuildContext { let default_ws = default_workspace(); debug!("Calling build recursively with %? and %?", default_ws.to_str(), pkgid.to_str()); - return self.build(exec, PkgSrc::new(default_ws, false, pkgid.clone())); + return self.build(&mut PkgSrc::new(default_ws, false, pkgid.clone()), what_to_build); } // Is there custom build logic? If so, use it @@ -395,12 +387,21 @@ impl CtxMethods for BuildContext { let cfgs = match pkg_src.package_script_option() { Some(package_script_path) => { let sysroot = self.sysroot_to_use(); - let (cfgs, hook_result) = { - let pscript = PkgScript::parse(@sysroot.clone(), - package_script_path.clone(), - &workspace.clone(), - &pkgid); - pscript.run_custom(exec, &sysroot) + let (cfgs, hook_result) = + do self.workcache_context.with_prep(package_script_path.to_str()) |prep| { + let sub_sysroot = sysroot.clone(); + let package_script_path_clone = package_script_path.clone(); + let sub_ws = workspace.clone(); + let sub_id = pkgid.clone(); + declare_package_script_dependency(prep, &*pkg_src); + do prep.exec |exec| { + let pscript = PkgScript::parse(@sub_sysroot.clone(), + package_script_path_clone.clone(), + &sub_ws, + &sub_id); + + pscript.run_custom(exec, &sub_sysroot) + } }; debug!("Command return code = %?", hook_result); if hook_result != 0 { @@ -419,10 +420,31 @@ impl CtxMethods for BuildContext { // If there was a package script, it should have finished // the build already. Otherwise... if !custom { - // Find crates inside the workspace - pkg_src.find_crates(); + match what_to_build { + // Find crates inside the workspace + &Everything => pkg_src.find_crates(), + // Don't infer any crates -- just build the one that was requested + &JustOne(ref p) => { + // We expect that p is relative to the package source's start directory, + // so check that assumption + debug!("JustOne: p = %s", p.to_str()); + assert!(os::path_exists(&pkg_src.start_dir.push_rel(p))); + if is_lib(p) { + PkgSrc::push_crate(&mut pkg_src.libs, 0, p); + } else if is_main(p) { + PkgSrc::push_crate(&mut pkg_src.mains, 0, p); + } else if is_test(p) { + PkgSrc::push_crate(&mut pkg_src.tests, 0, p); + } else if is_bench(p) { + PkgSrc::push_crate(&mut pkg_src.benchs, 0, p); + } else { + warn(fmt!("Not building any crates for dependency %s", p.to_str())); + return workspace.clone(); + } + } + } // Build it! - let rs_path = pkg_src.build(exec, self, cfgs); + let rs_path = pkg_src.build(self, cfgs); Path(rs_path) } else { @@ -452,56 +474,54 @@ impl CtxMethods for BuildContext { fail!("info not yet implemented"); } - fn install(&self, pkg_src: PkgSrc) -> (~[Path], ~[(~str, ~str)]) { - - let id = &pkg_src.id; - - let installed_files = RWArc::new(~[]); - let inputs = RWArc::new(~[]); - // FIXME #7402: Use RUST_PATH to determine target dir - self.workcache_context.with_prep(id.to_str(), |p| pkg_src.declare_inputs(p)); - do self.workcache_context.with_prep(id.to_str()) |prep| { - let sub_inputs = inputs.clone(); - let sub_files = installed_files.clone(); - let subsrc = pkg_src.clone(); - let subself = self.clone(); - let id_str = id.to_str(); - let sub_id = id.clone(); - sub_inputs.write(|r| *r = prep.lookup_declared_inputs().map(|v| - { (~"file", (*v).clone()) })); - do prep.exec |exec| { - let destination_workspace = subself.build(exec, subsrc.clone()).to_str(); - // See #7402: This still isn't quite right yet; we want to - // install to the first workspace in the RUST_PATH if there's - // a non-default RUST_PATH. This code installs to the same - // workspace the package was built in. - let actual_workspace = if path_util::user_set_rust_path() { - default_workspace() - } - else { - Path(destination_workspace) - }; - debug!("install: destination workspace = %s, id = %s, installing to %s", - destination_workspace, id_str, actual_workspace.to_str()); - let result = subself.install_no_build(&Path(destination_workspace), - &actual_workspace, - &sub_id); - debug!("install: id = %s, about to call discover_outputs, %?", - id_str, result.to_str()); - - discover_outputs(exec, result.clone()); - sub_files.write(|r| { *r = result.clone(); }); - sub_inputs.write(|r| { *r = *r + exec.lookup_discovered_inputs() }); - note(fmt!("Installed package %s to %s", id_str, actual_workspace.to_str())); + fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) { + + let id = pkg_src.id.clone(); + + let mut installed_files = ~[]; + let inputs = ~[]; + + // workcache only knows about *crates*. Building a package + // just means inferring all the crates in it, then building each one. + let destination_workspace = self.build(&mut pkg_src, what).to_str(); + + let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(), + pkg_src.tests.clone(), pkg_src.benchs.clone()]; + debug!("In declare inputs for %s", id.to_str()); + for cs in to_do.iter() { + for c in cs.iter() { + let path = pkg_src.start_dir.push_rel(&c.file).normalize(); + debug!("Recording input: %s", path.to_str()); + installed_files.push(path); } + } + // See #7402: This still isn't quite right yet; we want to + // install to the first workspace in the RUST_PATH if there's + // a non-default RUST_PATH. This code installs to the same + // workspace the package was built in. + let actual_workspace = if path_util::user_set_rust_path() { + default_workspace() + } + else { + Path(destination_workspace) }; - (installed_files.unwrap(), inputs.unwrap()) + debug!("install: destination workspace = %s, id = %s, installing to %s", + destination_workspace, id.to_str(), actual_workspace.to_str()); + let result = self.install_no_build(&Path(destination_workspace), + &actual_workspace, + &id).map(|s| Path(*s)); + debug!("install: id = %s, about to call discover_outputs, %?", + id.to_str(), result.to_str()); + installed_files = installed_files + result; + note(fmt!("Installed package %s to %s", id.to_str(), actual_workspace.to_str())); + (installed_files, inputs) } + // again, working around lack of Encodable for Path fn install_no_build(&self, source_workspace: &Path, target_workspace: &Path, - id: &PkgId) -> ~[Path] { + id: &PkgId) -> ~[~str] { use conditions::copy_failed::cond; // Now copy stuff into the install dirs @@ -511,32 +531,59 @@ impl CtxMethods for BuildContext { let target_lib = maybe_library.map(|_p| target_library_in_workspace(id, target_workspace)); debug!("target_exec = %s target_lib = %? \ - maybe_executable = %? maybe_library = %?", + maybe_executable = %? maybe_library = %?", target_exec.to_str(), target_lib, maybe_executable, maybe_library); - let mut outputs = ~[]; - - for exec in maybe_executable.iter() { - debug!("Copying: %s -> %s", exec.to_str(), target_exec.to_str()); - if !(os::mkdir_recursive(&target_exec.dir_path(), U_RWX) && - os::copy_file(exec, &target_exec)) { - cond.raise(((*exec).clone(), target_exec.clone())); + do self.workcache_context.with_prep(id.install_tag()) |prep| { + for ee in maybe_executable.iter() { + prep.declare_input("binary", + ee.to_str(), + workcache_support::digest_only_date(ee)); } - outputs.push(target_exec.clone()); - } - for lib in maybe_library.iter() { - let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \ - didn't install it!", lib.to_str())); - let target_lib = target_lib.pop().push(lib.filename().expect("weird target lib")); - debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str()); - if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && - os::copy_file(lib, &target_lib)) { - cond.raise(((*lib).clone(), target_lib.clone())); + for ll in maybe_library.iter() { + prep.declare_input("binary", + ll.to_str(), + workcache_support::digest_only_date(ll)); + } + let subex = maybe_executable.clone(); + let sublib = maybe_library.clone(); + let sub_target_ex = target_exec.clone(); + let sub_target_lib = target_lib.clone(); + + do prep.exec |exe_thing| { + let mut outputs = ~[]; + + for exec in subex.iter() { + debug!("Copying: %s -> %s", exec.to_str(), sub_target_ex.to_str()); + if !(os::mkdir_recursive(&sub_target_ex.dir_path(), U_RWX) && + os::copy_file(exec, &sub_target_ex)) { + cond.raise(((*exec).clone(), sub_target_ex.clone())); + } + exe_thing.discover_output("binary", + sub_target_ex.to_str(), + workcache_support::digest_only_date(&sub_target_ex)); + outputs.push(sub_target_ex.to_str()); + } + for lib in sublib.iter() { + let target_lib = sub_target_lib + .clone().expect(fmt!("I built %s but apparently \ + didn't install it!", lib.to_str())); + let target_lib = target_lib + .pop().push(lib.filename().expect("weird target lib")); + debug!("Copying: %s -> %s", lib.to_str(), sub_target_lib.to_str()); + if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && + os::copy_file(lib, &target_lib)) { + cond.raise(((*lib).clone(), target_lib.clone())); + } + exe_thing.discover_output("binary", + target_lib.to_str(), + workcache_support::digest_only_date(&target_lib)); + outputs.push(target_lib.to_str()); + } + outputs } - outputs.push(target_lib.clone()); } - outputs } fn prefer(&self, _id: &str, _vers: Option<~str>) { @@ -594,53 +641,53 @@ pub fn main_args(args: &[~str]) { let matches = &match getopts::getopts(args, opts) { result::Ok(m) => m, result::Err(f) => { - error(fmt!("%s", getopts::fail_str(f))); + error(fmt!("%s", f.to_err_msg())); return; } }; - let mut help = getopts::opt_present(matches, "h") || - getopts::opt_present(matches, "help"); - let no_link = getopts::opt_present(matches, "no-link"); - let no_trans = getopts::opt_present(matches, "no-trans"); - let supplied_sysroot = getopts::opt_val(matches, "sysroot"); - let generate_asm = getopts::opt_present(matches, "S") || - getopts::opt_present(matches, "assembly"); - let parse_only = getopts::opt_present(matches, "parse-only"); - let pretty = getopts::opt_present(matches, "pretty"); - let emit_llvm = getopts::opt_present(matches, "emit-llvm"); - - if getopts::opt_present(matches, "v") || - getopts::opt_present(matches, "version") { + let mut help = matches.opt_present("h") || + matches.opt_present("help"); + let no_link = matches.opt_present("no-link"); + let no_trans = matches.opt_present("no-trans"); + let supplied_sysroot = matches.opt_val("sysroot"); + let generate_asm = matches.opt_present("S") || + matches.opt_present("assembly"); + let parse_only = matches.opt_present("parse-only"); + let pretty = matches.opt_present("pretty"); + let emit_llvm = matches.opt_present("emit-llvm"); + + if matches.opt_present("v") || + matches.opt_present("version") { rustc::version(args[0]); return; } - let use_rust_path_hack = getopts::opt_present(matches, "r") || - getopts::opt_present(matches, "rust-path-hack"); + let use_rust_path_hack = matches.opt_present("r") || + matches.opt_present("rust-path-hack"); - let linker = getopts::opt_maybe_str(matches, "linker"); - let link_args = getopts::opt_maybe_str(matches, "link-args"); - let cfgs = getopts::opt_strs(matches, "cfg") + getopts::opt_strs(matches, "c"); + let linker = matches.opt_str("linker"); + let link_args = matches.opt_str("link-args"); + let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c"); let mut user_supplied_opt_level = true; - let opt_level = match getopts::opt_maybe_str(matches, "opt-level") { + let opt_level = match matches.opt_str("opt-level") { Some(~"0") => session::No, Some(~"1") => session::Less, Some(~"2") => session::Default, Some(~"3") => session::Aggressive, - _ if getopts::opt_present(matches, "O") => session::Default, + _ if matches.opt_present("O") => session::Default, _ => { user_supplied_opt_level = false; session::No } }; - let save_temps = getopts::opt_present(matches, "save-temps"); - let target = getopts::opt_maybe_str(matches, "target"); - let target_cpu = getopts::opt_maybe_str(matches, "target-cpu"); + let save_temps = matches.opt_present("save-temps"); + let target = matches.opt_str("target"); + let target_cpu = matches.opt_str("target-cpu"); let experimental_features = { - let strs = getopts::opt_strs(matches, "Z"); - if getopts::opt_present(matches, "Z") { + let strs = matches.opt_strs("Z"); + if matches.opt_present("Z") { Some(strs) } else { @@ -726,15 +773,27 @@ pub fn main_args(args: &[~str]) { debug!("Using sysroot: %s", sroot.to_str()); debug!("Will store workcache in %s", default_workspace().to_str()); - BuildContext { - context: Context { - cfgs: cfgs, - rustc_flags: rustc_flags, - use_rust_path_hack: use_rust_path_hack, - sysroot: sroot, // Currently, only tests override this - }, - workcache_context: api::default_context(default_workspace()).workcache_context - }.run(*cmd, remaining_args) + + let rm_args = remaining_args.clone(); + let sub_cmd = cmd.clone(); + // Wrap the rest in task::try in case of a condition failure in a task + let result = do task::try { + BuildContext { + context: Context { + cfgs: cfgs.clone(), + rustc_flags: rustc_flags.clone(), + use_rust_path_hack: use_rust_path_hack, + sysroot: sroot.clone(), // Currently, only tests override this + }, + workcache_context: api::default_context(default_workspace()).workcache_context + }.run(sub_cmd, rm_args.clone()) + }; + // FIXME #9262: This is using the same error code for all errors, + // and at least one test case succeeds if rustpkg returns copy_failed_code, + // when actually, it might set the exit code for that even if a different + // unhandled condition got raised. + if result.is_err() { os::set_exit_status(copy_failed_code); } + } /** diff --git a/src/librustpkg/target.rs b/src/librustpkg/target.rs index 03c2f5a4fe4..9d3ad1f39a7 100644 --- a/src/librustpkg/target.rs +++ b/src/librustpkg/target.rs @@ -16,8 +16,45 @@ pub enum OutputType { Main, Lib, Bench, Test } #[deriving(Eq)] pub enum Target { - // In-place build + /// In-place build Build, - // Install to bin/ or lib/ dir + /// Install to bin/ or lib/ dir Install } + +#[deriving(Eq, Clone)] +pub enum WhatToBuild { + /// Build just one lib.rs file in `path`, which is relative to the active workspace's src/ dir + JustOne(Path), + /// Build everything + Everything +} + +pub fn is_lib(p: &Path) -> bool { + file_is(p, "lib") +} + +pub fn is_main(p: &Path) -> bool { + file_is(p, "main") +} + +pub fn is_test(p: &Path) -> bool { + file_is(p, "test") +} + +pub fn is_bench(p: &Path) -> bool { + file_is(p, "bench") +} + +fn file_is(p: &Path, stem: &str) -> bool { + match p.filestem() { + Some(s) if s == stem => true, + _ => false + } +} + +pub fn lib_name_of(p: &Path) -> Path { + p.push("lib.rs") +} + +pub static lib_crate_filename: &'static str = "lib.rs"; diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 952931fa97a..918cc366799 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -34,11 +34,7 @@ use rustc::driver::driver::{build_session, build_session_options, host_triple, o use syntax::diagnostic; use target::*; use package_source::PkgSrc; - -/// Returns the last-modified date as an Option -fn datestamp(p: &Path) -> Option<libc::time_t> { - p.stat().map(|stat| stat.st_mtime) -} +use util::datestamp; fn fake_ctxt(sysroot: Path, workspace: &Path) -> BuildContext { let context = workcache::Context::new( @@ -224,18 +220,26 @@ fn rustpkg_exec() -> Path { } fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput { - command_line_test_with_env(args, cwd, None).expect("Command line test failed") + match command_line_test_with_env(args, cwd, None) { + Success(r) => r, + _ => fail!("Command line test failed") + } } -fn command_line_test_partial(args: &[~str], cwd: &Path) -> Option<ProcessOutput> { +fn command_line_test_partial(args: &[~str], cwd: &Path) -> ProcessResult { command_line_test_with_env(args, cwd, None) } +enum ProcessResult { + Success(ProcessOutput), + Fail(int) // exit code +} + /// Runs `rustpkg` (based on the directory that this executable was /// invoked from) with the given arguments, in the given working directory. /// Returns the process's output. fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>) - -> Option<ProcessOutput> { + -> ProcessResult { let cmd = rustpkg_exec().to_str(); let env_str = match env { Some(ref pairs) => pairs.map(|&(ref k, ref v)| { fmt!("%s=%s", *k, *v) }).connect(","), @@ -266,10 +270,10 @@ to make sure the command succeeded debug!("Command %s %? failed with exit code %?; its output was {{{ %s }}}", cmd, args, output.status, str::from_utf8(output.output) + str::from_utf8(output.error)); - None + Fail(output.status) } else { - Some(output) + Success(output) } } @@ -410,8 +414,11 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] { fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] { let mut result = ~[]; - let p_output = command_line_test_with_env(args, - &os::getcwd(), Some(env)).expect("Command-line test failed"); + let p_output = match command_line_test_with_env(args, + &os::getcwd(), Some(env)) { + Fail(_) => fail!("Command-line test failed"), + Success(r) => r + }; let test_output = str::from_utf8(p_output.output); for s in test_output.split_iter('\n') { result.push(s.to_owned()); @@ -420,9 +427,9 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~ } // assumes short_name and path are one and the same -- I should fix -fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path { - debug!("lib_output_file_name: given %s and parent %s and short name %s", - workspace.to_str(), parent, short_name); +fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { + debug!("lib_output_file_name: given %s and short name %s", + workspace.to_str(), short_name); library_in_workspace(&Path(short_name), short_name, Build, @@ -450,19 +457,18 @@ fn touch_source_file(workspace: &Path, pkgid: &PkgId) { } /// Add a comment at the end -fn frob_source_file(workspace: &Path, pkgid: &PkgId) { +fn frob_source_file(workspace: &Path, pkgid: &PkgId, filename: &str) { use conditions::bad_path::cond; let pkg_src_dir = workspace.push_many([~"src", pkgid.to_str()]); - let contents = os::list_dir_path(&pkg_src_dir); let mut maybe_p = None; - for p in contents.iter() { - if p.filetype() == Some(".rs") { - maybe_p = Some(p); - break; - } + let maybe_file = pkg_src_dir.push(filename); + debug!("Trying to frob %s -- %s", pkg_src_dir.to_str(), filename); + if os::path_exists(&maybe_file) { + maybe_p = Some(maybe_file); } + debug!("Frobbed? %?", maybe_p); match maybe_p { - Some(p) => { + Some(ref p) => { let w = io::file_writer(p, &[io::Append]); match w { Err(s) => { let _ = cond.raise((p.clone(), fmt!("Bad path: %s", s))); } @@ -499,7 +505,7 @@ fn test_install_valid() { debug!("temp_workspace = %s", temp_workspace.to_str()); // should have test, bench, lib, and main let src = PkgSrc::new(temp_workspace.clone(), false, temp_pkg_id.clone()); - ctxt.install(src); + ctxt.install(src, &Everything); // Check that all files exist let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace); debug!("exec = %s", exec.to_str()); @@ -528,7 +534,7 @@ fn test_install_invalid() { // Uses task::try because of #9001 let result = do task::try { let pkg_src = PkgSrc::new(temp_workspace.clone(), false, pkgid.clone()); - ctxt.install(pkg_src); + ctxt.install(pkg_src, &Everything); }; // Not the best test -- doesn't test that we failed in the right way. // Best we can do for now. @@ -939,26 +945,28 @@ fn no_rebuilding() { } #[test] -#[ignore] fn no_rebuilding_dep() { let p_id = PkgId::new("foo"); let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); - let bar_date_1 = datestamp(&lib_output_file_name(&workspace, - ".rust", - "bar")); - let foo_date_1 = datestamp(&output_file_name(&workspace, ~"foo")); + let bar_lib = lib_output_file_name(&workspace, "bar"); + let bar_date_1 = datestamp(&bar_lib); + + frob_source_file(&workspace, &p_id, "main.rs"); + + // Now make `bar` read-only so that subsequent rebuilds of it will fail + assert!(chmod_read_only(&bar_lib)); + + match command_line_test_partial([~"build", ~"foo"], &workspace) { + Success(*) => (), // ok + Fail(status) if status == 65 => fail!("no_rebuilding_dep failed: it tried to rebuild bar"), + Fail(_) => fail!("no_rebuilding_dep failed for some other reason") + } - frob_source_file(&workspace, &p_id); - command_line_test([~"build", ~"foo"], &workspace); let bar_date_2 = datestamp(&lib_output_file_name(&workspace, - ".rust", - "bar")); - let foo_date_2 = datestamp(&output_file_name(&workspace, ~"foo")); + "bar")); assert_eq!(bar_date_1, bar_date_2); - assert!(foo_date_1 < foo_date_2); - assert!(foo_date_1 > bar_date_1); } #[test] @@ -967,7 +975,7 @@ fn do_rebuild_dep_dates_change() { let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); - let bar_lib_name = lib_output_file_name(&workspace, "build", "bar"); + let bar_lib_name = lib_output_file_name(&workspace, "bar"); let bar_date = datestamp(&bar_lib_name); debug!("Datestamp on %s is %?", bar_lib_name.to_str(), bar_date); touch_source_file(&workspace, &dep_id); @@ -983,11 +991,11 @@ fn do_rebuild_dep_only_contents_change() { let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); - let bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar")); - frob_source_file(&workspace, &dep_id); + let bar_date = datestamp(&lib_output_file_name(&workspace, "bar")); + frob_source_file(&workspace, &dep_id, "lib.rs"); // should adjust the datestamp command_line_test([~"build", ~"foo"], &workspace); - let new_bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar")); + let new_bar_date = datestamp(&lib_output_file_name(&workspace, "bar")); assert!(new_bar_date > bar_date); } @@ -1309,7 +1317,6 @@ fn rust_path_hack_build_no_arg() { } #[test] -#[ignore (reason = "#7402 not yet implemented")] fn rust_path_install_target() { let dir_for_path = mkdtemp(&os::tmpdir(), "source_workspace").expect("rust_path_install_target failed"); @@ -1464,10 +1471,13 @@ fn test_cfg_fail() { let workspace = create_local_package(&p_id); writeFile(&workspace.push_many(["src", "foo-0.1", "main.rs"]), "#[cfg(quux)] fn main() {}"); - assert!(command_line_test_partial([test_sysroot().to_str(), + match command_line_test_partial([test_sysroot().to_str(), ~"build", ~"foo"], - &workspace).is_none()); + &workspace) { + Success(*) => fail!("test_cfg_fail failed"), + _ => () + } } @@ -1680,6 +1690,21 @@ fn test_target_specific_install_dir() { assert_executable_exists(&workspace, "foo"); } +#[test] +fn test_dependencies_terminate() { + // let a_id = PkgId::new("a"); + let b_id = PkgId::new("b"); +// let workspace = create_local_package_with_dep(&b_id, &a_id); + let workspace = create_local_package(&b_id); + let b_dir = workspace.push_many([~"src", ~"b-0.1"]); + // writeFile(&b_dir.push("lib.rs"), "extern mod a; pub fn f() {}"); + let b_subdir = b_dir.push("test"); + assert!(os::mkdir_recursive(&b_subdir, U_RWX)); + writeFile(&b_subdir.push("test.rs"), + "extern mod b; use b::f; #[test] fn g() { f() }"); + command_line_test([~"install", ~"b"], &workspace); +} + /// Returns true if p exists and is executable fn is_executable(p: &Path) -> bool { use std::libc::consts::os::posix88::{S_IXUSR}; @@ -1689,3 +1714,25 @@ fn is_executable(p: &Path) -> bool { Some(mode) => mode & S_IXUSR as uint == S_IXUSR as uint } } + +#[cfg(target_os = "win32")] +fn chmod_read_only(p: &Path) -> bool { + #[fixed_stack_segment]; + unsafe { + do p.to_str().with_c_str |src_buf| { + libc::chmod(src_buf, libc::consts::os::posix88::S_IRUSR as c_int) == 0 as libc::c_int + } + } +} + +#[cfg(not(target_os = "win32"))] +fn chmod_read_only(p: &Path) -> bool { + #[fixed_stack_segment]; + unsafe { + do p.to_str().with_c_str |src_buf| { + libc::chmod(src_buf, + libc::consts::os::posix88::S_IRUSR as libc::mode_t) == 0 + as libc::c_int + } + } +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index ab883b50f8c..64f76dcdc60 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::libc; use std::os; use extra::workcache; use rustc::driver::{driver, session}; @@ -26,7 +27,7 @@ use workspace::pkg_parent_workspaces; use path_util::{installed_library_in_workspace, U_RWX, rust_path, system_library, target_build_dir}; use messages::error; -pub use target::{OutputType, Main, Lib, Bench, Test}; +pub use target::{OutputType, Main, Lib, Bench, Test, JustOne, lib_name_of, lib_crate_filename}; use workcache_support::{digest_file_with_date, digest_only_date}; // It would be nice to have the list of commands in just one place -- for example, @@ -171,6 +172,8 @@ pub fn compile_input(context: &BuildContext, // not sure if we should support anything else let out_dir = target_build_dir(workspace).push_rel(&pkg_id.path); + // Make the output directory if it doesn't exist already + assert!(os::mkdir_recursive(&out_dir, U_RWX)); let binary = os::args()[0].to_managed(); @@ -220,7 +223,7 @@ pub fn compile_input(context: &BuildContext, optimize: if opt { session::Aggressive } else { session::No }, test: what == Test || what == Bench, maybe_sysroot: Some(sysroot_to_use), - addl_lib_search_paths: @mut (~[out_dir.clone()]), + addl_lib_search_paths: @mut (~[]), output_type: output_type, .. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone() }; @@ -329,6 +332,9 @@ pub fn compile_crate_from_input(input: &Path, // Register dependency on the source file exec.discover_input("file", input.to_str(), digest_file_with_date(input)); + debug!("Built %s, date = %?", outputs.out_filename.to_str(), + datestamp(&outputs.out_filename)); + Some(outputs.out_filename) } @@ -409,7 +415,8 @@ pub fn find_and_install_dependencies(context: &BuildContext, workspaces[0] }; let (outputs_disc, inputs_disc) = - context.install(PkgSrc::new(dep_workspace.clone(), false, pkg_id)); + context.install(PkgSrc::new(dep_workspace.clone(), + false, pkg_id), &JustOne(Path(lib_crate_filename))); debug!("Installed %s, returned %? dependencies and \ %? transitive dependencies", lib_name, outputs_disc.len(), inputs_disc.len()); @@ -435,10 +442,11 @@ pub fn find_and_install_dependencies(context: &BuildContext, debug!("Adding additional search path: %s", lib_name); let installed_library = installed_library_in_workspace(&Path(lib_name), &dep_workspace) - .expect( fmt!("rustpkg failed to install dependency %s", + .expect(fmt!("rustpkg failed to install dependency %s", lib_name)); let install_dir = installed_library.pop(); - debug!("Installed %s into %s", lib_name, install_dir.to_str()); + debug!("Installed %s into %s [%?]", lib_name, install_dir.to_str(), + datestamp(&installed_library)); save(install_dir); } }} @@ -449,37 +457,6 @@ pub fn find_and_install_dependencies(context: &BuildContext, }; } -#[cfg(windows)] -pub fn link_exe(_src: &Path, _dest: &Path) -> bool { - #[fixed_stack_segment]; #[inline(never)]; - - /* 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 { - #[fixed_stack_segment]; #[inline(never)]; - - use std::c_str::ToCStr; - use std::libc; - - unsafe { - do src.with_c_str |src_buf| { - do dest.with_c_str |dest_buf| { - libc::link(src_buf, dest_buf) == 0 as libc::c_int && - libc::chmod(dest_buf, 755) == 0 as libc::c_int - } - } - } -} - pub fn mk_string_lit(s: @str) -> ast::lit { Spanned { node: ast::lit_str(s), @@ -516,3 +493,12 @@ pub fn option_to_vec<T>(x: Option<T>) -> ~[T] { // tjc: cheesy fn debug_flags() -> ~[~str] { ~[] } // static DEBUG_FLAGS: ~[~str] = ~[~"-Z", ~"time-passes"]; + + +/// Returns the last-modified date as an Option +pub fn datestamp(p: &Path) -> Option<libc::time_t> { + debug!("Scrutinizing datestamp for %s - does it exist? %?", p.to_str(), os::path_exists(p)); + let out = p.stat().map(|stat| stat.st_mtime); + debug!("Date = %?", out); + out.map(|t| { *t as libc::time_t }) +} diff --git a/src/librustpkg/workcache_support.rs b/src/librustpkg/workcache_support.rs index e2416782d98..daf35c988c8 100644 --- a/src/librustpkg/workcache_support.rs +++ b/src/librustpkg/workcache_support.rs @@ -56,3 +56,8 @@ pub fn discover_outputs(e: &mut workcache::Exec, outputs: ~[Path]) { e.discover_output("binary", p.to_str(), digest_only_date(p)); } } + +/// Returns the function name for building a crate +pub fn crate_tag(p: &Path) -> ~str { + p.to_str() // implicitly, it's "build(p)"... +} diff --git a/src/libstd/borrow.rs b/src/libstd/borrow.rs index 6c3d4c5f1fb..0626b3fc618 100644 --- a/src/libstd/borrow.rs +++ b/src/libstd/borrow.rs @@ -22,7 +22,7 @@ pub fn to_uint<T>(thing: &T) -> uint { /// Determine if two borrowed pointers point to the same thing. #[inline] pub fn ref_eq<'a, 'b, T>(thing: &'a T, other: &'b T) -> bool { - to_uint(thing) == to_uint(other) + (thing as *T) == (other as *T) } // Equality for region pointers @@ -70,3 +70,17 @@ impl<'self, T: TotalEq> TotalEq for &'self T { #[inline] fn equals(&self, other: & &'self T) -> bool { (**self).equals(*other) } } + +#[cfg(test)] +mod tests { + use super::ref_eq; + + #[test] + fn test_ref_eq() { + let x = 1; + let y = 1; + + assert!(ref_eq(&x, &x)); + assert!(!ref_eq(&x, &y)); + } +} diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 75598b300a3..67b5aff8466 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -30,9 +30,7 @@ pub enum NullByteResolution { condition! { // This should be &[u8] but there's a lifetime issue (#5370). - // NOTE: this super::NullByteResolution should be NullByteResolution - // Change this next time the snapshot is updated. - pub null_byte: (~[u8]) -> super::NullByteResolution; + pub null_byte: (~[u8]) -> NullByteResolution; } /// The representation of a C String. diff --git a/src/libstd/char.rs b/src/libstd/char.rs index 911d883f88a..431fc27a202 100644 --- a/src/libstd/char.rs +++ b/src/libstd/char.rs @@ -281,11 +281,11 @@ pub fn escape_unicode(c: char, f: &fn(char)) { // avoid calling str::to_str_radix because we don't really need to allocate // here. f('\\'); - let pad = cond!( - (c <= '\xff') { f('x'); 2 } - (c <= '\uffff') { f('u'); 4 } - _ { f('U'); 8 } - ); + let pad = match () { + _ if c <= '\xff' => { f('x'); 2 } + _ if c <= '\uffff' => { f('u'); 4 } + _ => { f('U'); 8 } + }; for offset in range_step::<i32>(4 * (pad - 1), -1, -4) { unsafe { match ((c as i32) >> offset) & 0xf { @@ -329,13 +329,13 @@ pub fn len_utf8_bytes(c: char) -> uint { static MAX_FOUR_B: uint = 2097152u; let code = c as uint; - cond!( - (code < MAX_ONE_B) { 1u } - (code < MAX_TWO_B) { 2u } - (code < MAX_THREE_B) { 3u } - (code < MAX_FOUR_B) { 4u } - _ { fail!("invalid character!") } - ) + match () { + _ if code < MAX_ONE_B => 1u, + _ if code < MAX_TWO_B => 2u, + _ if code < MAX_THREE_B => 3u, + _ if code < MAX_FOUR_B => 4u, + _ => fail!("invalid character!"), + } } impl ToStr for char { diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 790dc886c04..a5a2def450e 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -294,7 +294,6 @@ pub mod types { pub type ssize_t = i32; } #[cfg(target_arch = "x86")] - #[cfg(target_arch = "mips")] pub mod posix01 { use libc::types::os::arch::c95::{c_short, c_long, time_t}; use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t}; @@ -305,7 +304,6 @@ pub mod types { pub type blksize_t = i32; pub type blkcnt_t = i32; - #[cfg(target_arch = "x86")] pub struct stat { st_dev: dev_t, __pad1: c_short, @@ -328,30 +326,6 @@ pub mod types { __unused4: c_long, __unused5: c_long, } - - #[cfg(target_arch = "mips")] - pub struct stat { - st_dev: c_ulong, - st_pad1: [c_long, ..3], - st_ino: ino_t, - st_mode: mode_t, - st_nlink: nlink_t, - st_uid: uid_t, - st_gid: gid_t, - st_rdev: c_ulong, - st_pad2: [c_long, ..2], - st_size: off_t, - st_pad3: c_long, - st_atime: time_t, - st_atime_nsec: c_long, - st_mtime: time_t, - st_mtime_nsec: c_long, - st_ctime: time_t, - st_ctime_nsec: c_long, - st_blksize: blksize_t, - st_blocks: blkcnt_t, - st_pad5: [c_long, ..14], - } } #[cfg(target_arch = "arm")] pub mod posix01 { @@ -385,6 +359,40 @@ pub mod types { st_ino: c_ulonglong } } + #[cfg(target_arch = "mips")] + pub mod posix01 { + use libc::types::os::arch::c95::{c_long, c_ulong, time_t}; + use libc::types::os::arch::posix88::{gid_t, ino_t}; + use libc::types::os::arch::posix88::{mode_t, off_t}; + use libc::types::os::arch::posix88::{uid_t}; + + pub type nlink_t = u32; + pub type blksize_t = i32; + pub type blkcnt_t = i32; + + pub struct stat { + st_dev: c_ulong, + st_pad1: [c_long, ..3], + st_ino: ino_t, + st_mode: mode_t, + st_nlink: nlink_t, + st_uid: uid_t, + st_gid: gid_t, + st_rdev: c_ulong, + st_pad2: [c_long, ..2], + st_size: off_t, + st_pad3: c_long, + st_atime: time_t, + st_atime_nsec: c_long, + st_mtime: time_t, + st_mtime_nsec: c_long, + st_ctime: time_t, + st_ctime_nsec: c_long, + st_blksize: blksize_t, + st_blocks: blkcnt_t, + st_pad5: [c_long, ..14], + } + } pub mod posix08 {} pub mod bsd44 {} pub mod extra {} @@ -1633,6 +1641,111 @@ pub mod consts { pub static EPIPE : c_int = 32; pub static EDOM : c_int = 33; pub static ERANGE : c_int = 34; + + pub static ENOMSG: c_int = 35; + pub static EIDRM: c_int = 36; + pub static ECHRNG: c_int = 37; + pub static EL2NSYNC: c_int = 38; + pub static EL3HLT: c_int = 39; + pub static EL3RST: c_int = 40; + pub static ELNRNG: c_int = 41; + pub static EUNATCH: c_int = 42; + pub static ENOCSI: c_int = 43; + pub static EL2HLT: c_int = 44; + pub static EDEADLK: c_int = 45; + pub static ENOLCK: c_int = 46; + pub static EBADE: c_int = 50; + pub static EBADR: c_int = 51; + pub static EXFULL: c_int = 52; + pub static ENOANO: c_int = 53; + pub static EBADRQC: c_int = 54; + pub static EBADSLT: c_int = 55; + pub static EDEADLOCK: c_int = 56; + pub static EBFONT: c_int = 59; + pub static ENOSTR: c_int = 60; + pub static ENODATA: c_int = 61; + pub static ETIME: c_int = 62; + pub static ENOSR: c_int = 63; + pub static ENONET: c_int = 64; + pub static ENOPKG: c_int = 65; + pub static EREMOTE: c_int = 66; + pub static ENOLINK: c_int = 67; + pub static EADV: c_int = 68; + pub static ESRMNT: c_int = 69; + pub static ECOMM: c_int = 70; + pub static EPROTO: c_int = 71; + pub static EDOTDOT: c_int = 73; + pub static EMULTIHOP: c_int = 74; + pub static EBADMSG: c_int = 77; + pub static ENAMETOOLONG: c_int = 78; + pub static EOVERFLOW: c_int = 79; + pub static ENOTUNIQ: c_int = 80; + pub static EBADFD: c_int = 81; + pub static EREMCHG: c_int = 82; + pub static ELIBACC: c_int = 83; + pub static ELIBBAD: c_int = 84; + pub static ELIBSCN: c_int = 95; + pub static ELIBMAX: c_int = 86; + pub static ELIBEXEC: c_int = 87; + pub static EILSEQ: c_int = 88; + pub static ENOSYS: c_int = 89; + pub static ELOOP: c_int = 90; + pub static ERESTART: c_int = 91; + pub static ESTRPIPE: c_int = 92; + pub static ENOTEMPTY: c_int = 93; + pub static EUSERS: c_int = 94; + pub static ENOTSOCK: c_int = 95; + pub static EDESTADDRREQ: c_int = 96; + pub static EMSGSIZE: c_int = 97; + pub static EPROTOTYPE: c_int = 98; + pub static ENOPROTOOPT: c_int = 99; + pub static EPROTONOSUPPORT: c_int = 120; + pub static ESOCKTNOSUPPORT: c_int = 121; + pub static EOPNOTSUPP: c_int = 122; + pub static EPFNOSUPPORT: c_int = 123; + pub static EAFNOSUPPORT: c_int = 124; + pub static EADDRINUSE: c_int = 125; + pub static EADDRNOTAVAIL: c_int = 126; + pub static ENETDOWN: c_int = 127; + pub static ENETUNREACH: c_int = 128; + pub static ENETRESET: c_int = 129; + pub static ECONNABORTED: c_int = 130; + pub static ECONNRESET: c_int = 131; + pub static ENOBUFS: c_int = 132; + pub static EISCONN: c_int = 133; + pub static ENOTCONN: c_int = 134; + pub static EUCLEAN: c_int = 135; + pub static ENOTNAM: c_int = 137; + pub static ENAVAIL: c_int = 138; + pub static EISNAM: c_int = 139; + pub static EREMOTEIO: c_int = 140; + pub static ESHUTDOWN: c_int = 143; + pub static ETOOMANYREFS: c_int = 144; + pub static ETIMEDOUT: c_int = 145; + pub static ECONNREFUSED: c_int = 146; + pub static EHOSTDOWN: c_int = 147; + pub static EHOSTUNREACH: c_int = 148; + pub static EWOULDBLOCK: c_int = EAGAIN; + pub static EALREADY: c_int = 149; + pub static EINPROGRESS: c_int = 150; + pub static ESTALE: c_int = 151; + pub static ECANCELED: c_int = 158; + + pub static ENOMEDIUM: c_int = 159; + pub static EMEDIUMTYPE: c_int = 160; + pub static ENOKEY: c_int = 161; + pub static EKEYEXPIRED: c_int = 162; + pub static EKEYREVOKED: c_int = 163; + pub static EKEYREJECTED: c_int = 164; + + pub static EOWNERDEAD: c_int = 165; + pub static ENOTRECOVERABLE: c_int = 166; + + pub static ERFKILL: c_int = 167; + + pub static EHWPOISON: c_int = 168; + + pub static EDQUOT: c_int = 1133; } pub mod posix01 { use libc::types::os::arch::c95::c_int; diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs index b39b3102a34..1a463a499cb 100644 --- a/src/libstd/logging.rs +++ b/src/libstd/logging.rs @@ -37,17 +37,6 @@ pub fn console_off() { rt::logging::console_off(); } -#[cfg(not(test), stage0)] -#[lang="log_type"] -#[allow(missing_doc)] -pub fn log_type<T>(_level: u32, object: &T) { - use sys; - - // XXX: Bad allocation - let msg = sys::log_str(object); - newsched_log_str(msg); -} - fn newsched_log_str(msg: ~str) { use rt::task::Task; use rt::local::Local; diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 0addcce3eb6..2787e028645 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -206,35 +206,35 @@ impl Orderable for f32 { /// Returns `NaN` if either of the numbers are `NaN`. #[inline] fn min(&self, other: &f32) -> f32 { - cond!( - (self.is_NaN()) { *self } - (other.is_NaN()) { *other } - (*self < *other) { *self } - _ { *other } - ) + match () { + _ if self.is_NaN() => *self, + _ if other.is_NaN() => *other, + _ if *self < *other => *self, + _ => *other, + } } /// Returns `NaN` if either of the numbers are `NaN`. #[inline] fn max(&self, other: &f32) -> f32 { - cond!( - (self.is_NaN()) { *self } - (other.is_NaN()) { *other } - (*self > *other) { *self } - _ { *other } - ) + match () { + _ if self.is_NaN() => *self, + _ if other.is_NaN() => *other, + _ if *self > *other => *self, + _ => *other, + } } /// Returns the number constrained within the range `mn <= self <= mx`. /// If any of the numbers are `NaN` then `NaN` is returned. #[inline] fn clamp(&self, mn: &f32, mx: &f32) -> f32 { - cond!( - (self.is_NaN()) { *self } - (!(*self <= *mx)) { *mx } - (!(*self >= *mn)) { *mn } - _ { *self } - ) + match () { + _ if self.is_NaN() => *self, + _ if !(*self <= *mx) => *mx, + _ if !(*self >= *mn) => *mn, + _ => *self, + } } } @@ -823,39 +823,6 @@ impl num::ToStrRadix for f32 { } /// -/// Convert a string in base 10 to a float. -/// Accepts a optional decimal exponent. -/// -/// This function accepts strings such as -/// -/// * '3.14' -/// * '+3.14', equivalent to '3.14' -/// * '-3.14' -/// * '2.5E10', or equivalently, '2.5e10' -/// * '2.5E-10' -/// * '.' (understood as 0) -/// * '5.' -/// * '.5', or, equivalently, '0.5' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str(num: &str) -> Option<f32> { - strconv::from_str_common(num, 10u, true, true, true, - strconv::ExpDec, false, false) -} - -/// /// Convert a string in base 16 to a float. /// Accepts a optional binary exponent. /// @@ -888,40 +855,65 @@ pub fn from_str_hex(num: &str) -> Option<f32> { strconv::ExpBin, false, false) } -/// -/// Convert a string in an given base to a float. -/// -/// Due to possible conflicts, this function does **not** accept -/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** -/// does it recognize exponents of any kind. -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// * radix - The base to use. Must lie in the range [2 .. 36] -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str_radix(num: &str, rdx: uint) -> Option<f32> { - strconv::from_str_common(num, rdx, true, true, false, - strconv::ExpNone, false, false) -} - impl FromStr for f32 { + /// + /// Convert a string in base 10 to a float. + /// Accepts a optional decimal exponent. + /// + /// This function accepts strings such as + /// + /// * '3.14' + /// * '+3.14', equivalent to '3.14' + /// * '-3.14' + /// * '2.5E10', or equivalently, '2.5e10' + /// * '2.5E-10' + /// * '.' (understood as 0) + /// * '5.' + /// * '.5', or, equivalently, '0.5' + /// * '+inf', 'inf', '-inf', 'NaN' + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] - fn from_str(val: &str) -> Option<f32> { from_str(val) } + fn from_str(val: &str) -> Option<f32> { + strconv::from_str_common(val, 10u, true, true, true, + strconv::ExpDec, false, false) + } } impl num::FromStrRadix for f32 { + /// + /// Convert a string in an given base to a float. + /// + /// Due to possible conflicts, this function does **not** accept + /// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + /// does it recognize exponents of any kind. + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// * radix - The base to use. Must lie in the range [2 .. 36] + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option<f32> { - from_str_radix(val, rdx) + strconv::from_str_common(val, rdx, true, true, false, + strconv::ExpNone, false, false) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index b0675278238..afc22ec212b 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -229,35 +229,35 @@ impl Orderable for f64 { /// Returns `NaN` if either of the numbers are `NaN`. #[inline] fn min(&self, other: &f64) -> f64 { - cond!( - (self.is_NaN()) { *self } - (other.is_NaN()) { *other } - (*self < *other) { *self } - _ { *other } - ) + match () { + _ if self.is_NaN() => *self, + _ if other.is_NaN() => *other, + _ if *self < *other => *self, + _ => *other, + } } /// Returns `NaN` if either of the numbers are `NaN`. #[inline] fn max(&self, other: &f64) -> f64 { - cond!( - (self.is_NaN()) { *self } - (other.is_NaN()) { *other } - (*self > *other) { *self } - _ { *other } - ) + match () { + _ if self.is_NaN() => *self, + _ if other.is_NaN() => *other, + _ if *self > *other => *self, + _ => *other, + } } /// Returns the number constrained within the range `mn <= self <= mx`. /// If any of the numbers are `NaN` then `NaN` is returned. #[inline] fn clamp(&self, mn: &f64, mx: &f64) -> f64 { - cond!( - (self.is_NaN()) { *self } - (!(*self <= *mx)) { *mx } - (!(*self >= *mn)) { *mn } - _ { *self } - ) + match () { + _ if self.is_NaN() => *self, + _ if !(*self <= *mx) => *mx, + _ if !(*self >= *mn) => *mn, + _ => *self, + } } } @@ -870,39 +870,6 @@ impl num::ToStrRadix for f64 { } /// -/// Convert a string in base 10 to a float. -/// Accepts a optional decimal exponent. -/// -/// This function accepts strings such as -/// -/// * '3.14' -/// * '+3.14', equivalent to '3.14' -/// * '-3.14' -/// * '2.5E10', or equivalently, '2.5e10' -/// * '2.5E-10' -/// * '.' (understood as 0) -/// * '5.' -/// * '.5', or, equivalently, '0.5' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str(num: &str) -> Option<f64> { - strconv::from_str_common(num, 10u, true, true, true, - strconv::ExpDec, false, false) -} - -/// /// Convert a string in base 16 to a float. /// Accepts a optional binary exponent. /// @@ -935,40 +902,65 @@ pub fn from_str_hex(num: &str) -> Option<f64> { strconv::ExpBin, false, false) } -/// -/// Convert a string in an given base to a float. -/// -/// Due to possible conflicts, this function does **not** accept -/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** -/// does it recognize exponents of any kind. -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// * radix - The base to use. Must lie in the range [2 .. 36] -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str_radix(num: &str, rdx: uint) -> Option<f64> { - strconv::from_str_common(num, rdx, true, true, false, - strconv::ExpNone, false, false) -} - impl FromStr for f64 { + /// + /// Convert a string in base 10 to a float. + /// Accepts a optional decimal exponent. + /// + /// This function accepts strings such as + /// + /// * '3.14' + /// * '+3.14', equivalent to '3.14' + /// * '-3.14' + /// * '2.5E10', or equivalently, '2.5e10' + /// * '2.5E-10' + /// * '.' (understood as 0) + /// * '5.' + /// * '.5', or, equivalently, '0.5' + /// * '+inf', 'inf', '-inf', 'NaN' + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] - fn from_str(val: &str) -> Option<f64> { from_str(val) } + fn from_str(val: &str) -> Option<f64> { + strconv::from_str_common(val, 10u, true, true, true, + strconv::ExpDec, false, false) + } } impl num::FromStrRadix for f64 { + /// + /// Convert a string in an given base to a float. + /// + /// Due to possible conflicts, this function does **not** accept + /// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + /// does it recognize exponents of any kind. + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// * radix - The base to use. Must lie in the range [2 .. 36] + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option<f64> { - from_str_radix(val, rdx) + strconv::from_str_common(val, rdx, true, true, false, + strconv::ExpNone, false, false) } } diff --git a/src/libstd/num/float.rs b/src/libstd/num/float.rs index 3952f5478f7..b86422edc03 100644 --- a/src/libstd/num/float.rs +++ b/src/libstd/num/float.rs @@ -188,39 +188,6 @@ impl num::ToStrRadix for float { } /// -/// Convert a string in base 10 to a float. -/// Accepts a optional decimal exponent. -/// -/// This function accepts strings such as -/// -/// * '3.14' -/// * '+3.14', equivalent to '3.14' -/// * '-3.14' -/// * '2.5E10', or equivalently, '2.5e10' -/// * '2.5E-10' -/// * '.' (understood as 0) -/// * '5.' -/// * '.5', or, equivalently, '0.5' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str(num: &str) -> Option<float> { - strconv::from_str_common(num, 10u, true, true, true, - strconv::ExpDec, false, false) -} - -/// /// Convert a string in base 16 to a float. /// Accepts a optional binary exponent. /// @@ -253,40 +220,65 @@ pub fn from_str_hex(num: &str) -> Option<float> { strconv::ExpBin, false, false) } -/// -/// Convert a string in an given base to a float. -/// -/// Due to possible conflicts, this function does **not** accept -/// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** -/// does it recognize exponents of any kind. -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// * radix - The base to use. Must lie in the range [2 .. 36] -/// -/// # Return value -/// -/// `none` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `num`. -/// -#[inline] -pub fn from_str_radix(num: &str, radix: uint) -> Option<float> { - strconv::from_str_common(num, radix, true, true, false, - strconv::ExpNone, false, false) -} - impl FromStr for float { + /// + /// Convert a string in base 10 to a float. + /// Accepts a optional decimal exponent. + /// + /// This function accepts strings such as + /// + /// * '3.14' + /// * '+3.14', equivalent to '3.14' + /// * '-3.14' + /// * '2.5E10', or equivalently, '2.5e10' + /// * '2.5E-10' + /// * '.' (understood as 0) + /// * '5.' + /// * '.5', or, equivalently, '0.5' + /// * '+inf', 'inf', '-inf', 'NaN' + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] - fn from_str(val: &str) -> Option<float> { from_str(val) } + fn from_str(val: &str) -> Option<float> { + strconv::from_str_common(val, 10u, true, true, true, + strconv::ExpDec, false, false) + } } impl num::FromStrRadix for float { + /// + /// Convert a string in an given base to a float. + /// + /// Due to possible conflicts, this function does **not** accept + /// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** + /// does it recognize exponents of any kind. + /// + /// Leading and trailing whitespace represent an error. + /// + /// # Arguments + /// + /// * num - A string + /// * radix - The base to use. Must lie in the range [2 .. 36] + /// + /// # Return value + /// + /// `none` if the string did not represent a valid number. Otherwise, + /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// #[inline] fn from_str_radix(val: &str, radix: uint) -> Option<float> { - from_str_radix(val, radix) + strconv::from_str_common(val, radix, true, true, false, + strconv::ExpNone, false, false) } } @@ -1316,49 +1308,49 @@ mod tests { #[test] pub fn test_from_str() { - assert_eq!(from_str("3"), Some(3.)); - assert_eq!(from_str("3.14"), Some(3.14)); - assert_eq!(from_str("+3.14"), Some(3.14)); - assert_eq!(from_str("-3.14"), Some(-3.14)); - assert_eq!(from_str("2.5E10"), Some(25000000000.)); - assert_eq!(from_str("2.5e10"), Some(25000000000.)); - assert_eq!(from_str("25000000000.E-10"), Some(2.5)); - assert_eq!(from_str("."), Some(0.)); - assert_eq!(from_str(".e1"), Some(0.)); - assert_eq!(from_str(".e-1"), Some(0.)); - assert_eq!(from_str("5."), Some(5.)); - assert_eq!(from_str(".5"), Some(0.5)); - assert_eq!(from_str("0.5"), Some(0.5)); - assert_eq!(from_str("-.5"), Some(-0.5)); - assert_eq!(from_str("-5"), Some(-5.)); - assert_eq!(from_str("inf"), Some(infinity)); - assert_eq!(from_str("+inf"), Some(infinity)); - assert_eq!(from_str("-inf"), Some(neg_infinity)); + assert_eq!(from_str::<float>("3"), Some(3.)); + assert_eq!(from_str::<float>("3.14"), Some(3.14)); + assert_eq!(from_str::<float>("+3.14"), Some(3.14)); + assert_eq!(from_str::<float>("-3.14"), Some(-3.14)); + assert_eq!(from_str::<float>("2.5E10"), Some(25000000000.)); + assert_eq!(from_str::<float>("2.5e10"), Some(25000000000.)); + assert_eq!(from_str::<float>("25000000000.E-10"), Some(2.5)); + assert_eq!(from_str::<float>("."), Some(0.)); + assert_eq!(from_str::<float>(".e1"), Some(0.)); + assert_eq!(from_str::<float>(".e-1"), Some(0.)); + assert_eq!(from_str::<float>("5."), Some(5.)); + assert_eq!(from_str::<float>(".5"), Some(0.5)); + assert_eq!(from_str::<float>("0.5"), Some(0.5)); + assert_eq!(from_str::<float>("-.5"), Some(-0.5)); + assert_eq!(from_str::<float>("-5"), Some(-5.)); + assert_eq!(from_str::<float>("inf"), Some(infinity)); + assert_eq!(from_str::<float>("+inf"), Some(infinity)); + assert_eq!(from_str::<float>("-inf"), Some(neg_infinity)); // note: NaN != NaN, hence this slightly complex test - match from_str("NaN") { + match from_str::<float>("NaN") { Some(f) => assert!(f.is_NaN()), None => fail!() } // note: -0 == 0, hence these slightly more complex tests - match from_str("-0") { + match from_str::<float>("-0") { Some(v) if v.is_zero() => assert!(v.is_negative()), _ => fail!() } - match from_str("0") { + match from_str::<float>("0") { Some(v) if v.is_zero() => assert!(v.is_positive()), _ => fail!() } - assert!(from_str("").is_none()); - assert!(from_str("x").is_none()); - assert!(from_str(" ").is_none()); - assert!(from_str(" ").is_none()); - assert!(from_str("e").is_none()); - assert!(from_str("E").is_none()); - assert!(from_str("E1").is_none()); - assert!(from_str("1e1e1").is_none()); - assert!(from_str("1e1.1").is_none()); - assert!(from_str("1e1-1").is_none()); + assert!(from_str::<float>("").is_none()); + assert!(from_str::<float>("x").is_none()); + assert!(from_str::<float>(" ").is_none()); + assert!(from_str::<float>(" ").is_none()); + assert!(from_str::<float>("e").is_none()); + assert!(from_str::<float>("E").is_none()); + assert!(from_str::<float>("E1").is_none()); + assert!(from_str::<float>("1e1e1").is_none()); + assert!(from_str::<float>("1e1.1").is_none()); + assert!(from_str::<float>("1e1-1").is_none()); } #[test] diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index 0a9c912a6e2..7cd1be7ab74 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -70,11 +70,11 @@ impl Orderable for $T { /// Returns the number constrained within the range `mn <= self <= mx`. #[inline] fn clamp(&self, mn: &$T, mx: &$T) -> $T { - cond!( - (*self > *mx) { *mx } - (*self < *mn) { *mn } - _ { *self } - ) + match () { + _ if (*self > *mx) => *mx, + _ if (*self < *mn) => *mn, + _ => *self, + } } } diff --git a/src/libstd/option.rs b/src/libstd/option.rs index 9b6d0a77cd8..42878f6effb 100644 --- a/src/libstd/option.rs +++ b/src/libstd/option.rs @@ -467,15 +467,6 @@ impl<T: Default> Option<T> { None => Default::default() } } - - /// Returns self or `Some`-wrapped default value - #[inline] - pub fn or_default(self) -> Option<T> { - match self { - None => Some(Default::default()), - x => x, - } - } } impl<T> Default for Option<T> { @@ -483,7 +474,7 @@ impl<T> Default for Option<T> { fn default() -> Option<T> { None } } -impl<T:Zero> Option<T> { +impl<T: Zero> Option<T> { /// Returns the contained value or zero (for this type) #[inline] pub fn unwrap_or_zero(self) -> T { @@ -492,15 +483,6 @@ impl<T:Zero> Option<T> { None => Zero::zero() } } - - /// Returns self or `Some`-wrapped zero value - #[inline] - pub fn or_zero(self) -> Option<T> { - match self { - None => Some(Zero::zero()), - x => x - } - } } /// An iterator that yields either one or zero elements diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 215bda264ad..1dc1d1d6776 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -148,18 +148,6 @@ pub mod win32 { } } -#[cfg(stage0)] -mod macro_hack { -#[macro_escape]; -macro_rules! externfn( - (fn $name:ident ()) => ( - extern { - fn $name(); - } - ) -) -} - /* Accessing environment variables is not generally threadsafe. Serialize access through a global lock. @@ -1659,7 +1647,7 @@ pub mod consts { pub use os::consts::arm::*; #[cfg(target_arch = "mips")] - use os::consts::mips::*; + pub use os::consts::mips::*; pub mod unix { pub static FAMILY: &'static str = "unix"; diff --git a/src/libstd/ptr.rs b/src/libstd/ptr.rs index 6e90e2a1070..135acb106a1 100644 --- a/src/libstd/ptr.rs +++ b/src/libstd/ptr.rs @@ -16,31 +16,13 @@ use clone::Clone; use cmp::Equiv; use iter::{range, Iterator}; use option::{Option, Some, None}; -#[cfg(stage0)] -use sys; use unstable::intrinsics; use util::swap; #[cfg(not(test))] use cmp::{Eq, Ord}; -/// Calculate the offset from a pointer. The count *must* be in bounds or -/// otherwise the loads of this address are undefined. -#[inline] -#[cfg(stage0)] -pub unsafe fn offset<T>(ptr: *T, count: int) -> *T { - (ptr as uint + (count as uint) * sys::size_of::<T>()) as *T -} - -/// Calculate the offset from a mut pointer -#[inline] -#[cfg(stage0)] -pub unsafe fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T { - (ptr as uint + (count as uint) * sys::size_of::<T>()) as *mut T -} - /// Calculate the offset from a pointer #[inline] -#[cfg(not(stage0))] pub unsafe fn offset<T>(ptr: *T, count: int) -> *T { intrinsics::offset(ptr, count) } @@ -48,7 +30,6 @@ pub unsafe fn offset<T>(ptr: *T, count: int) -> *T { /// Calculate the offset from a mut pointer. The count *must* be in bounds or /// otherwise the loads of this address are undefined. #[inline] -#[cfg(not(stage0))] pub unsafe fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T { intrinsics::offset(ptr as *T, count) as *mut T } @@ -383,17 +364,7 @@ impl<T> RawPtr<T> for *mut T { } // Equality for pointers -#[cfg(stage0, not(test))] -impl<T> Eq for *T { - #[inline] - fn eq(&self, other: &*T) -> bool { - (*self as uint) == (*other as uint) - } - #[inline] - fn ne(&self, other: &*T) -> bool { !self.eq(other) } -} - -#[cfg(not(stage0), not(test))] +#[cfg(not(test))] impl<T> Eq for *T { #[inline] fn eq(&self, other: &*T) -> bool { @@ -403,17 +374,7 @@ impl<T> Eq for *T { fn ne(&self, other: &*T) -> bool { !self.eq(other) } } -#[cfg(stage0, not(test))] -impl<T> Eq for *mut T { - #[inline] - fn eq(&self, other: &*mut T) -> bool { - (*self as uint) == (*other as uint) - } - #[inline] - fn ne(&self, other: &*mut T) -> bool { !self.eq(other) } -} - -#[cfg(not(stage0), not(test))] +#[cfg(not(test))] impl<T> Eq for *mut T { #[inline] fn eq(&self, other: &*mut T) -> bool { @@ -480,27 +441,7 @@ mod externfnpointers { } // Comparison for pointers -#[cfg(stage0, not(test))] -impl<T> Ord for *T { - #[inline] - fn lt(&self, other: &*T) -> bool { - (*self as uint) < (*other as uint) - } - #[inline] - fn le(&self, other: &*T) -> bool { - (*self as uint) <= (*other as uint) - } - #[inline] - fn ge(&self, other: &*T) -> bool { - (*self as uint) >= (*other as uint) - } - #[inline] - fn gt(&self, other: &*T) -> bool { - (*self as uint) > (*other as uint) - } -} - -#[cfg(not(stage0), not(test))] +#[cfg(not(test))] impl<T> Ord for *T { #[inline] fn lt(&self, other: &*T) -> bool { @@ -520,27 +461,7 @@ impl<T> Ord for *T { } } -#[cfg(stage0, not(test))] -impl<T> Ord for *mut T { - #[inline] - fn lt(&self, other: &*mut T) -> bool { - (*self as uint) < (*other as uint) - } - #[inline] - fn le(&self, other: &*mut T) -> bool { - (*self as uint) <= (*other as uint) - } - #[inline] - fn ge(&self, other: &*mut T) -> bool { - (*self as uint) >= (*other as uint) - } - #[inline] - fn gt(&self, other: &*mut T) -> bool { - (*self as uint) > (*other as uint) - } -} - -#[cfg(not(stage0), not(test))] +#[cfg(not(test))] impl<T> Ord for *mut T { #[inline] fn lt(&self, other: &*mut T) -> bool { diff --git a/src/libstd/rand.rs b/src/libstd/rand.rs index 1330096ee36..8ca247edb59 100644 --- a/src/libstd/rand.rs +++ b/src/libstd/rand.rs @@ -915,7 +915,7 @@ pub fn seed() -> ~[u8] { } // used to make space in TLS for a random number generator -static tls_rng_state: local_data::Key<@@mut IsaacRng> = &local_data::Key; +local_data_key!(tls_rng_state: @@mut IsaacRng) /** * Gives back a lazily initialized task-local random number generator, diff --git a/src/libstd/reflect_stage0.rs b/src/libstd/reflect_stage0.rs deleted file mode 100644 index 56e0f83e05c..00000000000 --- a/src/libstd/reflect_stage0.rs +++ /dev/null @@ -1,493 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -Runtime type reflection - -*/ - -#[allow(missing_doc)]; - -use unstable::intrinsics::{Opaque, TyDesc, TyVisitor}; -use libc::c_void; -use sys; -use unstable::raw; - -/** - * Trait for visitor that wishes to reflect on data. To use this, create a - * struct that encapsulates the set of pointers you wish to walk through a - * data structure, and implement both `MovePtr` for it as well as `TyVisitor`; - * then build a MovePtrAdaptor wrapped around your struct. - */ -pub trait MovePtr { - fn move_ptr(&self, adjustment: &fn(*c_void) -> *c_void); - fn push_ptr(&self); - fn pop_ptr(&self); -} - -/// Helper function for alignment calculation. -#[inline] -pub fn align(size: uint, align: uint) -> uint { - ((size + align) - 1u) & !(align - 1u) -} - -/// Adaptor to wrap around visitors implementing MovePtr. -pub struct MovePtrAdaptor<V> { - inner: V -} -pub fn MovePtrAdaptor<V:TyVisitor + MovePtr>(v: V) -> MovePtrAdaptor<V> { - MovePtrAdaptor { inner: v } -} - -impl<V:TyVisitor + MovePtr> MovePtrAdaptor<V> { - #[inline] - pub fn bump(&self, sz: uint) { - do self.inner.move_ptr() |p| { - ((p as uint) + sz) as *c_void - }; - } - - #[inline] - pub fn align(&self, a: uint) { - do self.inner.move_ptr() |p| { - align(p as uint, a) as *c_void - }; - } - - #[inline] - pub fn align_to<T>(&self) { - self.align(sys::min_align_of::<T>()); - } - - #[inline] - pub fn bump_past<T>(&self) { - self.bump(sys::size_of::<T>()); - } -} - -/// Abstract type-directed pointer-movement using the MovePtr trait -impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> { - fn visit_bot(&self) -> bool { - self.align_to::<()>(); - if ! self.inner.visit_bot() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_nil(&self) -> bool { - self.align_to::<()>(); - if ! self.inner.visit_nil() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_bool(&self) -> bool { - self.align_to::<bool>(); - if ! self.inner.visit_bool() { return false; } - self.bump_past::<bool>(); - true - } - - fn visit_int(&self) -> bool { - self.align_to::<int>(); - if ! self.inner.visit_int() { return false; } - self.bump_past::<int>(); - true - } - - fn visit_i8(&self) -> bool { - self.align_to::<i8>(); - if ! self.inner.visit_i8() { return false; } - self.bump_past::<i8>(); - true - } - - fn visit_i16(&self) -> bool { - self.align_to::<i16>(); - if ! self.inner.visit_i16() { return false; } - self.bump_past::<i16>(); - true - } - - fn visit_i32(&self) -> bool { - self.align_to::<i32>(); - if ! self.inner.visit_i32() { return false; } - self.bump_past::<i32>(); - true - } - - fn visit_i64(&self) -> bool { - self.align_to::<i64>(); - if ! self.inner.visit_i64() { return false; } - self.bump_past::<i64>(); - true - } - - fn visit_uint(&self) -> bool { - self.align_to::<uint>(); - if ! self.inner.visit_uint() { return false; } - self.bump_past::<uint>(); - true - } - - fn visit_u8(&self) -> bool { - self.align_to::<u8>(); - if ! self.inner.visit_u8() { return false; } - self.bump_past::<u8>(); - true - } - - fn visit_u16(&self) -> bool { - self.align_to::<u16>(); - if ! self.inner.visit_u16() { return false; } - self.bump_past::<u16>(); - true - } - - fn visit_u32(&self) -> bool { - self.align_to::<u32>(); - if ! self.inner.visit_u32() { return false; } - self.bump_past::<u32>(); - true - } - - fn visit_u64(&self) -> bool { - self.align_to::<u64>(); - if ! self.inner.visit_u64() { return false; } - self.bump_past::<u64>(); - true - } - - fn visit_float(&self) -> bool { - self.align_to::<float>(); - if ! self.inner.visit_float() { return false; } - self.bump_past::<float>(); - true - } - - fn visit_f32(&self) -> bool { - self.align_to::<f32>(); - if ! self.inner.visit_f32() { return false; } - self.bump_past::<f32>(); - true - } - - fn visit_f64(&self) -> bool { - self.align_to::<f64>(); - if ! self.inner.visit_f64() { return false; } - self.bump_past::<f64>(); - true - } - - fn visit_char(&self) -> bool { - self.align_to::<char>(); - if ! self.inner.visit_char() { return false; } - self.bump_past::<char>(); - true - } - - fn visit_estr_box(&self) -> bool { - self.align_to::<@str>(); - if ! self.inner.visit_estr_box() { return false; } - self.bump_past::<@str>(); - true - } - - fn visit_estr_uniq(&self) -> bool { - self.align_to::<~str>(); - if ! self.inner.visit_estr_uniq() { return false; } - self.bump_past::<~str>(); - true - } - - fn visit_estr_slice(&self) -> bool { - self.align_to::<&'static str>(); - if ! self.inner.visit_estr_slice() { return false; } - self.bump_past::<&'static str>(); - true - } - - fn visit_estr_fixed(&self, n: uint, - sz: uint, - align: uint) -> bool { - self.align(align); - if ! self.inner.visit_estr_fixed(n, sz, align) { return false; } - self.bump(sz); - true - } - - fn visit_box(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<@u8>(); - if ! self.inner.visit_box(mtbl, inner) { return false; } - self.bump_past::<@u8>(); - true - } - - fn visit_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~u8>(); - if ! self.inner.visit_uniq(mtbl, inner) { return false; } - self.bump_past::<~u8>(); - true - } - - fn visit_uniq_managed(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~u8>(); - if ! self.inner.visit_uniq_managed(mtbl, inner) { return false; } - self.bump_past::<~u8>(); - true - } - - fn visit_ptr(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<*u8>(); - if ! self.inner.visit_ptr(mtbl, inner) { return false; } - self.bump_past::<*u8>(); - true - } - - fn visit_rptr(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner.visit_rptr(mtbl, inner) { return false; } - self.bump_past::<&'static u8>(); - true - } - - fn visit_unboxed_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<raw::Vec<()>>(); - if ! self.inner.visit_vec(mtbl, inner) { return false; } - true - } - - fn visit_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner.visit_vec(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_box(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<@[u8]>(); - if ! self.inner.visit_evec_box(mtbl, inner) { return false; } - self.bump_past::<@[u8]>(); - true - } - - fn visit_evec_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner.visit_evec_uniq(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_uniq_managed(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[@u8]>(); - if ! self.inner.visit_evec_uniq_managed(mtbl, inner) { return false; } - self.bump_past::<~[@u8]>(); - true - } - - fn visit_evec_slice(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static [u8]>(); - if ! self.inner.visit_evec_slice(mtbl, inner) { return false; } - self.bump_past::<&'static [u8]>(); - true - } - - fn visit_evec_fixed(&self, n: uint, sz: uint, align: uint, - mtbl: uint, inner: *TyDesc) -> bool { - self.align(align); - if ! self.inner.visit_evec_fixed(n, sz, align, mtbl, inner) { - return false; - } - self.bump(sz); - true - } - - fn visit_enter_rec(&self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner.visit_enter_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_rec_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - unsafe { self.align((*inner).align); } - if ! self.inner.visit_rec_field(i, name, mtbl, inner) { - return false; - } - unsafe { self.bump((*inner).size); } - true - } - - fn visit_leave_rec(&self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner.visit_leave_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_class(&self, n_fields: uint, sz: uint, align: uint) - -> bool { - self.align(align); - if ! self.inner.visit_enter_class(n_fields, sz, align) { - return false; - } - true - } - - fn visit_class_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - unsafe { self.align((*inner).align); } - if ! self.inner.visit_class_field(i, name, mtbl, inner) { - return false; - } - unsafe { self.bump((*inner).size); } - true - } - - fn visit_leave_class(&self, n_fields: uint, sz: uint, align: uint) - -> bool { - if ! self.inner.visit_leave_class(n_fields, sz, align) { - return false; - } - true - } - - fn visit_enter_tup(&self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner.visit_enter_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_tup_field(&self, i: uint, inner: *TyDesc) -> bool { - unsafe { self.align((*inner).align); } - if ! self.inner.visit_tup_field(i, inner) { return false; } - unsafe { self.bump((*inner).size); } - true - } - - fn visit_leave_tup(&self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner.visit_leave_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_fn(&self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner.visit_enter_fn(purity, proto, n_inputs, retstyle) { - return false - } - true - } - - fn visit_fn_input(&self, i: uint, mode: uint, inner: *TyDesc) -> bool { - if ! self.inner.visit_fn_input(i, mode, inner) { return false; } - true - } - - fn visit_fn_output(&self, retstyle: uint, inner: *TyDesc) -> bool { - if ! self.inner.visit_fn_output(retstyle, inner) { return false; } - true - } - - fn visit_leave_fn(&self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner.visit_leave_fn(purity, proto, n_inputs, retstyle) { - return false; - } - true - } - - fn visit_enter_enum(&self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> int, - sz: uint, align: uint) - -> bool { - self.align(align); - if ! self.inner.visit_enter_enum(n_variants, get_disr, sz, align) { - return false; - } - true - } - - fn visit_enter_enum_variant(&self, variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool { - if ! self.inner.visit_enter_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool { - self.inner.push_ptr(); - self.bump(offset); - if ! self.inner.visit_enum_variant_field(i, offset, inner) { return false; } - self.inner.pop_ptr(); - true - } - - fn visit_leave_enum_variant(&self, variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool { - if ! self.inner.visit_leave_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_leave_enum(&self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> int, - sz: uint, align: uint) -> bool { - if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) { - return false; - } - self.bump(sz); - true - } - - fn visit_trait(&self) -> bool { - self.align_to::<@TyVisitor>(); - if ! self.inner.visit_trait() { return false; } - self.bump_past::<@TyVisitor>(); - true - } - - fn visit_param(&self, i: uint) -> bool { - if ! self.inner.visit_param(i) { return false; } - true - } - - fn visit_self(&self) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner.visit_self() { return false; } - self.align_to::<&'static u8>(); - true - } - - fn visit_type(&self) -> bool { - if ! self.inner.visit_type() { return false; } - true - } - - fn visit_opaque_box(&self) -> bool { - self.align_to::<@u8>(); - if ! self.inner.visit_opaque_box() { return false; } - self.bump_past::<@u8>(); - true - } - - fn visit_closure_ptr(&self, ck: uint) -> bool { - self.align_to::<@fn()>(); - if ! self.inner.visit_closure_ptr(ck) { return false; } - self.bump_past::<@fn()>(); - true - } -} diff --git a/src/libstd/repr_stage0.rs b/src/libstd/repr_stage0.rs deleted file mode 100644 index cbce2005141..00000000000 --- a/src/libstd/repr_stage0.rs +++ /dev/null @@ -1,626 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! - -More runtime type reflection - -*/ - -#[allow(missing_doc)]; - -use cast::transmute; -use char; -use container::Container; -use io::{Writer, WriterUtil}; -use iter::Iterator; -use libc::c_void; -use option::{Some, None}; -use ptr; -use reflect; -use reflect::{MovePtr, align}; -use str::StrSlice; -use to_str::ToStr; -use vec::OwnedVector; -use unstable::intrinsics::{Opaque, TyDesc, TyVisitor, get_tydesc, visit_tydesc}; -use unstable::raw; - -#[cfg(test)] use io; - -/// Helpers - -trait EscapedCharWriter { - fn write_escaped_char(&self, ch: char); -} - -impl EscapedCharWriter for @Writer { - fn write_escaped_char(&self, ch: char) { - match ch { - '\t' => self.write_str("\\t"), - '\r' => self.write_str("\\r"), - '\n' => self.write_str("\\n"), - '\\' => self.write_str("\\\\"), - '\'' => self.write_str("\\'"), - '"' => self.write_str("\\\""), - '\x20'..'\x7e' => self.write_char(ch), - _ => { - do char::escape_unicode(ch) |c| { - self.write_char(c); - } - } - } - } -} - -/// Representations - -trait Repr { - fn write_repr(&self, writer: @Writer); -} - -impl Repr for () { - fn write_repr(&self, writer: @Writer) { writer.write_str("()"); } -} - -impl Repr for bool { - fn write_repr(&self, writer: @Writer) { - writer.write_str(if *self { "true" } else { "false" }) - } -} - -macro_rules! int_repr(($ty:ident) => (impl Repr for $ty { - fn write_repr(&self, writer: @Writer) { - do ::$ty::to_str_bytes(*self, 10u) |bits| { - writer.write(bits); - } - } -})) - -int_repr!(int) -int_repr!(i8) -int_repr!(i16) -int_repr!(i32) -int_repr!(i64) -int_repr!(uint) -int_repr!(u8) -int_repr!(u16) -int_repr!(u32) -int_repr!(u64) - -macro_rules! num_repr(($ty:ident) => (impl Repr for $ty { - fn write_repr(&self, writer: @Writer) { - let s = self.to_str(); - writer.write(s.as_bytes()); - } -})) - -num_repr!(float) -num_repr!(f32) -num_repr!(f64) - -// New implementation using reflect::MovePtr - -enum VariantState { - SearchingFor(int), - Matched, - AlreadyFound -} - -pub struct ReprVisitor { - ptr: @mut *c_void, - ptr_stk: @mut ~[*c_void], - var_stk: @mut ~[VariantState], - writer: @Writer -} -pub fn ReprVisitor(ptr: *c_void, writer: @Writer) -> ReprVisitor { - ReprVisitor { - ptr: @mut ptr, - ptr_stk: @mut ~[], - var_stk: @mut ~[], - writer: writer, - } -} - -impl MovePtr for ReprVisitor { - #[inline] - fn move_ptr(&self, adjustment: &fn(*c_void) -> *c_void) { - *self.ptr = adjustment(*self.ptr); - } - fn push_ptr(&self) { - self.ptr_stk.push(*self.ptr); - } - fn pop_ptr(&self) { - *self.ptr = self.ptr_stk.pop(); - } -} - -impl ReprVisitor { - // Various helpers for the TyVisitor impl - - #[inline] - pub fn get<T>(&self, f: &fn(&T)) -> bool { - unsafe { - f(transmute::<*c_void,&T>(*self.ptr)); - } - true - } - - #[inline] - pub fn visit_inner(&self, inner: *TyDesc) -> bool { - self.visit_ptr_inner(*self.ptr, inner) - } - - #[inline] - pub fn visit_ptr_inner(&self, ptr: *c_void, inner: *TyDesc) -> bool { - unsafe { - let u = ReprVisitor(ptr, self.writer); - let v = reflect::MovePtrAdaptor(u); - visit_tydesc(inner, @v as @TyVisitor); - true - } - } - - #[inline] - pub fn write<T:Repr>(&self) -> bool { - do self.get |v:&T| { - v.write_repr(self.writer); - } - } - - pub fn write_escaped_slice(&self, slice: &str) { - self.writer.write_char('"'); - for ch in slice.iter() { - self.writer.write_escaped_char(ch); - } - self.writer.write_char('"'); - } - - pub fn write_mut_qualifier(&self, mtbl: uint) { - if mtbl == 0 { - self.writer.write_str("mut "); - } else if mtbl == 1 { - // skip, this is ast::m_imm - } else { - assert_eq!(mtbl, 2); - self.writer.write_str("const "); - } - } - - pub fn write_vec_range(&self, - _mtbl: uint, - ptr: *(), - len: uint, - inner: *TyDesc) - -> bool { - let mut p = ptr as *u8; - let (sz, al) = unsafe { ((*inner).size, (*inner).align) }; - self.writer.write_char('['); - let mut first = true; - let mut left = len; - // unit structs have 0 size, and don't loop forever. - let dec = if sz == 0 {1} else {sz}; - while left > 0 { - if first { - first = false; - } else { - self.writer.write_str(", "); - } - self.visit_ptr_inner(p as *c_void, inner); - unsafe { - p = align(ptr::offset(p, sz as int) as uint, al) as *u8; - } - left -= dec; - } - self.writer.write_char(']'); - true - } - - pub fn write_unboxed_vec_repr(&self, - mtbl: uint, - v: &raw::Vec<()>, - inner: *TyDesc) - -> bool { - self.write_vec_range(mtbl, ptr::to_unsafe_ptr(&v.data), - v.fill, inner) - } -} - -impl TyVisitor for ReprVisitor { - fn visit_bot(&self) -> bool { - self.writer.write_str("!"); - true - } - fn visit_nil(&self) -> bool { self.write::<()>() } - fn visit_bool(&self) -> bool { self.write::<bool>() } - fn visit_int(&self) -> bool { self.write::<int>() } - fn visit_i8(&self) -> bool { self.write::<i8>() } - fn visit_i16(&self) -> bool { self.write::<i16>() } - fn visit_i32(&self) -> bool { self.write::<i32>() } - fn visit_i64(&self) -> bool { self.write::<i64>() } - - fn visit_uint(&self) -> bool { self.write::<uint>() } - fn visit_u8(&self) -> bool { self.write::<u8>() } - fn visit_u16(&self) -> bool { self.write::<u16>() } - fn visit_u32(&self) -> bool { self.write::<u32>() } - fn visit_u64(&self) -> bool { self.write::<u64>() } - - fn visit_float(&self) -> bool { self.write::<float>() } - fn visit_f32(&self) -> bool { self.write::<f32>() } - fn visit_f64(&self) -> bool { self.write::<f64>() } - - fn visit_char(&self) -> bool { - do self.get::<char> |&ch| { - self.writer.write_char('\''); - self.writer.write_escaped_char(ch); - self.writer.write_char('\''); - } - } - - fn visit_estr_box(&self) -> bool { - do self.get::<@str> |s| { - self.writer.write_char('@'); - self.write_escaped_slice(*s); - } - } - fn visit_estr_uniq(&self) -> bool { - do self.get::<~str> |s| { - self.writer.write_char('~'); - self.write_escaped_slice(*s); - } - } - fn visit_estr_slice(&self) -> bool { - do self.get::<&str> |s| { - self.write_escaped_slice(*s); - } - } - - // Type no longer exists, vestigial function. - fn visit_estr_fixed(&self, _n: uint, _sz: uint, - _align: uint) -> bool { fail!(); } - - fn visit_box(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.writer.write_char('@'); - self.write_mut_qualifier(mtbl); - do self.get::<&raw::Box<()>> |b| { - let p = ptr::to_unsafe_ptr(&b.data) as *c_void; - self.visit_ptr_inner(p, inner); - } - } - - fn visit_uniq(&self, _mtbl: uint, inner: *TyDesc) -> bool { - self.writer.write_char('~'); - do self.get::<*c_void> |b| { - self.visit_ptr_inner(*b, inner); - } - } - - fn visit_uniq_managed(&self, _mtbl: uint, inner: *TyDesc) -> bool { - self.writer.write_char('~'); - do self.get::<&raw::Box<()>> |b| { - let p = ptr::to_unsafe_ptr(&b.data) as *c_void; - self.visit_ptr_inner(p, inner); - } - } - - fn visit_ptr(&self, _mtbl: uint, _inner: *TyDesc) -> bool { - do self.get::<*c_void> |p| { - self.writer.write_str(fmt!("(0x%x as *())", - *p as uint)); - } - } - - fn visit_rptr(&self, mtbl: uint, inner: *TyDesc) -> bool { - self.writer.write_char('&'); - self.write_mut_qualifier(mtbl); - do self.get::<*c_void> |p| { - self.visit_ptr_inner(*p, inner); - } - } - - // Type no longer exists, vestigial function. - fn visit_vec(&self, _mtbl: uint, _inner: *TyDesc) -> bool { fail!(); } - - - fn visit_unboxed_vec(&self, mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<raw::Vec<()>> |b| { - self.write_unboxed_vec_repr(mtbl, b, inner); - } - } - - fn visit_evec_box(&self, mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<&raw::Box<raw::Vec<()>>> |b| { - self.writer.write_char('@'); - self.write_mut_qualifier(mtbl); - self.write_unboxed_vec_repr(mtbl, &b.data, inner); - } - } - - fn visit_evec_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<&raw::Vec<()>> |b| { - self.writer.write_char('~'); - self.write_unboxed_vec_repr(mtbl, *b, inner); - } - } - - fn visit_evec_uniq_managed(&self, mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<&raw::Box<raw::Vec<()>>> |b| { - self.writer.write_char('~'); - self.write_unboxed_vec_repr(mtbl, &b.data, inner); - } - } - - fn visit_evec_slice(&self, mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<raw::Slice<()>> |s| { - self.writer.write_char('&'); - self.write_vec_range(mtbl, s.data, s.len, inner); - } - } - - fn visit_evec_fixed(&self, _n: uint, sz: uint, _align: uint, - mtbl: uint, inner: *TyDesc) -> bool { - do self.get::<()> |b| { - self.write_vec_range(mtbl, ptr::to_unsafe_ptr(b), sz, inner); - } - } - - fn visit_enter_rec(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - self.writer.write_char('{'); - true - } - - fn visit_rec_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - if i != 0 { - self.writer.write_str(", "); - } - self.write_mut_qualifier(mtbl); - self.writer.write_str(name); - self.writer.write_str(": "); - self.visit_inner(inner); - true - } - - fn visit_leave_rec(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - self.writer.write_char('}'); - true - } - - fn visit_enter_class(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - self.writer.write_char('{'); - true - } - fn visit_class_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - if i != 0 { - self.writer.write_str(", "); - } - self.write_mut_qualifier(mtbl); - self.writer.write_str(name); - self.writer.write_str(": "); - self.visit_inner(inner); - true - } - fn visit_leave_class(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - self.writer.write_char('}'); - true - } - - fn visit_enter_tup(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - self.writer.write_char('('); - true - } - fn visit_tup_field(&self, i: uint, inner: *TyDesc) -> bool { - if i != 0 { - self.writer.write_str(", "); - } - self.visit_inner(inner); - true - } - fn visit_leave_tup(&self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { - if _n_fields == 1 { - self.writer.write_char(','); - } - self.writer.write_char(')'); - true - } - - fn visit_enter_enum(&self, - _n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> int, - _sz: uint, - _align: uint) -> bool { - let var_stk: &mut ~[VariantState] = self.var_stk; - let disr = unsafe { - get_disr(transmute(*self.ptr)) - }; - var_stk.push(SearchingFor(disr)); - true - } - - fn visit_enter_enum_variant(&self, _variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool { - let mut write = false; - match self.var_stk.pop() { - SearchingFor(sought) => { - if disr_val == sought { - self.var_stk.push(Matched); - write = true; - } else { - self.var_stk.push(SearchingFor(sought)); - } - } - Matched | AlreadyFound => { - self.var_stk.push(AlreadyFound); - } - } - - if write { - self.writer.write_str(name); - if n_fields > 0 { - self.writer.write_char('('); - } - } - true - } - - fn visit_enum_variant_field(&self, - i: uint, - _offset: uint, - inner: *TyDesc) - -> bool { - match self.var_stk[self.var_stk.len() - 1] { - Matched => { - if i != 0 { - self.writer.write_str(", "); - } - if ! self.visit_inner(inner) { - return false; - } - } - _ => () - } - true - } - - fn visit_leave_enum_variant(&self, _variant: uint, - _disr_val: int, - n_fields: uint, - _name: &str) -> bool { - match self.var_stk[self.var_stk.len() - 1] { - Matched => { - if n_fields > 0 { - self.writer.write_char(')'); - } - } - _ => () - } - true - } - - fn visit_leave_enum(&self, - _n_variants: uint, - _get_disr: extern unsafe fn(ptr: *Opaque) -> int, - _sz: uint, - _align: uint) - -> bool { - let var_stk: &mut ~[VariantState] = self.var_stk; - match var_stk.pop() { - SearchingFor(*) => fail!("enum value matched no variant"), - _ => true - } - } - - fn visit_enter_fn(&self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - fn visit_fn_input(&self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { - true - } - fn visit_fn_output(&self, _retstyle: uint, _inner: *TyDesc) -> bool { - true - } - fn visit_leave_fn(&self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - - - fn visit_trait(&self) -> bool { true } - fn visit_param(&self, _i: uint) -> bool { true } - fn visit_self(&self) -> bool { true } - fn visit_type(&self) -> bool { true } - - fn visit_opaque_box(&self) -> bool { - self.writer.write_char('@'); - do self.get::<&raw::Box<()>> |b| { - let p = ptr::to_unsafe_ptr(&b.data) as *c_void; - self.visit_ptr_inner(p, b.type_desc); - } - } - - fn visit_closure_ptr(&self, _ck: uint) -> bool { true } -} - -pub fn write_repr<T>(writer: @Writer, object: &T) { - unsafe { - let ptr = ptr::to_unsafe_ptr(object) as *c_void; - let tydesc = get_tydesc::<T>(); - let u = ReprVisitor(ptr, writer); - let v = reflect::MovePtrAdaptor(u); - visit_tydesc(tydesc, @v as @TyVisitor) - } -} - -#[cfg(test)] -struct P {a: int, b: float} - -#[test] -fn test_repr() { - - fn exact_test<T>(t: &T, e:&str) { - let s : &str = io::with_str_writer(|w| write_repr(w, t)); - if s != e { - error!("expected '%s', got '%s'", - e, s); - } - assert_eq!(s, e); - } - - exact_test(&10, "10"); - exact_test(&true, "true"); - exact_test(&false, "false"); - exact_test(&1.234, "1.234"); - exact_test(&(&"hello"), "\"hello\""); - exact_test(&(@"hello"), "@\"hello\""); - exact_test(&(~"he\u10f3llo"), "~\"he\\u10f3llo\""); - - exact_test(&(@10), "@10"); - exact_test(&(@mut 10), "@10"); // FIXME: #4210: incorrect - exact_test(&((@mut 10, 2)), "(@mut 10, 2)"); - exact_test(&(~10), "~10"); - exact_test(&(&10), "&10"); - let mut x = 10; - exact_test(&(&mut x), "&mut 10"); - exact_test(&(@mut [1, 2]), "@mut [1, 2]"); - - exact_test(&(1,), "(1,)"); - exact_test(&(@[1,2,3,4,5,6,7,8]), - "@[1, 2, 3, 4, 5, 6, 7, 8]"); - exact_test(&(@[1u8,2u8,3u8,4u8]), - "@[1, 2, 3, 4]"); - exact_test(&(@["hi", "there"]), - "@[\"hi\", \"there\"]"); - exact_test(&(~["hi", "there"]), - "~[\"hi\", \"there\"]"); - exact_test(&(&["hi", "there"]), - "&[\"hi\", \"there\"]"); - exact_test(&(P{a:10, b:1.234}), - "{a: 10, b: 1.234}"); - exact_test(&(@P{a:10, b:1.234}), - "@{a: 10, b: 1.234}"); - exact_test(&(~P{a:10, b:1.234}), - "~{a: 10, b: 1.234}"); - exact_test(&(10_u8, ~"hello"), - "(10, ~\"hello\")"); - exact_test(&(10_u16, ~"hello"), - "(10, ~\"hello\")"); - exact_test(&(10_u32, ~"hello"), - "(10, ~\"hello\")"); - exact_test(&(10_u64, ~"hello"), - "(10, ~\"hello\")"); - - struct Foo; - exact_test(&(~[Foo, Foo, Foo]), "~[{}, {}, {}]"); -} diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index afa8d3261fc..d8317c34f50 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -117,18 +117,6 @@ mod imp { } } - #[cfg(stage0)] - mod macro_hack { - #[macro_escape]; - macro_rules! externfn( - (fn $name:ident () $(-> $ret_ty:ty),*) => ( - extern { - fn $name() $(-> $ret_ty),*; - } - ) - ) - } - externfn!(fn rust_take_global_args_lock()) externfn!(fn rust_drop_global_args_lock()) externfn!(fn rust_get_global_args_ptr() -> *mut Option<~~[~str]>) diff --git a/src/libstd/rt/io/buffered.rs b/src/libstd/rt/io/buffered.rs index 579e581d87e..7988f640687 100644 --- a/src/libstd/rt/io/buffered.rs +++ b/src/libstd/rt/io/buffered.rs @@ -126,7 +126,7 @@ impl<R: Reader> Decorator<R> for BufferedReader<R> { /// Wraps a Writer and buffers output to it /// -/// NOTE: `BufferedWriter` will NOT flush its buffer when dropped. +/// Note that `BufferedWriter` will NOT flush its buffer when dropped. pub struct BufferedWriter<W> { priv inner: W, priv buf: ~[u8], @@ -204,7 +204,7 @@ impl<W: Reader> Reader for InternalBufferedWriter<W> { /// Wraps a Stream and buffers input and output to and from it /// -/// NOTE: `BufferedStream` will NOT flush its output buffer when dropped. +/// Note that `BufferedStream` will NOT flush its output buffer when dropped. // FIXME #9155 this should be a newtype struct pub struct BufferedStream<S> { priv inner: BufferedReader<InternalBufferedWriter<S>> diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 871b41039d1..6b405b0948a 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -260,6 +260,9 @@ pub use self::net::ip::IpAddr; pub use self::net::tcp::TcpListener; pub use self::net::tcp::TcpStream; pub use self::net::udp::UdpStream; +pub use self::pipe::PipeStream; +pub use self::pipe::UnboundPipeStream; +pub use self::process::Process; // Some extension traits that all Readers and Writers get. pub use self::extensions::ReaderUtil; @@ -269,6 +272,12 @@ pub use self::extensions::WriterByteConversions; /// Synchronous, non-blocking file I/O. pub mod file; +/// Synchronous, in-memory I/O. +pub mod pipe; + +/// Child process management. +pub mod process; + /// Synchronous, non-blocking network I/O. pub mod net; @@ -388,17 +397,13 @@ impl ToStr for IoErrorKind { // XXX: Can't put doc comments on macros // Raised by `I/O` operations on error. condition! { - // NOTE: this super::IoError should be IoError - // Change this next time the snapshot is updated. - pub io_error: super::IoError -> (); + pub io_error: IoError -> (); } // XXX: Can't put doc comments on macros // Raised by `read` on error condition! { - // NOTE: this super::IoError should be IoError - // Change this next time the snapshot it updated. - pub read_error: super::IoError -> (); + pub read_error: IoError -> (); } /// Helper for wrapper calls where you want to diff --git a/src/libstd/rt/io/pipe.rs b/src/libstd/rt/io/pipe.rs new file mode 100644 index 00000000000..7e6c59ffd0b --- /dev/null +++ b/src/libstd/rt/io/pipe.rs @@ -0,0 +1,76 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Synchronous, in-memory pipes. +//! +//! Currently these aren't particularly useful, there only exists bindings +//! enough so that pipes can be created to child processes. + +use prelude::*; +use super::{Reader, Writer}; +use rt::io::{io_error, read_error, EndOfFile}; +use rt::local::Local; +use rt::rtio::{RtioPipe, RtioPipeObject, IoFactoryObject, IoFactory}; +use rt::rtio::RtioUnboundPipeObject; + +pub struct PipeStream(RtioPipeObject); +pub struct UnboundPipeStream(~RtioUnboundPipeObject); + +impl PipeStream { + /// Creates a new pipe initialized, but not bound to any particular + /// source/destination + pub fn new() -> Option<UnboundPipeStream> { + let pipe = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).pipe_init(false) + }; + match pipe { + Ok(p) => Some(UnboundPipeStream(p)), + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } + } + + pub fn bind(inner: RtioPipeObject) -> PipeStream { + PipeStream(inner) + } +} + +impl Reader for PipeStream { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { + match (**self).read(buf) { + Ok(read) => Some(read), + Err(ioerr) => { + // EOF is indicated by returning None + if ioerr.kind != EndOfFile { + read_error::cond.raise(ioerr); + } + return None; + } + } + } + + fn eof(&mut self) -> bool { fail!() } +} + +impl Writer for PipeStream { + fn write(&mut self, buf: &[u8]) { + match (**self).write(buf) { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } + } + + fn flush(&mut self) { fail!() } +} diff --git a/src/libstd/rt/io/process.rs b/src/libstd/rt/io/process.rs new file mode 100644 index 00000000000..e92b0d3b7b5 --- /dev/null +++ b/src/libstd/rt/io/process.rs @@ -0,0 +1,278 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Bindings for executing child processes + +use prelude::*; + +use libc; +use rt::io; +use rt::io::io_error; +use rt::local::Local; +use rt::rtio::{RtioProcess, RtioProcessObject, IoFactoryObject, IoFactory}; + +pub struct Process { + priv handle: ~RtioProcessObject, + io: ~[Option<io::PipeStream>], +} + +/// This configuration describes how a new process should be spawned. This is +/// translated to libuv's own configuration +pub struct ProcessConfig<'self> { + /// Path to the program to run + program: &'self str, + + /// Arguments to pass to the program (doesn't include the program itself) + args: &'self [~str], + + /// Optional environment to specify for the program. If this is None, then + /// it will inherit the current process's environment. + env: Option<&'self [(~str, ~str)]>, + + /// Optional working directory for the new process. If this is None, then + /// the current directory of the running process is inherited. + cwd: Option<&'self str>, + + /// Any number of streams/file descriptors/pipes may be attached to this + /// process. This list enumerates the file descriptors and such for the + /// process to be spawned, and the file descriptors inherited will start at + /// 0 and go to the length of this array. + /// + /// Standard file descriptors are: + /// + /// 0 - stdin + /// 1 - stdout + /// 2 - stderr + io: ~[StdioContainer] +} + +/// Describes what to do with a standard io stream for a child process. +pub enum StdioContainer { + /// This stream will be ignored. This is the equivalent of attaching the + /// stream to `/dev/null` + Ignored, + + /// The specified file descriptor is inherited for the stream which it is + /// specified for. + InheritFd(libc::c_int), + + // XXX: these two shouldn't have libuv-specific implementation details + + /// The specified libuv stream is inherited for the corresponding file + /// descriptor it is assigned to. + // XXX: this needs to be thought out more. + //InheritStream(uv::net::StreamWatcher), + + /// Creates a pipe for the specified file descriptor which will be directed + /// into the previously-initialized pipe passed in. + /// + /// The first boolean argument is whether the pipe is readable, and the + /// second is whether it is writable. These properties are from the view of + /// the *child* process, not the parent process. + CreatePipe(io::UnboundPipeStream, + bool /* readable */, + bool /* writable */), +} + +impl Process { + /// Creates a new pipe initialized, but not bound to any particular + /// source/destination + pub fn new(config: ProcessConfig) -> Option<Process> { + let process = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).spawn(config) + }; + match process { + Ok((p, io)) => Some(Process{ + handle: p, + io: io.move_iter().map(|p| + p.map_move(|p| io::PipeStream::bind(p)) + ).collect() + }), + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } + } + + /// Returns the process id of this child process + pub fn id(&self) -> libc::pid_t { self.handle.id() } + + /// Sends the specified signal to the child process, returning whether the + /// signal could be delivered or not. + /// + /// Note that this is purely a wrapper around libuv's `uv_process_kill` + /// function. + /// + /// If the signal delivery fails, then the `io_error` condition is raised on + pub fn signal(&mut self, signal: int) { + match self.handle.kill(signal) { + Ok(()) => {} + Err(err) => { + io_error::cond.raise(err) + } + } + } + + /// Wait for the child to exit completely, returning the status that it + /// exited with. This function will continue to have the same return value + /// after it has been called at least once. + pub fn wait(&mut self) -> int { self.handle.wait() } +} + +impl Drop for Process { + fn drop(&mut self) { + // Close all I/O before exiting to ensure that the child doesn't wait + // forever to print some text or something similar. + for _ in range(0, self.io.len()) { + self.io.pop(); + } + + self.wait(); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::*; + + use rt::io::{Reader, Writer}; + use rt::io::pipe::*; + use str; + + #[test] + #[cfg(unix, not(android))] + fn smoke() { + let io = ~[]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"true"], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert_eq!(p.wait(), 0); + } + + #[test] + #[cfg(unix, not(android))] + fn smoke_failure() { + let io = ~[]; + let args = ProcessConfig { + program: "if-this-is-a-binary-then-the-world-has-ended", + args: [], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert!(p.wait() != 0); + } + + #[test] + #[cfg(unix, not(android))] + fn exit_reported_right() { + let io = ~[]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"exit 1"], + env: None, + cwd: None, + io: io, + }; + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert_eq!(p.wait(), 1); + } + + fn read_all(input: &mut Reader) -> ~str { + let mut ret = ~""; + let mut buf = [0, ..1024]; + loop { + match input.read(buf) { + None | Some(0) => { break } + Some(n) => { ret = ret + str::from_utf8(buf.slice_to(n)); } + } + } + return ret; + } + + fn run_output(args: ProcessConfig) -> ~str { + let p = Process::new(args); + assert!(p.is_some()); + let mut p = p.unwrap(); + assert!(p.io[0].is_none()); + assert!(p.io[1].is_some()); + let ret = read_all(p.io[1].get_mut_ref() as &mut Reader); + assert_eq!(p.wait(), 0); + return ret; + } + + #[test] + #[cfg(unix, not(android))] + fn stdout_works() { + let pipe = PipeStream::new().unwrap(); + let io = ~[Ignored, CreatePipe(pipe, false, true)]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"echo foobar"], + env: None, + cwd: None, + io: io, + }; + assert_eq!(run_output(args), ~"foobar\n"); + } + + #[test] + #[cfg(unix, not(android))] + fn set_cwd_works() { + let pipe = PipeStream::new().unwrap(); + let io = ~[Ignored, CreatePipe(pipe, false, true)]; + let cwd = Some("/"); + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"pwd"], + env: None, + cwd: cwd, + io: io, + }; + assert_eq!(run_output(args), ~"/\n"); + } + + #[test] + #[cfg(unix, not(android))] + fn stdin_works() { + let input = PipeStream::new().unwrap(); + let output = PipeStream::new().unwrap(); + let io = ~[CreatePipe(input, true, false), + CreatePipe(output, false, true)]; + let args = ProcessConfig { + program: "/bin/sh", + args: [~"-c", ~"read line; echo $line"], + env: None, + cwd: None, + io: io, + }; + let mut p = Process::new(args).expect("didn't create a proces?!"); + p.io[0].get_mut_ref().write("foobar".as_bytes()); + p.io[0] = None; // close stdin; + let out = read_all(p.io[1].get_mut_ref() as &mut Reader); + assert_eq!(p.wait(), 0); + assert_eq!(out, ~"foobar\n"); + } +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 53f62786b62..6df857b8d55 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -198,18 +198,6 @@ pub fn start_on_main_thread(argc: int, argv: **u8, crate_map: *u8, main: ~fn()) return exit_code; } -#[cfg(stage0)] -mod macro_hack { -#[macro_escape]; -macro_rules! externfn( - (fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => ( - extern { - fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*; - } - ) -) -} - /// One-time runtime initialization. /// /// Initializes global state, including frobbing diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index d05a3a26169..ca521c792dc 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -8,11 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use libc; use option::*; use result::*; use libc::c_int; use rt::io::IoError; +use super::io::process::ProcessConfig; use super::io::net::ip::{IpAddr, SocketAddr}; use rt::uv::uvio; use path::Path; @@ -31,6 +33,9 @@ pub type RtioTcpListenerObject = uvio::UvTcpListener; pub type RtioUdpSocketObject = uvio::UvUdpSocket; pub type RtioTimerObject = uvio::UvTimer; pub type PausibleIdleCallback = uvio::UvPausibleIdleCallback; +pub type RtioPipeObject = uvio::UvPipeStream; +pub type RtioUnboundPipeObject = uvio::UvUnboundPipe; +pub type RtioProcessObject = uvio::UvProcess; pub trait EventLoop { fn run(&mut self); @@ -79,6 +84,9 @@ pub trait IoFactory { fn fs_rmdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>; fn fs_readdir<P: PathLike>(&mut self, path: &P, flags: c_int) -> Result<~[Path], IoError>; + fn pipe_init(&mut self, ipc: bool) -> Result<~RtioUnboundPipeObject, IoError>; + fn spawn(&mut self, config: ProcessConfig) + -> Result<(~RtioProcessObject, ~[Option<RtioPipeObject>]), IoError>; } pub trait RtioTcpListener : RtioSocket { @@ -135,3 +143,14 @@ pub trait RtioFileStream { fn tell(&self) -> Result<u64, IoError>; fn flush(&mut self) -> Result<(), IoError>; } + +pub trait RtioProcess { + fn id(&self) -> libc::pid_t; + fn kill(&mut self, signal: int) -> Result<(), IoError>; + fn wait(&mut self) -> int; +} + +pub trait RtioPipe { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError>; + fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 09f5ee7febb..09bd89ec94a 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -444,17 +444,10 @@ impl Unwinder { } extern { - #[cfg(not(stage0))] #[rust_stack] fn rust_try(f: extern "C" fn(*c_void, *c_void), code: *c_void, data: *c_void) -> uintptr_t; - - #[cfg(stage0)] - #[rust_stack] - fn rust_try(f: *u8, - code: *c_void, - data: *c_void) -> uintptr_t; } } @@ -490,10 +483,10 @@ mod test { fn tls() { use local_data; do run_in_newsched_task() { - static key: local_data::Key<@~str> = &local_data::Key; + local_data_key!(key: @~str) local_data::set(key, @~"data"); assert!(*local_data::get(key, |k| k.map_move(|k| *k)).unwrap() == ~"data"); - static key2: local_data::Key<@~str> = &local_data::Key; + local_data_key!(key2: @~str) local_data::set(key2, @~"data"); assert!(*local_data::get(key2, |k| k.map_move(|k| *k)).unwrap() == ~"data"); } diff --git a/src/libstd/rt/uv/mod.rs b/src/libstd/rt/uv/mod.rs index 451d454d2d8..95b2059d538 100644 --- a/src/libstd/rt/uv/mod.rs +++ b/src/libstd/rt/uv/mod.rs @@ -58,6 +58,8 @@ pub use self::net::{StreamWatcher, TcpWatcher, UdpWatcher}; pub use self::idle::IdleWatcher; pub use self::timer::TimerWatcher; pub use self::async::AsyncWatcher; +pub use self::process::Process; +pub use self::pipe::Pipe; /// The implementation of `rtio` for libuv pub mod uvio; @@ -71,6 +73,8 @@ pub mod idle; pub mod timer; pub mod async; pub mod addrinfo; +pub mod process; +pub mod pipe; /// XXX: Loop(*handle) is buggy with destructors. Normal structs /// with dtors may not be destructured, but tuple structs can, @@ -127,6 +131,8 @@ pub type NullCallback = ~fn(); pub type IdleCallback = ~fn(IdleWatcher, Option<UvError>); pub type ConnectionCallback = ~fn(StreamWatcher, Option<UvError>); pub type FsCallback = ~fn(&mut FsRequest, Option<UvError>); +// first int is exit_status, second is term_signal +pub type ExitCallback = ~fn(Process, int, int, Option<UvError>); pub type TimerCallback = ~fn(TimerWatcher, Option<UvError>); pub type AsyncCallback = ~fn(AsyncWatcher, Option<UvError>); pub type UdpReceiveCallback = ~fn(UdpWatcher, int, Buf, SocketAddr, uint, Option<UvError>); @@ -145,7 +151,8 @@ struct WatcherData { timer_cb: Option<TimerCallback>, async_cb: Option<AsyncCallback>, udp_recv_cb: Option<UdpReceiveCallback>, - udp_send_cb: Option<UdpSendCallback> + udp_send_cb: Option<UdpSendCallback>, + exit_cb: Option<ExitCallback>, } pub trait WatcherInterop { @@ -177,7 +184,8 @@ impl<H, W: Watcher + NativeHandle<*H>> WatcherInterop for W { timer_cb: None, async_cb: None, udp_recv_cb: None, - udp_send_cb: None + udp_send_cb: None, + exit_cb: None, }; let data = transmute::<~WatcherData, *c_void>(data); uvll::set_data_for_uv_handle(self.native_handle(), data); diff --git a/src/libstd/rt/uv/pipe.rs b/src/libstd/rt/uv/pipe.rs new file mode 100644 index 00000000000..1147c731a60 --- /dev/null +++ b/src/libstd/rt/uv/pipe.rs @@ -0,0 +1,66 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; +use libc; + +use rt::uv; +use rt::uv::net; +use rt::uv::uvll; + +pub struct Pipe(*uvll::uv_pipe_t); + +impl uv::Watcher for Pipe {} + +impl Pipe { + pub fn new(loop_: &uv::Loop, ipc: bool) -> Pipe { + unsafe { + let handle = uvll::malloc_handle(uvll::UV_NAMED_PIPE); + assert!(handle.is_not_null()); + let ipc = ipc as libc::c_int; + assert_eq!(uvll::pipe_init(loop_.native_handle(), handle, ipc), 0); + let mut ret: Pipe = + uv::NativeHandle::from_native_handle(handle); + ret.install_watcher_data(); + ret + } + } + + pub fn as_stream(&self) -> net::StreamWatcher { + net::StreamWatcher(**self as *uvll::uv_stream_t) + } + + pub fn close(self, cb: uv::NullCallback) { + { + let mut this = self; + let data = this.get_watcher_data(); + assert!(data.close_cb.is_none()); + data.close_cb = Some(cb); + } + + unsafe { uvll::close(self.native_handle(), close_cb); } + + extern fn close_cb(handle: *uvll::uv_pipe_t) { + let mut process: Pipe = uv::NativeHandle::from_native_handle(handle); + process.get_watcher_data().close_cb.take_unwrap()(); + process.drop_watcher_data(); + unsafe { uvll::free_handle(handle as *libc::c_void) } + } + } +} + +impl uv::NativeHandle<*uvll::uv_pipe_t> for Pipe { + fn from_native_handle(handle: *uvll::uv_pipe_t) -> Pipe { + Pipe(handle) + } + fn native_handle(&self) -> *uvll::uv_pipe_t { + match self { &Pipe(ptr) => ptr } + } +} diff --git a/src/libstd/rt/uv/process.rs b/src/libstd/rt/uv/process.rs new file mode 100644 index 00000000000..ccfa1ff87db --- /dev/null +++ b/src/libstd/rt/uv/process.rs @@ -0,0 +1,219 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; +use cell::Cell; +use libc; +use ptr; +use util; +use vec; + +use rt::io::process::*; +use rt::uv; +use rt::uv::uvio::UvPipeStream; +use rt::uv::uvll; + +/// A process wraps the handle of the underlying uv_process_t. +pub struct Process(*uvll::uv_process_t); + +impl uv::Watcher for Process {} + +impl Process { + /// Creates a new process, ready to spawn inside an event loop + pub fn new() -> Process { + let handle = unsafe { uvll::malloc_handle(uvll::UV_PROCESS) }; + assert!(handle.is_not_null()); + let mut ret: Process = uv::NativeHandle::from_native_handle(handle); + ret.install_watcher_data(); + return ret; + } + + /// Spawn a new process inside the specified event loop. + /// + /// The `config` variable will be passed down to libuv, and the `exit_cb` + /// will be run only once, when the process exits. + /// + /// Returns either the corresponding process object or an error which + /// occurred. + pub fn spawn(&mut self, loop_: &uv::Loop, mut config: ProcessConfig, + exit_cb: uv::ExitCallback) + -> Result<~[Option<UvPipeStream>], uv::UvError> + { + let cwd = config.cwd.map_move(|s| s.to_c_str()); + + extern fn on_exit(p: *uvll::uv_process_t, + exit_status: libc::c_int, + term_signal: libc::c_int) { + let mut p: Process = uv::NativeHandle::from_native_handle(p); + let err = match exit_status { + 0 => None, + _ => uv::status_to_maybe_uv_error(-1) + }; + p.get_watcher_data().exit_cb.take_unwrap()(p, + exit_status as int, + term_signal as int, + err); + } + + let io = util::replace(&mut config.io, ~[]); + let mut stdio = vec::with_capacity::<uvll::uv_stdio_container_t>(io.len()); + let mut ret_io = vec::with_capacity(io.len()); + unsafe { + vec::raw::set_len(&mut stdio, io.len()); + for (slot, other) in stdio.iter().zip(io.move_iter()) { + let io = set_stdio(slot as *uvll::uv_stdio_container_t, other); + ret_io.push(io); + } + } + + let exit_cb = Cell::new(exit_cb); + let ret_io = Cell::new(ret_io); + do with_argv(config.program, config.args) |argv| { + do with_env(config.env) |envp| { + let options = uvll::uv_process_options_t { + exit_cb: on_exit, + file: unsafe { *argv }, + args: argv, + env: envp, + cwd: match cwd { + Some(ref cwd) => cwd.with_ref(|p| p), + None => ptr::null(), + }, + flags: 0, + stdio_count: stdio.len() as libc::c_int, + stdio: stdio.as_imm_buf(|p, _| p), + uid: 0, + gid: 0, + }; + + match unsafe { + uvll::spawn(loop_.native_handle(), **self, options) + } { + 0 => { + (*self).get_watcher_data().exit_cb = Some(exit_cb.take()); + Ok(ret_io.take()) + } + err => Err(uv::UvError(err)) + } + } + } + } + + /// Sends a signal to this process. + /// + /// This is a wrapper around `uv_process_kill` + pub fn kill(&self, signum: int) -> Result<(), uv::UvError> { + match unsafe { + uvll::process_kill(self.native_handle(), signum as libc::c_int) + } { + 0 => Ok(()), + err => Err(uv::UvError(err)) + } + } + + /// Returns the process id of a spawned process + pub fn pid(&self) -> libc::pid_t { + unsafe { uvll::process_pid(**self) as libc::pid_t } + } + + /// Closes this handle, invoking the specified callback once closed + pub fn close(self, cb: uv::NullCallback) { + { + let mut this = self; + let data = this.get_watcher_data(); + assert!(data.close_cb.is_none()); + data.close_cb = Some(cb); + } + + unsafe { uvll::close(self.native_handle(), close_cb); } + + extern fn close_cb(handle: *uvll::uv_process_t) { + let mut process: Process = uv::NativeHandle::from_native_handle(handle); + process.get_watcher_data().close_cb.take_unwrap()(); + process.drop_watcher_data(); + unsafe { uvll::free_handle(handle as *libc::c_void) } + } + } +} + +unsafe fn set_stdio(dst: *uvll::uv_stdio_container_t, + io: StdioContainer) -> Option<UvPipeStream> { + match io { + Ignored => { + uvll::set_stdio_container_flags(dst, uvll::STDIO_IGNORE); + None + } + InheritFd(fd) => { + uvll::set_stdio_container_flags(dst, uvll::STDIO_INHERIT_FD); + uvll::set_stdio_container_fd(dst, fd); + None + } + CreatePipe(pipe, readable, writable) => { + let mut flags = uvll::STDIO_CREATE_PIPE as libc::c_int; + if readable { + flags |= uvll::STDIO_READABLE_PIPE as libc::c_int; + } + if writable { + flags |= uvll::STDIO_WRITABLE_PIPE as libc::c_int; + } + let handle = pipe.pipe.as_stream().native_handle(); + uvll::set_stdio_container_flags(dst, flags); + uvll::set_stdio_container_stream(dst, handle); + Some(pipe.bind()) + } + } +} + +/// Converts the program and arguments to the argv array expected by libuv +fn with_argv<T>(prog: &str, args: &[~str], f: &fn(**libc::c_char) -> T) -> T { + // First, allocation space to put all the C-strings (we need to have + // ownership of them somewhere + let mut c_strs = vec::with_capacity(args.len() + 1); + c_strs.push(prog.to_c_str()); + for arg in args.iter() { + c_strs.push(arg.to_c_str()); + } + + // Next, create the char** array + let mut c_args = vec::with_capacity(c_strs.len() + 1); + for s in c_strs.iter() { + c_args.push(s.with_ref(|p| p)); + } + c_args.push(ptr::null()); + c_args.as_imm_buf(|buf, _| f(buf)) +} + +/// Converts the environment to the env array expected by libuv +fn with_env<T>(env: Option<&[(~str, ~str)]>, f: &fn(**libc::c_char) -> T) -> T { + let env = match env { + Some(s) => s, + None => { return f(ptr::null()); } + }; + // As with argv, create some temporary storage and then the actual array + let mut envp = vec::with_capacity(env.len()); + for &(ref key, ref value) in env.iter() { + envp.push(fmt!("%s=%s", *key, *value).to_c_str()); + } + let mut c_envp = vec::with_capacity(envp.len() + 1); + for s in envp.iter() { + c_envp.push(s.with_ref(|p| p)); + } + c_envp.push(ptr::null()); + c_envp.as_imm_buf(|buf, _| f(buf)) +} + +impl uv::NativeHandle<*uvll::uv_process_t> for Process { + fn from_native_handle(handle: *uvll::uv_process_t) -> Process { + Process(handle) + } + fn native_handle(&self) -> *uvll::uv_process_t { + match self { &Process(ptr) => ptr } + } +} diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 76dcf6daae6..ed6e16c8fdb 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -13,7 +13,7 @@ use cast::transmute; use cast; use cell::Cell; use clone::Clone; -use libc::{c_int, c_uint, c_void}; +use libc::{c_int, c_uint, c_void, pid_t}; use ops::Drop; use option::*; use ptr; @@ -22,6 +22,8 @@ use result::*; use rt::io::IoError; use rt::io::net::ip::{SocketAddr, IpAddr}; use rt::io::{standard_error, OtherIoError, SeekStyle, SeekSet, SeekCur, SeekEnd}; +use rt::io::process::ProcessConfig; +use rt::kill::BlockedTask; use rt::local::Local; use rt::rtio::*; use rt::sched::{Scheduler, SchedHandle}; @@ -735,6 +737,64 @@ impl IoFactory for UvIoFactory { assert!(!result_cell.is_empty()); return result_cell.take(); } + + fn pipe_init(&mut self, ipc: bool) -> Result<~RtioUnboundPipeObject, IoError> { + let home = get_handle_to_current_scheduler!(); + Ok(~UvUnboundPipe { pipe: Pipe::new(self.uv_loop(), ipc), home: home }) + } + + fn spawn(&mut self, config: ProcessConfig) + -> Result<(~RtioProcessObject, ~[Option<RtioPipeObject>]), IoError> + { + // Sadly, we must create the UvProcess before we actually call uv_spawn + // so that the exit_cb can close over it and notify it when the process + // has exited. + let mut ret = ~UvProcess { + process: Process::new(), + home: None, + exit_status: None, + term_signal: None, + exit_error: None, + descheduled: None, + }; + let ret_ptr = unsafe { + *cast::transmute::<&~UvProcess, &*mut UvProcess>(&ret) + }; + + // The purpose of this exit callback is to record the data about the + // exit and then wake up the task which may be waiting for the process + // to exit. This is all performed in the current io-loop, and the + // implementation of UvProcess ensures that reading these fields always + // occurs on the current io-loop. + let exit_cb: ExitCallback = |_, exit_status, term_signal, error| { + unsafe { + assert!((*ret_ptr).exit_status.is_none()); + (*ret_ptr).exit_status = Some(exit_status); + (*ret_ptr).term_signal = Some(term_signal); + (*ret_ptr).exit_error = error; + match (*ret_ptr).descheduled.take() { + Some(task) => { + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task); + } + None => {} + } + } + }; + + match ret.process.spawn(self.uv_loop(), config, exit_cb) { + Ok(io) => { + // Only now do we actually get a handle to this scheduler. + ret.home = Some(get_handle_to_current_scheduler!()); + Ok((ret, io)) + } + Err(uverr) => { + // We still need to close the process handle we created, but + // that's taken care for us in the destructor of UvProcess + Err(uv_error_to_io_error(uverr)) + } + } + } } pub struct UvTcpListener { @@ -856,6 +916,126 @@ impl RtioTcpAcceptor for UvTcpAcceptor { } } +fn read_stream(mut watcher: StreamWatcher, + scheduler: ~Scheduler, + buf: &mut [u8]) -> Result<uint, IoError> { + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell<Result<uint, IoError>> = &result_cell; + + let buf_ptr: *&mut [u8] = &buf; + do scheduler.deschedule_running_task_and_then |_sched, task| { + let task_cell = Cell::new(task); + // XXX: We shouldn't reallocate these callbacks every + // call to read + let alloc: AllocCallback = |_| unsafe { + slice_to_uv_buf(*buf_ptr) + }; + do watcher.read_start(alloc) |mut watcher, nread, _buf, status| { + + // Stop reading so that no read callbacks are + // triggered before the user calls `read` again. + // XXX: Is there a performance impact to calling + // stop here? + watcher.read_stop(); + + let result = if status.is_none() { + assert!(nread >= 0); + Ok(nread as uint) + } else { + Err(uv_error_to_io_error(status.unwrap())) + }; + + unsafe { (*result_cell_ptr).put_back(result); } + + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + result_cell.take() +} + +fn write_stream(mut watcher: StreamWatcher, + scheduler: ~Scheduler, + buf: &[u8]) -> Result<(), IoError> { + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell; + let buf_ptr: *&[u8] = &buf; + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; + do watcher.write(buf) |_watcher, status| { + let result = if status.is_none() { + Ok(()) + } else { + Err(uv_error_to_io_error(status.unwrap())) + }; + + unsafe { (*result_cell_ptr).put_back(result); } + + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } + } + + assert!(!result_cell.is_empty()); + result_cell.take() +} + +pub struct UvUnboundPipe { + pipe: Pipe, + home: SchedHandle, +} + +impl HomingIO for UvUnboundPipe { + fn home<'r>(&'r mut self) -> &'r mut SchedHandle { &mut self.home } +} + +impl Drop for UvUnboundPipe { + fn drop(&mut self) { + do self.home_for_io |self_| { + let scheduler: ~Scheduler = Local::take(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + do self_.pipe.close { + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } + } + } + } +} + +impl UvUnboundPipe { + pub unsafe fn bind(~self) -> UvPipeStream { + UvPipeStream { inner: self } + } +} + +pub struct UvPipeStream { + priv inner: ~UvUnboundPipe, +} + +impl UvPipeStream { + pub fn new(inner: ~UvUnboundPipe) -> UvPipeStream { + UvPipeStream { inner: inner } + } +} + +impl RtioPipe for UvPipeStream { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { + do self.inner.home_for_io_with_sched |self_, scheduler| { + read_stream(self_.pipe.as_stream(), scheduler, buf) + } + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + do self.inner.home_for_io_with_sched |self_, scheduler| { + write_stream(self_.pipe.as_stream(), scheduler, buf) + } + } +} + pub struct UvTcpStream { watcher: TcpWatcher, home: SchedHandle, @@ -890,70 +1070,13 @@ impl RtioSocket for UvTcpStream { impl RtioTcpStream for UvTcpStream { fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { do self.home_for_io_with_sched |self_, scheduler| { - let result_cell = Cell::new_empty(); - let result_cell_ptr: *Cell<Result<uint, IoError>> = &result_cell; - - let buf_ptr: *&mut [u8] = &buf; - do scheduler.deschedule_running_task_and_then |_sched, task| { - let task_cell = Cell::new(task); - // XXX: We shouldn't reallocate these callbacks every - // call to read - let alloc: AllocCallback = |_| unsafe { - slice_to_uv_buf(*buf_ptr) - }; - let mut watcher = self_.watcher.as_stream(); - do watcher.read_start(alloc) |mut watcher, nread, _buf, status| { - - // Stop reading so that no read callbacks are - // triggered before the user calls `read` again. - // XXX: Is there a performance impact to calling - // stop here? - watcher.read_stop(); - - let result = if status.is_none() { - assert!(nread >= 0); - Ok(nread as uint) - } else { - Err(uv_error_to_io_error(status.unwrap())) - }; - - unsafe { (*result_cell_ptr).put_back(result); } - - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - } - } - - assert!(!result_cell.is_empty()); - result_cell.take() + read_stream(self_.watcher.as_stream(), scheduler, buf) } } fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { do self.home_for_io_with_sched |self_, scheduler| { - let result_cell = Cell::new_empty(); - let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell; - let buf_ptr: *&[u8] = &buf; - do scheduler.deschedule_running_task_and_then |_, task| { - let task_cell = Cell::new(task); - let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; - let mut watcher = self_.watcher.as_stream(); - do watcher.write(buf) |_watcher, status| { - let result = if status.is_none() { - Ok(()) - } else { - Err(uv_error_to_io_error(status.unwrap())) - }; - - unsafe { (*result_cell_ptr).put_back(result); } - - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - } - } - - assert!(!result_cell.is_empty()); - result_cell.take() + write_stream(self_.watcher.as_stream(), scheduler, buf) } } @@ -1240,8 +1363,7 @@ impl UvTimer { impl Drop for UvTimer { fn drop(&mut self) { - let self_ = unsafe { transmute::<&UvTimer, &mut UvTimer>(self) }; - do self_.home_for_io_with_sched |self_, scheduler| { + do self.home_for_io_with_sched |self_, scheduler| { rtdebug!("closing UvTimer"); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); @@ -1356,13 +1478,12 @@ impl UvFileStream { impl Drop for UvFileStream { fn drop(&mut self) { - let self_ = unsafe { transmute::<&UvFileStream, &mut UvFileStream>(self) }; if self.close_on_drop { - do self_.home_for_io_with_sched |self_, scheduler| { + do self.home_for_io_with_sched |self_, scheduler| { do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let close_req = file::FsRequest::new(); - do close_req.close(&self.loop_, self_.fd) |_,_| { + do close_req.close(&self_.loop_, self_.fd) |_,_| { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; @@ -1405,6 +1526,86 @@ impl RtioFileStream for UvFileStream { } } +pub struct UvProcess { + process: process::Process, + + // Sadly, this structure must be created before we return it, so in that + // brief interim the `home` is None. + home: Option<SchedHandle>, + + // All None until the process exits (exit_error may stay None) + priv exit_status: Option<int>, + priv term_signal: Option<int>, + priv exit_error: Option<UvError>, + + // Used to store which task to wake up from the exit_cb + priv descheduled: Option<BlockedTask>, +} + +impl HomingIO for UvProcess { + fn home<'r>(&'r mut self) -> &'r mut SchedHandle { self.home.get_mut_ref() } +} + +impl Drop for UvProcess { + fn drop(&mut self) { + let close = |self_: &mut UvProcess| { + let scheduler: ~Scheduler = Local::take(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task = Cell::new(task); + do self_.process.close { + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task.take()); + } + } + }; + + // If home is none, then this process never actually successfully + // spawned, so there's no need to switch event loops + if self.home.is_none() { + close(self) + } else { + self.home_for_io(close) + } + } +} + +impl RtioProcess for UvProcess { + fn id(&self) -> pid_t { + self.process.pid() + } + + fn kill(&mut self, signal: int) -> Result<(), IoError> { + do self.home_for_io |self_| { + match self_.process.kill(signal) { + Ok(()) => Ok(()), + Err(uverr) => Err(uv_error_to_io_error(uverr)) + } + } + } + + fn wait(&mut self) -> int { + // Make sure (on the home scheduler) that we have an exit status listed + do self.home_for_io |self_| { + match self_.exit_status { + Some(*) => {} + None => { + // If there's no exit code previously listed, then the + // process's exit callback has yet to be invoked. We just + // need to deschedule ourselves and wait to be reawoken. + let scheduler: ~Scheduler = Local::take(); + do scheduler.deschedule_running_task_and_then |_, task| { + assert!(self_.descheduled.is_none()); + self_.descheduled = Some(task); + } + assert!(self_.exit_status.is_some()); + } + } + } + + self.exit_status.unwrap() + } +} + #[test] fn test_simple_io_no_connect() { do run_in_mt_newsched_task { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 42102a52e2e..790bf53a291 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -31,7 +31,6 @@ use c_str::ToCStr; use libc::{size_t, c_int, c_uint, c_void, c_char, uintptr_t}; -#[cfg(not(stage0))] use libc::ssize_t; use libc::{malloc, free}; use libc; @@ -67,6 +66,19 @@ pub mod errors { pub static EPIPE: c_int = -libc::EPIPE; } +pub static PROCESS_SETUID: c_int = 1 << 0; +pub static PROCESS_SETGID: c_int = 1 << 1; +pub static PROCESS_WINDOWS_VERBATIM_ARGUMENTS: c_int = 1 << 2; +pub static PROCESS_DETACHED: c_int = 1 << 3; +pub static PROCESS_WINDOWS_HIDE: c_int = 1 << 4; + +pub static STDIO_IGNORE: c_int = 0x00; +pub static STDIO_CREATE_PIPE: c_int = 0x01; +pub static STDIO_INHERIT_FD: c_int = 0x02; +pub static STDIO_INHERIT_STREAM: c_int = 0x04; +pub static STDIO_READABLE_PIPE: c_int = 0x10; +pub static STDIO_WRITABLE_PIPE: c_int = 0x20; + // see libuv/include/uv-unix.h #[cfg(unix)] pub struct uv_buf_t { @@ -81,6 +93,26 @@ pub struct uv_buf_t { base: *u8, } +pub struct uv_process_options_t { + exit_cb: uv_exit_cb, + file: *libc::c_char, + args: **libc::c_char, + env: **libc::c_char, + cwd: *libc::c_char, + flags: libc::c_uint, + stdio_count: libc::c_int, + stdio: *uv_stdio_container_t, + uid: uv_uid_t, + gid: uv_gid_t, +} + +// These fields are private because they must be interfaced with through the +// functions below. +pub struct uv_stdio_container_t { + priv flags: libc::c_int, + priv stream: *uv_stream_t, +} + pub type uv_handle_t = c_void; pub type uv_loop_t = c_void; pub type uv_idle_t = c_void; @@ -95,6 +127,8 @@ pub type uv_stream_t = c_void; pub type uv_fs_t = c_void; pub type uv_udp_send_t = c_void; pub type uv_getaddrinfo_t = c_void; +pub type uv_process_t = c_void; +pub type uv_pipe_t = c_void; pub struct uv_timespec_t { tv_sec: libc::c_long, @@ -149,76 +183,39 @@ impl uv_stat_t { } } -#[cfg(stage0)] -pub type uv_idle_cb = *u8; -#[cfg(stage0)] -pub type uv_alloc_cb = *u8; -#[cfg(stage0)] -pub type uv_read_cb = *u8; -#[cfg(stage0)] -pub type uv_udp_send_cb = *u8; -#[cfg(stage0)] -pub type uv_udp_recv_cb = *u8; -#[cfg(stage0)] -pub type uv_close_cb = *u8; -#[cfg(stage0)] -pub type uv_walk_cb = *u8; -#[cfg(stage0)] -pub type uv_async_cb = *u8; -#[cfg(stage0)] -pub type uv_connect_cb = *u8; -#[cfg(stage0)] -pub type uv_connection_cb = *u8; -#[cfg(stage0)] -pub type uv_timer_cb = *u8; -#[cfg(stage0)] -pub type uv_write_cb = *u8; -#[cfg(stage0)] -pub type uv_getaddrinfo_cb = *u8; - -#[cfg(not(stage0))] pub type uv_idle_cb = extern "C" fn(handle: *uv_idle_t, status: c_int); -#[cfg(not(stage0))] pub type uv_alloc_cb = extern "C" fn(stream: *uv_stream_t, suggested_size: size_t) -> uv_buf_t; -#[cfg(not(stage0))] pub type uv_read_cb = extern "C" fn(stream: *uv_stream_t, nread: ssize_t, buf: uv_buf_t); -#[cfg(not(stage0))] pub type uv_udp_send_cb = extern "C" fn(req: *uv_udp_send_t, status: c_int); -#[cfg(not(stage0))] pub type uv_udp_recv_cb = extern "C" fn(handle: *uv_udp_t, nread: ssize_t, buf: uv_buf_t, addr: *sockaddr, flags: c_uint); -#[cfg(not(stage0))] pub type uv_close_cb = extern "C" fn(handle: *uv_handle_t); -#[cfg(not(stage0))] pub type uv_walk_cb = extern "C" fn(handle: *uv_handle_t, arg: *c_void); -#[cfg(not(stage0))] pub type uv_async_cb = extern "C" fn(handle: *uv_async_t, status: c_int); -#[cfg(not(stage0))] pub type uv_connect_cb = extern "C" fn(handle: *uv_connect_t, status: c_int); -#[cfg(not(stage0))] pub type uv_connection_cb = extern "C" fn(handle: *uv_connection_t, status: c_int); -#[cfg(not(stage0))] pub type uv_timer_cb = extern "C" fn(handle: *uv_timer_t, status: c_int); -#[cfg(not(stage0))] pub type uv_write_cb = extern "C" fn(handle: *uv_write_t, status: c_int); -#[cfg(not(stage0))] pub type uv_getaddrinfo_cb = extern "C" fn(req: *uv_getaddrinfo_t, status: c_int, res: *addrinfo); +pub type uv_exit_cb = extern "C" fn(handle: *uv_process_t, + exit_status: c_int, + term_signal: c_int); pub type sockaddr = c_void; pub type sockaddr_in = c_void; @@ -267,6 +264,11 @@ pub struct addrinfo { ai_next: *addrinfo } +#[cfg(unix)] pub type uv_uid_t = libc::types::os::arch::posix88::uid_t; +#[cfg(unix)] pub type uv_gid_t = libc::types::os::arch::posix88::gid_t; +#[cfg(windows)] pub type uv_uid_t = libc::c_uchar; +#[cfg(windows)] pub type uv_gid_t = libc::c_uchar; + #[deriving(Eq)] pub enum uv_handle_type { UV_UNKNOWN_HANDLE, @@ -828,6 +830,45 @@ pub unsafe fn fs_req_cleanup(req: *uv_fs_t) { rust_uv_fs_req_cleanup(req); } +pub unsafe fn spawn(loop_ptr: *c_void, result: *uv_process_t, + options: uv_process_options_t) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + return rust_uv_spawn(loop_ptr, result, options); +} + +pub unsafe fn process_kill(p: *uv_process_t, signum: c_int) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + return rust_uv_process_kill(p, signum); +} + +pub unsafe fn process_pid(p: *uv_process_t) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + return rust_uv_process_pid(p); +} + +pub unsafe fn set_stdio_container_flags(c: *uv_stdio_container_t, + flags: libc::c_int) { + #[fixed_stack_segment]; #[inline(never)]; + rust_set_stdio_container_flags(c, flags); +} + +pub unsafe fn set_stdio_container_fd(c: *uv_stdio_container_t, + fd: libc::c_int) { + #[fixed_stack_segment]; #[inline(never)]; + rust_set_stdio_container_fd(c, fd); +} + +pub unsafe fn set_stdio_container_stream(c: *uv_stdio_container_t, + stream: *uv_stream_t) { + #[fixed_stack_segment]; #[inline(never)]; + rust_set_stdio_container_stream(c, stream); +} + +pub unsafe fn pipe_init(loop_ptr: *c_void, p: *uv_pipe_t, ipc: c_int) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + rust_uv_pipe_init(loop_ptr, p, ipc) +} + // data access helpers pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int { #[fixed_stack_segment]; #[inline(never)]; @@ -1050,4 +1091,13 @@ extern { node: *c_char, service: *c_char, hints: *addrinfo) -> c_int; fn rust_uv_freeaddrinfo(ai: *addrinfo); + fn rust_uv_spawn(loop_ptr: *c_void, outptr: *uv_process_t, + options: uv_process_options_t) -> c_int; + fn rust_uv_process_kill(p: *uv_process_t, signum: c_int) -> c_int; + fn rust_uv_process_pid(p: *uv_process_t) -> c_int; + fn rust_set_stdio_container_flags(c: *uv_stdio_container_t, flags: c_int); + fn rust_set_stdio_container_fd(c: *uv_stdio_container_t, fd: c_int); + fn rust_set_stdio_container_stream(c: *uv_stdio_container_t, + stream: *uv_stream_t); + fn rust_uv_pipe_init(loop_ptr: *c_void, p: *uv_pipe_t, ipc: c_int) -> c_int; } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 83646dc59b3..362eab17fe7 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -643,15 +643,28 @@ fn spawn_process_os(prog: &str, args: &[~str], use libc::funcs::bsd44::getdtablesize; mod rustrt { - use libc::c_void; - #[abi = "cdecl"] extern { pub fn rust_unset_sigprocmask(); - pub fn rust_set_environ(envp: *c_void); } } + #[cfg(windows)] + unsafe fn set_environ(_envp: *c_void) {} + #[cfg(target_os = "macos")] + unsafe fn set_environ(envp: *c_void) { + externfn!(fn _NSGetEnviron() -> *mut *c_void); + + *_NSGetEnviron() = envp; + } + #[cfg(not(target_os = "macos"), not(windows))] + unsafe fn set_environ(envp: *c_void) { + extern { + static mut environ: *c_void; + } + environ = envp; + } + unsafe { let pid = fork(); @@ -685,7 +698,7 @@ fn spawn_process_os(prog: &str, args: &[~str], do with_envp(env) |envp| { if !envp.is_null() { - rustrt::rust_set_environ(envp); + set_environ(envp); } do with_argv(prog, args) |argv| { execvp(*argv, argv); @@ -736,8 +749,6 @@ fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T { let mut tmps = vec::with_capacity(env.len()); for pair in env.iter() { - // Use of match here is just to workaround limitations - // in the stage0 irrefutable pattern impl. let kv = fmt!("%s=%s", pair.first(), pair.second()); tmps.push(kv.to_c_str()); } diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 05433c47059..e9d5dd416ad 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -179,14 +179,8 @@ pub mod run; pub mod sys; pub mod cast; pub mod fmt; -#[cfg(stage0)] #[path = "repr_stage0.rs"] -pub mod repr; -#[cfg(not(stage0))] pub mod repr; pub mod cleanup; -#[cfg(stage0)] #[path = "reflect_stage0.rs"] -pub mod reflect; -#[cfg(not(stage0))] pub mod reflect; pub mod condition; pub mod logging; diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 93cac8797bb..9707d592a2e 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -1107,8 +1107,8 @@ pub mod raw { Some(limit) => (true, limit), None => (false, 0) }; - while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char - && ((limited_count && ctr < limit) || !limited_count)) { + while(((limited_count && ctr < limit) || !limited_count) + && *(curr_ptr as *libc::c_char) != 0 as libc::c_char) { let env_pair = from_c_str( curr_ptr as *libc::c_char); result.push(env_pair); diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs index 3d35de0f898..c315c3f9dfc 100644 --- a/src/libstd/sys.rs +++ b/src/libstd/sys.rs @@ -14,8 +14,6 @@ use c_str::ToCStr; use cast; -#[cfg(stage0)] -use io; use libc; use libc::{c_char, size_t}; use repr; @@ -92,7 +90,6 @@ pub fn refcount<T>(t: @T) -> uint { } } -#[cfg(not(stage0))] pub fn log_str<T>(t: &T) -> ~str { use rt::io; use rt::io::Decorator; @@ -101,12 +98,6 @@ pub fn log_str<T>(t: &T) -> ~str { repr::write_repr(&mut result as &mut io::Writer, t); str::from_utf8_owned(result.inner()) } -#[cfg(stage0)] -pub fn log_str<T>(t: &T) -> ~str { - do io::with_str_writer |w| { - repr::write_repr(w, t) - } -} /// Trait for initiating task failure. pub trait FailWithCause { diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs index c3791d18b38..349739a5ea6 100644 --- a/src/libstd/unstable/intrinsics.rs +++ b/src/libstd/unstable/intrinsics.rs @@ -76,103 +76,7 @@ pub struct TyDesc { pub enum Opaque { } #[lang="ty_visitor"] -#[cfg(not(test), stage0)] -pub trait TyVisitor { - fn visit_bot(&self) -> bool; - fn visit_nil(&self) -> bool; - fn visit_bool(&self) -> bool; - - fn visit_int(&self) -> bool; - fn visit_i8(&self) -> bool; - fn visit_i16(&self) -> bool; - fn visit_i32(&self) -> bool; - fn visit_i64(&self) -> bool; - - fn visit_uint(&self) -> bool; - fn visit_u8(&self) -> bool; - fn visit_u16(&self) -> bool; - fn visit_u32(&self) -> bool; - fn visit_u64(&self) -> bool; - - fn visit_float(&self) -> bool; - fn visit_f32(&self) -> bool; - fn visit_f64(&self) -> bool; - - fn visit_char(&self) -> bool; - - fn visit_estr_box(&self) -> bool; - fn visit_estr_uniq(&self) -> bool; - fn visit_estr_slice(&self) -> bool; - fn visit_estr_fixed(&self, n: uint, sz: uint, align: uint) -> bool; - - fn visit_box(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_uniq_managed(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_ptr(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_rptr(&self, mtbl: uint, inner: *TyDesc) -> bool; - - fn visit_vec(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_unboxed_vec(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_evec_box(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_evec_uniq(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_evec_uniq_managed(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_evec_slice(&self, mtbl: uint, inner: *TyDesc) -> bool; - fn visit_evec_fixed(&self, n: uint, sz: uint, align: uint, - mtbl: uint, inner: *TyDesc) -> bool; - - fn visit_enter_rec(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - fn visit_rec_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool; - fn visit_leave_rec(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - - fn visit_enter_class(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - fn visit_class_field(&self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool; - fn visit_leave_class(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - - fn visit_enter_tup(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - fn visit_tup_field(&self, i: uint, inner: *TyDesc) -> bool; - fn visit_leave_tup(&self, n_fields: uint, - sz: uint, align: uint) -> bool; - - fn visit_enter_enum(&self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> int, - sz: uint, align: uint) -> bool; - fn visit_enter_enum_variant(&self, variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool; - fn visit_enum_variant_field(&self, i: uint, offset: uint, inner: *TyDesc) -> bool; - fn visit_leave_enum_variant(&self, variant: uint, - disr_val: int, - n_fields: uint, - name: &str) -> bool; - fn visit_leave_enum(&self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> int, - sz: uint, align: uint) -> bool; - - fn visit_enter_fn(&self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool; - fn visit_fn_input(&self, i: uint, mode: uint, inner: *TyDesc) -> bool; - fn visit_fn_output(&self, retstyle: uint, inner: *TyDesc) -> bool; - fn visit_leave_fn(&self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool; - - fn visit_trait(&self) -> bool; - fn visit_param(&self, i: uint) -> bool; - fn visit_self(&self) -> bool; - fn visit_type(&self) -> bool; - fn visit_opaque_box(&self) -> bool; - fn visit_closure_ptr(&self, ck: uint) -> bool; -} - -#[lang="ty_visitor"] -#[cfg(not(test), not(stage0))] +#[cfg(not(test))] pub trait TyVisitor { fn visit_bot(&mut self) -> bool; fn visit_nil(&mut self) -> bool; @@ -424,9 +328,6 @@ extern "rust-intrinsic" { /// Returns `true` if a type is managed (will be allocated on the local heap) pub fn contains_managed<T>() -> bool; - #[cfg(stage0)] - pub fn visit_tydesc(td: *TyDesc, tv: &TyVisitor); - #[cfg(not(stage0))] pub fn visit_tydesc(td: *TyDesc, tv: &mut TyVisitor); pub fn frame_address(f: &once fn(*u8)); diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index 1dafeb27a0e..c2ef2300fc2 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -411,23 +411,6 @@ impl<T:Send> Exclusive<T> { } } -#[cfg(stage0)] -mod macro_hack { -#[macro_escape]; -macro_rules! externfn( - (fn $name:ident () $(-> $ret_ty:ty),*) => ( - extern { - fn $name() $(-> $ret_ty),*; - } - ); - (fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => ( - extern { - fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*; - } - ) -) -} - externfn!(fn rust_create_little_lock() -> rust_little_lock) externfn!(fn rust_destroy_little_lock(lock: rust_little_lock)) externfn!(fn rust_lock_little_lock(lock: rust_little_lock)) diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index cbc6b604bb7..f0f86911f50 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -804,7 +804,7 @@ pub fn new_sctable_internal() -> SCTable { // fetch the SCTable from TLS, create one if it doesn't yet exist. pub fn get_sctable() -> @mut SCTable { - static sctable_key: local_data::Key<@@mut SCTable> = &local_data::Key; + local_data_key!(sctable_key: @@mut SCTable) match local_data::get(sctable_key, |k| k.map_move(|k| *k)) { None => { let new_table = @@mut new_sctable_internal(); @@ -841,7 +841,7 @@ pub type ResolveTable = HashMap<(Name,SyntaxContext),Name>; // okay, I admit, putting this in TLS is not so nice: // fetch the SCTable from TLS, create one if it doesn't yet exist. pub fn get_resolve_table() -> @mut ResolveTable { - static resolve_table_key: local_data::Key<@@mut ResolveTable> = &local_data::Key; + local_data_key!(resolve_table_key: @@mut ResolveTable) match local_data::get(resolve_table_key, |k| k.map(|&k| *k)) { None => { let new_table = @@mut HashMap::new(); diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs index 536267a2235..5e9714ca5b2 100644 --- a/src/libsyntax/diagnostic.rs +++ b/src/libsyntax/diagnostic.rs @@ -187,7 +187,7 @@ fn diagnosticcolor(lvl: level) -> term::color::Color { } fn print_maybe_styled(msg: &str, color: term::attr::Attr) { - static tls_terminal: local_data::Key<@Option<term::Terminal>> = &local_data::Key; + local_data_key!(tls_terminal: @Option<term::Terminal>) let stderr = io::stderr(); diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 6cb5a93a313..889c2a5976e 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -644,25 +644,11 @@ impl AstBuilder for @ExtCtxt { self.expr(span, ast::ExprFnBlock(fn_decl, blk)) } - #[cfg(stage0)] - fn lambda0(&self, _span: Span, blk: ast::Block) -> @ast::Expr { - let ext_cx = *self; - let blk_e = self.expr(blk.span, ast::ExprBlock(blk.clone())); - quote_expr!(|| $blk_e ) - } - #[cfg(not(stage0))] fn lambda0(&self, _span: Span, blk: ast::Block) -> @ast::Expr { let blk_e = self.expr(blk.span, ast::ExprBlock(blk.clone())); quote_expr!(*self, || $blk_e ) } - #[cfg(stage0)] - fn lambda1(&self, _span: Span, blk: ast::Block, ident: ast::Ident) -> @ast::Expr { - let ext_cx = *self; - let blk_e = self.expr(blk.span, ast::ExprBlock(blk.clone())); - quote_expr!(|$ident| $blk_e ) - } - #[cfg(not(stage0))] fn lambda1(&self, _span: Span, blk: ast::Block, ident: ast::Ident) -> @ast::Expr { let blk_e = self.expr(blk.span, ast::ExprBlock(blk.clone())); quote_expr!(*self, |$ident| $blk_e ) diff --git a/src/libsyntax/ext/deriving/default.rs b/src/libsyntax/ext/deriving/default.rs index 0c7bbefc690..d8c78842808 100644 --- a/src/libsyntax/ext/deriving/default.rs +++ b/src/libsyntax/ext/deriving/default.rs @@ -47,9 +47,7 @@ fn default_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Exp cx.ident_of("Default"), cx.ident_of("default") ]; - let default_call = || { - cx.expr_call_global(span, default_ident.clone(), ~[]) - }; + let default_call = cx.expr_call_global(span, default_ident.clone(), ~[]); return match *substr.fields { StaticStruct(_, ref summary) => { @@ -58,13 +56,13 @@ fn default_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Exp if count == 0 { cx.expr_ident(span, substr.type_ident) } else { - let exprs = vec::from_fn(count, |_| default_call()); + let exprs = vec::from_elem(count, default_call); cx.expr_call_ident(span, substr.type_ident, exprs) } } Right(ref fields) => { let default_fields = do fields.map |ident| { - cx.field_imm(span, *ident, default_call()) + cx.field_imm(span, *ident, default_call) }; cx.expr_struct_ident(span, substr.type_ident, default_fields) } diff --git a/src/libsyntax/ext/deriving/zero.rs b/src/libsyntax/ext/deriving/zero.rs index fc527d44b53..2546cfc2e39 100644 --- a/src/libsyntax/ext/deriving/zero.rs +++ b/src/libsyntax/ext/deriving/zero.rs @@ -62,9 +62,7 @@ fn zero_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr { cx.ident_of("Zero"), cx.ident_of("zero") ]; - let zero_call = || { - cx.expr_call_global(span, zero_ident.clone(), ~[]) - }; + let zero_call = cx.expr_call_global(span, zero_ident.clone(), ~[]); return match *substr.fields { StaticStruct(_, ref summary) => { @@ -73,13 +71,13 @@ fn zero_substructure(cx: @ExtCtxt, span: Span, substr: &Substructure) -> @Expr { if count == 0 { cx.expr_ident(span, substr.type_ident) } else { - let exprs = vec::from_fn(count, |_| zero_call()); + let exprs = vec::from_elem(count, zero_call); cx.expr_call_ident(span, substr.type_ident, exprs) } } Right(ref fields) => { let zero_fields = do fields.map |ident| { - cx.field_imm(span, *ident, zero_call()) + cx.field_imm(span, *ident, zero_call) }; cx.expr_struct_ident(span, substr.type_ident, zero_fields) } diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index ac8a7d513dd..e97af9cbfb1 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -22,18 +22,6 @@ use ext::build::AstBuilder; use std::os; -#[cfg(stage0)] -pub fn expand_option_env(ext_cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) - -> base::MacResult { - let var = get_single_str_from_tts(ext_cx, sp, tts, "option_env!"); - - let e = match os::getenv(var) { - None => quote_expr!(::std::option::None::<&'static str>), - Some(s) => quote_expr!(::std::option::Some($s)) - }; - MRExpr(e) -} -#[cfg(not(stage0))] pub fn expand_option_env(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult { let var = get_single_str_from_tts(cx, sp, tts, "option_env!"); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index ac094c27a81..0bee7895420 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -863,9 +863,7 @@ pub fn std_macros() -> @str { use super::*; - static key: ::std::local_data::Key< - @::std::condition::Handler<$input, $out>> = - &::std::local_data::Key; + local_data_key!(key: @::std::condition::Handler<$input, $out>) pub static cond : ::std::condition::Condition<$input,$out> = @@ -884,9 +882,7 @@ pub fn std_macros() -> @str { use super::*; - static key: ::std::local_data::Key< - @::std::condition::Handler<$input, $out>> = - &::std::local_data::Key; + local_data_key!(key: @::std::condition::Handler<$input, $out>) pub static cond : ::std::condition::Condition<$input,$out> = @@ -898,42 +894,6 @@ pub fn std_macros() -> @str { } ) - // - // A scheme-style conditional that helps to improve code clarity in some instances when - // the `if`, `else if`, and `else` keywords obscure predicates undesirably. - // - // # Example - // - // ~~~ - // let clamped = - // if x > mx { mx } - // else if x < mn { mn } - // else { x }; - // ~~~ - // - // Using `cond!`, the above could be written as: - // - // ~~~ - // let clamped = cond!( - // (x > mx) { mx } - // (x < mn) { mn } - // _ { x } - // ); - // ~~~ - // - // The optional default case is denoted by `_`. - // - macro_rules! cond ( - ( $(($pred:expr) $body:block)+ _ $default:block ) => ( - $(if $pred $body else)+ - $default - ); - // for if the default case was ommitted - ( $(($pred:expr) $body:block)+ ) => ( - $(if $pred $body)else+ - ); - ) - // NOTE(acrichto): start removing this after the next snapshot macro_rules! printf ( ($arg:expr) => ( @@ -975,8 +935,6 @@ pub fn std_macros() -> @str { ($($arg:tt)*) => (::std::io::println(format!($($arg)*))) ) - // NOTE: use this after a snapshot lands to abstract the details - // of the TLS interface. macro_rules! local_data_key ( ($name:ident: $ty:ty) => ( static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key; diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 9f4e55b1a92..cace4648df2 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -76,6 +76,7 @@ impl Context { self.ecx.span_err(sp, "expected token: `,`"); return (extra, None); } + if *p.token == token::EOF { break } // accept trailing commas if named || (token::is_ident(p.token) && p.look_ahead(1, |t| *t == token::EQ)) { named = true; diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs index c267a673fce..0bc9e619274 100644 --- a/src/libsyntax/parse/lexer.rs +++ b/src/libsyntax/parse/lexer.rs @@ -699,6 +699,7 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token { '\\' => { c2 = '\\'; } '\'' => { c2 = '\''; } '"' => { c2 = '"'; } + '0' => { c2 = '\x00'; } 'x' => { c2 = scan_numeric_escape(rdr, 2u); } 'u' => { c2 = scan_numeric_escape(rdr, 4u); } 'U' => { c2 = scan_numeric_escape(rdr, 8u); } @@ -738,6 +739,7 @@ fn next_token_inner(rdr: @mut StringReader) -> token::Token { '\'' => accum_str.push_char('\''), '"' => accum_str.push_char('"'), '\n' => consume_whitespace(rdr), + '0' => accum_str.push_char('\x00'), 'x' => { accum_str.push_char(scan_numeric_escape(rdr, 2u)); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index fa00b536837..a8df737e49f 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -490,8 +490,7 @@ fn mk_fresh_ident_interner() -> @ident_interner { // if an interner exists in TLS, return it. Otherwise, prepare a // fresh one. pub fn get_ident_interner() -> @ident_interner { - static key: local_data::Key<@@::parse::token::ident_interner> = - &local_data::Key; + local_data_key!(key: @@::parse::token::ident_interner) match local_data::get(key, |k| k.map_move(|k| *k)) { Some(interner) => *interner, None => { diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index af2a4977082..615f3301373 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -243,7 +243,7 @@ pub fn mk_printer(out: @io::Writer, linewidth: uint) -> @mut Printer { * the entire buffered window, but can't output anything until the size is >= * 0 (sizes are set to negative while they're pending calculation). * - * So SCAN takeks input and buffers tokens and pending calculations, while + * So SCAN takes input and buffers tokens and pending calculations, while * PRINT gobbles up completed calculations and tokens from the buffer. The * theory is that the two can never get more than 3N tokens apart, because * once there's "obviously" too much data to fit on a line, in a size diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ec84cbda973..b5868cbc63d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1535,15 +1535,15 @@ fn print_path_(s: @ps, print_ident(s, segment.identifier); - if segment.lifetime.is_some() || !segment.types.is_empty() { - // If this is the last segment, print the bounds. - if i == path.segments.len() - 1 { - match *opt_bounds { - None => {} - Some(ref bounds) => print_bounds(s, bounds, true), - } + // If this is the last segment, print the bounds. + if i == path.segments.len() - 1 { + match *opt_bounds { + None => {} + Some(ref bounds) => print_bounds(s, bounds, true), } + } + if segment.lifetime.is_some() || !segment.types.is_empty() { if colons_before_params { word(s.s, "::") } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 4b718303f2c..8877b082588 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -643,6 +643,29 @@ rust_valgrind_stack_deregister(unsigned int id) { VALGRIND_STACK_DEREGISTER(id); } +#if defined(__WIN32__) + +extern "C" CDECL void +rust_unset_sigprocmask() { + // empty stub for windows to keep linker happy +} + +#else + +#include <signal.h> +#include <unistd.h> + +extern "C" CDECL void +rust_unset_sigprocmask() { + // this can't be safely converted to rust code because the + // representation of sigset_t is platform-dependent + sigset_t sset; + sigemptyset(&sset); + sigprocmask(SIG_SETMASK, &sset, NULL); +} + +#endif + // // Local Variables: // mode: C++ diff --git a/src/rt/rust_run_program.cpp b/src/rt/rust_run_program.cpp deleted file mode 100644 index 25cbaf822f0..00000000000 --- a/src/rt/rust_run_program.cpp +++ /dev/null @@ -1,71 +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 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -#include "rust_globals.h" - -#ifdef __APPLE__ -#include <crt_externs.h> -#endif - -#if defined(__WIN32__) - -extern "C" CDECL void -rust_unset_sigprocmask() { - // empty stub for windows to keep linker happy -} - -extern "C" CDECL void -rust_set_environ(void* envp) { - // empty stub for windows to keep linker happy -} - -#elif defined(__GNUC__) - -#include <signal.h> -#include <unistd.h> - -#ifdef __FreeBSD__ -extern char **environ; -#endif - -extern "C" CDECL void -rust_unset_sigprocmask() { - // this can't be safely converted to rust code because the - // representation of sigset_t is platform-dependent - sigset_t sset; - sigemptyset(&sset); - sigprocmask(SIG_SETMASK, &sset, NULL); -} - -extern "C" CDECL void -rust_set_environ(void* envp) { - // FIXME: this could actually be converted to rust (see issue #2674) -#ifdef __APPLE__ - *_NSGetEnviron() = (char **) envp; -#else - environ = (char **) envp; -#endif -} - -#else -#error "Platform not supported." -#endif - -// -// Local Variables: -// mode: C++ -// fill-column: 78; -// indent-tabs-mode: nil -// c-basic-offset: 4 -// buffer-file-coding-system: utf-8-unix -// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; -// End: -// diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 9b460cffd74..3e9b8ba136e 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -602,3 +602,38 @@ extern "C" int rust_uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { return uv_fs_readdir(loop, req, path, flags, cb); } + +extern "C" int +rust_uv_spawn(uv_loop_t *loop, uv_process_t *p, uv_process_options_t options) { + return uv_spawn(loop, p, options); +} + +extern "C" int +rust_uv_process_kill(uv_process_t *p, int signum) { + return uv_process_kill(p, signum); +} + +extern "C" void +rust_set_stdio_container_flags(uv_stdio_container_t *c, int flags) { + c->flags = (uv_stdio_flags) flags; +} + +extern "C" void +rust_set_stdio_container_fd(uv_stdio_container_t *c, int fd) { + c->data.fd = fd; +} + +extern "C" void +rust_set_stdio_container_stream(uv_stdio_container_t *c, uv_stream_t *stream) { + c->data.stream = stream; +} + +extern "C" int +rust_uv_process_pid(uv_process_t* p) { + return p->pid; +} + +extern "C" int +rust_uv_pipe_init(uv_loop_t *loop, uv_pipe_t* p, int ipc) { + return uv_pipe_init(loop, p, ipc); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 3be958837dc..7a9149187d8 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -25,7 +25,6 @@ rust_list_dir_wfd_fp_buf rust_log_console_on rust_log_console_off rust_should_log_console -rust_set_environ rust_unset_sigprocmask rust_env_pairs upcall_rust_personality @@ -198,3 +197,10 @@ rust_drop_linenoise_lock rust_get_test_int rust_get_task rust_uv_get_loop_from_getaddrinfo_req +rust_uv_spawn +rust_uv_process_kill +rust_set_stdio_container_flags +rust_set_stdio_container_fd +rust_set_stdio_container_stream +rust_uv_process_pid +rust_uv_pipe_init diff --git a/src/rustdoc_ng/rustdoc_ng.rs b/src/rustdoc_ng/rustdoc_ng.rs index ec6ad6974e9..a4e3bc50d11 100644 --- a/src/rustdoc_ng/rustdoc_ng.rs +++ b/src/rustdoc_ng/rustdoc_ng.rs @@ -41,7 +41,6 @@ pub fn main() { } pub fn main_args(args: &[~str]) { - use extra::getopts::*; use extra::getopts::groups::*; let opts = ~[ @@ -56,20 +55,20 @@ pub fn main_args(args: &[~str]) { let matches = getopts(args.tail(), opts).unwrap(); - if opt_present(&matches, "h") || opt_present(&matches, "help") { + if matches.opt_present("h") || matches.opt_present("help") { println(usage(args[0], opts)); return; } - let libs = Cell::new(opt_strs(&matches, "L").map(|s| Path(*s))); + let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s))); - let mut passes = if opt_present(&matches, "n") { + let mut passes = if matches.opt_present("n") { ~[] } else { ~[~"collapse-docs", ~"clean-comments", ~"collapse-privacy" ] }; - opt_strs(&matches, "a").map(|x| passes.push(x.clone())); + matches.opt_strs("a").map(|x| passes.push(x.clone())); if matches.free.len() != 1 { println(usage(args[0], opts)); @@ -99,7 +98,7 @@ pub fn main_args(args: &[~str]) { }) } - for pname in opt_strs(&matches, "p").move_iter() { + for pname in matches.opt_strs("p").move_iter() { pm.load_plugin(pname); } diff --git a/src/snapshots.txt b/src/snapshots.txt index ad170d9173f..705cf50632a 100644 --- a/src/snapshots.txt +++ b/src/snapshots.txt @@ -1,3 +1,11 @@ +S 2013-09-17 cbd1eef + freebsd-x86_64 9166867a8859076343cb3e57da918b5c0eea720b + linux-i386 38347b579312ff30c36d257a1161660eb0ae8422 + linux-x86_64 0c169bba5d6729d0c0f096d61d9274fb082b4b34 + macos-i386 1eb229510dd12b91800674566b8dad401a3f80d3 + macos-x86_64 1c5d8e29b9671af93963e1b5fa9fcca081124a39 + winnt-i386 56baa04a1f02235ebc5a75be05aa65fdc822a4e6 + S 2013-08-14 e7b5729 freebsd-x86_64 9de0b5583a5c4413f9e77df7071498385e936dd2 linux-i386 29119a9072f74c639c2bad998edc40e582da540e diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index 7029ca2a492..0896682b322 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -64,7 +64,7 @@ fn parse_opts(argv: ~[~str]) -> Config { match getopts::getopts(opt_args, opts) { Ok(ref m) => { - return Config {stress: getopts::opt_present(m, "stress")} + return Config {stress: m.opt_present("stress")} } Err(_) => { fail!(); } } diff --git a/src/test/pretty/path-type-bounds.rs b/src/test/pretty/path-type-bounds.rs new file mode 100644 index 00000000000..a62fbdeeb18 --- /dev/null +++ b/src/test/pretty/path-type-bounds.rs @@ -0,0 +1,13 @@ +// pp-exact + +trait Tr { } +impl Tr for int; + +fn foo(x: ~Tr: Freeze) -> ~Tr: Freeze { x } + +fn main() { + let x: ~Tr: Freeze; + + ~1 as ~Tr: Freeze; +} + diff --git a/src/test/run-pass/cond-macro-no-default.rs b/src/test/run-pass/cond-macro-no-default.rs deleted file mode 100644 index 8bd1a772e55..00000000000 --- a/src/test/run-pass/cond-macro-no-default.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn clamp<T:Ord + Signed>(x: T, mn: T, mx: T) -> T { - cond!( - (x > mx) { return mx; } - (x < mn) { return mn; } - ) - return x; -} - -fn main() { - assert_eq!(clamp(1, 2, 4), 2); - assert_eq!(clamp(8, 2, 4), 4); - assert_eq!(clamp(3, 2, 4), 3); -} diff --git a/src/test/run-pass/extern-pass-TwoU64s-ref.rs b/src/test/run-pass/extern-pass-TwoU64s-ref.rs index 1937c366831..ed9871b5edb 100644 --- a/src/test/run-pass/extern-pass-TwoU64s-ref.rs +++ b/src/test/run-pass/extern-pass-TwoU64s-ref.rs @@ -9,6 +9,7 @@ // except according to those terms. // Test that we ignore modes when calling extern functions. +// xfail-fast #9205 #[deriving(Eq)] struct TwoU64s { diff --git a/src/test/run-pass/extern-pass-TwoU64s.rs b/src/test/run-pass/extern-pass-TwoU64s.rs index b543099b3b8..772970ce8a3 100644 --- a/src/test/run-pass/extern-pass-TwoU64s.rs +++ b/src/test/run-pass/extern-pass-TwoU64s.rs @@ -14,6 +14,7 @@ // xfail-fast This works standalone on windows but not with check-fast. // possibly because there is another test that uses this extern fn but gives it // a different signature +// xfail-fast #9205 #[deriving(Eq)] struct TwoU64s { diff --git a/src/test/run-pass/extern-return-TwoU64s.rs b/src/test/run-pass/extern-return-TwoU64s.rs index 4dc31d71526..7fcbe3670fc 100644 --- a/src/test/run-pass/extern-return-TwoU64s.rs +++ b/src/test/run-pass/extern-return-TwoU64s.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-fast #9205 + struct TwoU64s { one: u64, two: u64 } diff --git a/src/test/run-pass/getopts_ref.rs b/src/test/run-pass/getopts_ref.rs index cebd2910070..d1964b5d94b 100644 --- a/src/test/run-pass/getopts_ref.rs +++ b/src/test/run-pass/getopts_ref.rs @@ -20,8 +20,8 @@ pub fn main() { match getopts(args, opts) { Ok(ref m) => - assert!(!opt_present(m, "b")), - Err(ref f) => fail!(fail_str((*f).clone())) + assert!(!m.opt_present("b")), + Err(ref f) => fail!((*f).clone().to_err_msg()) }; } diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 351bad193da..6ca21c6c5a1 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -243,6 +243,10 @@ pub fn main() { } test_format_args(); + + // test that trailing commas are acceptable + format!("{}", "test",); + format!("{foo}", foo="test",); } // Basic test to make sure that we can invoke the `write!` macro with an diff --git a/src/test/run-pass/issue-7673-cast-generically-implemented-trait.rs b/src/test/run-pass/issue-7673-cast-generically-implemented-trait.rs index 1492b5895ba..2f2b736294a 100644 --- a/src/test/run-pass/issue-7673-cast-generically-implemented-trait.rs +++ b/src/test/run-pass/issue-7673-cast-generically-implemented-trait.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-pretty #9253 pretty printer doesn't preserve the bounds on trait objects - /* #7673 Polymorphically creating traits barely works diff --git a/src/test/run-pass/nul-characters.rs b/src/test/run-pass/nul-characters.rs new file mode 100644 index 00000000000..2a301d0b0fd --- /dev/null +++ b/src/test/run-pass/nul-characters.rs @@ -0,0 +1,44 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn main() +{ + let all_nuls1 = "\0\x00\u0000\U00000000"; + let all_nuls2 = "\U00000000\u0000\x00\0"; + let all_nuls3 = "\u0000\U00000000\x00\0"; + let all_nuls4 = "\x00\u0000\0\U00000000"; + + // sizes for two should suffice + assert_eq!(all_nuls1.len(), 4); + assert_eq!(all_nuls2.len(), 4); + + // string equality should pass between the strings + assert_eq!(all_nuls1, all_nuls2); + assert_eq!(all_nuls2, all_nuls3); + assert_eq!(all_nuls3, all_nuls4); + + // all extracted characters in all_nuls are equivalent to each other + for c1 in all_nuls1.iter() + { + for c2 in all_nuls1.iter() + { + assert_eq!(c1,c2); + } + } + + // testing equality between explicit character literals + assert_eq!('\0', '\x00'); + assert_eq!('\u0000', '\x00'); + assert_eq!('\u0000', '\U00000000'); + + // NUL characters should make a difference + assert!("Hello World" != "Hello \0World"); + assert!("Hello World" != "Hello World\0"); +} diff --git a/src/test/run-pass/struct-return.rs b/src/test/run-pass/struct-return.rs index 3f63902eb31..a1f5a2392b1 100644 --- a/src/test/run-pass/struct-return.rs +++ b/src/test/run-pass/struct-return.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-fast #9205 + pub struct Quad { a: u64, b: u64, c: u64, d: u64 } pub struct Floats { a: f64, b: u8, c: f64 } |
