diff --git a/.nanvix/Makefile.nanvix b/.nanvix/Makefile.nanvix index cd21dd3fc..3966f796d 100644 --- a/.nanvix/Makefile.nanvix +++ b/.nanvix/Makefile.nanvix @@ -5,11 +5,22 @@ # MEMORY_SIZE= NANVIX_HOME= NANVIX_TOOLCHAIN= [target] # # Targets: -# all Build liblxml_etree.a, liblxml_elementpath.a, and the functional test ELF +# all Build liblxml_etree.{a,so}, liblxml_elementpath.{a,so}, and the functional test ELF # test Run the functional test ELF on nanvixd.elf # package Create a release tarball # verify-package Verify the contents of the release tarball # clean Remove build artifacts +# +# Dependencies: +# The shared-library build requires: +# * The Nanvix dynamic loader to honour `.init_array` constructors and +# DT_NEEDED resolution chains (esaurez/nanvix#27). +# * `libxml2.so`, `libxslt.so`, and `libexslt.so` to be present in the +# buildroot, shipped by the companion releases of esaurez/libxml2 and +# esaurez/libxslt. +# liblxml_etree.so is linked with DT_NEEDED libxslt.so libexslt.so +# libxml2.so so the loader pulls the lower-level libraries +# automatically at dlopen time, eliminating per-module duplication. # =========================================================================== # Global Variables @@ -30,10 +41,12 @@ EXE = .elf SRC_DIR := $(CURDIR) STATICLIB_ETREE := dist/obj/liblxml_etree.a STATICLIB_ELEMENTPATH := dist/obj/liblxml_elementpath.a +SHAREDLIB_ETREE := dist/obj/liblxml_etree.so +SHAREDLIB_ELEMENTPATH := dist/obj/liblxml_elementpath.so TEST_SRC := .nanvix/test/test_lxml.c TEST_ELF := test_lxml$(EXE) -_NANVIX_DOCKER_BUILD_GOALS := all $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH) $(TEST_ELF) +_NANVIX_DOCKER_BUILD_GOALS := all $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH) $(SHAREDLIB_ETREE) $(SHAREDLIB_ELEMENTPATH) $(TEST_ELF) _NANVIX_GOALS := $(or $(MAKECMDGOALS),$(.DEFAULT_GOAL)) # Ensure required variables are defined. @@ -68,13 +81,17 @@ endif # Build Targets # =========================================================================== -all: $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH) $(TEST_ELF) +all: $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH) $(SHAREDLIB_ETREE) $(SHAREDLIB_ELEMENTPATH) $(TEST_ELF) +# Compile the Cython-generated C sources with -fPIC so they can be +# linked into both the static .a (preserved for downstream consumers +# that still bundle lxml statically) and the new position-independent +# .so files below. $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH): sh -c '\ export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \ mkdir -p dist/obj && \ - i686-nanvix-gcc -m32 -march=pentiumpro -Os -fdata-sections -ffunction-sections \ + i686-nanvix-gcc -m32 -march=pentiumpro -Os -fPIC -fdata-sections -ffunction-sections \ -I$(SRC_DIR)/.nanvix/nanvix-port/cpython-headers \ -I$(BUILDROOT_PATH)/include \ -I$(BUILDROOT_PATH)/include/libxml2 \ @@ -82,7 +99,7 @@ $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH): -I$(SRC_DIR)/src \ -I$(SRC_DIR)/src/lxml/includes \ -c $(SRC_DIR)/src/lxml/etree.c -o dist/obj/lxml_etree.o && \ - i686-nanvix-gcc -m32 -march=pentiumpro -Os -fdata-sections -ffunction-sections \ + i686-nanvix-gcc -m32 -march=pentiumpro -Os -fPIC -fdata-sections -ffunction-sections \ -I$(SRC_DIR)/.nanvix/nanvix-port/cpython-headers \ -I$(BUILDROOT_PATH)/include \ -I$(BUILDROOT_PATH)/include/libxml2 \ @@ -95,6 +112,36 @@ $(STATICLIB_ETREE) $(STATICLIB_ELEMENTPATH): i686-nanvix-ranlib dist/obj/liblxml_etree.a && \ i686-nanvix-ranlib dist/obj/liblxml_elementpath.a' +# liblxml_etree.so: links against libxslt.so + libxml2.so (DT_NEEDED +# entries), with only the cython-generated lxml.etree.c embedded. +# libxslt, libxml2, and libz live in their own .so files and are +# pulled in transitively by the loader at dlopen time, eliminating +# the multi-megabyte duplication that a self-contained build would +# cause. CPython C API symbols (PyType_Type, PyExc_*, ...) and +# libc/libm/libposix entry points stay unresolved and bind against +# python.elf's .dynsym at dlopen time. +$(SHAREDLIB_ETREE): $(STATICLIB_ETREE) + sh -c '\ + export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \ + i686-nanvix-gcc -shared -fPIC -nostdlib \ + -Wl,-soname,liblxml_etree.so -Wl,-z,noexecstack \ + -L$(BUILDROOT_PATH)/lib \ + -Wl,--whole-archive $(STATICLIB_ETREE) -Wl,--no-whole-archive \ + -lxslt -lexslt -lxml2 \ + -o $(SHAREDLIB_ETREE)' + +# liblxml_elementpath.so: pure-Cython module with no native deps +# beyond CPython itself, so no DT_NEEDED entries. +$(SHAREDLIB_ELEMENTPATH): $(STATICLIB_ELEMENTPATH) + sh -c '\ + export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \ + i686-nanvix-gcc -shared -fPIC -nostdlib \ + -Wl,-soname,liblxml_elementpath.so -Wl,-z,noexecstack \ + -Wl,--whole-archive \ + $(STATICLIB_ELEMENTPATH) \ + -Wl,--no-whole-archive \ + -o $(SHAREDLIB_ELEMENTPATH)' + $(TEST_ELF): $(TEST_SRC) @test -f $(STATICLIB_ETREE) || { echo " FAIL: $(STATICLIB_ETREE) not found; run 'build' first"; exit 1; } @test -f $(STATICLIB_ELEMENTPATH) || { echo " FAIL: $(STATICLIB_ELEMENTPATH) not found; run 'build' first"; exit 1; } @@ -138,6 +185,8 @@ package: $(eval ARTIFACT_NAME := lxml-$(PLATFORM)-$(PROCESS_MODE)-$(MEMORY_SIZE)) @test -f $(STATICLIB_ETREE) || { echo " FAIL: $(STATICLIB_ETREE) not found; run 'build' first"; exit 1; } @test -f $(STATICLIB_ELEMENTPATH) || { echo " FAIL: $(STATICLIB_ELEMENTPATH) not found; run 'build' first"; exit 1; } + @test -f $(SHAREDLIB_ETREE) || { echo " FAIL: $(SHAREDLIB_ETREE) not found; run 'build' first"; exit 1; } + @test -f $(SHAREDLIB_ELEMENTPATH) || { echo " FAIL: $(SHAREDLIB_ELEMENTPATH) not found; run 'build' first"; exit 1; } rm -rf dist/$(ARTIFACT_NAME) mkdir -p dist/$(ARTIFACT_NAME)/sysroot/lib \ dist/$(ARTIFACT_NAME)/sysroot/python-packages/lxml/html \ @@ -145,6 +194,8 @@ package: dist/$(ARTIFACT_NAME)/sysroot/python-packages/lxml/includes cp -f $(STATICLIB_ETREE) dist/$(ARTIFACT_NAME)/sysroot/lib/ cp -f $(STATICLIB_ELEMENTPATH) dist/$(ARTIFACT_NAME)/sysroot/lib/ + cp -f $(SHAREDLIB_ETREE) dist/$(ARTIFACT_NAME)/sysroot/lib/ + cp -f $(SHAREDLIB_ELEMENTPATH) dist/$(ARTIFACT_NAME)/sysroot/lib/ cp -f $(SRC_DIR)/src/lxml/*.py dist/$(ARTIFACT_NAME)/sysroot/python-packages/lxml/ -cp -f $(SRC_DIR)/src/lxml/html/*.py dist/$(ARTIFACT_NAME)/sysroot/python-packages/lxml/html/ 2>/dev/null || true -cp -f $(SRC_DIR)/src/lxml/isoschematron/*.py dist/$(ARTIFACT_NAME)/sysroot/python-packages/lxml/isoschematron/ 2>/dev/null || true @@ -158,6 +209,8 @@ verify-package: @test -f "dist/$(ARTIFACT_NAME).tar.gz" || { echo " FAIL: tarball not found"; exit 1; } @tar tzf "dist/$(ARTIFACT_NAME).tar.gz" | grep -q 'sysroot/lib/liblxml_etree.a' || { echo " FAIL: missing liblxml_etree.a"; exit 1; } @tar tzf "dist/$(ARTIFACT_NAME).tar.gz" | grep -q 'sysroot/lib/liblxml_elementpath.a' || { echo " FAIL: missing liblxml_elementpath.a"; exit 1; } + @tar tzf "dist/$(ARTIFACT_NAME).tar.gz" | grep -q 'sysroot/lib/liblxml_etree.so' || { echo " FAIL: missing liblxml_etree.so"; exit 1; } + @tar tzf "dist/$(ARTIFACT_NAME).tar.gz" | grep -q 'sysroot/lib/liblxml_elementpath.so' || { echo " FAIL: missing liblxml_elementpath.so"; exit 1; } @tar tzf "dist/$(ARTIFACT_NAME).tar.gz" | grep -q 'sysroot/python-packages/lxml/' || { echo " FAIL: missing python-packages"; exit 1; } @echo " PASS: lxml package verification" diff --git a/.nanvix/z.py b/.nanvix/z.py index 743e0ee36..ec95d45ba 100644 --- a/.nanvix/z.py +++ b/.nanvix/z.py @@ -54,6 +54,8 @@ def docker_config(self, image: str): cfg.output_files = [ "dist/obj/liblxml_etree.a", "dist/obj/liblxml_elementpath.a", + "dist/obj/liblxml_etree.so", + "dist/obj/liblxml_elementpath.so", "test_lxml.elf", ] return cfg