Move platform-specific arg handling to sys::args

This commit is contained in:
Brian Anderson 2016-09-29 22:00:44 +00:00
parent 29e0235415
commit 4d76ac8492
11 changed files with 294 additions and 282 deletions

View file

@ -21,6 +21,7 @@ use ffi::{OsStr, OsString};
use fmt;
use io;
use path::{Path, PathBuf};
use sys;
use sys::os as os_imp;
/// Returns the current working directory as a `PathBuf`.
@ -557,7 +558,7 @@ pub struct Args { inner: ArgsOs }
///
/// This structure is created through the `std::env::args_os` method.
#[stable(feature = "env", since = "1.0.0")]
pub struct ArgsOs { inner: os_imp::Args }
pub struct ArgsOs { inner: sys::args::Args }
/// Returns the arguments which this program was started with (normally passed
/// via the command line).
@ -606,7 +607,7 @@ pub fn args() -> Args {
/// ```
#[stable(feature = "env", since = "1.0.0")]
pub fn args_os() -> ArgsOs {
ArgsOs { inner: os_imp::args() }
ArgsOs { inner: sys::args::args() }
}
#[stable(feature = "env", since = "1.0.0")]

View file

@ -51,7 +51,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
thread_info::set(main_guard, thread);
// Store our args if necessary in a squirreled away location
sys_common::args::init(argc, argv);
sys::args::init(argc, argv);
// Let's run some code!
let res = panic::catch_unwind(mem::transmute::<_, fn()>(main));

View file

@ -1,100 +0,0 @@
// Copyright 2012-2015 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.
//! Global storage for command line arguments
//!
//! The current incarnation of the Rust runtime expects for
//! the processes `argc` and `argv` arguments to be stored
//! in a globally-accessible location for use by the `os` module.
//!
//! Only valid to call on Linux. Mac and Windows use syscalls to
//! discover the command line arguments.
//!
//! FIXME #7756: Would be nice for this to not exist.
#![allow(dead_code)] // different code on OSX/linux/etc
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
/// One-time global cleanup.
pub unsafe fn cleanup() { imp::cleanup() }
/// Make a clone of the global arguments.
pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() }
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "emscripten",
target_os = "haiku"))]
mod imp {
use libc::c_char;
use mem;
use ffi::CStr;
use sys_common::mutex::Mutex;
static mut GLOBAL_ARGS_PTR: usize = 0;
static LOCK: Mutex = Mutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) {
let args = (0..argc).map(|i| {
CStr::from_ptr(*argv.offset(i) as *const c_char).to_bytes().to_vec()
}).collect();
LOCK.lock();
let ptr = get_global_ptr();
assert!((*ptr).is_none());
(*ptr) = Some(box args);
LOCK.unlock();
}
pub unsafe fn cleanup() {
LOCK.lock();
*get_global_ptr() = None;
LOCK.unlock();
}
pub fn clone() -> Option<Vec<Vec<u8>>> {
unsafe {
LOCK.lock();
let ptr = get_global_ptr();
let ret = (*ptr).as_ref().map(|s| (**s).clone());
LOCK.unlock();
return ret
}
}
fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> {
unsafe { mem::transmute(&GLOBAL_ARGS_PTR) }
}
}
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "windows"))]
mod imp {
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
}
pub fn cleanup() {
}
pub fn clone() -> Option<Vec<Vec<u8>>> {
panic!()
}
}

View file

@ -25,7 +25,6 @@ macro_rules! rtassert {
})
}
pub mod args;
pub mod at_exit_imp;
#[cfg(any(not(cargobuild), feature = "backtrace"))]
pub mod backtrace;
@ -92,7 +91,7 @@ pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
args::cleanup();
sys::args::cleanup();
sys::stack_overflow::cleanup();
at_exit_imp::cleanup();
});

211
src/libstd/sys/unix/args.rs Normal file
View file

