Skip to content

Better Non-Rust Dependency Tracking in Cargo #1972

@mark-i-m

Description

@mark-i-m

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-cargoRelevant to the Cargo team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions