Skip to content

feat(moose): bundle pure-Perl Moose runtime dependencies#627

Merged
fglock merged 1 commit intomasterfrom
feature/moose-bundled-deps-20260429-175124
Apr 29, 2026
Merged

feat(moose): bundle pure-Perl Moose runtime dependencies#627
fglock merged 1 commit intomasterfrom
feature/moose-bundled-deps-20260429-175124

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 29, 2026

Summary

examples/moose.pl (and any use Moose; script) failed on a fresh PerlOnJava install with:

Can't locate Class/Load.pm in @INC

It worked on machines where jcpan had already populated ~/.perlonjava/lib, but not on a clean install — because the bundled Moose 2.4000 in src/main/perl/lib/Moose.pm requires Class::Load, Module::Runtime, Data::OptList, etc., and none of those were bundled.

This PR bundles the missing pure-Perl runtime dependencies into src/main/perl/lib/, copied verbatim from CPAN (all under the same Perl-5 license as Perl itself):

  • Class/Load.pm, Class/Load/PP.pm
  • Module/Runtime.pm, Module/Implementation.pm
  • Data/OptList.pm
  • Params/Util.pm, Params/Util/PP.pm
  • Sub/Install.pm, Sub/Exporter.pm, Sub/Exporter/Progressive.pm
  • MRO/Compat.pm
  • Devel/OverloadInfo.pm, Devel/StackTrace.pm, Devel/StackTrace/Frame.pm
  • Dist/CheckConflicts.pm
  • Eval/Closure.pm
  • Package/DeprecationManager.pm

Why Class/Load/XS.pm is intentionally NOT bundled

Its only job is to call XSLoader::load(), which is a no-op on the JVM. If we shipped it in the jar:

  1. Class::Load's Module::Implementation loader tries XS first.
  2. XSLoader::load("Class::Load::XS") is called.
  3. The Java-side XSLoader walks its fallbacks. Eventually it hits loadJarShimOverrides, which reads the jar copy of Class/Load/XS.pm and reports success — even though no symbols got installed.
  4. Module::Implementation thinks XS loaded fine, copies nothing into Class::Load, and the next caller blows up with Undefined subroutine &Class::Load::XS::is_class_loaded.

By keeping Class/Load/XS.pm out of the jar, Module::Implementation cleanly falls through to Class::Load::PP. Same reasoning is why we've never bundled Package::Stash::XS.

What if the user later installs Class::Load::XS via jcpan?

Still safe. The chain becomes:

  1. Class/Load/XS.pm loads from ~/.perlonjava/lib, calls XSLoader::load("Class::Load::XS").
  2. Java-side XSLoader::load walks its fallbacks: no Java class, no functional @ISA, no ::PP in %INC, and no jar shim for Class::Load::XS (since we didn't bundle one).
  3. It dies with "Can't load loadable object for module Class::Load::XS: no Java XS implementation available".
  4. Module::Implementation wraps the require in try { require_module(...) }, catches the die, and falls back to Class::Load::PP.

Verified with a faked install:

mkdir -p /tmp/fake-cpan/Class/Load
cat > /tmp/fake-cpan/Class/Load/XS.pm <<'EOF'
package Class::Load::XS;
our $VERSION = '0.10';
use Class::Load 0.20;
use XSLoader;
XSLoader::load(__PACKAGE__, $VERSION);
1;
EOF
JAVA_TOOL_OPTIONS="-Duser.home=/tmp/empty-home" \
  PERL5LIB=/tmp/fake-cpan ./jperl examples/moose.pl
# 1..7  (all subtests pass)

Test plan

  • Repro confirmed before the change:
    JAVA_TOOL_OPTIONS="-Duser.home=/tmp/empty-home" ./jperl examples/moose.pl # Can't locate Class/Load.pm in @INC
  • After the change, all 7 subtests in examples/moose.pl pass with a clobbered user.home:
    JAVA_TOOL_OPTIONS="-Duser.home=/tmp/empty-home" ./jperl examples/moose.pl # 1..7 (all ok)
  • Same example still passes when a (jcpan-style) Class::Load::XS is present in PERL5LIB.
  • make succeeds (full unit-test suite, no regressions).

Generated with Devin

Bundle the upstream pure-Perl modules that the bundled Moose 2.4000
needs to load, so `examples/moose.pl` (and any `use Moose;` script)
works on a fresh PerlOnJava install without first running `jcpan` to
populate ~/.perlonjava/lib.

Repro before this change:

    JAVA_TOOL_OPTIONS="-Duser.home=/tmp/empty-home" \
        ./jperl examples/moose.pl
    # Can't locate Class/Load.pm in @inc

Modules bundled (copied verbatim from CPAN, all pure-Perl, all under
the same Perl-5 license):

  Class/Load.pm
  Class/Load/PP.pm
  Module/Runtime.pm
  Module/Implementation.pm
  Data/OptList.pm
  Params/Util.pm
  Params/Util/PP.pm
  Sub/Install.pm
  Sub/Exporter.pm
  Sub/Exporter/Progressive.pm
  MRO/Compat.pm
  Devel/OverloadInfo.pm
  Devel/StackTrace.pm
  Devel/StackTrace/Frame.pm
  Dist/CheckConflicts.pm
  Eval/Closure.pm
  Package/DeprecationManager.pm

Class/Load/XS.pm is intentionally NOT bundled: its only job is to
XSLoader::load() a native .so, which does nothing on the JVM and
leaves Module::Implementation thinking the XS impl loaded
successfully but with no symbols, so callers blow up with
"Undefined subroutine &Class::Load::XS::is_class_loaded". With
Class/Load/XS.pm absent, Module::Implementation falls back cleanly
to Class::Load::PP. The same reasoning is why we don't ship
Package::Stash::XS.

Verification:

    JAVA_TOOL_OPTIONS="-Duser.home=/tmp/empty-home" \
        ./jperl examples/moose.pl
    # 1..7
    # all 7 subtests pass
    make
    # BUILD SUCCESSFUL, all unit tests pass

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock merged commit ffa3d8a into master Apr 29, 2026
2 checks passed
@fglock fglock deleted the feature/moose-bundled-deps-20260429-175124 branch April 29, 2026 16: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