@ -0,0 +1,211 @@
// Copyright 2012-2015 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.
//! Global initialization and retreival of command line arguments.
//!
//! On some platforms these are stored during runtime startup,
//! and on some they are retrieved from the system on demand.
#![allow(dead_code)] // runtime init functions not used during testing
use ffi::OsString;
use marker::PhantomData;
use vec;
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
/// One-time global cleanup.
pub unsafe fn cleanup() { imp::cleanup() }
/// Returns the command line arguments
pub fn args() -> Args {
imp::args()
}
pub struct Args {
iter: vec::IntoIter<OsString>,
_dont_send_or_sync_me: PhantomData<*mut ()>,
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.iter.len() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
}
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "emscripten",
target_os = "haiku"))]
mod imp {
use os::unix::prelude::*;
use mem;
use ffi::{CStr, OsString};
use marker::PhantomData;
use libc;
use super::Args;
use sys_common::mutex::Mutex;
static mut GLOBAL_ARGS_PTR: usize = 0;
static LOCK: Mutex = Mutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) {
let args = (0..argc).map(|i| {
CStr::from_ptr(*argv.offset(i) as *const libc::c_char).to_bytes().to_vec()
}).collect();
LOCK.lock();
let ptr = get_global_ptr();
assert!((*ptr).is_none());
(*ptr) = Some(box args);
LOCK.unlock();
}
pub unsafe fn cleanup() {
LOCK.lock();
*get_global_ptr() = None;
LOCK.unlock();
}
pub fn args() -> Args {
let bytes = clone().unwrap_or(Vec::new());
let v: Vec<OsString> = bytes.into_iter().map(|v| {
OsStringExt::from_vec(v)
}).collect();
Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData }
}
fn clone() -> Option<Vec<Vec<u8>>> {
unsafe {
LOCK.lock();
let ptr = get_global_ptr();
let ret = (*ptr).as_ref().map(|s| (**s).clone());
LOCK.unlock();
return ret
}
}
fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> {
unsafe { mem::transmute(&GLOBAL_ARGS_PTR) }
}
}
#[cfg(any(target_os = "macos",
target_os = "ios"))]
mod imp {
use ffi::CStr;
use marker::PhantomData;
use libc;
use super::Args;
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
}
pub fn cleanup() {
}
#[cfg(target_os = "macos")]
pub fn args() -> Args {
use os::unix::prelude::*;
extern {
// These functions are in crt_externs.h.
fn _NSGetArgc() -> *mut libc::c_int;
fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
}
let vec = unsafe {
let (argc, argv) = (*_NSGetArgc() as isize,
*_NSGetArgv() as *const *const libc::c_char);
(0.. argc as isize).map(|i| {
let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
OsStringExt::from_vec(bytes)
}).collect::<Vec<_>>()
};
Args {
iter: vec.into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}
// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
// and use underscores in their names - they're most probably
// are considered private and therefore should be avoided
// Here is another way to get arguments using Objective C
// runtime
//
// In general it looks like:
// res = Vec::new()
// let args = [[NSProcessInfo processInfo] arguments]
// for i in (0..[args count])
// res.push([args objectAtIndex:i])
// res
#[cfg(target_os = "ios")]
pub fn args() -> Args {
use ffi::OsString;
use mem;
use str;
extern {
fn sel_registerName(name: *const libc::c_uchar) -> Sel;
fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
}
#[link(name = "Foundation", kind = "framework")]
#[link(name = "objc")]
#[cfg(not(cargobuild))]
extern {}
type Sel = *const libc::c_void;
type NsId = *const libc::c_void;
let mut res = Vec::new();
unsafe {
let process_info_sel = sel_registerName("processInfo\0".as_ptr());
let arguments_sel = sel_registerName("arguments\0".as_ptr());
let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
let count_sel = sel_registerName("count\0".as_ptr());
let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
let klass = objc_getClass("NSProcessInfo\0".as_ptr());
let info = objc_msgSend(klass, process_info_sel);
let args = objc_msgSend(info, arguments_sel);
let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
for i in 0..cnt {
let tmp = objc_msgSend(args, object_at_sel, i);
let utf_c_str: *const libc::c_char =
mem::transmute(objc_msgSend(tmp, utf8_sel));
let bytes = CStr::from_ptr(utf_c_str).to_bytes();
res.push(OsString::from(str::from_utf8(bytes).unwrap()))
}
}
Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
}
}

View file

@ -30,6 +30,7 @@ use libc;
#[macro_use]
pub mod weak;
pub mod args;
pub mod android;
#[cfg(any(not(cargobuild), feature = "backtrace"))]
pub mod backtrace;

View file

