21

My project A depends on library B that depends on library C.

Library B sets the dependency version to "*" (any) so Cargo will download the latest version of C.

How can I instruct Cargo to build library B using a specific version of library C?


I'm trying to build iron.

The current build is failing, but I can see the last successful build, including Rust and Cargo package versions.

I downloaded the specific Rust nightly used in the build and I've set the the direct dependencies of iron to the ones used in that build by editing Cargo.toml:

[dependencies]
hyper = "0.0.18"
typemap = "0.0.5"
url = "0.2.9"

rust-serialized, which is a dependency of the url and time packages, is downloaded as the latest version which doesn't compile with my specific Rust version.

If I used the version used in the Travis build above I'm sure it will compile successfully.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
diogovk
  • 2,108
  • 2
  • 19
  • 24
  • 1
    As an aside, you shouldn't manually edit `Cargo.lock`. Humans edit `Cargo.toml`, Cargo edits `Cargo.lock`. – Shepmaster Jan 04 '15 at 20:49
  • Yeah, but if I had the exact Cargo.lock which generated that build, I should be able to generate the exact same build. So ideally there should be an way for me to tell cargo to build a Cargo.lock exactly like the one that generated that build, including dependency of dependency's version. – diogovk Jan 04 '15 at 21:27
  • @John do you have a specific set of crates that exhibit the problem today? It's very hard to test with the 6-year-old examples in the post. – Shepmaster Aug 30 '21 at 16:51
  • Yes, here is my example: I have a crate that depends on zstd. zstd depends on zstd-safe. zstd-safe depends on zstd-sys. My crate gets linked into a C++ binary that also links zstd as a C library. The versions of the zstd C library and the zstd vendored by zstd-sys must match exactly, or I get [link errors](https://github.com/gyscos/zstd-rs/issues/58). So I want to pin an exact version of zstd-sys. – John Aug 30 '21 at 16:58

2 Answers2

12

Manual editing

You can check out Iron, modify Cargo.toml to specify versions (as you have already done). Then you repeat the process, checking out url, modifying its Cargo.toml, then make sure you are using your version of url in Iron's Cargo.toml. Rinse and repeat.

Patch overrides

From the Cargo docs:

The [patch] section of Cargo.toml can be used to override dependencies with other copies. The syntax is similar to the [dependencies] section:

[patch.crates-io]
foo = { git = 'https://github.com/example/foo' }
bar = { path = 'my/local/bar' }

Sources can be patched with versions of crates that do not exist, and they can also be patched with versions of crates that already exist. If a source is patched with a crate version that already exists in the source, then the source's original crate is replaced.

Path overrides

From the Cargo docs:

Sometimes you're only temporarily working on a crate and you don't want to have to modify Cargo.toml like with the [patch] section above. For this use case Cargo offers a much more limited version of overrides called path overrides.

Path overrides are specified through .cargo/config.toml instead of Cargo.toml. Inside of .cargo/config.toml you'll specify a key called paths:

paths = ["/path/to/uuid"]

Specific versions

You might be able to simply specify versions (or SHA hashes) for each dependency that you know works with your Rust version. Cargo should be able to resolve the transitive dependencies and lock you to a previous version if there is one that fits all the requirements.

Alternatively, you can use cargo update -p somecrate --precise major.minor.patch to specify the exact version of a crate and record it in your Cargo.lock.

This may not work in all cases; Rust can have multiple versions of the same library compiled into one binary. That would mean that there's no one place you can specify a version that applies all over.

Addressing the bounty

John adds:

I have a crate that depends on zstd. zstd depends on zstd-safe. zstd-safe depends on zstd-sys. My crate gets linked into a C++ binary that also links zstd as a C library. The versions of the zstd C library and the zstd vendored by zstd-sys must match exactly, or I get link errors. So I want to pin an exact version of zstd-sys

This case can follow the "specific versions" example above, but because zstd-sys uses a links key, there can only ever be exactly one of that crate in the entire crate graph. That means you can add zstd-sys to your top-level dependencies and feel comfortable that Cargo will complain if a conflicting version is introduced:

[dependencies]
zstd = "0.9.0"
zstd-sys = "=1.6.1"

If I edit this to specify version 1.6.0, I get an error:

error: failed to select a version for `zstd-sys`.
    ... required by package `zstd-safe v4.1.1+zstd.1.5.0`
    ... which is depended on by `zstd v0.9.0+zstd.1.5.0`
    ... which is depended on by `so v0.1.0 (/private/tmp/so)`
versions that meet the requirements `=1.6.1` are: 1.6.1+zstd.1.5.0

the package `zstd-sys` links to the native library `zstd`, but it conflicts with a previous package which links to `zstd` as well:
package `zstd-sys v1.6.0+zstd.1.5.0`
    ... which is depended on by `so v0.1.0 (/private/tmp/so)`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • FWIW, this would require manually downloading corresponding package versions and extracting them somewhere. – Vladimir Matveev Jan 04 '15 at 20:45
  • @VladimirMatveev yeah, good point. I reordered my two suggestions as "specify all the versions" is probably a better solution (if it works in this case) – Shepmaster Jan 04 '15 at 20:50
  • I know the repository for rustc-serialize, but I don't know which commit generated the package version 0.1.5. How do I download the package source for a specific version? How do I know which commit generated a specific package version? – diogovk Jan 04 '15 at 21:10
  • Normally I'd say to just grab the git tag for `0.1.5`, but it seems they aren't pushing those. That's no good... That being said, you can check [the history for that file](https://github.com/rust-lang/rustc-serialize/commits/master/Cargo.toml), and you can spelunk a bit and get to [the commit that bumped the version number](https://github.com/rust-lang/rustc-serialize/commit/523f31969ffbaae9bd71760bc1f96013382142aa) – Shepmaster Jan 04 '15 at 21:15
  • Yeah, sure I can guess the commit which generated the package. But by cargo's philosophy I should be able to generate the exact same build. If I guessed the commit there's no guarantee that it would be the exact build. So it would solve my problem, but it's not ideal. – diogovk Jan 04 '15 at 21:21
  • Well, you can only generate the same build if you have the same `Cargo.lock`. In this case, Iron is a library, so it doesn't maintain a lockfile and it's up to the end application(s) to create a lockfile. The CI build isn't the ideal place to be starting this, but neither is it fun trying to keep pace with a language going to 1.0! – Shepmaster Jan 04 '15 at 21:47
  • I'm sorry but it "feels wrong" to me. If I could go back in time to the state crate.io was in the date of the compilation I could get a build running. But because there were updates in packages in crate.io I no longer can, even though crate.io contains the packages in the specific version I want. It feels to me like a "regression" that is not really cargo's user's fault. The user is then obligated to download and compile each of those packages, and set the path in .cargo/config. This would be useful even after rust is stabilized. – diogovk Jan 04 '15 at 22:46
  • FWIW, the "first idea" works perfectly, and is the recommended way if you have multiple libraries that depend on the same crate that need to interoperate. – rix0rrr Apr 10 '16 at 17:16
  • I'd award the bounty to this answer with the following improvements: * Add an introduction to contextualize the details. Something like: "Cargo does not have a feature specifically addressed to the use case of pinning indirect dependencies. However, the following techniques may help in certain circumstances." * Lead with the "Specific versions" / "Addressing the bounty" sections, as (when applicable), it seems preferable to forking dependencies. * Explain "Patch overrides" / "Path overrides" in greater detail. Are these what to do after manual editing, to use your manual edits? – John Sep 04 '21 at 19:26
0

Since the anwser provided, Cargo has added the [patch] section to the manifest which allows you to do this use case.

overriding-dependencies

Rene
  • 466
  • 5
  • 12
  • 23
    This answer would be more helpful if it included an example. The use case as mentioned in the question is not covered in the official documentation. – Thomas Oct 22 '20 at 14:50
  • 2
    I could not figure out how to use `[patch]` to accomplish this. – John Aug 30 '21 at 16:40
  • 3
    yeah, it is non-obvious how `[patch]` solves this problem, at least from the documentation – nrdxp Nov 08 '21 at 19:14