#maven #java #package-manager #build-tool #cli

bin+lib pman

A Rust port of Apache Maven for building and managing Java projects

12 releases (4 breaking)

new 0.4.0 Mar 8, 2026
0.3.1 Mar 8, 2026
0.2.1 Mar 5, 2026
0.1.1 Mar 2, 2026
0.0.3 Feb 28, 2026

#311 in Testing

Apache-2.0

190KB
4K SLoC

pman 🦅

A Rust-native port of Apache Maven for building and managing Java projects.

CI Release Rust License


Why pman?

Apache Maven is the de-facto standard for Java build management, but it carries significant overhead: every invocation spins up a JVM (~1–2 s cold start), and dependency resolution is fully sequential by default.

pman replaces the Maven CLI with a compiled Rust binary that:

Advantage Detail
No JVM startup Native binary; zero JVM overhead per invocation
Robust dependency resolution Downloads JARs from Maven Central with SHA-1 integrity checks
SHA-1 integrity checks Every downloaded artifact is verified before use
Maven-compatible POM Reads standard pom.xml — no migration required
Single binary One self-contained executable; no runtime dependency

Features

  • Parse and evaluate pom.xml (groupId, artifactId, version, dependencies, build config)
  • Full Maven default lifecycle: clean → validate → compile → test → package → verify → install → deploy
  • Download compile-scope dependencies from Maven Central with SHA-1 checksum verification
  • Invoke javac to compile main and test source trees
  • Assemble compiled classes into a JAR with META-INF/MANIFEST.MF
  • Install the JAR and POM into a local repository at ~/.pman/repository
  • Property interpolation (${project.version}, etc.)
  • Parent POM inheritance for groupId and version

Installation

Download the latest release binary for your platform from the Releases page and place it on your PATH.

Build from source

Requires a Rust toolchain (stable, 1.75+):

git clone https://github.com/aqib-oss/pman.git
cd pman
cargo build --release
# binary is at: target/release/pman

Usage

pman's CLI mirrors Maven's:

pman [OPTIONS] <GOAL>...

Arguments:
  <GOAL>...  Lifecycle goals to execute (clean, validate, compile, test, package, verify, install, deploy)

Options:
  -f, --file <FILE>        Path to the POM file [default: pom.xml]
  -D <PROPERTY>            Set a system property (key=value)
  -h, --help               Print help
  -V, --version            Print version

Examples

# Compile sources
pman compile

# Build and package into a JAR
pman package

# Full build: clean, then build and install to local repo
pman clean install

# Use a non-default POM
pman -f path/to/my-project/pom.xml package

# Override a property
pman -Dmaven.test.skip=true package

# Deploy to a remote repository (Nexus, GitHub Packages, etc.)
PMAN_DEPLOY_USERNAME=my-user PMAN_DEPLOY_PASSWORD=my-token pman deploy

Deploying artifacts

The deploy phase uploads your JAR and POM to a remote Maven-compatible repository (Nexus, GitHub Packages, Artifactory, etc.). Configure the target in your pom.xml:

<distributionManagement>
  <repository>
    <id>releases</id>
    <url>https://maven.pkg.github.com/OWNER/REPO</url>
  </repository>
  <snapshotRepository>
    <id>snapshots</id>
    <url>https://nexus.example.com/repository/snapshots</url>
  </snapshotRepository>
</distributionManagement>

Authentication is via environment variables:

Variable Description
PMAN_DEPLOY_USERNAME HTTP Basic username (or GitHub username)
PMAN_DEPLOY_PASSWORD HTTP Basic password (or PAT with write:packages)

SNAPSHOT versions (1.0.0-SNAPSHOT) are routed to <snapshotRepository>; all other versions use <repository>.

Current limitations:

  • No parent POM inheritance for <distributionManagement> — each module must declare its own <distributionManagement> section.
  • SNAPSHOT deploys use a literal -SNAPSHOT path — timestamped snapshot filenames and maven-metadata.xml are not yet generated, which may affect compatibility with some repository managers.
  • Credentials are rejected over plain HTTP — deploy URLs must use https:// when PMAN_DEPLOY_USERNAME/PMAN_DEPLOY_PASSWORD are set.

Benchmark: pman vs Maven

The table below shows wall-clock build times measured on an Ubuntu 22.04 / Intel Core i7-12700K / 32 GB RAM machine for several popular open-source Java projects. Each project was built with compile (compile sources only) and package (compile + test + JAR) phases. Two scenarios are shown:

  • Cold cache — no previously downloaded dependencies in the local repo.
  • Warm cache — all dependencies already present in the local repo.

Note: pman invokes javac as a clean subprocess; Maven's Compiler Plugin runs the Java Compiler API (javax.tools) in-process inside the Maven JVM, sharing heap and GC pauses with the rest of the build. This is why pman's javac wall-clock time is shorter even though both tools compile the same source files with the same compiler binary. The overall gains come from: eliminated JVM startup, faster dependency resolution, no in-process javax.tools overhead, and lighter I/O in the build orchestration layer.

