-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Hey all,
I have been working on a few projects that involve C++ and assembly code with Rust, and one of the annoying pain points is build systems. Here are some thoughts I had. Any feedback or alternate solutions are welcome. Thanks 😄
I propose adding an optional field in the [dependencies]
section(s) of Cargo.toml
to allow Cargo to be minimally aware of non-Rust dependencies and remove some boilerplate.
[dependencies]
# these are the same old rust dependencies we already have
hello_utils = { path = "hello_utils", version = "0.1.0" }
rand = { version = "0.3", optional = true }
rlibc = "*"
# but we can also have non-rust dependencies
my_asm = {path = "../my_asm/", non-rust-build = "make build"}
# can fetch from github, just as with other dependencies
my_cpp = {git = "http://github.com/example/my_cpp.git", non-rust-build = "make build"}
Suppose the my_asm
dependency contains some assembly that needs to be built before the rust project. The non-rust-build
field in the snippet above tells Cargo that it should just invoke make build
and ensure that it completes with a 0 error code before continuing with the rest of the build. To the very best of my knowledge and from spending some time skimming through the docs on dependencies on crates.io, I don't think such a feature exists yet (please correct me if I am wrong).
Motivation
So far, Cargo is great at tracking rust dependencies, but not so great for projects with mixes of rust and not-rust. For example, suppose you have a Rust project with a dependency on some assembly code. You need to first compile the assembly into some sort of object before it can be linked in with your Rust program (aside: #1489 would make the linking portion significantly easier). AFAIK, you can take either of two routes: (1) write a big makefile (or some other build tool) to invoke Cargo, gcc, and then the linker, or (2) use a Cargo build script to invoke the makefile. Such build systems can get messy and hard to debug and require a lot of boilerplate. In some cases, it is just more ergonomic to write a makefile that invokes rustc directly, and I have done this in one of my projects.
To be clear, I not proposing Cargo as a replacement for make. I simply think that Cargo could be made more aware of the other dependencies and build systems in a project in some minimal way.
Specifically, I think there are two cases from the point of view of Cargo: (1) the Rust crate is dependent on some non-Rust object which needs to be obtained somehow, and (2) the Rust crate is a dependency of some non-Rust object which needs to obtain the Rust object before it can be compiled/linked. I would argue that in case (2), some other build system should be responsible for tracking dependencies and making sure that Cargo is invoked when needed. However, in case (1), I believe Cargo should be responsible for obtaining the needed object, even if that simply involves invoking some programmer-defined command (e.g. make build
).
In my opinion, its ok to have a build system with multiple parts (e.g. cargo + make) as long as the parts are modular, somewhat independent (i.e. they have very limited interactions that are easy to trace and debug), and they don't contain lots of boilerplate.