@ -347,126 +347,6 @@ pub fn current_exe() -> io::Result<PathBuf> {
}
}
pub struct Args {
iter: vec::IntoIter<OsString>,
_dont_send_or_sync_me: PhantomData<*mut ()>,
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.iter.len() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
}
/// Returns the command line arguments
///
/// Returns a list of the command line arguments.
#[cfg(target_os = "macos")]
pub fn args() -> Args {
extern {
// These functions are in crt_externs.h.
fn _NSGetArgc() -> *mut c_int;
fn _NSGetArgv() -> *mut *mut *mut c_char;
}
let vec = unsafe {
let (argc, argv) = (*_NSGetArgc() as isize,
*_NSGetArgv() as *const *const c_char);
(0.. argc as isize).map(|i| {
let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
OsStringExt::from_vec(bytes)
}).collect::<Vec<_>>()
};
Args {
iter: vec.into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}
// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
// and use underscores in their names - they're most probably
// are considered private and therefore should be avoided
// Here is another way to get arguments using Objective C
// runtime
//
// In general it looks like:
// res = Vec::new()
// let args = [[NSProcessInfo processInfo] arguments]
// for i in (0..[args count])
// res.push([args objectAtIndex:i])
// res
#[cfg(target_os = "ios")]
pub fn args() -> Args {
use mem;
extern {
fn sel_registerName(name: *const libc::c_uchar) -> Sel;
fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
}
#[link(name = "Foundation", kind = "framework")]
#[link(name = "objc")]
#[cfg(not(cargobuild))]
extern {}
type Sel = *const libc::c_void;
type NsId = *const libc::c_void;
let mut res = Vec::new();
unsafe {
let process_info_sel = sel_registerName("processInfo\0".as_ptr());
let arguments_sel = sel_registerName("arguments\0".as_ptr());
let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
let count_sel = sel_registerName("count\0".as_ptr());
let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
let klass = objc_getClass("NSProcessInfo\0".as_ptr());
let info = objc_msgSend(klass, process_info_sel);
let args = objc_msgSend(info, arguments_sel);
let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
for i in 0..cnt {
let tmp = objc_msgSend(args, object_at_sel, i);
let utf_c_str: *const libc::c_char =
mem::transmute(objc_msgSend(tmp, utf8_sel));
let bytes = CStr::from_ptr(utf_c_str).to_bytes();
res.push(OsString::from(str::from_utf8(bytes).unwrap()))
}
}
Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
}
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "bitrig",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "nacl",
target_os = "emscripten",
target_os = "haiku"))]
pub fn args() -> Args {
use sys_common;
let bytes = sys_common::args::clone().unwrap_or(Vec::new());
let v: Vec<OsString> = bytes.into_iter().map(|v| {
OsStringExt::from_vec(v)
}).collect();
Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData }
}
pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>,
_dont_send_or_sync_me: PhantomData<*mut ()>,

View file

@ -0,0 +1,76 @@
// Copyright 2012-2015 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.
#![allow(dead_code)] // runtime init functions not used during testing
use os::windows::prelude::*;
use sys::c;
use slice;
use ops::Range;
use ffi::OsString;
use libc::{c_int, c_void};
pub unsafe fn init(_argc: isize, _argv: *const *const u8) { }
pub unsafe fn cleanup() { }
pub fn args() -> Args {
unsafe {
let mut nArgs: c_int = 0;
let lpCmdLine = c::GetCommandLineW();
let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
// szArcList can be NULL if CommandLinToArgvW failed,
// but in that case nArgs is 0 so we won't actually
// try to read a null pointer
Args { cur: szArgList, range: 0..(nArgs as isize) }
}
}
pub struct Args {
range: Range<isize>,
cur: *mut *mut u16,
}
unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString {
let mut len = 0;
while *ptr.offset(len) != 0 { len += 1; }
// Push it onto the list.
let ptr = ptr as *const u16;
let buf = slice::from_raw_parts(ptr, len as usize);
OsStringExt::from_wide(buf)
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.range.len() }
}
impl Drop for Args {
fn drop(&mut self) {
// self.cur can be null if CommandLineToArgvW previously failed,
// but LocalFree ignores NULL pointers
unsafe { c::LocalFree(self.cur as *mut c_void); }
}
}

View file

@ -18,6 +18,7 @@ use time::Duration;
#[macro_use] pub mod compat;
pub mod args;
pub mod backtrace;
pub mod c;
pub mod condvar;

View file

@ -18,8 +18,6 @@ use error::Error as StdError;
use ffi::{OsString, OsStr};
use fmt;
use io;
use libc::{c_int, c_void};
use ops::Range;
use os::windows::ffi::EncodeWide;
use path::{self, PathBuf};
use ptr;
@ -272,60 +270,6 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
}).map(|_| ())
}
pub struct Args {
range: Range<isize>,
cur: *mut *mut u16,
}
unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString {
let mut len = 0;
while *ptr.offset(len) != 0 { len += 1; }
// Push it onto the list.
let ptr = ptr as *const u16;
let buf = slice::from_raw_parts(ptr, len as usize);
OsStringExt::from_wide(buf)
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.range.len() }
}
impl Drop for Args {
fn drop(&mut self) {
// self.cur can be null if CommandLineToArgvW previously failed,
// but LocalFree ignores NULL pointers
unsafe { c::LocalFree(self.cur as *mut c_void); }
}
}
pub fn args() -> Args {
unsafe {
let mut nArgs: c_int = 0;
let lpCmdLine = c::GetCommandLineW();
let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);
// szArcList can be NULL if CommandLinToArgvW failed,
// but in that case nArgs is 0 so we won't actually
// try to read a null pointer
Args { cur: szArgList, range: 0..(nArgs as isize) }
}
}
pub fn temp_dir() -> PathBuf {
super::fill_utf16_buf(|buf, sz| unsafe {
c::GetTempPathW(sz, buf)

View file

@ -71,7 +71,6 @@ const EXCEPTION_PATHS: &'static [&'static str] = &[
"src/libstd/num/f64.rs",
"src/libstd/thread/local.rs",
"src/libstd/sys/common/mod.rs",
"src/libstd/sys/common/args.rs",
"src/libstd/sys/common/net.rs",
"src/libstd/sys/common/util.rs",
"src/libterm", // Not sure how to make this crate portable, but test needs it