diff --git a/CLAUDE.md b/CLAUDE.md index 02bea6d71..6cd359844 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -397,7 +397,16 @@ that the instrument notices.* nothing declares), `apehdr-sweep.py` (every `sys/include/ape` header standalone, plus a TOGETHER case for collisions only a combination can show), `rceq-sweep.py` (**gates**: a bare `=` in an mkfile recipe cannot -parse), `mkcont-sweep.py` (**gates**: a comment ending a continuation), +parse), `mkcont-sweep.py` (**gates**: a logical line mk cannot parse. TWO +shapes, and *the second was added after the first reported 0 over +it*: a comment ending a continuation, and an INDENTED line that is +not a recipe — `cmd/php/mkfile` ended its `OFILES` at an ordinary +entry and left a tab-indented one orphaned five comment lines later. +**Recipes are the whole difficulty**, since every recipe body is +indented and has no `:` or `=`, so the discriminator is what the last +non-indented logical line was; `--control` carries one of each shape +*and a real recipe*, because a control without one would pass for a +version that reports every mkfile), `ishadow-sweep.py` (does NOT gate — most hits are deliberate), `hosthelper-sweep.py` (**does NOT gate, on purpose**: which mkfile recipes run a binary the same mkfile builds, and whether it was built diff --git a/docs/notes/php.md b/docs/notes/php.md index 06f7527f6..2af204abe 100644 --- a/docs/notes/php.md +++ b/docs/notes/php.md @@ -295,11 +295,46 @@ Which maps to: `mbstring` (oniguruma), `openssl` (libressl), `curl`, `zlib`, `bz2`, `libxml`/`dom`/`simplexml`/`xml`/`xmlreader`/`xmlwriter`, `sqlite3` + `pdo_sqlite`, `zip`, `readline`, `gd` (png only), `intl`. +***THESE ARE A DIFFERENT RISK CLASS FROM ANYTHING ON PECL, AND THAT IS +THE DISTINCTION TO KEEP.*** Every one of them SHIPS WITH php 8.5, is +tested by upstream against this exact version, and is turned on with a +`--with-X` against a library this tree already has. A PECL package is +a separate source tree with its own maintenance state and its own +target php version. *The first is a flag; the second is a port.* + **`ext/pcre` bundles its own pcre2 and that is what the 29 objects are.** `--with-external-pcre` would use `sys/src/ape/lib/pcre2` instead; the bundled copy is the better starting point, because it is the version php was tested against and it removes a link dependency from the first build. +### php-lua and php-tk: a different risk class, and the answer is no for now + +Asked directly, so recorded rather than left to be re-asked. **Neither +is vendored and neither ships with php** -- `sys/src/external/lua` and +`external/tk` are the LIBRARIES, and `php/ext/` has no `lua` or `tk`. +Both would be PECL packages to bring in, and PECL extensions build as +shared modules by default, which Plan 9 cannot load at all; they would +have to be linked statically and listed in the mkfile and +`internal_functions_cli.c` by hand. *That part is mechanical and the +port directory is already arranged for it.* The problem is upstream. + +**`tk` is PECL 0.1.1 beta, dated 2004-11-14, and its own page says the +package is unmaintained and invites someone to take it over.** That is +the PHP 4 extension API: no `zend_string`, the PHP 7 zval rework, the +arginfo requirements and the TSRMLS removal all postdate it. This is +not an extension to enable, it is a rewrite -- *bigger than most of +what this tree has ported* -- and a GUI binding additionally needs an +event loop married to php's request lifecycle, on top of our own +`external/tk/plan9` backend. No. + +**`lua` is less clear and still not first.** The PECL `lua` package +looks PHP 7-era; **LuaSandbox** is the one actually packaged for PHP +8.0 through 8.5. *Which also means it is a different extension with a +different API*, and it is worth checking before any work whether it +wants Lua **5.1** -- it comes from MediaWiki's Scribunto, which +standardised there -- because this tree ships **5.5.0**. A version +mismatch would be its own port. + **No extension should be enabled in the first build.** The order that has worked in this tree is: get the thing to compile and print its own version, then add one library at a time. diff --git a/sys/lib/tests/mkcont-sweep.py b/sys/lib/tests/mkcont-sweep.py index 641acedd6..ba6357b8e 100644 --- a/sys/lib/tests/mkcont-sweep.py +++ b/sys/lib/tests/mkcont-sweep.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 """ -mkcont-sweep -- which mkfile has a COMMENT that silently ends a -continued assignment, leaving the rest of the list as a bare word? +mkcont-sweep -- which mkfile has a logical line mk cannot parse, +because a continuation ended where nobody meant it to? + +Two shapes, and the second was added after the first missed it. python3 sys/lib/tests/mkcont-sweep.py @@ -41,6 +43,28 @@ because mk accepts it. It is still wrong -- the dropped entries are silently not built -- but it has no symptom this can see, so this is a LOWER BOUND like every other sweep here. + +------------------------------------------------------------------ +SHAPE TWO: AN ORPHAN INDENTED LINE, AND THIS SWEEP REPORTED 0 OVER IT. + +`cmd/php/mkfile' ended its `OFILES' at the last generated entry and +then had a blank line, five comment lines and a tab-indented +`internal_functions_cli.$O' that was meant to be part of the list. mk +answered `mkfile:612: syntax error; expected one of :<=' -- and **the +check above never fired, because the continuation was ended by an +ordinary entry rather than by a comment.** + +The generalisation is the one mk itself uses, and it covers both: a +logical line that starts with a bare word -- no `:', `=' or leading +`<' -- is a syntax error WHEREVER it came from. An INDENTED one is a +syntax error unless it is a recipe, which means the rule above it had +a `:'. So this now also reports an indented logical line that follows +an ASSIGNMENT rather than a rule. + +*Recipes are the whole difficulty*: every recipe body line is indented +and has no `:' or `=', so a naive version of this check reports the +entire tree. The discriminator is what the last non-indented logical +line was. """ import os @@ -53,6 +77,37 @@ "report 0 findings from the wrong tree" % ROOT) +def logical(lines): + """yield (start_line_index, joined_text, first_raw) per logical line""" + i = 0 + while i < len(lines): + start = i + parts = [lines[i].rstrip()] + while parts[-1].endswith("\\") and i + 1 < len(lines): + i += 1 + parts.append(lines[i].rstrip()) + yield start, "\n".join(parts), lines[start] + i += 1 + + +def orphans(path): + """shape two: an indented logical line that is not a recipe""" + lines = open(path, encoding="latin1").read().split("\n") + out = [] + in_recipe = False + for start, text, raw in logical(lines): + bare = raw.strip() + if not bare or bare.startswith("#"): + continue # blank and comment end nothing, deliberately + if raw[:1] in (" ", "\t"): + if not in_recipe: + out.append((start + 1, bare)) + continue + # a non-indented logical line: does a recipe follow it? + in_recipe = ":" in text.split("\n")[0] + return out + + def check(path): lines = open(path, encoding="latin1").read().split("\n") out = [] @@ -73,7 +128,59 @@ def check(path): return out +CONTROL_ONE = """\ +OFILES=\\ +\ta.$O\\ +# a comment that forgets its backslash +\tb.$O\\ +\tc.$O +""" + +CONTROL_TWO = """\ +OFILES=\\ +\ta.$O\\ +\tb.$O + +# a comment block, and then an entry meant to be part of the list +\tc.$O + +install:V: +\techo this recipe must NOT be reported +""" + + +def control(): + """Both shapes, because a sweep that only proves it can say one + thing has not been shown to say the other. CONTROL_TWO carries a + real recipe as well: the discriminator between an orphan and a + recipe body is the whole difficulty, so a control that omits the + recipe would pass for a version that reports every mkfile.""" + import tempfile + ok = True + with tempfile.TemporaryDirectory() as d: + for name, body, fn, want in ( + ("one", CONTROL_ONE, lambda p: check(p), 1), + ("two", CONTROL_TWO, lambda p: orphans(p), 1)): + p = os.path.join(d, "mkfile") + open(p, "w").write(body) + got = len(fn(p)) + print("control %s: %d finding(s), want %d -- %s" + % (name, got, want, "ok" if got == want else "FAILED")) + if got != want: + ok = False + # and the recipe in CONTROL_TWO must not be among them + p = os.path.join(d, "mkfile") + open(p, "w").write(CONTROL_TWO) + for (ln, text) in orphans(p): + if "echo" in text: + print("control two: REPORTED A RECIPE BODY -- FAILED") + ok = False + return 0 if ok else 1 + + def main(): + if "--control" in sys.argv: + return control() findings = 0 for base in ("sys/src/ape", "sys/src/cmd", "sys/src/cmd2"): top = os.path.join(ROOT, base) @@ -90,6 +197,11 @@ def main(): print(" %s" % text.strip()[:68]) print(" :%d is then a bare word mk cannot parse:" % nl) print(" %s" % nxt.strip()[:68]) + for (ln, text) in orphans(p): + findings += 1 + print("%s:%d indented line that is not a recipe:" % + (os.path.relpath(p, ROOT), ln)) + print(" %s" % text[:68]) print() print("%d broken continuation(s)." % findings) if findings: diff --git a/sys/src/ape/cmd/php/mkfile b/sys/src/ape/cmd/php/mkfile index 2215169da..02073c71e 100644 --- a/sys/src/ape/cmd/php/mkfile +++ b/sys/src/ape/cmd/php/mkfile @@ -601,13 +601,16 @@ OFILES=\ sapi/cli/php_cli_process_title.$O\ sapi/cli/php_cli_server.$O\ sapi/cli/php_http_parser.$O\ - sapi/cli/ps_title.$O - -# `internal_functions_cli.c' is generated by configure -- it is the -# table of built-in extension entries -- and ours is beside this -# mkfile rather than in `external/php', so the vendored tree stays -# untouched. Same for `php_config.h', `zend_config.h', -# `timelib_config.h' and `build-defs.h'. + sapi/cli/ps_title.$O\ +# `internal_functions_cli.c' is generated by configure -- it is the\ +# table of built-in extension entries -- and ours is beside this\ +# mkfile rather than in `external/php', so the vendored tree stays\ +# untouched. Same for `php_config.h', `zend_config.h',\ +# `timelib_config.h' and `build-defs.h'. ***IT IS ALSO THE LAST\ +# ENTRY, AND THE ONLY LINE IN THIS LIST WITHOUT A TRAILING\ +# BACKSLASH*** -- the first version ended the list one line above and\ +# left this tab-indented line orphaned, which mk answered with\ +# `mkfile:612: syntax error; expected one of :<='.\ internal_functions_cli.$O # ***WITHOUT THIS LINE, EDITING `php_config.h' REBUILDS NOTHING.***