auto merge of #15319 : alexcrichton/rust/no-crate-id, r=brson

This is an implementation of [RFC 35](https://github.com/rust-lang/rfcs/blob/master/active/0035-remove-crate-id.md).

The summary for this PR is the same as that of the RFC, with one addendum:


* Removes the `#[crate_id]` attribute and knowledge of versions from rustc.
* Added a `#[crate_name]` attribute similar to the old `#[crate_id]` attribute
* Output filenames no longer have versions or hashes
* Symbols no longer have versions (they still have hashes)
* A new flag, `--extern`, is used to override searching for external crates
* A new flag, `-C metadata=foo`, used when hashing symbols
* [added] An old flag, `--crate-name`, was re purposed to specify the crate name from the command line.

I tried to maintain backwards compatibility wherever possible (with warnings being printed). If I missed anywhere, however, please let me know!

[breaking-change]

Closes #14468
Closes #14469
Closes #14470
Closes #14471
This commit is contained in:
bors 2014-07-05 22:51:38 +00:00
commit c3ef04be55
113 changed files with 1010 additions and 718 deletions

View file

@ -11,7 +11,7 @@
use back::archive::{Archive, METADATA_FILENAME};
use back::rpath;
use back::svh::Svh;
use driver::driver::{CrateTranslation, OutputFilenames};
use driver::driver::{CrateTranslation, OutputFilenames, Input, FileInput};
use driver::config::NoDebugInfo;
use driver::session::Session;
use driver::config;
@ -19,7 +19,7 @@ use lib::llvm::llvm;
use lib::llvm::ModuleRef;
use lib;
use metadata::common::LinkMeta;
use metadata::{encoder, cstore, filesearch, csearch, loader};
use metadata::{encoder, cstore, filesearch, csearch, loader, creader};
use middle::trans::context::CrateContext;
use middle::trans::common::gensym_name;
use middle::ty;
@ -40,9 +40,8 @@ use syntax::abi;
use syntax::ast;
use syntax::ast_map::{PathElem, PathElems, PathName};
use syntax::ast_map;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::crateid::CrateId;
use syntax::codemap::Span;
use syntax::parse::token;
#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)]
@ -546,32 +545,69 @@ pub mod write {
* system linkers understand.
*/
// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn find_crate_id(attrs: &[ast::Attribute], out_filestem: &str) -> CrateId {
match attr::find_crateid(attrs) {
None => from_str(out_filestem).unwrap_or_else(|| {
let mut s = out_filestem.chars().filter(|c| c.is_XID_continue());
from_str(s.collect::<String>().as_slice())
.or(from_str("rust-out")).unwrap()
}),
Some(s) => s,
pub fn find_crate_name(sess: Option<&Session>,
attrs: &[ast::Attribute],
input: &Input) -> String {
use syntax::crateid::CrateId;
let validate = |s: String, span: Option<Span>| {
creader::validate_crate_name(sess, s.as_slice(), span);
s
};
match sess {
Some(sess) => {
match sess.opts.crate_name {
Some(ref s) => return validate(s.clone(), None),
None => {}
}
}
None => {}
}
let crate_name = attrs.iter().find(|at| at.check_name("crate_name"))
.and_then(|at| at.value_str().map(|s| (at, s)));
match crate_name {
Some((attr, s)) => return validate(s.get().to_string(), Some(attr.span)),
None => {}
}
let crate_id = attrs.iter().find(|at| at.check_name("crate_id"))
.and_then(|at| at.value_str().map(|s| (at, s)))
.and_then(|(at, s)| {
from_str::<CrateId>(s.get()).map(|id| (at, id))
});
match crate_id {
Some((attr, id)) => {
match sess {
Some(sess) => {
sess.span_warn(attr.span, "the #[crate_id] attribute is \
deprecated for the \
#[crate_name] attribute");
}
None => {}
}
return validate(id.name, Some(attr.span))
}
None => {}
}
match *input {
FileInput(ref path) => {
match path.filestem_str() {
Some(s) => return validate(s.to_string(), None),
None => {}
}
}
_ => {}
}
"rust-out".to_string()
}
pub fn crate_id_hash(crate_id: &CrateId) -> String {
// This calculates CMH as defined above. Note that we don't use the path of
// the crate id in the hash because lookups are only done by (name/vers),
// not by path.
let mut s = Sha256::new();
s.input_str(crate_id.short_name_with_version().as_slice());
truncated_hash_result(&mut s).as_slice().slice_to(8).to_string()
}
// FIXME (#9639): This needs to handle non-utf8 `out_filestem` values
pub fn build_link_meta(krate: &ast::Crate, out_filestem: &str) -> LinkMeta {
pub fn build_link_meta(sess: &Session, krate: &ast::Crate,
name: String) -> LinkMeta {
let r = LinkMeta {
crateid: find_crate_id(krate.attrs.as_slice(), out_filestem),
crate_hash: Svh::calculate(krate),
crate_name: name,
crate_hash: Svh::calculate(sess, krate),
};
info!("{}", r);
return r;
@ -594,9 +630,12 @@ fn symbol_hash(tcx: &ty::ctxt,
// to be independent of one another in the crate.
symbol_hasher.reset();
symbol_hasher.input_str(link_meta.crateid.name.as_slice());
symbol_hasher.input_str(link_meta.crate_name.as_slice());
symbol_hasher.input_str("-");
symbol_hasher.input_str(link_meta.crate_hash.as_str());
for meta in tcx.sess.crate_metadata.borrow().iter() {
symbol_hasher.input_str(meta.as_slice());
}
symbol_hasher.input_str("-");
symbol_hasher.input_str(encoder::encoded_ty(tcx, t).as_slice());
// Prefix with 'h' so that it never blends into adjacent digits
@ -666,8 +705,7 @@ pub fn sanitize(s: &str) -> String {
}
pub fn mangle<PI: Iterator<PathElem>>(mut path: PI,
hash: Option<&str>,
vers: Option<&str>) -> String {
hash: Option<&str>) -> String {
// Follow C++ namespace-mangling style, see
// http://en.wikipedia.org/wiki/Name_mangling for more info.
//
@ -698,25 +736,13 @@ pub fn mangle<PI: Iterator<PathElem>>(mut path: PI,
Some(s) => push(&mut n, s),
None => {}
}
match vers {
Some(s) => push(&mut n, s),
None => {}
}
n.push_char('E'); // End name-sequence.
n
}
pub fn exported_name(path: PathElems, hash: &str, vers: &str) -> String {
// The version will get mangled to have a leading '_', but it makes more
// sense to lead with a 'v' b/c this is a version...
let vers = if vers.len() > 0 && !char::is_XID_start(vers.char_at(0)) {
format!("v{}", vers)
} else {
vers.to_string()
};
mangle(path, Some(hash), Some(vers.as_slice()))
pub fn exported_name(path: PathElems, hash: &str) -> String {
mangle(path, Some(hash))
}
pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems,
@ -741,9 +767,7 @@ pub fn mangle_exported_name(ccx: &CrateContext, path: PathElems,
hash.push_char(EXTRA_CHARS.as_bytes()[extra2] as char);
hash.push_char(EXTRA_CHARS.as_bytes()[extra3] as char);
exported_name(path,
hash.as_slice(),
ccx.link_meta.crateid.version_or_default())
exported_name(path, hash.as_slice())
}
pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext,
@ -753,15 +777,11 @@ pub fn mangle_internal_name_by_type_and_seq(ccx: &CrateContext,
let path = [PathName(token::intern(s.as_slice())),
gensym_name(name)];
let hash = get_symbol_hash(ccx, t);
mangle(ast_map::Values(path.iter()), Some(hash.as_slice()), None)
mangle(ast_map::Values(path.iter()), Some(hash.as_slice()))
}
pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> String {
mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None)
}
pub fn output_lib_filename(id: &CrateId) -> String {
format!("{}-{}-{}", id.name, crate_id_hash(id), id.version_or_default())
mangle(path.chain(Some(gensym_name(flav)).move_iter()), None)
}
pub fn get_cc_prog(sess: &Session) -> String {
@ -803,14 +823,15 @@ fn remove(sess: &Session, path: &Path) {
pub fn link_binary(sess: &Session,
trans: &CrateTranslation,
outputs: &OutputFilenames,
id: &CrateId) -> Vec<Path> {
crate_name: &str) -> Vec<Path> {
let mut out_filenames = Vec::new();
for &crate_type in sess.crate_types.borrow().iter() {
if invalid_output_for_target(sess, crate_type) {
sess.bug(format!("invalid output type `{}` for target os `{}`",
crate_type, sess.targ_cfg.os).as_slice());
}
let out_file = link_binary_output(sess, trans, crate_type, outputs, id);
let out_file = link_binary_output(sess, trans, crate_type, outputs,
crate_name);
out_filenames.push(out_file);
}
@ -859,9 +880,11 @@ fn is_writeable(p: &Path) -> bool {
}
}
pub fn filename_for_input(sess: &Session, crate_type: config::CrateType,
id: &CrateId, out_filename: &Path) -> Path {
let libname = output_lib_filename(id);
pub fn filename_for_input(sess: &Session,
crate_type: config::CrateType,
name: &str,
out_filename: &Path) -> Path {
let libname = format!("{}{}", name, sess.opts.cg.extra_filename);
match crate_type {
config::CrateTypeRlib => {
out_filename.with_filename(format!("lib{}.rlib", libname))
@ -891,13 +914,13 @@ fn link_binary_output(sess: &Session,
trans: &CrateTranslation,
crate_type: config::CrateType,
outputs: &OutputFilenames,
id: &CrateId) -> Path {
crate_name: &str) -> Path {
let obj_filename = outputs.temp_path(OutputTypeObject);
let out_filename = match outputs.single_output_file {
Some(ref file) => file.clone(),
None => {
let out_filename = outputs.path(OutputTypeExe);
filename_for_input(sess, crate_type, id, &out_filename)
filename_for_input(sess, crate_type, crate_name, &out_filename)
}
};

View file

@ -53,6 +53,8 @@ use std::iter::range_step;
use syntax::ast;
use syntax::visit;
use driver::session::Session;
#[deriving(Clone, PartialEq)]
pub struct Svh {
hash: String,
@ -68,7 +70,7 @@ impl Svh {
self.hash.as_slice()
}
pub fn calculate(krate: &ast::Crate) -> Svh {
pub fn calculate(sess: &Session, krate: &ast::Crate) -> Svh {
// FIXME (#14132): This is better than it used to be, but it still not
// ideal. We now attempt to hash only the relevant portions of the
// Crate AST as well as the top-level crate attributes. (However,
@ -80,6 +82,10 @@ impl Svh {
// avoid collisions.
let mut state = SipState::new();
for data in sess.opts.cg.metadata.iter() {
data.hash(&mut state);
}
{
let mut visit = svh_visitor::make(&mut state);
visit::walk_crate(&mut visit, krate, ());

View file

@ -11,7 +11,7 @@
//! Contains infrastructure for configuring the compiler, including parsing
//! command line options.
use driver::early_error;
use driver::{early_error, early_warn};
use driver::driver;
use driver::session::Session;
@ -30,7 +30,7 @@ use syntax::diagnostic::{ColorConfig, Auto, Always, Never};
use syntax::parse;
use syntax::parse::token::InternedString;
use std::collections::HashSet;
use std::collections::{HashSet, HashMap};
use getopts::{optopt, optmulti, optflag, optflagopt};
use getopts;
use lib::llvm::llvm;
@ -91,10 +91,12 @@ pub struct Options {
pub debugging_opts: u64,
/// Whether to write dependency files. It's (enabled, optional filename).
pub write_dependency_info: (bool, Option<Path>),
/// Crate id-related things to maybe print. It's (crate_id, crate_name, crate_file_name).
pub print_metas: (bool, bool, bool),
/// Crate id-related things to maybe print. It's (crate_name, crate_file_name).
pub print_metas: (bool, bool),
pub cg: CodegenOptions,
pub color: ColorConfig,
pub externs: HashMap<String, Vec<String>>,
pub crate_name: Option<String>,
}
/// Some reasonable defaults
@ -117,9 +119,11 @@ pub fn basic_options() -> Options {
no_analysis: false,
debugging_opts: 0,
write_dependency_info: (false, None),
print_metas: (false, false, false),
print_metas: (false, false),
cg: basic_codegen_options(),
color: Auto,
externs: HashMap::new(),
crate_name: None,
}
}
@ -318,6 +322,10 @@ cgoptions!(
"use an external assembler rather than LLVM's integrated one"),
relocation_model: String = ("pic".to_string(), parse_string,
"choose the relocation model to use (llc -relocation-model for details)"),
metadata: Vec<String> = (Vec::new(), parse_list,
"metadata to mangle symbol names with"),
extra_filename: String = ("".to_string(), parse_string,
"extra data to put in each output filename"),
)
pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
@ -505,10 +513,12 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
"[bin|lib|rlib|dylib|staticlib]"),
optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
"[asm|bc|ir|obj|link]"),
optflag("", "crate-id", "Output the crate id and exit"),
optflag("", "crate-name", "Output the crate name and exit"),
optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
optopt("", "crate-name", "Specify the name of the crate being built",
"NAME"),
optflag("", "print-crate-name", "Output the crate name and exit"),
optflag("", "print-file-name", "Output the file(s) that would be written if compilation \
continued and exit"),
optflag("", "crate-file-name", "deprecated in favor of --print-file-name"),
optflag("g", "", "Equivalent to --debuginfo=2"),
optopt("", "debuginfo", "Emit DWARF debug info to the objects created:
0 = no debug info,
@ -548,7 +558,9 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
optopt("", "color", "Configure coloring of output:
auto = colorize, if output goes to a tty (default);
always = always colorize output;
never = never colorize output", "auto|always|never")
never = never colorize output", "auto|always|never"),
optmulti("", "extern", "Specify where an external rust library is located",
"PATH"),
)
}
@ -709,9 +721,13 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
matches.opt_str("dep-info")
.map(|p| Path::new(p)));
let print_metas = (matches.opt_present("crate-id"),
matches.opt_present("crate-name"),
let print_metas = (matches.opt_present("print-crate-name"),
matches.opt_present("print-file-name") ||
matches.opt_present("crate-file-name"));
if matches.opt_present("crate-file-name") {
early_warn("the --crate-file-name argument has been renamed to \
--print-file-name");
}
let cg = build_codegen_options(matches);
let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
@ -728,6 +744,23 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
}
};
let mut externs = HashMap::new();
for arg in matches.opt_strs("extern").iter() {
let mut parts = arg.as_slice().splitn('=', 1);
let name = match parts.next() {
Some(s) => s,
None => early_error("--extern value must not be empty"),
};
let location = match parts.next() {
Some(s) => s,
None => early_error("--extern value must be of the format `foo=bar`"),
};
let locs = externs.find_or_insert(name.to_string(), Vec::new());
locs.push(location.to_string());
}
let crate_name = matches.opt_str("crate-name");
Options {
crate_types: crate_types,
gc: gc,
@ -748,7 +781,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
write_dependency_info: write_dependency_info,
print_metas: print_metas,
cg: cg,
color: color
color: color,
externs: externs,
crate_name: crate_name,
}
}

View file

@ -41,7 +41,6 @@ use std::io::MemReader;
use syntax::ast;
use syntax::attr;
use syntax::attr::{AttrMetaMethods};
use syntax::crateid::CrateId;
use syntax::parse;
use syntax::parse::token;
use syntax::print::{pp, pprust};
@ -69,7 +68,7 @@ pub fn compile_input(sess: Session,
// large chunks of memory alive and we want to free them as soon as
// possible to keep the peak memory usage low
let (outputs, trans, sess) = {
let (outputs, expanded_crate, ast_map) = {
let (outputs, expanded_crate, ast_map, id) = {
let krate = phase_1_parse_input(&sess, cfg, input);
if stop_after_phase_1(&sess) { return; }
let outputs = build_output_filenames(input,
@ -77,25 +76,25 @@ pub fn compile_input(sess: Session,
output,
krate.attrs.as_slice(),
&sess);
let id = link::find_crate_id(krate.attrs.as_slice(),
outputs.out_filestem.as_slice());
let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(),
input);
let (expanded_crate, ast_map)
= match phase_2_configure_and_expand(&sess, krate, &id) {
= match phase_2_configure_and_expand(&sess, krate, id.as_slice()) {
None => return,
Some(p) => p,
};
(outputs, expanded_crate, ast_map)
(outputs, expanded_crate, ast_map, id)
};
write_out_deps(&sess, input, &outputs, &expanded_crate);
write_out_deps(&sess, input, &outputs, id.as_slice());
if stop_after_phase_2(&sess) { return; }
let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
let analysis = phase_3_run_analysis_passes(sess, &expanded_crate,
ast_map, id);
phase_save_analysis(&analysis.ty_cx.sess, &expanded_crate, &analysis, outdir);
if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
analysis, &outputs);
let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate, analysis);
// Discard interned strings as they are no longer required.
token::get_ident_interner().clear();
@ -181,11 +180,14 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
/// Returns `None` if we're aborting after handling -W help.
pub fn phase_2_configure_and_expand(sess: &Session,
mut krate: ast::Crate,
crate_id: &CrateId)
crate_name: &str)
-> Option<(ast::Crate, syntax::ast_map::Map)> {
let time_passes = sess.time_passes();
*sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
*sess.crate_types.borrow_mut() =
collect_crate_types(sess, krate.attrs.as_slice());
*sess.crate_metadata.borrow_mut() =
collect_crate_metadata(sess, krate.attrs.as_slice());
time(time_passes, "gated feature checking", (), |_|
front::feature_gate::check_crate(sess, &krate));
@ -247,7 +249,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
}
let cfg = syntax::ext::expand::ExpansionConfig {
deriving_hash_type_parameter: sess.features.default_type_params.get(),
crate_id: crate_id.clone(),
crate_name: crate_name.to_string(),
};
syntax::ext::expand::expand_crate(&sess.parse_sess,
cfg,
@ -286,6 +288,7 @@ pub struct CrateAnalysis {
pub public_items: middle::privacy::PublicItems,
pub ty_cx: ty::ctxt,
pub reachable: NodeSet,
pub name: String,
}
/// Run the resolution, typechecking, region checking and other
@ -293,7 +296,8 @@ pub struct CrateAnalysis {
/// structures carrying the results of the analysis.
pub fn phase_3_run_analysis_passes(sess: Session,
krate: &ast::Crate,
ast_map: syntax::ast_map::Map) -> CrateAnalysis {
ast_map: syntax::ast_map::Map,
name: String) -> CrateAnalysis {
let time_passes = sess.time_passes();
@ -398,6 +402,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
exported_items: exported_items,
public_items: public_items,
reachable: reachable_map,
name: name,
}
}
@ -426,8 +431,7 @@ pub struct CrateTranslation {
/// Run the translation phase to LLVM, after which the AST and analysis can
/// be discarded.
pub fn phase_4_translate_to_llvm(krate: ast::Crate,
analysis: CrateAnalysis,
outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
analysis: CrateAnalysis) -> (ty::ctxt, CrateTranslation) {
let time_passes = analysis.ty_cx.sess.time_passes();
time(time_passes, "resolving dependency formats", (), |_|
@ -435,7 +439,7 @@ pub fn phase_4_translate_to_llvm(krate: ast::Crate,
// Option dance to work around the lack of stack once closures.
time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
trans::base::trans_crate(krate, analysis, outputs))
trans::base::trans_crate(krate, analysis))
}
/// Run LLVM itself, producing a bitcode file, assembly file or object file
@ -473,7 +477,7 @@ pub fn phase_6_link_output(sess: &Session,
link::link_binary(sess,
trans,
outputs,
&trans.link.crateid));
trans.link.crate_name.as_slice()));
}
pub fn stop_after_phase_3(sess: &Session) -> bool {
@ -514,9 +518,7 @@ pub fn stop_after_phase_5(sess: &Session) -> bool {
fn write_out_deps(sess: &Session,
input: &Input,
outputs: &OutputFilenames,
krate: &ast::Crate) {
let id = link::find_crate_id(krate.attrs.as_slice(),
outputs.out_filestem.as_slice());
id: &str) {
let mut out_filenames = Vec::new();
for output_type in sess.opts.output_types.iter() {
@ -524,7 +526,8 @@ fn write_out_deps(sess: &Session,
match *output_type {
link::OutputTypeExe => {
for output in sess.crate_types.borrow().iter() {
let p = link::filename_for_input(sess, *output, &id, &file);
let p = link::filename_for_input(sess, *output,
id, &file);
out_filenames.push(p);
}
}
@ -649,13 +652,13 @@ pub fn pretty_print_input(sess: Session,
ppm: PpMode,
ofile: Option<Path>) {
let krate = phase_1_parse_input(&sess, cfg, input);
let id = link::find_crate_id(krate.attrs.as_slice(),
input.filestem().as_slice());
let id = link::find_crate_name(Some(&sess), krate.attrs.as_slice(), input);
let (krate, ast_map, is_expanded) = match ppm {
PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
let (krate, ast_map)
= match phase_2_configure_and_expand(&sess, krate, &id) {
= match phase_2_configure_and_expand(&sess, krate,
id.as_slice()) {
None => return,
Some(p) => p,
};
@ -695,7 +698,7 @@ pub fn pretty_print_input(sess: Session,
}
PpmTyped => {
let ast_map = ast_map.expect("--pretty=typed missing ast_map");
let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map, id);
let annotation = TypedAnnotation {
analysis: analysis
};
@ -728,7 +731,8 @@ pub fn pretty_print_input(sess: Session,
}
}
};
let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
let analysis = phase_3_run_analysis_passes(sess, &krate,
ast_map, id);
print_flowgraph(analysis, block, out)
}
_ => {
@ -845,6 +849,11 @@ pub fn collect_crate_types(session: &Session,
}).collect()
}
pub fn collect_crate_metadata(session: &Session,
_attrs: &[ast::Attribute]) -> Vec<String> {
session.opts.cg.metadata.clone()
}
pub struct OutputFilenames {
pub out_directory: Path,
pub out_filestem: String,
@ -893,14 +902,11 @@ pub fn build_output_filenames(input: &Input,
None => Path::new(".")
};
let mut stem = input.filestem();
// If a crateid is present, we use it as the link name
let crateid = attr::find_crateid(attrs);
match crateid {
None => {}
Some(crateid) => stem = crateid.name.to_string(),
}
// If a crate name is present, we use it as the link name
let stem = match attr::find_crate_name(attrs) {
None => input.filestem(),
Some(name) => name.get().to_string(),
};
OutputFilenames {
out_directory: dirpath,
out_filestem: stem,

View file

@ -294,28 +294,26 @@ fn print_crate_info(sess: &Session,
odir: &Option<Path>,
ofile: &Option<Path>)
-> bool {
let (crate_id, crate_name, crate_file_name) = sess.opts.print_metas;
let (crate_name, crate_file_name) = sess.opts.print_metas;
// these nasty nested conditions are to avoid doing extra work
if crate_id || crate_name || crate_file_name {
if crate_name || crate_file_name {
let attrs = parse_crate_attrs(sess, input);
let t_outputs = driver::build_output_filenames(input,
odir,
ofile,
attrs.as_slice(),
sess);
let id = link::find_crate_id(attrs.as_slice(),
t_outputs.out_filestem.as_slice());
let id = link::find_crate_name(Some(sess), attrs.as_slice(), input);
if crate_id {
println!("{}", id.to_str());
}
if crate_name {
println!("{}", id.name);
println!("{}", id);
}
if crate_file_name {
let crate_types = driver::collect_crate_types(sess, attrs.as_slice());
let metadata = driver::collect_crate_metadata(sess, attrs.as_slice());
*sess.crate_metadata.borrow_mut() = metadata;
for &style in crate_types.iter() {
let fname = link::filename_for_input(sess, style, &id,
let fname = link::filename_for_input(sess, style, id.as_slice(),
&t_outputs.with_extension(""));
println!("{}", fname.filename_display());
}
@ -390,6 +388,11 @@ pub fn early_error(msg: &str) -> ! {
fail!(diagnostic::FatalError);
}
pub fn early_warn(msg: &str) {
let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto);
emitter.emit(None, msg, diagnostic::Warning);
}
pub fn list_metadata(sess: &Session, path: &Path,
out: &mut io::Writer) -> io::IoResult<()> {
metadata::loader::list_file_metadata(sess.targ_cfg.os, path, out)

View file

@ -47,6 +47,7 @@ pub struct Session {
pub lints: RefCell<NodeMap<Vec<(lint::LintId, codemap::Span, String)>>>,
pub node_id: Cell<ast::NodeId>,
pub crate_types: RefCell<Vec<config::CrateType>>,
pub crate_metadata: RefCell<Vec<String>>,
pub features: front::feature_gate::Features,
/// The maximum recursion limit for potentially infinitely recursive
@ -243,6 +244,7 @@ pub fn build_session_(sopts: config::Options,
lints: RefCell::new(NodeMap::new()),
node_id: Cell::new(1),
crate_types: RefCell::new(Vec::new()),
crate_metadata: RefCell::new(Vec::new()),
features: front::feature_gate::Features::new(),
recursion_limit: Cell::new(64),
};

View file

@ -25,8 +25,6 @@ use syntax::util::small_vector::SmallVector;
use std::mem;
use std::gc::{Gc, GC};
pub static VERSION: &'static str = "0.11.0";
pub fn maybe_inject_crates_ref(sess: &Session, krate: ast::Crate)
-> ast::Crate {
if use_std(&krate) {
@ -60,24 +58,12 @@ struct StandardLibraryInjector<'a> {
sess: &'a Session,
}
pub fn with_version(krate: &str) -> Option<(InternedString, ast::StrStyle)> {
match option_env!("CFG_DISABLE_INJECT_STD_VERSION") {
Some("1") => None,
_ => {
Some((token::intern_and_get_ident(format!("{}#{}",
krate,
VERSION).as_slice()),
ast::CookedStr))
}
}
}
impl<'a> fold::Folder for StandardLibraryInjector<'a> {
fn fold_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
let mut vis = vec!(ast::ViewItem {
node: ast::ViewItemExternCrate(token::str_to_ident("std"),
with_version("std"),
ast::DUMMY_NODE_ID),
None,
ast::DUMMY_NODE_ID),
attrs: vec!(
attr::mk_attr_outer(attr::mk_attr_id(), attr::mk_list_item(
InternedString::new("phase"),
@ -95,8 +81,8 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> {
if use_start(&krate) && any_exe {
vis.push(ast::ViewItem {
node: ast::ViewItemExternCrate(token::str_to_ident("native"),
with_version("native"),
ast::DUMMY_NODE_ID),
None,
ast::DUMMY_NODE_ID),
attrs: Vec::new(),
vis: ast::Inherited,
span: DUMMY_SP

View file

@ -15,7 +15,6 @@
use driver::session::Session;
use front::config;
use front::std_inject::with_version;
use std::cell::RefCell;
use std::gc::{Gc, GC};
@ -154,7 +153,7 @@ fn generate_test_harness(sess: &Session, krate: ast::Crate)
ext_cx: ExtCtxt::new(&sess.parse_sess, sess.opts.cfg.clone(),
ExpansionConfig {
deriving_hash_type_parameter: false,
crate_id: from_str("test").unwrap(),
crate_name: "test".to_string(),
}),
path: RefCell::new(Vec::new()),
testfns: RefCell::new(Vec::new()),
@ -298,9 +297,7 @@ fn mk_std(cx: &TestCtxt) -> ast::ViewItem {
ast::DUMMY_NODE_ID))),
ast::Public)
} else {
(ast::ViewItemExternCrate(id_test,
with_version("test"),
ast::DUMMY_NODE_ID),
(ast::ViewItemExternCrate(id_test, None, ast::DUMMY_NODE_ID),
ast::Inherited)
};
ast::ViewItem {
@ -395,8 +392,8 @@ fn mk_tests(cx: &TestCtxt) -> Gc<ast::Item> {
}
fn is_test_crate(krate: &ast::Crate) -> bool {
match attr::find_crateid(krate.attrs.as_slice()) {
Some(ref s) if "test" == s.name.as_slice() => true,
match attr::find_crate_name(krate.attrs.as_slice()) {
Some(ref s) if "test" == s.get().as_slice() => true,
_ => false
}
}

View file

@ -18,7 +18,8 @@ This API is completely unstable and subject to change.
*/
#![crate_id = "rustc#0.11.0"]
#![crate_id = "rustc#0.11.0"] // NOTE: remove after stage0
#![crate_name = "rustc"]
#![experimental]
#![comment = "The Rust compiler"]
#![license = "MIT/ASL2"]
@ -31,6 +32,7 @@ This API is completely unstable and subject to change.
#![allow(deprecated)]
#![feature(macro_rules, globs, struct_variant, managed_boxes, quote)]
#![feature(default_type_params, phase, unsafe_destructor)]
#![allow(unused_attribute)] // NOTE: remove after stage0
extern crate arena;
extern crate debug;

View file

@ -11,7 +11,6 @@
#![allow(non_camel_case_types)]
use std::mem;
use syntax::crateid::CrateId;
use back::svh::Svh;
// EBML enum definitions and utils shared by the encoder and decoder
@ -71,9 +70,9 @@ pub static tag_crate_deps: uint = 0x18;
pub static tag_crate_dep: uint = 0x19;
pub static tag_crate_hash: uint = 0x1a;
pub static tag_crate_crateid: uint = 0x1b;
pub static tag_crate_crate_name: uint = 0x1b;
pub static tag_crate_dep_crateid: uint = 0x1d;
pub static tag_crate_dep_crate_name: uint = 0x1d;
pub static tag_crate_dep_hash: uint = 0x1e;
pub static tag_mod_impl: uint = 0x1f;
@ -215,7 +214,7 @@ pub static tag_items_data_item_stability: uint = 0x92;
#[deriving(Clone, Show)]
pub struct LinkMeta {
pub crateid: CrateId,
pub crate_name: String,
pub crate_hash: Svh,
}

View file

@ -12,7 +12,6 @@
//! Validates all used crates and extern libraries and loads their metadata
use back::link;
use back::svh::Svh;
use driver::session::Session;
use driver::{driver, config};
@ -33,7 +32,6 @@ use syntax::codemap::{Span};
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::parse::token;
use syntax::crateid::CrateId;
use syntax::visit;
struct Env<'a> {
@ -69,7 +67,7 @@ impl<'a> visit::Visitor<()> for Env<'a> {
fn dump_crates(cstore: &CStore) {
debug!("resolved crates:");
cstore.iter_crate_data_origins(|_, data, opt_source| {
debug!("crate_id: {}", data.crate_id());
debug!(" name: {}", data.name());
debug!(" cnum: {}", data.cnum);
debug!(" hash: {}", data.hash());
opt_source.map(|cs| {
@ -83,20 +81,17 @@ fn dump_crates(cstore: &CStore) {
fn warn_if_multiple_versions(diag: &SpanHandler, cstore: &CStore) {
let mut map = HashMap::new();
cstore.iter_crate_data(|cnum, data| {
let crateid = data.crate_id();
let key = (crateid.name.clone(), crateid.path.clone());
map.find_or_insert_with(key, |_| Vec::new()).push(cnum);
map.find_or_insert_with(data.name(), |_| Vec::new()).push(cnum);
});
for ((name, _), dupes) in map.move_iter() {
for (name, dupes) in map.move_iter() {
if dupes.len() == 1 { continue }
diag.handler().warn(
format!("using multiple versions of crate `{}`",
name).as_slice());
format!("using multiple versions of crate `{}`", name).as_slice());
for dupe in dupes.move_iter() {
let data = cstore.get_crate_data(dupe);
diag.span_note(data.span, "used here");
loader::note_crateid_attr(diag, &data.crate_id());
loader::note_crate_name(diag, data.name().as_slice());
}
}
}
@ -129,7 +124,7 @@ fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
let (cnum, _, _) = resolve_crate(e,
&None,
info.ident.as_slice(),
&info.crate_id,
info.name.as_slice(),
None,
i.span);
e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
@ -140,7 +135,7 @@ fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
struct CrateInfo {
ident: String,
crate_id: CrateId,
name: String,
id: ast::NodeId,
should_link: bool,
}
@ -151,22 +146,18 @@ fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
let ident = token::get_ident(ident);
debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
ident, path_opt);
let crate_id = match *path_opt {
let name = match *path_opt {
Some((ref path_str, _)) => {
let crateid: Option<CrateId> = from_str(path_str.get());
match crateid {
None => {
e.sess.span_err(i.span, "malformed crate id");
return None
}
Some(id) => id
}
let name = path_str.get().to_str();
validate_crate_name(Some(e.sess), name.as_slice(),
Some(i.span));
name
}
None => from_str(ident.get().to_str().as_slice()).unwrap()
None => ident.get().to_str(),
};
Some(CrateInfo {
ident: ident.get().to_string(),
crate_id: crate_id,
name: name,
id: id,
should_link: should_link(i),
})
@ -175,6 +166,28 @@ fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
}
}
pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
let err = |s: &str| {
match (sp, sess) {
(_, None) => fail!("{}", s),
(Some(sp), Some(sess)) => sess.span_err(sp, s),
(None, Some(sess)) => sess.err(s),
}
};
if s.len() == 0 {
err("crate name must not be empty");
}
for c in s.chars() {
if c.is_alphanumeric() { continue }
if c == '_' || c == '-' { continue }
err(format!("invalid character `{}` in crate name: `{}`", c, s).as_slice());
}
match sess {
Some(sess) => sess.abort_if_errors(),
None => {}
}
}
fn visit_item(e: &Env, i: &ast::Item) {
match i.node {
ast::ItemForeignMod(ref fm) => {
@ -263,17 +276,36 @@ fn visit_item(e: &Env, i: &ast::Item) {
}
}
fn existing_match(e: &Env, crate_id: &CrateId,
fn existing_match(e: &Env, name: &str,
hash: Option<&Svh>) -> Option<ast::CrateNum> {
let mut ret = None;
e.sess.cstore.iter_crate_data(|cnum, data| {
let other_id = data.crate_id();
if crate_id.matches(&other_id) {
let other_hash = data.hash();
match hash {
Some(hash) if *hash != other_hash => {}
Some(..) | None => { ret = Some(cnum); }
if data.name().as_slice() != name { return }
match hash {
Some(hash) if *hash == data.hash() => { ret = Some(cnum); return }
Some(..) => return,
None => {}
}
// When the hash is None we're dealing with a top-level dependency in
// which case we may have a specification on the command line for this
// library. Even though an upstream library may have loaded something of
// the same name, we have to make sure it was loaded from the exact same
// location as well.
let source = e.sess.cstore.get_used_crate_source(cnum).unwrap();
let dylib = source.dylib.as_ref().map(|p| p.as_vec());
let rlib = source.rlib.as_ref().map(|p| p.as_vec());
match e.sess.opts.externs.find_equiv(&name) {
Some(locs) => {
let found = locs.iter().any(|l| {
Some(l.as_bytes()) == dylib || Some(l.as_bytes()) == rlib
});
if found {
ret = Some(cnum);
}
}
None => ret = Some(cnum),
}
});
return ret;
@ -282,7 +314,7 @@ fn existing_match(e: &Env, crate_id: &CrateId,
fn register_crate<'a>(e: &mut Env,
root: &Option<CratePaths>,
ident: &str,
crate_id: &CrateId,
name: &str,
span: Span,
lib: loader::Library)
-> (ast::CrateNum, Rc<cstore::crate_metadata>,
@ -309,7 +341,7 @@ fn register_crate<'a>(e: &mut Env,
let loader::Library{ dylib, rlib, metadata } = lib;
let cmeta = Rc::new( cstore::crate_metadata {
name: crate_id.name.to_string(),
name: name.to_string(),
data: metadata,
cnum_map: cnum_map,
cnum: cnum,
@ -330,20 +362,18 @@ fn register_crate<'a>(e: &mut Env,
fn resolve_crate<'a>(e: &mut Env,
root: &Option<CratePaths>,
ident: &str,
crate_id: &CrateId,
name: &str,
hash: Option<&Svh>,
span: Span)
-> (ast::CrateNum, Rc<cstore::crate_metadata>,
cstore::CrateSource) {
match existing_match(e, crate_id, hash) {
match existing_match(e, name, hash) {
None => {
let id_hash = link::crate_id_hash(crate_id);
let mut load_ctxt = loader::Context {
sess: e.sess,
span: span,
ident: ident,
crate_id: crate_id,
id_hash: id_hash.as_slice(),
crate_name: name,
hash: hash.map(|a| &*a),
filesearch: e.sess.target_filesearch(),
os: e.sess.targ_cfg.os,
@ -351,9 +381,10 @@ fn resolve_crate<'a>(e: &mut Env,
root: root,
rejected_via_hash: vec!(),
rejected_via_triple: vec!(),
should_match_name: true,
};
let library = load_ctxt.load_library_crate();
register_crate(e, root, ident, crate_id, span, library)
register_crate(e, root, ident, name, span, library)
}
Some(cnum) => (cnum,
e.sess.cstore.get_crate_data(cnum),
@ -370,10 +401,10 @@ fn resolve_crate_deps(e: &mut Env,
// The map from crate numbers in the crate we're resolving to local crate
// numbers
decoder::get_crate_deps(cdata).iter().map(|dep| {
debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
debug!("resolving dep crate {} hash: `{}`", dep.name, dep.hash);
let (local_cnum, _, _) = resolve_crate(e, root,
dep.crate_id.name.as_slice(),
&dep.crate_id,
dep.name.as_slice(),
dep.name.as_slice(),
Some(&dep.hash),
span);
(dep.cnum, local_cnum)
@ -399,14 +430,12 @@ impl<'a> PluginMetadataReader<'a> {
let target_triple = self.env.sess.targ_cfg.target_strs.target_triple.as_slice();
let is_cross = target_triple != driver::host_triple();
let mut should_link = info.should_link && !is_cross;
let id_hash = link::crate_id_hash(&info.crate_id);
let os = config::get_os(driver::host_triple()).unwrap();
let mut load_ctxt = loader::Context {
sess: self.env.sess,
span: krate.span,
ident: info.ident.as_slice(),
crate_id: &info.crate_id,
id_hash: id_hash.as_slice(),
crate_name: info.name.as_slice(),
hash: None,
filesearch: self.env.sess.host_filesearch(),
triple: driver::host_triple(),
@ -414,6 +443,7 @@ impl<'a> PluginMetadataReader<'a> {
root: &None,
rejected_via_hash: vec!(),
rejected_via_triple: vec!(),
should_match_name: true,
};
let library = match load_ctxt.maybe_load_library_crate() {
Some (l) => l,
@ -448,10 +478,11 @@ impl<'a> PluginMetadataReader<'a> {
macros: macros,
registrar_symbol: registrar,
};
if should_link && existing_match(&self.env, &info.crate_id, None).is_none() {
if should_link && existing_match(&self.env, info.name.as_slice(),
None).is_none() {
// register crate now to avoid double-reading metadata
register_crate(&mut self.env, &None, info.ident.as_slice(),
&info.crate_id, krate.span, library);
info.name.as_slice(), krate.span, library);
}
pc
}

View file

@ -22,7 +22,6 @@ use std::c_vec::CVec;
use std::rc::Rc;
use std::collections::HashMap;
use syntax::ast;
use syntax::crateid::CrateId;
use syntax::codemap::Span;
use syntax::parse::token::IdentInterner;
@ -220,7 +219,7 @@ impl CStore {
impl crate_metadata {
pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() }
pub fn crate_id(&self) -> CrateId { decoder::get_crate_id(self.data()) }
pub fn name(&self) -> String { decoder::get_crate_name(self.data()) }
pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) }
}

View file

@ -46,7 +46,6 @@ use syntax::parse::token;
use syntax::print::pprust;
use syntax::ast;
use syntax::codemap;
use syntax::crateid::CrateId;
pub type Cmd<'a> = &'a crate_metadata;
@ -1101,7 +1100,7 @@ pub fn get_crate_attributes(data: &[u8]) -> Vec<ast::Attribute> {
#[deriving(Clone)]
pub struct CrateDep {
pub cnum: ast::CrateNum,
pub crate_id: CrateId,
pub name: String,
pub hash: Svh,
}
@ -1115,13 +1114,11 @@ pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
d.as_str_slice().to_string()
}
reader::tagged_docs(depsdoc, tag_crate_dep, |depdoc| {
let crate_id =
from_str(docstr(depdoc,
tag_crate_dep_crateid).as_slice()).unwrap();
let name = docstr(depdoc, tag_crate_dep_crate_name);
let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash).as_slice());
deps.push(CrateDep {
cnum: crate_num,
crate_id: crate_id,
name: name,
hash: hash,
});
crate_num += 1;
@ -1133,7 +1130,7 @@ pub fn get_crate_deps(data: &[u8]) -> Vec<CrateDep> {
fn list_crate_deps(data: &[u8], out: &mut io::Writer) -> io::IoResult<()> {
try!(write!(out, "=External Dependencies=\n"));
for dep in get_crate_deps(data).iter() {
try!(write!(out, "{} {}-{}\n", dep.cnum, dep.crate_id, dep.hash));
try!(write!(out, "{} {}-{}\n", dep.cnum, dep.name, dep.hash));
}
try!(write!(out, "\n"));
Ok(())
@ -1152,23 +1149,21 @@ pub fn get_crate_hash(data: &[u8]) -> Svh {
Svh::new(hashdoc.as_str_slice())
}
pub fn maybe_get_crate_id(data: &[u8]) -> Option<CrateId> {
pub fn maybe_get_crate_name(data: &[u8]) -> Option<String> {
let cratedoc = ebml::Doc::new(data);
reader::maybe_get_doc(cratedoc, tag_crate_crateid).map(|doc| {
from_str(doc.as_str_slice()).unwrap()
reader::maybe_get_doc(cratedoc, tag_crate_crate_name).map(|doc| {
doc.as_str_slice().to_string()
})
}
pub fn get_crate_triple(data: &[u8]) -> String {
pub fn get_crate_triple(data: &[u8]) -> Option<String> {
let cratedoc = ebml::Doc::new(data);
let triple_doc = reader::maybe_get_doc(cratedoc, tag_crate_triple);
triple_doc.expect("No triple in crate").as_str().to_string()
triple_doc.map(|s| s.as_str().to_string())
}
pub fn get_crate_id(data: &[u8]) -> CrateId {
let cratedoc = ebml::Doc::new(data);
let hashdoc = reader::get_doc(cratedoc, tag_crate_crateid);
from_str(hashdoc.as_str_slice()).unwrap()
pub fn get_crate_name(data: &[u8]) -> String {
maybe_get_crate_name(data).expect("no crate name in crate")
}
pub fn list_crate_metadata(bytes: &[u8], out: &mut io::Writer) -> io::IoResult<()> {

View file

@ -44,11 +44,9 @@ use syntax::ast_map::{PathElem, PathElems};
use syntax::ast_map;
use syntax::ast_util::*;
use syntax::ast_util;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::crateid::CrateId;
use syntax::attr::AttrMetaMethods;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::parse::token::special_idents;
use syntax::parse::token;
use syntax::visit::Visitor;
@ -1494,35 +1492,6 @@ fn encode_attributes(ebml_w: &mut Encoder, attrs: &[Attribute]) {
ebml_w.end_tag();
}
// So there's a special crate attribute called 'crate_id' which defines the
// metadata that Rust cares about for linking crates. If the user didn't
// provide it we will throw it in anyway with a default value.
fn synthesize_crate_attrs(ecx: &EncodeContext,
krate: &Crate) -> Vec<Attribute> {
fn synthesize_crateid_attr(ecx: &EncodeContext) -> Attribute {
assert!(!ecx.link_meta.crateid.name.is_empty());
attr::mk_attr_inner(attr::mk_attr_id(),
attr::mk_name_value_item_str(
InternedString::new("crate_id"),
token::intern_and_get_ident(ecx.link_meta
.crateid
.to_str()
.as_slice())))
}
let mut attrs = Vec::new();
for attr in krate.attrs.iter() {
if !attr.check_name("crate_id") {
attrs.push(*attr);
}
}
attrs.push(synthesize_crateid_attr(ecx));
attrs
}
fn encode_crate_deps(ebml_w: &mut Encoder, cstore: &cstore::CStore) {
fn get_ordered_deps(cstore: &cstore::CStore) -> Vec<decoder::CrateDep> {
// Pull the cnums and name,vers,hash out of cstore
@ -1530,8 +1499,8 @@ fn encode_crate_deps(ebml_w: &mut Encoder, cstore: &cstore::CStore) {
cstore.iter_crate_data(|key, val| {
let dep = decoder::CrateDep {
cnum: key,
crate_id: decoder::get_crate_id(val.data()),
hash: decoder::get_crate_hash(val.data())
name: decoder::get_crate_name(val.data()),
hash: decoder::get_crate_hash(val.data()),
};
deps.push(dep);
});
@ -1766,8 +1735,8 @@ fn encode_reachable_extern_fns(ecx: &EncodeContext, ebml_w: &mut Encoder) {
fn encode_crate_dep(ebml_w: &mut Encoder,
dep: decoder::CrateDep) {
ebml_w.start_tag(tag_crate_dep);
ebml_w.start_tag(tag_crate_dep_crateid);
ebml_w.writer.write(dep.crate_id.to_str().as_bytes());
ebml_w.start_tag(tag_crate_dep_crate_name);
ebml_w.writer.write(dep.name.as_bytes());
ebml_w.end_tag();
ebml_w.start_tag(tag_crate_dep_hash);
ebml_w.writer.write(dep.hash.as_str().as_bytes());
@ -1781,9 +1750,9 @@ fn encode_hash(ebml_w: &mut Encoder, hash: &Svh) {
ebml_w.end_tag();
}
fn encode_crate_id(ebml_w: &mut Encoder, crate_id: &CrateId) {
ebml_w.start_tag(tag_crate_crateid);
ebml_w.writer.write(crate_id.to_str().as_bytes());
fn encode_crate_name(ebml_w: &mut Encoder, crate_name: &str) {
ebml_w.start_tag(tag_crate_crate_name);
ebml_w.writer.write(crate_name.as_bytes());
ebml_w.end_tag();
}
@ -1880,7 +1849,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
let mut ebml_w = writer::Encoder::new(wr);
encode_crate_id(&mut ebml_w, &ecx.link_meta.crateid);
encode_crate_name(&mut ebml_w, ecx.link_meta.crate_name.as_slice());
encode_crate_triple(&mut ebml_w,
tcx.sess
.targ_cfg
@ -1891,8 +1860,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
encode_dylib_dependency_formats(&mut ebml_w, &ecx);
let mut i = ebml_w.writer.tell().unwrap();
let crate_attrs = synthesize_crate_attrs(&ecx, krate);
encode_attributes(&mut ebml_w, crate_attrs.as_slice());
encode_attributes(&mut ebml_w, krate.attrs.as_slice());
stats.attr_bytes = ebml_w.writer.tell().unwrap() - i;
i = ebml_w.writer.tell().unwrap();

View file

@ -9,6 +9,208 @@
// except according to those terms.
//! Finds crate binaries and loads their metadata
//!
//! Might I be the first to welcome you to a world of platform differences,
//! version requirements, dependency graphs, conficting desires, and fun! This
//! is the major guts (along with metadata::creader) of the compiler for loading
//! crates and resolving dependencies. Let's take a tour!
//!
//! # The problem
//!
//! Each invocation of the compiler is immediately concerned with one primary
//! problem, to connect a set of crates to resolved crates on the filesystem.
//! Concretely speaking, the compiler follows roughly these steps to get here:
//!
//! 1. Discover a set of `extern crate` statements.
//! 2. Transform these directives into crate names. If the directive does not
//! have an explicit name, then the identifier is the name.
//! 3. For each of these crate names, find a corresponding crate on the
//! filesystem.
//!
//! Sounds easy, right? Let's walk into some of the nuances.
//!
//! ## Transitive Dependencies
//!
//! Let's say we've got three crates: A, B, and C. A depends on B, and B depends
//! on C. When we're compiling A, we primarily need to find and locate B, but we
//! also end up needing to find and locate C as well.
//!
//! The reason for this is that any of B's types could be composed of C's types,
//! any function in B could return a type from C, etc. To be able to guarantee
//! that we can always typecheck/translate any function, we have to have
//! complete knowledge of the whole ecosystem, not just our immediate
//! dependencies.
//!
//! So now as part of the "find a corresponding crate on the filesystem" step
//! above, this involves also finding all crates for *all upstream
//! dependencies*. This includes all dependencies transitively.
//!
//! ## Rlibs and Dylibs
//!
//! The compiler has two forms of intermediate dependencies. These are dubbed
//! rlibs and dylibs for the static and dynamic variants, respectively. An rlib
//! is a rustc-defined file format (currently just an ar archive) while a dylib
//! is a platform-defined dynamic library. Each library has a metadata somewhere
//! inside of it.
//!
//! When translating a crate name to a crate on the filesystem, we all of a
//! sudden need to take into account both rlibs and dylibs! Linkage later on may
//! use either one of these files, as each has their pros/cons. The job of crate
//! loading is to discover what's possible by finding all candidates.
//!
//! Most parts of this loading systems keep the dylib/rlib as just separate
//! variables.
//!
//! ## Where to look?
//!
//! We can't exactly scan your whole hard drive when looking for dependencies,
//! so we need to places to look. Currently the compiler will implicitly add the
//! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation,
//! and otherwise all -L flags are added to the search paths.
//!
//! ## What criterion to select on?
//!
//! This a pretty tricky area of loading crates. Given a file, how do we know
//! whether it's the right crate? Currently, the rules look along these lines:
//!
//! 1. Does the filename match an rlib/dylib pattern? That is to say, does the
//! filename have the right prefix/suffix?
//! 2. Does the filename have the right prefix for the crate name being queried?
//! This is filtering for files like `libfoo*.rlib` and such.
//! 3. Is the file an actual rust library? This is done by loading the metadata
//! from the library and making sure it's actually there.
//! 4. Does the name in the metadata agree with the name of the library?
//! 5. Does the target in the metadata agree with the current target?
//! 6. Does the SVH match? (more on this later)
//!
//! If the file answeres `yes` to all these questions, then the file is
//! considered as being *candidate* for being accepted. It is illegal to have
//! more than two candidates as the compiler has no method by which to resolve
//! this conflict. Additionally, rlib/dylib candidates are considered
//! separately.
//!
//! After all this has happened, we have 1 or two files as candidates. These
//! represent the rlib/dylib file found for a library, and they're returned as
//! being found.
//!
//! ### What about versions?
//!
//! A lot of effort has been put forth to remove versioning from the compiler.
//! There have been forays in the past to have versioning baked in, but it was
//! largely always deemed insufficient to the point that it was recognized that
//! it's probably something the compiler shouldn't do anyway due to its
//! complicated nature and the state of the half-baked solutions.
//!
//! With a departure from versioning, the primary criterion for loading crates
//! is just the name of a crate. If we stopped here, it would imply that you
//! could never link two crates of the same name from different sources
//! together, which is clearly a bad state to be in.
//!
//! To resolve this problem, we come to the next section!
//!
//! # Expert Mode
//!
//! A number of flags have been added to the compiler to solve the "version
//! problem" in the previous section, as well as generally enabling more
//! powerful usage of the crate loading system of the compiler. The goal of
//! these flags and options are to enable third-party tools to drive the
//! compiler with prior knowledge about how the world should look.
//!
//! ## The `--extern` flag
//!
//! The compiler accepts a flag of this form a number of times:
//!
//! ```notrust
//! --extern crate-name=path/to/the/crate.rlib
//! ```
//!
//! This flag is basically the following letter to the compiler:
//!
//! > Dear rustc,
//! >
//! > When you are attempting to load the immediate dependency `crate-name`, I
//! > would like you too assume that the library is located at
//! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not
//! > assume that the path I specified has the name `crate-name`.
//!
//! This flag basically overrides most matching logic except for validating that
//! the file is indeed a rust library. The same `crate-name` can be specified
//! twice to specify the rlib/dylib pair.
//!
//! ## Enabling "multiple versions"
//!
//! This basically boils down to the ability to specify arbitrary packages to
//! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it
//! would look something like:
//!
//! ```ignore
//! extern crate b1;
//! extern crate b2;
//!
//! fn main() {}
//! ```
//!
//! and the compiler would be invoked as:
//!
//! ```notrust
//! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib
//! ```
//!
//! In this scenario there are two crates named `b` and the compiler must be
//! manually driven to be informed where each crate is.
//!
//! ## Frobbing symbols
//!
//! One of the immediate problems with linking the same library together twice
//! in the same problem is dealing with duplicate symbols. The primary way to
//! deal with this in rustc is to add hashes to the end of each symbol.
//!
//! In order to force hashes to change between versions of a library, if
//! desired, the compiler exposes an option `-C metadata=foo`, which is used to
//! initially seed each symbol hash. The string `foo` is prepended to each
//! string-to-hash to ensure that symbols change over time.
//!
//! ## Loading transitive dependencies
//!
//! Dealing with same-named-but-distinct crates is not just a local problem, but
//! one that also needs to be dealt with for transitive dependences. Note that
//! in the letter above `--extern` flags only apply to the *local* set of
//! dependencies, not the upstream transitive dependencies. Consider this
//! dependency graph:
//!
//! ```notrust
//! A.1 A.2
//! | |
//! | |
//! B C
//! \ /
//! \ /
//! D
//! ```
//!
//! In this scenario, when we compile `D`, we need to be able to distinctly
//! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these
//! transitive dependencies.
//!
//! Note that the key idea here is that `B` and `C` are both *already compiled*.
//! That is, they have already resolved their dependencies. Due to unrelated
//! technical reasons, when a library is compiled, it is only compatible with
//! the *exact same* version of the upstream libraries it was compiled against.
//! We use the "Strict Version Hash" to identify the exact copy of an upstream
//! library.
//!
//! With this knowledge, we know that `B` and `C` will depend on `A` with
//! different SVH values, so we crawl the normal `-L` paths looking for
//! `liba*.rlib` and filter based on the contained SVH.
//!
//! In the end, this ends up not needing `--extern` to specify upstream
//! transitive dependencies.
//!
//! # Wrapping up
//!
//! That's the general overview of loading crates in the compiler, but it's by
//! 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::{ArchiveRO, METADATA_FILENAME};
use back::svh::Svh;
@ -21,8 +223,6 @@ use metadata::filesearch::{FileSearch, FileMatches, FileDoesntMatch};
use syntax::abi;
use syntax::codemap::Span;
use syntax::diagnostic::SpanHandler;
use syntax::crateid::CrateId;
use syntax::attr::AttrMetaMethods;
use util::fs;
use std::c_str::ToCStr;
@ -61,8 +261,7 @@ pub struct Context<'a> {
pub sess: &'a Session,
pub span: Span,
pub ident: &'a str,
pub crate_id: &'a CrateId,
pub id_hash: &'a str,
pub crate_name: &'a str,
pub hash: Option<&'a Svh>,
pub triple: &'a str,
pub os: abi::Os,
@ -70,6 +269,7 @@ pub struct Context<'a> {
pub root: &'a Option<CratePaths>,
pub rejected_via_hash: Vec<CrateMismatch>,
pub rejected_via_triple: Vec<CrateMismatch>,
pub should_match_name: bool,
}
pub struct Library {
@ -167,19 +367,30 @@ impl<'a> Context<'a> {
}
fn find_library_crate(&mut self) -> Option<Library> {
// If an SVH is specified, then this is a transitive dependency that
// must be loaded via -L plus some filtering.
if self.hash.is_none() {
self.should_match_name = false;
match self.find_commandline_library() {
Some(l) => return Some(l),
None => {}
}
self.should_match_name = true;
}
let dypair = self.dylibname();
// want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
let dylib_prefix = dypair.map(|(prefix, _)| {
format!("{}{}-", prefix, self.crate_id.name)
format!("{}{}", prefix, self.crate_name)
});
let rlib_prefix = format!("lib{}-", self.crate_id.name);
let rlib_prefix = format!("lib{}", self.crate_name);
let mut candidates = HashMap::new();
// First, find all possible candidate rlibs and dylibs purely based on
// the name of the files themselves. We're trying to match against an
// exact crate_id and a possibly an exact hash.
// exact crate name and a possibly an exact hash.
//
// During this step, we can filter all found libraries based on the
// name and id found in the crate id (we ignore the path portion for
@ -195,49 +406,32 @@ impl<'a> Context<'a> {
None => return FileDoesntMatch,
Some(file) => file,
};
if file.starts_with(rlib_prefix.as_slice()) &&
let (hash, rlib) = if file.starts_with(rlib_prefix.as_slice()) &&
file.ends_with(".rlib") {
info!("rlib candidate: {}", path.display());
match self.try_match(file, rlib_prefix.as_slice(), ".rlib") {
Some(hash) => {
info!("rlib accepted, hash: {}", hash);
let slot = candidates.find_or_insert_with(hash, |_| {
(HashSet::new(), HashSet::new())
});
let (ref mut rlibs, _) = *slot;
rlibs.insert(fs::realpath(path).unwrap());
FileMatches
}
None => {
info!("rlib rejected");
FileDoesntMatch
}
}
(file.slice(rlib_prefix.len(), file.len() - ".rlib".len()),
true)
} else if dypair.map_or(false, |(_, suffix)| {
file.starts_with(dylib_prefix.get_ref().as_slice()) &&
file.ends_with(suffix)
}) {
let (_, suffix) = dypair.unwrap();
let dylib_prefix = dylib_prefix.get_ref().as_slice();
info!("dylib candidate: {}", path.display());
match self.try_match(file, dylib_prefix, suffix) {
Some(hash) => {
info!("dylib accepted, hash: {}", hash);
let slot = candidates.find_or_insert_with(hash, |_| {
(HashSet::new(), HashSet::new())
});
let (_, ref mut dylibs) = *slot;
dylibs.insert(fs::realpath(path).unwrap());
FileMatches
}
None => {
info!("dylib rejected");
FileDoesntMatch
}
}
(file.slice(dylib_prefix.len(), file.len() - suffix.len()),
false)
} else {
FileDoesntMatch
return FileDoesntMatch
};
info!("lib candidate: {}", path.display());
let slot = candidates.find_or_insert_with(hash.to_string(), |_| {
(HashSet::new(), HashSet::new())
});
let (ref mut rlibs, ref mut dylibs) = *slot;
if rlib {
rlibs.insert(fs::realpath(path).unwrap());
} else {
dylibs.insert(fs::realpath(path).unwrap());
}
FileMatches
});
// We have now collected all known libraries into a set of candidates
@ -274,7 +468,7 @@ impl<'a> Context<'a> {
_ => {
self.sess.span_err(self.span,
format!("multiple matching crates for `{}`",
self.crate_id.name).as_slice());
self.crate_name).as_slice());
self.sess.note("candidates:");
for lib in libraries.iter() {
match lib.dylib {
@ -292,50 +486,14 @@ impl<'a> Context<'a> {
None => {}
}
let data = lib.metadata.as_slice();
let crate_id = decoder::get_crate_id(data);
note_crateid_attr(self.sess.diagnostic(), &crate_id);
let name = decoder::get_crate_name(data);
note_crate_name(self.sess.diagnostic(), name.as_slice());
}
None
}
}
}
// Attempts to match the requested version of a library against the file
// specified. The prefix/suffix are specified (disambiguates between
// rlib/dylib).
//
// The return value is `None` if `file` doesn't look like a rust-generated
// library, or if a specific version was requested and it doesn't match the
// apparent file's version.
//
// If everything checks out, then `Some(hash)` is returned where `hash` is
// the listed hash in the filename itself.
fn try_match(&self, file: &str, prefix: &str, suffix: &str) -> Option<String>{
let middle = file.slice(prefix.len(), file.len() - suffix.len());
debug!("matching -- {}, middle: {}", file, middle);
let mut parts = middle.splitn('-', 1);
let hash = match parts.next() { Some(h) => h, None => return None };
debug!("matching -- {}, hash: {} (want {})", file, hash, self.id_hash);
let vers = match parts.next() { Some(v) => v, None => return None };
debug!("matching -- {}, vers: {} (want {})", file, vers,
self.crate_id.version);
match self.crate_id.version {
Some(ref version) if version.as_slice() != vers => return None,
Some(..) => {} // check the hash
// hash is irrelevant, no version specified
None => return Some(hash.to_string())
}
debug!("matching -- {}, vers ok", file);
// hashes in filenames are prefixes of the "true hash"
if self.id_hash == hash.as_slice() {
debug!("matching -- {}, hash ok", file);
Some(hash.to_string())
} else {
None
}
}
// Attempts to extract *one* library from the set `m`. If the set has no
// elements, `None` is returned. If the set has more than one element, then
// the errors and notes are emitted about the set of libraries.
@ -382,7 +540,7 @@ impl<'a> Context<'a> {
format!("multiple {} candidates for `{}` \
found",
flavor,
self.crate_id.name).as_slice());
self.crate_name).as_slice());
self.sess.span_note(self.span,
format!(r"candidate #1: {}",
ret.get_ref()
@ -404,9 +562,11 @@ impl<'a> Context<'a> {
}
fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool {
match decoder::maybe_get_crate_id(crate_data) {
Some(ref id) if self.crate_id.matches(id) => {}
_ => { info!("Rejecting via crate_id"); return false }
if self.should_match_name {
match decoder::maybe_get_crate_name(crate_data) {
Some(ref name) if self.crate_name == name.as_slice() => {}
_ => { info!("Rejecting via crate name"); return false }
}
}
let hash = match decoder::maybe_get_crate_hash(crate_data) {
Some(hash) => hash, None => {
@ -415,7 +575,10 @@ impl<'a> Context<'a> {
}
};
let triple = decoder::get_crate_triple(crate_data);
let triple = match decoder::get_crate_triple(crate_data) {
None => { debug!("triple not present"); return false }
Some(t) => t,
};
if triple.as_slice() != self.triple {
info!("Rejecting via crate triple: expected {} got {}", self.triple, triple);
self.rejected_via_triple.push(CrateMismatch {
@ -456,10 +619,72 @@ impl<'a> Context<'a> {
}
}
fn find_commandline_library(&mut self) -> Option<Library> {
let locs = match self.sess.opts.externs.find_equiv(&self.crate_name) {
Some(s) => s,
None => return None,
};
// First, filter out all libraries that look suspicious. We only accept
// files which actually exist that have the correct naming scheme for
// rlibs/dylibs.
let sess = self.sess;
let dylibname = self.dylibname();
let mut locs = locs.iter().map(|l| Path::new(l.as_slice())).filter(|loc| {
if !loc.exists() {
sess.err(format!("extern location does not exist: {}",
loc.display()).as_slice());
return false;
}
let file = loc.filename_str().unwrap();
if file.starts_with("lib") && file.ends_with(".rlib") {
return true
} else {
match dylibname {
Some((prefix, suffix)) => {
if file.starts_with(prefix) && file.ends_with(suffix) {
return true
}
}
None => {}
}
}
sess.err(format!("extern location is of an unknown type: {}",
loc.display()).as_slice());
false
});
// Now that we have an itertor of good candidates, make sure there's at
// most one rlib and at most one dylib.
let mut rlibs = HashSet::new();
let mut dylibs = HashSet::new();
for loc in locs {
if loc.filename_str().unwrap().ends_with(".rlib") {
rlibs.insert(loc.clone());
} else {
dylibs.insert(loc.clone());
}
}
// Extract the rlib/dylib pair.
let mut metadata = None;
let rlib = self.extract_one(rlibs, "rlib", &mut metadata);
let dylib = self.extract_one(dylibs, "dylib", &mut metadata);
if rlib.is_none() && dylib.is_none() { return None }
match metadata {
Some(metadata) => Some(Library {
dylib: dylib,
rlib: rlib,
metadata: metadata,
}),
None => None,
}
}
}
pub fn note_crateid_attr(diag: &SpanHandler, crateid: &CrateId) {
diag.handler().note(format!("crate_id: {}", crateid.to_str()).as_slice());
pub fn note_crate_name(diag: &SpanHandler, name: &str) {
diag.handler().note(format!("crate name: {}", name).as_slice());
}
impl ArchiveMetadata {

View file

@ -1373,15 +1373,15 @@ pub fn process_crate(sess: &Session,
return;
}
let (cratename, crateid) = match attr::find_crateid(krate.attrs.as_slice()) {
Some(crateid) => (crateid.name.clone(), crateid.to_str()),
let cratename = match attr::find_crate_name(krate.attrs.as_slice()) {
Some(name) => name.get().to_string(),
None => {
info!("Could not find crate name, using 'unknown_crate'");
(String::from_str("unknown_crate"),"unknown_crate".to_owned())
String::from_str("unknown_crate")
},
};
info!("Dumping crate {} ({})", cratename, crateid);
info!("Dumping crate {}", cratename);
// find a path to dump our data to
let mut root_path = match os::getenv("DXR_RUST_TEMP_FOLDER") {

View file

@ -30,7 +30,6 @@ use back::{link, abi};
use driver::config;
use driver::config::{NoDebugInfo, FullDebugInfo};
use driver::session::Session;
use driver::driver::OutputFilenames;
use driver::driver::{CrateAnalysis, CrateTranslation};
use lib::llvm::{ModuleRef, ValueRef, BasicBlockRef};
use lib::llvm::{llvm, Vector};
@ -2270,8 +2269,9 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
}.as_slice());
let llmeta = C_bytes(cx, compressed.as_slice());
let llconst = C_struct(cx, [llmeta], false);
let name = format!("rust_metadata_{}_{}_{}", cx.link_meta.crateid.name,
cx.link_meta.crateid.version_or_default(), cx.link_meta.crate_hash);
let name = format!("rust_metadata_{}_{}",
cx.link_meta.crate_name,
cx.link_meta.crate_hash);
let llglobal = name.with_c_str(|buf| {
unsafe {
llvm::LLVMAddGlobal(cx.metadata_llmod, val_ty(llconst).to_ref(), buf)
@ -2288,9 +2288,8 @@ pub fn write_metadata(cx: &CrateContext, krate: &ast::Crate) -> Vec<u8> {
}
pub fn trans_crate(krate: ast::Crate,
analysis: CrateAnalysis,
output: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, .. } = analysis;
analysis: CrateAnalysis) -> (ty::ctxt, CrateTranslation) {
let CrateAnalysis { ty_cx: tcx, exp_map2, reachable, name, .. } = analysis;
// Before we touch LLVM, make sure that multithreading is enabled.
unsafe {
@ -2310,8 +2309,7 @@ pub fn trans_crate(krate: ast::Crate,
}
}
let link_meta = link::build_link_meta(&krate,
output.out_filestem.as_slice());
let link_meta = link::build_link_meta(&tcx.sess, &krate, name);
// Append ".rs" to crate name as LLVM module identifier.
//
@ -2321,7 +2319,7 @@ pub fn trans_crate(krate: ast::Crate,
// crashes if the module identifier is same as other symbols
// such as a function name in the module.
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
let mut llmod_id = link_meta.crateid.name.clone();
let mut llmod_id = link_meta.crate_name.clone();
llmod_id.push_str(".rs");
let ccx = CrateContext::new(llmod_id.as_slice(), tcx, exp_map2,

View file

@ -1496,7 +1496,7 @@ fn compile_unit_metadata(cx: &CrateContext) {
});
fn fallback_path(cx: &CrateContext) -> CString {
cx.link_meta.crateid.name.as_slice().to_c_str()
cx.link_meta.crate_name.as_slice().to_c_str()
}
}
@ -3972,7 +3972,7 @@ impl NamespaceTreeNode {
}
fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str {
cx.link_meta.crateid.name.as_slice()
cx.link_meta.crate_name.as_slice()
}
fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc<NamespaceTreeNode> {

View file

@ -566,7 +566,7 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: &CrateContext,
let ps = ccx.tcx.map.with_path(id, |path| {
let abi = Some(ast_map::PathName(special_idents::clownshoe_abi.name));
link::mangle(path.chain(abi.move_iter()), None, None)
link::mangle(path.chain(abi.move_iter()), None)
});
// Compute the type that the function would have if it were just a

View file

@ -129,9 +129,7 @@ pub fn monomorphic_fn(ccx: &CrateContext,
hash_id.hash(&mut state);
mono_ty.hash(&mut state);
exported_name(path,
format!("h{}", state.result()).as_slice(),
ccx.link_meta.crateid.version_or_default())
exported_name(path, format!("h{}", state.result()).as_slice())
});
debug!("monomorphize_fn mangled to {}", s);

View file

@ -37,7 +37,6 @@ use syntax::codemap;
use syntax::codemap::{Span, CodeMap, DUMMY_SP};
use syntax::diagnostic::{Level, RenderSpan, Bug, Fatal, Error, Warning, Note};
use syntax::ast;
use syntax::crateid::CrateId;
use util::ppaux::{ty_to_str, UserString};
struct Env<'a> {
@ -116,11 +115,8 @@ fn test_env(_test_name: &str,
let krate_config = Vec::new();
let input = driver::StrInput(source_string.to_owned());
let krate = driver::phase_1_parse_input(&sess, krate_config, &input);
let krate_id = CrateId { path: "test".to_owned(),
name: "test".to_owned(),
version: None };
let (krate, ast_map) =
driver::phase_2_configure_and_expand(&sess, krate, &krate_id)
driver::phase_2_configure_and_expand(&sess, krate, "test")
.expect("phase 2 aborted");
// run just enough stuff to build a tcx: