groovy force - Recommended way to stop a Gradle build




task fail (6)

How can I stop a Gradle build after detecting a problem? I can use an assert, throw an exception, do a System.exit (bad idea), or use a dedicated function in Gradle (but I could not find one). What is the best way for Gradle (and why?).


Answers

I usually throw the relevant exception from the org.gradle.api package, for example InvalidUserDataException for when someone has entered something invalid, or GradleScriptException for more general errors.

If you want to stop the current task or action, and move on to the next, you can also throw a StopActionException


Here is a code fragment that tries to emulate how the Gradle javac task throws errors:

task myCommand(type:Exec) {

    ... normal task setup ....

    ignoreExitValue true
    standardOutput = new ByteArrayOutputStream()
    ext.output = { standardOutput.toString() }
    doLast {
        if (execResult.exitValue) {
            logger.error(output())
            throw new TaskExecutionException( it,
                new Exception( "Command '${commandLine.join(' ')}' failed; "
                              + "see task output for details." )
            )
        }
    }
}

When the command returns 0 there is no output. Any other value will print the standardOutput and halt the build.

NOTE: If your command writes to errorOutput as well, you may need to include that in the error log.


There is currently no dedicated method, although there have been discussions to add one.

The recommended way to stop a Gradle build is to throw an exception. Since Groovy doesn't have checked exceptions, and Gradle by default does not print the exception type, it's not that critical which exception is thrown. In build scripts, GradleException is often used, but a Groovy assertion also seems reasonable (depending on the circumstances and audience). What's important is to provide a clear message. Adding a cause (if available) helps for debugging (--stacktrace).

Gradle provides dedicated exception types StopExecutionException/StopActionException for stopping the current task/task action but continuing the build.


If you want to stop the build, throw:

throw new GradleException('error occurred')

or throw the subclasses for the above exception. Some of the subclass exceptions actually only fail the current task but continue with the build.


Throwing a simple GradleException works in stopping the build script. This works great for checking required environment setup.

GradleException('your message, why the script is stopped.')

Example:

if(null == System.getenv()['GRADLE_USER_HOME']) {
    throw new GradleException('Required GRADLE_USER_HOME environment variable not set.')
}

Well, it is hard to tell what serves you best without actually seeing your build file.

I could assume that stetting up your environment as multi-project build should provide you the abstraction you are looking for.

In your project root build.gradle you define all your domain specific stuff as well as the things that apply to all your subprojects:

repositories {
    add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) {
        name = 'destRepo'
        addIvyPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/ivys/ivy(-[revision]).xml')
        addArtifactPattern( file( project.properties['repo.dest.dir']).absolutePath + '/[organisation]/[module]/[type]s/[artifact](-[revision]).[ext]')
        descriptor = 'optional'
        checkmodified = true
    }
    ...
}
...
subprojects {
    sourceCompatibility = 1.5
    targetCompatibility = 1.5
    group = 'my.group'
    version = '1.0'
    uploadArchives {
        uploadDescriptor = true
        repositories {
            add rootProject.repositories.destRepo
        }
    }
    apply{ type my.group.gradle.api.plugins.MyPlugin }
    ...
}

dependsOnChildren()

The project root directory might also contain a gradle.properties file where you define properties used by your projects:

buildDirName=staging
repo.dest.dir=/var/repo
...

Then in an additional file from your project root named settings.gradle you actually point to your subprojects:

include 'my-first-component',
        'my-second-component'
...
project(':my-first-component').projectDir = new File(rootDir, 'path/to/first/component')
project(':my-second-component').projectDir = new File(rootDir, 'path/to/second/component')
...

Each sub-project directory contains a build.gradle file containing the sub-project specific stuff only.

No matter if you invoke gradle from your project root or sub-project directory, gradle will automatically consider all your definitions done in the various files.

Also note that no compile task will be executed for your project root as long as you don't load any plugin beyond the default plugin at the root level.





groovy build gradle