Skip to content

Commit 3b9dbfe

Browse files
jcschaffclaude
andcommitted
Add CLAUDE.md with build/architecture guidance
Document the Python+GraalVM two-layer architecture, build pipeline, and commands. Includes session-learned gotchas: running vcell-native Java tests via Maven, the submodule working-tree-vs-recorded-pointer drift and how to resync it, and the requirement to install submodule artifacts into .m2 before a local vcell-native build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3546dcd commit 3b9dbfe

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

‎CLAUDE.md‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
libvcell is a Python package that wraps a subset of VCell (Virtual Cell) Java algorithms as a native shared library via GraalVM native-image. It provides Python functions for converting between VCML, SBML, and finite volume solver input formats, plus VCell-to-Python math expression translation.
8+
9+
## Build & Development Commands
10+
11+
### Setup
12+
```bash
13+
make install # Install poetry env + pre-commit hooks
14+
```
15+
16+
### Build the native shared library (requires GraalVM JDK 23 with native-image)
17+
```bash
18+
scripts/local_build_native.sh # Full local native build (macOS)
19+
poetry build # Build Python wheel (triggers build.py which builds Java/native)
20+
```
21+
22+
### Quality checks
23+
```bash
24+
make check # Runs: poetry check --lock, pre-commit, mypy, deptry
25+
```
26+
27+
### Tests
28+
```bash
29+
poetry run pytest # Run all tests
30+
poetry run pytest tests/test_libvcell.py # Run specific test file
31+
poetry run pytest tests/test_libvcell.py::test_name # Run single test
32+
poetry run pytest --cov --cov-config=pyproject.toml --cov-report=xml # With coverage
33+
```
34+
35+
The Java/native unit tests (`vcell-native/src/test/`) run separately via Maven and exercise the entry points against vcell-core directly (no native-image step):
36+
```bash
37+
mvn -o test -f vcell-native # All vcell-native Java tests (offline)
38+
mvn -o test -f vcell-native -Dtest=MiscTests # Single test class
39+
```
40+
These require the submodule artifacts to already be installed in `.m2` (see Key dependencies); otherwise compilation fails with errors like `cannot find symbol` for vcell-core methods. The CI `quality` job is what runs these (it's the first to fail on a broken vcell-native test). Avoid asserting against full exception stack traces in these tests — frames embed vcell-core line numbers (drift on submodule bumps) and differ between IDE and Surefire runners.
41+
42+
### Type checking & linting
43+
```bash
44+
poetry run mypy # Type check (strict mode, covers libvcell/, tests/, build.py)
45+
```
46+
47+
Pre-commit hooks run ruff (lint + format) and prettier automatically.
48+
49+
## Architecture
50+
51+
### Two-layer design: Python wrapper over GraalVM native library
52+
53+
**Python layer** (`libvcell/`):
54+
- `__init__.py` — Public API: `vcml_to_finite_volume_input`, `sbml_to_finite_volume_input`, `sbml_to_vcml`, `vcml_to_sbml`, `vcml_to_vcml`, `vcell_infix_to_python_infix`, `vcell_infix_to_num_expr_infix`. Also exposes `__version__` (read from installed package metadata; `"0.0.0"` when running from source tree)
55+
- `solver_utils.py` / `model_utils.py` — Thin wrappers that instantiate `VCellNativeCalls` and delegate to native methods
56+
- `_internal/native_utils.py` — Loads the platform-specific shared library (`.so`/`.dylib`/`.dll`) from `libvcell/lib/` via ctypes; defines `IsolateManager` context manager for GraalVM isolate lifecycle
57+
- `_internal/native_calls.py` — ctypes FFI calls to the native library entry points; handles GraalVM isolate creation/teardown per call, JSON deserialization of `ReturnValue`
58+
59+
**Native/Java layer** (`vcell-native/`):
60+
- `Entrypoints.java` — `@CEntryPoint` methods exposed as C symbols (`vcmlToFiniteVolumeInput`, `sbmlToFiniteVolumeInput`, `vcmlToSbml`, `sbmlToVcml`, `vcmlToVcml`, `vcellInfixToPythonInfix`)
61+
- `ModelUtils.java` / `SolverUtils.java` — Java implementation using vcell-core from the `vcell_submodule`
62+
- Built with Maven, then compiled to a shared library via GraalVM `native-maven-plugin` using the `shared-dll` profile
63+
- `MainRecorder.java` — Used with `native-image-agent` to record dynamic reflection/resource configs before native compilation
64+
65+
**Build pipeline** (`build.py`):
66+
1. `mvn clean install -DskipTests` on `vcell_submodule/` (full VCell Java project)
67+
2. `mvn clean install` on `vcell-native/` (builds the shaded JAR)
68+
3. Run JAR with `native-image-agent` to record native-image config into `target/recording/`
69+
4. `mvn package -P shared-dll` to produce the native shared library
70+
5. Copy resulting `libvcell.{so,dylib,dll}` into `libvcell/lib/`
71+
72+
Linux wheels are built inside the `docker/Dockerfile_manylinux_*` images (manylinux 2_28 and 2_34, for both `aarch64` and `x86_64`), which provide the GraalVM toolchain needed for native compilation in CI.
73+
74+
### Key dependencies
75+
- `vcell_submodule/` — Git submodule pointing to the full VCell Java repository (provides vcell-core). After cloning or pulling a submodule pointer bump, run `git submodule update --init --recursive` — git does NOT auto-update the submodule working tree, so it can sit at an older commit than the recorded pointer (shows as `M vcell_submodule` in `git status`). A stale checkout causes `vcell-native` to fail compiling against vcell-core. To make vcell-core/math available for a local `vcell-native` build, install them into `.m2` first: `mvn -DskipTests clean install -f vcell_submodule` (or run the full `scripts/local_build_native.sh`).
76+
- GraalVM JDK 23 with `native-image` tool required for building native library (`.java-version` pins `graalvm64-23.0.2`)
77+
- Python >=3.10,<4.0, pydantic for data models
78+
79+
### FFI pattern
80+
Each Python API call: creates `VCellNativeCalls` → loads native lib → creates GraalVM isolate → calls C entry point → receives JSON string → deserializes to `ReturnValue(success, message)` → tears down isolate. The `IsolateManager` context manager handles isolate lifecycle.
81+
82+
### Test fixtures
83+
Test data lives in `tests/fixtures/data/` (VCML and SBML XML files). Fixtures are defined in `tests/fixtures/data_fixtures.py` and imported via `tests/conftest.py`.
84+
85+
## CI
86+
87+
GitHub Actions (`.github/workflows/main.yml`) runs on push to main and PRs:
88+
- Quality checks (pre-commit, mypy, deptry) on ubuntu
89+
- Tests + type checking across matrix: macOS (Intel + ARM), Windows, Ubuntu
90+
- All CI jobs require GraalVM setup for native library compilation

0 commit comments

Comments
 (0)