Tweak how preference factors into linkage
The new methodology can be found in the re-worded comment, but the gist of it is that -C prefer-dynamic doesn't turn off static linkage. The error messages should also be a little more sane now. Closes #12133
This commit is contained in:
parent
98b07755dd
commit
35c6e22fab
9 changed files with 246 additions and 91 deletions
|
@ -537,10 +537,6 @@ TEST_SREQ$(1)_T_$(2)_H_$(3) = \
|
||||||
# remove directive, if present, from CFG_RUSTC_FLAGS (issue #7898).
|
# remove directive, if present, from CFG_RUSTC_FLAGS (issue #7898).
|
||||||
CTEST_RUSTC_FLAGS := $$(subst --cfg ndebug,,$$(CFG_RUSTC_FLAGS))
|
CTEST_RUSTC_FLAGS := $$(subst --cfg ndebug,,$$(CFG_RUSTC_FLAGS))
|
||||||
|
|
||||||
# There's no need our entire test suite to take up gigabytes of space on disk
|
|
||||||
# including copies of libstd/libextra all over the place
|
|
||||||
CTEST_RUSTC_FLAGS := $$(CTEST_RUSTC_FLAGS) -C prefer-dynamic
|
|
||||||
|
|
||||||
# The tests can not be optimized while the rest of the compiler is optimized, so
|
# The tests can not be optimized while the rest of the compiler is optimized, so
|
||||||
# filter out the optimization (if any) from rustc and then figure out if we need
|
# filter out the optimization (if any) from rustc and then figure out if we need
|
||||||
# to be optimized
|
# to be optimized
|
||||||
|
|
|
@ -32,6 +32,8 @@ pub struct TestProps {
|
||||||
force_host: bool,
|
force_host: bool,
|
||||||
// Check stdout for error-pattern output as well as stderr
|
// Check stdout for error-pattern output as well as stderr
|
||||||
check_stdout: bool,
|
check_stdout: bool,
|
||||||
|
// Don't force a --crate-type=dylib flag on the command line
|
||||||
|
no_prefer_dynamic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load any test directives embedded in the file
|
// Load any test directives embedded in the file
|
||||||
|
@ -45,6 +47,7 @@ pub fn load_props(testfile: &Path) -> TestProps {
|
||||||
let mut check_lines = ~[];
|
let mut check_lines = ~[];
|
||||||
let mut force_host = false;
|
let mut force_host = false;
|
||||||
let mut check_stdout = false;
|
let mut check_stdout = false;
|
||||||
|
let mut no_prefer_dynamic = false;
|
||||||
iter_header(testfile, |ln| {
|
iter_header(testfile, |ln| {
|
||||||
match parse_error_pattern(ln) {
|
match parse_error_pattern(ln) {
|
||||||
Some(ep) => error_patterns.push(ep),
|
Some(ep) => error_patterns.push(ep),
|
||||||
|
@ -67,6 +70,10 @@ pub fn load_props(testfile: &Path) -> TestProps {
|
||||||
check_stdout = parse_check_stdout(ln);
|
check_stdout = parse_check_stdout(ln);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !no_prefer_dynamic {
|
||||||
|
no_prefer_dynamic = parse_no_prefer_dynamic(ln);
|
||||||
|
}
|
||||||
|
|
||||||
match parse_aux_build(ln) {
|
match parse_aux_build(ln) {
|
||||||
Some(ab) => { aux_builds.push(ab); }
|
Some(ab) => { aux_builds.push(ab); }
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -99,6 +106,7 @@ pub fn load_props(testfile: &Path) -> TestProps {
|
||||||
check_lines: check_lines,
|
check_lines: check_lines,
|
||||||
force_host: force_host,
|
force_host: force_host,
|
||||||
check_stdout: check_stdout,
|
check_stdout: check_stdout,
|
||||||
|
no_prefer_dynamic: no_prefer_dynamic,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +175,10 @@ fn parse_check_stdout(line: &str) -> bool {
|
||||||
parse_name_directive(line, "check-stdout")
|
parse_name_directive(line, "check-stdout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_no_prefer_dynamic(line: &str) -> bool {
|
||||||
|
parse_name_directive(line, "no-prefer-dynamic")
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_exec_env(line: &str) -> Option<(~str, ~str)> {
|
fn parse_exec_env(line: &str) -> Option<(~str, ~str)> {
|
||||||
parse_name_value_directive(line, ~"exec-env").map(|nv| {
|
parse_name_value_directive(line, ~"exec-env").map(|nv| {
|
||||||
// nv is either FOO or FOO=BAR
|
// nv is either FOO or FOO=BAR
|
||||||
|
|
|
@ -704,9 +704,13 @@ fn compose_and_run_compiler(
|
||||||
for rel_ab in props.aux_builds.iter() {
|
for rel_ab in props.aux_builds.iter() {
|
||||||
let abs_ab = config.aux_base.join(rel_ab.as_slice());
|
let abs_ab = config.aux_base.join(rel_ab.as_slice());
|
||||||
let aux_props = load_props(&abs_ab);
|
let aux_props = load_props(&abs_ab);
|
||||||
|
let crate_type = if aux_props.no_prefer_dynamic {
|
||||||
|
~[]
|
||||||
|
} else {
|
||||||
|
~[~"--crate-type=dylib"]
|
||||||
|
};
|
||||||
let aux_args =
|
let aux_args =
|
||||||
make_compile_args(config, &aux_props, ~[~"--crate-type=dylib"]
|
make_compile_args(config, &aux_props, crate_type + extra_link_args,
|
||||||
+ extra_link_args,
|
|
||||||
|a,b| {
|
|a,b| {
|
||||||
let f = make_lib_name(a, b, testfile);
|
let f = make_lib_name(a, b, testfile);
|
||||||
ThisDirectory(f.dir_path())
|
ThisDirectory(f.dir_path())
|
||||||
|
@ -770,6 +774,10 @@ fn make_compile_args(config: &config,
|
||||||
~"-L", config.build_base.as_str().unwrap().to_owned(),
|
~"-L", config.build_base.as_str().unwrap().to_owned(),
|
||||||
~"--target=" + target]
|
~"--target=" + target]
|
||||||
+ extras;
|
+ extras;
|
||||||
|
if !props.no_prefer_dynamic {
|
||||||
|
args.push(~"-C");
|
||||||
|
args.push(~"prefer-dynamic");
|
||||||
|
}
|
||||||
let path = match xform_file {
|
let path = match xform_file {
|
||||||
ThisFile(path) => { args.push(~"-o"); path }
|
ThisFile(path) => { args.push(~"-o"); path }
|
||||||
ThisDirectory(path) => { args.push(~"--out-dir"); path }
|
ThisDirectory(path) => { args.push(~"--out-dir"); path }
|
||||||
|
|
|
@ -1220,6 +1220,74 @@ fn add_local_native_libraries(args: &mut ~[~str], sess: Session) {
|
||||||
// the intermediate rlib version)
|
// the intermediate rlib version)
|
||||||
fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
||||||
dylib: bool, tmpdir: &Path) {
|
dylib: bool, tmpdir: &Path) {
|
||||||
|
|
||||||
|
// As a limitation of the current implementation, we require that everything
|
||||||
|
// must be static or everything must be dynamic. The reasons for this are a
|
||||||
|
// little subtle, but as with staticlibs and rlibs, the goal is to prevent
|
||||||
|
// duplicate copies of the same library showing up. For example, a static
|
||||||
|
// immediate dependency might show up as an upstream dynamic dependency and
|
||||||
|
// we currently have no way of knowing that. We know that all dynamic
|
||||||
|
// libraries require dynamic dependencies (see above), so it's satisfactory
|
||||||
|
// to include either all static libraries or all dynamic libraries.
|
||||||
|
//
|
||||||
|
// With this limitation, we expose a compiler default linkage type and an
|
||||||
|
// option to reverse that preference. The current behavior looks like:
|
||||||
|
//
|
||||||
|
// * If a dylib is being created, upstream dependencies must be dylibs
|
||||||
|
// * If nothing else is specified, static linking is preferred
|
||||||
|
// * If the -C prefer-dynamic flag is given, dynamic linking is preferred
|
||||||
|
// * If one form of linking fails, the second is also attempted
|
||||||
|
// * If both forms fail, then we emit an error message
|
||||||
|
|
||||||
|
let dynamic = get_deps(sess.cstore, cstore::RequireDynamic);
|
||||||
|
let statik = get_deps(sess.cstore, cstore::RequireStatic);
|
||||||
|
match (dynamic, statik, sess.opts.cg.prefer_dynamic, dylib) {
|
||||||
|
(_, Some(deps), false, false) => {
|
||||||
|
add_static_crates(args, sess, tmpdir, deps)
|
||||||
|
}
|
||||||
|
|
||||||
|
(None, Some(deps), true, false) => {
|
||||||
|
// If you opted in to dynamic linking and we decided to emit a
|
||||||
|
// static output, you should probably be notified of such an event!
|
||||||
|
sess.warn("dynamic linking was preferred, but dependencies \
|
||||||
|
could not all be found in an dylib format.");
|
||||||
|
sess.warn("linking statically instead, using rlibs");
|
||||||
|
add_static_crates(args, sess, tmpdir, deps)
|
||||||
|
}
|
||||||
|
|
||||||
|
(Some(deps), _, _, _) => add_dynamic_crates(args, sess, deps),
|
||||||
|
|
||||||
|
(None, _, _, true) => {
|
||||||
|
sess.err("dylib output requested, but some depenencies could not \
|
||||||
|
be found in the dylib format");
|
||||||
|
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
|
||||||
|
for (cnum, path) in deps.move_iter() {
|
||||||
|
if path.is_some() { continue }
|
||||||
|
let name = sess.cstore.get_crate_data(cnum).name.clone();
|
||||||
|
sess.note(format!("dylib not found: {}", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(None, None, pref, false) => {
|
||||||
|
let (pref, name) = if pref {
|
||||||
|
sess.err("dynamic linking is preferred, but dependencies were \
|
||||||
|
not found in either dylib or rlib format");
|
||||||
|
(cstore::RequireDynamic, "dylib")
|
||||||
|
} else {
|
||||||
|
sess.err("dependencies were not all found in either dylib or \
|
||||||
|
rlib format");
|
||||||
|
(cstore::RequireStatic, "rlib")
|
||||||
|
};
|
||||||
|
sess.note(format!("dependencies not found in the `{}` format",
|
||||||
|
name));
|
||||||
|
for (cnum, path) in sess.cstore.get_used_crates(pref).move_iter() {
|
||||||
|
if path.is_some() { continue }
|
||||||
|
let name = sess.cstore.get_crate_data(cnum).name.clone();
|
||||||
|
sess.note(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Converts a library file-stem into a cc -l argument
|
// Converts a library file-stem into a cc -l argument
|
||||||
fn unlib(config: @session::Config, stem: &str) -> ~str {
|
fn unlib(config: @session::Config, stem: &str) -> ~str {
|
||||||
if stem.starts_with("lib") &&
|
if stem.starts_with("lib") &&
|
||||||
|
@ -1230,23 +1298,24 @@ fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cstore = sess.cstore;
|
// Attempts to find all dependencies with a certain linkage preference,
|
||||||
if !dylib && !sess.opts.cg.prefer_dynamic {
|
// returning `None` if not all libraries could be found with that
|
||||||
// With an executable, things get a little interesting. As a limitation
|
// preference.
|
||||||
// of the current implementation, we require that everything must be
|
fn get_deps(cstore: &cstore::CStore, preference: cstore::LinkagePreference)
|
||||||
// static or everything must be dynamic. The reasons for this are a
|
-> Option<~[(ast::CrateNum, Path)]>
|
||||||
// little subtle, but as with the above two cases, the goal is to
|
{
|
||||||
// prevent duplicate copies of the same library showing up. For example,
|
let crates = cstore.get_used_crates(preference);
|
||||||
// a static immediate dependency might show up as an upstream dynamic
|
|
||||||
// dependency and we currently have no way of knowing that. We know that
|
|
||||||
// all dynamic libraries require dynamic dependencies (see above), so
|
|
||||||
// it's satisfactory to include either all static libraries or all
|
|
||||||
// dynamic libraries.
|
|
||||||
let crates = cstore.get_used_crates(cstore::RequireStatic);
|
|
||||||
if crates.iter().all(|&(_, ref p)| p.is_some()) {
|
if crates.iter().all(|&(_, ref p)| p.is_some()) {
|
||||||
for (cnum, path) in crates.move_iter() {
|
Some(crates.move_iter().map(|(a, b)| (a, b.unwrap())).collect())
|
||||||
let cratepath = path.unwrap();
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the static "rlib" versions of all crates to the command line.
|
||||||
|
fn add_static_crates(args: &mut ~[~str], sess: Session, tmpdir: &Path,
|
||||||
|
crates: ~[(ast::CrateNum, Path)]) {
|
||||||
|
for (cnum, cratepath) in crates.move_iter() {
|
||||||
// When performing LTO on an executable output, all of the
|
// When performing LTO on an executable output, all of the
|
||||||
// bytecode from the upstream libraries has already been
|
// bytecode from the upstream libraries has already been
|
||||||
// included in our object file output. We need to modify all of
|
// included in our object file output. We need to modify all of
|
||||||
|
@ -1288,39 +1357,24 @@ fn add_upstream_rust_crates(args: &mut ~[~str], sess: Session,
|
||||||
args.push(cratepath.as_str().unwrap().to_owned());
|
args.push(cratepath.as_str().unwrap().to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same thing as above, but for dynamic crates instead of static crates.
|
||||||
|
fn add_dynamic_crates(args: &mut ~[~str], sess: Session,
|
||||||
|
crates: ~[(ast::CrateNum, Path)]) {
|
||||||
// If we're performing LTO, then it should have been previously required
|
// If we're performing LTO, then it should have been previously required
|
||||||
// that all upstream rust dependencies were available in an rlib format.
|
// that all upstream rust dependencies were available in an rlib format.
|
||||||
assert!(!sess.lto());
|
assert!(!sess.lto());
|
||||||
|
|
||||||
// This is a fallback of three different cases of linking:
|
for (_, cratepath) in crates.move_iter() {
|
||||||
//
|
// Just need to tell the linker about where the library lives and
|
||||||
// * When creating a dynamic library, all inputs are required to be dynamic
|
// what its name is
|
||||||
// as well
|
|
||||||
// * If an executable is created with a preference on dynamic linking, then
|
|
||||||
// this case is the fallback
|
|
||||||
// * If an executable is being created, and one of the inputs is missing as
|
|
||||||
// a static library, then this is the fallback case.
|
|
||||||
let crates = cstore.get_used_crates(cstore::RequireDynamic);
|
|
||||||
for &(cnum, ref path) in crates.iter() {
|
|
||||||
let cratepath = match *path {
|
|
||||||
Some(ref p) => p.clone(),
|
|
||||||
None => {
|
|
||||||
sess.err(format!("could not find dynamic library for: `{}`",
|
|
||||||
sess.cstore.get_crate_data(cnum).name));
|
|
||||||
return
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Just need to tell the linker about where the library lives and what
|
|
||||||
// its name is
|
|
||||||
let dir = cratepath.dirname_str().unwrap();
|
let dir = cratepath.dirname_str().unwrap();
|
||||||
if !dir.is_empty() { args.push("-L" + dir); }
|
if !dir.is_empty() { args.push("-L" + dir); }
|
||||||
let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap());
|
let libarg = unlib(sess.targ_cfg, cratepath.filestem_str().unwrap());
|
||||||
args.push("-l" + libarg);
|
args.push("-l" + libarg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link in all of our upstream crates' native dependencies. Remember that
|
// Link in all of our upstream crates' native dependencies. Remember that
|
||||||
|
|
13
src/test/auxiliary/issue-12133-dylib.rs
Normal file
13
src/test/auxiliary/issue-12133-dylib.rs
Normal file
|
@ -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.
|
||||||
|
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
#[crate_type = "dylib"];
|
13
src/test/auxiliary/issue-12133-rlib.rs
Normal file
13
src/test/auxiliary/issue-12133-rlib.rs
Normal file
|
@ -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.
|
||||||
|
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
#[crate_type = "rlib"];
|
19
src/test/compile-fail/issue-12133-1.rs
Normal file
19
src/test/compile-fail/issue-12133-1.rs
Normal file
|
@ -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.
|
||||||
|
|
||||||
|
// aux-build:issue-12133-rlib.rs
|
||||||
|
// aux-build:issue-12133-dylib.rs
|
||||||
|
|
||||||
|
// error-pattern: dynamic linking is preferred, but dependencies were not found
|
||||||
|
|
||||||
|
extern crate a = "issue-12133-rlib";
|
||||||
|
extern crate b = "issue-12133-dylib";
|
||||||
|
|
||||||
|
fn main() {}
|
20
src/test/compile-fail/issue-12133-2.rs
Normal file
20
src/test/compile-fail/issue-12133-2.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// aux-build:issue-12133-rlib.rs
|
||||||
|
// aux-build:issue-12133-dylib.rs
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
// error-pattern: dependencies were not all found in either dylib or rlib format
|
||||||
|
|
||||||
|
extern crate a = "issue-12133-rlib";
|
||||||
|
extern crate b = "issue-12133-dylib";
|
||||||
|
|
||||||
|
fn main() {}
|
20
src/test/compile-fail/issue-12133-3.rs
Normal file
20
src/test/compile-fail/issue-12133-3.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// aux-build:issue-12133-rlib.rs
|
||||||
|
// aux-build:issue-12133-dylib.rs
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
// error-pattern: dylib output requested, but some depenencies could not
|
||||||
|
|
||||||
|
#[crate_type = "dylib"];
|
||||||
|
|
||||||
|
extern crate a = "issue-12133-rlib";
|
||||||
|
extern crate b = "issue-12133-dylib";
|
Loading…
Add table
Add a link
Reference in a new issue