Skip to content

Fixes issue with leading zero in version #449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ target/*
.idea
.java-version
.mvn/wrapper/maven-wrapper.jar
.claude
110 changes: 110 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is the Launch4j Maven Plugin, which wraps Java JAR files in Windows executables using the Launch4j utility. The plugin integrates Launch4j functionality into the Maven build process, allowing developers to create Windows executables as part of their Maven builds.

## Core Architecture

### Main Components

- **Launch4jMojo.java** (`src/main/java/com/akathist/maven/plugins/launch4j/Launch4jMojo.java`): The main Maven plugin class that handles the launch4j goal execution
- **Configuration Classes**: POJO classes for plugin configuration (ClassPath, Jre, VersionInfo, etc.)
- **Generator Classes** (`src/main/java/com/akathist/maven/plugins/launch4j/generators/`): Handle generation of copyright and version info
- **ResourceIO** (`src/main/java/com/akathist/maven/plugins/launch4j/tools/ResourceIO.java`): Utility for handling resources

### Key Plugin Structure

- Plugin binds to the `package` phase by default
- Supports both GUI and console application types
- Handles dependency resolution in `runtime` and `compile` scopes
- Thread-safe execution with optional parallel execution control
- Can load configuration from external Launch4j XML files via `<infile>` parameter

## Build and Development Commands

### Standard Maven Commands
```bash
# Build the plugin
mvn clean compile

# Run tests
mvn test

# Full build with tests
mvn clean test

# Package the plugin
mvn clean package

# Install to local repository
mvn clean install
```

### Testing
- Uses JUnit 4.13.2 with JUnitParams and Mockito
- Maven Plugin Testing Harness for integration tests
- Test files located in `src/test/java/`

### Documentation Generation
```bash
# Generate plugin documentation site
mvn site

# Generate plugin documentation reports
mvn project-info-reports:dependencies
mvn plugin:report
```

## Important Configuration Details

### Maven Plugin Configuration
- The plugin uses Maven Plugin API 3.9.11
- Minimum Java version: 1.8
- Launch4j version: 3.50
- Supports Maven 3.6.x and above

### Key Plugin Parameters
- `<infile>`: Load configuration from external Launch4j XML file (default: `${project.basedir}/src/main/resources/${project.artifactId}-launch4j.xml`)
- `<outfile>`: Output executable path (default: `${project.build.directory}/${project.artifactId}.exe`)
- `<jar>`: JAR file to wrap (default: `${project.build.directory}/${project.build.finalName}.jar`)
- `<skip>`: Skip plugin execution (can also use `-DskipLaunch4j` property)
- `<disableVersionInfoDefaults>`: Disable automatic VersionInfo defaults

### ClassPath Configuration
- `<addDependencies>`: Include Maven dependencies in classpath (default: true)
- `<jarLocation>`: Prefix for JAR paths (useful for lib/ directories)
- `<preCp>` and `<postCp>`: Add custom classpath entries

## Architecture Notes

### Plugin Execution Flow
1. Validates configuration and parameters
2. Resolves platform-specific Launch4j binaries from Maven repositories
3. Builds configuration object from Maven plugin parameters
4. Optionally loads external Launch4j configuration file
5. Applies default values for VersionInfo if not disabled
6. Executes Launch4j Builder to create Windows executable

### Platform Support
- Runs on Windows, Linux, macOS, and Solaris
- Downloads platform-specific Launch4j binaries as Maven artifacts
- Linux 64-bit uses `linux64` platform, 32-bit uses `linux32`

### Version Info Defaults
The plugin automatically provides default values for VersionInfo parameters based on Maven project properties:
- File version from project version
- Product name from project name
- Company/copyright from organization
- Description from project description

This behavior can be disabled with `<disableVersionInfoDefaults>true</disableVersionInfoDefaults>`.

## Documentation References

- Main documentation: `src/main/resources/README.adoc`
- Parameter reference: `src/main/resources/MOJO.md`
- Version info details: `src/main/resources/VERSIONINFO.md`
- Launch4j documentation: http://launch4j.sourceforge.net/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ private Launch4jFileVersionGenerator() {
* For shorter versions like "x.x.x" it will append zeros (to the 4th level) at the end like "x.x.x.0".
* Every text flag like "-SNAPSHOT" or "-alpha" will be cut off.
* Too many nested numbers (more than 4 levels) will be cut off as well: "1.2.3.4.5.6" into "1.2.3.4".
* Leading zeros in version components are stripped to avoid "digit exceeds base" errors in windres.
* <p>
* Param should be taken from MavenProject property:
* @param projectVersion as ${project.version}
Expand All @@ -53,7 +54,8 @@ public static String generate(String projectVersion) {
}

String versionLevels = removeTextFlags(projectVersion);
String limitedVersionLevels = cutOffTooManyNestedLevels(versionLevels);
String normalizedVersionLevels = stripLeadingZeros(versionLevels);
String limitedVersionLevels = cutOffTooManyNestedLevels(normalizedVersionLevels);

return appendMissingNestedLevelsByZeros(limitedVersionLevels);
}
Expand All @@ -68,6 +70,32 @@ private static String removeTextFlags(String version) {
}
}

/**
* Strips leading zeros from each version component to prevent "digit exceeds base" errors in windres.
* For example, "302.08.01" becomes "302.8.1".
* Special case: "0" remains "0" (doesn't become empty string).
*
* @param version version string with components separated by dots
* @return version string with leading zeros stripped from each component
*/
private static String stripLeadingZeros(String version) {
String[] levels = version.split("\\.");

for (int i = 0; i < levels.length; i++) {
// Parse as integer and convert back to string to remove leading zeros
// This handles the special case where "000" becomes "0"
try {
levels[i] = String.valueOf(Integer.parseInt(levels[i]));
} catch (NumberFormatException e) {
// This should not happen given the regex validation, but keep original if it does
// The existing validation should have caught invalid numbers already
throw new IllegalArgumentException("Invalid number format in version component: " + levels[i], e);
}
}

return String.join(".", levels);
}

private static String cutOffTooManyNestedLevels(String versionLevels) {
String[] levels = versionLevels.split("\\.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,40 @@ public void shouldCutOffTooManyNestedDigits(String projectVersion, String expect
// then
assertEquals(expected, launch4jFileVersion);
}

@Test
@Parameters({
"302.08, 302.8.0.0",
"1.02.3, 1.2.3.0",
"01.02.03.04, 1.2.3.4",
"10.00.01, 10.0.1.0",
"0.08.09, 0.8.9.0",
"302.08-SNAPSHOT, 302.8.0.0",
"1.02.03.04.05.06, 1.2.3.4",
"000.001.002, 0.1.2.0"
})
public void shouldStripLeadingZerosFromVersionComponents(String projectVersion, String expected) {
// when
final String launch4jFileVersion = Launch4jFileVersionGenerator.generate(projectVersion);

// then
assertEquals(expected, launch4jFileVersion);
}

@Test
@Parameters({
"0, 0.0.0.0",
"00, 0.0.0.0",
"000, 0.0.0.0",
"0.0, 0.0.0.0",
"00.00, 0.0.0.0",
"000.000, 0.0.0.0"
})
public void shouldHandleZeroVersionsCorrectly(String projectVersion, String expected) {
// when
final String launch4jFileVersion = Launch4jFileVersionGenerator.generate(projectVersion);

// then
assertEquals(expected, launch4jFileVersion);
}
}