Description
The custom build
command is a very wonky mechanism. It is not cross-platform, it assumes that the user has certain softwares installed on his system and available in his PATH (usually make/gcc, sometimes curl), it does not support cross-compilation, etc. Basically it is like having a makefile without a configure script.
This is especially problematic on Windows. Because of this command, a lot of Rust libraries depend on the user having MinGW installed (even though rustc no longer requires this), and some libraries simply can't be compiled on Windows, like glfw-rs
. I have the sensation that all the efforts spent on improving support for Windows are worthless because of this.
I propose to deprecate the build
command. At first, print a warning when compiling a crate that uses it, and in a second time remove support for it entirely.
Replacements
This command should be replaced by two complementary mechanisms.
Binary dependencies
Add a pre-build
string entry in Cargo.toml
. This string must in the format $package/$bin
where $package
is the name of a dependency in the dependencies
array, and $bin
is the name of a binary found in this dependency. The binary will be compiled and then run by Cargo similarly to the current build
command.
The binary would have access to the environment variables that the build
currently has access to, plus an additional one named COMMAND_CARGO_MANIFEST_DIR
which contains the directory of the Cargo manifest of the dependency (instead of the manifest of the package where the command is being run).
Example:
/Cargo.toml
:
[package]
name = "whatever"
authors = []
pre-build = "my-compiler/compile"
[dependencies.my-compiler]
path = "my-compiler"
/my-compiler/Cargo.toml
:
[package]
name = "my-compiler"
authors = []
[[bin]]
name = "compile"
Why is this better than a regular build
command?
- Instead of having a makefile run
curl
for example, you can instead write a binary that uses a Rust http library, which is more cross-platform because it does not require the user to havecurl
in his path. - In situations where you would need a very complex pre-build script (for example invoking cmake (which requires determining the list of generators and invoking the right one)), instead of writing a difficult-to-maintain makefile that you copy-paste in multiple projects, you could simply use a Rust library.
Add native
Add a native
array in Cargo.toml
that specifies a list of non-rust libraries that must be present for this project to compile.
[[native]]
name = "GL"
[[native]]
name = "X11"
[[native]]
name = "Xxf86vm"
Cargo will process each entry in this order:
- Try to find a pre-compiled version of the library (if
X
is the library name, try to findlibX.*
andX.*
) in$package_root/native/$target
. If it finds a match, copy the file in$(OUT_DIR)
.
For example if you are compiling forarm-linux-androideabi
and require a library namedhello
, Cargo will look fornative/arm-linux-androideabi/libhello.*
andnative/arm-linux-androideabi/hello.*
. - If we are not cross-compiling (ie. no
--target
option has been passed) and if we didn't find anything in the previous step, try to invokepkg-config
. If a result is found, pass the path of the library to rustc with the-L
flag. - If we didn't find anything in the previous steps, try to find a pre-compiled version of the library in
$(HOME)/.rust
. There is no additional step because rustc already looks into$(HOME)/.rust
. Cargo must still check if the library is present. - If nothing is found, print an error and stop. An additional flag
--ignore-missing-native-libs
could be added to Cargo in order to bypass this error and print a warning instead.
This system could be improved in the future by adding more entries to native
, for example allowing the user to choose whether to link statically or dynamically.