Introduction
This collection of plugins allows developers to work with Rust in a much more gradlesque way and also easily integrate Rust into a polyglot way of working. Under the hood it utilises Cargo to do the heavy lifting. It does not require Rust or Cargo to be installed as it follows the norm of many new Gradle plugins to bootstrap the installation as part of the build.
The following features are supported:
-
Compiling Rust executable and libraries.
-
Running Rust tests.
-
Defining Cargo dependencies in Gradle’s
dependencies
block.
This is an incubating project. Until is 1.0 released one day, interfaces and DSL may change between 0.x releases. Please see the [limitations] of the plugin during early development.
|
For Rust people new to Gradle
This collection of plugins introduces a couple of conventions which might feel strange to people used to developing with Rust. The source layout follows a more Gradle/Maven convention as follows:
. ├── build.gradle ├── settings.gradle ├── src │ ├── main │ │ └── rust (1) │ ├── test │ │ └── rust (2) │ └── bench │ └── rust (3) └── build └── rust-project (4)
1 | Application & library code goes in src/main/rust . |
2 | Test code goes in src/test/rust |
3 | Benchmark code goes in src/bench/rust |
4 | Work directory for Rust build. |
During the build process Gradle will copy the souce to build/rust-project
folder using the processRustSource
task. This also allow for additional processing on the Rust source before compilation.
Output from Cargo
is not displayed during the build process, but will be captured for later analysis in build/tmp/cargo/${taskName}
folders.
The Cargo.toml
file is auto-generated during the build process from information which is specified in the compileExeRust
& CompileLibRust` tasks as well as defined in the dependencies.rust
block.
Limitations
- Publishing
-
Compilation and test are supported, but publishing to Cargo is not yet supported. See #3.
- Benchmarks
-
Compilation and execution of beanchmark code is not supported. See #1.
- Cargo
-
Arbitrary
cargo
commands cannot be executed. See #2. - Cargo.lock
-
How
Cargo.lock
files are interpreted are yet to be decided. At this current the the lock file will be generated in thebuild/rust-project
directory, which is not in a location which can be committed to source control. - Dependencies
-
Target-specific dependencies cannot be specified in os-arch-compiler triples. See #4.
Bootstrapping
These plugins are available from the plugin portal. Add the appropriate plugin identifiers to your build.gradle
file depending on the type of functionality you require.
plugins {
id 'org.ysb33r.rust.base' version '0.1' (1)
id 'org.ysb33r.rust.exe' version '0.1' (2)
id 'org.ysb33r.rust.lib' version '0.1' (3)
}
1 | Apply the base plugin. |
2 | Apply the application development plugin. |
3 | Apply the library development plugin. |
You need at least JDK8 & Gradle 3.3 to use this plugin. |
The base plugin provides:
-
A project extension called
rust
for configuring the Rust version. -
A handler extension to the
dependencies
block for specifying -
A
processRustSource
task to process Rust source code before building. It is aCopy
task and can be enhanced with any additional filtering and source locations.
The library development plugin provides:
-
A configuration for
processRustSource
to populatebuild/rust-project/(src|tests|benches)
folders fromsrc/(main|test|benches)/rust
folders. -
A
compileLibRust
task to compile Rust source. -
A
compileTestRust
task to compile Rist test source code. -
A
cargoManifest
task to generate aCargo.toml
file. -
A
testRust
task to execute Rust tests. -
Compilation tasks are part of the
assemble
lifecycle. -
Test tasks are part of the
check
lifecycle.
The application development plugin provides similar functionality to the application development plugin except that it creates a compileExeRust
instead of compileLibRust
task. In addition, the application plugin also creates a runApp
task to execute the executable created by the project.
Configuring and Accessing Tools
All configuration and bootstrapping happens within the rust
extension block.
rust {
executable version : '1.2.3' (1)
executable searchPath() (2)
abiToolsSearchPath = [ '/bin', '/usr/bin' ] (3)
abiToolsSearchPath 'bin', '/usr/bin' (4)
}
1 | Use a specified version of Rust. |
2 | Instead of bootstrapping Rust, discover the binaries in the system search path. |
3 | Set the path to search for system linker etc. |
4 | Add additional path to search |
If the abiToolsSearchPath is not specified, Gradle will default to using the system search path.
|
The extension also offers two methods for build script authors to get access to the rustc
and cargo
executables.
task displayLocations {
doLast {
println "cargo: ${rust.resolveCargoPath()}"
println "rustc: ${rust.resolveRustcPath()}"
}
}
Invoking any of the two methods will cause Rust to be bootstrapped.
The Rust Dependency Handler
Instead of using Cargo.toml
files, the Gradle dependencies.rust
block is used to define all required upstream dependencies. Doing it this way, will allow future releases of the plugin to use Gradle’s excellent artifact resolving capabilities to enhance that of Cargo. It is expected that dependencies.rust
supports all the methods for defining Carg-style dependencies.
Dependencies are defined sub-blocks (or configurations) called compile
, test
and build
which in turn, corresponds to Cargo’s dependencies
, dev-dependencies
and build-dependencies
.
Defining dependencies
dependencies {
rust { (1)
compile 'winhttp:0.4.0' (2)
compile { (3)
}
test 'winhttp:0.4.0' (4)
test { (5)
}
build 'winhttp:0.4.0' (6)
build { (7)
}
}
}
1 | All Rust dependencies are declared inside the rust block. |
2 | Shortcut for adding a dependency by name and version to the Cargo dependencies group. |
3 | Add a dependency using a fully configurable closure. |
4 | Shortcut for adding a dependency by name and version to the Cargo dev-dependencies group. |
5 | Add a test dependency using a fully configurable closure. |
6 | Shortcut for adding a dependency by name and version to the Cargo build-dependencies group. |
7 | Add a build dependency using fully configurable closure. |
Instead of a closure, Kotlin DSL users can use a Gradle Action to configure dependencies.
|
A number fo options are available for configuring a dependency at creation time.
dependencies {
rust {
compile { (1)
name 'winhttp'
version '0.4.0'
}
compile { (2)
name 'uuid_1'
git 'https://github.com/foo/uuid_1'
}
compile { (3)
name 'uuid_2'
git 'https://github.com/foo/uuid_2', 'my-branch'
}
compile { (4)
name 'uuid_3'
path 'path/to/uuid_3'
}
compile { (5)
name 'uuid_4'
path 'path/to/uuid_5'
version '0.2'
}
compile { (6)
target 'cfg(windows)'
name 'winhttp'
version '1.2.3'
}
}
1 | Add a dependency by name and version. |
2 | Add a dependency by name and Git repository |
3 | Add a dependency by name, Git repository and Git branch |
4 | Add a dependency by name and path |
5 | Add a dependency by name, path and version |
6 | Add a target-specific dependency. The string passed to target should correspond to something that Cargo understands. |
git and path cannot be specified in the same dependency.
|
Creating patches
Patches can also be specified via dependencies.rust
. Use the patch
keyword instead of specifying a configuration name such as compile
.
dependencies {
rust {
patch 'foo', '../path/to/foo' (1)
patch 'http://foo/bar'.toURI(), 'bar', '../path/to/bar' (2)
patch { (3)
name 'foo2'
path '../path/to/foo2'
}
patch ('http://foo/bar'.toURI()) { (4)
name 'foo3'
path '../path/to/foo3'
}
patch { (5)
name 'foo4'
git 'https://gitlab.com/foo/bar'
}
}
}
1 | Specify a cargo.io patch by name and local path. |
2 | Specify a non-cargo patch by URI, name and local path. The first parameter has to be a URI. |
3 | Create and configure a cargo.io patch by name and local path |
4 | Create and configure a non-cargo patch by URI, name and local path. The first parameter has to be a URI. |
5 | Create and configure a cargo.io patch by name and Git repository. |
Cargo Manifest
The Cargo.toml
is created by the` cargoManifest` task. It is possible to configure a number of basic values for the manifest:
cargoManifest {
manifest.packageInf {
name 'foo' (1)
version '1.2.3 (2)
authors = [ 'Schalk' ] (3)
authors 'Russel' (4)
}
}
1 | Name of the Rust package. Defaults to project.name . |
2 | Version of Rust package. Defaults to project.version . |
3 | Set the authors for the package. |
4 | Adds an author. |
If no authors were specified, Gradle will use name of user currently running the build. |
Alternative solutions
This is not the only solution. You might also want to look at
-
Ariel Cabib’s Rust plugin for Gradle. It requires Cargo be pre-installed and available on the system search path.
-
https://plugins.gradle.org/plugin/com.jtitor.rust. This is a fork for Airel Cabib’s plugin with some updates and released under a new Gradle ID.