]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/metadata/loader.rs
Add generation of static libraries to rustc
[rust.git] / src / librustc / metadata / loader.rs
index ecd1c8985bd00dc52404f3aa9e4eb71bfd67737f..1aff16cc23c4b86f75bb3ba868cde9cbe787a76b 100644 (file)
 
 //! Finds crate binaries and loads their metadata
 
-
-use lib::llvm::{False, llvm, mk_object_file, mk_section_iter};
+use back::archive::Archive;
+use driver::session::Session;
+use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
 use metadata::decoder;
 use metadata::encoder;
-use metadata::filesearch::{FileSearch, FileMatch, FileMatches, FileDoesntMatch};
+use metadata::filesearch::{FileMatches, FileDoesntMatch};
 use metadata::filesearch;
 use syntax::codemap::Span;
 use syntax::diagnostic::span_handler;
@@ -26,6 +27,7 @@
 use std::c_str::ToCStr;
 use std::cast;
 use std::io;
+use std::libc;
 use std::num;
 use std::option;
 use std::os::consts::{macos, freebsd, linux, android, win32};
@@ -43,103 +45,176 @@ pub enum Os {
 }
 
 pub struct Context {
-    diag: @mut span_handler,
-    filesearch: @FileSearch,
+    sess: Session,
     span: Span,
     ident: @str,
     metas: ~[@ast::MetaItem],
     hash: @str,
     os: Os,
-    is_static: bool,
     intr: @ident_interner
 }
 
