Skip to content

Commit a5b734d

Browse files
committed
Auto merge of #4515 - lukaslueg:issue1006, r=alexcrichton
Improve message for multiple links to native lib Proposal for a fix to #1006, as advertised in my comment; as discussed briefly with alexcrichton on IRC. In case multiple packages link to the same library, the error message is now > error: More than one package links to native library `a`, which can only be linked once. > > Package a-sys v0.5.0 (file:///home/lukas/dev/issue1006test/a) links to native library `a`. > (Dependency via foo v0.5.0 (file:///home/lukas/dev/issue1006test)) > > Package b-sys v0.5.0 (file:///home/lukas/dev/issue1006test/a/b) also links to native library `a`. > (Dependency via a-sys v0.5.0 (file:///home/lukas/dev/issue1006test/a) => foo v0.5.0 (file:///home/lukas/dev/issue1006test)) > > Try updating or pinning your dependencies to ensure that native library `a` is only linked once. > In case the root-package itself is an offender: > Package foo v0.5.0 (file:///home/lukas/dev/issue1006test) links to native library `a`. > (This is the root-package) > IMHO the wording is much clearer now (the term "native library" and "package" are repeated on purpose); printing the whole dependency-chain from the offending package up to the root allows the user to at least figure out where the native library actually comes in. Added a unit-test, which all pass. Please scrutinize this carefully, it's my first PR for cargo.
2 parents fdf0bab + 45b4a55 commit a5b734d

File tree

4 files changed

+117
-20
lines changed

4 files changed

+117
-20
lines changed

src/cargo/core/resolver/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,31 @@ struct Candidate {
114114
}
115115

