Making libraries for parts of rustup

For various reasons, I’m writing my own rustup-alternative named rustbud. While some things will necessarily differ (otherwise it wouldn’t be a separate tool!), rustup and rustbud will likely have a lot of things in common, like “reading a channel manifest file” and “extracting installable files from an archive”. These could conceivably become library crates shared by rustup, rustbud, and any other tools that might arise in future.

Recently, I published a crate called rust_release_channel which contains structs representing the contents of release channel manifests, along with all the magic Serde juice to read and write them. Compared to the equivalent code in rustup, there’s more documentation, stricter types (actual url::Url and chrono::NaiveDate fields instead of strings everywhere), and validation returns all the problems found at once, instead of just the first.

I don’t expect the rustup maintainers to rush out and add a dependency on a third-party crate with a single release and 12 downloads, but is there any appetite at all for sharing crates for this kind of toolchain package handling? If so, would rust_release_channel (in its current state, or with changes) be useful? What could I do to make it more suitable?

9 Likes

A minor update: I’ve just now released the first version of rust_release_artefact, a crate for extracting and installing the artefacts that the release channel manifest points to.

1 Like

Another update: I released another library, guess_host_triple (extracted from the relevant part of rustup), and I’ve written an article describing how to use all these libraries together to create The Simplest Rust Toolchain Installer.

2 Likes

Can you elaborate on your various reasons? I’m really curious to hear more about them.

As I understand it, there is a plan to move more rustup functionality into Cargo, so these libraries might come in useful there. It’d also be good to take your reasons into account while doing this work.

I go into more detail about the reasons in the rustbud README, but here’s an analogy:

The Python world has a system for isolating dependencies called “virtualenvs”: when you install a library in a virtualenv, it’s only available to programs and other libraries in that same virtualenv. The downside is that while Python packages can declare dependencies, they aren’t automatically synced with the virtualenv contents. It’s very easy to manually install a library to try it out, modify your code to use it, but forget to add it to the dependency list. When the next person checks out the project and tries to run it, boom, ImportError.

When I discovered Rust, Cargo was such an amazing upgrade from virtualenvs. You can’t use a library without first recording it in Cargo.toml, so your dependencies are always up-to-date. A whole class of problems eliminated, with zero additional effort!

Then I discovered rustup, where you can add toolchains and switch between them, without recording anything in the project you’re working on. While you can leave a rust-toolchain file lying around, it’s entirely optional, and it doesn’t say anything about what extra components you might have installed, or what tools you might have cargo-installed. It reminds me entirely too much of Python virtualenvs.

rustup is a fine tool, and it’s probably the right choice for most Rust developers, since it’s very “human friendly”—if you say “cargo build” then rustup will build with something, and trust that if it picks the wrong thing, a human will be there to hit Ctrl-C and give more detailed instructions. But for automated environments with no humans around, that kind of friendliness causes more problems than it solves, so there’s a need for something stricter. I doubt that something strict could be built on top of something flexible like rustup, but I’m sure rustup could be built on top of something strict.

Very interesting! On thinking about it a little bit, isn’t your approach similar to just managing the toolchain version inside Cargo.toml/Cargo.lock? That would be a more integrated/elegant solution to a similar set of problems, I think.

That would be a good step forward, yes!

However, Cargo is really part of the toolchain—sometimes Cargo and rustc have to change at the same time to implement a given feature. As of 1.26, they even share the same version number. If Cargo can’t give you an appropriate version of Cargo for your version of rustc, that’s going to cause problems. If Cargo can up and downgrade itself as needed, there’s a risk (however slight) that if something goes wrong you might up without a working Cargo at all. Alternatively, if there’s “wrapper Cargo” that downloads and updates “real Cargo”, well, that’s just a new member of the rustup/rustbud family.

Oh, fair point. So in every case, you probably want something that’s not part of the toolchain (and small enough that it only has to evolve very slowly) to manage the toolchain.

Exactly, yes! :slightly_smiling_face:

I’m still working on this, but I came across a small corner-case recently and figured I should post about it in case anybody could correct me or give me further information.

The release channel manifest includes a list of historically-renamed packages, so that toolchain-managers can automatically update their configuration to match the new names. As of 1.27.0, it looks like this:

[renames.rls]
to="rls-preview"

I intend to make these assumptions when validating a manifest, please tell me if I’m incorrect:

  • It’s an error if the old name exists as a package. For example, if “foo” is renamed to “bar”, but the channel contains both “foo” and “bar” packages.
  • It’s an error if the old and new names are the same.
  • If a package is renamed twice (let’s say rls-preview is renamed to rust-lang-serv), then the manifest will point all the historical names at the latest name (“rls” → “rust-lang-serv”, “rls-preview” → “rust-lang-serv”) rather than recording a chain of names (“rls” → “rls-preview”, “rls-preview” → “rust-lang-serv”).

Currently, rustup validates that the new name refers to an existing package, but (so far as I can tell) has no opinion about the things I listed above.

1 Like

And this is way it is good to have a second implementation.

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