| English | [Português (Brasil)] — PT-BR translation pending |
Status: this audit was followed by
chore/packaging-hardening, which resolved P2-1, P2-2, P2-3, P3-1, and P3-2 at package version0.5.12. The original findings below are preserved as historical context; see the Resolution section for verification.
This is an independent packaging, distribution, and clean-installation audit of
the NES Pascal compiler. Its goal is to verify that a user can obtain the
project from the repository, build and install the Python package in a clean
environment, invoke the CLI correctly, compile a valid NES Pascal program, and
understand the external ca65/ld65 toolchain requirement.
The audit did not change compiler semantics, runtime behavior, packaging configuration, CI, package structure, or CLI behavior. Only mechanical documentation changes were made.
Result: the packaged compiler is functional. No P0 or P1 findings. Three P2 findings and several P3 findings are reported below. The P2 findings concern missing console-script entry point, incomplete installation documentation for installed-package usage, and absent CI packaging coverage. None of the findings block obtaining, installing, invoking, or using the compiler today.
audit/packaging-and-clean-installdee2471 (Merge pull request #32 from
Andropovbr/chore/examples-docs-p2-hardening), in sync with origin/main0.5.8build 1.5.0; isolated backend setuptools 84.0.0ca65/ld65 V2.18 (Ubuntu 2.19-1), present on PATHMESEN_PATH=/usr/local/bin/mesen), 29 runtime tests OKSource of truth: pyproject.toml.
| Field | Declared value |
|---|---|
| Package name | nes-pascal |
| Version | 0.5.8 |
| Requires-Python | >=3.11 |
| Build backend | setuptools.build_meta (setuptools>=68) |
| Description / Summary | Prototype Pascal compiler specialized for the NES |
| Runtime dependencies | none declared (Requires-Dist absent) |
| Optional / dev dependencies | none declared |
| License metadata | none declared (setuptools auto-detects the LICENSE file; wheel METADATA is Dynamic: license-file) |
| README metadata | none declared (no readme key; METADATA has no Long Description) |
| Project URLs | none declared |
| Console-script / CLI entry points | none declared (no [project.scripts]) |
| Package discovery | [tool.setuptools.packages.find] include nes_pascal* |
The installed-wheel METADATA reflects exactly this:
Metadata-Version: 2.4
Name: nes-pascal
Version: 0.5.8
Summary: Prototype Pascal compiler specialized for the NES
Requires-Python: >=3.11
License-File: LICENSE
Dynamic: license-file
The metadata is internally consistent (name, version, Python range, summary).
The notable gaps are the missing console-script entry point, README/long
description, license expression, and project URLs. The version 0.5.8 is a
stale milestone-era value relative to implemented features; see
Version consistency.
The system interpreter is /usr/bin/python3 (3.12.3) and has neither pip
nor ensurepip (Debian/Ubuntu without the python3-venv package, no sudo
available). Virtual environments were created with
python3 -m venv --without-pip and pip was bootstrapped with the official
get-pip.py into each venv. Each venv is fully isolated in /tmp/opencode
with its own site-packages; no repository-local PYTHONPATH, no previously
installed dependencies, and no working-tree files were involved.
pip install . from the repository (fresh venv): OK
import nes_pascal → version 0.5.8; import nes_pascal.cli → OKpip install . from a pristine git archive checkout: OKpip install -e . from the repository (fresh venv): OK
python -m nes_pascal.cli --version → nes-pascal 0.5.8pip install <wheel> (fresh venv): OKpip install <sdist> (fresh venv): OKInstallation does not depend on repository-local PYTHONPATH, pre-installed
dependencies, generated files in the working tree, or editable-install-only
behavior.
Built with the declared backend using the standards-based frontend
(python -m build), installed into an isolated audit environment only (not a
runtime dependency):
nes_pascal-0.5.8-py3-none-any.whl (91,998 bytes)
nes_pascal-0.5.8-py3-none-any.whl).nes_pascal.__version__
is 0.5.8.python -m nes_pascal.cli.nes_pascal-0.5.8.tar.gz (161,048 bytes)
.nsp source, so
there is nothing that could be “present only because installation came from
a repository checkout.” An import nes_pascal alone is therefore a
sufficient packaging check for this project.tests/test_*.py modules but not
tests/fixtures/, tests/golden/, tests/mesen/, tests/__init__.py,
examples/, or docs/. This is conventional and acceptable for sdists;
none of those are required at runtime.From the clean wheel-installed environment (no repository context):
| Invocation | Result |
|---|---|
python -m nes_pascal.cli --version |
nes-pascal 0.5.8, exit 0 |
python -m nes_pascal.cli -V |
nes-pascal 0.5.8, exit 0 |
python -m nes_pascal.cli --help |
usage + option help, exit 0 |
python -m nes_pascal.cli (no args) |
argparse usage error, exit 2 |
python -m nes_pascal.cli /nonexistent.nsp -o x.nes |
file-access diagnostic, exit 1 |
nes-pascal --version |
not available — no console script is installed |
Version output matches package metadata. Diagnostics display normally on
stderr with E-codes and exit code 1. Invalid invocation exits with code 2.
A temporary project directory outside the repository
(/tmp/opencode/nes-proj-outside) was created containing a minimal valid
.nsp program. Using only the wheel-installed package, with the repository
not on the path and not the working directory:
source .nsp -> installed NES Pascal -> .asm/.cfg/.map -> ca65 -> ld65 -> .nes
Result: successful. Generated main.asm, main.cfg, main.map,
main.o, and main.nes; the ROM is 40,976 bytes (16-byte header + 32 KiB
PRG + 8 KiB CHR) with a valid NES\x1a header.
Missing-toolchain diagnostic: with PATH restricted so that neither
ca65 nor ld65 is discoverable, the compiler stage still runs, writes the
.asm/.cfg/.map files, and then exits 1 with:
E5001: missing toolchain component: ca65 and ld65. Install the cc65 package and try again.
This matches the documented E5001 behavior.
Real bundled assets were copied into the temporary outside-repository project
(chr_asset.chr, nametable_loading.nam, game.chr, player_idle.json),
and project-relative paths were passed to the installed compiler:
--chr assets/chr_asset.chr --nametable assets/nametable_loading.nam).--chr assets/game.chr --metasprite assets/player_idle.json).User-supplied project files continue to resolve relative to the .nsp source
after the compiler is installed elsewhere. Nothing needs to be shipped inside
the Python package.
Documented in docs/getting-started/prerequisites-and-installation.md
(EN and PT-BR) and docs/reference/diagnostics/code-generation.md
(E5001/E5002, EN and PT-BR):
ca65 and ld65 are external toolchain dependencies required for final ROM
generation; the E5001 diagnostic states this explicitly.PATH (documented trigger: “with cc65 absent from PATH”).PATH discovery is required: the CLI uses shutil.which("ca65") /
shutil.which("ld65")..asm, .cfg, and .map outputs are written before the
E5001 diagnostic is raised. The full ROM build does not work without the
toolchain.Documentation is accurate; no misleading setup instructions were found.
Wheel contents (13 modules + dist-info):
nes_pascal/{__init__,assets,ast,backend_ca65,builtins,cli,codegen_analysis,
diagnostics,lexer,memory_layout,metasprite_assets,parser,semantic}.py
nes_pascal-0.5.8.dist-info/{licenses/LICENSE,METADATA,WHEEL,RECORD,top_level.txt}
No unexpected contents in the wheel: no tests/, no docs/, no examples/,
no .pyc, no caches, no build/ artifacts, no generated Assembly/ROMs, no
benchmark output. The LICENSE file is intentionally included.
sdist contents: the package, pyproject.toml, README.md, LICENSE,
generated setup.cfg, nes_pascal.egg-info, and the top-level tests/*.py
modules (conventional for sdists; fixtures/golden/mesen data and examples are
not included and are not required at runtime).
No large or unexpected files were found in either artifact.
| Source | Value |
|---|---|
pyproject.toml (authoritative) |
0.5.8 |
nes_pascal/__init__.py fallback |
0.5.8 |
| CLI version output | nes-pascal 0.5.8 |
| Wheel METADATA / filename | 0.5.8 |
| sdist filename / PKG-INFO | 0.5.8 |
| README / docs version claims | none |
All values agree today. Findings:
0.5.8 is a stale milestone-era value. The
code implements through milestone 0.5.12 (records, expression temporaries,
functions), while the package version still matches milestone 0.5.8. This is
not a breakage, but the version no longer communicates implemented scope.nes_pascal/__init__.py hard-codes the 0.5.8 fallback, a
second source of truth that can drift from pyproject.toml. (The
importlib.metadata lookup is the primary source; the fallback duplicates
the version.)Resolution: the package version is now
0.5.12and the hard-coded fallback was removed (see Resolution).
>=3.11.nes_pascal/, tests/, and tools/ modules were parsed with
ast.parse(feature_version=(3, 11)): all parse under the 3.11 grammar,
so no syntax newer than the declared minimum was found.The working tree contained build/ and __pycache__/ artifacts during the
first build. A second build was run from the same tree, and a third from a
pristine git archive checkout:
No dependence on stale generated files was found. (Note: build/,
dist/, *.egg-info/, and __pycache__/ are already gitignored.)
A new user following docs/getting-started/prerequisites-and-installation.md
and docs/getting-started/first-program.md (EN and PT-BR mirror pages):
Python 3.11 or newer).pip install -e . is documented. No documented pip install ., wheel,
or sdist install path.python -m nes_pascal.cli is documented. No
documented nes-pascal console command (which is consistent with the
missing entry point, but the reader is never told the module invocation is
the only public entry).PATH
requirement are present.build/*.asm, .cfg, .map,
.o, .nes).Gap: the documentation never covers using the package after a clean install outside the repository. This is finding P2-2.
Resolution: the installation documentation now covers installed-package usage (see Resolution).
Current CI (.github/workflows/ci.yml) installs the project with
pip install -e . from the repository checkout in both jobs
(compiler-toolchain and mesen-runtime) and never builds or installs a
release artifact. Gaps:
[project.scripts] went
unnoticed);This is finding P2-3. Minimal recommended packaging smoke coverage for a future packaging-hardening branch (not implemented during this audit):
python -m build);python -m nes_pascal.cli --version and, once an entry point
exists, nes-pascal --version;.nsp from a directory outside the repository using the
installed wheel (with and without the toolchain on PATH).Resolution: the
packaging-smokeCI job implements this coverage and is now required byci-gate(see Resolution).
P0 — released/installed compiler is unusable or produces invalid output: none.
P1 — clean installation or packaged CLI materially broken: none.
Installation succeeds from wheel, sdist, pip install ., and
pip install -e .; the packaged CLI works; end-to-end ROM builds work from
outside the repository.
P2 — important packaging/release regression gap or misleading install docs:
pyproject.toml declares no
[project.scripts], so pip install never provides a nes-pascal command.
The only public entry is python -m nes_pascal.cli. The argparse prog
name nes-pascal suggests an intended command that is never installed.
(Resolved — see Resolution.)pip install . / wheel) or invocation path outside the repository.
(Resolved — see Resolution.)pip install -e . from
the checkout. No wheel/sdist build, no install-from-artifact test, no
console-script smoke test, no outside-repository compile test. This let
P2-1 go undetected.
(Resolved — see Resolution.)P3 — metadata, polish, optional CI hardening:
readme/long description, no license
expression (only auto-detected LICENSE file), no [project.urls] in the
wheel METADATA.
(Resolved — see Resolution.)0.5.8 lags
implemented milestones 0.5.9–0.5.12, and the 0.5.8 fallback in
nes_pascal/__init__.py is a second version source that can drift.
(Resolved — see Resolution.)docs/pt-BR/ mirror index and translation-status list were not updated
(consistent with the precedent of diagnostic-consistency-audit.md).
(Still open — the report remains English-only.)tests/*.py
modules (without fixtures/golden/mesen data). Conventional and acceptable;
listed for completeness. The wheel ships no tests.Implemented by branch chore/packaging-hardening (follow-up to this audit).
No compiler semantics or runtime behavior changed.
pyproject.toml now declares
[project.scripts] with nes-pascal = "nes_pascal.cli:main". The built
wheel contains entry_points.txt with that mapping. Verified from fresh
wheel and sdist installations: nes-pascal --version →
nes-pascal 0.5.12; nes-pascal --help works; python -m nes_pascal.cli
remains supported. Added regression tests in tests/test_cli.py
(PackagingMetadataTests) asserting the declared entry point and version
consistency.docs/getting-started/prerequisites-and-installation.md
(EN and PT-BR) now documents supported Python (>=3.11), the cc65 external
dependency, pip install ., editable pip install -e ., the installed
nes-pascal command, the python -m nes_pascal.cli fallback, and the
ca65/ld65 PATH requirement for final ROM generation. Both
first-program.md pages note the equivalent nes-pascal invocation..github/workflows/ci.yml adds a small
packaging-smoke job that builds wheel and sdist with python -m build,
installs the wheel into a fresh virtual environment, smoke-tests
nes-pascal --version and python -m nes_pascal.cli --version, compiles a
minimal .nsp from a directory outside the repository (expecting the
compiler stage to succeed and E5001 without the toolchain, then a full ROM
build after installing cc65), and confirms the installed package does not
depend on editable-install behavior. ci-gate now requires
packaging-smoke to succeed.pyproject.toml now declares readme = "README.md",
license = "MIT", and [project.urls] (Repository →
https://github.com/Andropovbr/nes-pascal). The built wheel METADATA shows
License-Expression: MIT, Project-URL: Repository, ..., and the README as
the long description. No runtime dependencies were added.pyproject.toml version is now 0.5.12, matching the
implemented milestone scope. nes_pascal/__init__.py no longer hard-codes a
fallback version: it reads importlib.metadata and, when metadata is
unavailable in a source-tree context, parses pyproject.toml (the single
authoritative source) with a neutral 0.0.0 last resort. Verified
agreement across pyproject.toml, nes_pascal.__version__,
nes-pascal --version, python -m nes_pascal.cli --version, wheel
METADATA, wheel filename, and sdist filename (all 0.5.12).Clean-artifact verification (branch state): nes_pascal-0.5.12-py3-none-any.whl
and nes_pascal-0.5.12.tar.gz built from a clean tree with python -m build;
each installed into a fresh virtual environment; imports, version, console
script, module invocation, and outside-repository full ROM build all verified.
No Requires-Dist appears in the wheel METADATA.
make PYTHON=python3 test — 537 tests, OK.make PYTHON=python3 validate (test-all + benchmark + rom) — exit 0.make PYTHON=python3 test-mesen with MESEN_PATH=/usr/local/bin/mesen —
29 tests, OK./tmp/opencode).The audit branch is intended to be pushed and validated by the repository’s
authoritative GitHub Actions gate (ci-gate). See the branch push/CI result
for the final gate status.
python3 3.12.3 (/usr/bin/python3), CPython.get-pip.py into each venv).build 1.5.0; isolated backend setuptools 84.0.0.ca65/ld65 V2.18 (Ubuntu 2.19-1).python -m build --outdir <dir> <source-tree>.pip install ., pip install -e .,
pip install <wheel>, pip install <sdist> in fresh venvs.python -m nes_pascal.cli main.nsp -o out/main.nes
from /tmp/opencode/nes-proj-outside.