From acb169e2c4359f45c8372f68a31f14563770aedb Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 7 Aug 2026 20:11:55 +0200 Subject: [PATCH] feat: ports - zlib, libwapcaplet, libparserutils Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018Ae69wJS7sueQMUwNxV3nX --- .gitignore | 2 + .gitmodules | 12 +++ Makefile | 3 + dev-libs/libparserutils/Makefile | 35 ++++++++ dev-libs/libparserutils/upstream | 1 + dev-libs/libwapcaplet/Makefile | 28 ++++++ dev-libs/libwapcaplet/upstream | 1 + dev-libs/zlib/Makefile | 51 +++++++++++ dev-libs/zlib/upstream | 1 + dev-util/netsurf-buildsystem/upstream | 1 + mk/port.mk | 117 +++++++++++++++++++++++++- test/Makefile | 49 +++++++++++ test/porttest.c | 61 ++++++++++++++ 13 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 dev-libs/libparserutils/Makefile create mode 160000 dev-libs/libparserutils/upstream create mode 100644 dev-libs/libwapcaplet/Makefile create mode 160000 dev-libs/libwapcaplet/upstream create mode 100644 dev-libs/zlib/Makefile create mode 160000 dev-libs/zlib/upstream create mode 160000 dev-util/netsurf-buildsystem/upstream create mode 100644 test/Makefile create mode 100644 test/porttest.c diff --git a/.gitignore b/.gitignore index d256d26..7f500f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ out/ +sysroot/ obj/ +build-montauk/ *.o *.d *.a diff --git a/.gitmodules b/.gitmodules index c20dac0..c8851f7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,15 @@ [submodule "games-fps/doom/upstream"] path = games-fps/doom/upstream url = https://github.com/ozkl/doomgeneric.git +[submodule "dev-libs/zlib/upstream"] + path = dev-libs/zlib/upstream + url = https://github.com/madler/zlib.git +[submodule "dev-util/netsurf-buildsystem/upstream"] + path = dev-util/netsurf-buildsystem/upstream + url = https://github.com/netsurf-browser/buildsystem.git +[submodule "dev-libs/libwapcaplet/upstream"] + path = dev-libs/libwapcaplet/upstream + url = https://github.com/netsurf-browser/libwapcaplet.git +[submodule "dev-libs/libparserutils/upstream"] + path = dev-libs/libparserutils/upstream + url = https://github.com/netsurf-browser/libparserutils.git diff --git a/Makefile b/Makefile index 49b5a9f..c94e046 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ export OUTDIR # come before the applications that link them. Categories follow Gentoo # naming (dev-libs, media-libs, net-libs, www-client, games-fps, ...). PORTS := \ + dev-libs/zlib \ + dev-libs/libwapcaplet \ + dev-libs/libparserutils \ games-fps/doom # Bundle directories are keyed by port name, not category path. diff --git a/dev-libs/libparserutils/Makefile b/dev-libs/libparserutils/Makefile new file mode 100644 index 0000000..510f146 --- /dev/null +++ b/dev-libs/libparserutils/Makefile @@ -0,0 +1,35 @@ +# libparserutils for MontaukOS +# +# Upstream: https://github.com/netsurf-browser/libparserutils (official mirror +# of git.netsurf-browser.org; submodule at upstream/, pinned, no local patches). +# +# Input streams, buffers and charset conversion -- the base every other +# NetSurf parsing library sits on (libhubbub, libcss, libdom). +# +# Built WITHOUT_ICONV_FILTER, an upstream-supported switch: src/input/filter.c +# otherwise wants , which MontaukOS has no port of. That leaves the +# library's own charset codecs (ascii, 8859, ext8, utf8, utf16), which covers +# UTF-8 and the Latin encodings. Exotic ones (Shift-JIS, Big5, ...) are +# unsupported until something ports iconv. + +UPSTREAM := upstream + +PORT_NAME := libparserutils +PORT_LIB := 1 +PORT_ARCHIVE := libparserutils.a +PORT_HEADER_ROOT := $(UPSTREAM)/include +PORT_HEADERS := $(shell find $(UPSTREAM)/include -name '*.h' 2>/dev/null) + +include ../../mk/port.mk + +NS_CFLAGS_EXTRA := -DWITHOUT_ICONV_FILTER + +.PHONY: all clean +all: install-lib + +$(PORT_ARCHIVE): $(shell find $(UPSTREAM)/src $(UPSTREAM)/include -name '*.c' -o -name '*.h' 2>/dev/null) + $(call ns_build,$(UPSTREAM),parserutils) + +clean: clean-lib + @$(call ns_clean,$(UPSTREAM)) + rm -rf $(UPSTREAM)/$(NS_BUILDDIR) diff --git a/dev-libs/libparserutils/upstream b/dev-libs/libparserutils/upstream new file mode 160000 index 0000000..6b0cbf0 --- /dev/null +++ b/dev-libs/libparserutils/upstream @@ -0,0 +1 @@ +Subproject commit 6b0cbf086ca8eb8fe74b69f0c9ecf274eb2397ca diff --git a/dev-libs/libwapcaplet/Makefile b/dev-libs/libwapcaplet/Makefile new file mode 100644 index 0000000..510202f --- /dev/null +++ b/dev-libs/libwapcaplet/Makefile @@ -0,0 +1,28 @@ +# libwapcaplet for MontaukOS +# +# Upstream: https://github.com/netsurf-browser/libwapcaplet (official mirror of +# git.netsurf-browser.org; submodule at upstream/, pinned, no local patches). +# +# Interned string handling. The smallest NetSurf library and the first one +# ported, chosen to prove their buildsystem cross-compiles for x86_64-montauk. +# It does, unpatched -- see the NetSurf section of mk/port.mk. + +UPSTREAM := upstream + +PORT_NAME := libwapcaplet +PORT_LIB := 1 +PORT_ARCHIVE := libwapcaplet.a +PORT_HEADER_ROOT := $(UPSTREAM)/include +PORT_HEADERS := $(UPSTREAM)/include/libwapcaplet/libwapcaplet.h + +include ../../mk/port.mk + +.PHONY: all clean +all: install-lib + +$(PORT_ARCHIVE): $(wildcard $(UPSTREAM)/src/*.c $(UPSTREAM)/include/libwapcaplet/*.h) + $(call ns_build,$(UPSTREAM),wapcaplet) + +clean: clean-lib + @$(call ns_clean,$(UPSTREAM)) + rm -rf $(UPSTREAM)/$(NS_BUILDDIR) diff --git a/dev-libs/libwapcaplet/upstream b/dev-libs/libwapcaplet/upstream new file mode 160000 index 0000000..c7c128d --- /dev/null +++ b/dev-libs/libwapcaplet/upstream @@ -0,0 +1 @@ +Subproject commit c7c128d3eb3223b216c974471f82e9337fbcf4ba diff --git a/dev-libs/zlib/Makefile b/dev-libs/zlib/Makefile new file mode 100644 index 0000000..35b0982 --- /dev/null +++ b/dev-libs/zlib/Makefile @@ -0,0 +1,51 @@ +# zlib for MontaukOS +# +# Upstream: https://github.com/madler/zlib (submodule at upstream/, pinned to +# v1.3.1; no local patches). Needed by NetSurf for gzip Content-Encoding, and +# by libpng. +# +# DYNAMIC_CRC_TABLE is deliberately NOT defined: it pulls in and +# a runtime once() init, where the default path uses the checked-in static CRC +# tables in crc32.h. Deterministic and one less dependency. + +UPSTREAM := upstream + +PORT_NAME := zlib +PORT_LIB := 1 +PORT_ARCHIVE := libz.a +PORT_HEADER_ROOT := $(UPSTREAM) +PORT_HEADERS := $(UPSTREAM)/zlib.h $(UPSTREAM)/zconf.h + +include ../../mk/port.mk + +CFLAGS := \ + -std=gnu11 \ + $(PORT_CFLAGS_COMMON) \ + -Wall \ + -Wno-implicit-fallthrough \ + -DHAVE_UNISTD_H \ + -DHAVE_STDARG_H \ + -I $(UPSTREAM) \ + -isystem $(shell $(CC) -print-file-name=include) + +SRCS := \ + adler32.c compress.c crc32.c deflate.c gzclose.c gzlib.c gzread.c \ + gzwrite.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c \ + zutil.c + +OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.c=.o)) + +.PHONY: all clean +all: install-lib + +$(PORT_ARCHIVE): $(OBJS) + $(AR) rcs $@ $(OBJS) + @echo "Built: $@" + +$(OBJDIR)/%.o: $(UPSTREAM)/%.c Makefile + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ + +clean: clean-lib + +-include $(OBJS:.o=.d) diff --git a/dev-libs/zlib/upstream b/dev-libs/zlib/upstream new file mode 160000 index 0000000..51b7f2a --- /dev/null +++ b/dev-libs/zlib/upstream @@ -0,0 +1 @@ +Subproject commit 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf diff --git a/dev-util/netsurf-buildsystem/upstream b/dev-util/netsurf-buildsystem/upstream new file mode 160000 index 0000000..0005ae3 --- /dev/null +++ b/dev-util/netsurf-buildsystem/upstream @@ -0,0 +1 @@ +Subproject commit 0005ae300283ff01c2e2b05e7376b3e55dea21f7 diff --git a/mk/port.mk b/mk/port.mk index 4db8063..5ada495 100644 --- a/mk/port.mk +++ b/mk/port.mk @@ -1,7 +1,23 @@ # port.mk - Shared rules for MontaukOS ports. # -# A port builds against a MontaukOS checkout (the SDK) and produces an app -# bundle in $(OUTDIR)/apps// containing: +# There are two kinds of port. +# +# NOTE: a port must set its PORT_* variables BEFORE including this file -- +# the install rules are expanded at include time. +# +# LIBRARY PORTS set PORT_LIB := 1 and install headers and a static archive +# into the shared ports sysroot at $(PORT_SYSROOT): +# +# sysroot/include/... public headers +# sysroot/lib/lib.a static archive +# +# Dependent ports pick these up automatically: $(PORT_CFLAGS_COMMON) already +# includes -I $(PORT_SYSROOT_INC), so a port that needs zlib just adds +# $(PORT_SYSROOT_LIB)/libz.a to its link line. A library port declares its own +# prerequisites with PORT_DEPENDS (names only, for documentation and for the +# top-level build order). +# +# APPLICATION PORTS produce an app bundle in $(OUTDIR)/apps// containing: # # .elf the binary # manifest.toml launcher metadata (name, binary, icon, category) @@ -75,8 +91,16 @@ SDK_API_VERSION := $(shell sed -n 's/.*apiVersion = \([0-9]\+\);.*/\1/p' \ # ==== Output ========================================================== OUTDIR ?= $(MTKPORTS_ROOT)/out -BUNDLE_DIR := $(OUTDIR)/apps/$(PORT_NAME) OBJDIR ?= obj + +# Shared sysroot that library ports install into and every port compiles +# against. Kept separate from the MontaukOS SDK so `make clean` here never +# touches the OS tree. +PORT_SYSROOT ?= $(MTKPORTS_ROOT)/sysroot +PORT_SYSROOT_INC := $(PORT_SYSROOT)/include +PORT_SYSROOT_LIB := $(PORT_SYSROOT)/lib + +BUNDLE_DIR := $(OUTDIR)/apps/$(PORT_NAME) TARGET := $(BUNDLE_DIR)/$(PORT_NAME).elf # ==== Common flags ==================================================== @@ -93,13 +117,96 @@ PORT_CFLAGS_COMMON := \ -msse \ -msse2 \ -isystem $(SDK_LIBC_INC) \ - -I $(SDK_INC) + -I $(SDK_INC) \ + -I $(PORT_SYSROOT_INC) PORT_LDFLAGS_COMMON := \ -nostdlib \ -Wl,--gc-sections \ -T $(SDK_LINK_LD) +# ==== NetSurf library ports =========================================== +# NetSurf's libraries share one buildsystem (dev-util/netsurf-buildsystem) +# and all build the same way, so the invocation lives here rather than being +# copied into each port. +# +# It cross-compiles for x86_64-montauk unpatched: HOST is auto-detected from +# `$(CC) -dumpmachine`, and BUILD != HOST puts it in cross mode. CC/AR must be +# passed on the command line (their `origin ... default` checks skip them +# otherwise), while CFLAGS must go through the ENVIRONMENT -- their makefiles +# do `CFLAGS := -I... $(CFLAGS)`, and a command-line CFLAGS would override +# that assignment outright and drop their own include paths. + +NS_BUILDSYSTEM := $(MTKPORTS_ROOT)/dev-util/netsurf-buildsystem/upstream +NS_BUILDDIR := build-montauk + +NS_CFLAGS := \ + -ffreestanding \ + -fno-stack-protector \ + -fno-stack-check \ + -msse \ + -msse2 \ + -isystem $(SDK_LIBC_INC) \ + -I $(SDK_INC) \ + -I $(PORT_SYSROOT_INC) + +NS_MAKE_ARGS := \ + NSSHARED=$(NS_BUILDSYSTEM) \ + CC=$(CC) \ + AR=$(AR) \ + COMPONENT_TYPE=lib-static \ + BUILDDIR=$(NS_BUILDDIR) \ + WANT_TEST=no + +# MAKEFLAGS must be cleared for the sub-make. This file sets `-rR`, which +# disables built-in variables, and their buildsystem probes those with +# `ifeq ($(origin RM),default)` style tests -- under -rR the origin is +# "undefined" instead, so RM/ARFLAGS silently expand to nothing and the +# archive rule tries to *execute* the .a file (exit 127). +NS_MAKE := MAKEFLAGS= $(MAKE) + +# Build a NetSurf library in $(1) (its checkout dir) and copy the archive to +# $(PORT_ARCHIVE). Usage in a recipe: $(call ns_build,upstream,wapcaplet) +# A port may add its own flags by setting NS_CFLAGS_EXTRA (expanded at call +# time, so it can be set after this file is included). +ns_build = \ + CFLAGS="$(NS_CFLAGS) $(NS_CFLAGS_EXTRA)" $(NS_MAKE) -C $(1) $(NS_MAKE_ARGS) && \ + cp $(1)/$(NS_BUILDDIR)/lib$(2).a $(PORT_ARCHIVE) + +ns_clean = $(NS_MAKE) -C $(1) $(NS_MAKE_ARGS) clean >/dev/null 2>&1 || true + +ifeq ($(PORT_LIB),1) + +# ==== Library ports =================================================== +# The port builds $(PORT_ARCHIVE) and lists its public headers in +# $(PORT_HEADERS); port.mk installs both into the shared sysroot. Headers +# keep any subdirectory they are named with (e.g. parserutils/parserutils.h +# installs to sysroot/include/parserutils/parserutils.h), so set +# PORT_HEADER_ROOT to the directory those paths are relative to. + +PORT_HEADER_ROOT ?= include + +INSTALLED_LIB := $(PORT_SYSROOT_LIB)/$(notdir $(PORT_ARCHIVE)) +INSTALLED_HEADERS := $(patsubst $(PORT_HEADER_ROOT)/%,$(PORT_SYSROOT_INC)/%,$(PORT_HEADERS)) + +$(INSTALLED_LIB): $(PORT_ARCHIVE) + @mkdir -p $(dir $@) + cp $< $@ + +$(PORT_SYSROOT_INC)/%: $(PORT_HEADER_ROOT)/% + @mkdir -p $(dir $@) + cp $< $@ + +.PHONY: install-lib +install-lib: $(INSTALLED_LIB) $(INSTALLED_HEADERS) + @echo "installed lib: $(PORT_NAME) -> $(PORT_SYSROOT)" + +.PHONY: clean-lib +clean-lib: + rm -rf $(OBJDIR) $(PORT_ARCHIVE) $(INSTALLED_LIB) $(INSTALLED_HEADERS) + +else + # ==== Bundle assembly ================================================= # Ports depend on $(BUNDLE_FILES) to get manifest + icon + marker staged # alongside the ELF their link rule produces. @@ -129,3 +236,5 @@ bundle: $(TARGET) $(BUNDLE_FILES) .PHONY: clean-bundle clean-bundle: rm -rf $(OBJDIR) $(BUNDLE_DIR) + +endif diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..6a2a410 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,49 @@ +# Smoke test for the ports sysroot. +# +# Links one program against every library port and exercises a little of each, +# printing PASS/FAIL per library. Building it proves the sysroot's headers and +# archives resolve; RUNNING it on MontaukOS is the only way to prove the code +# is correct, since none of this can execute on the build host. +# +# make -C test build out/test/porttest.elf +# make -C test install copy it into the MontaukOS ramdisk as a system +# program, then rebuild the image and run +# `porttest.elf` from the terminal +# make -C test uninstall remove it again (do this before cutting a +# release -- it is not an app bundle, so it +# carries no .port marker and the release step +# will NOT prune it automatically) + +PORT_NAME := porttest + +include ../mk/port.mk + +SYSROOT_LIBS := \ + $(PORT_SYSROOT_LIB)/libparserutils.a \ + $(PORT_SYSROOT_LIB)/libwapcaplet.a \ + $(PORT_SYSROOT_LIB)/libz.a + +TEST_ELF := $(OUTDIR)/test/porttest.elf + +CFLAGS := -std=gnu11 $(PORT_CFLAGS_COMMON) -Wall + +.PHONY: all clean install uninstall +all: $(TEST_ELF) + +$(TEST_ELF): porttest.c $(SYSROOT_LIBS) $(LIBC_A) Makefile + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) $(PORT_LDFLAGS_COMMON) porttest.c \ + $(SDK_LIB)/libc/crt1.o $(SYSROOT_LIBS) $(LIBC_A) -o $@ + @echo "Built: $@" + +install: $(TEST_ELF) + cp $(TEST_ELF) $(MONTAUKOS)/programs/bin/os/porttest.elf + @echo "installed: 0:/os/porttest.elf - rebuild the image, then run 'porttest.elf'" + @echo "REMINDER: 'make -C test uninstall' before cutting a release ISO." + +uninstall: + rm -f $(MONTAUKOS)/programs/bin/os/porttest.elf + @echo "removed: $(MONTAUKOS)/programs/bin/os/porttest.elf" + +clean: + rm -rf $(OUTDIR)/test diff --git a/test/porttest.c b/test/porttest.c new file mode 100644 index 0000000..5870bd0 --- /dev/null +++ b/test/porttest.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +int main(void) { + lwc_string *a = NULL, *b = NULL; + bool same = false; + parserutils_buffer *buf = NULL; + size_t len = 0; + unsigned char comp[128]; + uLongf clen = sizeof(comp); + int gzok = 0; + const char *s = "MontaukOS"; + + /* libwapcaplet: interning must make equal strings identical */ + lwc_intern_string("netsurf", 7, &a); + lwc_intern_string("netsurf", 7, &b); + /* assign the result: the macro ends in a comma expression, so discarding + it warns under -Wall */ + lwc_error lwcerr = lwc_string_isequal(a, b, &same); + (void) lwcerr; + + /* libparserutils: buffer + utf8 length */ + parserutils_buffer_create(&buf); + parserutils_buffer_append(buf, (const uint8_t *)s, strlen(s)); + parserutils_charset_utf8_length((const uint8_t *)s, strlen(s), &len); + + /* zlib: gzip-wrapper inflate is the path NetSurf uses for + Content-Encoding: gzip, so exercise that rather than plain compress() */ + { + z_stream z; unsigned char back[128]; int r; + memset(&z, 0, sizeof(z)); + deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15+16, 8, + Z_DEFAULT_STRATEGY); + z.next_in = (Bytef *)s; z.avail_in = strlen(s); + z.next_out = comp; z.avail_out = sizeof(comp); + deflate(&z, Z_FINISH); clen = z.total_out; deflateEnd(&z); + memset(&z, 0, sizeof(z)); + inflateInit2(&z, 15+16); + z.next_in = comp; z.avail_in = clen; + z.next_out = back; z.avail_out = sizeof(back); + r = inflate(&z, Z_FINISH); inflateEnd(&z); + gzok = (r == Z_STREAM_END && z.total_out == strlen(s) && + memcmp(back, s, strlen(s)) == 0); + } + + printf("libwapcaplet: intern-equal=%s length=%zu\n", + same ? "PASS" : "FAIL", (size_t)lwc_string_length(a)); + printf("libparserutils: buffer=%zu utf8len=%zu %s\n", + buf->length, len, + (buf->length == strlen(s) && len == strlen(s)) ? "PASS" : "FAIL"); + printf("zlib %s: gzip roundtrip %lu->%lu %s\n", + zlibVersion(), (unsigned long)strlen(s), (unsigned long)clen, + gzok ? "PASS" : "FAIL"); + lwc_string_unref(a); lwc_string_unref(b); + parserutils_buffer_destroy(buf); + return 0; +}