From 471edd782ccb25475dc6b8e80266783dc13f14d8 Mon Sep 17 00:00:00 2001 From: "Flavio S. Glock" Date: Thu, 30 Apr 2026 16:18:23 +0200 Subject: [PATCH] feat: add YAML::XS shim that delegates to bundled YAML::PP Before this change `./jcpan -t YAML::XS` failed catastrophically with "Can't locate YAML/XS/LibYAML.pm in @INC" on every test, because the upstream YAML::XS distribution (YAML-LibYAML) is XS-only and PerlOnJava cannot load XS. We already ship a JVM-backed YAML::PP (SnakeYAML under the hood), so this commit adds two pure-Perl shims that re-expose YAML::PP under the YAML::XS namespace: - src/main/perl/lib/YAML/XS::LibYAML.pm Tiny stub that exports Load/Dump by delegating to YAML::PP. The cpan-installed upstream YAML::XS.pm in ~/.perlonjava/lib uses this to satisfy `use YAML::XS::LibYAML qw(Load Dump);`. - src/main/perl/lib/YAML/XS.pm Bundled fallback so `use YAML::XS;` works out of the box without first running jcpan. Implements Load/Dump/LoadFile/DumpFile and declares the standard YAML::XS package globals. With the shim in place `jcpan -t YAML::XS` configures, builds and runs the test suite, going from 0/49 test programs running to 17/49 passing (192/283 subtests). The remaining failures are YAML::PP/SnakeYAML semantic limitations (booleans, blessed/tied refs, !!perl/code, !!perl/regexp, !!perl/glob round-tripping, alias output format, upstream error message wording) and are not caused by the shim. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../org/perlonjava/core/Configuration.java | 4 +- src/main/perl/lib/YAML/XS.pm | 87 +++++++++++++++++++ src/main/perl/lib/YAML/XS/LibYAML.pm | 40 +++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 src/main/perl/lib/YAML/XS.pm create mode 100644 src/main/perl/lib/YAML/XS/LibYAML.pm diff --git a/src/main/java/org/perlonjava/core/Configuration.java b/src/main/java/org/perlonjava/core/Configuration.java index 7447a6d29..27f509f7e 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 = "c5509171f"; + public static final String gitCommitId = "0416ffb3b"; /** * 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 30 2026 15:47:59"; + public static final String buildTimestamp = "Apr 30 2026 16:17:32"; // Prevent instantiation private Configuration() { diff --git a/src/main/perl/lib/YAML/XS.pm b/src/main/perl/lib/YAML/XS.pm new file mode 100644 index 000000000..2f9492157 --- /dev/null +++ b/src/main/perl/lib/YAML/XS.pm @@ -0,0 +1,87 @@ +package YAML::XS; + +# +# PerlOnJava shim for YAML::XS. +# +# The real YAML::XS distribution wraps libyaml via XS. PerlOnJava cannot +# load XS, so this module re-implements YAML::XS' public API on top of +# the bundled YAML::PP (backed by SnakeYAML on the JVM). +# +# This shim is bundled with PerlOnJava so `use YAML::XS;` works out of +# the box. If jcpan installs the upstream YAML::XS.pm into +# ~/.perlonjava/lib, that copy will be picked up first, but it relies on +# YAML::XS::LibYAML which we also provide as a shim. +# +# Limitations: +# - YAML::XS configuration globals ($Boolean, $Indent, $LoadCode, +# $DumpCode, $UseCode, $LoadBlessed, $ForbidDuplicateKeys, +# $QuoteNumericStrings, $coderef2text, $glob2hash) are accepted but +# have no effect. +# - !!perl/code, !!perl/regexp and !!perl/glob round-tripping is not +# supported. +# + +use strict; +use warnings; + +our $VERSION = '0.906.0'; + +use base 'Exporter'; +our @EXPORT = qw(Load Dump); +our @EXPORT_OK = qw(Load Dump LoadFile DumpFile); +our %EXPORT_TAGS = ( all => [qw(Load Dump LoadFile DumpFile)] ); + +our ( + $Boolean, + $DumpCode, + $ForbidDuplicateKeys, + $Indent, + $LoadBlessed, + $LoadCode, + $UseCode, + $QuoteNumericStrings, + $coderef2text, + $glob2hash, +); +$ForbidDuplicateKeys = 0; +$QuoteNumericStrings = 1; + +use YAML::PP (); +use Scalar::Util qw(openhandle); + +sub Load { YAML::PP::Load(@_) } +sub Dump { YAML::PP::Dump(@_) } + +sub LoadFile { + my $filename = shift; + my $IN; + if (openhandle($filename)) { + $IN = $filename; + } + else { + open $IN, '<', $filename + or die "Can't open '$filename' for input:\n$!"; + } + my $yaml = do { local $/; scalar <$IN> }; + return YAML::PP::Load($yaml); +} + +sub DumpFile { + my $filename = shift; + my $OUT; + if (openhandle($filename)) { + $OUT = $filename; + } + else { + my $mode = '>'; + if ($filename =~ /^\s*(>{1,2})\s*(.*)$/) { + ($mode, $filename) = ($1, $2); + } + open $OUT, $mode, $filename + or die "Can't open '$filename' for output:\n$!"; + } + local $/ = "\n"; + print $OUT YAML::PP::Dump(@_); +} + +1; diff --git a/src/main/perl/lib/YAML/XS/LibYAML.pm b/src/main/perl/lib/YAML/XS/LibYAML.pm new file mode 100644 index 000000000..b4767bbce --- /dev/null +++ b/src/main/perl/lib/YAML/XS/LibYAML.pm @@ -0,0 +1,40 @@ +package YAML::XS::LibYAML; + +# +# PerlOnJava shim for YAML::XS::LibYAML. +# +# The CPAN distribution YAML::XS (a.k.a. YAML-LibYAML) ships an XS module +# called YAML::XS::LibYAML which wraps the C libyaml library. PerlOnJava +# cannot load XS, so we provide this pure-Perl stub that exports Load/Dump +# by delegating to the bundled YAML::PP implementation (backed by +# SnakeYAML on the JVM). This lets `use YAML::XS;` work for the common +# Load/Dump round-trip use cases. +# +# Limitations: +# - YAML::XS-specific globals such as $YAML::XS::Boolean, $LoadCode, +# $DumpCode, $UseCode, $LoadBlessed, $ForbidDuplicateKeys, $Indent +# are accepted (the upstream YAML::XS.pm declares them) but are not +# honoured here. +# - Code, regexp and glob round-tripping (the !!perl/code, !!perl/regexp, +# !!perl/glob tags) is not supported. +# + +use strict; +use warnings; + +our $VERSION = '0.906.0'; + +use base 'Exporter'; +our @EXPORT_OK = qw(Load Dump); + +use YAML::PP (); + +sub Load { + return YAML::PP::Load(@_); +} + +sub Dump { + return YAML::PP::Dump(@_); +} + +1;