Configuring custom tools

Consider for a moment that you 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.

Specify the custom tool

build.gradle
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).

The 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 GrabaToolDescriptor will be passed. The description will contain methods for retrieving the appropriate operating system as well as a requested version.

Add a uri block inside the tool('go') block

build.gradle
uri { cfg -> (1)
    String platform
    String ext = 'tar.gz'

    if (cfg.os.windows) { (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 {
            cfg.fail("${cfg.os.arch} is not a supported configuration for Windows") (3)
        }
    } else if (cfg.os.linux) {
        if (cfg.os.arch == OperatingSystem.Arch.X86) {
            platform = 'linux-386'
        } else if (cfg.os.arch == OperatingSystem.Arch.X86_64) {
            platform = 'linux-amd64'
        } else {
            cfg.fail("${cfg.os.arch} is not a supported configuration for Linux")
        }
    } else if (cfg.os.macOsX) {
        platform = 'darwin-amd64'
    } else {
        cfg.fail("${cfg.os} is not supported")
    }

  "https://dl.google.com/go/goVERSION.${platform}.${ext}".toURI() (4)
}
1 In this example a Closure (or an Action) which takes a GrabaToolDescriptor 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 Use the fail statement when specific platforms are not supported.
4 Create a download URI which will be returned. Replace VERSION with the version you need.

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 optional step is to resolve specific binaries.

Add an entrypoint block inside the tool('go') block for each binary that are in the distribution.

build.gradle
execPatterns 'bin/*' (1)
entrypoint('go') { ep -> (2)
    String ext = ep.os.windows ? '.exe' : '' (3)
    "bin/go${ext}" (4)
}
1 Specify any files inside the unpacked package which needs to be marked as executable. This is an optional step, but will be necessary if the execute permissions are lost during unpacking.
2 Name the entrypoint.
3 This function needs to return a subpath from the unpacked distribution root where to find the entrypoint file.

Accessing cached content

build.gradle
Provider<File> location = grabatools.tools.named('go').flatMap { it.location } (1)
Provider<File> go = grabatool.tools.named('go').map { it.entrypoints.get()['go'] } (2)
Provider<File> gofmt = grabatool.tools.go.entrypoints.map { it['gofmt'] } (3)
1 Gets the root of the unpacked package.
2 Used the named approach to get an entrypoint, which was previously defined.
3 Instantiate the tool reference early but delay the download until the point that it is required.

Archives with no root directory

In most cases downloaded archives have some form of root directory. The downloaders are primed by default to use that directory as the actual distribution root directory. There are cases where this is not the case and to deal with them the noRootDir flag can be set to true.

grabatool {
  tool('structurizr') {
    execPatterns '*.sh'
    noRootDir = true (1)
    entrypoint('cli') { ep ->
        String ext = ep.os.windows ? '.bat' : '.sh'
        "structurizr${ext}"
    }
  }
}
1 Set noRootDir = true to use the unpacked directory as the distribution directory.