Skip to content

Commit e03cba1

Browse files
fglockcodex
andcommitted
fix: alias extended errno to errno
Initialize $^E with the same errno dualvar used by $!, matching Unix Perl behavior for numeric and string errno contexts. Add regression coverage for $^E startup, assignment, localization, and clearing behavior, and update the CPANPLUS progress note with the resolved File::Copy warning. Generated with [Codex](https://openai.com/codex) Co-Authored-By: Codex <codex@openai.com>
1 parent 09c334f commit e03cba1

3 files changed

Lines changed: 55 additions & 12 deletions

File tree

dev/modules/cpanplus.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@ EXIT: 0
1313

1414
The run also verifies the dependency chain that previously blocked CPANPLUS: `Archive::Extract`, `Object::Accessor`, `File::Fetch`, `Log::Message`, `Module::Loaded`, `Package::Constants`, `Log::Message::Simple`, and `Term::UI`.
1515

16-
The only observed remaining issue is a non-fatal warning during CPANPLUS' own suite:
17-
18-
```text
19-
Use of uninitialized value in addition (+) at jar:PERL5LIB/File/Copy.pm line 303.
20-
```
16+
The previously observed non-fatal warning during CPANPLUS' own suite is now fixed:
17+
`$^E` aliases `$!` as a defined errno dualvar, so `File::Copy` can snapshot
18+
`($! + 0, $^E + 0)` without an uninitialized warning.
2119

2220
## Symptom
2321

@@ -70,6 +68,11 @@ Version checks then exposed decimal vs dotted-version numification differences.
7068

7169
The final CPANPLUS blocker was in the generated Makefile for a dummy `Foo-Bar` distribution. When `Makefile.PL` was rerun after `blib/lib` already existed, PerlOnJava's MakeMaker treated staged `blib/lib/*.pm` files as source files. Its generated `pm_to_blib` target could delete `blib/lib/Foo/Bar.pm` and then try to copy that same path back to itself. MakeMaker now prefers real `lib/` sources over stale `blib/` entries and does not stage already-staged files back into `blib`.
7270

71+
The last warning-only issue came from `File::Copy` saving both `$!` and `$^E`
72+
after a failed move fallback. Perl on Unix aliases `$^E` to `$!`; PerlOnJava
73+
had initialized `$^E` as a plain undef scalar, so numeric `$^E` warned under
74+
`use warnings`. `$^E` now uses the same `ErrnoVariable` instance as `$!`.
75+
7376
## Completed Work
7477

7578
- Fixed loop-control parsing in [`OperatorParser.java`](../../src/main/java/org/perlonjava/frontend/parser/OperatorParser.java).
@@ -86,8 +89,10 @@ The final CPANPLUS blocker was in the generated Makefile for a dummy `Foo-Bar` d
8689
- Added regression coverage in [`version_numify.t`](../../src/test/resources/unit/version_numify.t).
8790
- Fixed PerlOnJava MakeMaker reruns after `blib/lib` exists so stale staged files are not copied onto themselves.
8891
- Added regression coverage in [`makemaker_stale_blib_source.t`](../../src/test/resources/unit/makemaker_stale_blib_source.t).
92+
- Fixed `$^E` to alias `$!` as a defined errno dualvar, matching Unix Perl and removing the `File::Copy.pm line 303` warning.
93+
- Added regression coverage in [`errno_special_vars.t`](../../src/test/resources/unit/errno_special_vars.t).
8994
- Verified `Object::Accessor` upstream suite passes: `Files=7, Tests=155, Result: PASS`.
90-
- Verified `./jcpan -t CPANPLUS` passes: `Files=20, Tests=1751, Result: PASS`.
95+
- Verified `./jcpan -t CPANPLUS` passes without the `File::Copy.pm line 303` warning: `Files=20, Tests=1751, Result: PASS`.
9196
- Verified `make` passes.
9297

9398
## Acceptance
@@ -101,14 +106,17 @@ make
101106

102107
Before running the full `jcpan -t CPANPLUS` acceptance, make sure no local CPANPLUS distropref is masking the dependency path. A previous investigation generated `/Users/fglock/.perlonjava/cpan/prefs/CPANPLUS.yml`; move it aside or use an isolated CPAN home before judging dependency discovery.
103108

109+
Archive::Extract's `.Z` test invokes `/usr/bin/uncompress -c`. In the Codex
110+
sandbox this can fail with `uncompress: /dev/stdout: Operation not permitted`,
111+
which prevents CPANPLUS from reaching its own tests. Run the full CPANPLUS
112+
acceptance outside the sandbox when verifying the `.Z` dependency path.
113+
104114
## Next Steps
105115

106-
1. Reduce the non-fatal `File::Copy.pm line 303` warning from CPANPLUS' own suite. It appears to come from numeric conversion of `$!` or `$^E` after a failed move fallback, but it does not currently fail CPANPLUS.
107-
2. Re-run `timeout 1200 ./jcpan -t CPANPLUS` from a fresh or isolated CPAN home before merging if cache independence is required.
108-
3. Audit whether MakeMaker still needs to discover installable files from `blib/lib`; if it does, keep the new no-self-staging behavior as the regression guard.
109-
4. Keep CPANPLUS as a regression target when touching `Archive::Extract`, `Object::Accessor`, `ExtUtils::MakeMaker`, parser precedence, or `version`.
116+
1. Re-run `timeout 1200 ./jcpan -t CPANPLUS` from a fresh or isolated CPAN home before merging if cache independence is required.
117+
2. Audit whether MakeMaker still needs to discover installable files from `blib/lib`; if it does, keep the new no-self-staging behavior as the regression guard.
118+
3. Keep CPANPLUS as a regression target when touching `Archive::Extract`, `Object::Accessor`, `ExtUtils::MakeMaker`, parser precedence, `version`, or errno special variables.
110119

111120
## Open Questions
112121

113-
- Does the `File::Copy` warning reveal a generic `$!` / `$^E` numeric conversion difference when the error variables are unset?
114122
- Are there CPAN distributions that intentionally rely on MakeMaker installing files that only exist under `blib/lib` after configure/build, and should that path be modeled more explicitly?

src/main/java/org/perlonjava/runtime/runtimetypes/GlobalContext.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ public static void initializeGlobals(CompilerOptions compilerOptions) {
7676
GlobalVariable.getGlobalVariable("main::\"").set(" "); // initialize $" to " "
7777
GlobalVariable.getGlobalVariable("main::a"); // initialize $a to "undef"
7878
GlobalVariable.getGlobalVariable("main::b"); // initialize $b to "undef"
79-
GlobalVariable.globalVariables.put("main::!", new ErrnoVariable()); // initialize $! with dualvar support
79+
ErrnoVariable errnoVariable = new ErrnoVariable();
80+
GlobalVariable.globalVariables.put("main::!", errnoVariable); // initialize $! with dualvar support
81+
// Model $^E as $! until platform-specific extended errors are needed.
82+
GlobalVariable.globalVariables.put(encodeSpecialVar("E"), errnoVariable);
8083
// Initialize $, (output field separator) with special variable class
8184
if (!GlobalVariable.globalVariables.containsKey("main::,")) {
8285
var ofs = new OutputFieldSeparator();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use strict;
2+
use warnings;
3+
use Test::More;
4+
5+
my @warnings;
6+
local $SIG{__WARN__} = sub { push @warnings, @_ };
7+
8+
ok(defined $^E, '$^E is defined at startup');
9+
is(0 + $^E, 0, '$^E defaults to numeric zero');
10+
is("$^E", '', '$^E defaults to an empty string');
11+
is_deeply(\@warnings, [], 'numeric $^E does not warn when errno is clear');
12+
13+
$! = 2;
14+
is(0 + $^E, 2, '$^E reflects numeric $!');
15+
is("$^E", "$!", '$^E reflects string $!');
16+
17+
$^E = 3;
18+
is(0 + $!, 3, 'assigning $^E updates numeric $!');
19+
is("$!", "$^E", 'assigning $^E updates string $!');
20+
21+
$! = 2;
22+
{
23+
local $^E = 0;
24+
is(0 + $!, 0, 'local $^E localizes shared numeric errno');
25+
}
26+
is(0 + $!, 2, 'leaving local $^E restores shared numeric errno');
27+
28+
$! = 0;
29+
is(0 + $^E, 0, 'clearing $! clears numeric $^E');
30+
is("$^E", '', 'clearing $! clears string $^E');
31+
32+
done_testing();

0 commit comments

Comments
 (0)