Skip to content

Commit 8e6e846

Browse files
committed
rustc: Implement -l and include! tweaks
This is an implementation of the rustc bits of [RFC 403][rfc]. This adds a new flag to the compiler, `-l`, as well as tweaking the `include!` macro (and related source-centric macros). The compiler's new `-l` flag is used to link libraries in from the command line. This flag stacks with `#[link]` directives already found in the program. The purpose of this flag, also stated in the RFC, is to ease linking against native libraries which have wildly different requirements across platforms and even within distributions of one platform. This flag accepts a string of the form `NAME[:KIND]` where `KIND` is optional or one of dylib, static, or framework. This is roughly equivalent to if the equivalent `#[link]` directive were just written in the program. The `include!` macro has been modified to recursively expand macros to allow usage of `concat!` as an argument, for example. The use case spelled out in RFC 403 was for `env!` to be used as well to include compile-time generated files. The macro also received a bit of tweaking to allow it to expand to either an expression or a series of items, depending on what context it's used in. [rfc]: rust-lang/rfcs#403
1 parent fd53657 commit 8e6e846

16 files changed

+234
-40
lines changed

src/librustc/driver/config.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use back::write;
2020
use back::target_strs;
2121
use back::{arm, x86, x86_64, mips, mipsel};
2222
use lint;
23+
use metadata::cstore;
2324

2425
use syntax::abi;
2526
use syntax::ast;
@@ -78,6 +79,7 @@ pub struct Options {
7879
// parsed code. It remains mutable in case its replacements wants to use
7980
// this.
8081
pub addl_lib_search_paths: RefCell<Vec<Path>>,
82+
pub libs: Vec<(String, cstore::NativeLibaryKind)>,
8183
pub maybe_sysroot: Option<Path>,
8284
pub target_triple: String,
8385
// User-specified cfg meta items. The compiler itself will add additional
@@ -130,6 +132,7 @@ pub fn basic_options() -> Options {
130132
externs: HashMap::new(),
131133
crate_name: None,
132134
alt_std_name: None,
135+
libs: Vec::new(),
133136
}
134137
}
135138

@@ -575,6 +578,10 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
575578
optflag("h", "help", "Display this message"),
576579
optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
577580
optmulti("L", "", "Add a directory to the library search path", "PATH"),
581+
optmulti("l", "", "Link the generated crate(s) to the specified native
582+
library NAME. The optional KIND can be one of,
583+
static, dylib, or framework. If omitted, dylib is
584+
assumed.", "NAME[:KIND]"),
578585
optmulti("", "crate-type", "Comma separated list of types of crates
579586
for the compiler to emit",
580587
"[bin|lib|rlib|dylib|staticlib]"),
@@ -767,6 +774,23 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
767774
Path::new(s.as_slice())
768775
}).collect();
769776

777+
let libs = matches.opt_strs("l").into_iter().map(|s| {
778+
let mut parts = s.as_slice().rsplitn(1, ':');
779+
let kind = parts.next().unwrap();
780+
let (name, kind) = match (parts.next(), kind) {
781+
(None, name) |
782+
(Some(name), "dylib") => (name, cstore::NativeUnknown),
783+
(Some(name), "framework") => (name, cstore::NativeFramework),
784+
(Some(name), "static") => (name, cstore::NativeStatic),
785+
(_, s) => {
786+
early_error(format!("unknown library kind `{}`, expected \
787+
one of dylib, framework, or static",
788+
s).as_slice());
789+
}
790+
};
791+
(name.to_string(), kind)
792+
}).collect();
793+
770794
let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
771795
let test = matches.opt_present("test");
772796
let write_dependency_info = (matches.opt_present("dep-info"),
@@ -843,7 +867,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
843867
color: color,
844868
externs: externs,
845869
crate_name: crate_name,
846-
alt_std_name: None
870+
alt_std_name: None,
871+
libs: libs,
847872
}
848873
}
849874

src/librustc/metadata/creader.rs