116116
impl Resolve {
117+
/// Resolves one of the paths from the given dependent package up to
118+
/// the root.
119+
pub fn path_to_top(&self, pkg: &PackageId) -> Vec<&PackageId> {
120+
let mut result = Vec::new();
121+
let mut pkg = pkg;
122+
loop {
123+
match self.graph
124+
.get_nodes()
125+
.iter()
126+
.filter_map(|(pulling, pulled)|
127+
if pulled.contains(pkg) {
128+
Some(pulling)
129+
} else {
130+
None
131+
})
132+
.nth(0) {
133+
Some(pulling) => {
134+
result.push(pulling);
135+
pkg = pulling;
136+
},
137+
None => break
138+
}
139+
}
140+
result
141+
}
117142
pub fn register_used_patches(&mut self,
118143
patches: &HashMap<Url, Vec<Summary>>) {
119144
for summary in patches.values().flat_map(|v| v) {

src/cargo/ops/cargo_rustc/links.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::{HashMap, HashSet};
2+
use std::fmt::Write;
23

3-
use core::PackageId;
4+
use core::{Resolve, PackageId};
45
use util::CargoResult;
56
use super::Unit;
67

@@ -17,7 +18,7 @@ impl<'a> Links<'a> {
1718
}
1819
}
1920

20-
pub fn validate(&mut self, unit: &Unit<'a>) -> CargoResult<()> {
21+
pub fn validate(&mut self, resolve: &Resolve, unit: &Unit<'a>) -> CargoResult<()> {
2122
if !self.validated.insert(unit.pkg.package_id()) {
2223
return Ok(())
2324
}
@@ -27,17 +28,31 @@ impl<'a> Links<'a> {
2728
};
2829
if let Some(prev) = self.links.get(lib) {
2930
let pkg = unit.pkg.package_id();
30-
if prev.name() == pkg.name() && prev.source_id() == pkg.source_id() {
31-
bail!("native library `{}` is being linked to by more \
32-
than one version of the same package, but it can \
33-
only be linked once; try updating or pinning your \
34-
dependencies to ensure that this package only shows \
35-
up once\n\n {}\n {}", lib, prev, pkg)
36-
} else {
37-
bail!("native library `{}` is being linked to by more than \
38-
one package, and can only be linked to by one \
39-
package\n\n {}\n {}", lib, prev, pkg)
40-
}
31+
32+
let describe_path = |pkgid: &PackageId| -> String {
33+
let dep_path = resolve.path_to_top(pkgid);
34+
if dep_path.is_empty() {
35+
String::from("The root-package ")
36+
} else {
37+
let mut dep_path_desc = format!("Package `{}`\n", pkgid);
38+
for dep in dep_path {
39+
write!(dep_path_desc,
40+
" ... which is depended on by `{}`\n",
41+
dep).unwrap();
42+
}
43+
dep_path_desc
44+
}
45+
};
46+
47+
bail!("Multiple packages link to native library `{}`. \
48+
A native library can be linked only once.\n\
49+
\n\
50+
{}links to native library `{}`.\n\
51+
\n\
52+
{}also links to native library `{}`.",
53+
lib,
54+
describe_path(prev), lib,
55+
describe_path(pkg), lib)
4156
}
4257
if !unit.pkg.manifest().targets().iter().any(|t| t.is_custom_build()) {
4358
bail!("package `{}` specifies that it links to `{}` but does not \

src/cargo/ops/cargo_rustc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ fn compile<'a, 'cfg: 'a>(cx: &mut Context<'a, 'cfg>,
237237
let p = profile::start(format!("preparing: {}/{}", unit.pkg,
238238
unit.target.name()));
239239
fingerprint::prepare_init(cx, unit)?;
240-
cx.links.validate(unit)?;
240+
cx.links.validate(&cx.resolve, unit)?;
241241

242242
let (dirty, fresh, freshness) = if unit.profile.run_custom_build {
243243
custom_build::prepare(cx, unit)?

tests/build-script.rs

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,47 @@ not have a custom build script
220220

221221
#[test]
222222
fn links_duplicates() {
223+
let p = project("foo")
224+
.file("Cargo.toml", r#"
225+
[project]
226+
name = "foo"
227+
version = "0.5.0"
228+
authors = []
229+
links = "a"
230+
build = "build.rs"
231+
232+
[dependencies.a-sys]
233+
path = "a-sys"
234+
"#)
235+
.file("src/lib.rs", "")
236+
.file("build.rs", "")
237+
.file("a-sys/Cargo.toml", r#"
238+
[project]
239+
name = "a-sys"
240+
version = "0.5.0"
241+
authors = []
242+
links = "a"
243+
build = "build.rs"
244+
"#)
245+
.file("a-sys/src/lib.rs", "")
246+
.file("a-sys/build.rs", "");
247+
248+
assert_that(p.cargo_process("build"),
249+
execs().with_status(101)
250+
.with_stderr("\
251+
[ERROR] Multiple packages link to native library `a`. A native library can be \
252+
linked only once.
253+
254+
The root-package links to native library `a`.
255+
256+
Package `a-sys v0.5.0 (file://[..])`
257+
... which is depended on by `foo v0.5.0 (file://[..])`
258+
also links to native library `a`.
259+
"));
260+
}
261+
262+
#[test]
263+
fn links_duplicates_deep_dependency() {
223264
let p = project("foo")
224265
.file("Cargo.toml", r#"
225266
[project]
@@ -239,20 +280,36 @@ fn links_duplicates() {
239280
name = "a"
240281
version = "0.5.0"
241282
authors = []
242-
links = "a"
243283
build = "build.rs"
284+
285+
[dependencies.a-sys]
286+
path = "a-sys"
244287
"#)
245288
.file("a/src/lib.rs", "")
246-
.file("a/build.rs", "");
289+
.file("a/build.rs", "")
290+
.file("a/a-sys/Cargo.toml", r#"
291+
[project]
292+
name = "a-sys"
293+
version = "0.5.0"
294+
authors = []
295+
links = "a"
296+
build = "build.rs"
297+
"#)
298+
.file("a/a-sys/src/lib.rs", "")
299+
.file("a/a-sys/build.rs", "");
247300

248301
assert_that(p.cargo_process("build"),
249302
execs().with_status(101)
250303
.with_stderr("\
251-
[ERROR] native library `a` is being linked to by more than one package, and can only be \
252-
linked to by one package
304+
[ERROR] Multiple packages link to native library `a`. A native library can be \
305+
linked only once.
306+
307+
The root-package links to native library `a`.
253308
254-
[..] v0.5.0 (file://[..])
255-
[..] v0.5.0 (file://[..])
309+
Package `a-sys v0.5.0 (file://[..])`
310+
... which is depended on by `a v0.5.0 (file://[..])`
311+
... which is depended on by `foo v0.5.0 (file://[..])`
312+
also links to native library `a`.
256313
"));
257314
}
258315

0 commit comments

Comments
 (0)