gogradle

Getting Started

Gogradle is a Gradle plugin. Gradle is a automation build tool working with DSL written in Groovy. Users can write their own build scripts. Here is Gradle documentation.

Start

Before starting your work, you don’t need to install Go and set GOPATH, of course, there’s no harm if you already did.

plugins {
    id 'com.github.blindpirate.gogradle' version '0.11.4'
}

golang {
    packagePath = 'github.com/your/package' // go import path of project to be built, NOT local file system path!
}

Then, if you are using one of glide/glock/godep/gom/gopm/govendor/gvt/gbvendor/trash/gpm, please run

./gradlew goInit # *nix

gradlew goInit # Windows

to perform a migration. This will tell Gogradle to read the dependency lock file generated by them automatically and import them into build.gradle. You can generate Gogradle’s own gogradle.lock later. See Dependency Lock for more details.

Resolve dependencies and install them into vendor

Enter root directory of the project and run:

./gradlew goVendor # *nix

gradlew goVendor # Windows

Hereinafter, we will use uniform command form gradlew <task> on both *nix and Windows.

Gogradle will resolve all dependency packages and their transitive dependencies, flatten them, and install them into vendor according to build.gradle or gogradle.lock (which will be talked later). Gogradle sees vendor as a temporary dependency which contains project-level dependencies. Every time you run this task, Gogradle ensures dependencies in vendor consistent with build.gradle or gogradle.lock. There’re two modes in Gogradle: REPRODUCIBLE and DEVELOP (REP and DEV for abbreviation). In DEVELOP mode, Gogradle will resolve dependencies declared in build.gradle; in REPRODUCIBLE mode, Gogradle will resolve dependencies locked in gogradle.lock (if it exists). Default mode is REPRODUCIBLE. To switch mode, add a command line argument to any task:

gradle xxx -Dgogradle.mode=DEV or gradle xxx -Dgogradle.mode=DEVELOP (DEVELOP mode)

and

gradle xxx -Dgogradle.mode=REP or gradle xxx -Dgogradle.mode=REPRODUCIBLE (REPRODUCIBLE mode)

You can decide whether to check in vendor directory. Personally, I recommend against it.

Build a Project

./gradlew goBuild # *nix

gradlew goBuild # Windows

goBuild task depends on resolveBuildDependencies task. Therefore, even you never specify resolveBuildDependencies task, it will be executed by Gogradle first, which is similar to make. That is to say, when build starts, resolveBuildDependencies task has already been executed and all dependency packages have been installed into vendor. By default, build is equivalent to run go build <current package path> -o <output location>. To configure the build, see goBuild任务.

Test a Project

Enter root directory of the project and run:

gradlew goTest 

If you want to specify some tests:

gradlew goTest --tests main_test.go // Specify a single test
gradlew goTest --tests *_test.go // Wildcard test

If you want to let build depend on test, just add the following line to build.gradle:

goBuild.dependsOn goTest

Test reports will be generated in <project root>/.gogradle/reports/test. See goTest task for more details.

Custom task

If you want to define some custom tasks, I recommend reading Gradle documentation of task mechanism. It’s similar to make rule but more flexible and powerful.

For example, to add a task to run golint, add following lines to build.gradle:

task golint(type: com.github.blindpirate.gogradle.Go) {
    dependsOn goVendor // make this task depend on vendor task to guarantee all dependency packages are installed into vendor 
    environment MY_OWN_ENV1: 'value1', MY_OWN_ENV2: 'value2' // set environment variables
    run 'golint github.com/my/project' // specify the command in task
}

goCheck.dependsOn golint

Note that stdout/stderr redirection and pipe is not available. To do a redirection, e.g., a cross-platform tee, you should use closure in Groovy:

task myTee(type: com.github.blindpirate.gogradle.Go){
    dependsOn goVendor // make this task depend on vendor task to guarantee all dependency packages are installed into vendor 
    go('build -v github.com/my/project') {
        stdout { stdoutLine ->
            println stdoutLine
            new File('stdout.txt').append(stdoutLine)
        }
        stderr { stderrLine ->
            println stderrLine
            new File('stderr.txt').append(stderrLine)
        }
    }    
}

And more simple:

task myTee(type: com.github.blindpirate.gogradle.Go){
    dependsOn goVendor // make this task depend on vendor task to guarantee all dependency packages are installed into vendor 
    go('build -v github.com/my/project') {
        stdout writeTo('stdout.txt')
        stderr appendTo('/this/is/absolute/path/stderr.txt')
    } 
}

This code snippet tells Gogradle to run build -v github.com/my/project, and write standard out into stdout.txt in project root and append standard error to /this/is/absolute/path/stderr.txt.

Here is a cross-platform /dev/null:

task outputToDevNull(type: com.github.blindpirate.gogradle.Go){
    dependsOn goVendor // make this task depend on vendor task to guarantee all dependency packages are installed into vendor 
    go('build -v github.com/my/project') {
        stdout devNull()
        stderr devNull()
    }    
    
    doLast {
        if(exitValue!=0){
            println "return code is ${exitValue}"
        }
    }
}

It tells Gogradle to run go build -v github.com/my/project and discard all standard out and standard error. If go process’s return value is not zero, print it. You might have doubts, why not use Shell and make instead of such long code?

The answer is:

Add a dependency

To add a dependency package, just add its name and version into dependencies block of build.gradle:

dependencies {
    golang {
        build 'github.com/a/b@v1.0.0' 
        test 'github.com/c/d#d3fbe10ecf7294331763e5c219bb5aa3a6a86e80'
    }    
}

The build and test are two independent dependency sets. Java developer should be familiar with the concept. By default, build dependes on resolveBuildDependencies, thus when build runs, only build dependencies exist in vendor; test depends on both resolveBuildDependencies and resolveTestDependencies, thus when test runs, both build and test dependencies exist in vendor.

To learn more details about dependency, see Dependency Management.

Display Dependencies

gradlew goDependencies

The output is as follows:

build:
github.com/aws/aws-sdk-go
|-- github.com/go-ini/ini:e7fea39
|-- github.com/jmespath/go-jmespath:bd40a43
\-- golang.org/x/tools:bf4b54d
    |-- golang.org/x/crypto:0fe9631
    \-- golang.org/x/net:5139290
        |-- golang.org/x/crypto:0fe9631 (*)
        \-- golang.org/x/text:19e5161
            \-- golang.org/x/tools:bf4b54d -> bf4b54d (*)

test:
github.com/aws/aws-sdk-go
\-- github.com/stretchr/testify:4d4bfba
    |-- github.com/davecgh/go-spew:04cdfd4
    |-- github.com/pmezard/go-difflib:d8ed262
    \-- github.com/stretchr/objx:cbeaeb1

This is dependency tree of aws-sdk-go at 31484500fe.

Arrow -> indicates that the dependency conflicts with another dependency and is resolved to another version; star * indicates this node’s descendants are ignored because they have been displayed before.

Dependency Lock

gradlew goLock

This command will let Gogradle generate a gogradle.lock file in project root directory, which records all dependencies of this project.

gogradle.lock is recommended by Gogradle. Locking dependencies play an important role in reproducible build. Similar to other package management tools, Gogradle can lock the versions of all dependency packages. Moreover, Gogradle can lock dependency packages even when they are in vendor!

Gogradle supports transitive dependency/dependency exclusion/customized url, see Dependency Management.

Currently, only Git and Mercurial dependencies are supported, support for other vcs is under development.

Configuration

The following complete configuration is located in golang block of build.gradle.

golang {
    
    // Import path of package to be built
    packagePath = 'github.com/user/project'
    
    // The buid mode. There are two alternatives: DEVELOP/REPRODUCIBLE, REPRODUCIBLE by default
    // This configuration will be overwritten by command line argument -Dgogradle.mode
    // REP/DEV for abbreviation
    buildMode = 'REPRODUCIBLE'
    
    // The Go version to use. See https://golang.org/dl/
    // If not specified, Gogradle will find local go process. If not found, the latest version will be downloaded and installed automatically
    // If specified, Gogradle will check if local go version matches the specific version. If not, the specific version will be downloaded and installed automatcally
    goVersion = '1.8.1'
    
    // Default value is "go". Modify this when go is not in $PATH
    // You can use goExecutable = 'IGNORE_LOCAL' to ignore local go forcibly 
    goExecutable = '/path/to/go/executable'
    
    // For custom distribution repository. Gogradle will try to download go distributions from
    // http://golangtc.com/static/go/${version}/go${version}.${os}-${arch}${extension}
    goBinaryDownloadTemplate == 'http://golangtc.com/static/go/${version}/go${version}.${os}-${arch}${extension}'

    // If not set, GOROOT will be <directory of go binary>/..
    goRoot = '/path/to/my/goroot'
    
    // aka. build constraint. See https://golang.org/pkg/go/build/#hdr-Build_Constraints
    buildTags = ['appengine','anothertag']
}