+33-17
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ pub fn read_crates(sess: &Session,
5252
visit_crate(&e, krate);
5353
visit::walk_crate(&mut e, krate);
5454
dump_crates(&sess.cstore);
55-
warn_if_multiple_versions(sess.diagnostic(), &sess.cstore)
55+
warn_if_multiple_versions(sess.diagnostic(), &sess.cstore);
56+
57+
for &(ref name, kind) in sess.opts.libs.iter() {
58+
register_native_lib(sess, None, name.clone(), kind);
59+
}
5660
}
5761

5862
impl<'a, 'v> visit::Visitor<'v> for Env<'a> {
@@ -233,14 +237,7 @@ fn visit_item(e: &Env, i: &ast::Item) {
233237
Some(k) => {
234238
if k.equiv(&("static")) {
235239
cstore::NativeStatic
236-
} else if (e.sess.targ_cfg.os == abi::OsMacos ||
237-
e.sess.targ_cfg.os == abi::OsiOS) &&
238-
k.equiv(&("framework")) {
239-
cstore::NativeFramework
240240
} else if k.equiv(&("framework")) {
241-
e.sess.span_err(m.span,
242-
"native frameworks are only available \
243-
on OSX targets");
244241
cstore::NativeUnknown
245242
} else {
246243
e.sess.span_err(m.span,
@@ -263,15 +260,8 @@ fn visit_item(e: &Env, i: &ast::Item) {
263260
InternedString::new("foo")
264261
}
265262
};
266-
if n.get().is_empty() {
267-
e.sess.span_err(m.span,
268-
"#[link(name = \"\")] given with \
269-
empty name");
270-
} else {
271-
e.sess
272-
.cstore
273-
.add_used_library(n.get().to_string(), kind);
274-
}
263+
register_native_lib(e.sess, Some(m.span),
264+
n.get().to_string(), kind);
275265
}
276266
None => {}
277267
}
@@ -281,6 +271,32 @@ fn visit_item(e: &Env, i: &ast::Item) {
281271
}
282272
}
283273

274+
fn register_native_lib(sess: &Session, span: Option<Span>, name: String,
275+
kind: cstore::NativeLibaryKind) {
276+
if name.as_slice().is_empty() {
277+
match span {
278+
Some(span) => {
279+
sess.span_err(span, "#[link(name = \"\")] given with \
280+
empty name");
281+
}
282+
None => {
283+
sess.err("empty library name given via `-l`");
284+
}
285+
}
286+
return
287+
}
288+
let is_osx = sess.targ_cfg.os == abi::OsMacos ||
289+
sess.targ_cfg.os == abi::OsiOS;
290+
if kind == cstore::NativeFramework && !is_osx {
291+
let msg = "native frameworks are only available on OSX targets";
292+
match span {
293+
Some(span) => sess.span_err(span, msg),
294+
None => sess.err(msg),
295+
}
296+
}
297+
sess.cstore.add_used_library(name, kind);
298+
}
299+
284300
fn existing_match(e: &Env, name: &str,
285301
hash: Option<&Svh>) -> Option<ast::CrateNum> {
286302
let mut ret = None;

src/librustc/metadata/cstore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub enum LinkagePreference {
5050
RequireStatic,
5151
}
5252

53-
#[deriving(PartialEq, FromPrimitive)]
53+
#[deriving(PartialEq, FromPrimitive, Clone)]
5454
pub enum NativeLibaryKind {
5555
NativeStatic, // native static library (.a archive)
5656
NativeFramework, // OSX-specific

src/libsyntax/ext/base.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -675,26 +675,19 @@ pub fn check_zero_tts(cx: &ExtCtxt,
675675

676676
/// Extract the string literal from the first token of `tts`. If this
677677
/// is not a string literal, emit an error and return None.
678-
pub fn get_single_str_from_tts(cx: &ExtCtxt,
678+
pub fn get_single_str_from_tts(cx: &mut ExtCtxt,
679679
sp: Span,
680680
tts: &[ast::TokenTree],
681681
name: &str)
682682
-> Option<String> {
683-
if tts.len() != 1 {
684-
cx.span_err(sp, format!("{} takes 1 argument.", name).as_slice());
685-
} else {
686-
match tts[0] {
687-
ast::TtToken(_, token::LitStr(ident)) => return Some(parse::str_lit(ident.as_str())),
688-
ast::TtToken(_, token::LitStrRaw(ident, _)) => {
689-
return Some(parse::raw_str_lit(ident.as_str()))
690-
}
691-
_ => {
692-
cx.span_err(sp,
693-
format!("{} requires a string.", name).as_slice())
694-
}
695-
}
683+
let mut p = cx.new_parser_from_tts(tts);
684+
let ret = cx.expander().fold_expr(p.parse_expr());
685+
if p.token != token::Eof {
686+
cx.span_err(sp, format!("{} takes 1 argument", name).as_slice());
696687
}
697-
None
688+
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| {
689+
s.get().to_string()
690+
})
698691
}
699692

700693
/// Extract comma-separated expressions from `tts`. If there is a

src/libsyntax/ext/source_util.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
// except according to those terms.
1010

1111
use ast;
12-
use codemap;
1312
use codemap::{Pos, Span};
13+
use codemap;
1414
use ext::base::*;
1515
use ext::base;
1616
use ext::build::AstBuilder;
17-
use parse;
1817
use parse::token;
18+
use parse;
1919
use print::pprust;
20+
use ptr::P;
21+
use util::small_vector::SmallVector;
2022

2123
use std::io::File;
2224
use std::rc::Rc;
@@ -82,14 +84,14 @@ pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
8284
/// include! : parse the given file as an expr
8385
/// This is generally a bad idea because it's going to behave
8486
/// unhygienically.
85-
pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
86-
-> Box<base::MacResult+'static> {
87+
pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
88+
-> Box<base::MacResult+'cx> {
8789
let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
8890
Some(f) => f,
8991
None => return DummyResult::expr(sp),
9092
};
9193
// The file will be added to the code map by the parser
92-
let mut p =
94+
let p =
9395
parse::new_sub_parser_from_file(cx.parse_sess(),
9496
cx.cfg(),
9597
&res_rel_file(cx,
@@ -98,7 +100,28 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
98100
true,
99101
None,
100102
sp);
101-
base::MacExpr::new(p.parse_expr())
103+
104+
struct ExpandResult<'a> {
105+
p: parse::parser::Parser<'a>,
106+
}
107+
impl<'a> base::MacResult for ExpandResult<'a> {
108+
fn make_expr(mut self: Box<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
109+
Some(self.p.parse_expr())
110+
}
111+
fn make_items(mut self: Box<ExpandResult<'a>>)
112+
-> Option<SmallVector<P<ast::Item>>> {
113+
let mut ret = SmallVector::zero();
114+
loop {
115+
match self.p.parse_item_with_outer_attributes() {
116+
Some(item) => ret.push(item),
117+
None => break
118+
}
119+
}
120+
Some(ret)
121+
}
122+
}
123+
124+
box ExpandResult { p: p }
102125
}
103126

104127
// include_str! : read the given file, insert it as a literal string expr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-test: this is not a test
12+
13+
1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-test: this is not a test
12+
13+
fn foo() { bar() }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags:-l :static
12+
// error-pattern: empty library name given via `-l`
13+
14+
fn main() {
15+
}
16+
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags:-l foo:bar
12+
// error-pattern: unknown library kind `bar`, expected one of dylib, framework, or static
13+
14+
fn main() {
15+
}
16+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-macos
12+
// ignore-ios
13+
// compile-flags:-l foo:framework
14+
// error-pattern: native frameworks are only available on OSX targets
15+
16+
fn main() {
17+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-include ../tools.mk
2+
3+
all: $(TMPDIR)/libbar.a
4+
$(RUSTC) foo.rs -lbar:static
5+
$(RUSTC) main.rs
6+
$(call RUN,main)
7+

src/test/run-make/manual-link/bar.c

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void bar() {}

src/test/run-make/manual-link/foo.c

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void bar() {}

src/test/run-make/manual-link/foo.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "rlib"]
12+
13+
extern {
14+
fn bar();
15+
}
16+
17+
pub fn foo() {
18+
unsafe { bar(); }
19+
}

src/test/run-make/manual-link/main.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
extern crate foo;
12+
13+
fn main() {
14+
foo::foo();
15+
}

0 commit comments

Comments
 (0)