This is an automated email from the ASF dual-hosted git repository.

mmerli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new edb9e79e333 [improve] [pip] PIP-463: Migrate Build System from Maven 
to Gradle (#25359)
edb9e79e333 is described below

commit edb9e79e33378d7ee64c0db86a29f0c2153a96d6
Author: Matteo Merli <[email protected]>
AuthorDate: Tue Mar 24 12:29:17 2026 -0700

    [improve] [pip] PIP-463: Migrate Build System from Maven to Gradle (#25359)
---
 pip/pip-463.md | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 220 insertions(+)

diff --git a/pip/pip-463.md b/pip/pip-463.md
new file mode 100644
index 00000000000..e59c34ff516
--- /dev/null
+++ b/pip/pip-463.md
@@ -0,0 +1,220 @@
+# PIP-463: Migrate Build System from Maven to Gradle
+
+# Background Knowledge
+
+Apache Pulsar currently uses Maven as its build system. The project has grown 
to over 100 modules
+with complex dependency relationships, shaded JARs, NAR packaging, and Docker 
image builds.
+Maven's sequential execution model and limited caching capabilities result in 
long build times
+that impact developer productivity and CI throughput.
+
+[Gradle](https://gradle.org/) is a modern build system used by large-scale 
Java projects
+(e.g., Spring Boot, Micronaut, Apache Kafka). It provides parallel task 
execution,
+fine-grained caching, and incremental compilation out of the box.
+
+# Motivation
+
+The current Maven build has several pain points that affect developer velocity 
and CI efficiency:
+
+**Slow local builds.** A full `mvn install -DskipTests` takes 5-8 minutes on a 
modern machine.
+Developers frequently wait for unrelated modules to rebuild when iterating on 
a single component.
+Maven has no built-in mechanism to skip unchanged modules — it rebuilds 
everything in the reactor.
+
+**Slow CI.** The CI pipeline takes 50-60 minutes end-to-end. Maven's lack of 
caching means
+each CI run starts from scratch. Test jobs must either rebuild everything or 
rely on fragile
+artifact-sharing workarounds.
+
+**Imprecise dependency tracking.** Maven treats the entire module as the unit 
of rebuild.
+Changing a test resource file triggers a full recompile of the module. There 
is no way to
+run "only the tests affected by my change" — developers must run the entire 
test suite
+for a module or manually specify test classes.
+
+**Limited parallelism.** Maven's `-T` flag enables module-level parallelism, 
but tasks within
+a module still run sequentially. The Pulsar build has several bottleneck 
modules (e.g.,
+`pulsar-broker`) where compilation, resource processing, and test execution 
could overlap
+with other modules but don't.
+
+**Complex shading and packaging.** The project uses Maven Shade plugin, NAR 
plugin, and
+custom Ant tasks for packaging. These configurations are verbose, hard to 
maintain, and
+have subtle interactions (e.g., the `ahc-default.properties` content 
replacement for
+AsyncHttpClient requires an Ant `<replace>` task in Maven but is a single 
`filesMatching`
+call in Gradle).
+
+**Poor IDE integration for multi-module builds.** IntelliJ IDEA's Maven import 
for a project
+of Pulsar's size is slow and memory-intensive. Gradle's tooling API provides 
faster,
+more reliable IDE synchronization.
+
+# Goals
+
+## In Scope
+
+- **1:1 functional equivalence with Maven.** The Gradle build produces 
identical artifacts:
+  - Server distribution tarball (`apache-pulsar-X.Y.Z-bin.tar.gz`) with the 
same JARs
+  - Shell distribution tarball
+  - IO connectors distribution (NAR files)
+  - Offloaders distribution (NAR files)
+  - Docker images (`pulsar`, `pulsar-all`, `java-test-image`, 
`pulsar-test-latest-version`)
+  - Shaded client JARs (`pulsar-client`, `pulsar-client-admin`, 
`pulsar-client-all`)
+    verified to contain the same classes and relocations as Maven output
+
+- **All CI tests passing.** Unit tests, integration tests, system tests, shade 
tests (Java 17/21/24),
+  and backward compatibility tests all pass on the Gradle build.
+
+- **Enforced dependency management.** A `pulsar-dependencies` platform module 
(Gradle's equivalent
+  of Maven's `dependencyManagement`) ensures consistent dependency versions 
across all modules.
+
+- **Version catalog.** A single `gradle/libs.versions.toml` file defines all 
dependency coordinates
+  and versions, replacing scattered version properties across 100+ POM files.
+
+- **CI workflow migration.** All GitHub Actions workflows converted from Maven 
to Gradle commands.
+
+## Out of Scope
+
+- Changing the project's module structure or merging/splitting modules
+- Migrating to Kotlin DSL for production source code
+- Gradle-specific optimizations beyond what Maven provides (e.g., build cache 
server,
+  remote caching) — these are future enhancements enabled by the migration
+- Removing the ability to build individual modules in isolation
+
+# High Level Design
+
+The migration introduces Gradle build scripts alongside (and eventually 
replacing) the existing
+Maven POM files. The approach is:
+
+1. **Add Gradle build files** for all modules (`build.gradle.kts`, 
`settings.gradle.kts`,
+   `gradle/libs.versions.toml`)
+2. **Convert CI workflows** from Maven to Gradle commands
+3. **Remove Maven files** (`pom.xml`, `mvnw`, `.mvn/`)
+
+The Gradle build is structured as:
+
+```
+settings.gradle.kts          # Module includes and plugin repositories
+build.gradle.kts              # Root build: common config, enforced platform
+gradle/libs.versions.toml     # Version catalog (single source of truth for 
versions)
+pulsar-dependencies/          # Enforced platform module (replaces 
dependencyManagement)
+<module>/build.gradle.kts     # Per-module build script
+```
+
+Key design decisions:
+
+- **Shadow plugin** for shaded JARs (replaces Maven Shade), with 
`filesMatching` for
+  property file content relocation
+- **NAR plugin** (`io.github.merlimat.nar`) for connector packaging
+- **LightProto plugin** for protobuf/lightproto code generation
+- **Conditional project includes** for shade test modules (avoids implicit 
parent project conflicts)
+- **Enforced platform** (`pulsar-dependencies`) for version pinning across all 
modules
+
+# Detailed Design
+
+## Design & Implementation Details
+
+### Build Performance Improvements
+
+| Aspect | Maven | Gradle |
+|--------|-------|--------|
+| Incremental compilation | No | Yes — only recompiles changed files |
+| Task-level caching | No | Yes — skips tasks whose inputs haven't changed |
+| Parallel execution | Module-level only (`-T`) | Task-level (automatic 
dependency graph) |
+| Configuration caching | No | Yes — reuses build configuration across runs |
+| Local build cache | No | Yes — persists across builds |
+| Remote build cache | No | Yes — shared across CI and developers (future) |
+
+**Expected impact:**
+- Local incremental builds (after initial): **seconds** instead of minutes
+- CI with caching: **30-50% faster** (exact numbers depend on cache hit rates)
+- "Build only what I need to test": `./gradlew :pulsar-broker:test` builds only
+  the broker and its dependencies, skipping unrelated modules entirely
+
+### Develocity Integration
+
+Gradle provides native integration with 
[Develocity](https://gradle.com/develocity/)
+(formerly Gradle Enterprise), hosted by the ASF at `develocity.apache.org`. 
Every CI
+build automatically publishes a build scan that provides:
+
+- **Test execution details**: per-test timings, pass/fail status, output logs, 
and
+  stack traces — all searchable and filterable without downloading CI artifacts
+- **Task execution timeline**: visual breakdown of what ran, what was cached, 
and what
+  was up-to-date, making it easy to identify bottleneck tasks
+- **Dependency resolution**: full dependency tree with conflict resolution 
details
+- **Build comparison**: diff two builds to see what changed in task execution 
or outputs
+- **Failure analysis**: aggregated view of flaky tests across builds
+
+Example build scan from the PoC CI run:
+[https://develocity.apache.org/s/h6ckzn3nn4w2s](https://develocity.apache.org/s/h6ckzn3nn4w2s)
+
+This level of observability is not available with the Maven build today.
+
+### Dependency Management
+
+Maven's `dependencyManagement` in the root POM is replaced by:
+
+1. **Version catalog** (`gradle/libs.versions.toml`): Defines all dependency 
coordinates
+   and version numbers in one file. Modules reference dependencies as 
`libs.netty.buffer`
+   instead of hardcoded group:artifact:version strings.
+
+2. **Enforced platform** (`pulsar-dependencies`): A `java-platform` module 
that creates
+   version constraints from the catalog. Applied to all subprojects via
+   `implementation(enforcedPlatform(project(":pulsar-dependencies")))`. This 
ensures
+   transitive dependencies are pinned to the same versions Maven would resolve.
+
+### Shaded JAR Configuration
+
+The Shadow plugin replaces Maven Shade. Key differences handled:
+
+- **AsyncHttpClient properties**: Maven uses Ant `<replace>` to fix property 
name prefixes
+  in `ahc-default.properties`. Gradle uses `filesMatching { filter { } }`.
+- **Dependency include/exclude**: Shadow's `dependencies { include/exclude }` 
DSL replaces
+  Maven Shade's `<includes>/<excludes>`.
+- **Relocation**: Shadow's `relocate()` is functionally identical to Maven 
Shade's.
+
+### NAR Packaging
+
+A custom NAR Gradle plugin (`io.github.merlimat.nar`) handles connector 
packaging.
+Global exclusions for platform modules (provided by `java-instance.jar` at 
runtime)
+are configured in the root `build.gradle.kts`.
+
+### Module-Specific Overrides
+
+Some modules require version overrides that differ from the enforced platform:
+
+- **`kinesis-kpl-shaded`**: Forces `protobuf-java:4.29.0` (KPL requires 
protobuf 4.x,
+  while Pulsar uses 3.x). The protobuf is relocated so no runtime conflict.
+- **`jclouds-shaded`**: Forces Guice 7.0.0, `jakarta.annotation-api:3.0.0`,
+  `jakarta.ws.rs-api:3.1.0`, `jakarta.inject-api:2.0.1` (jclouds 2.6.0 requires
+  Jakarta EE 10+ APIs). All are bundled in the shadow JAR.
+
+## Public-facing Changes
+
+### Configuration
+
+No new broker/client configuration options. The build system change is 
transparent to users.
+
+### CLI
+
+- `mvn` commands replaced by `./gradlew` commands in documentation and scripts
+- `src/set-project-version.sh` updated to modify `gradle/libs.versions.toml`
+
+### Binary Artifacts
+
+Artifacts are functionally identical. Minor differences:
+- Some shaded JARs may have slightly different class counts due to Shadow vs 
Shade plugin
+  differences in handling `package-info.class` files (no runtime impact)
+
+# Security Considerations
+
+No security implications. The build system change does not affect Pulsar's 
runtime
+security model, authentication, or authorization.
+
+The Gradle wrapper (`gradlew`) is committed to the repository with a 
checksum-verified
+distribution URL, following the same security model as the Maven wrapper.
+
+# General Notes
+
+The implementation PR demonstrates full CI green status across all test suites,
+confirming functional equivalence with the Maven build.
+
+# Links
+
+* Proof of Concept PR (CI fully green): 
https://github.com/merlimat/pulsar/pull/16
+* Mailing List discussion thread: [link]
+* Mailing List voting thread: [link]

Reply via email to