]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #26903 - steveklabnik:io_function_docs, r=alexcricthon
authorbors <bors@rust-lang.org>
Sat, 11 Jul 2015 04:44:55 +0000 (04:44 +0000)
committerbors <bors@rust-lang.org>
Sat, 11 Jul 2015 04:44:55 +0000 (04:44 +0000)
This round: io::Result and the free functions.

25 files changed:
mk/rustllvm.mk
src/liblibc/lib.rs
src/librustc/lib.rs
src/librustc/metadata/loader.rs
src/librustc_back/archive.rs [deleted file]
src/librustc_back/lib.rs
src/librustc_back/target/linux_base.rs
src/librustc_back/target/mod.rs
src/librustc_back/target/windows_base.rs
src/librustc_llvm/archive_ro.rs
src/librustc_llvm/lib.rs
src/librustc_trans/back/archive.rs [new file with mode: 0644]
src/librustc_trans/back/link.rs
src/librustc_trans/back/linker.rs
src/librustc_trans/lib.rs
src/libstd/env.rs
src/libstd/ffi/c_str.rs
src/libstd/io/buffered.rs
src/libstd/lib.rs
src/libstd/sys/unix/fs.rs
src/libstd/sys/unix/os.rs
src/libstd/sys/windows/c.rs
src/libstd/sys/windows/fs.rs
src/rustllvm/ArchiveWrapper.cpp [new file with mode: 0644]
src/rustllvm/RustWrapper.cpp

index 50d993701421e896a18a0be34baf5c923c161e47..6adffda7d1b322ab10f5c7a9e83a73f13a8c5aad 100644 (file)
@@ -24,7 +24,8 @@ LLVM_EXTRA_INCDIRS_$(1)= $$(call CFG_CC_INCLUDE_$(1),$(S)src/llvm/include) \
 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))) \
index 0a32c73a848d0dccd0f121dab06c441040ec61a3..c41a161c82c7675574f70a6edc1df74079364f56 100644 (file)
@@ -4978,6 +4978,8 @@ pub mod posix01 {
             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;
 
@@ -5130,6 +5132,7 @@ pub mod extra {
             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;
@@ -5151,6 +5154,8 @@ pub mod extra {
             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;
index e4e7459f8c60581557b7285f14a241acba3684a7..732b77a626e88053381857c200484007e05b64ea 100644 (file)
@@ -99,7 +99,6 @@
 
 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;
index 2af3c393868d8ef355af0a523ae6f178495e8147..81acaf66e08fcf742fa4376d9b36afdabaaa431a 100644 (file)
 //! 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;
@@ -280,6 +279,8 @@ pub struct CratePaths {
     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) {
diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs
deleted file mode 100644 (file)
index c7968db..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-// 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(())
-    }
-}
index 297041a99079072577940109fe7e01c2dded21d3..ef5ba625e114b68bfef689aa4416e904fbf4a6a6 100644 (file)
@@ -51,7 +51,6 @@
 #[macro_use] extern crate log;
 
 pub mod abi;
-pub mod archive;
 pub mod tempdir;
 pub mod arm;
 pub mod mips;
index 3ae70ca854bdbf5c6083978bdf41923cd0d5200a..b2dcd5aae21139feaaf4275b2ac7abd001f92533 100644 (file)
@@ -29,6 +29,7 @@ pub fn opts() -> TargetOptions {
             "-Wl,--as-needed".to_string(),
         ],
         position_independent_executables: true,
+        archive_format: "gnu".to_string(),
         .. Default::default()
     }
 }
index bc5f306cd3568b8ad8a5521a5172eac59f9997fa..8ecee61621941a7eb735028f8e234ce8fd0e6e68 100644 (file)
@@ -166,6 +166,11 @@ pub struct TargetOptions {
     /// 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 {
@@ -202,6 +207,7 @@ fn default() -> TargetOptions {
             position_independent_executables: false,
             pre_link_objects: Vec::new(),
             post_link_objects: Vec::new(),
+            archive_format: String::new(),
         }
     }
 }
index 148be8cab76e37aab16f2a2d7bb91d941f8879c9..fd29fe89271cb8f38401794aa6175098edcb7953 100644 (file)
@@ -25,6 +25,7 @@ pub fn opts() -> TargetOptions {
         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.
index b591e37f893e254d919441924b5176aefd6f935e..2c6022bc6149584488f988552e1759a06b40f2b3 100644 (file)
@@ -13,6 +13,7 @@
 use ArchiveRef;
 
 use std::ffi::CString;
+use std::marker;
 use std::path::Path;
 use std::slice;
 use std::str;
@@ -25,8 +26,8 @@ pub struct Iter<'a> {
 }
 
 pub struct Child<'a> {
