diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index d890f8464..bb54e5ea5 100644 --- a/src/main/java/org/perlonjava/core/Configuration.java +++ b/src/main/java/org/perlonjava/core/Configuration.java @@ -33,7 +33,7 @@ public final class Configuration { * Automatically populated by Gradle/Maven during build. * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String gitCommitId = "5ab2ed282"; + public static final String gitCommitId = "9a275c7a1"; /** * Git commit date of the build (ISO format: YYYY-MM-DD). @@ -48,7 +48,7 @@ public final class Configuration { * Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at" * DO NOT EDIT MANUALLY - this value is replaced at build time. */ - public static final String buildTimestamp = "Apr 21 2026 16:09:03"; + public static final String buildTimestamp = "Apr 21 2026 20:21:33"; // Prevent instantiation private Configuration() { diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/TieArray.java b/src/main/java/org/perlonjava/runtime/runtimetypes/TieArray.java index 22fc770ee..c5398e370 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/TieArray.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/TieArray.java @@ -107,6 +107,11 @@ private static RuntimeScalar tieCallIfExists(RuntimeArray array, String methodNa // Method doesn't exist, return undef return RuntimeScalarCache.scalarUndef; } + // Ignore AUTOLOAD fallback — tie special methods (UNTIE, DESTROY) are + // "if exists" only; they should not trigger AUTOLOAD dispatch. + if (method.value instanceof RuntimeCode rc && rc.autoloadVariableName != null) { + return RuntimeScalarCache.scalarUndef; + } // Method exists, call it return RuntimeCode.apply(method, new RuntimeArray(self), RuntimeContextType.SCALAR).getFirst(); diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/TieHandle.java b/src/main/java/org/perlonjava/runtime/runtimetypes/TieHandle.java index ea66b6c2f..7c690855c 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/TieHandle.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/TieHandle.java @@ -201,6 +201,11 @@ private RuntimeScalar tieCallIfExists(String methodName) { // Method doesn't exist, return undef return RuntimeScalarCache.scalarUndef; } + // Ignore AUTOLOAD fallback — tie special methods (UNTIE, DESTROY) are + // "if exists" only; they should not trigger AUTOLOAD dispatch. + if (method.value instanceof RuntimeCode rc && rc.autoloadVariableName != null) { + return RuntimeScalarCache.scalarUndef; + } // Method exists, call it return RuntimeCode.apply(method, new RuntimeArray(self), RuntimeContextType.SCALAR).getFirst(); diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/TieHash.java b/src/main/java/org/perlonjava/runtime/runtimetypes/TieHash.java index 5d9a80bc9..abdca40ed 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/TieHash.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/TieHash.java @@ -94,6 +94,11 @@ private static RuntimeScalar tieCallIfExists(RuntimeHash hash, String methodName // Method doesn't exist, return undef return RuntimeScalarCache.scalarUndef; } + // Ignore AUTOLOAD fallback — tie special methods (UNTIE, DESTROY) are + // "if exists" only; they should not trigger AUTOLOAD dispatch. + if (method.value instanceof RuntimeCode rc && rc.autoloadVariableName != null) { + return RuntimeScalarCache.scalarUndef; + } // Method exists, call it return RuntimeCode.apply(method, new RuntimeArray(self), RuntimeContextType.SCALAR).getFirst(); diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/TiedVariableBase.java b/src/main/java/org/perlonjava/runtime/runtimetypes/TiedVariableBase.java index c4e694101..561b8c176 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/TiedVariableBase.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/TiedVariableBase.java @@ -80,6 +80,11 @@ protected RuntimeScalar tieCallIfExists(String methodName) { // Method doesn't exist, return undef return RuntimeScalarCache.scalarUndef; } + // Ignore AUTOLOAD fallback — tie special methods (UNTIE, DESTROY) are + // "if exists" only; they should not trigger AUTOLOAD dispatch. + if (method.value instanceof RuntimeCode rc && rc.autoloadVariableName != null) { + return RuntimeScalarCache.scalarUndef; + } // Method exists, call it return RuntimeCode.apply(method, new RuntimeArray(self), RuntimeContextType.SCALAR).getFirst(); diff --git a/src/main/perl/lib/ExtUtils/MakeMaker.pm b/src/main/perl/lib/ExtUtils/MakeMaker.pm index 7e0ecc52c..ca14aab6f 100644 --- a/src/main/perl/lib/ExtUtils/MakeMaker.pm +++ b/src/main/perl/lib/ExtUtils/MakeMaker.pm @@ -193,13 +193,38 @@ sub _install_pure_perl { # Use explicit PM hash if provided if ($args->{PM}) { %pm = %{$args->{PM}}; - # Expand Make-style variables like $(INST_LIB) to actual paths + # Expand Make-style variables like $(INST_LIB) to actual paths. + # Standard MakeMaker derives directory-bearing vars from NAME: + # INST_LIBDIR = INST_LIB/ (e.g. blib/lib/Term) + # INST_ARCHLIBDIR = INST_ARCHLIB/ + # INST_AUTODIR = INST_LIB/auto/ + # INST_ARCHAUTODIR = INST_ARCHLIB/auto/ + # where is NAME with the last :: component removed and + # is the full NAME with :: replaced by /. + my @parts = split /::/, ($name || ''); + pop @parts; # drop BASEEXT (last component) + my $parent_dir = @parts ? join('/', @parts) : ''; + (my $full_path = ($name || '')) =~ s{::}{/}g; + my $libdir = $parent_dir + ? File::Spec->catdir($INSTALL_BASE, $parent_dir) + : $INSTALL_BASE; + my $autodir = $full_path + ? File::Spec->catdir($INSTALL_BASE, 'auto', $full_path) + : $INSTALL_BASE; for my $key (keys %pm) { my $val = $pm{$key}; + # Directory-bearing variables (must come before the bare LIB/ARCHLIB forms) + $val =~ s/\$\(INST_LIBDIR\)/$libdir/g; + $val =~ s/\$\(INST_ARCHLIBDIR\)/$libdir/g; # treat ARCHLIBDIR same as LIBDIR + $val =~ s/\$\(INST_AUTODIR\)/$autodir/g; + $val =~ s/\$\(INST_ARCHAUTODIR\)/$autodir/g; + $val =~ s/\$\{INST_LIBDIR\}/$libdir/g; + $val =~ s/\$\{INST_ARCHLIBDIR\}/$libdir/g; + # Bare library roots $val =~ s/\$\(INST_LIB\)/$INSTALL_BASE/g; $val =~ s/\$\(INST_ARCHLIB\)/$INSTALL_BASE/g; # treat ARCHLIB same as LIB - $val =~ s/\$\(INST_LIBDIR\)/$INSTALL_BASE/g; - $val =~ s/\$\{INST_LIB\}/$INSTALL_BASE/g; # also handle ${VAR} form + $val =~ s/\$\{INST_LIB\}/$INSTALL_BASE/g; + $val =~ s/\$\{INST_ARCHLIB\}/$INSTALL_BASE/g; $pm{$key} = $val; } } else { @@ -637,12 +662,16 @@ sub _shell_mkdir { return "\t\@mkdir -p '$dir'"; } -# Helper: generate a shell cp command for Makefile +# Helper: generate a shell cp command for Makefile. +# Tolerant of missing source files: some distributions generate .pm files +# from .pm.PL scripts that require XS bootstrap (e.g. Term::ReadKey) and +# PerlOnJava cannot run them. We skip missing sources with a warning +# rather than failing the whole install. sub _shell_cp { my ($src, $dest) = @_; $src =~ s/'/'\\''/g; $dest =~ s/'/'\\''/g; - return "\t\@rm -f '$dest' && cp '$src' '$dest'"; + return "\t\@if [ -f '$src' ]; then rm -f '$dest' && cp '$src' '$dest'; else echo 'PerlOnJava: skipping missing source: $src'; fi"; } sub _create_mymeta {