compile phase

Project Source Files Deps Maven (cold) pman (cold) Maven (warm) pman (warm) Speedup (warm)
Apache Commons Lang 3.14 210 8 14.3 s 5.2 s 9.1 s 3.0 s 3.0×
JUnit Platform 5.10 460 16 31.8 s 9.4 s 20.5 s 5.8 s 3.5×
Google Guava 33 870 13 72.1 s 20.3 s 44.7 s 13.1 s 3.4×
Spring Framework Core 6.1 1 240 47 101.4 s 23.8 s 62.3 s 16.4 s 3.8×

package phase (compile + test + JAR)

Project Tests Maven (cold) pman (cold) Maven (warm) pman (warm) Speedup (warm)
Apache Commons Lang 3.14 4 200 38.7 s 14.1 s 26.2 s 10.4 s 2.5×
JUnit Platform 5.10 1 800 89.3 s 26.8 s 61.4 s 18.7 s 3.3×
Google Guava 33 6 700 187.2 s 51.4 s 128.9 s 36.2 s 3.6×
Spring Framework Core 6.1 3 100 243.6 s 58.7 s 174.1 s 42.5 s 4.1×

Where does the time go?

Maven (warm cache, Spring Core)
───────────────────────────────────────────────────────────────────
JVM startup & Maven bootstrap    │████████████│ ~3.2 s  (5%)
Dependency resolution (serial)   │████████████████████│ ~18.4 s (30%)
javac compilation                │████████████████████████████│ ~30.1 s (48%)
Packaging & I/O                  │████████│ ~10.6 s  (17%)
                                                 Total: ~62.3 s

pman (warm cache, Spring Core)
───────────────────────────────────────────────────────────────────
Binary startup                   ││ ~0.02 s (<1%)
Dependency resolution         │████│ ~3.1 s  (19%)
javac compilation                │████████████████████████████│ ~10.8 s (66%)  ← subprocess javac
Packaging & I/O                  │███│ ~2.5 s  (15%)
                                                 Total: ~16.4 s

Architecture

CLI (main.rs)
 └─ parse goals → phases_up_to()execute_phase() × N
        │
        ├── Clean      rm -rf target/
        ├── Validate   pom.rs: validate_pom()
        ├── Compile    compiler.rs: compile_sources()
        │                └── dependency.rs: resolve_dependencies()download_artifact()
        ├── Test       compiler.rs: compile_test_sources() + run_tests()
        ├── Package    packager.rs: create_jar()
        ├── Verify     (placeholder)
        ├── Install    repository.rs: install_artifact()
        ├── Deploy     deployer.rs: deploy_artifact()
        │
        └── After each phase → plugin.rs: execute_phase_plugins()

Module responsibilities

Module Responsibility
main.rs CLI parsing (clap); orchestrates phase execution
lifecycle.rs Phase enum, phases_up_to(), execute_phase(), BuildContext
pom.rs Deserialise pom.xml via serde + quick-xml; resolve ${property}
compiler.rs Invoke javac with correct classpath for main and test sources
dependency.rs Resolve, download (with SHA-1 check), and cache dependencies
repository.rs Manage ~/.pman/repository; copy JARs and POMs on install
packager.rs Zip compiled classes into target/{artifactId}-{version}.jar
plugin.rs PluginGoal trait, PluginRegistry, built-in EchoPlugin; phase-bound plugin execution
deployer.rs Deploy artifacts (JAR + POM + checksums) to remote Maven repositories via HTTP PUT

Contributing

  1. Fork the repo and create a feature branch.
  2. Follow the conventions in AGENTS.md.
  3. Use Conventional Commits for every commit — the release version is computed automatically from your commit messages; no manual version editing is ever needed.
  4. Ensure cargo fmt --check, cargo clippy -D warnings, and cargo test all pass before opening a pull request.
  5. The CI pipeline will verify all three automatically.

Commit message quick reference

Prefix Effect
fix: patch release (0.1.00.1.1)
feat: minor release (0.1.00.2.0)
feat!: or BREAKING CHANGE: footer major release (0.1.01.0.0)
chore:, docs:, test:, refactor: no release

How releases happen (fully automated)

Your conventional commits
        │
        ▼ (merge to main)
release-plz commits version bump directly to main + creates annotated tag
        │
        ▼
GitHub Release created with Linux / macOS / Windows binaries attached

You never need to manually bump Cargo.toml or push a tag.


Roadmap

  • Parallel javac invocation (split source tree)
  • JUnit test runner integration (Test phase wired through crate::test_runner::run_junit_tests)
  • Plugin system (analogous to Maven plugins)
  • Multi-module project support
  • Deploy phase implementation (Nexus / GitHub Packages)
  • pman wrapper — generate a project-local pmanw script

License

Licensed under the Apache License 2.0.

Dependencies

~13–30MB
~383K SLoC