diff --git a/Configure.pl b/Configure.pl index d482ced2d..6166ee4ce 100755 --- a/Configure.pl +++ b/Configure.pl @@ -7,19 +7,42 @@ use Getopt::Long; use URI::Escape; -# This script is designed to manage configuration settings and dependencies -# for a Java project. It provides options to update configuration values -# in a Java configuration file and manage dependencies using Maven coordinates. -# The script can search for JDBC drivers by class name or use direct Maven -# coordinates to update build files like build.gradle or pom.xml. - -# Note: This script can be run using the following commands: +# PerlOnJava Configuration Script +# +# This script manages configuration settings and dependencies for the PerlOnJava project. +# It updates Configuration.java (the single source of truth for version info) and +# propagates version changes to all relevant files in the repository. +# +# USAGE: +# +# ./Configure.pl # Show current configuration +# ./Configure.pl -D version=5.43.0 # Update version everywhere +# ./Configure.pl --upgrade # Upgrade dependencies to latest versions +# +# VERSION UPDATE BEHAVIOR: +# +# The 'version' field is special - when updated, it also: +# - Updates build.gradle and pom.xml version fields +# - Updates jperl and jperl.bat launcher scripts +# - Updates README.md feature support line +# - Replaces all occurrences of perlonjava-X.Y.Z.jar in documentation +# - Updates specific version references in Config.pm (paths, compatibility checks) +# +# Safety measures to protect historical version references: +# - Only versions with minor >= 40 can be updated (protects 5.10, 5.38, etc.) +# - Excluded directories: src/main/perl/, dev/ +# - Excluded files: docs/about/changelog.md +# - Config.pm uses targeted patterns (not blanket replacement) # -# ./Configure.pl -D perlVersion=v5.40.0 -# ./Configure.pl -D jarVersion=3.0.1 +# Note: gitCommitId and gitCommitDate are managed by the build system (Gradle/Maven) +# and should NOT be set via this script. # +# DEPENDENCY MANAGEMENT: +# +# ./Configure.pl --search org.h2.Driver # Search for JDBC driver +# ./Configure.pl --direct com.h2database:h2:2.2.224 # Add direct Maven coordinates -# Define supported actions and options for both dependency management and configuration +# Define supported actions and options my $search = 0; # Flag to indicate if a search for JDBC driver by class name is requested my $direct = 0; # Flag to indicate if direct Maven coordinates are provided my $verbose = 0; # Flag to enable verbose output for debugging @@ -27,7 +50,7 @@ my %config; # Hash to store key-value pairs for configuration updates my $help = 0; # Flag to show help message -my $java_config_filename = 'src/main/java/org/perlonjava/Configuration.java'; +my $java_config_filename = 'src/main/java/org/perlonjava/core/Configuration.java'; # Parse command-line options Getopt::Long::Configure("no_ignore_case"); @@ -64,6 +87,14 @@ sub show_help { -h, --help Show this help message -D key=value Set configuration value + Supported configuration keys: + version - PerlOnJava version (e.g., 5.43.0) + Updates Configuration.java, build files, and all JAR references + + Read-only keys (managed by build system): + gitCommitId - Git commit hash (set during build) + gitCommitDate - Git commit date (set during build) + Dependency Management Options: --search Search for JDBC driver by class name --direct Use direct Maven coordinates @@ -71,10 +102,11 @@ sub show_help { --upgrade Upgrade dependencies to their latest versions Examples: - ./Configure.pl -D strict_mode=true -D enable_optimizations=false - ./Configure.pl --search org.h2.Driver + ./Configure.pl # Show current configuration + ./Configure.pl -D version=5.43.0 # Update version everywhere + ./Configure.pl --search org.h2.Driver # Search for JDBC driver ./Configure.pl --direct com.h2database:h2:2.2.224 - ./Configure.pl --upgrade + ./Configure.pl --upgrade # Upgrade all dependencies '; exit; } @@ -83,7 +115,8 @@ sub show_help { sub show_config { my $java_config = read_file($java_config_filename); print "Current configuration:\n\n"; - while ($java_config =~ /public static final (\w+)\s+(\w+)\s*=\s*(.+?);/g) { + # Use robust pattern that handles quoted strings and simple values + while ($java_config =~ /public static final (\w+)\s+(\w+)\s*=\s*("[^"]*"|[^;]+);/g) { print "$2 = $3\n"; } exit; @@ -97,38 +130,208 @@ sub update_configuration { foreach my $key (keys %$config) { my $value = $config->{$key}; + + # Prevent setting git fields - they are managed by the build system + if ($key eq 'gitCommitId' || $key eq 'gitCommitDate') { + warn "Warning: $key is managed by the build system and cannot be set manually.\n"; + warn "These values are injected during Gradle/Maven builds.\n"; + next; + } + # Handle string values vs boolean/numeric values - $value = "\"$value\"" if $value !~ /^(?:true|false|\d+)$/; - - if ($content =~ /public static final \w+\s+\Q$key\E\s*=\s*.+?;/) { - my $old_value = $1 if $content =~ /public static final \w+\s+\Q$key\E\s*=\s*"(.+?)";/; - $content =~ s/(public static final \w+\s+\Q$key\E\s*=\s*).+?;/$1$value;/; - print "Updated $key = $value\n"; - - # Special handling for jarVersion updates - if ($key eq 'jarVersion' && $old_value) { - my $old_jar = "perlonjava-$old_value.jar"; - my $new_jar = "perlonjava-" . $value =~ s/"//gr . ".jar"; - - # Find and update all files in the repository - my @files = `find . -type f -not -path "*/\.*"`; - foreach my $file (@files) { - chomp $file; - next if -B $file; # Skip binary files - - my $file_content = read_file($file); - if ($file_content =~ s/\Q$old_jar\E/$new_jar/g) { - write_file($file, $file_content); - print "Updated jar version in $file\n"; - } - } + my $quoted_value = $value; + $quoted_value = "\"$value\"" if $value !~ /^(?:true|false|\d+)$/; + + # Use robust pattern that matches quoted strings or simple values + # Pattern: "..." for strings, or non-semicolon chars for booleans/numbers + if ($content =~ /public static final \w+\s+\Q$key\E\s*=\s*("[^"]*"|[^;]+);/) { + # Extract old value for special handling + my $old_value; + if ($content =~ /public static final \w+\s+\Q$key\E\s*=\s*"([^"]*)";/) { + $old_value = $1; } + + # Special handling for 'version' field - validate BEFORE making changes + if ($key eq 'version' && $old_value) { + validate_version_change($old_value, $value); + } + + # Perform the replacement + $content =~ s/(public static final \w+\s+\Q$key\E\s*=\s*)("[^"]*"|[^;]+);/${1}$quoted_value;/; + print "Updated $key = $quoted_value\n"; + + # Special handling for 'version' field - propagate to all relevant files + if ($key eq 'version' && $old_value) { + update_version_everywhere($old_value, $value); + } + } else { + warn "Warning: Key '$key' not found in $file\n"; } } write_file($file, $content); print "\nConfiguration updated successfully\n"; } + +# Function to validate version changes before applying them +sub validate_version_change { + my ($old_version, $new_version) = @_; + + # Safety check: only allow updating versions where minor >= 40 + # This prevents accidentally replacing historical Perl version references + # like 5.10, 5.38, etc. in documentation + my ($old_major, $old_minor) = $old_version =~ /^(\d+)\.(\d+)/; + my ($new_major, $new_minor) = $new_version =~ /^(\d+)\.(\d+)/; + + if (!defined $old_minor || $old_minor < 40) { + die "Error: Cannot update from version $old_version (minor version must be >= 40)\n" . + "This prevents accidentally replacing historical Perl version references.\n"; + } + if (!defined $new_minor || $new_minor < 40) { + die "Error: Cannot update to version $new_version (minor version must be >= 40)\n" . + "This prevents accidentally replacing historical Perl version references.\n"; + } +} + +# Function to update version references throughout the repository +sub update_version_everywhere { + my ($old_version, $new_version) = @_; + + # Note: Version validation is done in validate_version_change() before this is called + + print "\nPropagating version change from $old_version to $new_version...\n\n"; + + # Directories to exclude from raw version updates (but NOT from JAR reference updates) + # These contain historical version references that should not be changed + my @excluded_dirs = ( + './src/main/perl', # Perl modules with historical version docs + './dev', # Design documents and development notes + ); + + # Files to exclude from version updates (relative to project root) + # These contain historical changelogs and version history + my @excluded_files = ( + './docs/about/changelog.md', + ); + + # First pass: Update JAR filename references in ALL files (always safe) + # This includes: jperl, jperl.bat, docs, examples, src/main/perl comments, etc. + # Matches both perlonjava-X.Y.Z.jar and perlonjava-X.Y.Z-all.jar + my @all_files = `find . -type f -not -path "*/\\.*" -not -path "*/build/*" -not -path "*/target/*"`; + foreach my $file (@all_files) { + chomp $file; + next if -B $file; # Skip binary files + + my $file_content = read_file($file); + # Match perlonjava-VERSION.jar or perlonjava-VERSION-all.jar + if ($file_content =~ s/perlonjava-\Q$old_version\E(-all)?\.jar/"perlonjava-$new_version" . ($1 || "") . ".jar"/ge) { + write_file($file, $file_content); + print "Updated JAR reference in $file\n"; + } + } + + # Second pass: Update version patterns in non-excluded files + # Build exclusion patterns for find command + my $exclude_pattern = join(' ', + map { qq{-not -path "$_/*"} } @excluded_dirs + ); + + my @files = `find . -type f -not -path "*/\\.*" -not -path "*/build/*" -not -path "*/target/*" $exclude_pattern`; + foreach my $file (@files) { + chomp $file; + next if -B $file; # Skip binary files + next if grep { $file eq $_ } @excluded_files; # Skip excluded files + + my $file_content = read_file($file); + my $updated = 0; + + # Update version in build.gradle (project version only) + if ($file =~ /build\.gradle$/ && $file_content =~ s/^(version\s*=\s*')$old_version(')\s*$/$1$new_version$2/m) { + $updated = 1; + print "Updated version in $file\n"; + } + + # Update version in pom.xml (project version, not dependency versions) + if ($file =~ /pom\.xml$/) { + # Match only the project version (near the top, after artifactId) + if ($file_content =~ s|(perlonjava\s*)$old_version()|$1$new_version$2|s) { + $updated = 1; + print "Updated version in $file\n"; + } + } + + # Update version in README.md (feature support line) + if ($file =~ /README\.md$/) { + if ($file_content =~ s/(Supports most Perl )$old_version( features)/$1$new_version$2/g) { + $updated = 1; + print "Updated version in $file\n"; + } + } + + write_file($file, $file_content) if $updated; + } + + # Config.pm is handled separately since it's in an excluded directory + # but needs specific version updates for runtime compatibility + update_config_pm($old_version, $new_version); +} + +# Function to update Config.pm with new version +# This is separate because Config.pm is in src/main/perl (excluded dir) +# but needs version updates for Perl runtime compatibility checks +sub update_config_pm { + my ($old_version, $new_version) = @_; + + my $config_pm = './src/main/perl/lib/Config.pm'; + return unless -f $config_pm; + + my $content = read_file($config_pm); + my $updated = 0; + + # Update version strings that are clearly PerlOnJava version identifiers + # These patterns match Config.pm's version declarations, not historical references + + # Pattern: perlonjava => 'X.Y.Z' + if ($content =~ s/(perlonjava\s*=>\s*')$old_version(')/$1$new_version$2/g) { + $updated = 1; + } + + # Pattern: version => 'X.Y.Z' + if ($content =~ s/(^\s*version\s*=>\s*')$old_version(')/$1$new_version$2/gm) { + $updated = 1; + } + + # Pattern: api_versionstring => 'X.Y.Z' + if ($content =~ s/(api_versionstring\s*=>\s*')$old_version(')/$1$new_version$2/g) { + $updated = 1; + } + + # Pattern: lib version check - die "...(X.Y.Z)..." + if ($content =~ s/(Perl lib version \()$old_version(\))/$1$new_version$2/g) { + $updated = 1; + } + + # Pattern: $^V eq X.Y.Z + if ($content =~ s/(\$\^V eq )$old_version(\s)/$1$new_version$2/g) { + $updated = 1; + } + + # Pattern: path components like /5.42.0/ + if ($content =~ s|(/perl5/)$old_version(/)|$1$new_version$2|g) { + $updated = 1; + } + if ($content =~ s|(/site_perl/)$old_version(/)|$1$new_version$2|g) { + $updated = 1; + } + if ($content =~ s|(/vendor_perl/)$old_version(/)|$1$new_version$2|g) { + $updated = 1; + } + + if ($updated) { + write_file($config_pm, $content); + print "Updated version in $config_pm\n"; + } +} # Function to handle dependency management based on search or direct options sub handle_dependencies { # Show usage if no action specified diff --git a/README.md b/README.md index e10aa727e..d4f3f7ec1 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A Perl compiler and runtime for the JVM that: - Compiles Perl scripts to Java bytecode - Integrates with Java libraries (JDBC databases, Maven dependencies) -- Supports most Perl 5.42 features +- Supports most Perl 5.42.0 features - Includes 150+ core Perl modules (DBI, HTTP::Tiny, JSON, YAML, Text::CSV) ## Quick Start diff --git a/build.gradle b/build.gradle index 5369183ec..4445ac7ae 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,68 @@ tasks.buildDeb { // Project metadata group = 'org.perlonjava' -version = '3.0.0' +version = '5.42.0' +// Git info injection - injects commit ID and date into Configuration.java before compilation +// This ensures the built JAR contains accurate version information for -v output +def configFilePath = layout.projectDirectory.file('src/main/java/org/perlonjava/core/Configuration.java') + +tasks.register('injectGitInfo') { + description = 'Injects git commit info into Configuration.java' + group = 'build' + + // Declare inputs/outputs for configuration cache compatibility + def configFile = configFilePath.asFile + + doLast { + if (!configFile.exists()) { + logger.warn("Configuration.java not found, skipping git info injection") + return + } + + // Get git commit info using Runtime.exec + def gitCommitId = 'dev' + def gitCommitDate = 'unknown' + + try { + def commitIdProcess = ['git', 'rev-parse', '--short', 'HEAD'].execute() + commitIdProcess.waitFor() + if (commitIdProcess.exitValue() == 0) { + gitCommitId = commitIdProcess.text.trim() + } + + def commitDateProcess = ['git', 'log', '-1', '--format=%cs', 'HEAD'].execute() + commitDateProcess.waitFor() + if (commitDateProcess.exitValue() == 0) { + gitCommitDate = commitDateProcess.text.trim() + } + } catch (Exception e) { + logger.warn("Could not get git info: ${e.message}") + } + + // Only update if we got valid values + if (gitCommitId && gitCommitId != 'dev') { + def content = configFile.text + + // Use safe pattern matching for quoted string values + content = content.replaceAll( + /(gitCommitId\s*=\s*)"[^"]*"/, + "\$1\"${gitCommitId}\"" + ) + content = content.replaceAll( + /(gitCommitDate\s*=\s*)"[^"]*"/, + "\$1\"${gitCommitDate}\"" + ) + + configFile.text = content + logger.lifecycle("Injected git info: ${gitCommitId} (${gitCommitDate})") + } + } +} + +// Make compilation depend on git info injection +tasks.named('compileJava') { + dependsOn 'injectGitInfo' +} // OS package configuration for Debian packaging ospackage { diff --git a/dev/design/bytecode_debugging.md b/dev/design/bytecode_debugging.md index f810587d9..8a869d04d 100644 --- a/dev/design/bytecode_debugging.md +++ b/dev/design/bytecode_debugging.md @@ -139,5 +139,5 @@ Fix strategy: ## Notes -- `jperl` runs `target/perlonjava-3.0.0.jar`. Rebuild after changes, otherwise you may be debugging stale code. +- `jperl` runs `target/perlonjava-5.42.0.jar`. Rebuild after changes, otherwise you may be debugging stale code. - `JPERL_ASM_DEBUG_CLASS` is useful to avoid massive logs during large tests. diff --git a/dev/design/debugger.md b/dev/design/debugger.md index 7491721b8..81243f399 100644 --- a/dev/design/debugger.md +++ b/dev/design/debugger.md @@ -28,7 +28,7 @@ This client would use JPDA to communicate with the JVM, but would present the de ## Run PerlOnJava with Debug Flags ```bash -java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar target/perlonjava-3.0.0.jar myscript.pl +java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -jar target/perlonjava-5.42.0.jar myscript.pl ``` ## Start Debugging diff --git a/dev/design/dynamic_loading.md b/dev/design/dynamic_loading.md index 9378a1813..2863779a7 100644 --- a/dev/design/dynamic_loading.md +++ b/dev/design/dynamic_loading.md @@ -332,7 +332,7 @@ The build creates multiple JAR files in target/: ``` target/ - perlonjava-3.0.0.jar # Core runtime + perlonjava-5.42.0.jar # Core runtime perlonjava-module-dbi-3.0.0.jar # DBI module ``` diff --git a/dev/design/getting_started.md b/dev/design/getting_started.md index 5818d8b87..926ccbdb9 100644 --- a/dev/design/getting_started.md +++ b/dev/design/getting_started.md @@ -8,12 +8,12 @@ This guide helps you start using PerlOnJava to run Perl code on the Java Virtual 1. Download the JAR file: ```bash -java -jar target/perlonjava-3.0.0.jar +java -jar target/perlonjava-5.42.0.jar ``` 2. Run your first Perl script: ```bash -java -jar target/perlonjava-3.0.0.jar -E 'print "Hello from Perl on JVM!\n"' +java -jar target/perlonjava-5.42.0.jar -E 'print "Hello from Perl on JVM!\n"' ``` ## Basic Usage Examples @@ -22,12 +22,12 @@ java -jar target/perlonjava-3.0.0.jar -E 'print "Hello from Perl on JVM!\n"' Run a Perl file: ```bash -java -jar target/perlonjava-3.0.0.jar script.pl +java -jar target/perlonjava-5.42.0.jar script.pl ``` Run Perl code directly: ```bash -java -jar target/perlonjava-3.0.0.jar -E 'for (1..3) { print "$_\n" }' +java -jar target/perlonjava-5.42.0.jar -E 'for (1..3) { print "$_\n" }' ``` ### 2. Using Modules @@ -90,12 +90,12 @@ Common switches: Enable debugging output: ```bash -java -jar target/perlonjava-3.0.0.jar --debug script.pl +java -jar target/perlonjava-5.42.0.jar --debug script.pl ``` View generated bytecode: ```bash -java -jar target/perlonjava-3.0.0.jar --disassemble script.pl +java -jar target/perlonjava-5.42.0.jar --disassemble script.pl ``` ## Next Steps diff --git a/dev/design/graalvm.md b/dev/design/graalvm.md index 136800c14..5712fcca5 100644 --- a/dev/design/graalvm.md +++ b/dev/design/graalvm.md @@ -42,7 +42,7 @@ Added GraalVM support to pom.xml in a dedicated profile: Used GraalVM tracing agent to capture required runtime methods: ```bash -java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/perlonjava-3.0.0.jar examples/life.pl +java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image -jar target/perlonjava-5.42.0.jar examples/life.pl ``` ## Results diff --git a/dev/design/scriptingapi.md b/dev/design/scriptingapi.md index aef6ec697..5f814f936 100644 --- a/dev/design/scriptingapi.md +++ b/dev/design/scriptingapi.md @@ -7,7 +7,7 @@ - Note that `jrunscript` creates a new scope every time, so it doesn't keep lexical variables from one line to the next. ```sh - $ jrunscript -cp target/perlonjava-3.0.0.jar -l perl + $ jrunscript -cp target/perlonjava-5.42.0.jar -l perl Perl5> my $sub = sub { say $_[0] }; $sub->($_) for 4,5,6; 4 5 diff --git a/dev/design/windows_installer.md b/dev/design/windows_installer.md index 38af9401e..f0c0378d8 100644 --- a/dev/design/windows_installer.md +++ b/dev/design/windows_installer.md @@ -84,7 +84,7 @@ C:\Program Files\PerlOnJava\ ├── bin\ │ └── jperl.exe ├── lib\ -│ └── perlonjava-3.0.0.jar +│ └── perlonjava-5.42.0.jar └── runtime\ └── [JRE files] ``` \ No newline at end of file diff --git a/dev/presentations/German_Perl_Raku_Workshop_2026/slides-part1-intro.md b/dev/presentations/German_Perl_Raku_Workshop_2026/slides-part1-intro.md index 277c6e1b9..501cfbba4 100644 --- a/dev/presentations/German_Perl_Raku_Workshop_2026/slides-part1-intro.md +++ b/dev/presentations/German_Perl_Raku_Workshop_2026/slides-part1-intro.md @@ -68,7 +68,7 @@ JSR-223 is the standard Java scripting API, available since Java 6. It allows bi ## One JAR, Everything Included -**`perlonjava-3.0.0.jar`** — 25 MB, zero external dependencies +**`perlonjava-5.42.0.jar`** — 25 MB, zero external dependencies ``` perlonjava.jar diff --git a/dev/presentations/German_Perl_Raku_Workshop_2026/slides.md b/dev/presentations/German_Perl_Raku_Workshop_2026/slides.md index b2820b345..9f895f26c 100644 --- a/dev/presentations/German_Perl_Raku_Workshop_2026/slides.md +++ b/dev/presentations/German_Perl_Raku_Workshop_2026/slides.md @@ -773,7 +773,7 @@ Tied scalars, arrays, hashes, and filehandles are supported for core behaviors. The **300+ bundled Perl modules** live *inside the JAR file* itself: ``` %INC: 'Data/Dumper.pm' => - 'file:/path/to/perlonjava-3.0.0.jar!/lib/Data/Dumper.pm' + 'file:/path/to/perlonjava-5.42.0.jar!/lib/Data/Dumper.pm' ``` **Pure-Perl CPAN modules** typically work as-is — add their path to `PERL5LIB` or use `-I`. **XS modules** need Java equivalents (many are bundled already). diff --git a/dev/sandbox/command_line.pl b/dev/sandbox/command_line.pl index 80bc3ce13..204975369 100644 --- a/dev/sandbox/command_line.pl +++ b/dev/sandbox/command_line.pl @@ -8,7 +8,7 @@ my $perl; $perl = 'perl'; -$perl = 'java -jar target/perlonjava-3.0.0.jar'; +$perl = 'java -jar target/perlonjava-5.42.0.jar'; # Test -c (compile only) { diff --git a/docs/getting-started/docker.md b/docs/getting-started/docker.md index b9bb1eae8..e89ab0b32 100644 --- a/docs/getting-started/docker.md +++ b/docs/getting-started/docker.md @@ -86,7 +86,7 @@ Modify the `Dockerfile` to include additional dependencies: ```dockerfile # Add JDBC driver FROM eclipse-temurin:21-jdk -COPY --from=build /app/target/perlonjava-3.0.0.jar /app/perlonjava.jar +COPY --from=build /app/target/perlonjava-5.42.0.jar /app/perlonjava.jar COPY path/to/driver.jar /app/drivers/ ENV CLASSPATH=/app/drivers/driver.jar ENTRYPOINT ["java", "-jar", "/app/perlonjava.jar"] diff --git a/docs/guides/database-access.md b/docs/guides/database-access.md index 263483c3b..89fa0edbd 100644 --- a/docs/guides/database-access.md +++ b/docs/guides/database-access.md @@ -50,7 +50,7 @@ gradle clean build Calling java directly with the classpath is also possible: ```bash - java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp "jdbc-drivers/h2-2.2.224.jar:target/perlonjava-3.0.0.jar" org.perlonjava.app.cli.Main myscript.pl + java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp "jdbc-drivers/h2-2.2.224.jar:target/perlonjava-5.42.0.jar" org.perlonjava.app.cli.Main myscript.pl ``` ## Database Connection Examples diff --git a/docs/guides/java-integration.md b/docs/guides/java-integration.md index cb8a959fc..d2498348b 100644 --- a/docs/guides/java-integration.md +++ b/docs/guides/java-integration.md @@ -174,8 +174,8 @@ dependencies { 3. Add to your classpath: ```bash - javac -cp perlonjava-5.42.2-all.jar YourApp.java - java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp .:perlonjava-5.42.2-all.jar YourApp + javac -cp perlonjava-5.42.0-all.jar YourApp.java + java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp .:perlonjava-5.42.0-all.jar YourApp ``` ## Use Cases diff --git a/examples/ExifToolExample.java b/examples/ExifToolExample.java index 945a954ab..3d5ffc97b 100644 --- a/examples/ExifToolExample.java +++ b/examples/ExifToolExample.java @@ -27,9 +27,9 @@ * or: * ./gradlew shadowJar * 2. Compile this example: - * javac -cp target/perlonjava-3.0.0.jar examples/ExifToolExample.java + * javac -cp target/perlonjava-5.42.0.jar examples/ExifToolExample.java * 3. Run: - * java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp target/perlonjava-3.0.0.jar:. examples.ExifToolExample + * java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow -cp target/perlonjava-5.42.0.jar:. examples.ExifToolExample */ public class ExifToolExample { diff --git a/examples/dbi.pl b/examples/dbi.pl index 507b93f95..9b7aaac02 100644 --- a/examples/dbi.pl +++ b/examples/dbi.pl @@ -4,7 +4,7 @@ # curl https://repo1.maven.org/maven2/com/h2database/h2/2.2.224/h2-2.2.224.jar --output h2-2.2.224.jar # # Run using: -# java -cp "h2-2.2.224.jar:target/perlonjava-3.0.0.jar" org.perlonjava.app.cli.Main examples/dbi.pl +# java -cp "h2-2.2.224.jar:target/perlonjava-5.42.0.jar" org.perlonjava.app.cli.Main examples/dbi.pl # # or using: # CLASSPATH=h2-2.2.224.jar ./jperl examples/dbi.pl diff --git a/jperl b/jperl index ce18bf3fd..eb01934af 100755 --- a/jperl +++ b/jperl @@ -16,11 +16,11 @@ JPERL_PATH="$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo "$SCRIPT_DIR/j export PERLONJAVA_EXECUTABLE="$JPERL_PATH" # Check development environment first (target directory) -if [ -f "$SCRIPT_DIR/target/perlonjava-3.0.0.jar" ]; then - JAR_PATH="$SCRIPT_DIR/target/perlonjava-3.0.0.jar" +if [ -f "$SCRIPT_DIR/target/perlonjava-5.42.0.jar" ]; then + JAR_PATH="$SCRIPT_DIR/target/perlonjava-5.42.0.jar" else # Use installed package path (when installed via deb package) - JAR_PATH="$SCRIPT_DIR/../lib/perlonjava-3.0.0.jar" + JAR_PATH="$SCRIPT_DIR/../lib/perlonjava-5.42.0.jar" fi # Launch Java diff --git a/jperl.bat b/jperl.bat index 1a38aef9c..9910ed9f3 100755 --- a/jperl.bat +++ b/jperl.bat @@ -19,4 +19,4 @@ rem --enable-native-access=ALL-UNNAMED: Required by JNR-POSIX library for nati rem (file operations, process management). Can be removed if JNR-POSIX is replaced. rem --sun-misc-unsafe-memory-access=allow: Suppresses deprecation warnings from JFFI library rem (used by JNR). Can be removed when JFFI updates to use MemorySegment API (Java 22+). -java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow %JPERL_OPTS% -cp "%CLASSPATH%;%SCRIPT_DIR%target\perlonjava-3.0.0.jar" org.perlonjava.app.cli.Main %* +java --enable-native-access=ALL-UNNAMED --sun-misc-unsafe-memory-access=allow %JPERL_OPTS% -cp "%CLASSPATH%;%SCRIPT_DIR%target\perlonjava-5.42.0.jar" org.perlonjava.app.cli.Main %* diff --git a/pom.xml b/pom.xml index 2fb63237e..fd553879a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.perlonjava perlonjava - 3.0.0 + 5.42.0 jar perlonjava @@ -115,6 +115,7 @@ + org.apache.maven.plugins @@ -249,6 +250,87 @@ + + + inject-git-unix + + + unix + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + + inject-git-info + generate-sources + + exec + + + bash + + -c + /dev/null || echo "dev") + GIT_COMMIT_DATE=$(git log -1 --format=%cs HEAD 2>/dev/null || echo "unknown") + if [ "$GIT_COMMIT_ID" != "dev" ]; then + perl -i -pe "s/(gitCommitId\\s*=\\s*)\"[^\"]*\"/\$1\"$GIT_COMMIT_ID\"/" "$CONFIG_FILE" + perl -i -pe "s/(gitCommitDate\\s*=\\s*)\"[^\"]*\"/\$1\"$GIT_COMMIT_DATE\"/" "$CONFIG_FILE" + echo "Injected git info: $GIT_COMMIT_ID ($GIT_COMMIT_DATE)" + fi +fi + ]]> + + + + + + + + + + + inject-git-windows + + + windows + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + + inject-git-info + generate-sources + + exec + + + powershell + + -ExecutionPolicy + Bypass + -File + scripts/inject-git-info.ps1 + + + + + + + + diff --git a/scripts/inject-git-info.ps1 b/scripts/inject-git-info.ps1 new file mode 100644 index 000000000..f7630558c --- /dev/null +++ b/scripts/inject-git-info.ps1 @@ -0,0 +1,26 @@ +# inject-git-info.ps1 +# Injects git commit ID and date into Configuration.java for Windows builds + +$ConfigFile = "src/main/java/org/perlonjava/core/Configuration.java" + +if (Test-Path $ConfigFile) { + try { + $GitCommitId = git rev-parse --short HEAD 2>$null + $GitCommitDate = git log -1 --format=%cs HEAD 2>$null + + if ($GitCommitId -and $GitCommitId -ne "dev") { + $content = Get-Content $ConfigFile -Raw + + # Replace gitCommitId value + $content = $content -replace '(gitCommitId\s*=\s*)"[^"]*"', ('$1"' + $GitCommitId + '"') + + # Replace gitCommitDate value + $content = $content -replace '(gitCommitDate\s*=\s*)"[^"]*"', ('$1"' + $GitCommitDate + '"') + + Set-Content $ConfigFile -Value $content -NoNewline + Write-Host "Injected git info: $GitCommitId ($GitCommitDate)" + } + } catch { + Write-Host "Git info injection skipped: $_" + } +} diff --git a/src/main/java/org/perlonjava/app/cli/ArgumentParser.java b/src/main/java/org/perlonjava/app/cli/ArgumentParser.java index 68a107051..85ba5350a 100644 --- a/src/main/java/org/perlonjava/app/cli/ArgumentParser.java +++ b/src/main/java/org/perlonjava/app/cli/ArgumentParser.java @@ -565,9 +565,22 @@ private static void parseUnicodeFlags(String flags, CompilerOptions parsedArgs) } private static void printVersionInfo() { - String version = Configuration.getPerlVersionNoV(); + // Parse version into components (e.g., "5.42.0" -> major=5, minor=42, subversion=0) + String versionNoV = Configuration.getPerlVersionNoV(); + String[] parts = versionNoV.split("\\."); + String major = parts.length > 0 ? parts[0] : "5"; + String minor = parts.length > 1 ? parts[1] : "0"; + String subversion = parts.length > 2 ? parts[2] : "0"; + + // Build git info suffix if available + String gitInfo = ""; + if (!Configuration.gitCommitId.equals("dev")) { + gitInfo = " (" + Configuration.gitCommitId + " " + Configuration.gitCommitDate + ")"; + } + System.out.println(); - System.out.println("This is perl 5, version " + version + " built for JVM"); + System.out.println("This is perl " + major + ", version " + minor + ", subversion " + subversion + + " (v" + versionNoV + ") built for JVM" + gitInfo); System.out.println(); System.out.println("Copyright 1987-2025, Larry Wall"); System.out.println(); @@ -591,8 +604,21 @@ private static void printConfigurationInfo(String configVar, CompilerOptions par GlobalContext.initializeGlobals(parsedArgs); if (configVar != null) { - // Print specific configuration variable or 'UNKNOWN' if not found - String value = System.getProperty(configVar, "UNKNOWN"); + // Handle built-in configuration variables + String value; + switch (configVar) { + case "version": + value = Configuration.version; + break; + case "git_commit_id": + value = Configuration.gitCommitId; + break; + case "git_commit_date": + value = Configuration.gitCommitDate; + break; + default: + value = System.getProperty(configVar, "UNKNOWN"); + } System.out.println(configVar + "='" + value + "';"); } else { // Print all configuration information @@ -600,6 +626,13 @@ private static void printConfigurationInfo(String configVar, CompilerOptions par System.out.println("Summary of my perl5 (" + version + ") configuration:"); System.out.println(); + // Print PerlOnJava-specific info + System.out.println(" PerlOnJava:"); + System.out.println(" version: " + Configuration.version); + System.out.println(" git_commit_id: " + Configuration.gitCommitId); + System.out.println(" git_commit_date: " + Configuration.gitCommitDate); + System.out.println(); + System.out.println(" JVM properties:"); System.getProperties().forEach((key, value) -> System.out.println(" " + key + ": " + value)); @@ -1052,7 +1085,7 @@ private static void modifyCodeBasedOnFlags(CompilerOptions parsedArgs) { * Prints the help message detailing the usage of the program and its options. */ private static void printHelp() { - System.out.println("Usage: java -jar target/perlonjava-3.0.0.jar [options] [file] [args]"); + System.out.println("Usage: java -jar target/perlonjava-" + Configuration.version + ".jar [options] [file] [args]"); System.out.println(); System.out.println(" -0[octal/hexadecimal] specify record separator (\\0, if no argument)"); System.out.println(" -a autosplit mode with -n or -p (splits $_ into @F)"); diff --git a/src/main/java/org/perlonjava/app/scriptengine/PerlScriptEngineFactory.java b/src/main/java/org/perlonjava/app/scriptengine/PerlScriptEngineFactory.java index 27a0eb442..8ba261ef8 100644 --- a/src/main/java/org/perlonjava/app/scriptengine/PerlScriptEngineFactory.java +++ b/src/main/java/org/perlonjava/app/scriptengine/PerlScriptEngineFactory.java @@ -5,7 +5,7 @@ import java.util.Arrays; import java.util.List; -import static org.perlonjava.core.Configuration.perlVersion; +import static org.perlonjava.core.Configuration.version; /** * The PerlScriptEngineFactory class is an implementation of the ScriptEngineFactory interface. @@ -53,7 +53,7 @@ public String getLanguageName() { @Override public String getLanguageVersion() { - return perlVersion; + return version; } @Override diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index b3f3f2cbe..26c4af898 100644 --- a/src/main/java/org/perlonjava/core/Configuration.java +++ b/src/main/java/org/perlonjava/core/Configuration.java @@ -7,36 +7,65 @@ * Central configuration class for the Perl-to-Java compiler. * Contains constants that control compiler behavior and runtime settings. *