-    name: Option<&'a str>,
-    data: &'a [u8],
+    ptr: ::ArchiveChildRef,
+    _data: marker::PhantomData<&'a ArchiveRO>,
 }
 
 impl ArchiveRO {
@@ -60,6 +61,8 @@ fn path2cstr(p: &Path) -> CString {
         }
     }
 
+    pub fn raw(&self) -> ArchiveRef { self.ptr }
+
     pub fn iter(&self) -> Iter {
         unsafe {
             Iter { ptr: ::LLVMRustArchiveIteratorNew(self.ptr), archive: self }
@@ -79,28 +82,11 @@ impl<'a> Iterator for Iter<'a> {
     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 })
         }
     }
 }
@@ -114,6 +100,33 @@ fn drop(&mut self) {
 }
 
 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); }
+    }
 }
index c4960c8d6a3998ac1c59ee4804bfbc10c1dae3a4..7afcb54cce43613d2867aacbfd81ec2bbd832707 100644 (file)
@@ -522,6 +522,9 @@ pub enum DebugLoc_opaque {}
 #[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);
@@ -2069,12 +2072,12 @@ pub fn LLVMRustRunRestrictionPass(M: ModuleRef,
 
     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);
 
@@ -2111,6 +2114,15 @@ pub fn LLVMSetInlineAsmDiagnosticHandler(C: ContextRef,
                                              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
diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs
new file mode 100644 (file)
index 0000000..cc3d1d8
--- /dev/null
@@ -0,0 +1,504 @@
+// 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
+        }
+    }
+}
index 21bc61593c9cdbf451fadcb726ba0277e753be5d..d24d836975110b5e294bd7de7f4d2c22d6703a4c 100644 (file)
@@ -8,7 +8,7 @@
 // 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;
@@ -20,8 +20,9 @@
 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};
@@ -513,18 +514,22 @@ fn link_binary_output(sess: &Session,
         }
     }
 
+    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());
         }
     }
 
@@ -548,13 +553,13 @@ fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
 }
 
 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),
     }
@@ -569,11 +574,12 @@ fn archive_config<'a>(sess: &'a Session,
 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() {
@@ -587,13 +593,12 @@ fn link_rlib<'a>(sess: &'a Session,
     // 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
@@ -622,8 +627,7 @@ fn link_rlib<'a>(sess: &'a Session,
             // 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)
             }) {
@@ -633,8 +637,7 @@ fn link_rlib<'a>(sess: &'a Session,
                                         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
@@ -646,7 +649,9 @@ fn link_rlib<'a>(sess: &'a Session,
                 // 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| {
@@ -676,8 +681,7 @@ fn link_rlib<'a>(sess: &'a Session,
                     }
                 };
 
-                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.
@@ -692,7 +696,7 @@ fn link_rlib<'a>(sess: &'a Session,
             // 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();
             }
         }
@@ -749,12 +753,12 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write,
 // 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();
     }
@@ -781,7 +785,7 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) {
     }
 
     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 \
@@ -806,10 +810,10 @@ fn link_staticlib(sess: &Session, objects: &[PathBuf], out_filename: &Path) {
 // 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);
@@ -827,7 +831,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
         } 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");
@@ -1185,20 +1189,13 @@ fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path,
         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
@@ -1217,6 +1214,7 @@ fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path,
             }
 
             if any_objects {
+                archive.build();
                 cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst));
             }
         });
index 518a6c248407a995e38a6edc990b791c8fd18ad9..a272e7c4c8f04792c1cb75c6f9c09fc108249b4b 100644 (file)
@@ -13,7 +13,7 @@
 use std::process::Command;
 use std::fs;
 
-use rustc_back::archive;
+use back::archive;
 use session::Session;
 use session::config;
 
@@ -88,11 +88,7 @@ fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) {
             // -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);
         }
     }
index cd2e597db1333c70848548dc6f2763971ea0613b..0e425e1fd991029e66ef37b24a08a8194493f7b6 100644 (file)
@@ -53,8 +53,8 @@
 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;
@@ -68,7 +68,6 @@
 
 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;
@@ -78,6 +77,7 @@ pub mod back {
     pub use rustc_back::x86;
     pub use rustc_back::x86_64;
 
+    pub mod archive;
     pub mod linker;
     pub mod link;
     pub mod lto;
index 7c2cd615303c0c19e2e30a6466d3e3e9ec9d44da..b65e065fe9148e0d307f3027e40322d83ed2b2ba 100644 (file)
@@ -36,7 +36,6 @@
 ///
 /// * 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
 ///
index ffc204ada60fb92a0468654690c95afa0e51b6b3..f13c10156f579acd7b01801ba1f29a0d415d5584 100644 (file)
@@ -8,7 +8,7 @@
 // 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};
@@ -272,6 +272,11 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+#[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`.
@@ -444,6 +449,15 @@ fn cmp(&self, other: &CStr) -> Ordering {
     }
 }
 
