skip to content

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