Description
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 Use msvc on windows, not mingw #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. 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.