Hi all! At my company I was recently appointed with the task to explore if we could replace part of our codebase to Rust. Our initial product was in C++ (as we had to use some supplier third party libraries). For what I saw, the only the facto tool to integrate compiler-wise Rust and C++ is cxx.rs . However, making cargo recognize the dir structure of the C++ part appears to be possible, but it is strange (and I think I'll throw away the IDE support in the process). In the cxx docs it is advised to use Bazel to integrate the two.
My question is: what would you use in a similar scenario?
I've read good things of Bazel (purposefully multi-language), Meson (simple) and I guess Buck2 is fine too. What would you use? Thanks!
Unless I really needed Bazel's performance characteristics, I'd probably go for Meson, specifically because of the simplicity. Keep in mind that interacting with such a build system isn't a one-and-done, it requires ongoing maintenance (e.g. when adding new files or removing obsolete files, or during refactorings), which has real costs in terms of FTEs.
In addition, the less time I have to spend diagnosing and debugging issues, the better.
That said, others may have different preferences, especially if they have large C++ code bases.
I only have second-hand experience with Bazel, but it looks painful. Bazel has an option of replacing Cargo entirely, and turning Cargo dependencies into more-or-less regular Bazel dependencies. The upside for this is that everything is built by Bazel, and the C/C++ dependencies that are common between Rust and C++ can be unified, and configured once, and built once by Bazel.
Unfortunately, this makes use of more complex Cargo features complicated or impossible, and the whole setup is really weird and overcomplicated from perspective of a mere Cargo user. To me this makes Cargo dependencies behave too much like C++ dependencies, with all the pain points that C++ dependency management has.
Bazel does seem more complicated, but I can't use vanilla cargo anyway because I need to integrate with C++, so I have to choose IIUC between cargo + magic scripts, Bazel or Meson. Do you have experience with one of these?
I'm not sure if this is useful, since I am mostly not a C++ developer. Some of the code I interact with on a daily basis is written in C++ and the most common build dependency I've needed is Cmake. (FWIW previous versions of aws-lc-rs have additionally required Ninja and NASM for Windows builds, before they made precompiled binaries the default.)
And there is the cmake crate for integration, which is fairly seamless after Cmake, pkg-config, clang/gcc, and all the usual suspects are actually installed on the host.
That said, the disconnect between Cargo's all-in-one build solution and "integrating with any other language" is rather disappointing at best. I still find myself reaching for Docker to wrangle dozens of package managers and libraries when it comes to integrating with other ecosystems and cross compiling for mobile platforms.
I have separate experience with cmake C++, bazel C++ and cargo rust.
For C++ I would say that cmake is probably the better option for a small open source project (better integration with distro tooling and using dependencies from the installed system). For a proprietary or large code base, Bazel is a large step up in devex though. Things like caching of build artifacts just work .
Cargo has some pain points for Rust as your code base starts to grow large as well, sometimes inexplicably rebuilding things you believe it shouldn't.
I have not yet tried Bazel with Rust. Cargo does have very nice developer tooling though (both built in and as addons), that you would loose out on as I understand it with Bazel.
I have almost no experience with meson apart from building a couple of projects using it as a user.
Just using rustc is a pretty simple. So be more focused on a convenience of building C++ code. If you need a help with using just rustc, let me know, I wrote an article recently - Rust development without Cargo.
I wrote a bunch of the Rust support in Meson, so I'm slightly biased. I wrote most of what I did for Mesa (Gstreamer is the other main driver of Rust in Meson), which is a fairly large existing C and C++ codebase (which was already using Meson).
Meson has good first class support for C and C++, and fairly good support for Rust. We have some (still growing) support for buildings crates as native Meson sub-projects. We also have the traditional distro support, install targets, and all of the other stuff you would expect from a generic build system.
Bazel, from the limited experience I've had with it, is a fine build system for largely homogeneous and large build environments, but it's complicated. It's also more difficult for end users to setup and use, and for distros to use.
If you decide to try out Rust with Meson, we'd love to get your feedback on how things went for you, and issues you run into.
I'm curious as to what extent tooling integrates properly with meson: Rust-analyser, clippy and rustfmt are obviously musts, but there are so many useful cargo addons as well: cargo-flamegraph, cargo-deny, cargo-audit, cargo-update, cargo-about are all things I depend on in my normal workflow.
What is the state of these in Meson (or bazel for those who used that with Rust)?
Rust-analyzer works, with the caveat that if you're not using vscode with the Meson extension you probably need to point rust-analyzer to the $builddir/rust-project.json (or symlink it to $sourcedir) (Meson always does out of tree builds, and doesn't write files to the source directory).
There is support for clippy-driver, although it's not super nice because you have to configure with RUSTC=clippy-driver meson setup $builddir. That's been a longstanding "should fix" issue.
Meson doesn't invoke cargo, it just reads the Cargo.toml (and maybe the Cargo.lock, but there was some debate over whether that was a good idea and I can't remember the outcome). But some of these things have Meson equivalents.
cargo-about, cargo-deny: There is nothing exactly equivalent. Meson does have a JSON based introspection API that has the necessary information, but I'm not aware of a tool that reproduces those commands exactly
cargo-flamegraph: (I'm assuming you mean this one). Meson has built profiling support, although I've never finished support for LLVM based compilers (I now remember), so that doesn't work for Rust. That also generates gcov/llvm-cov style reports, which there are tools for generating flame graphs from. Call this one WIP.
cargo-update: Meson manages rust crates itself, and provides a meson subproject command for that. The crate support is still growing and a bit rough around the edges, so it quite possibly doesn't have all of the features that cargo-update does.
cargo-audit: We don't have anything like this to my knowledge. It certainly could be written, and would be a generally useful feature (IMHO) for C and C++ as well, as long as we could figure out how to query that information.
Thank you, I was checking out Meson yesterday but I couldn't find any documentation about how to start using it, only documentation about "how to do this", and since I'm using this for the first time I couldn't figure out where should I start. For instance, I managed to compile a Rust main.rs, but Meson did not generate any rust-project.json. Do you happen to have at hand an example repo, or tutorial, or something like that?
Thanks, it looks interesting! But I can't find it, do you have a link?
Also, I assume your answer also mean "use whatever build tool you prefer to drive invocations of your favorite CC and rustc together". Do you usually do this by hand, or do you use a build system that keeps track of dependencies?
Yeah, I think we're in a... decent place for existing projects using C/C++, D, and/or Fortran and want to add Rust to the mix. We're definitely not ready to be used as a full fledged replacement for cargo yet, but I hope we'll get there.
Probably the easiest thing to do is meson init -l rust --type library --build --builddir builddir. the rust-project.json will then be in the root of $builddir. We should have some better documentation about using meson init.
The --build --builddir option will make meson init do an initial meson setup $builddir && ninja -C $builddir`
There is a link to the article. Nothing will happen automatically (unless AI become more powerful). You need to specify all dependencies, but then the build script will figure out what is needed to be recompiled.
The main issue is that if the Rust code is a small part of the whole code in the application, you will have to link lots of C code into all targets, and cargo's solution with cxx.rs is quite painful as well. Integrating the Rust parts into an existing build system with cargo and a staticlib works at the beginning, but doesn't scale well. Meson's Rust support is the best compromise in my opinion but it's still in the works.
For the next version of Meson 1.7.0 I've started working on integrating clippy, rustfmt and doctests. These are the big three missing cargo features in Meson, in my opinion, especially doctests.
A workaround is to keep the meson build system and a minimal Cargo.toml workspace that is not able to produce working code (hence the emphasis on doctests, because this workaround won't give them) but can reason on it. A script that runs on the Meson side reads Cargo.toml to figure out the lint flags, so that they are only present in one place.
This workaround is enough to run cargo clippy or cargo fmt until the above support lands in Meson, but it should also make it possible to reason on dependencies with e.g. cargo about and cargo deny. I use it for cargo doc and cargo expand as well.