Skip to content

CPAN Phase 6: Enable CPAN.pm for pure Perl module installation#316

Merged
fglock merged 17 commits into
masterfrom
feature/cpan-phase6
Mar 14, 2026
Merged

CPAN Phase 6: Enable CPAN.pm for pure Perl module installation#316
fglock merged 17 commits into
masterfrom
feature/cpan-phase6

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Mar 14, 2026

Summary

This PR implements Phase 6 of CPAN client support for PerlOnJava, enabling CPAN.pm to install pure Perl modules.

Completed

  • Safe.pm stub

    • Created minimal Safe.pm that uses regular eval with no strict 'vars'
    • Sufficient for CPAN.pm which uses Safe to evaluate trusted metadata (CHECKSUMS files)
    • Methods: new(), reval(), permit(), deny(), etc.
  • Import CPAN.pm

    • Added CPAN.pm and all CPAN/* submodules via sync.pl
    • Added CPAN::Meta, CPAN::Meta::YAML, CPAN::Meta::Requirements
    • Added Parse::CPAN::Meta
  • Parser fixes for CPAN.pm compatibility

    • File test operators with function call operands (-f func())
    • Block argument parsing for undefined functions (func { } @args)
    • File test with qualified package names (-f CPAN::find_perl)
    • File test with empty parentheses (-f()) now correctly uses $_
  • Regex character class fixes

    • Ranges like [A-Z-0-9] now parse correctly (trailing hyphen after range is literal)
    • POSIX class followed by hyphen [[:digit:]-[:alpha:]] now works correctly
  • Try::Tiny compatibility

    • try and catch only treated as keywords when use feature 'try' is enabled
    • Without the feature flag, they work as regular subroutines (Try::Tiny works)
  • Cross-platform Cwd support

    • Added Internals::getcwd and Internals::abs_path in Java
    • Uses System.getProperty("user.dir") and File.getCanonicalPath()
    • Works reliably on Windows without shell fallbacks
  • POSIX :sys_wait_h export tag

Working Example

./jperl -MCPAN -e 'CPAN::Shell->install("Try::Tiny")'
# Downloads, validates checksums, installs to ~/.perlonjava/lib/

Remaining Items (documented in dev/design/cpan_client.md)

  1. Version parsing - "Error while parsing version number" warnings
  2. ExtUtils::MakeMaker Makefile output (cosmetic - installation works)
  3. YAML.pm version warning (cosmetic)
  4. Module::Build support (future Phase 6b)
  5. jcpan wrapper script (future Phase 6e)

Test Plan

  • ./jperl -MSafe -e 'print Safe->new->reval("1+1")' prints 2
  • ./jperl -MCPAN -e 'print $CPAN::VERSION' prints version
  • ./jperl -MCPAN -e 'CPAN::Shell->install("Try::Tiny")' installs module
  • ./jperl -MTry::Tiny -e 'print "OK"' works after install
  • CI passes on both Ubuntu and Windows
  • No test regressions (comp/require.t, op/stat.t, re/regexp.t all restored)

Generated with Devin

fglock and others added 6 commits March 14, 2026 10:44
Phase 6a: Safe.pm stub
- Created Safe.pm stub that uses regular eval instead of sandboxing
- Sufficient for CPAN.pm which uses Safe to eval trusted metadata

Phase 6c: Import CPAN.pm and dependencies
- Added CPAN.pm and all CPAN/* modules via sync.pl
- Added CPAN::Meta, CPAN::Meta::YAML, CPAN::Meta::Requirements
- Added Parse::CPAN::Meta

Other changes:
- Import original Cwd.pm from perl5 (has pure Perl fallbacks)
- Created stub Cwd.java for XSLoader compatibility
- Fixed parser bug: -f ($x = $path) now parses correctly
  (file test operators with assignment inside parentheses)

Still needed:
- POSIX :sys_wait_h export tag
- Module::Build stub
- Try::Tiny compatibility shim
- Testing and documentation

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
CPAN::Distribution uses `use POSIX ':sys_wait_h'` but only `wait_h` was
defined. Added `sys_wait_h` as an alias.

Known blocking issues for CPAN.pm:
- JVM bytecode error with `-f func()` patterns (ASM frame computation)
- `Package::Name::func { block }` needs module loaded for prototype

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
1. Fix file test operators with function call operands (-f func())
   - Store operand in local variable before pushing operator string
   - Avoids ASM stack frame computation errors at merge points

2. Fix block argument parsing for undefined functions (func { } @Args)
   - When function is not yet defined and followed by {, use &@ prototype
   - Matches Perl5 behavior: { } after function call is treated as block

3. Fix file test with qualified package names (-f CPAN::find_perl)
   - Do not treat identifier as bareword filehandle if followed by ::
   - Allows parsing of qualified subroutine calls in file test operands

These fixes enable loading CPAN.pm which uses all three patterns.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
1. Fix character class range parsing [A-Z-0-9]
   - After completing a range like A-Z, skip range end char to prevent
     Z-0 from being interpreted as another (invalid) range
   - Both ExtendedCharClass.java and RegexPreprocessorHelper.java fixed
   - Matches Perl5 behavior: [A-Z-0-9] means A-Z, literal hyphen, and 0-9

2. Fix Safe::reval() to work with CHECKSUMS files
   - Wrap eval code with 'no strict vars' to allow package variables
   - CHECKSUMS files use $cksum without declaration

These fixes enable CPAN.pm to validate CHECKSUMS files and install modules.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
try and catch are only keywords when 'use feature "try"' is enabled.
Without this feature, they are treated as regular subroutines,
allowing Try::Tiny to work correctly.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
- Document Safe.pm stub, parser fixes, regex fixes
- Document try/catch feature gating for Try::Tiny compatibility
- List remaining items: version parsing, Makefile output, YAML, Module::Build
- Update resolved questions

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock changed the title CPAN Phase 6: Safe.pm stub and CPAN.pm import (WIP) CPAN Phase 6: Enable CPAN.pm for pure Perl module installation Mar 14, 2026
fglock and others added 4 commits March 14, 2026 12:00
- ExtUtils::MM: Add platform selection (MM_Unix vs MM_Win32) and MM alias
- ExtUtils::MM_Unix: parse_version using regex extraction, maybe_command
- ExtUtils::MM_Win32: Windows-specific maybe_command with PATHEXT support
- ExtUtils::MakeMaker: Generate stub Makefile for CPAN.pm compatibility

This enables CPAN.pm to successfully install pure Perl modules:
  ./jperl -MCPAN -e 'CPAN::Shell->notest("install", "Try::Tiny")'

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
CPAN::Distribution now checks $Config{d_fork} and falls back to
system() when fork is unavailable. This enables `make test` to work
on PerlOnJava without the contention loop.

Tests now run directly without fork, which means:
- No timeout handling for hung tests
- No signal-based test cancellation
- Works for normal test scenarios

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Interpreter:
- Add LOCAL_GLOB_DYNAMIC opcode for dynamic glob localization
- Implement executeLocalGlobDynamic in InlineOpcodeHandler
- Add compiler and disassembly support

JVM backend:
- Fix return value aliasing bug with local variables
- Add cloneScalars() to RuntimeList to copy values before local teardown
- Track usesLocal flag in JavaClassInfo to only clone when needed
- Conditionally clone return values in handleReturnOperator

Other fixes:
- Increase YAML::PP code point limit from 3MB to 50MB for large CPAN metadata

Tests:
- Add local_glob_dynamic.t with 10 tests for dynamic glob localization

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
The Perl Cwd.pm uses shell-based fallbacks (cd via backticks) on
Windows which do not work correctly in PerlOnJava. By providing
Internals::getcwd, the Cwd.pm module will detect and use this native
Java implementation instead.

This fixes the Windows CI failures in cwd.t, directory.t, and related
tests that depend on Cwd functionality.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the feature/cpan-phase6 branch 3 times, most recently from 694b037 to 7709695 Compare March 14, 2026 11:57
The METHOD_MAP on Windows was aliasing cwd/getcwd/fastcwd/fastgetcwd to
_NT_cwd (which uses shell backticks) before checking for Internals::getcwd.
This caused these functions to return empty on Windows because the shell
fallback does not work in PerlOnJava.

Changes:
- Skip all cwd-related METHOD_MAP assignments when Internals::getcwd is defined
- Explicitly define cwd/fastcwd/fastgetcwd to use Internals::getcwd
- This ensures proper cross-platform behavior without shell dependencies

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock force-pushed the feature/cpan-phase6 branch from 7709695 to 4799516 Compare March 14, 2026 12:01
fglock and others added 6 commits March 14, 2026 13:09
The Windows CI test failure showed that abs_path was returning paths
with a trailing backslash-dot instead of resolving the dot component.
This adds Internals::abs_path using Java File.getCanonicalPath which
properly resolves . and .. components on all platforms.

Cwd.pm now uses both Internals::getcwd and Internals::abs_path when
available, providing reliable path handling without shell fallbacks.

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
The abs_path method was defined but not registered with registerMethod(),
so it wasn't discoverable from Perl code. This adds the registration
so Cwd.pm can detect and use the Java implementation.

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Two regressions fixed:

1. Parser: -f() with empty parentheses now correctly uses $_ as default
   The hasParenthesis branch was calling parseExpression(0) even when
   the next token was ), causing a syntax error.

2. Cwd.pm: Move Internals::getcwd/abs_path setup BEFORE the XSLoader
   check (line 80) to prevent XSLoader from being loaded. Since
   DynaLoader::boot_DynaLoader is defined in PerlOnJava, Cwd.pm was
   trying to load XSLoader.pm which doesn't exist.

This restores:
- comp/require.t: 51 -> 1743 passing tests
- op/stat.t: 0 -> 64 passing tests

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Pattern like [[:digit:]-[:alpha:]] should match digits, literal hyphen,
and alpha chars. The previous fix for [A-Z-0-9] incorrectly treated
the hyphen before a POSIX class as a range operator.

Two fixes:
1. When processing '-', check if next char is start of POSIX class
   ([ followed by :) and treat the hyphen as literal if so
2. After processing a POSIX class, set lastChar = -1 since POSIX
   classes can't be range endpoints

This restores re/regexp.t test 937 which tests [[:digit:]-[:alpha:]].

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Our Cwd.pm integrates with Internals::getcwd and Internals::abs_path
for cross-platform support. The upstream version would overwrite these
customizations and try to load XSLoader which doesn't exist.

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
Distribution.pm has a fork fallback for PerlOnJava since fork() is not
implemented. The protected flag ensures sync.pl won't overwrite this
customization when updating from upstream perl5.

Generated with Devin (https://cli.devin.ai/docs)

Co-Authored-By: Devin <noreply@cognition.ai>
@fglock fglock merged commit 7fc13ee into master Mar 14, 2026
2 checks passed
@fglock fglock deleted the feature/cpan-phase6 branch March 14, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant