Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix doctests linking too many libs. #5651

Merged
merged 5 commits into from
Jul 5, 2018
Merged

Conversation

ehuss
Copy link
Contributor

@ehuss ehuss commented Jun 26, 2018

Fixes #5650. cc #5435

As part of my recent work on profiles, I introduced some situations where a
library can be compiled multiple times with different settings. Doctests were
greedily grabbing all dependencies for a package, regardless of which target
is was for. This can cause doctests to fail if it links multiple copies of
the same library.

One way to trigger this is cargo test --release if you have dependencies, a
build script, and panic="abort". There are other (more obscure) ways to
trigger it with profile overrides.

Fixes rust-lang#5650.  cc rust-lang#5435

As part of my recent work on profiles, I introduced some situations where a
library can be compiled multiple times with different settings.  Doctests were
greedily grabbing all dependencies for a package, regardless of which target
is was for.  This can cause doctests to fail if it links multiple copies of
the same library.

One way to trigger this is `cargo test --release` if you have dependencies, a
build script, and `panic="abort"`.  There are other (more obscure) ways to
trigger it with profile overrides.
@rust-highfive
Copy link

r? @matklad

(rust_highfive has picked a reviewer for you, use r? to override)

@ehuss
Copy link
Contributor Author

ehuss commented Jun 26, 2018

Notes:

  • Compilation.libraries is no longer used by cargo, but I left it as-is. I noticed that cargo-bloat is using it, I'm not sure if there are other external users needing it. I'm not sure if we should just leave it, remove it, or replace it with something better.
  • Let me know if you want a more detailed description of what was happening, this is a little subtle.

@Eh2406
Copy link
Contributor

Eh2406 commented Jun 26, 2018

Don't take this as any kind of review. For my own reasons, I'd appreciate a more detailed explanation. Specifically someday the resolver is going to grow private dependencies witch will lead to the same crate with different features ending up in the build plan. And I want a heads up of the corner cases we will have to fix when we get there. :-P

pub to_doc_test: Vec<Package>,
/// Libraries to test with rustdoc.
/// The third value is the list of dependencies.
pub to_doc_test: Vec<(Package, Target, Vec<(Target, PathBuf)>)>,
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about changing this triple to a struct, so as to make comment not-necessary? It might also be worthwhile to move the

