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

bin+lib pman

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

9 releases

new 0.2.1 Mar 5, 2026
0.2.0 Mar 4, 2026
0.1.1 Mar 2, 2026
0.0.6 Mar 1, 2026
0.0.3 Feb 28, 2026

#336 in Testing

Apache-2.0

125KB
2.5K 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

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     (not yet implemented)
        │
        └── 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

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–29MB
~369K SLoC