// Extracting metadata from crate files import driver::session; import syntax::ast; import lib::llvm::False; import lib::llvm::llvm; import lib::llvm::mk_object_file; import lib::llvm::mk_section_iter; import front::attr; import middle::resolve; import syntax::walk; import syntax::codemap::span; import back::x86; import util::common; import std::ivec; import std::str; import std::vec; import std::ebml; import std::fs; import std::io; import std::option; import std::option::none; import std::option::some; import std::map::hashmap; import std::map::new_int_hash; import syntax::print::pprust; import common::*; export read_crates; export list_file_metadata; // Traverses an AST, reading all the information about use'd crates and native // libraries necessary for later resolving, typechecking, linking, etc. fn read_crates(session::session sess, &ast::crate crate) { auto e = @rec(sess=sess, crate_cache=@std::map::new_str_hash[int](), library_search_paths=sess.get_opts().library_search_paths, mutable next_crate_num=1); auto v = rec(visit_view_item_pre=bind visit_view_item(e, _), visit_item_pre=bind visit_item(e, _) with walk::default_visitor()); walk::walk_crate(v, crate); } type env = @rec(session::session sess, @hashmap[str, int] crate_cache, vec[str] library_search_paths, mutable ast::crate_num next_crate_num); fn visit_view_item(env e, &@ast::view_item i) { alt (i.node) { case (ast::view_item_use(?ident, ?meta_items, ?id)) { auto cnum = resolve_crate(e, ident, meta_items, i.span); cstore::add_use_stmt_cnum(e.sess.get_cstore(), id, cnum); } case (_) { } } } fn visit_item(env e, &@ast::item i) { alt (i.node) { case (ast::item_native_mod(?m)) { if (m.abi != ast::native_abi_rust && m.abi != ast::native_abi_cdecl) { ret; } auto cstore = e.sess.get_cstore(); if (!cstore::add_used_library(cstore, m.native_name)) { ret; } for (ast::attribute a in attr::find_attrs_by_name(i.attrs, "link_args")) { alt (attr::get_meta_item_value_str(attr::attr_meta(a))) { case (some(?linkarg)) { cstore::add_used_link_args(cstore, linkarg); } case (none) { /* fallthrough */ } } } } case (_) { } } } // A diagnostic function for dumping crate metadata to an output stream fn list_file_metadata(str path, io::writer out) { alt (get_metadata_section(path)) { case (option::some(?bytes)) { decoder::list_crate_metadata(bytes, out); } case (option::none) { out.write_str("Could not find metadata in " + path + ".\n"); } } } fn metadata_matches(&vec[u8] crate_data, &(@ast::meta_item)[] metas) -> bool { auto attrs = decoder::get_crate_attributes(crate_data); auto linkage_metas = attr::find_linkage_metas(attrs); log #fmt("matching %u metadata requirements against %u items", ivec::len(metas), ivec::len(linkage_metas)); for (@ast::meta_item needed in metas) { if (!attr::contains(linkage_metas, needed)) { log #fmt("missing %s", pprust::meta_item_to_str(*needed)); ret false; } } ret true; } fn default_native_lib_naming(session::session sess, bool static) -> rec(str prefix, str suffix) { if (static) { ret rec(prefix="lib", suffix=".rlib"); } alt (sess.get_targ_cfg().os) { case (session::os_win32) { ret rec(prefix="", suffix=".dll"); } case (session::os_macos) { ret rec(prefix="lib", suffix=".dylib"); } case (session::os_linux) { ret rec(prefix="lib", suffix=".so"); } } } fn find_library_crate(&session::session sess, &ast::ident ident, &(@ast::meta_item)[] metas, &vec[str] library_search_paths) -> option::t[tup(str, vec[u8])] { attr::require_unique_names(sess, metas); auto crate_name = { auto name_items = attr::find_meta_items_by_name(metas, "name"); alt (ivec::last(name_items)) { case (some(?i)) { alt (attr::get_meta_item_value_str(i)) { case (some(?n)) { n } case (_) { // FIXME: Probably want a warning here since the user // is using the wrong type of meta item ident } } } case (none) { ident } } }; auto nn = default_native_lib_naming(sess, sess.get_opts().static); auto x = find_library_crate_aux(nn, crate_name, metas, library_search_paths); if (x != none || sess.get_opts().static) { ret x; } auto nn2 = default_native_lib_naming(sess, true); ret find_library_crate_aux(nn2, crate_name, metas, library_search_paths); } fn find_library_crate_aux(&rec(str prefix, str suffix) nn, str crate_name, &(@ast::meta_item)[] metas, &vec[str] library_search_paths) -> option::t[tup(str, vec[u8])] { let str prefix = nn.prefix + crate_name; // FIXME: we could probably use a 'glob' function in std::fs but it will // be much easier to write once the unsafe module knows more about FFI // tricks. Currently the glob(3) interface is a bit more than we can // stomach from here, and writing a C++ wrapper is more work than just // manually filtering fs::list_dir here. for (str library_search_path in library_search_paths) { log #fmt("searching %s", library_search_path); for (str path in fs::list_dir(library_search_path)) { log #fmt("searching %s", path); let str f = fs::basename(path); if (!(str::starts_with(f, prefix) && str::ends_with(f, nn.suffix))) { log #fmt("skipping %s, doesn't look like %s*%s", path, prefix, nn.suffix); cont; } alt (get_metadata_section(path)) { case (option::some(?cvec)) { if (!metadata_matches(cvec, metas)) { log #fmt("skipping %s, metadata doesn't match", path); cont; } log #fmt("found %s with matching metadata", path); ret some(tup(path, cvec)); } case (_) { } } } } ret none; } fn get_metadata_section(str filename) -> option::t[vec[u8]] { auto b = str::buf(filename); auto mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(b); if (mb as int == 0) { ret option::none[vec[u8]]; } auto of = mk_object_file(mb); auto si = mk_section_iter(of.llof); while (llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False) { auto name_buf = llvm::LLVMGetSectionName(si.llsi); auto name = str::str_from_cstr(name_buf); if (str::eq(name, x86::get_meta_sect_name())) { auto cbuf = llvm::LLVMGetSectionContents(si.llsi); auto csz = llvm::LLVMGetSectionSize(si.llsi); auto cvbuf = cbuf as vec::vbuf; ret option::some[vec[u8]](vec::vec_from_vbuf[u8](cvbuf, csz)); } llvm::LLVMMoveToNextSection(si.llsi); } ret option::none[vec[u8]]; } fn load_library_crate(&session::session sess, span span, ast::crate_num cnum, &ast::ident ident, &(@ast::meta_item)[] metas, &vec[str] library_search_paths) { alt (find_library_crate(sess, ident, metas, library_search_paths)) { case (some(?t)) { auto cstore = sess.get_cstore(); auto cmeta = rec(name=ident, data=t._1, cnum_map = new_int_hash[ast::crate_num]()); cstore::set_crate_data(cstore, cnum, cmeta); cstore::add_used_crate_file(cstore, t._0); ret; } case (_) { } } sess.span_fatal(span, #fmt("can't find crate for '%s'", ident)); } fn resolve_crate(env e, ast::ident ident, (@ast::meta_item)[] metas, span span) -> ast::crate_num { if (!e.crate_cache.contains_key(ident)) { auto cnum = e.next_crate_num; load_library_crate(e.sess, span, cnum, ident, metas, e.library_search_paths); e.crate_cache.insert(ident, e.next_crate_num); e.next_crate_num += 1; ret cnum; } else { ret e.crate_cache.get(ident); } } // Local Variables: // mode: rust // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; // End: