Skip to content

Latest commit

 

History

44 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jar2native

Turn any executable JAR or WAR into a standalone native binary — no Java required at runtime.

中文文档 · Download Releases

jar2native packages a Java JAR/WAR into a self-contained executable with an embedded JRE. The output is a single binary file that runs anywhere the target platform supports, with zero external dependencies.

Use Cases

  • Distribute Java apps as native binaries — Ship a single executable instead of requiring users to install Java.
  • Simplify deployment — One file to copy and run. No java -jar, no CLASSPATH, no JRE setup.
  • Cross-platform builds — Package for Linux, macOS, or Windows from a single command.
  • Embed in containers — Smaller footprint than a full JDK image; just copy the binary in.

How It Works

  1. Inspect — Parse MANIFEST.MF, detect Spring Boot, validate Main-Class.
  2. Resolve JDK — From --jdk, JAVA_HOME, or auto-search standard platform locations.
  3. Build runtime — Full JRE via jlink (JDK 9+) or copy legacy JRE (Java 8). Use -analyze to run jdeps for module trimming.
  4. Assemble payload — Deterministic payload.zip (app + JRE + manifest.json) with fixed timestamps and content hashing.
  5. Stamp runner — A precompiled, platform-specific runner reads its configuration from the binary tail; jar2native appends the payload and JSON without compiling Go.
  6. No runtime toolchain — Supported targets use only the embedded precompiled runner; packaging never invokes go build.

Usage

Use a Release binary

Download the executable for your platform from the Releases page, then run it directly:

# Linux/macOS
chmod +x jar2native

# Package a JAR into ./myapp
./jar2native -jar app.jar -o myapp

# Package a WAR into ./myapp
./jar2native -jar app.war -o myapp

# Run the generated self-contained application
./myapp

On Windows, use PowerShell. Windows executables are generated with the .exe suffix:

# Package a JAR into .\myapp.exe
.\jar2native.exe -jar app.jar -o myapp

# Run the generated self-contained application
.\myapp.exe

The input JAR/WAR must be executable and contain a Main-Class. The packaging machine needs a compatible target-platform JDK. Release builds include the generic runners, so packaging does not need Go; the generated application needs neither Go, Java, nor a JRE at runtime.

Build from source

# Build the tool and its embedded platform runners (requires Go 1.21+)
make build

# Package a JAR — produces ./myapp
./jar2native -jar app.jar -o myapp

# Package a WAR (must be executable — have Main-Class)
./jar2native -jar app.war -o myapp

# With JVM arguments
./jar2native -jar app.jar -o myapp --jvm-args "-Xmx2g -Dfile.encoding=UTF-8"

# Run the result
./myapp

On Windows, build and run with:

make build
.\jar2native.exe -jar app.jar -o myapp
.\myapp.exe

--platform selects the embedded runner and JRE target platform. The embedded JRE is built by jlink from the JDK selected by --jdk or JAVA_HOME, so it must be a JDK for the target platform. The current implementation does not download or switch JDKs automatically. A binary built for one OS cannot run on another OS; build the matching target package instead.

Requirements

  • Build jar2native: Go 1.21+ (runs make build, which creates embedded runners)
  • Package an application: JDK 9+ (for jlink) or JDK 8 (legacy JRE copy); no Go
  • Run the generated application: No dependencies. The output binary is fully self-contained — no Go, Java, JRE, or external runtime.

Platform Support

Platform JDK Source Runtime Image Status
macOS (amd64/arm64) Homebrew, .jdks, /Library/Java/... jlink or legacy copy
Linux (amd64/arm64) /usr/lib/jvm, /usr/java, .jdks jlink or legacy copy
Windows (amd64) Program Files\Java, .jdks jlink or legacy copy

amd64 means x86-64 and works on both Intel 64-bit and AMD 64-bit CPUs. The Windows target currently documented and tested is windows/amd64; 32-bit x86 (windows/386) is not currently supported because it also requires a compatible 32-bit JDK/JRE.

The tool auto-detects the JDK from standard locations per OS. Override with --jdk or JAVA_HOME.

CLI Options

  -jar            Path to JAR or WAR (required)
  -o, --output     Output binary name (default: same as input without extension)
      --jdk        JDK home path (default: JAVA_HOME or auto-detect)
      --platform   Target os/arch (default: host, e.g. linux/amd64)
      --jre-mode   JRE build mode: auto, jlink, copy (default: auto)
      -analyze      Run jdeps to trim unused modules (default: full JRE)
      --modules    Extra JDK modules, comma-separated (use with -analyze)
      --jvm-args   JVM args passed to runner (e.g. "-Xmx2g -Dfile.encoding=UTF-8")
      --verbose    Verbose output

Key Design

  • Full JRE by defaultjdeps often fails on real-world WARs (obfuscated classes, new bytecode tags). Default to full JRE; jdeps is opt-in via -analyze.
  • Deterministic builds — Fixed timestamp (1980-01-01), normalized permissions (0755/0644), deflate compression. Same input always produces byte-identical output.
  • PID-based cache lock — Stale locks from killed processes are automatically reclaimed. Atomic extract to temp dir + rename.
  • Single source of truthrunner/shared.go handles zip-slip protection, cache management, and manifest verification. It's compiled normally by the tool AND written verbatim into each generated runner, eliminating build-time/runtime drift.

Project Structure

jar2native/
├── main.go                 # CLI entry, config, platform, logging, orchestration
├── go.mod
├── Makefile
├── jdk/jdk.go              # JDK discovery, version detection, validation, jlink compress
├── runtime/builder.go      # jlink full build + module trim + legacy JRE copy
├── payload/payload.go     # Artifact inspection, manifest, deterministic payload.zip, zip-slip extraction
├── analyzer/analyzer.go   # jdeps module dependency analysis (opt-in)
├── runner/runner.go       # Runner stamping and legacy template helpers
├── runner/stamp.go        # Deterministic payload/config trailer format
├── runner/generic/main.go # Runtime-configured precompiled runner
├── runner/bin/            # make runners output, embedded into jar2native
├── runner/shared.go       # Shared zip-slip, cache, and manifest logic
└── tests/e2e/run.sh       # End-to-end test (3-line shell script)

Testing

# Build the tool first
make build

# Run e2e — downloads Jenkins WAR, packages it, runs the binary
bash tests/e2e/run.sh

License

MIT

About

跨平台工具:将 JAR/WAR 打包为独立可执行文件(EXE/Linux 二进制),内置最小 JRE,用户无需安装 Java!🚀 Cross-platform tool to package JAR/WAR into standalone executables with a minimal JRE — no Java installation required! 🚀

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages