Skip to content

Claude/upgrade ape c library mm z gd - #736

Merged
staalmannen merged 2 commits into
mainfrom
claude/upgrade-ape-c-library-mmZGd
Nov 6, 2026
Merged

staalmannen merged 2 commits into
mainfrom
claude/upgrade-ape-c-library-mmZGd

Conversation

@staalmannen

Copy link
Copy Markdown
Owner

No description provided.

…nnot be included

`sys/include/ape/png.h:331' includes "pnglibconf.h" and `:336'
includes "pngconf.h", both by QUOTED name, so they are looked for
beside png.h and nowhere else -- and neither was installed.  Every
`#include <png.h>' in the tree answered

	png.h:331 Could not find include file "pnglibconf.h"

Nothing noticed because nothing in the tree included <png.h>: libpng
has been built and never consumed, so the gap was invisible until
php's bundled libgd reached for it.  Found by compiling ext/gd on the
host mirror -- 39 of 40 files clean and the one failure was ours, not
GD's.

Both are copied VERBATIM from sys/src/external/libpng and must stay
so: lib/png/mkfile compiles the library with `-I$PNGSOURCE', so the
vendored copies are the ones libpng itself was built against, and a
hand-edited copy here would be a consumer disagreeing with the
library about PNG_IMAGE_* sizes and which features are compiled in.
They carry no `#pragma lib' of their own on purpose -- png.h is the
header every consumer reaches, and it already has one.

png.h's bare pragma gets the explanation the other two arrangements
carry (<zlib.h>, <libxml/xmlversion.h>), including the re-sync hazard
and the reason the two new files must not be edited.

With this in, all 40 of php's bundled-libgd translation units compile
under the host kencc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs
sys/src/external/libgd (2.3.3, vendored whole) and
sys/src/ape/lib/gd building libgd.a, with the eleven public headers
installed into sys/include/ape.  Added to lib/mkfile as _IMAGE_LIBS,
which has to follow png and z in the sequential loop -- the same
reason _TLS_LIBS and _ZIP_LIBS exist.

It is here as an exercise as much as a capability: 47 files of
integer-heavy pixel arithmetic are good ground for a compiler whose
`long' is 32 bits and whose `sizeof' is 32-bit, and gd needs no
display, no X11 and no platform integration at all -- pixel buffers
in, file formats out.

ALL 47 COMPILE CLEAN UNDER kencc, first attempt, so every bug this
exercise can still find is a RUNTIME bug.  That is what gd-test.c is
for, and why gd-xcheck.sh exists beside it: there is no reference
implementation of libgd, so the cross-check builds the SAME vendored
sources with gcc and runs the SAME test file, making it an A/B of two
compilers over one source.  A failure on the VM that passes on the
host is a kencc or libap bug; a failure on both is the test's bug.

0 failures under gcc; the control (gdImageGetPixel off by one column)
fires with 4 across three sections.

Every assertion is exactly checkable without a golden image: rotate 90
four times is the identity whichever way gd turns it, a PNG round trip
must return its own pixels, scaling a FLAT image must leave it flat
whatever the filter does.

Both of the test's first two drafts were the test's own bug and gcc
found both:

  * gdImageRotate90 is INTERNAL (gd_intern.h, not installed), so the
    call was implicit-int, the pointer truncated, and the test
    segfaulted -- this tree's undeclared-function invariant, inside a
    test written to hunt that class.  The public call is
    gdImageRotateInterpolated, and the harness now builds the test
    with -Werror=implicit-function-declaration.

  * the palette section asserted that <=256 distinct colours survive
    conversion exactly; 1024 of 1024 pixels "changed" while the
    palette held exactly the right 128 entries, because gd_topal.c
    takes colour-cube CENTROIDS -- (0,0,0) comes back (4,2,4).  What
    is exact is that the mapping is a function of colour, which is
    what it asserts now, with the drift measured and bounded.

config.h is hand-answered, with the reason per line.  No FreeType, so
no scalable text -- gdft.c returns "libgd was not built with FreeType
font support" rather than failing silently -- and no JPEG, WebP,
AVIF, HEIF, TIFF or XPM library in the tree.  PNG, GIF, BMP, WBMP,
XBM, TGA and gd/gd2 all work, as does every drawing, cropping,
rotation, scaling, interpolation, palette and filter path, which is
the arithmetic worth exercising.

HAVE_PTHREAD is answered yes and is UNREACHABLE in this build: the
three gdMutex call sites are all in gdft.c, whose body is inside
HAVE_LIBFREETYPE.  Recorded so nobody reads a working libgd as
evidence that libap's mutexes work.

<gd.h> carries three pragmas, not one: #pragma lib records an archive
in the object being compiled and is not transitive through an
archive, so without libpng.a and libz.a beside libgd.a a program
using only gdImagePng would compile and fail at the link.  Measured:
an object including <gd.h> records libgd.a, libpng.a and libz.a; one
including only <zlib.h> records libz.a; one including neither records
neither.

The object list is src/Makefile.am's libgd_la_SOURCES, read rather
than recalled -- `ls *.c' gives 70, and the other 23 are upstream's
own demo programs with their own main.  Both the mkfile and the
cross-check derive it the same way and the harness gates if the count
stops being 47.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAwvvTwDg2yknFkmZ3qzs
@staalmannen
staalmannen merged commit 4c1bada into main Nov 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants