. "$BST_LIB" require perl libmodule-build-perl/present # BST-TIMEOUT: 1800 # Module::Build 0.4234 (poky meta/recipes-devtools/perl/libmodule-build-perl_ # 0.4234.bb) -- the pure-Perl build system CPAN distributions drive from a # Build.PL, and the fifteenth Perl module on this image under BRINTOS-999. # # NOTE THE LAYER: poky, not meta-oe. The ticket names "meta-openembedded/meta-oe" # as the recipe source and there is no libmodule-build-perl there -- meta-perl # carries only libmodule-build-TINY-perl, a 120-line reimplementation that is a # different distribution. The recipe used here is poky's own. # # THIS SUITE'S PASS CRITERION IS THE DISTRIBUTION'S OWN t/, NOT A HAND-WRITTEN # BATTERY -- the ticket's own wording. The port installs the suite # (libmodule-build-perl-ptest, from ptest-perl, enabled for this recipe alone) # and section 4 runs prove over it. # # Measured on an x86-64 control running the IDENTICAL 0.4234 tree, built the way # the recipe builds it (Build.PL --create_packlist=0, t/pod_parser.t removed) and # driven the way section 4 drives it: 52 files, 1173 tests, Result PASS, rc 0, # 26 s wall. One file skipped whole -- t/par.t, PAR::Dist not installed. Those # are the figures section 4 asserts against, and they are demonstrated rather # than guessed. # # WHAT IS DIFFERENT ABOUT THIS PORT, AND WHY IT IS WORTH SAYING. The two Perl # ports before it -- libdbi-perl (BRINTOS-1317) and libio-pty-perl (BRINTOS-1319) # -- are XS, and neither module can be loaded on this arm at all (BRINTOS-881), # so for both the criterion is unverifiable and the suite says so by name. # Module::Build is PURE PERL. There is no XS half, no loadable object, nothing # for a missing DynaLoader to fail to load -- so the module itself is expected to # load here, and section 2 asserts that as an ordinary green rather than a # self-flipping red. This is also the FIRST cpan_build recipe on this image: # every Perl port before it inherited `cpan` (Makefile.PL/ExtUtils::MakeMaker), # and the Build.PL path had never been exercised on wasm32-hwjs. # # WHAT THE SUITE ACTUALLY EXERCISES, which is why it is the right criterion and # not a smoke test: t/'s cases generate throwaway distributions in a temporary # directory and run configure/build/test/install on each, as perl SUBPROCESSES. # So a green here is a statement about fork/exec, File::Temp, File::Spec, the # TAP harness and the installer on this target, not merely about whether one .pm # parses. # # Four layers: # 1. filesystem facts -- what the port SHIPS, asserted before anything starts # perl, so they hold on a boot where the interpreter cannot load a module. # 2. can perl load Module::Build on this boot, classified on the diagnostic, # plus the one command-line tool the package installs. # 3. one throwaway distribution driven END TO END by hand, outside the harness. # 4. the criterion: prove over the installed suite. V=$(ls -d /usr/lib/perl5/vendor_perl/5.* 2>/dev/null | head -1) CORE=$(ls -d /usr/lib/perl5/5.* 2>/dev/null | head -1) PT=/usr/lib/libmodule-build-perl/ptest # ------------------------- 1. what the port SHIPS: filesystem facts ---------- # Module::Build is pure Perl, so it installs into the ARCH-INDEPENDENT # vendor_perl// -- NOT the -linux subdirectory the two XS ports before # it landed in. Asserted in that direction on purpose: a build that started # emitting an arch-dependent layout here would mean cpan_build had begun treating # this as an XS distribution, and no other case in this file would say so. check libmodule-build-perl/vendor-dir test -n "$V" check libmodule-build-perl/module-installed test -f "$V/Module/Build.pm" check libmodule-build-perl/base-installed test -f "$V/Module/Build/Base.pm" check libmodule-build-perl/compat-installed test -f "$V/Module/Build/Compat.pm" # ConfigData.pm is GENERATED by Build.PL, not shipped in the tarball. Asserted # because the suite reads feature flags out of it (t/ppm.t, t/par.t) and a build # that stopped generating it would leave those files with an unsatisfiable # `blib_load('Module::Build::ConfigData')` and no other case here would say so. check libmodule-build-perl/configdata-generated test -f "$V/Module/Build/ConfigData.pm" # The Unix platform driver -- the one this target actually dispatches to. check libmodule-build-perl/platform-unix test -f "$V/Module/Build/Platform/Unix.pm" # The one command-line tool the package installs. The recipe rewrites its shebang # to /usr/bin/env perl in do_install:append, so a packaging change that dropped # that rewrite leaves a script pointing at the BUILD HOST's perl. check libmodule-build-perl/config-data-installed test -x /usr/bin/config_data check_eq libmodule-build-perl/config-data-shebang '#!/usr/bin/env perl' \ "$(head -1 /usr/bin/config_data 2>/dev/null)" # THE INSTALLED SUITE -- the deliverable the ticket's criterion runs on. Unlike # the two ports before it, this recipe does not rely on ptest-perl's t/ copy # alone: its own do_install_ptest stages inc/, blib/, _build/ and lib/ beside it, # and every one of those is load-bearing. blib/ is what -Mblib puts on @INC; # _build/ is the build state Module::Build->current reads (MBTest::check_compiler # calls it before any test runs); inc/ carries the bundled build-time modules. check libmodule-build-perl/ptest-dir test -d "$PT/t" check libmodule-build-perl/ptest-runptest test -f "$PT/run-ptest" check libmodule-build-perl/ptest-blib test -d "$PT/blib/lib" check libmodule-build-perl/ptest-build test -f "$PT/_build/build_params" check libmodule-build-perl/ptest-inc test -f "$PT/inc/MBVersion.pm" check libmodule-build-perl/ptest-mbtest test -f "$PT/t/lib/MBTest.pm" check libmodule-build-perl/ptest-distgen test -f "$PT/t/lib/DistGen.pm" # Pinned EXACTLY at 52: the distribution ships 53 .t files and the recipe removes # exactly one (t/pod_parser.t, in do_install:prepend -- there is no # libpod-parser-perl recipe). A count that moves means the packaging changed. check_eq libmodule-build-perl/ptest-count 52 "$(find "$PT/t" -name '*.t' 2>/dev/null | wc -l | tr -d ' ')" # The removal itself, asserted in its own right: it is a deliberate act of the # recipe, and a count that stayed at 52 while pod_parser.t came back would need # some other file to have gone. check libmodule-build-perl/ptest-no-pod-parser test ! -e "$PT/t/pod_parser.t" # _build/build_params is rewritten at do_install_ptest to name the TARGET perl. # Left unrewritten it names the build host's perl-native and every generated # Build script in section 3 and 4 would point off this rootfs. check libmodule-build-perl/build-params-target-perl \ grep -q "/usr/bin/perl" "$PT/_build/build_params" # THE HARNESS THE CRITERION NAMES. prove reaches this image from libdbi-perl # (perl-misc + perl-module-app-prove) and RUNS because libdev-checklib-perl added # the 43 perl-module-tap-* splits. Asserted as ordinary reds, not as a `require`: # their absence is a regression to report, not a reason to skip. check libmodule-build-perl/prove-installed test -x /usr/bin/prove check libmodule-build-perl/prove-lib test -f "$CORE/App/Prove.pm" # ------------------ 2. can perl load it, and does the tool run --------------- # Pure Perl, so this is expected GREEN -- the first Perl port in three whose # module is loadable on this arm. A red here is classified rather than reported # bare, because attributing it to the wrong defect is the error a later reader # cannot detect. mout=$(perl -MModule::Build -e 'print "MB-OK ", Module::Build->VERSION, "\n"' 2>&1); mrc=$? case "$mout" in *MB-OK*) bst_ok libmodule-build-perl/import-load ;; *) mdiag=$(printf '%s\n' "$mout" | awk '/^perl: warning/ { skip=1; next } /^[ \t]/ && skip { next } { skip=0; print }') [ -n "$mdiag" ] || mdiag=$mout case "$mdiag" in *mremap_chunk*|*munmap_chunk*|*"corrupted size"*|*"corrupted double-linked list"*|\ *"double free or corruption"*|*"free(): "*|*"malloc(): "*|*"realloc(): "*) # The pattern set is WIDE on purpose and was widened by measurement under # BRINTOS-1011, not by guesswork: glibc has a dozen spellings for one # abort, and missing one turns an attributed red into a false alarm in # the single field a later reader attributes by. mwhy="linux-in-the-browser#154 -- perl aborts in glibc's allocator while compiling a module" ;; *"Can't locate Module/Build.pm"*) mwhy="OURS -- libmodule-build-perl is not on this image, or did not install into @INC" ;; *"Can't locate loadable object"*|*"Can't locate DynaLoader"*) # Should be UNREACHABLE for this distribution: Module::Build is pure Perl # and has no XS half at all. If it ever fires, something in its closure # grew an XS dependency and BRINTOS-881 has become this port's problem # too -- which is exactly what a later reader needs told, in the field # they attribute by. mwhy="BRINTOS-881 -- a module in Module::Build's closure needs a loadable object, which this -Uusedl perl cannot load; unexpected for a pure-Perl distribution" ;; *"Can't locate"*) mwhy="OURS -- a module in Module::Build's closure is missing from this image; read the name in the diagnostic" ;; *) if [ "$mrc" = 134 ]; then mwhy="unclassified SIGABRT -- rc=134 with no allocator diagnostic; probably #154 with a spelling this guard has not seen, but not asserted as one" else mwhy="unclassified -- none of #154's allocator abort, a missing loadable object, or a missing-module diagnostic" fi ;; esac bst_notok libmodule-build-perl/import-load "rc=$mrc $(_bst_1line "$mdiag") -- $mwhy" # NAMED, not silent. The ticket's criterion IS section 4, and a suite that # merely stopped here would report a dozen greens and read as a pass. bst_notok libmodule-build-perl/prove-unverified \ "not run: perl cannot load Module::Build ($mwhy) -- prove over the 52 installed test files (1173 assertions on the x86-64 control) is unverified on wasm32-hwjs" exit 0 ;; esac check_out libmodule-build-perl/version 0.4234 \ perl -MModule::Build -e 'print Module::Build->VERSION, "\n"' # The platform dispatch this target actually takes. Module::Build interposes a # Module::Build::Platform:: class into its own @ISA at load time, from $^O # and then from Perl::OSType's classification of it; anything but Unix here would # mean $^O is reporting something this distribution does not recognise, and every # path-handling case in section 4 would be running against the wrong driver. # Read out of @ISA because that IS the dispatch -- there is no accessor for it. check_out libmodule-build-perl/platform-dispatch 'MB-PLATFORM Module::Build::Platform::Unix' \ perl -MModule::Build -e 'print "MB-PLATFORM @Module::Build::ISA\n"' # config_data is the package's only executable. Run it for real, against # Module::Build's own ConfigData -- which section 1 asserted exists -- rather than # --help, which this distribution exits non-zero from by design. The header line # is asserted, not a particular feature's value: every feature in this file is # marked `dynamic`, so its value is recomputed from what is installed at the # moment of the call and is a property of the image, not of the port. check_out libmodule-build-perl/config-data-runs 'Features defined in Module::Build::ConfigData' \ config_data --module Module::Build --feature # ---------- 3. one throwaway distribution, end to end, outside the harness ---- # The suite in section 4 does this ~50 times under a TAP harness. Doing it once # here, plainly, means a `prove` that somehow reported success over zero real # work still has to answer a direct question -- and, when section 4 goes red, it # separates "Module::Build cannot build anything on this target" from "the # harness could not run the files". # # Deliberately NOT using t/lib/DistGen.pm: that is the same code section 4 runs, # so a defect in it would take both. This writes the distribution by hand. WD=${TMPDIR:-/tmp}/bst-libmodule-build-perl.$$ mkdir -p "$WD/dist/lib" cat > "$WD/dist/Build.PL" <<'EOF' use Module::Build; Module::Build->new( module_name => 'BstProbe', dist_abstract => 'brintos-selftests throwaway distribution', dist_author => 'brintos-selftests', dist_version => '0.01', license => 'perl', )->create_build_script; EOF cat > "$WD/dist/lib/BstProbe.pm" <<'EOF' package BstProbe; our $VERSION = '0.01'; sub answer { 42 } 1; EOF mkdir -p "$WD/dist/t" cat > "$WD/dist/t/probe.t" <<'EOF' use strict; use warnings; print "1..1\n"; require BstProbe; print BstProbe::answer() == 42 ? "ok 1\n" : "not ok 1\n"; EOF # configure: Build.PL must produce a Build script. cpan_build's own # `[ -e Build ] || bbfatal` says the same thing about the build host; this says # it about the target. bpl=$(cd "$WD/dist" && perl Build.PL 2>&1); bplrc=$? check_eq libmodule-build-perl/dist-buildpl-rc 0 "$bplrc" check libmodule-build-perl/dist-build-script test -f "$WD/dist/Build" if [ -f "$WD/dist/Build" ]; then # build: blib/ populated from lib/. This is the step that exercises # File::Copy, File::Path and the manifest logic on target. bo=$(cd "$WD/dist" && perl Build 2>&1); borc=$? check_eq libmodule-build-perl/dist-build-rc 0 "$borc" check libmodule-build-perl/dist-blib-populated test -f "$WD/dist/blib/lib/BstProbe.pm" # test: Module::Build drives the TAP harness itself, in a subprocess. to=$(cd "$WD/dist" && perl Build test 2>&1); torc=$? check_eq libmodule-build-perl/dist-test-rc 0 "$torc" case "$to" in *"Result: PASS"*|*"All tests successful"*) bst_ok libmodule-build-perl/dist-test-verdict ;; *) bst_notok libmodule-build-perl/dist-test-verdict "$(_bst_1line "$to")" ;; esac # install into a throwaway destdir: the installer half, which is what # t/install.t and t/destinations.t cover in section 4. io=$(cd "$WD/dist" && perl Build install --destdir "$WD/root" 2>&1); iorc=$? check_eq libmodule-build-perl/dist-install-rc 0 "$iorc" check libmodule-build-perl/dist-installed \ sh -c 'find "$1/root" -name BstProbe.pm | grep -q .' _ "$WD" else bst_notok libmodule-build-perl/dist-build-rc "not run: Build.PL produced no Build script -- $(_bst_1line "$bpl")" bst_notok libmodule-build-perl/dist-blib-populated "not run: no Build script" bst_notok libmodule-build-perl/dist-test-rc "not run: no Build script" bst_notok libmodule-build-perl/dist-test-verdict "not run: no Build script" bst_notok libmodule-build-perl/dist-install-rc "not run: no Build script" bst_notok libmodule-build-perl/dist-installed "not run: no Build script" fi # ------------------------- 4. THE CRITERION: prove over t/ ------------------- # Run from a WRITABLE COPY, not in place: the deployed rootfs is read-only at # browser runtime, and these tests write into their own tree (Build.PL # regenerates _build/, several cases chdir into t/ and create files there). # Copying is not optional here the way it was for the two-file ports before it. # # @INC is the recipe's own: run-ptest drives `perl -I inc -I blib -Mblib`, so the # equivalent for prove is `-b` (blib/lib + blib/arch) plus `-I inc`. Driving prove # WITHOUT them is not a stricter test, it is a broken one -- every file dies in # blib_load before its plan and the run reports 52 files / 0 tests. Measured on # the x86-64 control: bare `prove -r t/` gives Result FAIL / Tests=19; with # -b -I inc it gives Result PASS / Tests=1173. # # WHY SIX FILES AND NOT ALL FIFTY-TWO, which is what the ticket's criterion names. # The whole suite was run on this target, once, in full, and it costs 3041 s -- # 3036 s of prove against 26 s for the identical tree on x86-64, a factor of 117. # That is not a defect of this port and is not reported as one: it is the # platform's documented compute factor (linux-in-the-browser#117 measures tcl at # 46-68x native) multiplied by a suite that spawns a fresh perl per generated # distribution. It does mean the full run cannot be the standing in-harness # guard: the runner's per-suite bound may not exceed BST_SUITE_TIMEOUT_MAX # (3600 s), and a 3041 s criterion under a 3600 s ceiling has 1.18x headroom on a # host where the neighbouring build decides the rate. That is a bound that fires # on load rather than on breakage, which the packager README names as the exact # failure to avoid -- "a red that is neither true nor actionable". # # So the standing guard is six files, measured at 315 s together, under a # declared 1800 s bound; and the full 52-file figures live in the design doc and # docs/brintos-test-plan.md as dated out-of-harness evidence. The six are chosen # to walk the whole build system rather than to be cheap: basic.t (the # create/build/test/install cycle), runthrough.t (a distribution end to end), # notes.t (build-state persistence across Build invocations), metadata.t (META # generation), properties/dist_suffix.t (property handling) and # use_tap_harness.t (the TAP harness integration). None consults a compiler, so # none of them can go red for the reason section 3's environment already # explains. WD2=$WD/pt cp -r "$PT" "$WD2" 2>/dev/null check libmodule-build-perl/suite-staged test -f "$WD2/t/basic.t" SUBSET="t/basic.t t/runthrough.t t/notes.t t/metadata.t t/properties/dist_suffix.t t/use_tap_harness.t" out=$(cd "$WD2" && prove -b -I inc $SUBSET 2>&1); rc=$? # THE PORT'S OWN RESULT, and it is green. `Failed:` counts ASSERTIONS that # returned false, which is the half of prove's verdict Module::Build is # responsible for -- as distinct from the exit status below, which it is not. # Asserted as "no line reports a non-zero Failed", so a new red anywhere in the # subset is caught by name whatever else the run does. # # Phrased against the ABSENCE of a failure rather than the presence of prove's # `All N subtests passed`, and the difference matters: prove prints that line # only in the Test Summary Report, which it emits only for files it calls # dubious. Counting it would produce 6 on this target and 0 on a healthy host -- # a case that is green because the platform is broken and reddens the day it is # fixed, which is the exact inversion this file's section-2 comment argues # against. The form below reads 0 on both. nfailed=$(printf '%s\n' "$out" | grep -cE 'Failed: [1-9]') check_eq libmodule-build-perl/prove-no-failed-assertions 0 "$nfailed" # The file count is pinned EXACTLY: it is a property of SUBSET above, so a # harness that silently ran fewer files is the failure a green verdict cannot # show. files=$(printf '%s\n' "$out" | sed -n 's/^Files=\([0-9]*\),.*/\1/p' | tail -1) check_eq libmodule-build-perl/prove-files 6 "$files" # The assertion count is bounded from BELOW, not pinned. 123 is what these six # files ran on this target on 2026-08-22 (58+29+11+14+2+9), and the same six on # the x86-64 control run the same 123 -- none of them skips anything here. It is # bounded rather than pinned because a legitimate platform skip inside one of # them would move the total without meaning anything is wrong, while a total that # COLLAPSES means files stopped running. Set at half. tests=$(printf '%s\n' "$out" | sed -n 's/^Files=[0-9]*, Tests=\([0-9]*\),.*/\1/p' | tail -1) case "$tests" in ''|*[!0-9]*) bst_notok libmodule-build-perl/prove-tests "no Tests= total in prove's summary -- $(_bst_1line "$out")" ;; *) if [ "$tests" -ge 62 ]; then bst_ok libmodule-build-perl/prove-tests else bst_notok libmodule-build-perl/prove-tests "Tests=$tests, want >= 62 (this target ran 123 on 2026-08-22)"; fi ;; esac # ---- the two cases that are RED today, and self-flipping ---- # `Result: PASS` and rc 0 are the other half of the ticket's criterion -- # "reporting zero failures" as prove's own whole-run verdict. They cannot hold on # this arm today, and the cause is NOT Module::Build: every one of the 52 files # passes its assertions and then perl dies in global destruction with # # panic: magic_killbackrefs (flags=87a). # # which is linux-in-the-browser#93 -- a backref/free desync in which a referrer # still linked in a stash's backref list has already been freed. The issue was # filed against `warnings.pm`'s multi-sub delete and carries a perl-side # workaround for that one site (wasm32-hwjs-no-warnings-delete.patch, in this # layer's perl bbappend); its own text predicts the rest of this -- "the # underlying backref/free desync can bite any program that frees several # backref-linked CVs/GVs together". This suite is that prediction measured. Two # distinct sites were captured on 2026-08-22: t/xs.t reaches it through # `Test2/Hub.pm line 395` after `1..0 # SKIP No compiler found`, and # t/properties/dist_suffix.t reaches it at plain interpreter teardown with no # Test2 frame at all, having printed `ok 1` and `ok 2` first. # # Both cases are written in the direction that is TRUE ON A NORMAL PERL, so they # are red here and go green by themselves the day #93 is fixed. Nothing has to be # edited for that to happen, which is the point: a suite asserting "exits 2, as # expected" would go on passing after the platform fixed the thing it exists to # report. # The red's DETAIL carries prove's own summary lines, not the head of its output. # This image installs no locales, so every perl subprocess opens with four lines # of `perl: warning: Setting locale failed` -- harmless (perl falls back to C) # but enough to fill _bst_1line's collapsed detail completely, leaving the one # field a later reader attributes by saying nothing about the run. Extracting # `Files=`/`Result:` is both shorter and the part that matters. verdict=$(printf '%s\n' "$out" | grep -E '^(Files=|Result:)' | tr '\n' ' ') [ -n "$verdict" ] || verdict=$(_bst_1line "$out") case "$out" in *"Result: PASS"*) bst_ok libmodule-build-perl/prove-result ;; *) bst_notok libmodule-build-perl/prove-result \ "rc=$rc $verdict -- linux-in-the-browser#93: every file passes its assertions and perl then panics in magic_killbackrefs at teardown" ;; esac check_eq libmodule-build-perl/prove-rc 0 "$rc" rm -rf "$WD"