diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index 43687a31453d0..b07b58e56b5d5 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -20,6 +20,7 @@ use back::write; use back::target_strs; use back::{arm, x86, x86_64, mips, mipsel}; use lint; +use metadata::cstore; use syntax::abi; use syntax::ast; @@ -78,6 +79,7 @@ pub struct Options { // parsed code. It remains mutable in case its replacements wants to use // this. pub addl_lib_search_paths: RefCell>, + pub libs: Vec<(String, cstore::NativeLibaryKind)>, pub maybe_sysroot: Option, pub target_triple: String, // User-specified cfg meta items. The compiler itself will add additional @@ -130,6 +132,7 @@ pub fn basic_options() -> Options { externs: HashMap::new(), crate_name: None, alt_std_name: None, + libs: Vec::new(), } } @@ -575,6 +578,10 @@ pub fn optgroups() -> Vec { optflag("h", "help", "Display this message"), optmulti("", "cfg", "Configure the compilation environment", "SPEC"), optmulti("L", "", "Add a directory to the library search path", "PATH"), + optmulti("l", "", "Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of, + static, dylib, or framework. If omitted, dylib is + assumed.", "NAME[:KIND]"), optmulti("", "crate-type", "Comma separated list of types of crates for the compiler to emit", "[bin|lib|rlib|dylib|staticlib]"), @@ -767,6 +774,23 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { Path::new(s.as_slice()) }).collect(); + let libs = matches.opt_strs("l").into_iter().map(|s| { + let mut parts = s.as_slice().rsplitn(1, ':'); + let kind = parts.next().unwrap(); + let (name, kind) = match (parts.next(), kind) { + (None, name) | + (Some(name), "dylib") => (name, cstore::NativeUnknown), + (Some(name), "framework") => (name, cstore::NativeFramework), + (Some(name), "static") => (name, cstore::NativeStatic), + (_, s) => { + early_error(format!("unknown library kind `{}`, expected \ + one of dylib, framework, or static", + s).as_slice()); + } + }; + (name.to_string(), kind) + }).collect(); + let cfg = parse_cfgspecs(matches.opt_strs("cfg")); let test = matches.opt_present("test"); let write_dependency_info = (matches.opt_present("dep-info"), @@ -843,7 +867,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { color: color, externs: externs, crate_name: crate_name, - alt_std_name: None + alt_std_name: None, + libs: libs, } } diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 43d0156d72770..100ccf78f3e81 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -52,7 +52,11 @@ pub fn read_crates(sess: &Session, visit_crate(&e, krate); visit::walk_crate(&mut e, krate); dump_crates(&sess.cstore); - warn_if_multiple_versions(sess.diagnostic(), &sess.cstore) + warn_if_multiple_versions(sess.diagnostic(), &sess.cstore); + + for &(ref name, kind) in sess.opts.libs.iter() { + register_native_lib(sess, None, name.clone(), kind); + } } impl<'a, 'v> visit::Visitor<'v> for Env<'a> { @@ -233,14 +237,7 @@ fn visit_item(e: &Env, i: &ast::Item) { Some(k) => { if k.equiv(&("static")) { cstore::NativeStatic - } else if (e.sess.targ_cfg.os == abi::OsMacos || - e.sess.targ_cfg.os == abi::OsiOS) && - k.equiv(&("framework")) { - cstore::NativeFramework } else if k.equiv(&("framework")) { - e.sess.span_err(m.span, - "native frameworks are only available \ - on OSX targets"); cstore::NativeUnknown } else { e.sess.span_err(m.span, @@ -263,15 +260,8 @@ fn visit_item(e: &Env, i: &ast::Item) { InternedString::new("foo") } }; - if n.get().is_empty() { - e.sess.span_err(m.span, - "#[link(name = \"\")] given with \ - empty name"); - } else { - e.sess - .cstore - .add_used_library(n.get().to_string(), kind); - } + register_native_lib(e.sess, Some(m.span), + n.get().to_string(), kind); } None => {} } @@ -281,6 +271,32 @@ fn visit_item(e: &Env, i: &ast::Item) { } } +fn register_native_lib(sess: &Session, span: Option, name: String, + kind: cstore::NativeLibaryKind) { + if name.as_slice().is_empty() { + match span { + Some(span) => { + sess.span_err(span, "#[link(name = \"\")] given with \ + empty name"); + } + None => { + sess.err("empty library name given via `-l`"); + } + } + return + } + let is_osx = sess.targ_cfg.os == abi::OsMacos || + sess.targ_cfg.os == abi::OsiOS; + if kind == cstore::NativeFramework && !is_osx { + let msg = "native frameworks are only available on OSX targets"; + match span { + Some(span) => sess.span_err(span, msg), + None => sess.err(msg), + } + } + sess.cstore.add_used_library(name, kind); +} + fn existing_match(e: &Env, name: &str, hash: Option<&Svh>) -> Option { let mut ret = None; diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index 1d1012d9e4f04..e8c5f6f4910e0 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -50,7 +50,7 @@ pub enum LinkagePreference { RequireStatic, } -#[deriving(PartialEq, FromPrimitive)] +#[deriving(PartialEq, FromPrimitive, Clone)] pub enum NativeLibaryKind { NativeStatic, // native static library (.a archive) NativeFramework, // OSX-specific diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a8326e79ef368..d474cc8cf0a3e 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -675,26 +675,19 @@ pub fn check_zero_tts(cx: &ExtCtxt, /// Extract the string literal from the first token of `tts`. If this /// is not a string literal, emit an error and return None. -pub fn get_single_str_from_tts(cx: &ExtCtxt, +pub fn get_single_str_from_tts(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree], name: &str) -> Option { - if tts.len() != 1 { - cx.span_err(sp, format!("{} takes 1 argument.", name).as_slice()); - } else { - match tts[0] { - ast::TtToken(_, token::LitStr(ident)) => return Some(parse::str_lit(ident.as_str())), - ast::TtToken(_, token::LitStrRaw(ident, _)) => { - return Some(parse::raw_str_lit(ident.as_str())) - } - _ => { - cx.span_err(sp, - format!("{} requires a string.", name).as_slice()) - } - } + let mut p = cx.new_parser_from_tts(tts); + let ret = cx.expander().fold_expr(p.parse_expr()); + if p.token != token::Eof { + cx.span_err(sp, format!("{} takes 1 argument", name).as_slice()); } - None + expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| { + s.get().to_string() + }) } /// Extract comma-separated expressions from `tts`. If there is a diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 41967b0680cff..f192308440998 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -9,14 +9,16 @@ // except according to those terms. use ast; -use codemap; use codemap::{Pos, Span}; +use codemap; use ext::base::*; use ext::base; use ext::build::AstBuilder; -use parse; use parse::token; +use parse; use print::pprust; +use ptr::P; +use util::small_vector::SmallVector; use std::io::File; use std::rc::Rc; @@ -82,14 +84,14 @@ pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) /// include! : parse the given file as an expr /// This is generally a bad idea because it's going to behave /// unhygienically. -pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) - -> Box { +pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box { let file = match get_single_str_from_tts(cx, sp, tts, "include!") { Some(f) => f, None => return DummyResult::expr(sp), }; // The file will be added to the code map by the parser - let mut p = + let p = parse::new_sub_parser_from_file(cx.parse_sess(), cx.cfg(), &res_rel_file(cx, @@ -98,7 +100,28 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) true, None, sp); - base::MacExpr::new(p.parse_expr()) + + struct ExpandResult<'a> { + p: parse::parser::Parser<'a>, + } + impl<'a> base::MacResult for ExpandResult<'a> { + fn make_expr(mut self: Box>) -> Option> { + Some(self.p.parse_expr()) + } + fn make_items(mut self: Box>) + -> Option>> { + let mut ret = SmallVector::zero(); + loop { + match self.p.parse_item_with_outer_attributes() { + Some(item) => ret.push(item), + None => break + } + } + Some(ret) + } + } + + box ExpandResult { p: p } } // include_str! : read the given file, insert it as a literal string expr diff --git a/src/test/auxiliary/macro-include-items-expr.rs b/src/test/auxiliary/macro-include-items-expr.rs new file mode 100644 index 0000000000000..aea3c749930b6 --- /dev/null +++ b/src/test/auxiliary/macro-include-items-expr.rs @@ -0,0 +1,13 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test: this is not a test + +1 diff --git a/src/test/auxiliary/macro-include-items-item.rs b/src/test/auxiliary/macro-include-items-item.rs new file mode 100644 index 0000000000000..da72aaef80591 --- /dev/null +++ b/src/test/auxiliary/macro-include-items-item.rs @@ -0,0 +1,13 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test: this is not a test + +fn foo() { bar() } diff --git a/src/test/compile-fail/manual-link-bad-form.rs b/src/test/compile-fail/manual-link-bad-form.rs new file mode 100644 index 0000000000000..bd2a3eba0b5b8 --- /dev/null +++ b/src/test/compile-fail/manual-link-bad-form.rs @@ -0,0 +1,17 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-l :static +// error-pattern: empty library name given via `-l` + +fn main() { +} + + diff --git a/src/test/compile-fail/manual-link-bad-kind.rs b/src/test/compile-fail/manual-link-bad-kind.rs new file mode 100644 index 0000000000000..4614440ddafd5 --- /dev/null +++ b/src/test/compile-fail/manual-link-bad-kind.rs @@ -0,0 +1,16 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-l foo:bar +// error-pattern: unknown library kind `bar`, expected one of dylib, framework, or static + +fn main() { +} + diff --git a/src/test/compile-fail/manual-link-framework.rs b/src/test/compile-fail/manual-link-framework.rs new file mode 100644 index 0000000000000..96cc35049ee40 --- /dev/null +++ b/src/test/compile-fail/manual-link-framework.rs @@ -0,0 +1,17 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-macos +// ignore-ios +// compile-flags:-l foo:framework +// error-pattern: native frameworks are only available on OSX targets + +fn main() { +} diff --git a/src/test/run-make/manual-link/Makefile b/src/test/run-make/manual-link/Makefile new file mode 100644 index 0000000000000..d2a02adc9d4a4 --- /dev/null +++ b/src/test/run-make/manual-link/Makefile @@ -0,0 +1,7 @@ +-include ../tools.mk + +all: $(TMPDIR)/libbar.a + $(RUSTC) foo.rs -lbar:static + $(RUSTC) main.rs + $(call RUN,main) + diff --git a/src/test/run-make/manual-link/bar.c b/src/test/run-make/manual-link/bar.c new file mode 100644 index 0000000000000..e42599986781f --- /dev/null +++ b/src/test/run-make/manual-link/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/test/run-make/manual-link/foo.c b/src/test/run-make/manual-link/foo.c new file mode 100644 index 0000000000000..e42599986781f --- /dev/null +++ b/src/test/run-make/manual-link/foo.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/test/run-make/manual-link/foo.rs b/src/test/run-make/manual-link/foo.rs new file mode 100644 index 0000000000000..d67a4057afbfc --- /dev/null +++ b/src/test/run-make/manual-link/foo.rs @@ -0,0 +1,19 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rlib"] + +extern { + fn bar(); +} + +pub fn foo() { + unsafe { bar(); } +} diff --git a/src/test/run-make/manual-link/main.rs b/src/test/run-make/manual-link/main.rs new file mode 100644 index 0000000000000..756a47f386ae3 --- /dev/null +++ b/src/test/run-make/manual-link/main.rs @@ -0,0 +1,15 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate foo; + +fn main() { + foo::foo(); +} diff --git a/src/test/run-pass/macro-include-items.rs b/src/test/run-pass/macro-include-items.rs new file mode 100644 index 0000000000000..03eec668edde8 --- /dev/null +++ b/src/test/run-pass/macro-include-items.rs @@ -0,0 +1,18 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn bar() {} + +include!(concat!("", "", "../auxiliary/", "macro-include-items-item.rs")) + +fn main() { + foo(); + assert_eq!(include!(concat!("", "../auxiliary/", "macro-include-items-expr.rs")), 1u); +}