+#[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::*;
@@ -515,4 +529,28 @@ fn to_str() {
             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);
+    }
 }
index 98d8bc6bdaf723975326cbf22f87e612183914f5..44541b7875423a235fe010717a37368bb32511ae 100644 (file)
@@ -20,7 +20,7 @@
 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.
@@ -29,7 +29,7 @@
 ///
 /// # Examples
 ///
-/// ```no_run
+/// ```
 /// use std::io::prelude::*;
 /// use std::io::BufReader;
 /// use std::fs::File;
@@ -54,12 +54,40 @@ pub struct BufReader<R> {
 
 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 {
@@ -71,20 +99,65 @@ pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
     }
 
     /// 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 }
 }
index caf3f497e10b4c22e6a54088b9902def278c7655..1e82a03f286302d0e951d62c3e2622f198ebd598 100644 (file)
 #![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))]
 
index 8113d0ea8479cd98e7e41d301e8cea8719c8eb3d..867cdcbab94cb88750c0453ac1141a718ec3be42 100644 (file)
@@ -370,13 +370,25 @@ fn get_path(fd: c_int) -> Option<PathBuf> {
             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 {
@@ -390,7 +402,7 @@ fn get_mode(fd: c_int) -> Option<(bool, bool)> {
             }
         }
 
-        #[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
index 334dd6b5f18c1ef56b7c35967a3a6c9949c730e8..2b6b50a1a56d77c108c60d7f5f7bfb47c8e66a5d 100644 (file)
 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",
@@ -102,12 +94,24 @@ fn strerror_r(errnum: c_int, buf: *mut c_char,
 }
 
 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;
         }
     }
 }
@@ -129,11 +133,14 @@ pub struct SplitPaths<'a> {
 }
 
 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)
     }
 }
 
@@ -444,7 +451,7 @@ pub fn page_size() -> usize {
 }
 
 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 {
@@ -456,7 +463,7 @@ pub fn temp_dir() -> PathBuf {
 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"))]
index 16563be2cfb79ee1dbb9656bd5e34fe88eb2a121..06c14b39e124a640e9b6f85be6b565f1b6cb5af5 100644 (file)
 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;
 
@@ -71,6 +75,9 @@
 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 {
@@ -287,6 +294,40 @@ pub struct CRITICAL_SECTION {
 };
 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" {
@@ -437,6 +478,15 @@ pub fn CopyFileExW(lpExistingFileName: libc::LPCWSTR,
                        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
index ae6b20de63910079d53270f749ece6c32bfa446a..890cc455d5df2a2816e3b2eeb9531e337b4a392e 100644 (file)
@@ -30,12 +30,12 @@ pub struct File { handle: Handle }
 
 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 {
@@ -133,7 +133,7 @@ pub fn file_name(&self) -> OsString {
 
     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> {
@@ -146,7 +146,7 @@ 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,
         })
     }
 }
@@ -218,10 +218,12 @@ fn get_flags_and_attributes(&self) -> libc::DWORD {
 }
 
 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)
     }
 
@@ -278,10 +280,13 @@ pub fn file_attr(&self) -> io::Result<FileAttr> {
                     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)
         }
@@ -314,15 +319,11 @@ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
 
     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,
@@ -333,12 +334,20 @@ fn readlink(&self) -> io::Result<PathBuf> {
                                    &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);
@@ -383,7 +392,7 @@ pub fn perm(&self) -> FilePermissions {
     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) }
@@ -414,12 +423,12 @@ pub fn set_readonly(&mut self, readonly: bool) {
 }
 
 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
@@ -430,7 +439,9 @@ fn new(attrs: libc::DWORD, is_symlink: bool) -> FileType {
 
     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 {
@@ -488,7 +499,7 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
 }
 
 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()
 }
 
@@ -517,8 +528,15 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
 
 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 {
@@ -534,9 +552,10 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
                                          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)
     }
@@ -600,3 +619,124 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
     }));
     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(|_| ())
+        }
+    }
+}
diff --git a/src/rustllvm/ArchiveWrapper.cpp b/src/rustllvm/ArchiveWrapper.cpp
new file mode 100644 (file)
index 0000000..2e94c19
--- /dev/null
@@ -0,0 +1,168 @@
+// 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;
+}
index 70ef64afc433628a6b8d422c55a00eedbf681c5e..6861ad43a3cfa334b4db01dd177afb461d89d562 100644 (file)
@@ -825,107 +825,6 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) {
     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) {