if lib.extension() != Some(OsStr::new("rlib")) && !target.for_host() {

logic to the place where we generate these dependencies?

@matklad
Copy link
Member

matklad commented Jun 26, 2018

Thanks @ehuss ! The code looks correct to me, but I wonder if we could centralize decisions about doctest dependencies in one place? Currently we have two filtering steps: when genreating deps and then when we construct command line for rustdoc.

I'm not sure if we should just leave it, remove it, or replace it with something better.

In general, it's fine to remove stuff. I'd love to hear @alexcrichton opinion about this specifically though, because I don't know the original rational behind this field.

As this is a bug fix, let's not remove stuff right now, but leave a comment noting that this field is probably unused.

Should this be backported to beta?

In rare cases, the profile_for is set incorrectly for this temporary unit used
to compute dependencies.  However, it's not needed since the RunCustomBuild
Unit's parent is already in the map.
- Consolidate logic for determining doctest dependencies.
- Use a struct for the doctest info in `Compilation`.
@ehuss
Copy link
Contributor Author

ehuss commented Jun 27, 2018

@Eh2406 sure, it's very particular to how the rustdoc libs were collected and a side effect of build scripts, and dealing with panic. I'll try to walk through the issues:

  • Scenario: Library foo with panic set in profile.release, a build.rs script, and one dependency named bar. Run cargo test --release. It will fail when running doctests.
  • When panic is set in the profile, the code ensures that tests and all their dependencies do not set panic when building. This is done with the flag ProfileFor in unit_dependencies.rs to ensure that all the dependencies get the correct profile (without panic set).
  • When computing build.rs dependencies, this code attempts to find dependencies with links set. However, it is setting ProfileFor incorrectly here and poisons the dep-map with incorrect entries -- in this case it adds a dependency from foo-lib (needed by doctests) to bar and the bar entry has panic enabled.
    • This is the part that requires --release, because the Profile used for the "temp" entry matches the foo-lib unit.
  • bar gets incorrectly compiled twice:
    • Once as a dependency of the foo unittest.
    • Once as a dependency of the foo lib being compiled for doctests (and this incorrectly sets panic).
  • The code for running doctests collects a naive map in Compilation.libraries of the Package mapped to a list of dependencies (here). However, in general we can't guarantee that a library is only compiled once. In this case, it grabs the two bars and rustdoc fails.

There are multiple parts to the solution:

  • Don't poison the dep map when checking build script dependencies.
  • Ensure that a Doctest Unit is created in the target list so that we can accurately fetch the dependencies needed for running doctests.
  • Use a new list in Compilation.to_doc_test that includes the correct, minimal set of correct dependency information for a doctest.

I'm not aware of any other current issues with compiling a crate multiple times with different settings. There is an issue with rmeta files getting stomped on, because the names are not unique, but that's slightly different.

@ehuss
Copy link
Contributor Author

ehuss commented Jun 27, 2018

Should this be backported to beta?

I think that might be a good idea. I'm not sure how often people run test --release, though.

@ehuss
Copy link
Contributor Author

ehuss commented Jun 28, 2018

Note: This exacerbated an existing bug with how "Fresh" is displayed with doctests. There are some scenarios today where cargo test --doc -v would randomly print "Fresh". The issue is that if a "Fresh" Doctest unit happens to be the last to run in the JobQueue, it would always print "Fresh", even if other units for that package had already run. Whether or not it was last was kinda random, so it was unpredictable. The solution was to check for this scenario and skip printing "Fresh" (only if something was already printed, otherwise "Fresh" is printed).

@alexcrichton
Copy link
Member

ping @matklad, did you want to take another look at this?

@matklad
Copy link
Member

matklad commented Jul 5, 2018

LGTM!

@bors r+

@alexcrichton, should we remove compilation.iibraires? #5651 (comment)

@bors
Copy link
Contributor

bors commented Jul 5, 2018

📌 Commit 478d1ce has been approved by matklad

@alexcrichton
Copy link
Member

Sounds fine by me!

@bors
Copy link
Contributor

bors commented Jul 5, 2018

⌛ Testing commit 478d1ce with merge 4a8495c...

bors added a commit that referenced this pull request Jul 5, 2018
Fix doctests linking too many libs.

Fixes #5650.  cc #5435

As part of my recent work on profiles, I introduced some situations where a
library can be compiled multiple times with different settings.  Doctests were
greedily grabbing all dependencies for a package, regardless of which target
is was for.  This can cause doctests to fail if it links multiple copies of
the same library.

One way to trigger this is `cargo test --release` if you have dependencies, a
build script, and `panic="abort"`.  There are other (more obscure) ways to
trigger it with profile overrides.
@bors
Copy link
Contributor

bors commented Jul 5, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: matklad
Pushing 4a8495c to master...

@bors bors merged commit 478d1ce into rust-lang:master Jul 5, 2018
ehuss pushed a commit to ehuss/cargo that referenced this pull request Jul 5, 2018
bors added a commit that referenced this pull request Jul 5, 2018
[BETA] Fix doctests linking too many libs.

Backport of #5651.
alexcrichton added a commit to alexcrichton/cargo that referenced this pull request Jul 13, 2018
Some logic which was tweaked around the dependencies of build script targets was
tweaked slightly in a way that causes cargo to stack overflow by accientally
adding a dependency loop. This commit implements one of the strategies discussed
in rust-lang#5711 to fix this situation.

The problem here is that when calculating the deps of a build script we need the
build scripts of *other* packages, but the exact profile is somewhat difficult
to guess at the moment we're generating our build script unit. To solve this the
dependencies towards other build scripts' executions is added in a different
pass after all other units have been assembled. At this point we should know for
sure that all build script executions are in the dependency graph, and we just
need to add a few more edges.

Closes rust-lang#5708
bors added a commit that referenced this pull request Jul 13, 2018
Partially revert dep changes in #5651

Some logic which was tweaked around the dependencies of build script targets was
tweaked slightly in a way that causes cargo to stack overflow by accientally
adding a dependency loop. This commit implements one of the strategies discussed
in #5711 to fix this situation.

The problem here is that when calculating the deps of a build script we need the
build scripts of *other* packages, but the exact profile is somewhat difficult
to guess at the moment we're generating our build script unit. To solve this the
dependencies towards other build scripts' executions is added in a different
pass after all other units have been assembled. At this point we should know for
sure that all build script executions are in the dependency graph, and we just
need to add a few more edges.

Closes #5708
alexcrichton added a commit to alexcrichton/cargo that referenced this pull request Jul 13, 2018
Some logic which was tweaked around the dependencies of build script targets was
tweaked slightly in a way that causes cargo to stack overflow by accientally
adding a dependency loop. This commit implements one of the strategies discussed
in rust-lang#5711 to fix this situation.

The problem here is that when calculating the deps of a build script we need the
build scripts of *other* packages, but the exact profile is somewhat difficult
to guess at the moment we're generating our build script unit. To solve this the
dependencies towards other build scripts' executions is added in a different
pass after all other units have been assembled. At this point we should know for
sure that all build script executions are in the dependency graph, and we just
need to add a few more edges.

Closes rust-lang#5708
bors added a commit that referenced this pull request Jul 13, 2018
[beta] Partially revert dep changes in #5651

This is a beta backport of #5711
ehuss added a commit to ehuss/rust that referenced this pull request Jul 18, 2018
- [beta] Partially revert dep changes in rust-lang/cargo#5651 (rust-lang/cargo#5722)
bors added a commit to rust-lang/rust that referenced this pull request Jul 19, 2018
[BETA] Update cargo

- [beta] Partially revert dep changes in rust-lang/cargo#5651 (rust-lang/cargo#5722)
bors added a commit that referenced this pull request Oct 20, 2018
bors added a commit that referenced this pull request Oct 20, 2018
moshg pushed a commit to moshg/rust-std-ja that referenced this pull request Apr 4, 2020
- [beta] Partially revert dep changes in rust-lang/cargo#5651 (rust-lang/cargo#5722)
moshg pushed a commit to moshg/rust-std-ja that referenced this pull request Apr 4, 2020
alexcrichton added a commit to alexcrichton/cargo that referenced this pull request Dec 11, 2020
This commit fixes rust-lang#8966 by updating the unit generation logic to avoid
generating an erroneous circular dependency between the execution of two
build scripts. This has been present for Cargo in a long time since rust-lang#5651
(an accidental regression), but the situation appears rare enough that
we didn't get to it until now!

Closes rust-lang#8966
bors added a commit that referenced this pull request Dec 12, 2020
Fix the unit dependency graph with dev-dependency `links`

This commit fixes #8966 by updating the unit generation logic to avoid
generating an erroneous circular dependency between the execution of two
build scripts. This has been present for Cargo in a long time since #5651
(an accidental regression), but the situation appears rare enough that
we didn't get to it until now!

Closes #8966
@ehuss ehuss modified the milestones: 1.29.0, 1.28.0 Feb 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

multiple rlib candidates passed to rustdoc
6 participants