While I agree with most of what you wrote, CMake feels supercharged compared to the build system part of Cargo.
Cargo is failing hard on the integration front, both on integrating things, and being integrated. build.rs is a minimal substitute of a build system: "deal with it yourself". The way Cargo wants to have total control, on the other hand, makes it hard to integrate with existing projects: those which don't use Rust at the top level.
The only great thing about Cargo's build system is that it works well on the happy path of Rust-only, crates.io-only software.
I was a C++ developer in a past life and Cmake was a big reason I moved on to greener pastures. It’s the only language that is more toilsome to use than C++ itself, and that by an enormous margin. It’s stringly typed (no, that’s not a typo, everything is a string), it’s syntax is obscure, it extends so poorly that it just bakes-in support for building popular libraries (e.g., Google’s test framework, Qt, etc iirc), imports are implicit so it’s tedious to track down the definition for a particular symbol, it completely punts on package management—not only were builds not reproducible but you couldn’t even get it to download and install dependencies for you from a declarative list. Cmake isn’t a build system, it’s a toolkit for scripting your own bespoke build system (and a crumby one at that) which basically means that every project is a unique snowflake with its own distinct quirks which are tedious to learn and maintain—even though 99% of projects would be covered by something like cargo. Those are some of the things I remember off the top of my head ten years later (it was also doggedly slow and things would break across minor releases, but I’m told those things have improved).
Cargo is imperfect, but it’s the right tool for the job 99% of the time.
CMake may be toilsome, but it is acceptable for simple projects due to its builtin dependency resolution, and powerful enough to do anything you want on the complex side.
Cargo is perfect 80% of the time in my usage, but when it's not perfect, it's almost actively harmful, and much worse than CMake. And I say that as no fan of CMake.
I would not be bothered by Cargo not being a build system, except it's the one build system underpinning the entire Rust ecosystem via crates.io, and its "rules" are not interoperable with other build systems.
As a result, you have to deal with Cargo's terrible build system whenever you want to use an external crate, whether you want it or not.
I don't disagree, I find cargo insufficient for projects of any meaningful complexity (it doesn't even support post build steps...). But it's really good at one thing: compiling crates.
But I haven't had a ton of trouble integrating it into CMake projects. It falls into the category of "know your tools." Not everything can "just work" all the time.
> But it's really good at one thing: compiling crates.
Unless you're using "compiling" as strictly compiling, and not "building", then I don't agree either.
It falls flat on its face if you want a build time choice between dependency versions, for example. And build.rs means that Cargo washes its hands from compiling parts of crates that are not written in Rust, so it's arguably not good at compiling (of anything but pure Rust).
The way Cargo is integrating several concerns also makes it hard to create better build systems for Rust, because they would have to pull in the same kitchen sink in order to support Cargo.toml. So that's being bad at letting others compile as a bonus.
EDIT: Actually, that wouldn't be a problem if Cargo the decent package manager didn't mandate Cargo the awful build system.
As much as I agree that Rust needs a better story for builds and interacting with other languages, it sounds like you have a misunderstanding over what Cargo primarily does and what crates are. A crate is a single compilation unit of Rust. Cargo is a tool for compiling crates and pulling in other crates that it references.
build.rs is a half measure to include foreign symbols in compilation artifacts like static/shared libraries and executables. I'd go so far as to advise against using it for anything but specifying linker flags.
I don't think I've ever had a use case for specifying dependency versions at build time. That seems insane, and I do insane things in cmake with regularity. There's a reason versions are pinned to a config file committed to repos in almost every contemporary language.
fwiw, Cargo is a crate itself and you can use it as a library. You can even compile it with C language bindings to call through FFI in other build systems if you felt like it. The lang tools team has done a great job with keeping the scope of Cargo manageable and putting in the ground work to make better tooling around it.
For complex Rust builds, check out cargo-make. It does most of what you'd need in a predominantly Rust codebase. For polyglot environments, cmake with custom targets is the least bad way I've found to do it - and it's not hard to do that by shelling out to Cargo.
A crate may be a compilation unit, but it's irrelevant. Within the Rust space, a crate is a library. something like 95% of crates are using Cargo, and Cargo requires that dependencies are also using Cargo. Today it's impossible to ditch Cargo, and publish your crate with e.g. Bazel as the build system.
There's no misunderstanding that what Cargo does it build Cargo crates. The problem is that it doesn't allow for sanely built (so not using Cargo) crates.
> specifying dependency versions at build time
Packaging for different distributions, where different versions of a dependency are provided, is quite a common thing, and has justifications beyond technical reasons.
Cargo is failing hard on the integration front, both on integrating things, and being integrated. build.rs is a minimal substitute of a build system: "deal with it yourself". The way Cargo wants to have total control, on the other hand, makes it hard to integrate with existing projects: those which don't use Rust at the top level.
The only great thing about Cargo's build system is that it works well on the happy path of Rust-only, crates.io-only software.