diff options
Diffstat (limited to 'src')
83 files changed, 3649 insertions, 2536 deletions
diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 166638bc359..270b1097c55 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}; @@ -131,7 +130,7 @@ pub fn parse_config(args: ~[~str]) -> config { ratchet_noise_percent: getopts::opt_maybe_str(matches, "ratchet-noise-percent").map_move(|s| - f64::from_str(s).unwrap()), + from_str::<f64>(s).unwrap()), runtool: getopts::opt_maybe_str(matches, "runtool"), rustcflags: getopts::opt_maybe_str(matches, "rustcflags"), jit: getopts::opt_present(matches, "jit"), 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/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 24f44c8a2a8..039694f5881 100644 --- a/src/libextra/num/bigint.rs +++ b/src/libextra/num/bigint.rs @@ -697,6 +697,13 @@ impl BigUint { } return BigUint::new(shifted); } + + /// Determines the fewest bits necessary to express the BigUint. + pub fn bits(&self) -> uint { + if self.is_zero() { return 0; } + let zeros = self.data.last().leading_zeros(); + return self.data.len()*BigDigit::bits - (zeros as uint); + } } #[cfg(target_word_size = "64")] @@ -1115,10 +1122,23 @@ trait RandBigInt { /// Generate a random BigInt of the given bit size. fn gen_bigint(&mut self, bit_size: uint) -> BigInt; + + /// Generate a random BigUint less than the given bound. Fails + /// when the bound is zero. + fn gen_biguint_below(&mut self, bound: &BigUint) -> BigUint; + + /// Generate a random BigUint within the given range. The lower + /// bound is inclusive; the upper bound is exclusive. Fails when + /// the upper bound is not greater than the lower bound. + fn gen_biguint_range(&mut self, lbound: &BigUint, ubound: &BigUint) -> BigUint; + + /// Generate a random BigInt within the given range. The lower + /// bound is inclusive; the upper bound is exclusive. Fails when + /// the upper bound is not greater than the lower bound. + fn gen_bigint_range(&mut self, lbound: &BigInt, ubound: &BigInt) -> BigInt; } impl<R: Rng> RandBigInt for R { - /// Generate a random BigUint of the given bit size. fn gen_biguint(&mut self, bit_size: uint) -> BigUint { let (digits, rem) = bit_size.div_rem(&BigDigit::bits); let mut data = vec::with_capacity(digits+1); @@ -1132,7 +1152,6 @@ impl<R: Rng> RandBigInt for R { return BigUint::new(data); } - /// Generate a random BigInt of the given bit size. fn gen_bigint(&mut self, bit_size: uint) -> BigInt { // Generate a random BigUint... let biguint = self.gen_biguint(bit_size); @@ -1154,6 +1173,32 @@ impl<R: Rng> RandBigInt for R { }; return BigInt::from_biguint(sign, biguint); } + + fn gen_biguint_below(&mut self, bound: &BigUint) -> BigUint { + assert!(!bound.is_zero()); + let bits = bound.bits(); + loop { + let n = self.gen_biguint(bits); + if n < *bound { return n; } + } + } + + fn gen_biguint_range(&mut self, + lbound: &BigUint, + ubound: &BigUint) + -> BigUint { + assert!(*lbound < *ubound); + return *lbound + self.gen_biguint_below(&(*ubound - *lbound)); + } + + fn gen_bigint_range(&mut self, + lbound: &BigInt, + ubound: &BigInt) + -> BigInt { + assert!(*lbound < *ubound); + let delta = (*ubound - *lbound).to_biguint(); + return *lbound + self.gen_biguint_below(&delta).to_bigint(); + } } impl BigInt { @@ -1781,11 +1826,62 @@ mod biguint_tests { } #[test] + fn test_bits() { + assert_eq!(BigUint::new(~[0,0,0,0]).bits(), 0); + assert_eq!(BigUint::from_uint(0).bits(), 0); + assert_eq!(BigUint::from_uint(1).bits(), 1); + assert_eq!(BigUint::from_uint(3).bits(), 2); + let n: BigUint = FromStrRadix::from_str_radix("4000000000", 16).unwrap(); + assert_eq!(n.bits(), 39); + let one: BigUint = One::one(); + assert_eq!((one << 426).bits(), 427); + } + + #[test] fn test_rand() { let mut rng = task_rng(); let _n: BigUint = rng.gen_biguint(137); assert!(rng.gen_biguint(0).is_zero()); } + + #[test] + fn test_rand_range() { + let mut rng = task_rng(); + + do 10.times { + assert_eq!(rng.gen_bigint_range(&BigInt::from_uint(236), + &BigInt::from_uint(237)), + BigInt::from_uint(236)); + } + + let l = BigUint::from_uint(403469000 + 2352); + let u = BigUint::from_uint(403469000 + 3513); + do 1000.times { + let n: BigUint = rng.gen_biguint_below(&u); + assert!(n < u); + + let n: BigUint = rng.gen_biguint_range(&l, &u); + assert!(n >= l); + assert!(n < u); + } + } + + #[test] + #[should_fail] + fn test_zero_rand_range() { + task_rng().gen_biguint_range(&BigUint::from_uint(54), + &BigUint::from_uint(54)); + } + + #[test] + #[should_fail] + fn test_negative_rand_range() { + let mut rng = task_rng(); + let l = BigUint::from_uint(2352); + let u = BigUint::from_uint(3513); + // Switching u and l should fail: + let _n: BigUint = rng.gen_biguint_range(&u, &l); + } } #[cfg(test)] @@ -2237,6 +2333,48 @@ mod bigint_tests { let _n: BigInt = rng.gen_bigint(137); assert!(rng.gen_bigint(0).is_zero()); } + + #[test] + fn test_rand_range() { + let mut rng = task_rng(); + + do 10.times { + assert_eq!(rng.gen_bigint_range(&BigInt::from_uint(236), + &BigInt::from_uint(237)), + BigInt::from_uint(236)); + } + + fn check(l: BigInt, u: BigInt) { + let mut rng = task_rng(); + do 1000.times { + let n: BigInt = rng.gen_bigint_range(&l, &u); + assert!(n >= l); + assert!(n < u); + } + } + let l = BigInt::from_uint(403469000 + 2352); + let u = BigInt::from_uint(403469000 + 3513); + check( l.clone(), u.clone()); + check(-l.clone(), u.clone()); + check(-u.clone(), -l.clone()); + } + + #[test] + #[should_fail] + fn test_zero_rand_range() { + task_rng().gen_bigint_range(&IntConvertible::from_int(54), + &IntConvertible::from_int(54)); + } + + #[test] + #[should_fail] + fn test_negative_rand_range() { + let mut rng = task_rng(); + let l = BigInt::from_uint(2352); + let u = BigInt::from_uint(3513); + // Switching u and l should fail: + let _n: BigInt = rng.gen_bigint_range(&u, &l); + } } #[cfg(test)] 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/workcache.rs b/src/libextra/workcache.rs index 4d3b5ae035e..24ab8360e8f 100644 --- a/src/libextra/workcache.rs +++ b/src/libextra/workcache.rs @@ -198,7 +198,6 @@ impl Database { } } -// FIXME #4330: use &mut self here #[unsafe_destructor] impl Drop for Database { fn drop(&mut self) { 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/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 b4979c335b5..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) { @@ -2559,10 +2533,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { // LLVM type is not fully determined by the Rust type. let (v, inlineable) = consts::const_expr(ccx, expr); ccx.const_values.insert(id, v); - if !inlineable { - debug!("%s not inlined", sym); - ccx.non_inlineable_statics.insert(id); - } + let mut inlineable = inlineable; exprt = true; unsafe { @@ -2578,8 +2549,30 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { lib::llvm::SetUnnamedAddr(g, true); lib::llvm::SetLinkage(g, lib::llvm::InternalLinkage); + + // This is a curious case where we must make + // all of these statics inlineable. If a + // global is tagged as + // address_insignificant, then LLVM won't + // coalesce globals unless they have an + // internal linkage type. This means that + // external crates cannot use this global. + // This is a problem for things like inner + // statics in generic functions, because the + // function will be inlined into another + // crate and then attempt to link to the + // static in the original crate, only to + // find that it's not there. On the other + // side of inlininig, the crates knows to + // not declare this static as + // available_externally (because it isn't) + inlineable = true; } + if !inlineable { + debug!("%s not inlined", sym); + ccx.non_inlineable_statics.insert(id); + } ccx.item_symbols.insert(i.id, sym); g } @@ -2641,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/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs index a571e56a48e..8900b50b49d 100644 --- a/src/librustc/middle/trans/inline.rs +++ b/src/librustc/middle/trans/inline.rs @@ -21,6 +21,7 @@ use std::vec; use syntax::ast; use syntax::ast_map::path_name; use syntax::ast_util::local_def; +use syntax::attr; pub fn maybe_instantiate_inline(ccx: @mut CrateContext, fn_id: ast::DefId) -> ast::DefId { @@ -68,7 +69,12 @@ pub fn maybe_instantiate_inline(ccx: @mut CrateContext, fn_id: ast::DefId) match item.node { ast::item_static(*) => { let g = get_item_val(ccx, item.id); - SetLinkage(g, AvailableExternallyLinkage); + // see the comment in get_item_val() as to why this check is + // performed here. + if !attr::contains_name(item.attrs, + "address_insignificant") { + SetLinkage(g, AvailableExternallyLinkage); + } } _ => {} } diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index bca2ceb2f84..902734b05a2 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -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", 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/librustpkg/exit_codes.rs b/src/librustpkg/exit_codes.rs new file mode 100644 index 00000000000..484f6bdcaec --- /dev/null +++ b/src/librustpkg/exit_codes.rs @@ -0,0 +1,11 @@ +// 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 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 eef1dcabfd0..7cd30c7af9e 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,40 +175,26 @@ 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>); fn unprefer(&self, _id: &str, _vers: Option<~str>); + fn init(&self); } 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" => { @@ -214,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); } } } @@ -229,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 }; } @@ -270,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); } } } @@ -290,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); }; } } @@ -319,6 +310,13 @@ impl CtxMethods for BuildContext { "test" => { self.test(); } + "init" => { + if args.len() != 0 { + return usage::init(); + } else { + self.init(); + } + } "uninstall" => { if args.len() < 1 { return usage::uninstall(); @@ -358,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(); @@ -376,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 @@ -387,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 { @@ -411,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 { @@ -444,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 @@ -503,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>) { @@ -540,6 +595,13 @@ impl CtxMethods for BuildContext { fail!("test not yet implemented"); } + fn init(&self) { + os::mkdir_recursive(&Path("src"), U_RWX); + os::mkdir_recursive(&Path("lib"), U_RWX); + os::mkdir_recursive(&Path("bin"), U_RWX); + os::mkdir_recursive(&Path("build"), U_RWX); + } + fn uninstall(&self, _id: &str, _vers: Option<~str>) { fail!("uninstall not yet implemented"); } @@ -688,6 +750,7 @@ pub fn main_args(args: &[~str]) { ~"list" => usage::list(), ~"prefer" => usage::prefer(), ~"test" => usage::test(), + ~"init" => usage::init(), ~"uninstall" => usage::uninstall(), ~"unprefer" => usage::unprefer(), _ => usage::general() @@ -710,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 83110e22ed5..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. @@ -944,20 +950,23 @@ fn no_rebuilding_dep() { 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] @@ -966,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); @@ -982,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); } @@ -1308,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"); @@ -1463,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"), + _ => () + } } @@ -1679,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}; @@ -1688,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/usage.rs b/src/librustpkg/usage.rs index dae949541b3..c0601818f37 100644 --- a/src/librustpkg/usage.rs +++ b/src/librustpkg/usage.rs @@ -148,3 +148,10 @@ and exit code will be redirected. Options: -c, --cfg Pass a cfg flag to the package script"); } + +pub fn init() { + io::println("rustpkg init name + +This makes a new workspace for working on a project named name. +"); +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 71c4760f28d..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,14 +27,14 @@ 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, // you could update the match in rustpkg.rc but forget to update this list. I think // that should be fixed. static COMMANDS: &'static [&'static str] = - &["build", "clean", "do", "info", "install", "list", "prefer", "test", "uninstall", + &["build", "clean", "do", "info", "init", "install", "list", "prefer", "test", "uninstall", "unprefer"]; @@ -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/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/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..1bb488d0278 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -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..604eac0a0a7 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -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/os.rs b/src/libstd/os.rs index 6fe6a1e47e9..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. @@ -196,16 +184,7 @@ pub fn env() -> ~[(~str,~str)] { if (ch as uint == 0) { fail!("os::env() failure getting env string from OS: %s", os::last_os_error()); } - let mut curr_ptr: uint = ch as uint; - let mut result = ~[]; - while(*(curr_ptr as *libc::c_char) != 0 as libc::c_char) { - let env_pair = str::raw::from_c_str( - curr_ptr as *libc::c_char); - result.push(env_pair); - curr_ptr += - libc::strlen(curr_ptr as *libc::c_char) as uint - + 1; - } + let result = str::raw::from_c_multistring(ch as *libc::c_char, None); FreeEnvironmentStringsA(ch); result } @@ -1668,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/file.rs b/src/libstd/rt/io/file.rs index c24f4eb257e..a884961fd1e 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -8,17 +8,87 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! Synchronous File I/O + +This module provides a set of functions and traits for working +with regular files & directories on a filesystem. + +At the top-level of the module are a set of freestanding functions, +associated with various filesystem operations. They all operate +on a `PathLike` object. + +All operations in this module, including those as part of `FileStream` et al +block the task during execution. Most will raise `std::rt::io::{io_error,read_error}` +conditions in the event of failure. + +Also included in this module are the `FileInfo` and `DirectoryInfo` traits. When +`use`'d alongside a value whose type implements them (A `std::path::Path` impl is +a part of this module), they expose a set of functions for operations against +a given file location, depending on whether the path already exists. Whenever +possible, the `{FileInfo, DirectoryInfo}` preserve the same semantics as their +free function counterparts. +*/ + use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek}; -use super::{SeekSet, SeekCur, SeekEnd, SeekStyle}; +use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, + Open, Read, Write, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, - FileMode, FileAccess, Open, Read, Create, ReadWrite}; + FileMode, FileAccess, FileStat, IoError, + PathAlreadyExists, PathDoesntExist, + MismatchedFileTypeForOperation, ignore_io_error}; use rt::local::Local; -use rt::test::*; +use option::{Some, None}; +use path::Path; +use super::super::test::*; /// Open a file for reading/writing, as indicated by `path`. +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::open; +/// use std::rt::io::{FileMode, FileAccess}; +/// +/// let p = &Path("/some/file/path.txt"); +/// +/// do io_error::cond.trap(|_| { +/// // hoo-boy... +/// }).inside { +/// let stream = match open(p, Create, ReadWrite) { +/// Some(s) => s, +/// None => fail!("whoops! I'm sure this raised, anyways.."); +/// } +/// // do some stuff with that stream +/// +/// // the file stream will be closed at the end of this block +/// } +/// // .. +/// +/// `FileMode` and `FileAccess` provide information about the permissions +/// context in which a given stream is created. More information about them +/// can be found in `std::rt::io`'s docs. +/// +/// Note that, with this function, a `FileStream` is returned regardless of +/// the access-limitations indicated by `FileAccess` (e.g. calling `write` on a +/// `FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you +/// desire a more-correctly-constrained interface to files, use the +/// `{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` +/// +/// # Errors +/// +/// This function will raise an `io_error` condition under a number of different circumstances, +/// to include but not limited to: +/// +/// * Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. +/// opening a non-existant file with `FileMode` or `Open`) +/// * Attempting to open a file with a `FileAccess` that the user lacks permissions +/// for +/// * Filesystem-level errors (full disk, etc) pub fn open<P: PathLike>(path: &P, mode: FileMode, access: FileAccess @@ -39,8 +109,28 @@ pub fn open<P: PathLike>(path: &P, } } -/// Unlink (remove) a file from the filesystem, as indicated -/// by `path`. +/// Unlink a file from the underlying filesystem. +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::unlink; +/// +/// let p = &Path("/some/file/path.txt"); +/// unlink(p); +/// // if we made it here without failing, then the +/// // unlink operation was successful +/// +/// Note that, just because an unlink call was successful, it is not +/// guaranteed that a file is immediately deleted (e.g. depending on +/// platform, other open file descriptors may prevent immediate removal) +/// +/// # Errors +/// +/// This function will raise an `io_error` condition if the user lacks permissions to +/// remove the file or if some other filesystem-level error occurs pub fn unlink<P: PathLike>(path: &P) { let unlink_result = unsafe { let io: *mut IoFactoryObject = Local::unsafe_borrow(); @@ -54,26 +144,231 @@ pub fn unlink<P: PathLike>(path: &P) { } } -/// Abstraction representing *positional* access to a file. In this case, -/// *positional* refers to it keeping an encounter *cursor* of where in the -/// file a subsequent `read` or `write` will begin from. Users of a `FileStream` -/// can `seek` to move the cursor to a given location *within the bounds of the -/// file* and can ask to have the `FileStream` `tell` them the location, in -/// bytes, of the cursor. +/// Create a new, empty directory at the provided path +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::mkdir; +/// +/// let p = &Path("/some/dir"); +/// mkdir(p); +/// // If we got here, our directory exists! Horray! +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks permissions to make a +/// new directory at the provided path, or if the directory already exists +pub fn mkdir<P: PathLike>(path: &P) { + let mkdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_mkdir(path) + }; + match mkdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} + +/// Remove an existing, empty directory +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::rmdir; +/// +/// let p = &Path("/some/dir"); +/// rmdir(p); +/// // good riddance, you mean ol' directory +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks permissions to remove the +/// directory at the provided path, or if the directory isn't empty +pub fn rmdir<P: PathLike>(path: &P) { + let rmdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_rmdir(path) + }; + match rmdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} + +/// Get information on the file, directory, etc at the provided path +/// +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. +/// +/// Returns a `Some(std::rt::io::PathInfo)` on success +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::stat; +/// +/// let p = &Path("/some/file/path.txt"); +/// +/// do io_error::cond.trap(|_| { +/// // hoo-boy... +/// }).inside { +/// let info = match stat(p) { +/// Some(s) => s, +/// None => fail!("whoops! I'm sure this raised, anyways.."); +/// } +/// if stat.is_file { +/// // just imagine the possibilities ... +/// } +/// +/// // the file stream will be closed at the end of this block +/// } +/// // .. +/// +/// # Errors +/// +/// This call will raise an `io_error` condition if the user lacks the requisite +/// permissions to perform a `stat` call on the given path or if there is no +/// entry in the filesystem at the provided path. +pub fn stat<P: PathLike>(path: &P) -> Option<FileStat> { + let open_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_stat(path) + }; + match open_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } +} + +/// Retrieve a vector containing all entries within a provided directory +/// +/// # Example +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::support::PathLike; +/// use std::rt::io::file::readdir; +/// +/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { +/// if dir.is_dir() { +/// let contents = dir.readdir(); +/// for entry in contents.iter() { +/// if entry.is_dir() { visit_dirs(entry, cb); } +/// else { cb(entry); } +/// } +/// } +/// else { fail!("nope"); } +/// } +/// +/// # Errors +/// +/// Will raise an `io_error` condition if the provided `path` doesn't exist, +/// the process lacks permissions to view the contents or if the `path` points +/// at a non-directory file +pub fn readdir<P: PathLike>(path: &P) -> Option<~[Path]> { + let readdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_readdir(path, 0) + }; + match readdir_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } +} + +/// Constrained version of `FileStream` that only exposes read-specific operations. +/// +/// Can be retreived via `FileInfo.open_reader()`. +pub struct FileReader { priv stream: FileStream } + +/// a `std::rt::io::Reader` trait impl for file I/O. +impl Reader for FileReader { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { + self.stream.read(buf) + } + + fn eof(&mut self) -> bool { + self.stream.eof() + } +} + +/// a `std::rt::io::Seek` trait impl for file I/O. +impl Seek for FileReader { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Constrained version of `FileStream` that only exposes write-specific operations. +/// +/// Can be retreived via `FileInfo.open_writer()`. +pub struct FileWriter { priv stream: FileStream } + +/// a `std::rt::io::Writer` trait impl for file I/O. +impl Writer for FileWriter { + fn write(&mut self, buf: &[u8]) { + self.stream.write(buf); + } + + fn flush(&mut self) { + self.stream.flush(); + } +} + +/// a `std::rt::io::Seek` trait impl for file I/O. +impl Seek for FileWriter { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Unconstrained file access type that exposes read and write operations +/// +/// Can be retreived via `file::open()` and `FileInfo.open_stream()`. +/// +/// # Errors /// -/// This abstraction is roughly modeled on the access workflow as represented -/// by `open(2)`, `read(2)`, `write(2)` and friends. +/// This type will raise an io_error condition if operations are attempted against +/// it for which its underlying file descriptor was not configured at creation +/// time, via the `FileAccess` parameter to `file::open()`. /// -/// The `open` and `unlink` static methods are provided to manage creation/removal -/// of files. All other methods operatin on an instance of `FileStream`. +/// For this reason, it is best to use the access-constrained wrappers that are +/// exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. pub struct FileStream { fd: ~RtioFileStream, last_nread: int, } -impl FileStream { -} - +/// a `std::rt::io::Reader` trait impl for file I/O. impl Reader for FileStream { fn read(&mut self, buf: &mut [u8]) -> Option<uint> { match self.fd.read(buf) { @@ -99,6 +394,7 @@ impl Reader for FileStream { } } +/// a `std::rt::io::Writer` trait impl for file I/O. impl Writer for FileStream { fn write(&mut self, buf: &[u8]) { match self.fd.write(buf) { @@ -119,6 +415,7 @@ impl Writer for FileStream { } } +/// a `std::rt::io:Seek` trait impl for file I/O. impl Seek for FileStream { fn tell(&self) -> u64 { let res = self.fd.tell(); @@ -145,6 +442,242 @@ impl Seek for FileStream { } } +/// Shared functionality between `FileInfo` and `DirectoryInfo` +pub trait FileSystemInfo { + /// Get the filesystem path that this instance points at, + /// whether it is valid or not. In this way, it can be used to + /// to specify a path of a non-existent file which it + /// later creates + fn get_path<'a>(&'a self) -> &'a Path; + + /// Get information on the file, directory, etc at the provided path + /// + /// Consult the `file::stat` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with `file::stat` + fn stat(&self) -> Option<FileStat> { + stat(self.get_path()) + } + + /// Boolean value indicator whether the underlying file exists on the filesystem + /// + /// # Errors + /// + /// Will not raise a condition + fn exists(&self) -> bool { + match ignore_io_error(|| self.stat()) { + Some(_) => true, + None => false + } + } + +} + +/// Represents a file, whose underlying path may or may not be valid +/// +/// # Example +/// +/// * Check if a file exists, reading from it if so +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::file::{FileInfo, FileReader}; +/// +/// let f = &Path("/some/file/path.txt"); +/// if f.exists() { +/// let reader = f.open_reader(Open); +/// let mut mem = [0u8, 8*64000]; +/// reader.read(mem); +/// // ... +/// } +/// +/// * Is the given path a file? +/// +/// let f = get_file_path_from_wherever(); +/// match f.is_file() { +/// true => doing_something_with_a_file(f), +/// _ => {} +/// } +pub trait FileInfo : FileSystemInfo { + /// Whether the underlying implemention (be it a file path, + /// or something else) points at a "regular file" on the FS. Will return + /// false for paths to non-existent locations or directories or + /// other non-regular files (named pipes, etc). + /// + /// # Errors + /// + /// Will not raise a condition + fn is_file(&self) -> bool { + match ignore_io_error(|| self.stat()) { + Some(s) => s.is_file, + None => false + } + } + + /// Attempts to open a regular file for reading/writing based + /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions + fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option<FileStream> { + match ignore_io_error(|| self.stat()) { + Some(s) => match s.is_file { + true => open(self.get_path(), mode, access), + false => None + }, + None => open(self.get_path(), mode, access) + } + } + + /// Attempts to open a regular file in read-only mode, based + /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions + fn open_reader(&self, mode: FileMode) -> Option<FileReader> { + match self.open_stream(mode, Read) { + Some(s) => Some(FileReader { stream: s}), + None => None + } + } + + /// Attempts to open a regular file in write-only mode, based + /// on provided inputs + /// + /// See `file::open` for more information on runtime semantics and error conditions + fn open_writer(&self, mode: FileMode) -> Option<FileWriter> { + match self.open_stream(mode, Write) { + Some(s) => Some(FileWriter { stream: s}), + None => None + } + } + + /// Attempt to remove a file from the filesystem + /// + /// See `file::unlink` for more information on runtime semantics and error conditions + fn unlink(&self) { + unlink(self.get_path()); + } +} + +/// `FileSystemInfo` implementation for `Path`s +impl FileSystemInfo for Path { + fn get_path<'a>(&'a self) -> &'a Path { self } +} + +/// `FileInfo` implementation for `Path`s +impl FileInfo for Path { } + +/// Represents a directory, whose underlying path may or may not be valid +/// +/// # Example +/// +/// * Check if a directory exists, `mkdir`'ing it if not +/// +/// use std; +/// use std::path::Path; +/// use std::rt::io::file::{DirectoryInfo}; +/// +/// let dir = &Path("/some/dir"); +/// if !dir.exists() { +/// dir.mkdir(); +/// } +/// +/// * Is the given path a directory? If so, iterate on its contents +/// +/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { +/// if dir.is_dir() { +/// let contents = dir.readdir(); +/// for entry in contents.iter() { +/// if entry.is_dir() { visit_dirs(entry, cb); } +/// else { cb(entry); } +/// } +/// } +/// else { fail!("nope"); } +/// } +trait DirectoryInfo : FileSystemInfo { + /// Whether the underlying implemention (be it a file path, + /// or something else) is pointing at a directory in the underlying FS. + /// Will return false for paths to non-existent locations or if the item is + /// not a directory (eg files, named pipes, links, etc) + /// + /// # Errors + /// + /// Will not raise a condition + fn is_dir(&self) -> bool { + match ignore_io_error(|| self.stat()) { + Some(s) => s.is_dir, + None => false + } + } + + /// Create a directory at the location pointed to by the + /// type underlying the given `DirectoryInfo`. + /// + /// # Errors + /// + /// This method will raise a `PathAlreadyExists` kind of `io_error` condition + /// if the provided path exists + /// + /// See `file::mkdir` for more information on runtime semantics and error conditions + fn mkdir(&self) { + match ignore_io_error(|| self.stat()) { + Some(_) => { + io_error::cond.raise(IoError { + kind: PathAlreadyExists, + desc: "Path already exists", + detail: + Some(fmt!("%s already exists; can't mkdir it", self.get_path().to_str())) + }) + }, + None => mkdir(self.get_path()) + } + } + + /// Remove a directory at the given location. + /// + /// # Errors + /// + /// This method will raise a `PathDoesntExist` kind of `io_error` condition + /// if the provided path exists. It will raise a `MismatchedFileTypeForOperation` + /// kind of `io_error` condition if the provided path points at any + /// non-directory file type + /// + /// See `file::rmdir` for more information on runtime semantics and error conditions + fn rmdir(&self) { + match ignore_io_error(|| self.stat()) { + Some(s) => { + match s.is_dir { + true => rmdir(self.get_path()), + false => { + let ioerr = IoError { + kind: MismatchedFileTypeForOperation, + desc: "Cannot do rmdir() on a non-directory", + detail: Some(fmt!( + "%s is a non-directory; can't rmdir it", + self.get_path().to_str())) + }; + io_error::cond.raise(ioerr); + } + } + }, + None => + io_error::cond.raise(IoError { + kind: PathDoesntExist, + desc: "Path doesn't exist", + detail: Some(fmt!("%s doesn't exist; can't rmdir it", self.get_path().to_str())) + }) + } + } + + // Get a collection of all entries at the given + // directory + fn readdir(&self) -> Option<~[Path]> { + readdir(self.get_path()) + } +} + +/// `DirectoryInfo` impl for `path::Path` +impl DirectoryInfo for Path { } + fn file_test_smoke_test_impl() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; @@ -273,7 +806,6 @@ fn file_test_io_seek_and_tell_smoke_test() { } fn file_test_io_seek_and_write_impl() { - use io; do run_in_mt_newsched_task { use str; let initial_msg = "food-is-yummy"; @@ -294,7 +826,6 @@ fn file_test_io_seek_and_write_impl() { } unlink(filename); let read_str = str::from_utf8(read_mem); - io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg)); assert!(read_str == final_msg.to_owned()); } } @@ -343,3 +874,111 @@ fn file_test_io_seek_shakedown_impl() { fn file_test_io_seek_shakedown() { file_test_io_seek_shakedown_impl(); } + +#[test] +fn file_test_stat_is_correct_on_is_file() { + do run_in_mt_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); + { + let mut fs = open(filename, Create, ReadWrite).unwrap(); + let msg = "hw"; + fs.write(msg.as_bytes()); + } + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_file); + unlink(filename); + } +} + +#[test] +fn file_test_stat_is_correct_on_is_dir() { + do run_in_mt_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_dir"); + mkdir(filename); + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_dir); + rmdir(filename); + } +} + +#[test] +fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + do run_in_mt_newsched_task { + let dir = &Path("./tmp/fileinfo_false_on_dir"); + mkdir(dir); + assert!(dir.is_file() == false); + rmdir(dir); + } +} + +#[test] +fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + do run_in_mt_newsched_task { + let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt"); + { + let msg = "foo".as_bytes(); + let mut w = file.open_writer(Create); + w.write(msg); + } + assert!(file.exists()); + file.unlink(); + assert!(!file.exists()); + } +} + +#[test] +fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + do run_in_mt_newsched_task { + let dir = &Path("./tmp/before_and_after_dir"); + assert!(!dir.exists()); + dir.mkdir(); + assert!(dir.exists()); + assert!(dir.is_dir()); + dir.rmdir(); + assert!(!dir.exists()); + } +} + +#[test] +fn file_test_directoryinfo_readdir() { + use str; + do run_in_mt_newsched_task { + let dir = &Path("./tmp/di_readdir"); + dir.mkdir(); + let prefix = "foo"; + for n in range(0,3) { + let f = dir.push(fmt!("%d.txt", n)); + let mut w = f.open_writer(Create); + let msg_str = (prefix + n.to_str().to_owned()).to_owned(); + let msg = msg_str.as_bytes(); + w.write(msg); + } + match dir.readdir() { + Some(files) => { + let mut mem = [0u8, .. 4]; + for f in files.iter() { + { + let n = f.filestem(); + let mut r = f.open_reader(Open); + r.read(mem); + let read_str = str::from_utf8(mem); + let expected = match n { + Some(n) => prefix+n, + None => fail!("really shouldn't happen..") + }; + assert!(expected == read_str); + } + f.unlink(); + } + }, + None => fail!("shouldn't happen") + } + dir.rmdir(); + } +} \ No newline at end of file diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 59ca5d57759..6b405b0948a 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -245,6 +245,7 @@ Out of scope use prelude::*; use to_str::ToStr; use str::{StrSlice, OwnedStr}; +use path::Path; // Reexports pub use self::stdio::stdin; @@ -259,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; @@ -268,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; @@ -357,7 +367,10 @@ pub enum IoErrorKind { Closed, ConnectionRefused, ConnectionReset, - BrokenPipe + BrokenPipe, + PathAlreadyExists, + PathDoesntExist, + MismatchedFileTypeForOperation } // FIXME: #8242 implementing manually because deriving doesn't work for some reason @@ -373,7 +386,10 @@ impl ToStr for IoErrorKind { Closed => ~"Closed", ConnectionRefused => ~"ConnectionRefused", ConnectionReset => ~"ConnectionReset", - BrokenPipe => ~"BrokenPipe" + BrokenPipe => ~"BrokenPipe", + PathAlreadyExists => ~"PathAlreadyExists", + PathDoesntExist => ~"PathDoesntExist", + MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation" } } } @@ -381,17 +397,25 @@ 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 +/// ignore any io_errors that might be raised +pub fn ignore_io_error<T>(cb: &fn() -> T) -> T { + do io_error::cond.trap(|_| { + // just swallow the error.. downstream users + // who can make a decision based on a None result + // won't care + }).inside { + cb() + } } pub trait Reader { @@ -596,3 +620,22 @@ pub enum FileAccess { Write, ReadWrite } + +pub struct FileStat { + /// A `Path` object containing information about the `PathInfo`'s location + path: Path, + /// `true` if the file pointed at by the `PathInfo` is a regular file + is_file: bool, + /// `true` if the file pointed at by the `PathInfo` is a directory + is_dir: bool, + /// The file pointed at by the `PathInfo`'s size in bytes + size: u64, + /// The file pointed at by the `PathInfo`'s creation time + created: u64, + /// The file pointed at by the `PathInfo`'s last-modification time in + /// platform-dependent msecs + modified: u64, + /// The file pointed at by the `PathInfo`'s last-accessd time (e.g. read) in + /// platform-dependent msecs + accessed: u64, +} 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/rc.rs b/src/libstd/rt/rc.rs index 0a6890f627b..2ba00c3a2fb 100644 --- a/src/libstd/rt/rc.rs +++ b/src/libstd/rt/rc.rs @@ -78,17 +78,14 @@ impl<T> Drop for RC<T> { assert!(self.refcount() > 0); unsafe { - // FIXME(#4330) Need self by value to get mutability. - let this: &mut RC<T> = cast::transmute_mut(self); - - match *this.get_mut_state() { + match *self.get_mut_state() { (ref mut count, _) => { *count = *count - 1 } } - if this.refcount() == 0 { - let _: ~(uint, T) = cast::transmute(this.p); + if self.refcount() == 0 { + let _: ~(uint, T) = cast::transmute(self.p); } } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c9c402baaf0..ca521c792dc 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -8,17 +8,19 @@ // 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; use super::io::support::PathLike; use super::io::{SeekStyle}; -use super::io::{FileMode, FileAccess}; +use super::io::{FileMode, FileAccess, FileStat}; // XXX: ~object doesn't work currently so these are some placeholder // types to use instead @@ -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); @@ -74,6 +79,14 @@ pub trait IoFactory { -> Result<~RtioFileStream, IoError>; fn fs_unlink<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>; fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; + fn fs_stat<P: PathLike>(&mut self, path: &P) -> Result<FileStat, IoError>; + fn fs_mkdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>; + 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 { @@ -130,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/file.rs b/src/libstd/rt/uv/file.rs index e87e2d4b1e4..ada558036cf 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -17,6 +17,7 @@ use rt::uv::uvll; use rt::uv::uvll::*; use super::super::io::support::PathLike; use cast::transmute; +use libc; use libc::{c_int}; use option::{None, Some, Option}; @@ -24,77 +25,230 @@ pub struct FsRequest(*uvll::uv_fs_t); impl Request for FsRequest; pub struct RequestData { - complete_cb: Option<FsCallback>, - raw_fd: Option<c_int> + complete_cb: Option<FsCallback> } impl FsRequest { - pub fn new(cb: Option<FsCallback>) -> FsRequest { + pub fn new() -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); - fs_req.install_req_data(cb); fs_req } - fn open_common<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int, - cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 + pub fn open<P: PathLike>(self, loop_: &Loop, path: &P, flags: int, mode: int, + cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = path.path_as_str(|p| { + path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_open(loop_.native_handle(), - req.native_handle(), p, flags, mode, complete_cb_ptr) as int + self.native_handle(), p, flags, mode, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result } - pub fn open<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int, - cb: FsCallback) { - FsRequest::open_common(loop_, path, flags, mode, Some(cb)); + + pub fn open_sync<P: PathLike>(self, loop_: &Loop, path: &P, + flags: int, mode: int) -> Result<c_int, UvError> { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; + let result = path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_open(loop_.native_handle(), + self.native_handle(), p, flags, mode, complete_cb_ptr) + }) + }); + self.sync_cleanup(result) } - pub fn open_sync<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int) - -> Result<int, UvError> { - let result = FsRequest::open_common(loop_, path, flags, mode, None); - sync_cleanup(result) + pub fn unlink<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_unlink(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); } - fn unlink_common<P: PathLike>(loop_: &Loop, path: &P, cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 + pub fn unlink_sync<P: PathLike>(self, loop_: &Loop, path: &P) + -> Result<c_int, UvError> { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); let result = path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_unlink(loop_.native_handle(), - req.native_handle(), p, complete_cb_ptr) as int + self.native_handle(), p, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result + self.sync_cleanup(result) + } + + pub fn stat<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_stat(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); + } + + pub fn write(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; } - pub fn unlink<P: PathLike>(loop_: &Loop, path: &P, cb: FsCallback) { - let result = FsRequest::unlink_common(loop_, path, Some(cb)); - sync_cleanup(result); + pub fn write_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result<c_int, UvError> { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn read(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; } - pub fn unlink_sync<P: PathLike>(loop_: &Loop, path: &P) -> Result<int, UvError> { - let result = FsRequest::unlink_common(loop_, path, None); - sync_cleanup(result) + pub fn read_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result<c_int, UvError> { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) } - pub fn install_req_data(&self, cb: Option<FsCallback>) { + pub fn close(self, loop_: &Loop, fd: c_int, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + } + pub fn close_sync(self, loop_: &Loop, fd: c_int) -> Result<c_int, UvError> { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(None) + }; + let result = unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn mkdir<P: PathLike>(self, loop_: &Loop, path: &P, mode: int, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_mkdir(loop_.native_handle(), + self.native_handle(), p, mode, complete_cb_ptr) + }) + }); + } + + pub fn rmdir<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_rmdir(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); + } + + pub fn readdir<P: PathLike>(self, loop_: &Loop, path: &P, + flags: c_int, cb: FsCallback) { + let complete_cb_ptr = { + let mut me = self; + me.req_boilerplate(Some(cb)) + }; + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_readdir(loop_.native_handle(), + self.native_handle(), p, flags, complete_cb_ptr) + }) + }); + } + + // accessors/utility funcs + fn sync_cleanup(self, result: c_int) + -> Result<c_int, UvError> { + self.cleanup_and_delete(); + match status_to_maybe_uv_error(result as i32) { + Some(err) => Err(err), + None => Ok(result) + } + } + fn req_boilerplate(&mut self, cb: Option<FsCallback>) -> *u8 { + let result = match cb { + Some(_) => { + compl_cb as *u8 + }, + None => 0 as *u8 + }; + self.install_req_data(cb); + result + } + pub fn install_req_data(&mut self, cb: Option<FsCallback>) { let fs_req = (self.native_handle()) as *uvll::uv_write_t; let data = ~RequestData { - complete_cb: cb, - raw_fd: None + complete_cb: cb }; unsafe { let data = transmute::<~RequestData, *c_void>(data); @@ -106,7 +260,7 @@ impl FsRequest { unsafe { let data = uvll::get_data_for_req((self.native_handle())); let data = transmute::<&*c_void, &mut ~RequestData>(&data); - return &mut **data; + &mut **data } } @@ -120,6 +274,42 @@ impl FsRequest { unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} } } + pub fn get_stat(&self) -> uv_stat_t { + let stat = uv_stat_t::new(); + unsafe { uvll::populate_stat(self.native_handle(), &stat); } + stat + } + + pub fn get_ptr(&self) -> *libc::c_void { + unsafe { + uvll::get_ptr_from_fs_req(self.native_handle()) + } + } + + pub fn get_paths(&mut self) -> ~[~str] { + use str; + let ptr = self.get_ptr(); + match self.get_result() { + n if (n <= 0) => { + ~[] + }, + n => { + let n_len = n as uint; + // we pass in the len that uv tells us is there + // for the entries and we don't continue past that.. + // it appears that sometimes the multistring isn't + // correctly delimited and we stray into garbage memory? + // in any case, passing Some(n_len) fixes it and ensures + // good results + let raw_path_strs = unsafe { + str::raw::from_c_multistring(ptr as *libc::c_char, Some(n_len)) }; + let raw_len = raw_path_strs.len(); + assert_eq!(raw_len, n_len); + raw_path_strs + } + } + } + fn cleanup_and_delete(self) { unsafe { let data = uvll::get_data_for_req(self.native_handle()); @@ -148,97 +338,6 @@ fn sync_cleanup(result: int) } } -pub struct FileDescriptor(c_int); - -impl FileDescriptor { - fn new(fd: c_int) -> FileDescriptor { - FileDescriptor(fd) - } - - - pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor { - FileDescriptor::new(req.get_result()) - } - - // as per bnoordhuis in #libuv: offset >= 0 uses prwrite instead of write - fn write_common(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: Option<FsCallback>) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - let base_ptr = buf.base as *c_void; - let len = buf.len as uint; - req.get_req_data().raw_fd = Some(self.native_handle()); - let result = unsafe { - uvll::fs_write(loop_.native_handle(), req.native_handle(), - self.native_handle(), base_ptr, - len, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.write_common(loop_, buf, offset, Some(cb)); - } - pub fn write_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result<int, UvError> { - let result = self.write_common(loop_, buf, offset, None); - sync_cleanup(result) - } - - fn read_common(&mut self, loop_: &Loop, buf: Buf, - offset: i64, cb: Option<FsCallback>) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - req.get_req_data().raw_fd = Some(self.native_handle()); - let buf_ptr = buf.base as *c_void; - let result = unsafe { - uvll::fs_read(loop_.native_handle(), req.native_handle(), - self.native_handle(), buf_ptr, - buf.len as uint, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.read_common(loop_, buf, offset, Some(cb)); - } - pub fn read_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result<int, UvError> { - let result = self.read_common(loop_, buf, offset, None); - sync_cleanup(result) - } - - fn close_common(self, loop_: &Loop, cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = unsafe { - uvll::fs_close(loop_.native_handle(), req.native_handle(), - self.native_handle(), complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn close(self, loop_: &Loop, cb: FsCallback) { - self.close_common(loop_, Some(cb)); - } - pub fn close_sync(self, loop_: &Loop) -> Result<int, UvError> { - let result = self.close_common(loop_, None); - sync_cleanup(result) - } -} extern fn compl_cb(req: *uv_fs_t) { let mut req: FsRequest = NativeHandle::from_native_handle(req); // pull the user cb out of the req data @@ -261,15 +360,7 @@ extern fn compl_cb(req: *uv_fs_t) { req.cleanup_and_delete(); } -impl NativeHandle<c_int> for FileDescriptor { - fn from_native_handle(handle: c_int) -> FileDescriptor { - FileDescriptor(handle) - } - fn native_handle(&self) -> c_int { - match self { &FileDescriptor(ptr) => ptr } - } -} - +#[cfg(test)] mod test { use super::*; //use rt::test::*; @@ -279,11 +370,11 @@ mod test { use unstable::run_in_bare_thread; use path::Path; use rt::uv::{Loop, Buf, slice_to_uv_buf}; - use libc::{O_CREAT, O_RDWR, O_RDONLY, - S_IWUSR, S_IRUSR}; //NOTE: need defs for S_**GRP|S_**OTH in libc:: ... - //S_IRGRP, S_IROTH}; + use libc::{c_int, O_CREAT, O_RDWR, O_RDONLY, + S_IWUSR, S_IRUSR}; - fn file_test_full_simple_impl() { + #[test] + fn file_test_full_simple() { do run_in_bare_thread { let mut loop_ = Loop::new(); let create_flags = O_RDWR | O_CREAT; @@ -302,25 +393,27 @@ mod test { let read_buf = slice_to_uv_buf(read_mem); let read_buf_ptr: *Buf = &read_buf; let p = Path(path_str); - do FsRequest::open(&loop_, &p, create_flags as int, mode as int) + let open_req = FsRequest::new(); + do open_req.open(&loop_, &p, create_flags as int, mode as int) |req, uverr| { assert!(uverr.is_none()); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let buf = unsafe { *write_buf_ptr }; - do fd.write(&req.get_loop(), buf, -1) |req, uverr| { - let fd = FileDescriptor(raw_fd); - do fd.close(&req.get_loop()) |req, _| { - let loop_ = req.get_loop(); + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, -1) |req, uverr| { + let close_req = FsRequest::new(); + do close_req.close(&req.get_loop(), fd) |req, _| { assert!(uverr.is_none()); - do FsRequest::open(&loop_, &Path(path_str), read_flags as int,0) + let loop_ = req.get_loop(); + let open_req = FsRequest::new(); + do open_req.open(&loop_, &Path(path_str), read_flags as int,0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let read_buf = unsafe { *read_buf_ptr }; - do fd.read(&loop_, read_buf, 0) |req, uverr| { + let read_req = FsRequest::new(); + do read_req.read(&loop_, fd, read_buf, 0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); // we know nread >=0 because uverr is none.. @@ -334,15 +427,17 @@ mod test { read_buf.base, nread)) }; assert!(read_str == ~"hello"); - do FileDescriptor(raw_fd).close(&loop_) |req,uverr| { + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req,uverr| { assert!(uverr.is_none()); let loop_ = &req.get_loop(); - do FsRequest::unlink(loop_, &Path(path_str)) + let unlink_req = FsRequest::new(); + do unlink_req.unlink(loop_, &Path(path_str)) |_,uverr| { assert!(uverr.is_none()); }; }; - } + }; }; }; }; @@ -352,7 +447,9 @@ mod test { loop_.close(); } } - fn file_test_full_simple_impl_sync() { + + #[test] + fn file_test_full_simple_sync() { do run_in_bare_thread { // setup let mut loop_ = Loop::new(); @@ -368,26 +465,31 @@ mod test { let write_val = "hello".as_bytes().to_owned(); let write_buf = slice_to_uv_buf(write_val); // open/create - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), create_flags as int, mode as int); assert!(result.is_ok()); - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // write - let result = fd.write_sync(&loop_, write_buf, -1); + let write_req = FsRequest::new(); + let result = write_req.write_sync(&loop_, fd, write_buf, -1); assert!(result.is_ok()); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // re-open - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), read_flags as int,0); assert!(result.is_ok()); let len = 1028; - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // read let read_mem: ~[u8] = vec::from_elem(len, 0u8); let buf = slice_to_uv_buf(read_mem); - let result = fd.read_sync(&loop_, buf, 0); + let read_req = FsRequest::new(); + let result = read_req.read_sync(&loop_, fd, buf, 0); assert!(result.is_ok()); let nread = result.unwrap(); // nread == 0 would be EOF.. we know it's >= zero because otherwise @@ -397,31 +499,23 @@ mod test { read_mem.slice(0, nread as uint)); assert!(read_str == ~"hello"); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // unlink - let result = FsRequest::unlink_sync(&loop_, &Path(path_str)); + let unlink_req = FsRequest::new(); + let result = unlink_req.unlink_sync(&loop_, &Path(path_str)); assert!(result.is_ok()); } else { fail!("nread was 0.. wudn't expectin' that."); } loop_.close(); } } - #[test] - fn file_test_full_simple() { - file_test_full_simple_impl(); - } - - #[test] - fn file_test_full_simple_sync() { - file_test_full_simple_impl_sync(); - } - fn naive_print(loop_: &Loop, input: &str) { - let mut stdout = FileDescriptor(STDOUT_FILENO); let write_val = input.as_bytes(); let write_buf = slice_to_uv_buf(write_val); - stdout.write_sync(loop_, write_buf, -1); + let write_req = FsRequest::new(); + write_req.write_sync(loop_, STDOUT_FILENO, write_buf, -1); } #[test] @@ -433,4 +527,130 @@ mod test { loop_.close(); }; } + #[test] + fn file_test_stat_simple() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/file_test_stat_simple.txt"; + let create_flags = O_RDWR | + O_CREAT; + let mode = S_IWUSR | + S_IRUSR; + let write_val = "hello".as_bytes().to_owned(); + let write_buf = slice_to_uv_buf(write_val); + let write_buf_ptr: *Buf = &write_buf; + let open_req = FsRequest::new(); + do open_req.open(&loop_, &path, create_flags as int, mode as int) + |req, uverr| { + assert!(uverr.is_none()); + let fd = req.get_result(); + let buf = unsafe { *write_buf_ptr }; + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, 0) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let sz: uint = stat.st_size as uint; + assert!(sz > 0); + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let unlink_req = FsRequest::new(); + do unlink_req.unlink(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |_, uverr| { + // should cause an error because the + // file doesn't exist anymore + assert!(uverr.is_some()); + }; + }; + }; + }; + }; + }; + loop_.run(); + loop_.close(); + } + } + + #[test] + fn file_test_mk_rm_dir() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/mk_rm_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + naive_print(&loop_, fmt!("%?", stat)); + assert!(stat.is_dir()); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_some()); + } + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_mkdir_chokes_on_double_create() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/double_create_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_some()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_rmdir_chokes_on_nonexistant_path() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/never_existed_dir"; + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_some()); + } + loop_.run(); + loop_.close(); + } + } } 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 f3b97441e8e..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}; @@ -32,11 +34,13 @@ use rt::uv::idle::IdleWatcher; use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs}; use rt::uv::addrinfo::GetAddrInfoRequest; use unstable::sync::Exclusive; +use path::Path; use super::super::io::support::PathLike; use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, - S_IRUSR, S_IWUSR}; + S_IRUSR, S_IWUSR, S_IRWXU}; use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, - CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite}; + CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, + FileStat}; use task; #[cfg(test)] use container::Container; @@ -188,11 +192,7 @@ impl UvEventLoop { impl Drop for UvEventLoop { fn drop(&mut self) { - // XXX: Need mutable finalizer - let this = unsafe { - transmute::<&UvEventLoop, &mut UvEventLoop>(self) - }; - this.uvio.uv_loop().close(); + self.uvio.uv_loop().close(); } } @@ -411,6 +411,36 @@ impl UvIoFactory { } } +/// Helper for a variety of simple uv_fs_* functions that +/// have no ret val +fn uv_fs_helper<P: PathLike>(loop_: &mut Loop, path: &P, + cb: ~fn(&mut FsRequest, &mut Loop, &P, + ~fn(&FsRequest, Option<UvError>))) + -> Result<(), IoError> { + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let mut new_req = FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + do cb(&mut new_req, loop_, path) |_, err| { + let res = match err { + None => Ok(()), + Some(err) => Err(uv_error_to_io_error(err)) + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + }; + } + } + assert!(!result_cell.is_empty()); + return result_cell.take(); +} + impl IoFactory for UvIoFactory { // Connect to an address and return a new stream // NB: This blocks the task waiting on the connection. @@ -516,7 +546,6 @@ impl IoFactory for UvIoFactory { fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileStream { let loop_ = Loop {handle: self.uv_loop().native_handle()}; - let fd = file::FileDescriptor(fd); let home = get_handle_to_current_scheduler!(); ~UvFileStream::new(loop_, fd, close_on_drop, home) as ~RtioFileStream } @@ -547,15 +576,16 @@ impl IoFactory for UvIoFactory { let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let open_req = file::FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::open(self.uv_loop(), path, flags as int, create_mode as int) + do open_req.open(self.uv_loop(), path, flags as int, create_mode as int) |req,err| { if err.is_none() { let loop_ = Loop {handle: req.get_loop().native_handle()}; let home = get_handle_to_current_scheduler!(); - let fd = file::FileDescriptor(req.get_result()); + let fd = req.get_result() as c_int; let fs = ~UvFileStream::new( loop_, fd, true, home) as ~RtioFileStream; let res = Ok(fs); @@ -570,31 +600,56 @@ impl IoFactory for UvIoFactory { } }; }; - } + }; assert!(!result_cell.is_empty()); return result_cell.take(); } fn fs_unlink<P: PathLike>(&mut self, path: &P) -> Result<(), IoError> { + do uv_fs_helper(self.uv_loop(), path) |unlink_req, l, p, cb| { + do unlink_req.unlink(l, p) |req, err| { + cb(req, err) + }; + } + } + fn fs_stat<P: PathLike>(&mut self, path: &P) -> Result<FileStat, IoError> { + use str::StrSlice; let result_cell = Cell::new_empty(); - let result_cell_ptr: *Cell<Result<(), IoError>> = &result_cell; + let result_cell_ptr: *Cell<Result<FileStat, + IoError>> = &result_cell; let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let stat_req = file::FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::unlink(self.uv_loop(), path) |_, err| { + let path_str = path.path_as_str(|p| p.to_owned()); + do stat_req.stat(self.uv_loop(), path) + |req,err| { let res = match err { - None => Ok(()), - Some(err) => Err(uv_error_to_io_error(err)) + None => { + let stat = req.get_stat(); + Ok(FileStat { + path: Path(path_str), + is_file: stat.is_file(), + is_dir: stat.is_dir(), + size: stat.st_size, + created: stat.st_ctim.tv_sec as u64, + modified: stat.st_mtim.tv_sec as u64, + accessed: stat.st_atim.tv_sec as u64 + }) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } }; unsafe { (*result_cell_ptr).put_back(res); } let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; }; - } + }; assert!(!result_cell.is_empty()); return result_cell.take(); } @@ -629,6 +684,117 @@ impl IoFactory for UvIoFactory { assert!(!result_cell.is_empty()); return result_cell.take(); } + fn fs_mkdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError> { + let mode = S_IRWXU as int; + do uv_fs_helper(self.uv_loop(), path) |mkdir_req, l, p, cb| { + do mkdir_req.mkdir(l, p, mode as int) |req, err| { + cb(req, err) + }; + } + } + fn fs_rmdir<P: PathLike>(&mut self, path: &P) -> Result<(), IoError> { + do uv_fs_helper(self.uv_loop(), path) |rmdir_req, l, p, cb| { + do rmdir_req.rmdir(l, p) |req, err| { + cb(req, err) + }; + } + } + fn fs_readdir<P: PathLike>(&mut self, path: &P, flags: c_int) -> + Result<~[Path], IoError> { + use str::StrSlice; + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell<Result<~[Path], + IoError>> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let stat_req = file::FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + let path_str = path.path_as_str(|p| p.to_owned()); + do stat_req.readdir(self.uv_loop(), path, flags) + |req,err| { + let res = match err { + None => { + let rel_paths = req.get_paths(); + let mut paths = ~[]; + for r in rel_paths.iter() { + paths.push(Path(path_str+"/"+*r)); + } + Ok(paths) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + }; + }; + }; + 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 { @@ -648,9 +814,7 @@ impl UvTcpListener { impl Drop for UvTcpListener { fn drop(&mut self) { - // XXX need mutable finalizer - let self_ = unsafe { transmute::<&UvTcpListener, &mut UvTcpListener>(self) }; - 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::new(task); do self_.watcher.as_stream().close { @@ -752,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, @@ -763,9 +1047,7 @@ impl HomingIO for UvTcpStream { impl Drop for UvTcpStream { fn drop(&mut self) { - // XXX need mutable finalizer - let this = unsafe { transmute::<&UvTcpStream, &mut UvTcpStream>(self) }; - do this.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); do self_.watcher.as_stream().close { @@ -788,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) } } @@ -922,9 +1147,7 @@ impl HomingIO for UvUdpSocket { impl Drop for UvUdpSocket { fn drop(&mut self) { - // XXX need mutable finalizer - let this = unsafe { transmute::<&UvUdpSocket, &mut UvUdpSocket>(self) }; - do this.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); do self_.watcher.close { @@ -1140,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); @@ -1173,7 +1395,7 @@ impl RtioTimer for UvTimer { pub struct UvFileStream { loop_: Loop, - fd: file::FileDescriptor, + fd: c_int, close_on_drop: bool, home: SchedHandle } @@ -1183,7 +1405,7 @@ impl HomingIO for UvFileStream { } impl UvFileStream { - fn new(loop_: Loop, fd: file::FileDescriptor, close_on_drop: bool, + fn new(loop_: Loop, fd: c_int, close_on_drop: bool, home: SchedHandle) -> UvFileStream { UvFileStream { loop_: loop_, @@ -1200,7 +1422,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.read(&self_.loop_, buf, offset) |req, uverr| { + let read_req = file::FsRequest::new(); + do read_req.read(&self_.loop_, self_.fd, buf, offset) |req, uverr| { let res = match uverr { None => Ok(req.get_result() as int), Some(err) => Err(uv_error_to_io_error(err)) @@ -1221,7 +1444,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.write(&self_.loop_, buf, offset) |_, uverr| { + let write_req = file::FsRequest::new(); + do write_req.write(&self_.loop_, self_.fd, buf, offset) |_, uverr| { let res = match uverr { None => Ok(()), Some(err) => Err(uv_error_to_io_error(err)) @@ -1238,7 +1462,7 @@ impl UvFileStream { Result<u64, IoError>{ #[fixed_stack_segment]; #[inline(never)]; unsafe { - match lseek((*self.fd), pos as off_t, whence) { + match lseek(self.fd, pos as off_t, whence) { -1 => { Err(IoError { kind: OtherIoError, @@ -1254,12 +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); - do self_.fd.close(&self.loop_) |_,_| { + let close_req = file::FsRequest::new(); + do close_req.close(&self_.loop_, self_.fd) |_,_| { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; @@ -1302,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 8f3cef4d238..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,77 +127,95 @@ 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, + tv_nsec: libc::c_long +} + +pub struct uv_stat_t { + st_dev: libc::uint64_t, + st_mode: libc::uint64_t, + st_nlink: libc::uint64_t, + st_uid: libc::uint64_t, + st_gid: libc::uint64_t, + st_rdev: libc::uint64_t, + st_ino: libc::uint64_t, + st_size: libc::uint64_t, + st_blksize: libc::uint64_t, + st_blocks: libc::uint64_t, + st_flags: libc::uint64_t, + st_gen: libc::uint64_t, + st_atim: uv_timespec_t, + st_mtim: uv_timespec_t, + st_ctim: uv_timespec_t, + st_birthtim: uv_timespec_t +} + +impl uv_stat_t { + pub fn new() -> uv_stat_t { + uv_stat_t { + st_dev: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_ino: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_flags: 0, + st_gen: 0, + st_atim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_mtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_ctim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_birthtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 } + } + } + pub fn is_file(&self) -> bool { + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFREG as libc::uint64_t + } + pub fn is_dir(&self) -> bool { + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFDIR as libc::uint64_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; @@ -214,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, @@ -736,18 +791,95 @@ pub unsafe fn fs_close(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, rust_uv_fs_close(loop_ptr, req, fd, cb) } +pub unsafe fn fs_stat(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_stat(loop_ptr, req, path, cb) +} +pub unsafe fn fs_fstat(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_fstat(loop_ptr, req, fd, cb) +} +pub unsafe fn fs_mkdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, mode: int, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_mkdir(loop_ptr, req, path, mode as c_int, cb) +} +pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_rmdir(loop_ptr, req, path, cb) +} +pub unsafe fn fs_readdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + flags: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_readdir(loop_ptr, req, path, flags, cb) +} +pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_populate_uv_stat(req_in, stat_out) +} pub unsafe fn fs_req_cleanup(req: *uv_fs_t) { #[fixed_stack_segment]; #[inline(never)]; 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)]; rust_uv_get_result_from_fs_req(req) } +pub unsafe fn get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_get_ptr_from_fs_req(req) +} pub unsafe fn get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t { #[fixed_stack_segment]; #[inline(never)]; @@ -928,8 +1060,18 @@ extern { buf: *c_void, len: c_uint, offset: i64, cb: *u8) -> c_int; fn rust_uv_fs_close(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_stat(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int; + fn rust_uv_fs_fstat(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_mkdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + mode: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_rmdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int; + fn rust_uv_fs_readdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + flags: c_int, cb: *u8) -> c_int; fn rust_uv_fs_req_cleanup(req: *uv_fs_t); + fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t); fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int; + fn rust_uv_get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void; fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t; fn rust_uv_get_loop_from_getaddrinfo_req(req: *uv_fs_t) -> *uv_loop_t; @@ -949,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 94673ac5a59..362eab17fe7 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -749,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 bd484a5074c..9707d592a2e 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -938,6 +938,7 @@ static TAG_CONT_U8: u8 = 128u8; /// Unsafe operations pub mod raw { + use option::{Option, Some}; use cast; use libc; use ptr; @@ -1091,6 +1092,34 @@ pub mod raw { vec::raw::set_len(as_owned_vec(s), new_len) } + /// Parses a C "multistring", eg windows env values or + /// the req->ptr result in a uv_fs_readdir() call. + /// Optionally, a `count` can be passed in, limiting the + /// parsing to only being done `count`-times. + #[inline] + pub unsafe fn from_c_multistring(buf: *libc::c_char, count: Option<uint>) -> ~[~str] { + #[fixed_stack_segment]; #[inline(never)]; + + let mut curr_ptr: uint = buf as uint; + let mut result = ~[]; + let mut ctr = 0; + let (limited_count, limit) = match count { + Some(limit) => (true, limit), + None => (false, 0) + }; + 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); + curr_ptr += + libc::strlen(curr_ptr as *libc::c_char) as uint + + 1; + ctr += 1; + } + result + } + /// Sets the length of a string /// /// This will explicitly set the size of the string, without actually @@ -1106,6 +1135,25 @@ pub mod raw { } } + #[test] + fn test_str_multistring_parsing() { + use option::None; + unsafe { + let input = bytes!("zero", "\x00", "one", "\x00", "\x00"); + let ptr = vec::raw::to_ptr(input); + let mut result = from_c_multistring(ptr as *libc::c_char, None); + assert!(result.len() == 2); + let mut ctr = 0; + for x in result.iter() { + match ctr { + 0 => assert_eq!(x, &~"zero"), + 1 => assert_eq!(x, &~"one"), + _ => fail!("shouldn't happen!") + } + ctr += 1; + } + } + } } /* 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/atomics.rs b/src/libstd/unstable/atomics.rs index a32b52db1cc..e8835462a80 100644 --- a/src/libstd/unstable/atomics.rs +++ b/src/libstd/unstable/atomics.rs @@ -339,13 +339,7 @@ impl<T> AtomicOption<T> { #[unsafe_destructor] impl<T> Drop for AtomicOption<T> { fn drop(&mut self) { - // This will ensure that the contained data is - // destroyed, unless it's null. - unsafe { - // FIXME(#4330) Need self by value to get mutability. - let this : &mut AtomicOption<T> = cast::transmute(self); - let _ = this.take(SeqCst); - } + let _ = self.take(SeqCst); } } 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/bytes.rs b/src/libsyntax/ext/bytes.rs index cac311b0088..b27fcb6c9b9 100644 --- a/src/libsyntax/ext/bytes.rs +++ b/src/libsyntax/ext/bytes.rs @@ -30,43 +30,43 @@ pub fn expand_syntax_ext(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> bas // string literal, push each byte to vector expression ast::lit_str(s) => { for byte in s.byte_iter() { - bytes.push(cx.expr_u8(sp, byte)); + bytes.push(cx.expr_u8(expr.span, byte)); } } // u8 literal, push to vector expression ast::lit_uint(v, ast::ty_u8) => { if v > 0xFF { - cx.span_err(sp, "Too large u8 literal in bytes!") + cx.span_err(expr.span, "Too large u8 literal in bytes!") } else { - bytes.push(cx.expr_u8(sp, v as u8)); + bytes.push(cx.expr_u8(expr.span, v as u8)); } } // integer literal, push to vector expression ast::lit_int_unsuffixed(v) => { if v > 0xFF { - cx.span_err(sp, "Too large integer literal in bytes!") + cx.span_err(expr.span, "Too large integer literal in bytes!") } else if v < 0 { - cx.span_err(sp, "Negative integer literal in bytes!") + cx.span_err(expr.span, "Negative integer literal in bytes!") } else { - bytes.push(cx.expr_u8(sp, v as u8)); + bytes.push(cx.expr_u8(expr.span, v as u8)); } } // char literal, push to vector expression ast::lit_char(v) => { if char::from_u32(v).unwrap().is_ascii() { - bytes.push(cx.expr_u8(sp, v as u8)); + bytes.push(cx.expr_u8(expr.span, v as u8)); } else { - cx.span_err(sp, "Non-ascii char literal in bytes!") + cx.span_err(expr.span, "Non-ascii char literal in bytes!") } } - _ => cx.span_err(sp, "Unsupported literal in bytes!") + _ => cx.span_err(expr.span, "Unsupported literal in bytes!") }, - _ => cx.span_err(sp, "Non-literal in bytes!") + _ => cx.span_err(expr.span, "Non-literal in bytes!") } } 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..7254e8e775a 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> = @@ -975,8 +971,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/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_uv.cpp b/src/rt/rust_uv.cpp index bfdf0e67a9b..3e9b8ba136e 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -542,6 +542,10 @@ extern "C" int rust_uv_get_result_from_fs_req(uv_fs_t* req) { return req->result; } +extern "C" void* +rust_uv_get_ptr_from_fs_req(uv_fs_t* req) { + return req->ptr; +} extern "C" uv_loop_t* rust_uv_get_loop_from_fs_req(uv_fs_t* req) { return req->loop; @@ -551,3 +555,85 @@ extern "C" uv_loop_t* rust_uv_get_loop_from_getaddrinfo_req(uv_getaddrinfo_t* req) { return req->loop; } + +extern "C" int +rust_uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_stat(loop, req, path, cb); +} +extern "C" int +rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + return uv_fs_fstat(loop, req, file, cb); +} + +extern "C" void +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { + stat_out->st_dev = req_in->statbuf.st_dev; + stat_out->st_mode = req_in->statbuf.st_mode; + stat_out->st_nlink = req_in->statbuf.st_nlink; + stat_out->st_uid = req_in->statbuf.st_uid; + stat_out->st_gid = req_in->statbuf.st_gid; + stat_out->st_rdev = req_in->statbuf.st_rdev; + stat_out->st_ino = req_in->statbuf.st_ino; + stat_out->st_size = req_in->statbuf.st_size; + stat_out->st_blksize = req_in->statbuf.st_blksize; + stat_out->st_blocks = req_in->statbuf.st_blocks; + stat_out->st_flags = req_in->statbuf.st_flags; + stat_out->st_gen = req_in->statbuf.st_gen; + stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; + stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; + stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; + stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; +} + +extern "C" int +rust_uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + return uv_fs_mkdir(loop, req, path, mode, cb); +} +extern "C" int +rust_uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_rmdir(loop, req, path, cb); +} + +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 93e8d34ca5f..7a9149187d8 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -112,8 +112,15 @@ rust_uv_fs_write rust_uv_fs_read rust_uv_fs_close rust_uv_get_result_from_fs_req +rust_uv_get_ptr_from_fs_req rust_uv_get_loop_from_fs_req +rust_uv_fs_stat +rust_uv_fs_fstat rust_uv_fs_req_cleanup +rust_uv_populate_uv_stat +rust_uv_fs_mkdir +rust_uv_fs_rmdir +rust_uv_fs_readdir rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock @@ -190,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/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/auxiliary/xcrate_address_insignificant.rs b/src/test/auxiliary/xcrate_address_insignificant.rs new file mode 100644 index 00000000000..08e3eff0c8c --- /dev/null +++ b/src/test/auxiliary/xcrate_address_insignificant.rs @@ -0,0 +1,19 @@ +// 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 foo<T>() -> int { + #[address_insignificant] + static a: int = 3; + a +} + +pub fn bar() -> int { + foo::<int>() +} 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/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/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 } diff --git a/src/test/run-pass/xcrate-address-insignificant.rs b/src/test/run-pass/xcrate-address-insignificant.rs new file mode 100644 index 00000000000..1bf3763834a --- /dev/null +++ b/src/test/run-pass/xcrate-address-insignificant.rs @@ -0,0 +1,18 @@ +// 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. + +// xfail-fast windows doesn't like aux-build +// aux-build:xcrate_address_insignificant.rs + +extern mod foo(name = "xcrate_address_insignificant"); + +fn main() { + assert_eq!(foo::foo::<float>(), foo::bar()); +} |
