From 5111284981ec6693c41ea2e5390405314fd1ecfc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 09:00:32 +0000 Subject: [PATCH] php: the GNU basename, and json_parser's source is .tab.c Two stops from the first mk install, both cheap. The rule said `mkdir -p `{basename -d $target}', which is the Plan 9 idiom for a dirname -- and inside apexp-sh the union puts APExp's own /bin first, so basename is GNU coreutils' and has no -d. mk then reported the failure of the compile rather than of the mkdir, naming the victim again. The rule runs no external command now: the directory list is known statically, mkdir -p is idempotent, and one exec per compile is nothing beside 513 runs of pcc. Recorded as a build-system rule, since a command name inside apexp-sh is whatever the union resolves it to and Plan 9's own flags are the ones most likely to be missing. `no recipe to make ext/json/json_parser.6' had a better answer than expected: ext/json's parser is bison-generated and the generated file SHIPS IN THE TARBALL as json_parser.tab.c, so the object upstream calls json_parser.lo is built from a source whose name does not match it. One explicit rule covers it. The useful half is what the check found rather than what it fixed. All 513 objects were resolved against the vendored tree and this was the only name that does not, so no bison and no re2c are needed anywhere in this port: json_scanner.c and php_json_scanner_defs.h are shipped too, alongside the four Zend parsers and scanners and the 4.2MB zend_vm_execute.h. A question that could have cost two more build tools cost one grep. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs --- CLAUDE.md | 10 +++++++ docs/notes/php.md | 26 ++++++++++++++++++ sys/src/ape/cmd/php/mkfile | 56 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6cd359844..38004bf0e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1471,6 +1471,16 @@ in the topic file. it is repeated here. *Check which `6l` answers before reading anything into it*, and see the bind rule immediately below for how a window quietly acquires the host's. +- **A command name inside `apexp-sh` is whatever the UNION resolves it + to, and Plan 9's own flags are the ones most likely to be missing.** + `cmd/php/mkfile` used `` `{basename -d $target} `` -- the Plan 9 + idiom for a dirname -- and got `basename: invalid option -- d`, + because the union puts APExp's `/bin` first and that `basename` is + **GNU coreutils'**. *mk then reported the failure of the compile, + not of the `mkdir`*, so the message named the victim again. Same + family as "check which `6l` answers". A recipe that needs a Plan 9 + flag should not assume it is reachable; the php mkfile dropped the + external command entirely and uses a static directory list. - **`rm -rf` on a directory something has BOUND does not unbind it.** The bind still names the removed directory, a recreated one has a new qid, and the union component silently falls through to whatever is diff --git a/docs/notes/php.md b/docs/notes/php.md index 2af204abe..81a9402b8 100644 --- a/docs/notes/php.md +++ b/docs/notes/php.md @@ -384,6 +384,32 @@ entry aborts the whole tree's `mk`, and a port that has never been built has not been measured in that position. `cd sys/src/ape/cmd/php && mk` is how to try it. +### What the first `mk install` said, and both answers were cheap + +**`mkfile:612: syntax error`** -- the `OFILES` list ended one line +early, leaving a tab-indented entry orphaned. `mkcont-sweep` reported +0 over it, and is widened now; see `CLAUDE.md`. + +**`basename: invalid option -- d`** -- the rule used Plan 9's idiom +for a dirname, and inside `apexp-sh` the union puts APExp's own +`/bin` first, so `basename` is **GNU coreutils'**. *A command name +here is whatever the union resolves it to.* The rule uses a static +directory list now and runs no external command at all. + +***AND `no recipe to make 'ext/json/json_parser.6'` HAD A BETTER +ANSWER THAN EXPECTED.*** `ext/json`'s parser is bison-generated and +**the generated file ships in the tarball** as `json_parser.tab.c`, +so the object upstream calls `json_parser.lo` is built from a source +whose name does not match it. One explicit rule covers it. + +The useful half is what the check found rather than what it fixed: +**all 513 objects were resolved against the vendored tree and this was +the only name that does not** -- so **no bison and no re2c are needed +anywhere in this port**. `json_scanner.c` and +`php_json_scanner_defs.h` are shipped too, alongside the four Zend +parsers and scanners and the 4.2MB `zend_vm_execute.h`. *A question +that could have cost two more build tools cost one grep.* + ## What the config.h decided, and why by hand The long reasoning is in the file itself; what matters as a summary: diff --git a/sys/src/ape/cmd/php/mkfile b/sys/src/ape/cmd/php/mkfile index 02073c71e..867ee3aa4 100644 --- a/sys/src/ape/cmd/php/mkfile +++ b/sys/src/ape/cmd/php/mkfile @@ -654,6 +654,24 @@ LD=pcc DEFS=-D__x86_64__ -DHAVE_TIMELIB_CONFIG_H=1 -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1\ -DSHADOW_STACK_SYSCALL=0 -D_POSIX_SOURCE -D_BSD_EXTENSION -D_PLAN9_SOURCE +OBJDIRS=\ + TSRM Zend Zend/Optimizer ext/date ext/date/lib ext/hash\ + ext/hash/murmur ext/hash/sha3/generic64lc ext/json ext/lexbor\ + ext/lexbor/lexbor/core ext/lexbor/lexbor/css\ + ext/lexbor/lexbor/css/at_rule ext/lexbor/lexbor/css/property\ + ext/lexbor/lexbor/css/selectors ext/lexbor/lexbor/css/syntax\ + ext/lexbor/lexbor/css/syntax/tokenizer ext/lexbor/lexbor/dom\ + ext/lexbor/lexbor/dom/interfaces ext/lexbor/lexbor/encoding\ + ext/lexbor/lexbor/html ext/lexbor/lexbor/html/interfaces\ + ext/lexbor/lexbor/html/tokenizer ext/lexbor/lexbor/html/tree\ + ext/lexbor/lexbor/html/tree/insertion_mode ext/lexbor/lexbor/ns\ + ext/lexbor/lexbor/ports/posix/lexbor/core\ + ext/lexbor/lexbor/punycode ext/lexbor/lexbor/tag\ + ext/lexbor/lexbor/unicode ext/lexbor/lexbor/url ext/opcache\ + ext/pcre ext/pcre/pcre2lib ext/random ext/reflection ext/spl\ + ext/standard ext/standard/libavifinfo ext/uri\ + ext/uri/uriparser/src main main/streams sapi/cli + INCS=-I. -I$PHPSRC -I$PHPSRC/main -I$PHPSRC/Zend -I$PHPSRC/TSRM\ -I$PHPSRC/ext/date/lib -I$PHPSRC/ext/lexbor\ -I$PHPSRC/ext/uri/uriparser/include @@ -661,14 +679,48 @@ INCS=-I. -I$PHPSRC -I$PHPSRC/main -I$PHPSRC/Zend -I$PHPSRC/TSRM\ CFLAGS=-c $DEFS $INCS # The objects live beside their sources in the build directory, so the -# directory has to exist before `pcc -o' can write into it. +# directories have to exist before `pcc -o' can write into them. +# +# ***THE FIRST VERSION SAID ``mkdir -p `{basename -d $target}'' AND +# THAT IS A PLAN 9 IDIOM THIS SHELL DOES NOT HAVE.*** Inside +# `apexp-sh' the union mount puts APExp's own `/bin' first, so +# `basename' is **GNU coreutils'**, which has no `-d': +# +# basename: invalid option -- d +# mkdir: missing operand +# +# -- and mk then reported the failure of the compile, not of the +# `mkdir'. *The same shape as "check which `6l' answers"*: a command +# name inside `apexp-sh' is whatever the union resolves it to, and +# Plan 9's own flags are the ones most likely to be missing. +# +# So no external command at all: the directory list is known +# statically, `mkdir -p' is idempotent, and one exec per compile is +# nothing beside 513 runs of pcc. %.$O: $PHPSRC/%.c - mkdir -p `{basename -d $target} + mkdir -p $OBJDIRS $CC $CFLAGS $prereq -o $target %.$O: %.c $CC $CFLAGS $stem.c +# ***ONE OBJECT IS NOT NAMED AFTER ITS SOURCE.*** `ext/json's parser +# is bison-generated, and the generated file SHIPS IN THE TARBALL as +# `json_parser.tab.c' -- so the object upstream calls +# `ext/json/json_parser.lo' is built from `json_parser.tab.c', not +# from a `json_parser.c' that does not exist. mk said so plainly: +# `no recipe to make 'ext/json/json_parser.6''. +# +# **No bison and no re2c are needed anywhere in this port**, which was +# worth checking rather than assuming: `json_scanner.c' and +# `php_json_scanner_defs.h' are in the tarball too, as are the four +# Zend parsers and scanners and the 4.2MB `zend_vm_execute.h'. A +# sweep of all 513 objects against the vendored tree found this as the +# ONLY name that does not resolve. +ext/json/json_parser.$O: $PHPSRC/ext/json/json_parser.tab.c + mkdir -p $OBJDIRS + $CC $CFLAGS -I$PHPSRC/ext/json $prereq -o $target + # mkone's `clean' is `rm -f *.[$OS] ...', which cannot see an object in # a subdirectory -- and 512 of the 513 here are in one. Overriding it # means repeating what it does, the same as `cmd/bash/mkfile'.