Introduction
The less a person has to install manually the better. The more of a full development toolchain that can be bootstrapped by a person the less the chance for subtle build errors to creep in.
Gradle has well developed and very powerful dependency mechanism. Although these tend to work quite well for pure JVM projects, its limitations are soon reached when other (especially native) toolchains and SDKs are required. Quite a number of Gradle plugin now already offer the ability to bootstrap specific tools, buyt there is a good chance that a build script author might require other tools to be bootstrapped which are not supported via plugin atm. The grabatool plugin provides the build script author with that ability including customisation based upon operating system as needed.
Another common scenario is that build script authors want the flexibility to either have a property set from a project property, a system property or an environmental variable. This becomes really significant where in local development a project property might be preferred, but in a CI system an environmental variable might be more practical. The grabaprop plugin allows this to be simplief by using conventional name mappings to search all three spaces for a property.
This is an incubating project. Until is 1.0 released one day, interfaces and DSL may change between 0.x releases.
|
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.grabatool' version '0.3.0' // <1>
id 'org.ysb33r.grabaprop' version '0.3.0' // <2>
}
1 | Apply the grabatool plugin. This plugin provides an extension named grabatool with the ability to download and use various distributions and SDK via simple configuration. |
2 | Apply the grabaprop plugin. This plugin provides an extension named grabaprop with a simplified means of accessing properties. |
You need at least JDK8 & Gradle 3.3 to use these plugins. |
Configuring and Accessing Tools with GrabaTool
All configuration happens within the grabatool
extension block.
Consider for a moment that youi want to download a Go distribution. Depending on which platform you are building you will need to download the appropriate distribution, unpack it and then access the go
executable.
grabatool { (1)
tool('go') { (2)
// ... configuration to follow
}
}
1 | All configuration happens within the grabatool block |
2 | A tool specification is done using tool keyword, followed by a descriptive shortname. This then followed by a configuration closure (or an Action in case of a Kotlin DSL). |
Next step is to specify how to resolve the download URI. This is done via a Closure
or a java.util.Function
. In both cases a GrabaToolUriDescriptor will be passed. The description will contains methods for retrieving the appropriate operating system as well as a requested version.
Add a uri
block inside the tool('go')
block
uri { GrabaToolUriDescriptor cfg -> (1)
String platform
String ext = 'tar.gz'
if (cfg.os.isWindows()) { (2)
ext = 'zip'
if (cfg.os.arch == OperatingSystem.Arch.X86) {
platform = 'windows-386'
} else if (cfg.os.arch == OperatingSystem.Arch.X86_64) {
platform = 'windows-amd64'
} else {
throw new GradleException("${cfg.os.arch} is not a supported configuration for Windows") (3)
}
} else if (cfg.os.isLinux()) {
if (cfg.os.arch == OperatingSystem.Arch.X86) {
platform = 'linux-386'
} else if (cfg.os.arch == OperatingSystem.Arch.X86_64) {
platform = 'linux-amd64'
} else {
throw new GradleException("${cfg.os.arch} is not a supported configuration for Linux")
}
} else if (cfg.os.isMacOsX()) {
platform = 'darwin-amd64'
} else {
throw new GradleException("${cfg.os} is not supported")
}
"https://dl.google.com/go/go${cfg.version}.${platform}.${ext}".toURI() (4)
}
1 | In this example a Closure which takes a GradleToolUriDescriptor is used. |
2 | Use appropriate logic to determine the filename. For instance with Go this is dependent on both the operating system and the architecture. |
3 | If is fine th throw an exception when specific platforms are not supported. |
4 | Create a download URI which will be returned. |
Grabatool will use the above URI to download the distribution, if it is not already cached. Downloaded distributions are usually shared between Gradle projects.
The next required step is to resolve specific binaries. Once again this can be done via Closure
of Function
, but in this case the passed parameter is of type GrabaToolEntryPointDescriptor.
Add an entrypoint
block inside the tool('go')
block.
entrypoint { GrabaToolEntryPointDescriptor ep -> (1)
String ext = ep.os.isWindows() ? '.exe' : '' (1)
new File(ep.distributionRoot, "bin/${ep.name}${ext}")
}
Accessing Properties with GrabaProp
ext {
MySetting = grabaprop.get('my.specific.property') (1)
MySetting = grabaprop.get('my.specific.property', 'default.value') (2)
MySetting = grabaprop.get('my.specific.property', grabaprop.SYSTEM_ENV_PROPERTY) (3)
MySetting = grabaprop.get('my.specific.property', 'default.value', grabaprop.SYSTEM_ENV_PROPERTY) (4)
}
1 | Looks for my.specific.property as a project preropty and if not found as a system property of the same name. If not found will look for the environmental variable MY_SPECIFIC_PROPERTY . Returns null if not found. |
2 | Same behaviour as above, by returns the specified default value if not found. |
3 | Resolve property using a different search order. Return null if not found. |
4 | Resolve property using a different search order. Return specified default value if not found. |
Changing the search order
By default the search order is project properties, system properties and then environmental variables. Tiy can change this order to system properties, environmental vriables and then finally project properties by using grabaprop.order
.
grabaprop {
order = grabaprop.SYSTEM_ENV_PROPERTY
}
If the above still does not work for you, you can override it using your implementation of PropertyResolveOrder.
grabaprop {
order ( { Project proj, String prop -> (1)
// Place your own logic here to resolve 'prop' in the context of a project.
// Return null if the property cannot be resolved.
} )
}
1 | The exampe shows the usage of a closure, but you can use anything that implements the PropertyResolveOrder interface. |