mozilla/rust — Issue #1861: Support for custom build logic in Cargo
Author: thoughtpolice
Created: 2012-02-17 04:41:56+00:00
State: open

The metaticket is #1850.

Summary: invariably, due to wanting to keep packages self contained, write wrappers for C++ code, invoke configuration-based build logic (i.e. I want to invoke some transparent compression, providing `zlib` is on the system, etc.) or any number of things, there is often going a need for a more complicated build system than what the crate mechanism provides. Specifically, libraries which do things like foreign bindings are extremely prone to needing this in my experience a lot of times, and requiring arduous - sometimes totally manual - installation on the users part is incredibly unsatisfactory.

As of right now, Cargo only manually looks for crates in the source directory, followed by hitting them with `rustc`. I envision a world where most of the time - this is all that's needed. Cargo should not be responsible for complex code building logic.

However, it most certainly needs support for allowing library creators to defer to a build system to run if the simple case doesn't cut it. The compression example is a real one - in writing Haskell bindings for LevelDB, I found it very nice that I could use autoconf to detect if the `snappy` compression library was available, and compile the LevelDB source code inline with that assumption if it was.

I like the homebrew approach. Any build system it wants - but you express the build in Ruby as a 'formula' for building it, using system commands. So you can have cmake based software, autoconf based, just something with a makefile, etc. The formula merely invokes system commands and fails if they err. One thing to note here is that Homebrew *does* expect you to respect the installation prefix normally, so it gives ruby scripts a 'prefix' variable you can pass on, etc.

I envision something similar for Cargo - just a way to run commands for a build. A nice aspect of this is Cargo still doesn't really do any building on its own, you just give it commands. If your actual package build is handled in an `autoconf` and `makefile` like style, you can always just build like that during development. The metadata is only need to inform Cargo *how to* run a build at installation time.

With the running LevelDB example, let's say I have a package, just called `leveldb`. It has a file structure like this:

```
./configure
./Makefile
./leveldb.rc
./leveldb.rs
./build.cargo
```

Name is arbitrary, but `build.cargo` contains a list of commands to run - it's basically the trigger for custom configuration logic. In fact, you could just say it's any arbitrary executable script, e.g:

```
#!/bin/sh
./configure --prefix=$(CARGO_PREFIX)
make
make install
```

and `make` would take care of calling `gcc` and `rustc` on the crate, etc. This example in particular is kind of half-assed, but you get the gist. Ultimately, I think Cargo should always defer the responsibility of building onto a build system, and not say "you must have a configure script" or "must use a makefile." `build.cargo` is just some script - you could write it in Perl or ruby/etc. But there are downsides to this approach:

* Per #1768, rust should eventually work with `msvc`, and robustly on windows so people will need a way to specify builds for different platforms, particularly windows. I have no idea what this looks like.
* It adds the caveat all libraries must respect installation prefixes, especially since cargo is local by default.

This does allow even the simplest of build systems to work, though. Or just drop a package directly into your rust code (like sundown,) and build away with a makefile and just `cp` it to the prefix!

As an aside, this, along with support for something like #612 would most certainly make it very possible to bind a lot of packages, especially when you want autoconf-style feature detection at configuration time.

So I am personally a fan of this method or something like it. Does anybody have objections to something like this? Thoughts? I'm particularly hazy on saying something as flat out as "`build.cargo` is executable, just do it", but it is simple and flexible.