Skip to content

cargo-0.15.0 --frozen: Why does it try to query the registry? All dependencies are available locally... #3476

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

Closed
daym opened this issue Dec 30, 2016 · 13 comments

Comments

@daym
Copy link

daym commented Dec 30, 2016

I'm trying to use cargo in an offline manner. For that, I'm trying to build c-vec using cargo build --frozen with the following (modified) Cargo.toml :

[package]
name = "c_vec"
version = "1.0.12"
authors = ["Guillaume Gomez <[email protected]>"]

description = "Structures to wrap C arrays"
repository = "https://github.com/GuillaumeGomez/c_vec-rs.git"
readme = "README.md"
keywords = ["c", "vec", "c_vec", "array"]
license = "Apache-2.0/MIT"

[lib]
name = "c_vec"

[dev-dependencies]
libc = "0.2.16"

[replace]
"libc:0.2.16" = { path = "/gnu/store/qi3fp02sdpc2pp3vjlv3szcvynfkwp1y-rust-libc-0.2.16/rustsrc" }

Where /gnu/store/qi3fp02sdpc2pp3vjlv3szcvynfkwp1y-rust-libc-0.2.16/rustsrc contains: Cargo.lock Cargo.toml src/ where Cargo.toml contains:

[package]

name = "libc"
version = "0.2.16"
authors = ["The Rust Project Developers"]
license = "MIT/Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-lang/libc"
homepage = "https://github.com/rust-lang/libc"
documentation = "http://doc.rust-lang.org/libc"
description = """
A library for types and bindings to native C functions often found in libc or
other common platform libraries.
"""

[features]
default = ["use_std"]
use_std = []

[replace]

and Cargo.lock contains:

[root]
name = "libc"
version = "0.2.16"

And still it's trying to get something from crates.io:

$ export RUST_LOG=debug
$ cargo build --frozen
DEBUG:cargo::build: executing; cmd=cargo-build; args=["cargo", "build", "--frozen"]
DEBUG:cargo::core::workspace: find_root - trying /x/home/dannym/x/rust-x/Cargo.toml
DEBUG:cargo::core::workspace: find_root - trying /x/home/dannym/x/Cargo.toml
DEBUG:cargo::core::workspace: find_root - trying /x/home/dannym/Cargo.toml
DEBUG:cargo::core::workspace: find_root - trying /x/home/Cargo.toml
DEBUG:cargo::core::workspace: find_root - trying /x/Cargo.toml
DEBUG:cargo::core::workspace: find_root - trying /Cargo.toml
DEBUG:cargo::core::workspace: find_members - only me as a member
DEBUG:cargo::core::registry: load/missing  file:///x/home/dannym/x/rust-x/ngry89bb50arnll7f6pgzn8xvdmmw3y5-rust-c-vec-1.1.0-checkout
DEBUG:cargo::sources::config: loading: file:///x/home/dannym/x/rust-x/ngry89bb50arnll7f6pgzn8xvdmmw3y5-rust-c-vec-1.1.0-checkout
DEBUG:cargo::core::resolver: initial activation: c_vec v1.0.12 (file:///x/home/dannym/x/rust-x/ngry89bb50arnll7f6pgzn8xvdmmw3y5-rust-c-vec-1.1.0-checkout)
DEBUG:cargo::core::registry: load/missing  registry https://github.com/rust-lang/crates.io-index
DEBUG:cargo::sources::config: loading: registry https://github.com/rust-lang/crates.io-index
DEBUG:cargo: handle_error; err=CliError { error: Some(ChainedError { error: failed to load source for a dependency on `libc`, cause: ChainedError { error: Unable to update registry https://github.com/rust-lang/crates.io-index, cause: attempting to make an HTTP request, but --frozen was specified } }), unknown: false, exit_code: 101 }
error: failed to load source for a dependency on `libc`

Caused by:
  Unable to update registry https://github.com/rust-lang/crates.io-index

Caused by:
  attempting to make an HTTP request, but --frozen was specified

Why?

Note that I installed a lock file manually for libc, too.

To give a little bit better overview - we're trying to integrate Rust into the Guix System Distribution. Guix has reproducible builds already so we'd rather if Cargo didn't load random things from the internet and just let us use our own packages.

For comparsion, when I allow it to query the registry, it will still not pick up the rust-libc from /gnu/store/qi3fp02sdpc2pp3vjlv3szcvynfkwp1y-rust-libc-0.2.16/rustsrc but rather the one from crates.io . I can't find out why using the debugging log above.

strace shows: When I use --frozen, it never tries to access /gnu/store/qi3fp02sdpc2pp3vjlv3szcvynfkwp1y-rust-libc-0.2.16/rustsrc . When I don't use --frozen it does access /gnu/store/qi3fp02sdpc2pp3vjlv3szcvynfkwp1y-rust-libc-0.2.16/rustsrc/src/lib.rs successfully.

So I'd say it's checking the replacements too late - among other things.

How can I debug this further?

@daym
Copy link
Author

daym commented Dec 30, 2016

(See also #2111 )

@alexcrichton
Copy link
Member

Hello and thanks for the report! This currently looks like Cargo's working as intended, but I may be able to help clarify a few pieces. You may also be interested in the offline FAQ question.

Cargo does indeed support local vendoring and avoiding network requests, so you should be completely covered for your own use case. Precisely how this is done, however, is quite important. In general Cargo follows a simple policy of:

  • If you cargo build and then cargo build again without changing Cargo.toml, then the second build is guaranteed to never touch the network.

That basically means that Cargo aggressively caches projects/metadata locally as well as creates a deterministic dependency graph through the Cargo.lock file emitted on the first build.

As a second point, Cargo supports first-class vendoring where you also don't have to modify Cargo.toml.

So with that in mind, what's actually happening here (it looks like) is that you're working with Cargo that doesn't use source replacement and also doesn't have any caches previously. It looks like you may also not have a Cargo.lock to begin with. When you first issue cargo build then Cargo needs to generate a Cargo.lock, and to do so it needs to fetch the registry index and information to learn about dependencies. Note that [replace] is applied after this metadata is loaded. We have to figure out what version of libc you're depending on before it can be overridden!

Here's what I would recommend for your use case:

  • Always have a Cargo.lock if you want reproducible builds. This should exist and be checked into the source trees you're building. All of Cargo's support for reproducible builds stems from this, so it is quite important.
  • Use source replacement instead of [replace] in manifests. This allows you to not have to modify source code, and you can maintain your own registry locally of curated versions if you'd like. This will guarantee that Cargo never touches the network as the local replacement is used in lieu of crates.io

Does that make sense? Would that work for you locally?

@daym
Copy link
Author

daym commented Dec 30, 2016

Thanks for your answer! I'll read up on the matter.

In my example (which is just a small proof-of-concept) libc does have a Cargo.lock , created by cargo on the libc build.

c-vec, which is a small library that uses libc, doesn't have a Cargo.lock yet.

What I would have expected for c-vec: Cargo looks up the dependencies' source directories (however it does that), picks up the "Cargo.lock"s there (for recursion into the dependencies) and builds c-vec. It then creates a Cargo.lock for c-vec containing all the direct and indirect dependencies so we don't have to search for them all over again (for eventual clients of c-vec).

The dependencies of c-vec are listed in c-vec's Cargo.toml . It says right there what and which version it needs.

Guix builds each package in a chroot so it is ensured that it can't pick up stuff it's not supposed to pick up.

When you first issue cargo build then Cargo needs to generate a Cargo.lock, and to do so it needs to fetch the registry index and information to learn about dependencies.

I don't understand yet why it needs to fetch the registry index for anything. The dependencies are listed right in the Cargo.toml . But I guess it has to do with the fact that the dependency is a semver and the replacement is a specific version or something. I'd want to provide this semver by that directory, though.

In any case, I'll try source replacement. Seems that's for replacing all the libraries at once, though. Different libraries are in different Guix packages, though. So I'd like to have something like replace, just with semvers.

Also, cargo-vendor has like 30 dependencies. How are we supposed to bootstrap that?

Also, trying to build it manually, it failed because it can't find "cc" for compiling openssl_shim.c . That's because the compiler is called "gcc". So now I set the environment variable "CC" now.

Hmm... if really necessary, we can just bind mount lots of things into each package's source tree. Icky.

@alexcrichton
Copy link
Member

To clarify, Cargo does not use Cargo.lock from dependencies, only from the top-level crate. It's ignored in dependencies. Additionally Cargo needs to always generate a Cargo.lock on every build, representing a valid build plan. And yeah because versions in Cargo.toml are just semver constraints, Cargo needs to look at the registry to see what versions it has available to match that up. Note that, again, with source replacement this happens locally and not remotely.

You shouldn't need to actually package up cargo vendor, it just produces a directory you can place inside your chroot. The cargo vendor tool itself does not need to be available at build time.

@daym
Copy link
Author

daym commented Dec 31, 2016

We are trying that now. Thanks.

It seems that crates (example: https://crates.io/api/v1/crates/fs2/0.4.0/download ) don't actually contain .cargo-ok and .cargo-checksum.json - so we still have to generate them somewhen. It would be nice if they already did contain them... [also, there's a checksum of the crate file, named "package"]; but we'll just generate them when installing.

Basically, we already hash packages by content and use the hash value as key to find them, and a versioning system. There's a /gnu/store directory, mounted read-only, containing all the packages in a subdirectory whose name contains the hash. There's already isolation and there's already a feature that allows the system to know what is installed (profile directories linking to the right files in the right /gnu/store directories - only for the toplevel the user actually sees. Other things in store items directly refer to other store items). We'd like not to have all this stuff again redundantly in Cargo, especially since many FFI packages require system packages anyway. On the other hand, if the user wants to use cargo to install custom packages, that should be fine, too. So cargo should somehow check what packages are already installed system-wide by the package manager (i.e. available in the profile). If they aren't yet, fine, install your own. Otherwise use the installed package. As I said, I think Cargo has to do that for the system packages (for example gtk itself etc) anyhow.

Does this work?

We don't have much Rust experience yet, so I'm not sure if there's something like /usr/lib/python3/site-packages for Rust.

@daym
Copy link
Author

daym commented Jan 2, 2017

Hmm we are much farther now. We have a directory with all the dependencies and specify it as replace-with directory. However all the dependencies include C dependencies (sometimes) and so we get

error: failed to load source for a dependency on `git2`

Caused by:
  Unable to update registry https://github.com/rust-lang/crates.io-index

Caused by:
  failed to update replaced source `registry https://github.com/rust-lang/crates.io-index`

Caused by:
  failed to read `/gnu/store/dcsfk23iwhhsix5icr9lxdcwrd2qb8ks-icu4c-55.1/Cargo.toml`

Caused by:
  No such file or directory (os error 2)

The C directories contain neither a .cargo-checksum.json nor a Cargo.toml - shouldn't it have ignored these?

In any case, now we seperated dependencies written in Rust from dependencies written in something else (sigh) and it's working now.

Thanks for your help!

That means we have a cargo-build-system in Guix. We'll package programs written in Rust real soon (tm).

Do you think a local-registry approach would work too?

@alexcrichton
Copy link
Member

@daym hm are you using cargo vendor to create vendored directories? Files like .cargo-checksum.json don't exist in crate files, but they're generated by cargo vendor.

@alexcrichton
Copy link
Member

Oh and cargo vendor and cargo local-registry should basically be equivalent for this use case (just FYI)

@daym
Copy link
Author

daym commented Jan 4, 2017

Yes, we're using something like cargo vendor to create vendored directories (I just reimplemented it in Guile because I was too lazy to bootstrap cargo-vendor for this proof-of-concept :P). I'm just saying it would be even nicer if files like .cargo-checksum.json were part of the crate files - it's not like they are system-dependent or anything and it would be easier to set up a vendored directory then. I understand that there's a problem with the "package" entry self-referencing, though.

About cargo local-registry: Yeah, we Guix devs have quite some discussion about whether cargo vendor or cargo local-registry should be used for our system. Are there any trade-offs?

@alexcrichton
Copy link
Member

Vendor vs local-registry is basically up to you. One deals with source files and one deals with packed crate files. They're mostly equivalent and it just sorta depends on what you'd like to do. In theory local-registry scales better, but likely not at a point where it ever matters if the number of crates is small.

@sanmai-NL
Copy link

To clarify: cargo vendor is an extension to Cargo.

@cyplo
Copy link

cyplo commented Sep 4, 2017

How it's going ? Were you able to integrate it ? Thanks !

@daym
Copy link
Author

daym commented Sep 4, 2017

Yes, we are using "cargo vendor"-like files with a homebrew checksum generator now in the released distribution. It works fine and makes bootstrapping from source a little less bad. Thanks!

@daym daym closed this as completed Sep 4, 2017
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

No branches or pull requests

4 participants