Skip to content

Commit

Permalink
rustc: Implement -l and include! tweaks
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexcrichton committed Oct 31, 2014
1 parent fd53657 commit 8e6e846
Show file tree
Hide file tree
Showing 16 changed files with 234 additions and 40 deletions.
27 changes: 26 additions & 1 deletion src/librustc/driver/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Vec<Path>>,
pub libs: Vec<(String, cstore::NativeLibaryKind)>,
pub maybe_sysroot: Option<Path>,
pub target_triple: String,
// User-specified cfg meta items. The compiler itself will add additional
Expand Down Expand Up @@ -130,6 +132,7 @@ pub fn basic_options() -> Options {
externs: HashMap::new(),
crate_name: None,
alt_std_name: None,
libs: Vec::new(),
}
}

Expand Down Expand Up @@ -575,6 +578,10 @@ pub fn optgroups() -> Vec<getopts::OptGroup> {
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]"),
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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,
}
}

Expand Down
50 changes: 33 additions & 17 deletions src/librustc/metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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,
Expand All @@ -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 => {}
}
Expand All @@ -281,6 +271,32 @@ fn visit_item(e: &Env, i: &ast::Item) {
}
}

fn register_native_lib(sess: &Session, span: Option<Span>, 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<ast::CrateNum> {
let mut ret = None;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/metadata/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 8 additions & 15 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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
Expand Down
35 changes: 29 additions & 6 deletions src/libsyntax/ext/source_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<base::MacResult+'static> {
pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> Box<base::MacResult+'cx> {
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,
Expand All @@ -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<ExpandResult<'a>>) -> Option<P<ast::Expr>> {
Some(self.p.parse_expr())
}
fn make_items(mut self: Box<ExpandResult<'a>>)
-> Option<SmallVector<P<ast::Item>>> {
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
Expand Down
13 changes: 13 additions & 0 deletions src/test/auxiliary/macro-include-items-expr.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
13 changes: 13 additions & 0 deletions src/test/auxiliary/macro-include-items-item.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() }
17 changes: 17 additions & 0 deletions src/test/compile-fail/manual-link-bad-form.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {
}


16 changes: 16 additions & 0 deletions src/test/compile-fail/manual-link-bad-kind.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {
}

17 changes: 17 additions & 0 deletions src/test/compile-fail/manual-link-framework.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {
}
7 changes: 7 additions & 0 deletions src/test/run-make/manual-link/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-include ../tools.mk

all: $(TMPDIR)/libbar.a
$(RUSTC) foo.rs -lbar:static
$(RUSTC) main.rs
$(call RUN,main)

1 change: 1 addition & 0 deletions src/test/run-make/manual-link/bar.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void bar() {}
1 change: 1 addition & 0 deletions src/test/run-make/manual-link/foo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void bar() {}
19 changes: 19 additions & 0 deletions src/test/run-make/manual-link/foo.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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(); }
}
15 changes: 15 additions & 0 deletions src/test/run-make/manual-link/main.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate foo;

fn main() {
foo::foo();
}
Loading

2 comments on commit 8e6e846

@alexcrichton
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=brson

@alexcrichton
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors: retry

Please sign in to comment.