diff --git a/docs/about/changelog.md b/docs/about/changelog.md index ced4856f77..0ab501d2e0 100644 --- a/docs/about/changelog.md +++ b/docs/about/changelog.md @@ -6,6 +6,8 @@ priorities and future plans. ## Work in progress +- Run MakeMaker `MY::post_initialize` hooks so generated files join `pm_to_blib`, and parse Perl's `$]` version variable. + - Preserve hash-reference constant calls after typeglob undefinition, dereference glob-backed hash values during Morpheus configuration traversal, parse Error.pm's dynamic `catch $class with { ... }` form on both backends, and retain integer scalar identity through `int()` for decimal stringification; preserve regex referent types through blessed scalar references and cover Morpheus relational overload dispatch and caller filename normalization (#1342, #1376, #1427, #1428). - Implement `Time::HiRes::stat` with high-resolution file timestamps (#1912). diff --git a/src/main/java/org/perlonjava/frontend/parser/IdentifierParser.java b/src/main/java/org/perlonjava/frontend/parser/IdentifierParser.java index 4ffd58a155..496edf7c50 100644 --- a/src/main/java/org/perlonjava/frontend/parser/IdentifierParser.java +++ b/src/main/java/org/perlonjava/frontend/parser/IdentifierParser.java @@ -287,7 +287,7 @@ public static String parseComplexIdentifierInner(Parser parser, boolean insideBr // Special case for special variables like `$|`, `$'`, `$(`, `$)`, etc. char firstChar = token.text.charAt(0); - if (token.type == LexerTokenType.OPERATOR && "!|/*+-<>&~.=%'?()".indexOf(firstChar) >= 0) { + if (token.type == LexerTokenType.OPERATOR && "!|/*+-<>&~.=%'?()]".indexOf(firstChar) >= 0) { // Special case: * followed by { is glob dereference when inside braces // @{*{expr}} should be parsed as @{ *{expr} }, not @*{expr} (hash slice on @*) // But @*{key} outside braces IS a hash slice on @*, so only apply when insideBraces diff --git a/src/main/java/org/perlonjava/frontend/parser/StatementResolver.java b/src/main/java/org/perlonjava/frontend/parser/StatementResolver.java index 46147946c8..0c38100b7b 100644 --- a/src/main/java/org/perlonjava/frontend/parser/StatementResolver.java +++ b/src/main/java/org/perlonjava/frontend/parser/StatementResolver.java @@ -1693,7 +1693,8 @@ public static boolean isHashLiteral(Parser parser) { // If we've already seen a fat-comma hash indicator at depth 1, // keep preferring hash interpretation. This avoids false block // classification when keyword-like words leak from quoted payload. - if (!hasHashIndicator && !nextToken.text.equals("=>") && !nextToken.text.equals(",")) { + if ((!hasHashIndicator || nextToken.text.equals("(")) + && !nextToken.text.equals("=>") && !nextToken.text.equals(",")) { // Statement modifier - definitive block indicator if (CompilerOptions.DEBUG_ENABLED) parser.ctx.logDebug("isHashLiteral found statement modifier (block indicator)"); hasBlockIndicator = true; diff --git a/src/main/perl/lib/ExtUtils/MakeMaker.pm b/src/main/perl/lib/ExtUtils/MakeMaker.pm index 04e5ec4752..bf27e33f72 100644 --- a/src/main/perl/lib/ExtUtils/MakeMaker.pm +++ b/src/main/perl/lib/ExtUtils/MakeMaker.pm @@ -548,11 +548,24 @@ sub _install_pure_perl { } } } + + # ExtUtils::MakeMaker subclasses use post_initialize to extend the + # initialized MakeMaker object, including its PM mapping. Older pure-Perl + # distributions use this to add generated data files to the normal + # pm_to_blib staging pass (for example, Lingua::ZH::Romanize::Pinyin). + # Run the hook after discovering the default mapping and before deciding + # whether the distribution has installable files. + my $mm = PerlOnJava::MM::Installed->new($args); + $mm->{PM} = { %pm }; + $mm->{clean} = {}; + if (defined &MY::post_initialize) { + $mm->{post_initialize} = MY::post_initialize($mm) // ''; + %pm = %{$mm->{PM}} if ref $mm->{PM} eq 'HASH'; + } if (!%pm) { print "Warning: No installable files found (no .pm, .pl, .dat, etc.).\n"; print "Expected structure: lib/Your/Module.pm\n\n"; - my $mm = PerlOnJava::MM::Installed->new($args); _create_mymeta($name, $version, $args); _create_install_makefile($name, $version, $args, {}, {}, $mm); _create_build_script_proxy() if -f 'Build.PL'; @@ -635,8 +648,6 @@ sub _install_pure_perl { print "\n"; # Create MM object first (needed by postamble) - my $mm = PerlOnJava::MM::Installed->new($args); - # Create MYMETA.yml for CPAN.pm dependency resolution _create_mymeta($name, $version, $args); @@ -1091,6 +1102,9 @@ sub _create_install_makefile { my $depend_rules_str = _make_depend_rules($args->{depend}); my $makefile_comments = _make_makefile_comments($args, $distname, $version); + my $post_initialize = $mm->{post_initialize} || ''; + $post_initialize .= "\n" if length($post_initialize) + && $post_initialize !~ /\n\z/; # Honor user-supplied `macro => { ... }` from WriteMakefile by emitting # extra Makefile macro definitions. LaTeXML and others stuff custom @@ -1113,6 +1127,7 @@ sub _create_install_makefile { # This matches standard ExtUtils::MakeMaker semantics so that 'cpan -t' # (test only) does not install, while 'cpan -i' (install) does. $makefile_comments +$post_initialize NAME = $name DISTNAME = $distname VERSION = $version diff --git a/src/test/resources/unit/makemaker_post_initialize_generated_pm.t b/src/test/resources/unit/makemaker_post_initialize_generated_pm.t new file mode 100644 index 0000000000..0814dc76ab --- /dev/null +++ b/src/test/resources/unit/makemaker_post_initialize_generated_pm.t @@ -0,0 +1,41 @@ +use strict; +use warnings; +use Test::More tests => 4; +use File::Temp qw(tempdir); +use Cwd qw(getcwd); + +my $dir = tempdir(CLEANUP => 1); +my $cwd = getcwd(); +chdir $dir or die "chdir $dir: $!"; + +mkdir 'lib' or die "mkdir lib: $!"; +mkdir 'lib/Fixture' or die "mkdir lib/Fixture: $!"; +open my $module, '>', 'lib/Fixture/Module.pm' or die $!; +print {$module} "package Fixture::Module; 1;\n"; +close $module; +open my $makefile_pl, '>', 'Makefile.PL' or die $!; +print {$makefile_pl} "# generated test fixture\n"; +close $makefile_pl; + +{ + package MY; + sub post_initialize { + my ($self) = @_; + $self->{clean}{FILES} = 'lib/Fixture/Generated.store'; + $self->{PM}{'lib/Fixture/Generated.store'} = 'blib/lib/Fixture/Generated.store'; + return ''; + } + sub postamble { + return "\nlib/Fixture/Generated.store :\n\tprintf generated > lib/Fixture/Generated.store\n"; + } +} + +use ExtUtils::MakeMaker; +WriteMakefile(NAME => 'Fixture::Module', VERSION => '0.01'); + +ok(!-e 'lib/Fixture/Generated.store', 'configure does not fabricate generated data'); +ok(-f 'Makefile', 'Makefile was generated'); +is(system($ENV{MAKE} || 'make', 'all'), 0, 'make runs the custom generated-file rule'); +ok(-f 'blib/lib/Fixture/Generated.store', 'pm_to_blib stages the generated file'); + +chdir $cwd or die "chdir $cwd: $!"; diff --git a/src/test/resources/unit/special_version_variable.t b/src/test/resources/unit/special_version_variable.t new file mode 100644 index 0000000000..de09edfe31 --- /dev/null +++ b/src/test/resources/unit/special_version_variable.t @@ -0,0 +1,11 @@ +use strict; +use warnings; +use Test::More; + +{ + plan skip_all => 'Perl 5.8.1 or better is required to test this' unless ( $] >= 5.008001 ); + plan tests => 2; + ok($] >= 5.008001, '$] supports the standard Perl version comparison'); + my $supported = 1 unless ($] < 5.008001); + ok($supported, '$] parses in a parenthesized unless condition'); +}