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

Add rustc-bin-link-arg and rustc-link-arg custom build options #7811

Closed
wants to merge 2 commits into from

Conversation

npmccallum
Copy link
Contributor

@npmccallum npmccallum commented Jan 17, 2020

Both are similar to rustc-cdylib-link-arg. The rustc-bin-link-arg option adds -C link-arg=... on binary targets. The rustc-link-arg option adds -C link-arg=... on all supported targets (currently only binaries and cdylib libraries).

@rust-highfive
Copy link

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @ehuss (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 17, 2020
@npmccallum
Copy link
Contributor Author

@haraldh @cuviper FYI

@npmccallum
Copy link
Contributor Author

@ehuss @alexcrichton FYI, the Enarx project needs this for our microkernels.

@npmccallum
Copy link
Contributor Author

@joshtriplett Tagging you due to your comment here: #6298 (comment)

@joshtriplett
Copy link
Member

joshtriplett commented Jan 18, 2020

I like this very much, and it mostly looks reasonable to me. I don't think it's worth pulling in a new dependency just for this, though. Would you consider making a version that doesn't pull in bitflags, and just uses an enum? I would happily propose such a version for merge. (This introduces new interfaces, so I would want to get team consensus.

@npmccallum npmccallum force-pushed the linkarg branch 3 times, most recently from 66d69b6 to 2a66af5 Compare January 18, 2020 04:23
@npmccallum
Copy link
Contributor Author

@joshtriplett I pushed a version that does not add a bitflags dependency and uses instead a naked enum. I believe it is ready for review.

@joshtriplett
Copy link
Member

Looks good to me. Confirming consensus for the new interface:

@rfcbot merge

@rfcbot
Copy link
Collaborator

rfcbot commented Jan 18, 2020

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period An FCP proposal has started, but not yet signed off. disposition-merge FCP with intent to merge labels Jan 18, 2020
@alexcrichton
Copy link
Member

I think before merging this it would be good to add some tests. Additionally, to confirm, this does not transitively apply throughout the crate graph, right? Only for one particular package?

Also, we initially hesitated in adding this degree of custom link arguments for disambiguation reasons. For example there's no way to pass custom link args to just one binary in a multi-binary project. Similarly can you clarify the use cases of rustc-link-arg? When would you want to pass it to all linked artifacts? Additionally another concern is what to do about other sorts of native binaries. For example what about compiled unit tests? Unit tests compiled by rustdoc? (etc)

I personally think that we'll want to add the ability to pass custom link arguments for just one binary at a time, and then having an escape hatch for "pass this everywhere" also seems fine.

@npmccallum
Copy link
Contributor Author

@alexcrichton

I think before merging this it would be good to add some tests. Additionally, to confirm, this does not transitively apply throughout the crate graph, right? Only for one particular package?

A test for this (and probably other stuff) would probably be wise. I'm not super familiar with the cargo code base. Could you give me a recommendation of where to start?

Also, we initially hesitated in adding this degree of custom link arguments for disambiguation reasons. For example there's no way to pass custom link args to just one binary in a multi-binary project.

This strikes me as an orthogonal concern. Having a "global args" option doesn't preclude having a "local args" option.

Similarly can you clarify the use cases of rustc-link-arg?

We are producing a kernel. The most important use is a linker script to control layout. We also don't want rustc to strip what it thinks are unused symbols.

We are also producing a specialized ELF loader which needs to ensure its own contents are loaded out of the way. We use a linker script for this.

Someone might theoretically propose an alternative interface for just linker scripts. The problem is that they aren't portable at all. So you're already way into the weeds when you have need for one. The bottom line is that if Rust is a systems language (it is), you need to have control over the linker (is this the right place to complain about rustc forcing the use of -Wl,-Bdynamic on glibc?). That you can shoot yourself in the foot with it is your problem. There isn't really a safe way to do this.

When would you want to pass it to all linked artifacts?

A linker script to strip unwanted sections is something that would be fairly commonly used across all linked artifacts.

Additionally another concern is what to do about other sorts of native binaries. For example what about compiled unit tests? Unit tests compiled by rustdoc? (etc)

Although there may be need for this, I'm not aware of any.

I personally think that we'll want to add the ability to pass custom link arguments for just one binary at a time, and then having an escape hatch for "pass this everywhere" also seems fine.

As I mentioned above, nothing in this patch prevents this. I simply consider it out of scope of this patch. It is also perfectly possible to split crates to have one binary per crate to work around this problem.

@joshtriplett
Copy link
Member

We discussed this in the Cargo team meeting, and came to the following conclusions:

  • This needs a bit of further discussion on internals; could you please start a thread on it? (We're not asking for it to be bikeshedded extensively, just asking for it to be looked at by a broader audience to find out if it fits the needs of others.)
  • If you'd like to experiment with this before that discussion concludes, or provide a way for people to experiment with it, then you could make it unstable for a bit, requiring a -Z build-link-arg or similar to enable it. (We could detect that output from the build script, and if the flag is not present, provide a warning if not using the -Z build-link-arg option.) When the discussion concludes, we could drop that flag and process this build script output by default.

Does that sound reasonable, as a path forward?

@joshtriplett
Copy link
Member

Regarding crates with multiple binaries: I personally think it's reasonable to have an option for all binaries; that doesn't preclude us adding another option for a specific binary in the future.

@npmccallum
Copy link
Contributor Author

We discussed this in the Cargo team meeting, and came to the following conclusions:

* This needs a bit of further discussion on internals; could you please start a thread on it? (We're not asking for it to be bikeshedded extensively, just asking for it to be looked at by a broader audience to find out if it fits the needs of others.)

Done: https://internals.rust-lang.org/t/rustc-bin-link-arg-and-rustc-link-arg/11695

* If you'd like to experiment with this _before_ that discussion concludes, or provide a way for people to experiment with it, then you could make it unstable for a bit, requiring a `-Z build-link-arg` or similar to enable it. (We could detect that output from the build script, and if the flag is not present, provide a warning if not using the `-Z build-link-arg` option.) When the discussion concludes, we could drop that flag and process this build script output by default.

If this requires unstable Rust, we don't gain any value from this option since our project does not allow the use of nightly. I also personally dislike this option because it will likely break out of the box rls setups (which will be unable to build projects using this option).

@joshtriplett
Copy link
Member

joshtriplett commented Jan 24, 2020 via email

Both are similar to rustc-cdylib-link-arg. The rustc-bin-link-arg option
adds -C link-arg=... on binary targets. The rustc-link-arg option adds -C
link-arg=... on all supported targets (currently only binaries and cdylib
libraries).
This hides the new rustc-bin-link-arg and rustc-link-arg build script
configuration items behind an unstable flag.
@npmccallum
Copy link
Contributor Author

@joshtriplett I added -Z extra-link-arg.

@joshtriplett
Copy link
Member

Looks great, thank you!

I would be in favor of merging this, for experimentation purposes, and opening a tracking issue for future stabilization.

@alexcrichton @ehuss @nrc Does this seem reasonable, with the unstable option added to gate it?

@npmccallum
Copy link
Contributor Author

npmccallum commented Feb 3, 2020

Some minor notes:

The documentation should move to unstable.md.

No problem.

I would recommend displaying a warning or error if -Zextra-link-arg is not specified and a build script declares one of these options.

I actually tried to do this when I added -Zextra-link-arg and failed. There is no access to the Context from the parsing code. Can you recommend a place in the code that would be natural to issue such a warning?

I think it would be good to display errors or warnings if these options are emitted for packages that do not have a corresponding cdylib/bin. I think this could help avoid confusion.

It seems to me that such a warning would need to have a view of both the parsed options and all artifacts in the current crate. Could you recommend such a place?

This still needs some tests. The CONTRIBUTING docs has information on running tests. The support module has docs on writing tests. The build script tests have lots of examples. I would probably put these in a new file, since build_script.rs is crazy long.

I think it is reasonable to ask for tests. However, I'm not sure how to actually implement a test that can be applied everywhere. A build script, for example, will only work on some linkers. Adding -lLIBRARY will depend on that library in the target and is therefore target-specific. I'm simply not aware of a linker flag that:

  1. Isn't target-specific.
  2. Isn't linker-specific.
  3. Is measurable after a build.

Does anyone have a suggestion?

I don't have a strong preference for choosing which targets this applies to, but I do think it could get unnecessarily complex if more things are piled on afterwards. I think it would be bad if there was a multitude of script commands that all do essentially the same thing, just for different targets. Is the existing syntax extensible to something like rustc-bin-link-arg=BIN_NAME=FLAG?

I honestly don't know. Many linker arguments contain =. So I don't see any unambiguous way to parse this.

It also wasn't really clear to me, was there a specific need for rustc-link-arg? Like, there is a use case where you're building both a binary and cdylib in one package?

A binary that loads plugins (cdylib) would do this. And it may share a linker script to strip out unneeded ELF sections.

@ehuss
Copy link
Contributor

ehuss commented Feb 5, 2020

I actually tried to do this when I added -Zextra-link-arg and failed. There is no access to the Context from the parsing code. Can you recommend a place in the code that would be natural to issue such a warning?

You'll need to pass a bool into the parsing code that indicates whether or not the unstable flag is enabled. Code like custom_build::build_work will need to load that bool into a local variable outside of the closure so that the closure can capture it.

Code like config::target::parse_links_overrides already has access to the Config object, and can just pass the bool from there.

It seems to me that such a warning would need to have a view of both the parsed options and all artifacts in the current crate. Could you recommend such a place?

I think similar to above, inside custom_build::build_work, you can check which targets the package has (inspect unit.pkg.targets()), and store whatever information is needed in a local variable that will be captured in the closure where it can compare with the parsed output. For example, if there is a cdylib option, check if there is a cdylib target.

However, I'm not sure how to actually implement a test that can be applied everywhere. A build script, for example, will only work on some linkers.

I think it would be reasonable to create a very basic test that just makes sure the flags are passed to rustc, but they don't actually have to work. So, for example, have a build script that emits cargo:rustc-link-arg=--this_is_a_bogus_flag, and then run cargo build -v and make sure it gets passed, something like:

// This is just verifying the flag is passed. Since it is a bogus flag, it should cause a failure.
p.cargo("build -v")
    .with_status(101)
    .with_stderr_contains("[RUNNING] `rustc --crate-name foo [..]-C link-arg=--this_is_a_bogus_flag[..]")
    .run();

At least, I think that should fail on all platforms.

@bors
Copy link
Collaborator

bors commented Feb 7, 2020

☔ The latest upstream changes (presumably #7857) made this pull request unmergeable. Please resolve the merge conflicts.

@ehuss ehuss added S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 25, 2020
@LaurentMazare
Copy link

@npmccallum do you still plan on pushing on this? For some context this would also end up being pretty useful to the tch-rs create as we need to pass the --no-as-needed flag to the linker before passing libraries.
Happy to help if there is anything I can do.

@retep998
Copy link
Member

retep998 commented May 13, 2020

For example there's no way to pass custom link args to just one binary in a multi-binary project.

Is this the right place to complain about the fact that cargo still only allows a single cdylib crate in a package?

I disagree with this argument because if we ever do finally support multiple cdylibs in a single package, then the disambiguation problem will already exist with the current rustc-cdylib-link-arg. Therefore whatever solution we come up with for disambiguating cdylibs will apply equally well to rustc-bin-link-arg, so that should not block this feature from landing.

This feature would be extremely useful for Windows users, allowing us more control over linking to manifests, which is a necessary fact of life for any serious production software on Windows.

@joshtriplett
Copy link
Member

@npmccallum I'm still interested in this, if you have the bandwidth for it.

@npmccallum
Copy link
Contributor Author

@joshtriplett I still think this approach is valid for other reasons. However, we ended up taking a different approach. Specifically, we are working to have static-pie working by default on musl. This solves a variety of concerns for us.

@joshtriplett
Copy link
Member

@npmccallum I still have a need for this myself, if you have any plans to implement it in the future.

(Also, note that musl has some performance issues.)

@retep998
Copy link
Member

Windows users have a general need for this and would love to have this.

@luojia65
Copy link

luojia65 commented Jun 27, 2020

rustc-bin-link-arg could be useful when building runtime libraries for embedded systems. If I use

println!("cargo:rustc-bin-link-arg=-Tscript.ld")

in build.rs file of the library crate, my binary crate I depend on library crate; when I'm building the binary crate, this linker script script.ld would applied to the full binary. That would be very convenient for embedded application authors.

@reitermarkus
Copy link
Contributor

I still have a need for this myself, if you have any plans to implement it in the future.

@joshtriplett, I took a stab at finishing implementing this in #8441.

npmccallum added a commit to enarx-archive/sev that referenced this pull request Aug 31, 2020
Cargo currently cannot handle our needs. It cannot build static binaries
with the standard entry point on glibc. Nor can it build cdylib or
static binaries with a custom entry point on musl. Nor can it build
different crates for different targets in the same workspace.

While we work to resolve these issues in upstream cargo, we need to
ditch the top-level workspace for now. We can revisit this at a later
time.

For more background, see:

  rust-lang/cargo#7811
  rust-lang/cargo#7804
  https://github.com/rust-lang/rfcs/pull/2735/files
bors added a commit that referenced this pull request Nov 18, 2020
Finish implementation of `-Zextra-link-arg`.

This is a continuation of #7811, which adds tests and a warning if the `-Zextra-link-arg` flag is not specified. Also moved the documentation into `unstable.md`.
@ehuss
Copy link
Contributor

ehuss commented Nov 23, 2020

Closing, as I believe this is now implemented via #8441. Thanks @npmccallum for getting it started!

@ehuss ehuss closed this Nov 23, 2020
@rfcbot rfcbot removed proposed-final-comment-period An FCP proposal has started, but not yet signed off. disposition-merge FCP with intent to merge labels Nov 23, 2020
bors added a commit that referenced this pull request Jul 26, 2021
Stabilize the rustc-link-arg option

This change removes the unstable option (tracked by #9426) and unconditionally accepts additional linker arguments (as implemented in #7811 and #8441). Documentation is moved from unstable to what appeared to be the correct location.

I am not aware of any significant concerns with the option and it appears consistent with some other existing stable linker options.

Please let me know if this is not the appropriate process or if there is anything that I am missing from the PR.
bradjc added a commit to tock/tock that referenced this pull request Feb 9, 2022
Per the comment for `RUSTC_FLAGS_FOR_BIN`:

> Ultimately, this should move to the Cargo.toml, for example when
> rust-lang/cargo#7811 is merged into Cargo.

this moves setting the linker script search path to build.rs, which is
enabled by the addition of the `cargo:rustc-link-arg=FLAG` instruction
for build scripts.

This removes the need to special case the -L flag so that it is only
used when creating a binary and not when compiling dependencies, since
that is exactly what the new build script instruction handles.
@bradjc bradjc mentioned this pull request Feb 9, 2022
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: The marked PR is awaiting some action (such as code changes) from the PR author. T-cargo Team: Cargo
Projects
None yet
Development

Successfully merging this pull request may close these issues.