-pub fn load_library_crate(cx: &Context) -> (~str, @~[u8]) {
-    match find_library_crate(cx) {
-      Some(t) => t,
-      None => {
-        cx.diag.span_fatal(cx.span,
-                           format!("can't find crate for `{}`",
-                                cx.ident));
-      }
-    }
+pub struct Library {
+    dylib: Option<Path>,
+    rlib: Option<Path>,
+    metadata: @~[u8],
 }
 
-fn find_library_crate(cx: &Context) -> Option<(~str, @~[u8])> {
-    attr::require_unique_names(cx.diag, cx.metas);
-    find_library_crate_aux(cx, libname(cx), cx.filesearch)
-}
+impl Context {
+    pub fn load_library_crate(&self) -> Library {
+        match self.find_library_crate() {
+            Some(t) => t,
+            None => {
+                self.sess.span_fatal(self.span,
+                                     format!("can't find crate for `{}`",
+                                             self.ident));
+            }
+        }
+    }
 
-fn libname(cx: &Context) -> (~str, ~str) {
-    if cx.is_static { return (~"lib", ~".rlib"); }
-    let (dll_prefix, dll_suffix) = match cx.os {
-        OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
-        OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
-        OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
-        OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
-        OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
-    };
+    fn find_library_crate(&self) -> Option<Library> {
+        attr::require_unique_names(self.sess.diagnostic(), self.metas);
+        let filesearch = self.sess.filesearch;
+        let crate_name = crate_name_from_metas(self.metas);
+        let (dyprefix, dysuffix) = self.dylibname();
 
-    (dll_prefix.to_owned(), dll_suffix.to_owned())
-}
+        // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
+        let dylib_prefix = format!("{}{}-", dyprefix, crate_name);
+        let rlib_prefix = format!("lib{}-", crate_name);
 
-fn find_library_crate_aux(
-    cx: &Context,
-    (prefix, suffix): (~str, ~str),
-    filesearch: @filesearch::FileSearch
-) -> Option<(~str, @~[u8])> {
-    let crate_name = crate_name_from_metas(cx.metas);
-    // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
-    let prefix = format!("{}{}-", prefix, crate_name);
-    let mut matches = ~[];
-    filesearch::search(filesearch, |path| -> FileMatch {
-      // FIXME (#9639): This needs to handle non-utf8 paths
-      let path_str = path.filename_str();
-      match path_str {
-          None => FileDoesntMatch,
-          Some(path_str) =>
-              if path_str.starts_with(prefix) && path_str.ends_with(suffix) {
-                  debug!("{} is a candidate", path.display());
-                  match get_metadata_section(cx.os, path) {
-                      Some(cvec) =>
-                          if !crate_matches(cvec, cx.metas, cx.hash) {
-                              debug!("skipping {}, metadata doesn't match",
-                                  path.display());
-                              FileDoesntMatch
-                          } else {
-                              debug!("found {} with matching metadata", path.display());
-                              // FIXME (#9639): This needs to handle non-utf8 paths
-                              matches.push((path.as_str().unwrap().to_owned(), cvec));
-                              FileMatches
-                          },
-                      _ => {
-                          debug!("could not load metadata for {}", path.display());
-                          FileDoesntMatch
-                      }
-                  }
-               }
-               else {
-                   FileDoesntMatch
-               }
-      }
-    });
+        let mut matches = ~[];
+        do filesearch::search(filesearch) |path| {
+            match path.filename_str() {
+                None => FileDoesntMatch,
+                Some(file) => {
+                    let (candidate, existing) = if file.starts_with(rlib_prefix) &&
+                                                   file.ends_with(".rlib") {
+                        debug!("{} is an rlib candidate", path.display());
+                        (true, self.add_existing_rlib(matches, path, file))
+                    } else if file.starts_with(dylib_prefix) &&
+                              file.ends_with(dysuffix) {
+                        debug!("{} is a dylib candidate", path.display());
+                        (true, self.add_existing_dylib(matches, path, file))
+                    } else {
+                        (false, false)
+                    };
 
-    match matches.len() {
-        0 => None,
-        1 => Some(matches[0]),
-        _ => {
-            cx.diag.span_err(
-                    cx.span, format!("multiple matching crates for `{}`", crate_name));
-                cx.diag.handler().note("candidates:");
-                for pair in matches.iter() {
-                    let ident = pair.first();
-                    let data = pair.second();
-                    cx.diag.handler().note(format!("path: {}", ident));
-                    let attrs = decoder::get_crate_attributes(data);
-                    note_linkage_attrs(cx.intr, cx.diag, attrs);
+                    if candidate && existing {
+                        FileMatches
+                    } else if candidate {
+                        match get_metadata_section(self.sess, self.os, path,
+                                                   crate_name) {
+                            Some(cvec) =>
+                                if crate_matches(cvec, self.metas, self.hash) {
+                                    debug!("found {} with matching metadata",
+                                           path.display());
+                                    let (rlib, dylib) = if file.ends_with(".rlib") {
+                                        (Some(path.clone()), None)
+                                    } else {
+                                        (None, Some(path.clone()))
+                                    };
+                                    matches.push(Library {
+                                        rlib: rlib,
+                                        dylib: dylib,
+                                        metadata: cvec,
+                                    });
+                                    FileMatches
+                                } else {
+                                    debug!("skipping {}, metadata doesn't match",
+                                           path.display());
+                                    FileDoesntMatch
+                                },
+                                _ => {
+                                    debug!("could not load metadata for {}",
+                                           path.display());
+                                    FileDoesntMatch
+                                }
+                        }
+                    } else {
+                        FileDoesntMatch
+                    }
                 }
-                cx.diag.handler().abort_if_errors();
+            }
+        }
+
+        match matches.len() {
+            0 => None,
+            1 => Some(matches[0]),
+            _ => {
+                self.sess.span_err(self.span,
+                    format!("multiple matching crates for `{}`", crate_name));
+                self.sess.note("candidates:");
+                for lib in matches.iter() {
+                    match lib.dylib {
+                        Some(ref p) => {
+                            self.sess.note(format!("path: {}", p.display()));
+                        }
+                        None => {}
+                    }
+                    match lib.rlib {
+                        Some(ref p) => {
+                            self.sess.note(format!("path: {}", p.display()));
+                        }
+                        None => {}
+                    }
+                    let attrs = decoder::get_crate_attributes(lib.metadata);
+                    note_linkage_attrs(self.intr, self.sess.diagnostic(), attrs);
+                }
+                self.sess.abort_if_errors();
                 None
+            }
+        }
+    }
+
+    fn add_existing_rlib(&self, libs: &mut [Library],
+                         path: &Path, file: &str) -> bool {
+        let (prefix, suffix) = self.dylibname();
+        let file = file.slice_from(3); // chop off 'lib'
+        let file = file.slice_to(file.len() - 5); // chop off '.rlib'
+        let file = format!("{}{}{}", prefix, file, suffix);
+
+        for lib in libs.mut_iter() {
+            match lib.dylib {
+                Some(ref p) if p.filename_str() == Some(file.as_slice()) => {
+                    assert!(lib.rlib.is_none()); // XXX: legit compiler error
+                    lib.rlib = Some(path.clone());
+                    return true;
+                }
+                Some(*) | None => {}
+            }
+        }
+        return false;
+    }
+
+    fn add_existing_dylib(&self, libs: &mut [Library],
+                          path: &Path, file: &str) -> bool {
+        let (prefix, suffix) = self.dylibname();
+        let file = file.slice_from(prefix.len());
+        let file = file.slice_to(file.len() - suffix.len());
+        let file = format!("lib{}.rlib", file);
+
+        for lib in libs.mut_iter() {
+            match lib.rlib {
+                Some(ref p) if p.filename_str() == Some(file.as_slice()) => {
+                    assert!(lib.dylib.is_none()); // XXX: legit compiler error
+                    lib.dylib = Some(path.clone());
+                    return true;
+                }
+                Some(*) | None => {}
+            }
+        }
+        return false;
+    }
+
+    // Returns the corresponding (prefix, suffix) that files need to have for
+    // dynamic libraries
+    fn dylibname(&self) -> (&'static str, &'static str) {
+        match self.os {
+            OsWin32 => (win32::DLL_PREFIX, win32::DLL_SUFFIX),
+            OsMacos => (macos::DLL_PREFIX, macos::DLL_SUFFIX),
+            OsLinux => (linux::DLL_PREFIX, linux::DLL_SUFFIX),
+            OsAndroid => (android::DLL_PREFIX, android::DLL_SUFFIX),
+            OsFreebsd => (freebsd::DLL_PREFIX, freebsd::DLL_SUFFIX),
         }
     }
 }
@@ -196,16 +271,26 @@ pub fn metadata_matches(extern_metas: &[@ast::MetaItem],
     local_metas.iter().all(|needed| attr::contains(extern_metas, *needed))
 }
 
-fn get_metadata_section(os: Os,
-                        filename: &Path) -> Option<@~[u8]> {
+fn get_metadata_section(sess: Session, os: Os, filename: &Path,
+                        crate_name: &str) -> Option<@~[u8]> {
     unsafe {
-        let mb = filename.with_c_str(|buf| {
-            llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
-        });
-        if mb as int == 0 { return option::None::<@~[u8]>; }
-        let of = match mk_object_file(mb) {
-            option::Some(of) => of,
-            _ => return option::None::<@~[u8]>
+        let mb = if filename.filename_str().unwrap().ends_with(".rlib") {
+            let archive = Archive::open(sess, filename.clone());
+            let contents = archive.read(crate_name + ".o");
+            let ptr = vec::raw::to_ptr(contents);
+            crate_name.with_c_str(|name| {
+                llvm::LLVMCreateMemoryBufferWithMemoryRangeCopy(
+                    ptr as *i8, contents.len() as libc::size_t, name)
+            })
+        } else {
+            filename.with_c_str(|buf| {
+                llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf)
+            })
+        };
+        if mb as int == 0 { return None }
+        let of = match ObjectFile::new(mb) {
+            Some(of) => of,
+            _ => return None
         };
         let si = mk_section_iter(of.llof);
         while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False {
@@ -266,11 +351,17 @@ pub fn read_meta_section_name(os: Os) -> &'static str {
 }
 
 // A diagnostic function for dumping crate metadata to an output stream
-pub fn list_file_metadata(intr: @ident_interner,
+pub fn list_file_metadata(sess: Session,
+                          intr: @ident_interner,
                           os: Os,
                           path: &Path,
                           out: @mut io::Writer) {
-    match get_metadata_section(os, path) {
+    // guess the crate name from the pathname
+    let crate_name = path.filename_str().unwrap();
+    let crate_name = if crate_name.starts_with("lib") {
+        crate_name.slice_from(3) } else { crate_name };
+    let crate_name = crate_name.split_iter('-').next().unwrap();
+    match get_metadata_section(sess, os, path, crate_name) {
       option::Some(bytes) => decoder::list_crate_metadata(intr, bytes, out),
       option::None => {
         write!(out, "could not find metadata in {}.\n", path.display())