This round: io::Result and the free functions.
endif
RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \
- ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp)
+ ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp \
+ ArchiveWrapper.cpp)
RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \
$$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \
pub const F_GETFL : c_int = 3;
pub const F_SETFL : c_int = 4;
+ pub const O_ACCMODE : c_int = 3;
+
pub const SIGTRAP : c_int = 5;
pub const SIG_IGN: size_t = 1;
pub const O_DSYNC : c_int = 4194304;
pub const O_SYNC : c_int = 128;
pub const O_NONBLOCK : c_int = 4;
+ pub const F_GETPATH : c_int = 50;
pub const F_FULLFSYNC : c_int = 51;
pub const MAP_COPY : c_int = 0x0002;
pub const SO_DONTTRUNC: c_int = 0x2000;
pub const SO_WANTMORE: c_int = 0x4000;
pub const SO_WANTOOBFLAG: c_int = 0x8000;
+
+ pub const PATH_MAX: c_int = 1024;
}
pub mod sysconf {
use types::os::arch::c95::c_int;
pub mod back {
pub use rustc_back::abi;
- pub use rustc_back::archive;
pub use rustc_back::arm;
pub use rustc_back::mips;
pub use rustc_back::mipsel;
//! no means all of the necessary details. Take a look at the rest of
//! metadata::loader or metadata::creader for all the juicy details!
-use back::archive::METADATA_FILENAME;
use back::svh::Svh;
use session::Session;
use session::search_paths::PathKind;
pub rlib: Option<PathBuf>
}
+pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";
+
impl CratePaths {
fn paths(&self) -> Vec<PathBuf> {
match (&self.dylib, &self.rlib) {
+++ /dev/null
-// Copyright 2013-2014 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.
-
-//! A helper class for dealing with static archives
-
-use std::env;
-use std::ffi::OsString;
-use std::fs::{self, File};
-use std::io::prelude::*;
-use std::io;
-use std::path::{Path, PathBuf};
-use std::process::{Command, Output, Stdio};
-use std::str;
-use syntax::diagnostic::Handler as ErrorHandler;
-use rustc_llvm::archive_ro::ArchiveRO;
-
-use tempdir::TempDir;
-
-pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";
-
-pub struct ArchiveConfig<'a> {
- pub handler: &'a ErrorHandler,
- pub dst: PathBuf,
- pub lib_search_paths: Vec<PathBuf>,
- pub slib_prefix: String,
- pub slib_suffix: String,
- pub ar_prog: String,
- pub command_path: OsString,
-}
-
-pub struct Archive<'a> {
- config: ArchiveConfig<'a>,
-}
-
-/// Helper for adding many files to an archive with a single invocation of
-/// `ar`.
-#[must_use = "must call build() to finish building the archive"]
-pub struct ArchiveBuilder<'a> {
- archive: Archive<'a>,
- work_dir: TempDir,
- /// Filename of each member that should be added to the archive.
- members: Vec<PathBuf>,
- should_update_symbols: bool,
-}
-
-enum Action<'a> {
- Remove(&'a Path),
- AddObjects(&'a [&'a PathBuf], bool),
- UpdateSymbols,
-}
-
-pub fn find_library(name: &str, osprefix: &str, ossuffix: &str,
- search_paths: &[PathBuf],
- handler: &ErrorHandler) -> PathBuf {
- // On Windows, static libraries sometimes show up as libfoo.a and other
- // times show up as foo.lib
- let oslibname = format!("{}{}{}", osprefix, name, ossuffix);
- let unixlibname = format!("lib{}.a", name);
-
- for path in search_paths {
- debug!("looking for {} inside {:?}", name, path);
- let test = path.join(&oslibname[..]);
- if test.exists() { return test }
- if oslibname != unixlibname {
- let test = path.join(&unixlibname[..]);
- if test.exists() { return test }
- }
- }
- handler.fatal(&format!("could not find native static library `{}`, \
- perhaps an -L flag is missing?",
- name));
-}
-
-impl<'a> Archive<'a> {
- fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
- Archive { config: config }
- }
-
- /// Opens an existing static archive
- pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
- let archive = Archive::new(config);
- assert!(archive.config.dst.exists());
- archive
- }
-
- /// Removes a file from this archive
- pub fn remove_file(&mut self, file: &str) {
- self.run(None, Action::Remove(Path::new(file)));
- }
-
- /// Lists all files in an archive
- pub fn files(&self) -> Vec<String> {
- let archive = match ArchiveRO::open(&self.config.dst) {
- Some(ar) => ar,
- None => return Vec::new(),
- };
- let ret = archive.iter().filter_map(|child| child.name())
- .map(|name| name.to_string())
- .collect();
- return ret;
- }
-
- /// Creates an `ArchiveBuilder` for adding files to this archive.
- pub fn extend(self) -> ArchiveBuilder<'a> {
- ArchiveBuilder::new(self)
- }
-
- fn run(&self, cwd: Option<&Path>, action: Action) -> Output {
- let abs_dst = env::current_dir().unwrap().join(&self.config.dst);
- let ar = &self.config.ar_prog;
- let mut cmd = Command::new(ar);
- cmd.env("PATH", &self.config.command_path);
- cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
- self.prepare_ar_action(&mut cmd, &abs_dst, action);
- info!("{:?}", cmd);
-
- if let Some(p) = cwd {
- cmd.current_dir(p);
- info!("inside {:?}", p.display());
- }
-
- let handler = &self.config.handler;
- match cmd.spawn() {
- Ok(prog) => {
- let o = prog.wait_with_output().unwrap();
- if !o.status.success() {
- handler.err(&format!("{:?} failed with: {}", cmd, o.status));
- handler.note(&format!("stdout ---\n{}",
- str::from_utf8(&o.stdout).unwrap()));
- handler.note(&format!("stderr ---\n{}",
- str::from_utf8(&o.stderr).unwrap()));
- handler.abort_if_errors();
- }
- o
- },
- Err(e) => {
- handler.err(&format!("could not exec `{}`: {}",
- self.config.ar_prog, e));
- handler.abort_if_errors();
- panic!("rustc::back::archive::run() should not reach this point");
- }
- }
- }
-
- fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) {
- match action {
- Action::Remove(file) => {
- cmd.arg("d").arg(dst).arg(file);
- }
- Action::AddObjects(objs, update_symbols) => {
- cmd.arg(if update_symbols {"crs"} else {"crS"})
- .arg(dst)
- .args(objs);
- }
- Action::UpdateSymbols => {
- cmd.arg("s").arg(dst);
- }
- }
- }
-}
-
-impl<'a> ArchiveBuilder<'a> {
- fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> {
- ArchiveBuilder {
- archive: archive,
- work_dir: TempDir::new("rsar").unwrap(),
- members: vec![],
- should_update_symbols: false,
- }
- }
-
- /// Create a new static archive, ready for adding files.
- pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
- let archive = Archive::new(config);
- ArchiveBuilder::new(archive)
- }
-
- /// Adds all of the contents of a native library to this archive. This will
- /// search in the relevant locations for a library named `name`.
- pub fn add_native_library(&mut self, name: &str) -> io::Result<()> {
- let location = find_library(name,
- &self.archive.config.slib_prefix,
- &self.archive.config.slib_suffix,
- &self.archive.config.lib_search_paths,
- self.archive.config.handler);
- self.add_archive(&location, name, |_| false)
- }
-
- /// Adds all of the contents of the rlib at the specified path to this
- /// archive.
- ///
- /// This ignores adding the bytecode from the rlib, and if LTO is enabled
- /// then the object file also isn't added.
- pub fn add_rlib(&mut self, rlib: &Path, name: &str,
- lto: bool) -> io::Result<()> {
- // Ignoring obj file starting with the crate name
- // as simple comparison is not enough - there
- // might be also an extra name suffix
- let obj_start = format!("{}", name);
- let obj_start = &obj_start[..];
- // Ignoring all bytecode files, no matter of
- // name
- let bc_ext = ".bytecode.deflate";
-
- self.add_archive(rlib, &name[..], |fname: &str| {
- let skip_obj = lto && fname.starts_with(obj_start)
- && fname.ends_with(".o");
- skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME
- })
- }
-
- /// Adds an arbitrary file to this archive
- pub fn add_file(&mut self, file: &Path) -> io::Result<()> {
- let filename = Path::new(file.file_name().unwrap());
- let new_file = self.work_dir.path().join(&filename);
- try!(fs::copy(file, &new_file));
- self.members.push(filename.to_path_buf());
- Ok(())
- }
-
- /// Indicate that the next call to `build` should updates all symbols in
- /// the archive (run 'ar s' over it).
- pub fn update_symbols(&mut self) {
- self.should_update_symbols = true;
- }
-
- /// Combine the provided files, rlibs, and native libraries into a single
- /// `Archive`.
- pub fn build(self) -> Archive<'a> {
- // Get an absolute path to the destination, so `ar` will work even
- // though we run it from `self.work_dir`.
- let mut objects = Vec::new();
- let mut total_len = self.archive.config.dst.to_string_lossy().len();
-
- if self.members.is_empty() {
- if self.should_update_symbols {
- self.archive.run(Some(self.work_dir.path()),
- Action::UpdateSymbols);
- }
- return self.archive;
- }
-
- // Don't allow the total size of `args` to grow beyond 32,000 bytes.
- // Windows will raise an error if the argument string is longer than
- // 32,768, and we leave a bit of extra space for the program name.
- const ARG_LENGTH_LIMIT: usize = 32_000;
-
- for member_name in &self.members {
- let len = member_name.to_string_lossy().len();
-
- // `len + 1` to account for the space that's inserted before each
- // argument. (Windows passes command-line arguments as a single
- // string, not an array of strings.)
- if total_len + len + 1 > ARG_LENGTH_LIMIT {
- // Add the archive members seen so far, without updating the
- // symbol table.
- self.archive.run(Some(self.work_dir.path()),
- Action::AddObjects(&objects, false));
-
- objects.clear();
- total_len = self.archive.config.dst.to_string_lossy().len();
- }
-
- objects.push(member_name);
- total_len += len + 1;
- }
-
- // Add the remaining archive members, and update the symbol table if
- // necessary.
- self.archive.run(Some(self.work_dir.path()),
- Action::AddObjects(&objects, self.should_update_symbols));
-
- self.archive
- }
-
- fn add_archive<F>(&mut self, archive: &Path, name: &str,
- mut skip: F) -> io::Result<()>
- where F: FnMut(&str) -> bool,
- {
- let archive = match ArchiveRO::open(archive) {
- Some(ar) => ar,
- None => return Err(io::Error::new(io::ErrorKind::Other,
- "failed to open archive")),
- };
-
- // Next, we must rename all of the inputs to "guaranteed unique names".
- // We write each file into `self.work_dir` under its new unique name.
- // The reason for this renaming is that archives are keyed off the name
- // of the files, so if two files have the same name they will override
- // one another in the archive (bad).
- //
- // We skip any files explicitly desired for skipping, and we also skip
- // all SYMDEF files as these are just magical placeholders which get
- // re-created when we make a new archive anyway.
- for file in archive.iter() {
- let filename = match file.name() {
- Some(s) => s,
- None => continue,
- };
- if filename.contains(".SYMDEF") { continue }
- if skip(filename) { continue }
- let filename = Path::new(filename).file_name().unwrap()
- .to_str().unwrap();
-
- // Archives on unix systems typically do not have slashes in
- // filenames as the `ar` utility generally only uses the last
- // component of a path for the filename list in the archive. On
- // Windows, however, archives assembled with `lib.exe` will preserve
- // the full path to the file that was placed in the archive,
- // including path separators.
- //
- // The code below is munging paths so it'll go wrong pretty quickly
- // if there's some unexpected slashes in the filename, so here we
- // just chop off everything but the filename component. Note that
- // this can cause duplicate filenames, but that's also handled below
- // as well.
- let filename = Path::new(filename).file_name().unwrap()
- .to_str().unwrap();
-
- // An archive can contain files of the same name multiple times, so
- // we need to be sure to not have them overwrite one another when we
- // extract them. Consequently we need to find a truly unique file
- // name for us!
- let mut new_filename = String::new();
- for n in 0.. {
- let n = if n == 0 {String::new()} else {format!("-{}", n)};
- new_filename = format!("r{}-{}-{}", n, name, filename);
-
- // LLDB (as mentioned in back::link) crashes on filenames of
- // exactly
- // 16 bytes in length. If we're including an object file with
- // exactly 16-bytes of characters, give it some prefix so
- // that it's not 16 bytes.
- new_filename = if new_filename.len() == 16 {
- format!("lldb-fix-{}", new_filename)
- } else {
- new_filename
- };
-
- let present = self.members.iter().filter_map(|p| {
- p.file_name().and_then(|f| f.to_str())
- }).any(|s| s == new_filename);
- if !present {
- break
- }
- }
- let dst = self.work_dir.path().join(&new_filename);
- try!(try!(File::create(&dst)).write_all(file.data()));
- self.members.push(PathBuf::from(new_filename));
- }
-
- Ok(())
- }
-}
#[macro_use] extern crate log;
pub mod abi;
-pub mod archive;
pub mod tempdir;
pub mod arm;
pub mod mips;
"-Wl,--as-needed".to_string(),
],
position_independent_executables: true,
+ archive_format: "gnu".to_string(),
.. Default::default()
}
}
/// the functions in the executable are not randomized and can be used
/// during an exploit of a vulnerability in any code.
pub position_independent_executables: bool,
+ /// Format that archives should be emitted in. This affects whether we use
+ /// LLVM to assemble an archive or fall back to the system linker, and
+ /// currently only "gnu" is used to fall into LLVM. Unknown strings cause
+ /// the system linker to be used.
+ pub archive_format: String,
}
impl Default for TargetOptions {
position_independent_executables: false,
pre_link_objects: Vec::new(),
post_link_objects: Vec::new(),
+ archive_format: String::new(),
}
}
}
staticlib_suffix: ".lib".to_string(),
morestack: false,
is_like_windows: true,
+ archive_format: "gnu".to_string(),
pre_link_args: vec!(
// And here, we see obscure linker flags #45. On windows, it has been
// found to be necessary to have this flag to compile liblibc.
use ArchiveRef;
use std::ffi::CString;
+use std::marker;
use std::path::Path;
use std::slice;
use std::str;
}
pub struct Child<'a> {
- name: Option<&'a str>,
- data: &'a [u8],
+ ptr: ::ArchiveChildRef,
+ _data: marker::PhantomData<&'a ArchiveRO>,
}
impl ArchiveRO {
}
}
+ pub fn raw(&self) -> ArchiveRef { self.ptr }
+
pub fn iter(&self) -> Iter {
unsafe {
Iter { ptr: ::LLVMRustArchiveIteratorNew(self.ptr), archive: self }
type Item = Child<'a>;
fn next(&mut self) -> Option<Child<'a>> {
- unsafe {
- let ptr = ::LLVMRustArchiveIteratorCurrent(self.ptr);
- if ptr.is_null() {
- return None
- }
- let mut name_len = 0;
- let name_ptr = ::LLVMRustArchiveChildName(ptr, &mut name_len);
- let mut data_len = 0;
- let data_ptr = ::LLVMRustArchiveChildData(ptr, &mut data_len);
- let child = Child {
- name: if name_ptr.is_null() {
- None
- } else {
- let name = slice::from_raw_parts(name_ptr as *const u8,
- name_len as usize);
- str::from_utf8(name).ok().map(|s| s.trim())
- },
- data: slice::from_raw_parts(data_ptr as *const u8,
- data_len as usize),
- };
- ::LLVMRustArchiveIteratorNext(self.ptr);
- Some(child)
+ let ptr = unsafe { ::LLVMRustArchiveIteratorNext(self.ptr) };
+ if ptr.is_null() {
+ None
+ } else {
+ Some(Child { ptr: ptr, _data: marker::PhantomData })
}
}
}
}
impl<'a> Child<'a> {
- pub fn name(&self) -> Option<&'a str> { self.name }
- pub fn data(&self) -> &'a [u8] { self.data }
+ pub fn name(&self) -> Option<&'a str> {
+ unsafe {
+ let mut name_len = 0;
+ let name_ptr = ::LLVMRustArchiveChildName(self.ptr, &mut name_len);
+ if name_ptr.is_null() {
+ None
+ } else {
+ let name = slice::from_raw_parts(name_ptr as *const u8,
+ name_len as usize);
+ str::from_utf8(name).ok().map(|s| s.trim())
+ }
+ }
+ }
+
+ pub fn data(&self) -> &'a [u8] {
+ unsafe {
+ let mut data_len = 0;
+ let data_ptr = ::LLVMRustArchiveChildData(self.ptr, &mut data_len);
+ slice::from_raw_parts(data_ptr as *const u8, data_len as usize)
+ }
+ }
+
+ pub fn raw(&self) -> ::ArchiveChildRef { self.ptr }
+}
+
+impl<'a> Drop for Child<'a> {
+ fn drop(&mut self) {
+ unsafe { ::LLVMRustArchiveChildFree(self.ptr); }
+ }
}
#[allow(missing_copy_implementations)]
pub enum SMDiagnostic_opaque {}
pub type SMDiagnosticRef = *mut SMDiagnostic_opaque;
+#[allow(missing_copy_implementations)]
+pub enum RustArchiveMember_opaque {}
+pub type RustArchiveMemberRef = *mut RustArchiveMember_opaque;
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
pub fn LLVMRustOpenArchive(path: *const c_char) -> ArchiveRef;
pub fn LLVMRustArchiveIteratorNew(AR: ArchiveRef) -> ArchiveIteratorRef;
- pub fn LLVMRustArchiveIteratorNext(AIR: ArchiveIteratorRef);
- pub fn LLVMRustArchiveIteratorCurrent(AIR: ArchiveIteratorRef) -> ArchiveChildRef;
+ pub fn LLVMRustArchiveIteratorNext(AIR: ArchiveIteratorRef) -> ArchiveChildRef;
pub fn LLVMRustArchiveChildName(ACR: ArchiveChildRef,
size: *mut size_t) -> *const c_char;
pub fn LLVMRustArchiveChildData(ACR: ArchiveChildRef,
size: *mut size_t) -> *const c_char;
+ pub fn LLVMRustArchiveChildFree(ACR: ArchiveChildRef);
pub fn LLVMRustArchiveIteratorFree(AIR: ArchiveIteratorRef);
pub fn LLVMRustDestroyArchive(AR: ArchiveRef);
CX: *mut c_void);
pub fn LLVMWriteSMDiagnosticToString(d: SMDiagnosticRef, s: RustStringRef);
+
+ pub fn LLVMRustWriteArchive(Dst: *const c_char,
+ NumMembers: size_t,
+ Members: *const RustArchiveMemberRef,
+ WriteSymbtab: bool) -> c_int;
+ pub fn LLVMRustArchiveMemberNew(Filename: *const c_char,
+ Name: *const c_char,
+ Child: ArchiveChildRef) -> RustArchiveMemberRef;
+ pub fn LLVMRustArchiveMemberFree(Member: RustArchiveMemberRef);
}
// LLVM requires symbols from this library, but apparently they're not printed
--- /dev/null
+// Copyright 2013-2014 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.
+
+//! A helper class for dealing with static archives
+
+use std::env;
+use std::ffi::{CString, CStr, OsString};
+use std::fs::{self, File};
+use std::io::prelude::*;
+use std::io;
+use std::mem;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Output, Stdio};
+use std::str;
+
+use libc;
+use llvm::archive_ro::{ArchiveRO, Child};
+use llvm;
+use rustc::metadata::loader::METADATA_FILENAME;
+use rustc::session::Session;
+use rustc_back::tempdir::TempDir;
+
+pub struct ArchiveConfig<'a> {
+ pub sess: &'a Session,
+ pub dst: PathBuf,
+ pub src: Option<PathBuf>,
+ pub lib_search_paths: Vec<PathBuf>,
+ pub ar_prog: String,
+ pub command_path: OsString,
+}
+
+/// Helper for adding many files to an archive with a single invocation of
+/// `ar`.
+#[must_use = "must call build() to finish building the archive"]
+pub struct ArchiveBuilder<'a> {
+ config: ArchiveConfig<'a>,
+ work_dir: TempDir,
+ removals: Vec<String>,
+ additions: Vec<Addition>,
+ should_update_symbols: bool,
+ src_archive: Option<Option<ArchiveRO>>,
+}
+
+enum Addition {
+ File {
+ path: PathBuf,
+ name_in_archive: String,
+ },
+ Archive {
+ archive: ArchiveRO,
+ archive_name: String,
+ skip: Box<FnMut(&str) -> bool>,
+ },
+}
+
+enum Action<'a> {
+ Remove(&'a [String]),
+ AddObjects(&'a [&'a PathBuf], bool),
+ UpdateSymbols,
+}
+
+pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session)
+ -> PathBuf {
+ // On Windows, static libraries sometimes show up as libfoo.a and other
+ // times show up as foo.lib
+ let oslibname = format!("{}{}{}",
+ sess.target.target.options.staticlib_prefix,
+ name,
+ sess.target.target.options.staticlib_suffix);
+ let unixlibname = format!("lib{}.a", name);
+
+ for path in search_paths {
+ debug!("looking for {} inside {:?}", name, path);
+ let test = path.join(&oslibname[..]);
+ if test.exists() { return test }
+ if oslibname != unixlibname {
+ let test = path.join(&unixlibname[..]);
+ if test.exists() { return test }
+ }
+ }
+ sess.fatal(&format!("could not find native static library `{}`, \
+ perhaps an -L flag is missing?", name));
+}
+
+fn is_relevant_child(c: &Child) -> bool {
+ match c.name() {
+ Some(name) => !name.contains("SYMDEF"),
+ None => false,
+ }
+}
+
+impl<'a> ArchiveBuilder<'a> {
+ /// Create a new static archive, ready for modifying the archive specified
+ /// by `config`.
+ pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
+ ArchiveBuilder {
+ config: config,
+ work_dir: TempDir::new("rsar").unwrap(),
+ removals: Vec::new(),
+ additions: Vec::new(),
+ should_update_symbols: false,
+ src_archive: None,
+ }
+ }
+
+ /// Removes a file from this archive
+ pub fn remove_file(&mut self, file: &str) {
+ self.removals.push(file.to_string());
+ }
+
+ /// Lists all files in an archive
+ pub fn src_files(&mut self) -> Vec<String> {
+ if self.src_archive().is_none() {
+ return Vec::new()
+ }
+ let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap();
+ let ret = archive.iter()
+ .filter(is_relevant_child)
+ .filter_map(|child| child.name())
+ .filter(|name| !self.removals.iter().any(|x| x == name))
+ .map(|name| name.to_string())
+ .collect();
+ return ret;
+ }
+
+ fn src_archive(&mut self) -> Option<&ArchiveRO> {
+ if let Some(ref a) = self.src_archive {
+ return a.as_ref()
+ }
+ let src = match self.config.src {
+ Some(ref src) => src,
+ None => return None,
+ };
+ self.src_archive = Some(ArchiveRO::open(src));
+ self.src_archive.as_ref().unwrap().as_ref()
+ }
+
+ /// Adds all of the contents of a native library to this archive. This will
+ /// search in the relevant locations for a library named `name`.
+ pub fn add_native_library(&mut self, name: &str) -> io::Result<()> {
+ let location = find_library(name, &self.config.lib_search_paths,
+ self.config.sess);
+ self.add_archive(&location, name, |_| false)
+ }
+
+ /// Adds all of the contents of the rlib at the specified path to this
+ /// archive.
+ ///
+ /// This ignores adding the bytecode from the rlib, and if LTO is enabled
+ /// then the object file also isn't added.
+ pub fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool)
+ -> io::Result<()> {
+ // Ignoring obj file starting with the crate name
+ // as simple comparison is not enough - there
+ // might be also an extra name suffix
+ let obj_start = format!("{}", name);
+
+ // Ignoring all bytecode files, no matter of
+ // name
+ let bc_ext = ".bytecode.deflate";
+
+ self.add_archive(rlib, &name[..], move |fname: &str| {
+ let skip_obj = lto && fname.starts_with(&obj_start)
+ && fname.ends_with(".o");
+ skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME
+ })
+ }
+
+ fn add_archive<F>(&mut self, archive: &Path, name: &str, skip: F)
+ -> io::Result<()>
+ where F: FnMut(&str) -> bool + 'static
+ {
+ let archive = match ArchiveRO::open(archive) {
+ Some(ar) => ar,
+ None => return Err(io::Error::new(io::ErrorKind::Other,
+ "failed to open archive")),
+ };
+ self.additions.push(Addition::Archive {
+ archive: archive,
+ archive_name: name.to_string(),
+ skip: Box::new(skip),
+ });
+ Ok(())
+ }
+
+ /// Adds an arbitrary file to this archive
+ pub fn add_file(&mut self, file: &Path) {
+ let name = file.file_name().unwrap().to_str().unwrap();
+ self.additions.push(Addition::File {
+ path: file.to_path_buf(),
+ name_in_archive: name.to_string(),
+ });
+ }
+
+ /// Indicate that the next call to `build` should updates all symbols in
+ /// the archive (run 'ar s' over it).
+ pub fn update_symbols(&mut self) {
+ self.should_update_symbols = true;
+ }
+
+ /// Combine the provided files, rlibs, and native libraries into a single
+ /// `Archive`.
+ pub fn build(&mut self) {
+ let res = if self.using_llvm() {
+ self.build_with_llvm()
+ } else {
+ self.build_with_ar_cmd()
+ };
+ if let Err(e) = res {
+ self.config.sess.fatal(&format!("failed to build archive: {}", e));
+ }
+ }
+
+ pub fn using_llvm(&self) -> bool {
+ if unsafe { llvm::LLVMVersionMinor() < 7 } {
+ return false
+ }
+
+ // Currently LLVM only supports writing archives in the 'gnu' format.
+ match &self.config.sess.target.target.options.archive_format[..] {
+ "gnu" => true,
+ _ => false,
+ }
+ }
+
+ fn build_with_ar_cmd(&mut self) -> io::Result<()> {
+ let removals = mem::replace(&mut self.removals, Vec::new());
+ let additions = mem::replace(&mut self.additions, Vec::new());
+ let should_update_symbols = mem::replace(&mut self.should_update_symbols,
+ false);
+
+ // Don't use fs::copy because libs may be installed as read-only and we
+ // want to modify this archive, so we use `io::copy` to not preserve
+ // permission bits.
+ if let Some(ref s) = self.config.src {
+ try!(io::copy(&mut try!(File::open(s)),
+ &mut try!(File::create(&self.config.dst))));
+ }
+
+ if removals.len() > 0 {
+ self.run(None, Action::Remove(&removals));
+ }
+
+ let mut members = Vec::new();
+ for addition in additions {
+ match addition {
+ Addition::File { path, name_in_archive } => {
+ let dst = self.work_dir.path().join(&name_in_archive);
+ try!(fs::copy(&path, &dst));
+ members.push(PathBuf::from(name_in_archive));
+ }
+ Addition::Archive { archive, archive_name, mut skip } => {
+ try!(self.add_archive_members(&mut members, archive,
+ &archive_name, &mut *skip));
+ }
+ }
+ }
+
+ // Get an absolute path to the destination, so `ar` will work even
+ // though we run it from `self.work_dir`.
+ let mut objects = Vec::new();
+ let mut total_len = self.config.dst.to_string_lossy().len();
+
+ if members.is_empty() {
+ if should_update_symbols {
+ self.run(Some(self.work_dir.path()), Action::UpdateSymbols);
+ }
+ return Ok(())
+ }
+
+ // Don't allow the total size of `args` to grow beyond 32,000 bytes.
+ // Windows will raise an error if the argument string is longer than
+ // 32,768, and we leave a bit of extra space for the program name.
+ const ARG_LENGTH_LIMIT: usize = 32_000;
+
+ for member_name in &members {
+ let len = member_name.to_string_lossy().len();
+
+ // `len + 1` to account for the space that's inserted before each
+ // argument. (Windows passes command-line arguments as a single
+ // string, not an array of strings.)
+ if total_len + len + 1 > ARG_LENGTH_LIMIT {
+ // Add the archive members seen so far, without updating the
+ // symbol table.
+ self.run(Some(self.work_dir.path()),
+ Action::AddObjects(&objects, false));
+
+ objects.clear();
+ total_len = self.config.dst.to_string_lossy().len();
+ }
+
+ objects.push(member_name);
+ total_len += len + 1;
+ }
+
+ // Add the remaining archive members, and update the symbol table if
+ // necessary.
+ self.run(Some(self.work_dir.path()),
+ Action::AddObjects(&objects, should_update_symbols));
+ Ok(())
+ }
+
+ fn add_archive_members(&mut self, members: &mut Vec<PathBuf>,
+ archive: ArchiveRO, name: &str,
+ skip: &mut FnMut(&str) -> bool) -> io::Result<()> {
+ // Next, we must rename all of the inputs to "guaranteed unique names".
+ // We write each file into `self.work_dir` under its new unique name.
+ // The reason for this renaming is that archives are keyed off the name
+ // of the files, so if two files have the same name they will override
+ // one another in the archive (bad).
+ //
+ // We skip any files explicitly desired for skipping, and we also skip
+ // all SYMDEF files as these are just magical placeholders which get
+ // re-created when we make a new archive anyway.
+ for file in archive.iter().filter(is_relevant_child) {
+ let filename = file.name().unwrap();
+ if skip(filename) { continue }
+ let filename = Path::new(filename).file_name().unwrap()
+ .to_str().unwrap();
+
+ // Archives on unix systems typically do not have slashes in
+ // filenames as the `ar` utility generally only uses the last
+ // component of a path for the filename list in the archive. On
+ // Windows, however, archives assembled with `lib.exe` will preserve
+ // the full path to the file that was placed in the archive,
+ // including path separators.
+ //
+ // The code below is munging paths so it'll go wrong pretty quickly
+ // if there's some unexpected slashes in the filename, so here we
+ // just chop off everything but the filename component. Note that
+ // this can cause duplicate filenames, but that's also handled below
+ // as well.
+ let filename = Path::new(filename).file_name().unwrap()
+ .to_str().unwrap();
+
+ // An archive can contain files of the same name multiple times, so
+ // we need to be sure to not have them overwrite one another when we
+ // extract them. Consequently we need to find a truly unique file
+ // name for us!
+ let mut new_filename = String::new();
+ for n in 0.. {
+ let n = if n == 0 {String::new()} else {format!("-{}", n)};
+ new_filename = format!("r{}-{}-{}", n, name, filename);
+
+ // LLDB (as mentioned in back::link) crashes on filenames of
+ // exactly
+ // 16 bytes in length. If we're including an object file with
+ // exactly 16-bytes of characters, give it some prefix so
+ // that it's not 16 bytes.
+ new_filename = if new_filename.len() == 16 {
+ format!("lldb-fix-{}", new_filename)
+ } else {
+ new_filename
+ };
+
+ let present = members.iter().filter_map(|p| {
+ p.file_name().and_then(|f| f.to_str())
+ }).any(|s| s == new_filename);
+ if !present {
+ break
+ }
+ }
+ let dst = self.work_dir.path().join(&new_filename);
+ try!(try!(File::create(&dst)).write_all(file.data()));
+ members.push(PathBuf::from(new_filename));
+ }
+ Ok(())
+ }
+
+ fn run(&self, cwd: Option<&Path>, action: Action) -> Output {
+ let abs_dst = env::current_dir().unwrap().join(&self.config.dst);
+ let ar = &self.config.ar_prog;
+ let mut cmd = Command::new(ar);
+ cmd.env("PATH", &self.config.command_path);
+ cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
+ self.prepare_ar_action(&mut cmd, &abs_dst, action);
+ info!("{:?}", cmd);
+
+ if let Some(p) = cwd {
+ cmd.current_dir(p);
+ info!("inside {:?}", p.display());
+ }
+
+ let sess = &self.config.sess;
+ match cmd.spawn() {
+ Ok(prog) => {
+ let o = prog.wait_with_output().unwrap();
+ if !o.status.success() {
+ sess.err(&format!("{:?} failed with: {}", cmd, o.status));
+ sess.note(&format!("stdout ---\n{}",
+ str::from_utf8(&o.stdout).unwrap()));
+ sess.note(&format!("stderr ---\n{}",
+ str::from_utf8(&o.stderr).unwrap()));
+ sess.abort_if_errors();
+ }
+ o
+ },
+ Err(e) => {
+ sess.fatal(&format!("could not exec `{}`: {}",
+ self.config.ar_prog, e));
+ }
+ }
+ }
+
+ fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) {
+ match action {
+ Action::Remove(files) => {
+ cmd.arg("d").arg(dst).args(files);
+ }
+ Action::AddObjects(objs, update_symbols) => {
+ cmd.arg(if update_symbols {"crs"} else {"crS"})
+ .arg(dst)
+ .args(objs);
+ }
+ Action::UpdateSymbols => {
+ cmd.arg("s").arg(dst);
+ }
+ }
+ }
+
+ fn build_with_llvm(&mut self) -> io::Result<()> {
+ let mut archives = Vec::new();
+ let mut strings = Vec::new();
+ let mut members = Vec::new();
+ let removals = mem::replace(&mut self.removals, Vec::new());
+
+ unsafe {
+ if let Some(archive) = self.src_archive() {
+ for child in archive.iter() {
+ let child_name = match child.name() {
+ Some(s) => s,
+ None => continue,
+ };
+ if removals.iter().any(|r| r == child_name) {
+ continue
+ }
+
+ let name = try!(CString::new(child_name));
+ members.push(llvm::LLVMRustArchiveMemberNew(0 as *const _,
+ name.as_ptr(),
+ child.raw()));
+ strings.push(name);
+ }
+ }
+ for addition in mem::replace(&mut self.additions, Vec::new()) {
+ match addition {
+ Addition::File { path, name_in_archive } => {
+ let path = try!(CString::new(path.to_str().unwrap()));
+ let name = try!(CString::new(name_in_archive));
+ members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(),
+ name.as_ptr(),
+ 0 as *mut _));
+ strings.push(path);
+ strings.push(name);
+ }
+ Addition::Archive { archive, archive_name: _, mut skip } => {
+ for child in archive.iter().filter(is_relevant_child) {
+ let child_name = child.name().unwrap();
+ if skip(child_name) { continue }
+
+ let name = try!(CString::new(child_name));
+ let m = llvm::LLVMRustArchiveMemberNew(0 as *const _,
+ name.as_ptr(),
+ child.raw());
+ members.push(m);
+ strings.push(name);
+ }
+ archives.push(archive);
+ }
+ }
+ }
+
+ let dst = self.config.dst.to_str().unwrap().as_bytes();
+ let dst = try!(CString::new(dst));
+ let r = llvm::LLVMRustWriteArchive(dst.as_ptr(),
+ members.len() as libc::size_t,
+ members.as_ptr(),
+ self.should_update_symbols);
+ let ret = if r != 0 {
+ let err = llvm::LLVMRustGetLastError();
+ let msg = if err.is_null() {
+ "failed to write archive".to_string()
+ } else {
+ String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
+ .into_owned()
+ };
+ Err(io::Error::new(io::ErrorKind::Other, msg))
+ } else {
+ Ok(())
+ };
+ for member in members {
+ llvm::LLVMRustArchiveMemberFree(member);
+ }
+ return ret
+ }
+ }
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
+use super::archive::{ArchiveBuilder, ArchiveConfig};
use super::linker::{Linker, GnuLinker, MsvcLinker};
use super::rpath::RPathConfig;
use super::rpath;
use session::search_paths::PathKind;
use session::Session;
use metadata::common::LinkMeta;
-use metadata::{encoder, cstore, filesearch, csearch, creader};
use metadata::filesearch::FileDoesntMatch;
+use metadata::loader::METADATA_FILENAME;
+use metadata::{encoder, cstore, filesearch, csearch, creader};
use middle::ty::{self, Ty};
use rustc::ast_map::{PathElem, PathElems, PathName};
use trans::{CrateContext, CrateTranslation, gensym_name};
}
}
+ let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
match crate_type {
config::CrateTypeRlib => {
- link_rlib(sess, Some(trans), &objects, &out_filename).build();
+ link_rlib(sess, Some(trans), &objects, &out_filename,
+ tmpdir.path()).build();
}
config::CrateTypeStaticlib => {
- link_staticlib(sess, &objects, &out_filename);
+ link_staticlib(sess, &objects, &out_filename, tmpdir.path());
}
config::CrateTypeExecutable => {
- link_natively(sess, trans, false, &objects, &out_filename, outputs);
+ link_natively(sess, trans, false, &objects, &out_filename, outputs,
+ tmpdir.path());
}
config::CrateTypeDylib => {
- link_natively(sess, trans, true, &objects, &out_filename, outputs);
+ link_natively(sess, trans, true, &objects, &out_filename, outputs,
+ tmpdir.path());
}
}
}
fn archive_config<'a>(sess: &'a Session,
- output: &Path) -> ArchiveConfig<'a> {
+ output: &Path,
+ input: Option<&Path>) -> ArchiveConfig<'a> {
ArchiveConfig {
- handler: &sess.diagnostic().handler,
+ sess: sess,
dst: output.to_path_buf(),
+ src: input.map(|p| p.to_path_buf()),
lib_search_paths: archive_search_paths(sess),
- slib_prefix: sess.target.target.options.staticlib_prefix.clone(),
- slib_suffix: sess.target.target.options.staticlib_suffix.clone(),
ar_prog: get_ar_prog(sess),
command_path: command_path(sess),
}
fn link_rlib<'a>(sess: &'a Session,
trans: Option<&CrateTranslation>, // None == no metadata/bytecode
objects: &[PathBuf],
- out_filename: &Path) -> ArchiveBuilder<'a> {
+ out_filename: &Path,
+ tmpdir: &Path) -> ArchiveBuilder<'a> {
info!("preparing rlib from {:?} to {:?}", objects, out_filename);
- let mut ab = ArchiveBuilder::create(archive_config(sess, out_filename));
+ let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None));
for obj in objects {
- ab.add_file(obj).unwrap();
+ ab.add_file(obj);
}
for &(ref l, kind) in sess.cstore.get_used_libraries().borrow().iter() {
// symbol table of the archive.
ab.update_symbols();
- let mut ab = match sess.target.target.options.is_like_osx {
- // For OSX/iOS, we must be careful to update symbols only when adding
- // object files. We're about to start adding non-object files, so run
- // `ar` now to process the object files.
- true => ab.build().extend(),
- false => ab,
- };
+ // For OSX/iOS, we must be careful to update symbols only when adding
+ // object files. We're about to start adding non-object files, so run
+ // `ar` now to process the object files.
+ if sess.target.target.options.is_like_osx && !ab.using_llvm() {
+ ab.build();
+ }
// Note that it is important that we add all of our non-object "magical
// files" *after* all of the object files in the archive. The reason for
// contain the metadata in a separate file. We use a temp directory
// here so concurrent builds in the same directory don't try to use
// the same filename for metadata (stomping over one another)
- let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
- let metadata = tmpdir.path().join(METADATA_FILENAME);
+ let metadata = tmpdir.join(METADATA_FILENAME);
match fs::File::create(&metadata).and_then(|mut f| {
f.write_all(&trans.metadata)
}) {
metadata.display(), e));
}
}
- ab.add_file(&metadata).unwrap();
- remove(sess, &metadata);
+ ab.add_file(&metadata);
// For LTO purposes, the bytecode of this library is also inserted
// into the archive. If codegen_units > 1, we insert each of the
// would cause it to crash if the name of a file in an archive
// was exactly 16 bytes.
let bc_filename = obj.with_extension("bc");
- let bc_deflated_filename = obj.with_extension("bytecode.deflate");
+ let bc_deflated_filename = tmpdir.join({
+ obj.with_extension("bytecode.deflate").file_name().unwrap()
+ });
let mut bc_data = Vec::new();
match fs::File::open(&bc_filename).and_then(|mut f| {
}
};
- ab.add_file(&bc_deflated_filename).unwrap();
- remove(sess, &bc_deflated_filename);
+ ab.add_file(&bc_deflated_filename);
// See the bottom of back::write::run_passes for an explanation
// of when we do and don't keep .0.bc files around.
// After adding all files to the archive, we need to update the
// symbol table of the archive. This currently dies on OSX (see
// #11162), and isn't necessary there anyway
- if !sess.target.target.options.is_like_osx {
+ if !sess.target.target.options.is_like_osx || ab.using_llvm() {
ab.update_symbols();
}
}
// There's no need to include metadata in a static archive, so ensure to not
// link in the metadata object file (and also don't prepare the archive with a
// metadata file).
-fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) {
- let ab = link_rlib(sess, None, objects, out_filename);
- let mut ab = match sess.target.target.options.is_like_osx {
- true => ab.build().extend(),
- false => ab,
- };
+fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path,
+ tempdir: &Path) {
+ let mut ab = link_rlib(sess, None, objects, out_filename, tempdir);
+ if sess.target.target.options.is_like_osx && !ab.using_llvm() {
+ ab.build();
+ }
if sess.target.target.options.morestack {
ab.add_native_library("morestack").unwrap();
}
}
ab.update_symbols();
- let _ = ab.build();
+ ab.build();
if !all_native_libs.is_empty() {
sess.note("link against the following native artifacts when linking against \
// links to all upstream files as well.
fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
objects: &[PathBuf], out_filename: &Path,
- outputs: &OutputFilenames) {
+ outputs: &OutputFilenames,
+ tmpdir: &Path) {
info!("preparing dylib? ({}) from {:?} to {:?}", dylib, objects,
out_filename);
- let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
// The invocations of cc share some flags across platforms
let (pname, mut cmd) = get_linker(sess);
} else {
Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
};
- link_args(&mut *linker, sess, dylib, tmpdir.path(),
+ link_args(&mut *linker, sess, dylib, tmpdir,
trans, objects, out_filename, outputs);
if !sess.target.target.options.no_compiler_rt {
linker.link_staticlib("compiler-rt");
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
time(sess.time_passes(), &format!("altering {}.rlib", name), (), |()| {
- let err = (|| {
- io::copy(&mut try!(fs::File::open(&cratepath)),
- &mut try!(fs::File::create(&dst)))
- })();
- if let Err(e) = err {
- sess.fatal(&format!("failed to copy {} to {}: {}",
- cratepath.display(), dst.display(), e));
- }
-
- let mut archive = Archive::open(archive_config(sess, &dst));
+ let cfg = archive_config(sess, &dst, Some(cratepath));
+ let mut archive = ArchiveBuilder::new(cfg);
archive.remove_file(METADATA_FILENAME);
+ archive.update_symbols();
let mut any_objects = false;
- for f in archive.files() {
+ for f in archive.src_files() {
if f.ends_with("bytecode.deflate") {
archive.remove_file(&f);
continue
}
if any_objects {
+ archive.build();
cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst));
}
});
use std::process::Command;
use std::fs;
-use rustc_back::archive;
+use back::archive;
use session::Session;
use session::config;
// -force_load is the OSX equivalent of --whole-archive, but it
// involves passing the full path to the library to link.
let mut v = OsString::from("-Wl,-force_load,");
- v.push(&archive::find_library(lib,
- &target.options.staticlib_prefix,
- &target.options.staticlib_suffix,
- search_path,
- &self.sess.diagnostic().handler));
+ v.push(&archive::find_library(lib, search_path, &self.sess));
self.cmd.arg(&v);
}
}
extern crate libc;
extern crate rustc;
extern crate rustc_back;
-extern crate serialize;
extern crate rustc_llvm as llvm;
+extern crate serialize;
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
pub mod back {
pub use rustc_back::abi;
- pub use rustc_back::archive;
pub use rustc_back::arm;
pub use rustc_back::mips;
pub use rustc_back::mipsel;
pub use rustc_back::x86;
pub use rustc_back::x86_64;
+ pub mod archive;
pub mod linker;
pub mod link;
pub mod lto;
///
/// * Current directory does not exist.
/// * There are insufficient permissions to access the current directory.
-/// * The internal buffer is not large enough to hold the path.
///
/// # Examples
///
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use borrow::{Cow, ToOwned};
+use borrow::{Cow, ToOwned, Borrow};
use boxed::Box;
use clone::Clone;
use convert::{Into, From};
}
}
+#[stable(feature = "cstr_borrow", since = "1.3.0")]
+impl Borrow<CStr> for CString {
+ fn borrow(&self) -> &CStr { self }
+}
+
impl NulError {
/// Returns the position of the nul byte in the slice that was provided to
/// `CString::new`.
}
}
+#[stable(feature = "cstr_borrow", since = "1.3.0")]
+impl ToOwned for CStr {
+ type Owned = CString;
+
+ fn to_owned(&self) -> CString {
+ unsafe { CString::from_vec_unchecked(self.to_bytes().to_vec()) }
+ }
+}
+
#[cfg(test)]
mod tests {
use prelude::v1::*;
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}")));
}
}
+
+ #[test]
+ fn to_owned() {
+ let data = b"123\0";
+ let ptr = data.as_ptr() as *const libc::c_char;
+
+ let owned = unsafe { CStr::from_ptr(ptr).to_owned() };
+ assert_eq!(owned.as_bytes_with_nul(), data);
+ }
+
+ #[test]
+ fn equal_hash() {
+ use hash;
+
+ let data = b"123\xE2\xFA\xA6\0";
+ let ptr = data.as_ptr() as *const libc::c_char;
+ let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
+
+ let cstr_hash = hash::hash::<_, hash::SipHasher>(&cstr);
+ let cstring_hash =
+ hash::hash::<_, hash::SipHasher>(&CString::new(&data[..data.len() - 1]).unwrap());
+
+ assert_eq!(cstr_hash, cstring_hash);
+ }
}
use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom};
use ptr;
-/// Wraps a `Read` and buffers input from it.
+/// The `BufReader` struct adds buffering to any reader.
///
/// It can be excessively inefficient to work directly with a `Read` instance.
/// For example, every call to `read` on `TcpStream` results in a system call.
///
/// # Examples
///
-/// ```no_run
+/// ```
/// use std::io::prelude::*;
/// use std::io::BufReader;
/// use std::fs::File;
impl<R: Read> BufReader<R> {
/// Creates a new `BufReader` with a default buffer capacity.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f);
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(inner: R) -> BufReader<R> {
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
}
/// Creates a new `BufReader` with the specified buffer capacity.
+ ///
+ /// # Examples
+ ///
+ /// Creating a buffer with ten bytes of capacity:
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::with_capacity(10, f);
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
BufReader {
}
/// Gets a reference to the underlying reader.
+ ///
+ /// It is inadvisable to directly read from the underlying reader.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.get_ref();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_ref(&self) -> &R { &self.inner }
/// Gets a mutable reference to the underlying reader.
///
- /// # Warning
- ///
/// It is inadvisable to directly read from the underlying reader.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.get_mut();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
/// Unwraps this `BufReader`, returning the underlying reader.
///
/// Note that any leftover data in the internal buffer is lost.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::BufReader;
+ /// use std::fs::File;
+ ///
+ /// # fn foo() -> std::io::Result<()> {
+ /// let mut f1 = try!(File::open("log.txt"));
+ /// let mut reader = BufReader::new(f1);
+ ///
+ /// let f2 = reader.into_inner();
+ /// # Ok(())
+ /// # }
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn into_inner(self) -> R { self.inner }
}
#![feature(wrapping)]
#![feature(zero_one)]
#![cfg_attr(windows, feature(str_utf16))]
-#![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras))]
+#![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras, hash_default))]
#![cfg_attr(test, feature(test, rustc_private, float_consts))]
#![cfg_attr(target_env = "msvc", feature(link_args))]
readlink(&p).ok()
}
- #[cfg(not(target_os = "linux"))]
+ #[cfg(target_os = "macos")]
+ fn get_path(fd: c_int) -> Option<PathBuf> {
+ let mut buf = vec![0;libc::PATH_MAX as usize];
+ let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
+ if n == -1 {
+ return None;
+ }
+ let l = buf.iter().position(|&c| c == 0).unwrap();
+ buf.truncate(l as usize);
+ Some(PathBuf::from(OsString::from_vec(buf)))
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#24570): implement this for other Unix platforms
None
}
- #[cfg(target_os = "linux")]
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if mode == -1 {
}
}
- #[cfg(not(target_os = "linux"))]
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
// FIXME(#24570): implement this for other Unix platforms
None
use iter;
use libc::{self, c_int, c_char, c_void};
use mem;
-use ptr;
use path::{self, PathBuf};
+use ptr;
use slice;
use str;
use sys::c;
use sys::fd;
use vec;
-const BUF_BYTES: usize = 2048;
+const GETCWD_BUF_BYTES: usize = 2048;
const TMPBUF_SZ: usize = 128;
-fn bytes2path(b: &[u8]) -> PathBuf {
- PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
-}
-
-fn os2path(os: OsString) -> PathBuf {
- bytes2path(os.as_bytes())
-}
-
/// Returns the platform-specific value of errno
pub fn errno() -> i32 {
#[cfg(any(target_os = "macos",
}
pub fn getcwd() -> io::Result<PathBuf> {
- let mut buf = [0 as c_char; BUF_BYTES];
- unsafe {
- if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
- Err(io::Error::last_os_error())
- } else {
- Ok(bytes2path(CStr::from_ptr(buf.as_ptr()).to_bytes()))
+ let mut buf = Vec::new();
+ let mut n = GETCWD_BUF_BYTES;
+ loop {
+ unsafe {
+ buf.reserve(n);
+ let ptr = buf.as_mut_ptr() as *mut libc::c_char;
+ if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() {
+ let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
+ buf.set_len(len);
+ buf.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ } else {
+ let error = io::Error::last_os_error();
+ if error.raw_os_error() != Some(libc::ERANGE) {
+ return Err(error);
+ }
+ }
+ n *= 2;
}
}
}
}
pub fn split_paths<'a>(unparsed: &'a OsStr) -> SplitPaths<'a> {
+ fn bytes_to_path(b: &[u8]) -> PathBuf {
+ PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
+ }
fn is_colon(b: &u8) -> bool { *b == b':' }
let unparsed = unparsed.as_bytes();
SplitPaths {
iter: unparsed.split(is_colon as fn(&u8) -> bool)
- .map(bytes2path as fn(&'a [u8]) -> PathBuf)
+ .map(bytes_to_path as fn(&'a [u8]) -> PathBuf)
}
}
}
pub fn temp_dir() -> PathBuf {
- getenv("TMPDIR".as_ref()).map(os2path).unwrap_or_else(|| {
+ getenv("TMPDIR".as_ref()).map(PathBuf::from).unwrap_or_else(|| {
if cfg!(target_os = "android") {
PathBuf::from("/data/local/tmp")
} else {
pub fn home_dir() -> Option<PathBuf> {
return getenv("HOME".as_ref()).or_else(|| unsafe {
fallback()
- }).map(os2path);
+ }).map(PathBuf::from);
#[cfg(any(target_os = "android",
target_os = "ios"))]
pub const ERROR_NO_MORE_FILES: libc::DWORD = 18;
pub const TOKEN_READ: libc::DWORD = 0x20008;
pub const FILE_FLAG_OPEN_REPARSE_POINT: libc::DWORD = 0x00200000;
+pub const FILE_FLAG_BACKUP_SEMANTICS: libc::DWORD = 0x02000000;
pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
pub const FSCTL_GET_REPARSE_POINT: libc::DWORD = 0x900a8;
pub const IO_REPARSE_TAG_SYMLINK: libc::DWORD = 0xa000000c;
+pub const IO_REPARSE_TAG_MOUNT_POINT: libc::DWORD = 0xa0000003;
+pub const FSCTL_SET_REPARSE_POINT: libc::DWORD = 0x900a4;
+pub const FSCTL_DELETE_REPARSE_POINT: libc::DWORD = 0x900ac;
pub const SYMBOLIC_LINK_FLAG_DIRECTORY: libc::DWORD = 0x1;
pub const PROGRESS_STOP: libc::DWORD = 2;
pub const PROGRESS_QUIET: libc::DWORD = 3;
+pub const TOKEN_ADJUST_PRIVILEGES: libc::DWORD = 0x0020;
+pub const SE_PRIVILEGE_ENABLED: libc::DWORD = 2;
+
#[repr(C)]
#[cfg(target_arch = "x86")]
pub struct WSADATA {
};
pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ };
+#[repr(C)]
+pub struct LUID {
+ pub LowPart: libc::DWORD,
+ pub HighPart: libc::c_long,
+}
+
+pub type PLUID = *mut LUID;
+
+#[repr(C)]
+pub struct TOKEN_PRIVILEGES {
+ pub PrivilegeCount: libc::DWORD,
+ pub Privileges: [LUID_AND_ATTRIBUTES; 1],
+}
+
+pub type PTOKEN_PRIVILEGES = *mut TOKEN_PRIVILEGES;
+
+#[repr(C)]
+pub struct LUID_AND_ATTRIBUTES {
+ pub Luid: LUID,
+ pub Attributes: libc::DWORD,
+}
+
+#[repr(C)]
+pub struct REPARSE_MOUNTPOINT_DATA_BUFFER {
+ pub ReparseTag: libc::DWORD,
+ pub ReparseDataLength: libc::DWORD,
+ pub Reserved: libc::WORD,
+ pub ReparseTargetLength: libc::WORD,
+ pub ReparseTargetMaximumLength: libc::WORD,
+ pub Reserved1: libc::WORD,
+ pub ReparseTarget: libc::WCHAR,
+}
+
+
#[link(name = "ws2_32")]
#[link(name = "userenv")]
extern "system" {
lpData: libc::LPVOID,
pbCancel: LPBOOL,
dwCopyFlags: libc::DWORD) -> libc::BOOL;
+ pub fn LookupPrivilegeValueW(lpSystemName: libc::LPCWSTR,
+ lpName: libc::LPCWSTR,
+ lpLuid: PLUID) -> libc::BOOL;
+ pub fn AdjustTokenPrivileges(TokenHandle: libc::HANDLE,
+ DisableAllPrivileges: libc::BOOL,
+ NewState: PTOKEN_PRIVILEGES,
+ BufferLength: libc::DWORD,
+ PreviousState: PTOKEN_PRIVILEGES,
+ ReturnLength: *mut libc::DWORD) -> libc::BOOL;
}
// Functions that aren't available on Windows XP, but we still use them and just
pub struct FileAttr {
data: c::WIN32_FILE_ATTRIBUTE_DATA,
- is_symlink: bool,
+ reparse_tag: libc::DWORD,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum FileType {
- Dir, File, Symlink, ReparsePoint
+ Dir, File, Symlink, ReparsePoint, MountPoint,
}
pub struct ReadDir {
pub fn file_type(&self) -> io::Result<FileType> {
Ok(FileType::new(self.data.dwFileAttributes,
- self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK))
+ /* reparse_tag = */ self.data.dwReserved0))
}
pub fn metadata(&self) -> io::Result<FileAttr> {
nFileSizeHigh: self.data.nFileSizeHigh,
nFileSizeLow: self.data.nFileSizeLow,
},
- is_symlink: self.data.dwReserved0 == c::IO_REPARSE_TAG_SYMLINK,
+ reparse_tag: self.data.dwReserved0,
})
}
}
}
impl File {
- fn open_reparse_point(path: &Path) -> io::Result<File> {
+ fn open_reparse_point(path: &Path, write: bool) -> io::Result<File> {
let mut opts = OpenOptions::new();
- opts.read(true);
- opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT);
+ opts.read(!write);
+ opts.write(write);
+ opts.flags_and_attributes(c::FILE_FLAG_OPEN_REPARSE_POINT |
+ c::FILE_FLAG_BACKUP_SEMANTICS);
File::open(path, &opts)
}
nFileSizeHigh: info.nFileSizeHigh,
nFileSizeLow: info.nFileSizeLow,
},
- is_symlink: false,
+ reparse_tag: 0,
};
if attr.is_reparse_point() {
- attr.is_symlink = self.is_symlink();
+ let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ if let Ok((_, buf)) = self.reparse_point(&mut b) {
+ attr.reparse_tag = buf.ReparseTag;
+ }
}
Ok(attr)
}
pub fn handle(&self) -> &Handle { &self.handle }
- fn is_symlink(&self) -> bool {
- self.readlink().is_ok()
- }
-
- fn readlink(&self) -> io::Result<PathBuf> {
- let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
- let mut bytes = 0;
-
+ fn reparse_point<'a>(&self,
+ space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE])
+ -> io::Result<(libc::DWORD, &'a c::REPARSE_DATA_BUFFER)> {
unsafe {
+ let mut bytes = 0;
try!(cvt({
c::DeviceIoControl(self.handle.raw(),
c::FSCTL_GET_REPARSE_POINT,
&mut bytes,
0 as *mut _)
}));
- let buf: *const c::REPARSE_DATA_BUFFER = space.as_ptr() as *const _;
- if (*buf).ReparseTag != c::IO_REPARSE_TAG_SYMLINK {
- return Err(io::Error::new(io::ErrorKind::Other, "not a symlink"))
- }
+ Ok((bytes, &*(space.as_ptr() as *const c::REPARSE_DATA_BUFFER)))
+ }
+ }
+
+ fn readlink(&self) -> io::Result<PathBuf> {
+ let mut space = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let (_bytes, buf) = try!(self.reparse_point(&mut space));
+ if buf.ReparseTag != c::IO_REPARSE_TAG_SYMLINK {
+ return Err(io::Error::new(io::ErrorKind::Other, "not a symlink"))
+ }
+
+ unsafe {
let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
- &(*buf).rest as *const _ as *const _;
+ &buf.rest as *const _ as *const _;
let path_buffer = &(*info).PathBuffer as *const _ as *const u16;
let subst_off = (*info).SubstituteNameOffset / 2;
let subst_ptr = path_buffer.offset(subst_off as isize);
pub fn attrs(&self) -> u32 { self.data.dwFileAttributes as u32 }
pub fn file_type(&self) -> FileType {
- FileType::new(self.data.dwFileAttributes, self.is_symlink)
+ FileType::new(self.data.dwFileAttributes, self.reparse_tag)
}
pub fn created(&self) -> u64 { self.to_u64(&self.data.ftCreationTime) }
}
impl FileType {
- fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType {
+ fn new(attrs: libc::DWORD, reparse_tag: libc::DWORD) -> FileType {
if attrs & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- if is_symlink {
- FileType::Symlink
- } else {
- FileType::ReparsePoint
+ match reparse_tag {
+ c::IO_REPARSE_TAG_SYMLINK => FileType::Symlink,
+ c::IO_REPARSE_TAG_MOUNT_POINT => FileType::MountPoint,
+ _ => FileType::ReparsePoint,
}
} else if attrs & c::FILE_ATTRIBUTE_DIRECTORY != 0 {
FileType::Dir
pub fn is_dir(&self) -> bool { *self == FileType::Dir }
pub fn is_file(&self) -> bool { *self == FileType::File }
- pub fn is_symlink(&self) -> bool { *self == FileType::Symlink }
+ pub fn is_symlink(&self) -> bool {
+ *self == FileType::Symlink || *self == FileType::MountPoint
+ }
}
impl DirBuilder {
}
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
- let file = try!(File::open_reparse_point(p));
+ let file = try!(File::open_reparse_point(p, false));
file.readlink()
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
let attr = try!(lstat(p));
- if attr.data.dwFileAttributes & libc::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
- let opts = OpenOptions::new();
+
+ // If this is a reparse point, then we need to reopen the file to get the
+ // actual destination. We also pass the FILE_FLAG_BACKUP_SEMANTICS flag to
+ // ensure that we can open directories (this path may be a directory
+ // junction). Once the file is opened we ask the opened handle what its
+ // metadata information is.
+ if attr.is_reparse_point() {
+ let mut opts = OpenOptions::new();
+ opts.flags_and_attributes(c::FILE_FLAG_BACKUP_SEMANTICS);
let file = try!(File::open(p, &opts));
file.file_attr()
} else {
c::GetFileExInfoStandard,
&mut attr.data as *mut _ as *mut _)));
if attr.is_reparse_point() {
- attr.is_symlink = File::open_reparse_point(p).map(|f| {
- f.is_symlink()
- }).unwrap_or(false);
+ attr.reparse_tag = File::open_reparse_point(p, false).and_then(|f| {
+ let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ f.reparse_point(&mut b).map(|(_, b)| b.ReparseTag)
+ }).unwrap_or(0);
}
Ok(attr)
}
}));
Ok(size as u64)
}
+
+#[test]
+fn directory_junctions_are_directories() {
+ use ffi::OsStr;
+ use env;
+ use rand::{self, StdRng, Rng};
+
+ macro_rules! t {
+ ($e:expr) => (match $e {
+ Ok(e) => e,
+ Err(e) => panic!("{} failed with: {}", stringify!($e), e),
+ })
+ }
+
+ let d = DirBuilder::new();
+ let p = env::temp_dir();
+ let mut r = rand::thread_rng();
+ let ret = p.join(&format!("rust-{}", r.next_u32()));
+ let foo = ret.join("foo");
+ let bar = ret.join("bar");
+ t!(d.mkdir(&ret));
+ t!(d.mkdir(&foo));
+ t!(d.mkdir(&bar));
+
+ t!(create_junction(&bar, &foo));
+ let metadata = stat(&bar);
+ t!(delete_junction(&bar));
+
+ t!(rmdir(&foo));
+ t!(rmdir(&bar));
+ t!(rmdir(&ret));
+
+ let metadata = t!(metadata);
+ assert!(metadata.file_type().is_dir());
+
+ // Creating a directory junction on windows involves dealing with reparse
+ // points and the DeviceIoControl function, and this code is a skeleton of
+ // what can be found here:
+ //
+ // http://www.flexhex.com/docs/articles/hard-links.phtml
+ fn create_junction(src: &Path, dst: &Path) -> io::Result<()> {
+ let f = try!(opendir(src, true));
+ let h = f.handle().raw();
+
+ unsafe {
+ let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut db = data.as_mut_ptr()
+ as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
+ let mut buf = &mut (*db).ReparseTarget as *mut _;
+ let mut i = 0;
+ let v = br"\??\";
+ let v = v.iter().map(|x| *x as u16);
+ for c in v.chain(dst.as_os_str().encode_wide()) {
+ *buf.offset(i) = c;
+ i += 1;
+ }
+ *buf.offset(i) = 0;
+ i += 1;
+ (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
+ (*db).ReparseTargetMaximumLength = (i * 2) as libc::WORD;
+ (*db).ReparseTargetLength = ((i - 1) * 2) as libc::WORD;
+ (*db).ReparseDataLength =
+ (*db).ReparseTargetLength as libc::DWORD + 12;
+
+ let mut ret = 0;
+ cvt(c::DeviceIoControl(h as *mut _,
+ c::FSCTL_SET_REPARSE_POINT,
+ data.as_ptr() as *mut _,
+ (*db).ReparseDataLength + 8,
+ 0 as *mut _, 0,
+ &mut ret,
+ 0 as *mut _)).map(|_| ())
+ }
+ }
+
+ fn opendir(p: &Path, write: bool) -> io::Result<File> {
+ unsafe {
+ let mut token = 0 as *mut _;
+ let mut tp: c::TOKEN_PRIVILEGES = mem::zeroed();
+ try!(cvt(c::OpenProcessToken(c::GetCurrentProcess(),
+ c::TOKEN_ADJUST_PRIVILEGES,
+ &mut token)));
+ let name: &OsStr = if write {
+ "SeRestorePrivilege".as_ref()
+ } else {
+ "SeBackupPrivilege".as_ref()
+ };
+ let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
+ try!(cvt(c::LookupPrivilegeValueW(0 as *const _,
+ name.as_ptr(),
+ &mut tp.Privileges[0].Luid)));
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = c::SE_PRIVILEGE_ENABLED;
+ let size = mem::size_of::<c::TOKEN_PRIVILEGES>() as libc::DWORD;
+ try!(cvt(c::AdjustTokenPrivileges(token, libc::FALSE, &mut tp, size,
+ 0 as *mut _, 0 as *mut _)));
+ try!(cvt(libc::CloseHandle(token)));
+
+ File::open_reparse_point(p, write)
+ }
+ }
+
+ fn delete_junction(p: &Path) -> io::Result<()> {
+ unsafe {
+ let f = try!(opendir(p, true));
+ let h = f.handle().raw();
+ let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ let mut db = data.as_mut_ptr()
+ as *mut c::REPARSE_MOUNTPOINT_DATA_BUFFER;
+ (*db).ReparseTag = c::IO_REPARSE_TAG_MOUNT_POINT;
+ let mut bytes = 0;
+ cvt(c::DeviceIoControl(h as *mut _,
+ c::FSCTL_DELETE_REPARSE_POINT,
+ data.as_ptr() as *mut _,
+ (*db).ReparseDataLength + 8,
+ 0 as *mut _, 0,
+ &mut bytes,
+ 0 as *mut _)).map(|_| ())
+ }
+ }
+}
--- /dev/null
+// Copyright 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.
+
+#include "rustllvm.h"
+
+#include "llvm/Object/Archive.h"
+
+#if LLVM_VERSION_MINOR >= 7
+#include "llvm/Object/ArchiveWriter.h"
+#endif
+
+using namespace llvm;
+using namespace llvm::object;
+
+struct LLVMRustArchiveMember {
+ const char *filename;
+ const char *name;
+ Archive::Child child;
+
+ LLVMRustArchiveMember(): filename(NULL), name(NULL), child(NULL, NULL) {}
+ ~LLVMRustArchiveMember() {}
+};
+
+#if LLVM_VERSION_MINOR >= 6
+typedef OwningBinary<Archive> RustArchive;
+#define GET_ARCHIVE(a) ((a)->getBinary())
+#else
+typedef Archive RustArchive;
+#define GET_ARCHIVE(a) (a)
+#endif
+
+extern "C" void*
+LLVMRustOpenArchive(char *path) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> buf_or = MemoryBuffer::getFile(path,
+ -1,
+ false);
+ if (!buf_or) {
+ LLVMRustSetLastError(buf_or.getError().message().c_str());
+ return nullptr;
+ }
+
+#if LLVM_VERSION_MINOR >= 6
+ ErrorOr<std::unique_ptr<Archive>> archive_or =
+ Archive::create(buf_or.get()->getMemBufferRef());
+
+ if (!archive_or) {
+ LLVMRustSetLastError(archive_or.getError().message().c_str());
+ return nullptr;
+ }
+
+ OwningBinary<Archive> *ret = new OwningBinary<Archive>(
+ std::move(archive_or.get()), std::move(buf_or.get()));
+#else
+ std::error_code err;
+ Archive *ret = new Archive(std::move(buf_or.get()), err);
+ if (err) {
+ LLVMRustSetLastError(err.message().c_str());
+ return nullptr;
+ }
+#endif
+
+ return ret;
+}
+
+extern "C" void
+LLVMRustDestroyArchive(RustArchive *ar) {
+ delete ar;
+}
+
+struct RustArchiveIterator {
+ Archive::child_iterator cur;
+ Archive::child_iterator end;
+};
+
+extern "C" RustArchiveIterator*
+LLVMRustArchiveIteratorNew(RustArchive *ra) {
+ Archive *ar = GET_ARCHIVE(ra);
+ RustArchiveIterator *rai = new RustArchiveIterator();
+ rai->cur = ar->child_begin();
+ rai->end = ar->child_end();
+ return rai;
+}
+
+extern "C" const Archive::Child*
+LLVMRustArchiveIteratorNext(RustArchiveIterator *rai) {
+ if (rai->cur == rai->end)
+ return NULL;
+ const Archive::Child *cur = rai->cur.operator->();
+ Archive::Child *ret = new Archive::Child(*cur);
+ ++rai->cur;
+ return ret;
+}
+
+extern "C" void
+LLVMRustArchiveChildFree(Archive::Child *child) {
+ delete child;
+}
+
+extern "C" void
+LLVMRustArchiveIteratorFree(RustArchiveIterator *rai) {
+ delete rai;
+}
+
+extern "C" const char*
+LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) {
+ ErrorOr<StringRef> name_or_err = child->getName();
+ if (name_or_err.getError())
+ return NULL;
+ StringRef name = name_or_err.get();
+ *size = name.size();
+ return name.data();
+}
+
+extern "C" const char*
+LLVMRustArchiveChildData(Archive::Child *child, size_t *size) {
+ StringRef buf = child->getBuffer();
+ *size = buf.size();
+ return buf.data();
+}
+
+extern "C" LLVMRustArchiveMember*
+LLVMRustArchiveMemberNew(char *Filename, char *Name, Archive::Child *child) {
+ LLVMRustArchiveMember *Member = new LLVMRustArchiveMember;
+ Member->filename = Filename;
+ Member->name = Name;
+ if (child)
+ Member->child = *child;
+ return Member;
+}
+
+extern "C" void
+LLVMRustArchiveMemberFree(LLVMRustArchiveMember *Member) {
+ delete Member;
+}
+
+extern "C" int
+LLVMRustWriteArchive(char *Dst,
+ size_t NumMembers,
+ const LLVMRustArchiveMember **NewMembers,
+ bool WriteSymbtab) {
+#if LLVM_VERSION_MINOR >= 7
+ std::vector<NewArchiveIterator> Members;
+
+ for (size_t i = 0; i < NumMembers; i++) {
+ auto Member = NewMembers[i];
+ assert(Member->name);
+ if (Member->filename) {
+ Members.push_back(NewArchiveIterator(Member->filename, Member->name));
+ } else {
+ Members.push_back(NewArchiveIterator(Member->child, Member->name));
+ }
+ }
+ auto pair = writeArchive(Dst, Members, WriteSymbtab);
+ if (!pair.second)
+ return 0;
+ LLVMRustSetLastError(pair.second.message().c_str());
+#else
+ LLVMRustSetLastError("writing archives not supported with this LLVM version");
+#endif
+ return -1;
+}
return true;
}
-extern "C" void*
-LLVMRustOpenArchive(char *path) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> buf_or = MemoryBuffer::getFile(path,
- -1,
- false);
- if (!buf_or) {
- LLVMRustSetLastError(buf_or.getError().message().c_str());
- return nullptr;
- }
-
-#if LLVM_VERSION_MINOR >= 6
- ErrorOr<std::unique_ptr<Archive>> archive_or =
- Archive::create(buf_or.get()->getMemBufferRef());
-
- if (!archive_or) {
- LLVMRustSetLastError(archive_or.getError().message().c_str());
- return nullptr;
- }
-
- OwningBinary<Archive> *ret = new OwningBinary<Archive>(
- std::move(archive_or.get()), std::move(buf_or.get()));
-#else
- std::error_code err;
- Archive *ret = new Archive(std::move(buf_or.get()), err);
- if (err) {
- LLVMRustSetLastError(err.message().c_str());
- return nullptr;
- }
-#endif
-
- return ret;
-}
-
-#if LLVM_VERSION_MINOR >= 6
-typedef OwningBinary<Archive> RustArchive;
-#define GET_ARCHIVE(a) ((a)->getBinary())
-#else
-typedef Archive RustArchive;
-#define GET_ARCHIVE(a) (a)
-#endif
-
-extern "C" void
-LLVMRustDestroyArchive(RustArchive *ar) {
- delete ar;
-}
-
-struct RustArchiveIterator {
- Archive::child_iterator cur;
- Archive::child_iterator end;
-};
-
-extern "C" RustArchiveIterator*
-LLVMRustArchiveIteratorNew(RustArchive *ra) {
- Archive *ar = GET_ARCHIVE(ra);
- RustArchiveIterator *rai = new RustArchiveIterator();
- rai->cur = ar->child_begin();
- rai->end = ar->child_end();
- return rai;
-}
-
-extern "C" const Archive::Child*
-LLVMRustArchiveIteratorCurrent(RustArchiveIterator *rai) {
- if (rai->cur == rai->end)
- return NULL;
-#if LLVM_VERSION_MINOR >= 6
- const Archive::Child &ret = *rai->cur;
- return &ret;
-#else
- return rai->cur.operator->();
-#endif
-}
-
-extern "C" void
-LLVMRustArchiveIteratorNext(RustArchiveIterator *rai) {
- if (rai->cur == rai->end)
- return;
- ++rai->cur;
-}
-
-extern "C" void
-LLVMRustArchiveIteratorFree(RustArchiveIterator *rai) {
- delete rai;
-}
-
-extern "C" const char*
-LLVMRustArchiveChildName(const Archive::Child *child, size_t *size) {
- ErrorOr<StringRef> name_or_err = child->getName();
- if (name_or_err.getError())
- return NULL;
- StringRef name = name_or_err.get();
- *size = name.size();
- return name.data();
-}
-
-extern "C" const char*
-LLVMRustArchiveChildData(Archive::Child *child, size_t *size) {
- StringRef buf = child->getBuffer();
- *size = buf.size();
- return buf.data();
-}
-
extern "C" void
LLVMRustSetDLLStorageClass(LLVMValueRef Value,
GlobalValue::DLLStorageClassTypes Class) {