How do I develop a binary and library together if I intend to publish both on crates.io?

I am trying to develop a binary. I believe that a significant piece of its functionality is reusable and belongs in its own library. As a result, I intend to develop binary crate B and library crate L, where B has a dependency on L.

However, I'm not sure how to make the dependencies work out. For development, I need to have B depend on L with a path dependency, because L hasn't been pushed to crates.io yet. However, when I push them to crates.io they should be pushed as separate packages, and B's package should depend on L via a crates.io dependency.

How should I develop these crates?

What I have tried:

I put both crates into a workspace for local development, then used [patch.crates-io] in the workspace to rewrite B's dependency on L into a path dependency. This also has compile time benefits, and lets me run all the repository's tests simultaneously. However, if I try to set profile settings on B, I get a warning that I should set the profile settings in the workspace:

warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   /home/jrvanwhy/gitree/gitree/Cargo.toml
workspace: /home/jrvanwhy/gitree/Cargo.toml

However, I want those profile settings to apply when I publish B, where the workspace won't be present, so I don't want to specify them in the workspace.

If you intend on them being separate crates, they must be separate packages (2 separate Cargo.toml manifests). However, you could simply add a [[bin]] section to your manifest and the package can be both a library and an executable.

1 Like

I'm confident they should be separate crates.

You can set the package's profiles for when they are on crates.io, even if they are ignored. In the workspace it is possible to set override profiles per package.

[workspace]
# ...

[profile.dev.package.package_a]
# ... Profile settings for package_a ...

That doesn't seem to silence the warning, however, which makes me think I'm doing something wrong.

Nah, Cargo is simply wrong in this case. It doesn't take into account that the other sub-crate could be used separately outside of the workspace.

1 Like

An alternative here is to specify both a path and a version dependency. Cargo will automatically switch to the version one for publishing, while using the path when compiling locally.

Something like

[dependencies]
L = { version = "1.0", path = "./L" }

or alternatively

[dependencies.L]
version = "1.0"
path = "./L"

Projects with monorepos for many crates, like serde, often use this. See serde's Cargo.toml dependency on serde_derive

1 Like

Thank you daboross, that's what I was looking for! I didn't realize that Cargo would accept that.

I removed the workspace and instead used a Cargo config to combine the target directories between B and L, which gives me part of the advantage of a workspace.

1 Like

Glad that worked!

Out of curiosity, why remove the workspace too? I've found that they're generally well supported by tooling and IDEs, and don't have many downsides.

For situations like this, if you're OK with putting them both in subdirectories, having one workspace which just encompases all of them but isn't a crate itself should "just work", and won't require any maintenance (will target symlinking persist through cargo clean?)

I removed the workspace to remove the warning about the "ignored" profile in B. I value a no-warnings build more than I value the workspace.

Oh right! That makes sense.

One of us should probably be a bug report to Cargo for this incorrect warning! I wonder what the best way to handle that would be - since they should warn if the crate isn't published. Eh - either way, it's a problem.

Edit: filed https://github.com/rust-lang/cargo/issues/8264.

@jvranwhy pinging you since the team replied to the bug report I made.

If you have time, could you comment on https://github.com/rust-lang/cargo/issues/8264#issuecomment-634945059?

Quoted here:

The team discussed this a bit. We feel like it would probably be best if cargo publish would copy the profile from the workspace into the member package. Does that sound like it would resolve your use case?

I have definitely felt uneasy with that warning, since it cannot be silenced. However, I'm also a little uncomfortable with adding config and machinery for silencing it (just to avoid complexity).

If you have a github account, it would be ideal if you commented on the github issue. If not, I could also relay anything you say here to them.

Thank you! I just followed up on that issue.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.