rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.59k stars 2.39k forks source link

Per crate build target in workspace #7004

Closed jonblack closed 3 years ago

jonblack commented 5 years ago

Describe the problem you are trying to solve

I have a project with two crates in a workspace. The first crate should compile to asmjs with --target asmjs-unknown-emscripten and the result is used in the second crate, which should compile to the native binary format (default target).

When I run cargo build in the root crate that contains the workspace configuration in Cargo.toml, it builds both crates using the default target.

Describe the solution you'd like

I'd like to be able to specify the target for each crate. I'm not sure if this belongs in each crates' Cargo.toml or in the root crate's Cargo.toml.

j16r commented 5 years ago

Are you setting the target with a .cargo/config file in the second crate?

jonblack commented 5 years ago

If I add a .cargo/config in the first crate with the contents:

[build]

target = "asmjs-unknown-emscripten"

And then run cargo build from the root crate, I get the same compilation failure. It seems cargo ignores this.

kaimast commented 4 years ago

I have a similar problem, where I want to build some parts of my workspace as an SGX enclave (using the x86_64-fortanix-unknown-sgx target) and other parts as a regular binary.

Is there a hack-ish way to do this right now, that is simpler than writing a bash script that invokes cargo build in every crate of the workspace?

j16r commented 4 years ago

@kaimast I'm doing this currently: https://github.com/smartcontractkit/chainlink/blob/develop/sgx/Makefile

autodidaddict commented 4 years ago

If I add a .cargo/config in the first crate with the contents:

[build]

target = "asmjs-unknown-emscripten"

And then run cargo build from the root crate, I get the same compilation failure. It seems cargo ignores this.

I have this exact same problem. Cargo is ignoring the .cargo/config files when I build, which makes it difficult to set up a CI build.

leordev commented 4 years ago

I'm experiencing this problem a lot because my workspace has wasms and non-wasms crates...

diondokter commented 4 years ago

I ran into this problem too and it cost me 4 hours before I understood what was going on.

My situation: I'm have an embedded project. My binary is in one workspace member and there are two no_std libraries in the workspace as well. The libraries are made for specific chips that are on board of the hardware.

The solution would be that every member can have its own .Cargo/config and when that isn't found, it will then go look in the parent directories.

haraldh commented 4 years ago

The solution would be that every member can have its own .Cargo/config and when that isn't found, it will then go look in the parent directories.

Yeah, one would logically think that is the current case... General common config in the topdir.. specialized config in the subdirs.

diondokter commented 4 years ago

Yeah, one would logically think that is the current case... General common config in the topdir.. specialized config in the subdirs.

They do work if you call cargo build in the workspace member itself. But when building from the workspace root, they get ignored.

I have no idea if it's trivial to fix that. It may also break existing workspaces.

bkolobara commented 4 years ago

I also just run into this problem trying to set up a WASM sub crate. I think I will just use a Makefile to manually run the build per crate instead. But it is a bit inconvenient to do so.

whizsid commented 4 years ago

I have faced this issue today. I want to compile one crate with musl and the other crates with glibc. Now I am compiling with using separate cargo commands.

kristoff3r commented 4 years ago

I also faced this issue with a workspace of mostly normal crates and 2 wasm crates. It would be very convenient if the .cargo/config target setting was respected by the workspace.

mikevoronov commented 4 years ago

So, today and I have stumbled with the same issue with wasm and non-wasm crates in one project.

devsnek commented 3 years ago

Just ran into this. This seems pretty key as long as rust considers wasm a first class target.

TheAifam5 commented 3 years ago

Same problem. Any workaround? I really need it since I build stuff around efi and bios.

Use case:

repi commented 3 years ago

One workaround, albeit not a great one, but one we do use is to divide up the workspace into multiple workspaces, one per target, have them in different folders and have a .cargo/config in each that picks the target to build for.

It is not great though, you don't have a single workspace to build anymore. but doing cargo build inside each separate folder and workspace does build and run on the right target.

TheAifam5 commented 3 years ago

I've decided to go with rust-make and remove Cargo.toml workspace file and use rust-make's internal one. So I have Makefile.toml and just execute "cargo make" to build them all, it goes to each folder and executes "cargo build"

Richard-W commented 3 years ago

