Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .nanvix/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

config = load_sibling("config", __file__)
docker_mod = load_sibling("docker", __file__)
lxml_mod = load_sibling("lxml", __file__)
setup_local_mod = load_sibling("setup_local", __file__)


def make_args(
Expand Down Expand Up @@ -115,7 +115,7 @@ def build(
return
effective_sysroot = config.DOCKER_SYSROOT_PATH if docker else sysroot
effective_toolchain = config.DOCKER_TOOLCHAIN_PATH if docker else toolchain
lxml_mod.generate_setup_local(repo_root, Path(effective_sysroot))
setup_local_mod.generate_setup_local(repo_root, Path(effective_sysroot))
args = make_args(
effective_sysroot,
effective_toolchain,
Expand Down
8 changes: 3 additions & 5 deletions .nanvix/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,6 @@ def configure_opts(
# #323 wave 8 — regex and plistlib
"test_re",
"test_plistlib",
# #600 — lxml built-in smoke test
"test_nanvix_lxml",
# #526 — _lzma stdlib enablement
"test_lzma",
# #327 — network and protocol tests (IPv4 only; IPv6 disabled)
Expand Down Expand Up @@ -464,9 +462,9 @@ def configure_opts(
"lib/pkgconfig",
]

# site-packages is no longer trimmed because lxml runtime files may be
# installed there by downstream packaging. When the directory is empty
# it remains harmlessly on disk (ramfs.trim_sysroot only removes empty
# site-packages is not trimmed because downstream third-party packages
# may install runtime files there. When the directory is empty it
# remains harmlessly on disk (ramfs.trim_sysroot only removes empty
# bin/). To force-trim site-packages for minimal images, add the path
# back into SYSROOT_TRIM_DIRS above.

Expand Down
2 changes: 1 addition & 1 deletion .nanvix/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def _generate_setup_local_cmd() -> str:
"""Shell command to generate Modules/Setup.local inside the container.

Rendered from .nanvix/setup_local.py (single source of truth shared
with the host build path .nanvix/lxml.py::generate_setup_local).
with the host build path .nanvix/setup_local.py::generate_setup_local).
The rendered file body is emitted via a single ``printf '%s\\n' ...``
invocation with each line single-quoted for the container shell.
"""
Expand Down
86 changes: 0 additions & 86 deletions .nanvix/lxml.py

This file was deleted.

3 changes: 0 additions & 3 deletions .nanvix/nanvix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,4 @@ sqlite = "3.49.0"
openssl = "3.5.0"
bzip2 = "1.0.8"
libffi = "3.4.6"
libxml2 = "2.12.9"
libxslt = "1.1.42"
lxml = "5.3.0"
xz = "5.2.5"
4 changes: 0 additions & 4 deletions .nanvix/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

config = load_sibling("config", __file__)
build_mod = load_sibling("build", __file__)
lxml_mod = load_sibling("lxml", __file__)
ramfs_mod = load_sibling("ramfs", __file__)


Expand Down Expand Up @@ -102,9 +101,6 @@ def package(
if not sysroot_installed.is_dir():
raise FileNotFoundError(f"Install did not produce {sysroot_installed}")

# Stage lxml Python package into the installed sysroot.
lxml_mod.stage_lxml_runtime(repo_root, sysroot_installed)

# --- Buildroot: build dependencies ---
buildroot_pkg = release_staging / "buildroot-pkg"
buildroot_pkg.mkdir(parents=True)
Expand Down
55 changes: 24 additions & 31 deletions .nanvix/setup_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

"""Single source of truth for Modules/Setup.local on Nanvix builds.

The host-build path (.nanvix/lxml.py::generate_setup_local) and the
Docker-build path (.nanvix/docker.py::_generate_setup_local_cmd) both
consume SETUP_LOCAL_ENTRIES and render the same file body via
The host-build path (generate_setup_local, below) and the Docker-build
path (.nanvix/docker.py::_generate_setup_local_cmd) both consume
SETUP_LOCAL_ENTRIES and render the same file body via
render_setup_local().

Module ordering matters: makesetup applies "first rule wins" semantics
Expand All @@ -19,6 +19,7 @@
from __future__ import annotations

from enum import Enum
from pathlib import Path
from typing import Iterable, NamedTuple, Sequence


Expand Down Expand Up @@ -91,31 +92,6 @@ class SetupEntry(NamedTuple):
"take precedence."
),
),
SetupEntry(
name="_lxml_etree",
linkage=Linkage.SHARED,
tokens=(
"lxml_etree_builtin.c",
"-L{sysroot}/lib",
"-llxml_etree",
"-lxslt",
"-lexslt",
"-lxml2",
"-lz",
),
section_header=("lxml C extension modules (linked via pre-built archives)."),
),
SetupEntry(
name="_lxml_elementpath",
linkage=Linkage.SHARED,
tokens=(
"lxml_elementpath_builtin.c",
"-L{sysroot}/lib",
"-llxml_elementpath",
"-lxml2",
"-lz",
),
),
# ---------------- Data primitives (no external deps) ----------------
*(
SetupEntry(
Expand Down Expand Up @@ -329,9 +305,9 @@ def render_setup_local(
lines.append(entry.linkage.value)
current_linkage = entry.linkage
elif entry.section_header:
# Mid-section group separator (e.g. moving from lxml to the
# data-primitive modules without changing linkage). Emit as
# a blank-line-separated comment block before the entry.
# Mid-section group separator (e.g. moving from one shared
# group to the next without changing linkage). Emit as a
# blank-line-separated comment block before the entry.
lines.append("")
lines.extend(_wrap_comment(entry.section_header))

Expand All @@ -345,3 +321,20 @@ def render_setup_local(

lines.append("") # trailing newline
return "\n".join(lines)


def generate_setup_local(repo_root: Path, sysroot: Path) -> None:
"""Generate Modules/Setup.local from SETUP_LOCAL_ENTRIES.

Single source of truth shared with the Docker build path
(.nanvix/docker.py::_generate_setup_local_cmd). The {sysroot}
placeholder in entry tokens is substituted with the host build's
sysroot path here.
"""
setup_local = repo_root / "Modules" / "Setup.local"
content = render_setup_local(
sysroot=str(sysroot),
header_comment="Auto-generated by .nanvix/setup_local.py -- do not edit manually.",
)
setup_local.write_text(content, encoding="utf-8")
print(f"[setup_local] Generated {setup_local}")
32 changes: 1 addition & 31 deletions .nanvix/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

config = load_sibling("config", __file__)
build_mod = load_sibling("build", __file__)
lxml_mod = load_sibling("lxml", __file__)
ramfs_mod = load_sibling("ramfs", __file__)


Expand Down Expand Up @@ -547,11 +546,7 @@ def stage(
)

# Copy test script — a simple smoke test that validates the interpreter.
# The lxml import test is only included for standalone mode because
# xmlInitParser() hangs in multi-process/single-process modes where
# filesystem I/O goes through nanvixd's virtualized host-FS layer.
hello_script = sysroot_dir / "test_hello.py"
standalone = process_mode == "standalone"
# Phase 0 of the .a -> .so migration: `array` is now a shared
# extension at lib/python3.12/lib-dynload/array.cpython-312.so
# (built from `*shared* array arraymodule.c` in Setup.local).
Expand All @@ -567,26 +562,12 @@ def stage(
"print(f'CPYTHON_TEST_ARRAY_SO: array loaded via dlopen from "
"{array.__file__}')\n"
)
lxml_snippet = (
"try:\n"
" import lxml.etree\n"
" doc = lxml.etree.fromstring(b'<root><child>lxml OK</child></root>')\n"
" assert doc.tag == 'root'\n"
" assert doc[0].text == 'lxml OK'\n"
" print('CPYTHON_TEST_LXML: lxml.etree import and parse OK')\n"
"except ImportError as e:\n"
" print(f'CPYTHON_TEST_LXML_SKIP: {e}')\n"
"except Exception as e:\n"
" print(f'CPYTHON_TEST_LXML_FAIL: {e}')\n"
" sys.exit(1)\n"
)
hello_script.write_text(
"import sys\n"
"print('CPYTHON_TEST_HELLO: Hello from Python', sys.version_info[:2])\n"
"print('CPYTHON_TEST_PLATFORM:', sys.platform)\n"
+ array_snippet
+ _render_so_sanity_snippets()
+ (lxml_snippet if standalone else ""),
+ _render_so_sanity_snippets(),
)

# Copy the HTTP server smoke-test script from the repo root into the
Expand Down Expand Up @@ -823,8 +804,6 @@ def run_hello(
environment variable syntax. Multi-process and single-process modes
use direct host-filesystem access (no ramfs).
"""
standalone = process_mode == "standalone"

print(f"Test: Hello world ({process_mode})...")

returncode, output, elapsed_ms = _run_nanvixd_script(
Expand All @@ -846,26 +825,18 @@ def run_hello(

# Validate output.
found_hello = False
found_lxml = False
for line in output.splitlines():
if line.startswith("CPYTHON_TEST_"):
tag = line.split(":")[0].replace("CPYTHON_TEST_", "")
print(f" {tag}: {line.strip()}")
if tag == "HELLO":
found_hello = True
elif tag in ("LXML", "LXML_SKIP"):
found_lxml = True

if not found_hello:
print(" FAIL: Hello test did not produce expected output")
print(output)
raise RuntimeError("Hello test did not produce expected output")

if standalone and not found_lxml:
# lxml staging is best-effort — if the runtime package was not
# available (e.g. release asset missing), the test is non-fatal.
print(" WARNING: lxml import/parse test did not produce expected output")

print(" PASS")


Expand Down Expand Up @@ -1194,7 +1165,6 @@ def run_all(
run_fn=run_fn,
docker=docker,
)
lxml_mod.stage_lxml_runtime(repo_root, staging / "sysroot")

# Ramfs — only needed for standalone mode. Multi-process and
# single-process use host-filesystem access (no ramfs).
Expand Down
Loading