Create a PerlOnJava-specific ExtUtils::MakeMaker that:
- Pure Perl modules: Directly installs by copying .pm files
- XS modules: Detects and flags for manual/LLM-assisted resolution
Makefile.PL → WriteMakefile() → Makefile → make → make install
Key attributes parsed from Makefile.PL:
NAME- Module name (e.g., 'Some::Module')VERSION/VERSION_FROM- Module versionPREREQ_PM- DependenciesXS- Hash of XS files to compilePM- Hash of .pm files to installEXE_FILES- Scripts to installLIBS/INC- C library flags (XS only)
Makefile.PL → WriteMakefile() → Direct Install (or XS detection)
package ExtUtils::MakeMaker;
use strict;
use warnings;
our $VERSION = '7.70_perlonjava';
use Exporter 'import';
our @EXPORT = qw(WriteMakefile prompt);
use File::Copy;
use File::Path qw(make_path);
use File::Find;
use File::Spec;
use Cwd;
# Installation directory (configurable)
our $INSTALL_BASE = $ENV{PERLONJAVA_LIB} || './lib';
sub WriteMakefile {
my %args = @_;
my $name = $args{NAME} or die "NAME is required\n";
my $version = $args{VERSION} || $args{VERSION_FROM} && _extract_version($args{VERSION_FROM});
print "PerlOnJava MakeMaker: $name v$version\n";
# Check for XS files
my @xs_files = _find_xs_files(\%args);
if (@xs_files) {
return _handle_xs_module($name, \@xs_files, \%args);
}
# Pure Perl - proceed with installation
return _install_pure_perl(\%args);
}
sub _find_xs_files {
my ($args) = @_;
my @xs;
# Explicit XS hash
if ($args->{XS}) {
push @xs, keys %{$args->{XS}};
}
# Scan for .xs files
find(sub {
push @xs, $File::Find::name if /\.xs$/;
}, '.');
return @xs;
}
sub _handle_xs_module {
my ($name, $xs_files, $args) = @_;
print "\n";
print "=" x 60, "\n";
print "XS MODULE DETECTED: $name\n";
print "=" x 60, "\n";
print "\n";
print "This module contains XS/C code that requires porting to Java:\n";
for my $xs (@$xs_files) {
print " - $xs\n";
}
print "\n";
print "Options:\n";
print " 1. Check if PerlOnJava already has a Java port\n";
print " 2. Use the port-cpan-module skill to create a Java implementation\n";
print " 3. Find a pure Perl alternative module\n";
print "\n";
# Return a stub MM object
return PerlOnJava::MM::XSStub->new($name, $xs_files, $args);
}
sub _install_pure_perl {
my ($args) = @_;
my %pm;
# Use explicit PM hash or scan lib/
if ($args->{PM}) {
%pm = %{$args->{PM}};
} else {
# Default: lib/**/*.pm → $(INST_LIB)
find(sub {
return unless /\.pm$/;
my $src = $File::Find::name;
(my $dest = $src) =~ s{^lib/}{};
$pm{$src} = File::Spec->catfile($INSTALL_BASE, $dest);
}, 'lib') if -d 'lib';
}
# Install .pm files
for my $src (sort keys %pm) {
my $dest = $pm{$src};
my $dir = File::Spec->catpath((File::Spec->splitpath($dest))[0,1], '');
make_path($dir) unless -d $dir;
print "Installing $src → $dest\n";
copy($src, $dest) or warn "Failed to copy $src: $!\n";
}
# Install scripts
if ($args->{EXE_FILES}) {
for my $script (@{$args->{EXE_FILES}}) {
print "Installing script: $script\n";
# Copy to bin directory
}
}
print "\nInstallation complete!\n";
return PerlOnJava::MM::Installed->new($args);
}
sub _extract_version {
my ($file) = @_;
open my $fh, '<', $file or return '0';
while (<$fh>) {
if (/\$VERSION\s*=\s*['"]?([\d._]+)/) {
return $1;
}
}
return '0';
}
sub prompt {
my ($msg, $default) = @_;
$default //= '';
print "$msg [$default] ";
my $answer = <STDIN>;
chomp $answer if defined $answer;
return (defined $answer && $answer ne '') ? $answer : $default;
}
# Stub object for installed modules
package PerlOnJava::MM::Installed;
sub new { bless { args => $_[1] }, $_[0] }
sub flush { 1 }
# Stub object for XS modules (not installed)
package PerlOnJava::MM::XSStub;
sub new {
my ($class, $name, $xs_files, $args) = @_;
bless { name => $name, xs => $xs_files, args => $args }, $class;
}
sub flush {
my $self = shift;
print "Skipping XS module: $self->{name}\n";
0;
}
1;For XS modules, optionally call an LLM to generate Java implementations:
sub _handle_xs_module {
my ($name, $xs_files, $args) = @_;
if ($ENV{PERLONJAVA_AUTO_PORT}) {
print "Attempting automatic XS → Java port...\n";
for my $xs (@$xs_files) {
my $xs_content = _read_file($xs);
my $java_code = _call_llm_for_port($name, $xs_content);
if ($java_code) {
_write_java_module($name, $java_code);
print "Generated Java implementation for $xs\n";
}
}
}
# ... rest of handling
}
sub _call_llm_for_port {
my ($module_name, $xs_content) = @_;
# Call external LLM API (Claude, GPT, etc.)
# Using the port-cpan-module skill's knowledge
my $prompt = <<"END_PROMPT";
Port this Perl XS code to Java for PerlOnJava.
Module: $module_name
XS Code:
$xs_content
Create a Java class extending PerlModuleBase with:
1. Constructor calling super("$module_name", false)
2. Static initialize() method registering all methods
3. Static methods with signature: RuntimeList methodName(RuntimeArray args, int ctx)
END_PROMPT
# HTTP call to LLM API
# ...
return $java_code;
}sub WriteMakefile {
my %args = @_;
# Check prerequisites first
if ($args{PREREQ_PM}) {
my @missing;
for my $dep (keys %{$args{PREREQ_PM}}) {
my $version = $args{PREREQ_PM}{$dep};
unless (_module_available($dep, $version)) {
push @missing, "$dep (>= $version)";
}
}
if (@missing) {
print "Missing dependencies:\n";
print " - $_\n" for @missing;
if ($ENV{PERLONJAVA_AUTO_DEPS}) {
_install_dependencies(@missing);
} else {
print "\nInstall with: jcpan install @missing\n";
return;
}
}
}
# ... continue with installation
}- Create
ExtUtils/MakeMaker.pmthat interceptsWriteMakefile() - Implement pure Perl module installation (copy .pm files)
- Detect XS files and print helpful message
- Handle
PREREQ_PMdependency checking
- Parse XS files to identify functions
- Generate skeleton Java code
- Create issue/task for manual porting
- Integration with port-cpan-module skill
- API integration for LLM calls
- XS → Java translation prompts
- Verification and testing of generated code
- Human review workflow
src/main/perl/lib/ExtUtils/MakeMaker.pm- Main modulesrc/main/perl/lib/ExtUtils/MM.pm- Stub for compatibilitysrc/main/perl/lib/ExtUtils/MY.pm- Stub for compatibilitysrc/main/perl/lib/ExtUtils/MakeMaker/Config.pm- Config wrapper
perl Makefile.PLshould work and either install or report XSmakeandmake installbecome no-ops (or print helpful message)PREREQ_PMdependencies are checked but not auto-installed (Phase 1)- No actual Makefile is generated (no need for
make)
# Download a CPAN module
tar xzf Some-Module-1.00.tar.gz
cd Some-Module-1.00
# For pure Perl:
jperl Makefile.PL
# → "Installing lib/Some/Module.pm → /path/to/lib/Some/Module.pm"
# → "Installation complete!"
# For XS modules:
jperl Makefile.PL
# → "XS MODULE DETECTED: Some::Module"
# → "This module contains XS/C code..."
# → Lists .xs files and optionsPERLONJAVA_LIB- Installation directory (default: ./lib)PERLONJAVA_AUTO_PORT- Enable automatic LLM-based XS portingPERLONJAVA_AUTO_DEPS- Automatically install dependenciesPERLONJAVA_VERBOSE- Verbose output
cpan_client.md- CPAN client statusdocs/guides/module-porting.md- Module porting guide.agents/skills/port-cpan-module/- Port CPAN module skill