I have looked into this. cargo completely ignores config files in member directories if cargo is called from the workspace root. I came up with 3 different options to implement per-crate build targets but sadly none of them are trivial:

1. Parse member's .cargo/config.toml

Not a good idea. While this would work for some keys like build.target it is not a good idea for others like build.jobs. cargo could ignore such definitions in workspace members of course but it seems very hacky.

2. Dedicated .cargo/member.toml configuration

Special configuration file with a scope limited to a single member crate and a limited set of configuration values like build.target. Reusing config file parsing code from cargo would be difficult though since it all pretty much depends on Config (e.g. ConfigRelativePath::resolve_path takes &Config as its argument).

Example
# workspace/foo/.cargo/member.toml
[build]
target = "wasm32-unknown-unknown"

3. [member.'name'] table in toplevel .cargo/config.toml

Similar to .cargo/member.toml proposal but all information goes into the toplevel .cargo config.toml

Example
# workspace/.cargo/config.toml
[build]
# Default target
target = "x86_64-unknown-linux-gnu"

[member.foo]
# Override default target for `foo`
target = "wasm32-unknown-unknown"

I have implemented proposal 3 (here) and it seems to work fine (all tests pass). It's not an elegant implementation though and it feels pretty "wedged in".

TheAifam5 commented 3 years ago

IMHO much better idea would be to allow use of the workspace’s config as a base, where each package have the ability to overwrite properties in context of a package.

I am not aware how it works internally but if there is only once „Config” instance, maybe building „Config” for each package and switching it would also be an option?

Richard-W commented 3 years ago

IMHO much better idea would be to allow use of the workspace’s config as a base, where each package have the ability to overwrite properties in context of a package.

I am not aware how it works internally but if there is only once „Config” instance, maybe building „Config” for each package and switching it would also be an option?

I started work on this with a similar approach but I had to abandon it. The current configuration system in cargo is just not designed to deal with multiple configs in one run. Changing this would be a major refactoring and would probably lead to backwards-compatibility problems too.

Using [member.'crate'] tables just circumvents the problem by not challenging cargo's assumption that there is only one configuration per run.

kaimast commented 3 years ago

It is great that progress is made on this issue :)

I started work on this with a similar approach but I had to abandon it. The current configuration system in cargo is just not designed to deal with multiple configs in one run. Changing this would be a major refactoring and would probably lead to backwards-compatibility problems too.

I think eventually this refactor needs to happen or the Rust community has to decide on a meta build tool that is used on top of cargo. Many projects (especially those involving WASM or SGX) use Makefiles right now to circumvent these limitations, which is somewhat ugly and does not work on all platform.

lf- commented 3 years ago

This thread has a proposal on a fix to this, discussed by the cargo team (was linked on #8797): https://internals.rust-lang.org/t/proposal-move-some-cargo-config-settings-to-cargo-toml/13336

It appears the current status is that PRs are welcome for adding the properties discussed there to Cargo.toml.

Ekleog commented 3 years ago

Hey! In case people are interested, I've just submitted https://github.com/rust-lang/cargo/pull/9030 which should implement the solution discussed in the irlo thread listed just above.

TheAifam5 commented 3 years ago

Additionally would be awesome to have in Cargo.toml possibility to define triplets for each [[bin]] :) Just an idea.

tobin-crypto commented 3 years ago

@kaimast I'm doing this currently: https://github.com/smartcontractkit/chainlink/blob/develop/sgx/Makefile

This link is broken.

j16r commented 3 years ago

@tobin-crypto try this one https://github.com/smartcontractkit/chainlink-gh-actions/blob/fff5f02271b84e2ef40f437b23dd0b5983dc88e8/core/sgx/Makefile

ryanc-me commented 2 years ago

Thanks for your work @Ekleog, this was very helpful in a project I'm working on!

In case any newbies (like me) come across this in the future, and are confused about how to use the fix, it's documented here: https://doc.rust-lang.org/cargo/reference/unstable.html#per-package-target

Google is still showing this issue as the 1st result, so the doc link may be helpful.

Trolldemorted commented 1 year ago

@ryanc-me are there any solutions for stable rust?

mikkelens commented 1 year ago

I would also love to know which solution is recommended for stable rust.