skip to content

Running Builds Programmatically

Launching tasks through BuildLauncher with arguments, captured output, asynchronous result handlers, and cancellation tokens. Asked because cancellation is what makes an IDE's stop button actually work.

on this pageshow

questions

5

How do you run a Gradle build programmatically with the Tooling API using BuildLauncher, and how do you tell it which tasks to execute?

level: juniorimportance: must knowfreq 55%

answer

  1. connection.newBuild() -> BuildLauncher
  2. forTasks("clean","build")
  3. run() synchronous, blocks + throws
  4. fluent one-shot launcher
  5. close ProjectConnection (try-with-resources)

basics

~10 s

Get a ProjectConnection, call newBuild() to get a BuildLauncher, then forTasks("build") to select tasks, and run() to execute it synchronously.

solid answer

~40 s

The Tooling API runs builds through a `BuildLauncher`. From an open `ProjectConnection` you call `connection.newBuild()` to obtain the launcher, configure it (which tasks, arguments, I/O streams), then call `run()` to execute synchronously. Tasks are selected with `forTasks("clean", "build")` — these are the same task names you'd pass on the CLI. `run()` blocks until the build finishes and throws on failure (e.g. `BuildException`, `GradleConnectionException`). Always close the `ProjectConnection` when done, typically in a try-with-resources block, since it holds a connection to a (possibly reused) Gradle daemon. The launcher is a one-shot, fluent configuration object: you set it up fully before invoking `run()` (or `run(ResultHandler)` for async).

code

kotlin · 8 lines
kotlin
GradleConnector.newConnector()
    .forProjectDirectory(projectDir)
    .connect()
    .use { connection ->
        connection.newBuild()
            .forTasks("clean", "build")
            .run() // synchronous; throws BuildException on failure
    }

go deeper

for a junior

Recall the chain: newBuild() -> forTasks() -> run(), and that run() blocks.

for a middle

Explain the fluent one-shot nature, default-task behavior, and that the ProjectConnection (not the launcher) is the resource to close.

for a senior

Discuss sync vs async run, exception types thrown, and why a daemon-backed connection must be closed deterministically.

for a principal

Frame BuildLauncher within an IDE/CI integration: connection pooling, daemon reuse strategy, and isolating user builds from the host process.

## What the Tooling API is The **Gradle Tooling API** is a client library that lets an external process (an IDE, a CI plugin, a custom tool) drive a Gradle build *without* spawning the `gradle` CLI. It connects to a Gradle **daemon** and exchanges typed objects over that connection. IntelliJ IDEA, Android Studio, Eclipse Buildship and NetBeans all import and run Gradle builds through it. ## The execution entry point: BuildLauncher Once you have an open `ProjectConnection`, the object that *executes* a build is a `BuildLauncher`: ``` ProjectConnection connection -> connection.newBuild() -> BuildLauncher ``` `newBuild()` returns a fresh `BuildLauncher` each time. It is a **fluent, single-use configuration object**: you call setter-style methods that return `this`, then trigger the build. ## Selecting work - `forTasks(String... tasks)` — names the tasks to run, exactly as on the CLI (`forTasks("clean", "test")`). - `forTasks(Task... tasks)` — an overload that accepts `Task` model objects you previously queried from the build model, useful when you already hold task references. - If you call `forTasks()` with no arguments (or never call it), Gradle runs the **default tasks** declared in the build, mirroring a bare `gradle` invocation. ## Running it - `run()` — executes **synchronously**, blocking the calling thread until the build completes, and throws if it fails. - `run(ResultHandler)` — executes **asynchronously**, returning immediately and delivering success/failure via callback. ## Lifecycle and cleanup The `ProjectConnection` is the expensive, daemon-backed resource — *not* the launcher. Open it once, run as many builds as you like, and close it deterministically: ``` try (ProjectConnection c = connector.connect()) { c.newBuild().forTasks("build").run(); } ``` Failing to close leaks daemon connections. A single `BuildLauncher` should be configured then run once; create a new one per build.

  • What happens if you never call forTasks()?
    Gradle runs the project's default tasks, just like invoking `gradle` with no task arguments on the CLI.
  • Is the BuildLauncher reusable for multiple builds?
    No — treat it as one-shot. Call newBuild() again to get a fresh launcher for each build; the ProjectConnection is what you reuse.

saying these in an interview costs you the question

  • Thinking the Tooling API shells out to the `gradle` CLI — it connects to a daemon over a typed API.
  • Forgetting to close the ProjectConnection, leaking daemon connections.
  • Believing run() returns a result object; it returns void and throws on failure.

context

open as a page

How do you pass command-line arguments and capture build output (stdout/stderr) when launching a Gradle build through the Tooling API?

level: middleimportance: must knowfreq 50%

basics

~10 s

Use withArguments("--info", "-PsomeProp=x") to pass CLI args, and setStandardOutput(stream)/setStandardError(stream) to redirect the build's output into your own streams.

open as a page

How do you run a Gradle build asynchronously with the Tooling API, and how do you receive its success or failure?

level: middleimportance: should knowfreq 42%

basics

~10 s

Call run(ResultHandler) instead of run(). It returns immediately and invokes onComplete(Void) on success or onFailure(GradleConnectionException) on failure, on a background thread.

open as a page

How do you cancel an in-progress Gradle build started through the Tooling API, and what does an IDE need to wire up to support a Stop button?

level: seniorimportance: should knowfreq 38%

basics

~10 s

Create a CancellationTokenSource, pass its token to the launcher via withCancellationToken(token), and call source.cancel() to request cancellation. The build then fails with a BuildCancelledException.

open as a page

When building IDE integration, when would you launch work with BuildLauncher.forTasks() versus running a BuildAction, and what trade-offs guide the choice?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

Use BuildLauncher.forTasks() to execute named tasks (the build). Use connection.action(BuildAction) when you need to run logic inside the build and return a computed value, optionally also executing tasks via forTasks() on the action executer.

open as a page