- * Note: Configuration values can be set using the Configure.pl script. - * For example, to set the Perl version, you can run: + * Configuration values are managed using the Configure.pl script: *

- * ./Configure.pl -D perlVersion=v5.40.0 + * ./Configure.pl -D version=5.43.0 # Update version everywhere + * ./Configure.pl # Show current configuration *

- * To update the jar version across all files in the repository: + * This will update the version constant in this class and replace all + * occurrences of perlonjava-X.Y.Z.jar throughout the repository. *

- * ./Configure.pl -D jarVersion=3.0.1 - *

- * This will update both constants in this configuration class and replace - * all occurrences of perlonjava-3.0.0.jar with perlonjava-3.0.1.jar. + * Git commit information (gitCommitId, gitCommitDate) is automatically + * injected by the build system (Gradle or Maven) before compilation. + * Do not edit these values manually - they will be overwritten on each build. */ public final class Configuration { - // Perl version information - public static final String perlVersion = "v5.42.0"; - public static final String jarVersion = "3.0.0"; + /** + * Unified version number for PerlOnJava. + * This version is used for both the JAR artifact and Perl compatibility version. + * Updated via: ./Configure.pl -D version=X.Y.Z + */ + public static final String version = "5.42.0"; + + /** + * Git commit ID (short hash) of the build. + * Automatically populated by Gradle/Maven during build. + * DO NOT EDIT MANUALLY - this value is replaced at build time. + */ + public static final String gitCommitId = "610d942c5"; + + /** + * Git commit date of the build (ISO format: YYYY-MM-DD). + * Automatically populated by Gradle/Maven during build. + * DO NOT EDIT MANUALLY - this value is replaced at build time. + */ + public static final String gitCommitDate = "2026-03-10"; // Prevent instantiation private Configuration() { } + /** + * Returns the version for use with "use VERSION" feature bundles. + * For version 5.42.0, returns ":5.42" + */ public static String getPerlVersionBundle() { - return ":" + perlVersion.substring(1, perlVersion.lastIndexOf('.')); + int lastDot = version.lastIndexOf('.'); + return ":" + version.substring(0, lastDot); } + /** + * Returns the version string without 'v' prefix. + * Since version is already stored without 'v', this returns it directly. + */ public static String getPerlVersionNoV() { - return perlVersion.startsWith("v") ? perlVersion.substring(1) : perlVersion; + return version.startsWith("v") ? version.substring(1) : version; } + /** + * Returns the version in old Perl $] format (e.g., "5.042000" for 5.42.0). + */ public static String getPerlVersionOld() { String versionNoV = getPerlVersionNoV(); String[] parts = versionNoV.split("\\."); @@ -55,7 +84,7 @@ public static String getPerlVersionOld() { /** * Returns the Perl version as a vstring RuntimeScalar. - * For example, v5.42.0 becomes a vstring with bytes \u0005*\u0000 + * For example, 5.42.0 becomes a vstring with bytes \u0005*\u0000 * where each version component is represented as a character. * * @return RuntimeScalar with type VSTRING containing the version diff --git a/src/main/java/org/perlonjava/frontend/parser/StatementParser.java b/src/main/java/org/perlonjava/frontend/parser/StatementParser.java index dda9d18e1..cf76594f2 100644 --- a/src/main/java/org/perlonjava/frontend/parser/StatementParser.java +++ b/src/main/java/org/perlonjava/frontend/parser/StatementParser.java @@ -501,7 +501,7 @@ public static Node parseUseDeclaration(Parser parser, LexerToken token) { if (packageName == null) { parser.ctx.logDebug("use version: check Perl version"); VersionHelper.compareVersion( - new RuntimeScalar(Configuration.perlVersion), + new RuntimeScalar(Configuration.version), versionScalar, "Perl"); diff --git a/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java b/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java index 44bbb1925..094b1caa8 100644 --- a/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java +++ b/src/main/java/org/perlonjava/runtime/operators/ModuleOperators.java @@ -708,7 +708,7 @@ public static RuntimeScalar require(RuntimeScalar runtimeScalar) { // ===== CASE 1: Version checking ===== if (runtimeScalar.type == RuntimeScalarType.INTEGER || runtimeScalar.type == RuntimeScalarType.DOUBLE || runtimeScalar.type == RuntimeScalarType.VSTRING || runtimeScalar.type == RuntimeScalarType.BOOLEAN) { // `require VERSION` - use version comparison - String currentVersionStr = Configuration.perlVersion; + String currentVersionStr = Configuration.version; String displayVersion = VersionHelper.getDisplayVersionForRequire(runtimeScalar); String normalizedRequired = VersionHelper.normalizeVersionForRequireComparison(runtimeScalar); diff --git a/src/main/java/org/perlonjava/runtime/operators/VersionHelper.java b/src/main/java/org/perlonjava/runtime/operators/VersionHelper.java index 99716c858..8b809a469 100644 --- a/src/main/java/org/perlonjava/runtime/operators/VersionHelper.java +++ b/src/main/java/org/perlonjava/runtime/operators/VersionHelper.java @@ -9,30 +9,40 @@ public class VersionHelper { // Helper method to normalize version to a comparable decimal format for require private static String normalizeVersionToDecimalForRequire(String version) { - if (version.startsWith("v")) { - // v-string like v5.42.0 -> 5.042000, v5.5.630 -> 5.005630 - String[] parts = version.substring(1).split("\\."); - if (parts.length > 0) { - StringBuilder normalized = new StringBuilder(parts[0]); - if (parts.length > 1) { - normalized.append("."); - for (int i = 1; i < parts.length; i++) { - // Pad each component to 3 digits with leading zeros - String part = parts[i]; - while (part.length() < 3) { - part = "0" + part; // Pad with leading zeros - } - normalized.append(part); - } - } else { - // Handle cases like "v5" -> "5.000000" - normalized.append(".000000"); + // Remove v prefix if present + String v = version.startsWith("v") ? version.substring(1) : version; + + // Handle underscore versions like 5.005_63 -> 5.00563 + v = v.replace("_", ""); + + // Check if this is a dot-separated version like 5.42.0 (more than one dot) + String[] parts = v.split("\\."); + if (parts.length > 2 || (parts.length == 2 && version.startsWith("v"))) { + // Multi-component version like 5.42.0 or v5.42.0 -> 5.042000 + StringBuilder normalized = new StringBuilder(parts[0]); + normalized.append("."); + for (int i = 1; i < parts.length; i++) { + // Pad each component to 3 digits with leading zeros + String part = parts[i]; + while (part.length() < 3) { + part = "0" + part; // Pad with leading zeros } - return normalized.toString(); + normalized.append(part); + } + // Ensure at least 6 decimal digits for consistency + String decimalPart = normalized.substring(normalized.indexOf(".") + 1); + while (decimalPart.length() < 6) { + normalized.append("0"); + decimalPart = normalized.substring(normalized.indexOf(".") + 1); } + return normalized.toString(); + } else if (parts.length == 1) { + // Single number like "10" -> "10.000000" + return v + ".000000"; } - // Handle underscore versions like 5.005_63 -> 5.005063 - return version.replace("_", ""); + + // Already in decimal format like 5.042000 + return v; } // Helper method to normalize version for require comparison diff --git a/src/main/perl/lib/DBI.pm b/src/main/perl/lib/DBI.pm index 4c8550924..e60afd62a 100644 --- a/src/main/perl/lib/DBI.pm +++ b/src/main/perl/lib/DBI.pm @@ -10,7 +10,7 @@ XSLoader::load( 'DBI' ); # Example: # -# java -cp "h2-2.2.224.jar:target/perlonjava-3.0.0.jar" org.perlonjava.app.cli.Main dbi.pl +# java -cp "h2-2.2.224.jar:target/perlonjava-5.42.0.jar" org.perlonjava.app.cli.Main dbi.pl # # # Connect to H2 database # my $dbh = DBI->connect(