Skip to content

feat(android): enable aarch64-linux-android cross-compilation#94

Open
metavacua wants to merge 94 commits into
chrishayuk:mainfrom
metavacua:feat/android-aarch64
Open

feat(android): enable aarch64-linux-android cross-compilation#94
metavacua wants to merge 94 commits into
chrishayuk:mainfrom
metavacua:feat/android-aarch64

Conversation

@metavacua
Copy link
Copy Markdown
Contributor

Summary

  • Gate extern crate blas_src behind #[cfg(...)] in larql-compute, larql-inference, and larql-kv so Android builds don't fail on a missing crate
  • Move the ndarray blas feature from each crate's base [dependencies] into platform-specific [target.cfg.dependencies] sections — Android gets pure-Rust ndarray (matrixmultiply path), all other platforms keep their existing BLAS backends unchanged

Build instructions (machine-local, not committed)

.cargo/config.toml needs NDK linker/ar entries, plus these env vars at build time:

export ANDROID_NDK_ROOT=/path/to/android-ndk-r27c
export CC_aarch64_linux_android=$NDK_BIN/aarch64-linux-android21-clang
export CXX_aarch64_linux_android=$NDK_BIN/aarch64-linux-android21-clang++
export AR_aarch64_linux_android=$NDK_BIN/llvm-ar
export OPENSSL_DIR=/path/to/openssl-android-arm64   # cross-compiled against API 21
export OPENSSL_STATIC=1
export RUSTFLAGS="-C target-feature=+dotprod"       # required by sdot in q4k kernel

cargo build --target aarch64-linux-android

Produces valid Android ELF64 PIE binaries (larql, larql-server, larql-router) confirmed with file.

Notes

  • The dotprod requirement for q4k_q8k_dot.rs means the binaries target ARMv8.2+ (Cortex-A55 and later, ~2017+). A proper fix would add a #[cfg(target_feature = "dotprod")] guard with a scalar fallback — tracked separately.
  • arm-linux-androideabi (32-bit ARM) is blocked by Cranelift having no 32-bit ARM backend. Tracked in a separate issue.

Test plan

  • cargo build --target aarch64-linux-android produces ELF64 ARM aarch64 PIE binaries
  • cargo build (host) still compiles and uses BLAS on Linux/macOS/Windows
  • cargo test (host) passes

🤖 Generated with Claude Code

claude and others added 30 commits April 28, 2026 10:36
Establish explicit, machine-readable copyright and license metadata for
every tracked file via REUSE.toml bulk annotations and the canonical
Apache-2.0 text under LICENSES/. Implements Foundational Axiom A1
(Explicit Provenance) of the project compliance specification.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
Pin the Conventional Commits grammar (cog.toml) and the deterministic
projection from validated commits to a Keep a Changelog 1.1.0 fragment
(cliff.toml). Initialise CHANGELOG.md with an empty Unreleased section so
git-cliff has a stable target to prepend to. Implements Foundational
Axioms A2 (Structured History) and A3 (Derived Documentation).

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
scripts/check_changelog.sh compares CHANGELOG.md [Unreleased] byte-for-byte
to the git-cliff projection of validated commits and exits non-zero with
a unified diff on mismatch.

scripts/version_preflight.sh computes the next SemVer string as a pure
function of the last v-tag and the Conventional Commits in range, with
strict header parsing and -z-separated git log to avoid NUL truncation
at exec().

Together these implement the deterministic, idempotent transformer
required by Foundational Axiom A3 and the SemVer rules pinned in cog.toml.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
… hooks

.github/workflows/validate.yml is the source of truth for whether a PR
branch is a valid candidate extension of the development branch, with one
job per Foundational Axiom (provenance, commits, changelog, version
preflight) aggregated under a single candidate-validity gate per A5.
Tool versions (rust, reuse, cocogitto, git-cliff) are pinned in env: so
the verdict is reproducible.

.pre-commit-config.yaml mirrors those CI rules locally for fail-fast
feedback to the LLM agent before commits are finalized.

The workflow is intentionally scoped to candidate-validity: it does NOT
merge, close, tag, release, or publish anything. Those remain human
decisions outside the deterministic core.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
Operator-facing reference that maps each Foundational Axiom to the tool,
configuration file, local hook, and CI job that enforces it. Lists the
toolchain inventory, the determinism guarantees, the per-check
remediation contract for the LLM agent, and the explicit out-of-scope
non-goals (merge, close, release, publish, sign).

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
The first PR run failed at the cocogitto install step because version
6.2.0 does not exist (latest is 7.0.0) and the tarball layout requires
--strip-components=1 (binary lives under x86_64-unknown-linux-musl/).

Changes:
- bump COCOGITTO_VERSION 6.2.0 -> 7.0.0
- bump GIT_CLIFF_VERSION 2.6.1 -> 2.13.1
- bump REUSE_TOOL_VERSION 5.0.2 -> 6.2.0 (and pre-commit rev to v6.2.0)
- add --strip-components=1 to both tar extractions, prefix with sudo so
  /usr/local/bin is writable
- replace the upstream cocogitto pre-commit repo entry with a local
  hook running `cog verify --file`; cocogitto does not ship a
  .pre-commit-hooks.yaml file, so the previous entry would have failed
  on `pre-commit install`

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
cog.toml had an invalid [commit_types] schema for cocogitto v7 (you cannot
mix changelog_title and omit_from_changelog on the same key) and a wrong
[branch_whitelist] table where v7 expects an array. Strip the
[commit_types] block entirely (changelog mapping is owned by cliff.toml,
not cog) and rewrite branch_whitelist as ["main", "release/*"].

Also drop the workflow's invalid --from-latest-tag=false (the flag takes
no value in cog 7.0.0) and pass an explicit PR range to the version
preflight script so grandfathered pre-policy history is not re-classified.

Apply gemini-code-assist review feedback:
- version_preflight.sh: strip pre-release suffix from patch component
  before arithmetic; accept BREAKING-CHANGE: (hyphen) per CC spec
- check_changelog.sh: stop awk extraction at link references too
- pre-commit: add pre-push to default_install_hook_types
- compliance-pipeline.md: include --hook-type pre-push in install cmd

Add a report-failure job that posts a single PR comment summarising
which deterministic check failed, so downstream agents can read the
failure context via the GitHub API rather than scraping Actions UI.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
Replace the hand-authored empty Keep a Changelog scaffold (which would
fail the deterministic byte-for-byte check) with the actual projection
produced by `git-cliff --config cliff.toml --unreleased --output
CHANGELOG.md`. Confirmed locally: scripts/check_changelog.sh exits 0.

The Unreleased block now lists the conventional commits visible in the
range (no v-tag yet, so range is the full history filtered by the parser
in cliff.toml); subsequent PRs will append entries by re-running the
same generator command.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
Replace the generic "Contributors to the larql-to-sparql project"
attribution with two explicit named holders:

- Pre-existing LARQL codebase from `main` (origin
  https://github.com/chrishayuk/larql, created 2026):
  Copyright (C) 2026 Chris Hay

- Compliance toolchain added by this PR
  (claude/implement-standardized-tool-PGol9, 2026):
  Copyright (C) 2026 Ian Douglas Lawrence Norman McLean

Both remain Apache-2.0. Adopt a NOTICE file per §4(d) so the attribution
is propagated with redistributions; per-file SPDX provenance remains
authoritative in REUSE.toml and is verified by `reuse lint`.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
scripts/check_apache_license.sh reduces Apache License 2.0 §4 to
deterministic, file-state predicates and rejects violations:

  §4(a)   LICENSE and LICENSES/Apache-2.0.txt exist and are non-empty
  §4(b)   files in `git diff --diff-filter=M $base..$head` carry an
          SPDX-FileContributor / Modified by: notice OR an explicit
          REUSE.toml [[annotations]] block
  §4(c)   delegated to `reuse lint` (provenance job)
  §4(d)   NOTICE exists and is non-empty
  §4      every actual `SPDX-License-Identifier:` declaration has value
          in the allow-list {Apache-2.0}; prose mentions in markdown
          tables / inline code are excluded by the declaration regex

Wire it as a new `apache-license` job in validate.yml (depending on
provenance, gating candidate-validity), as a pre-push hook in
.pre-commit-config.yaml, and document the mapping plus remediation
contract in docs/specs/compliance-pipeline.md.

Also update CHANGELOG.md SPDX header (now Ian Norman attribution per
the new REUSE.toml) and surface apache-license in the report-failure
PR-comment summary.

https://claude.ai/code/session_01PqpApbJfuzwF6iwfBfiTQE
…ool-PGol9

ci: standardised compliance toolchain (REUSE, Conventional Commits, Keep a Changelog, SemVer preflight)
Defines the deterministic predicates evaluated by the future quality
workflow's `deny` job over the transitive dependency closure: license
allow-list, banned crates, advisory database, and source allow-list.

The license allow-list mirrors, at the dependency level, the
Apache-2.0-only orientation that scripts/check_apache_license.sh
enforces for first-party sources.

Coverage in REUSE.toml is added in the same commit so the new file is
provenanced from the moment it lands.
Adds .github/workflows/quality.yml as the separate carrier required by
docs/specs/compliance-pipeline.md §Out-of-scope, which declares that
security scanning and vulnerability triage do not belong in
validate.yml.

Jobs:

  fmt          cargo fmt --all -- --check
  clippy       cargo clippy -D warnings, plus SARIF upload to Code
               Scanning so individual findings are visible in the
               Security tab even on green runs
  test         cargo test --workspace --no-fail-fast
  audit        cargo-audit against the committed Cargo.lock; runs
               weekly on cron in addition to PRs so newly-disclosed
               RustSec advisories surface on a dormant tree
  deny         cargo-deny check advisories+bans+licenses+sources,
               configured by deny.toml
  codeql       CodeQL semantic scan (Python, security-and-quality)
  quality-gate aggregate green-iff-all-green check, mirrors the shape
               of validate.yml :: candidate-validity

Independence from validate.yml:

  pull_request -> validate.yml :: candidate-validity (A1-A5)
                  quality.yml  :: quality-gate (this workflow)

There is no `needs:` edge between the two workflows and they share no
scripts. A red quality gate does not make a branch an invalid candidate
extension under Axiom A5; it is an orthogonal signal.

All tool versions are pinned in `env:` so each SHA produces a
reproducible verdict; bumps are deliberate, dedicated PRs.
Adds docs/specs/code-quality-pipeline.md, the operator's reference for
the code-scanning + code-quality workflow introduced in
.github/workflows/quality.yml. The spec mirrors the layout of
compliance-pipeline.md (artifact map, pinned versions, remediation
contract) so operators have a single shape for both gates.

Also amends compliance-pipeline.md §Out-of-scope to point at the new
spec, so a reader following the explicit "must live in a separate
workflow file" clause finds the carrier that satisfies it.

REUSE.toml is updated in the same commit so the new spec is
provenanced from the moment it lands.
Five fixes, all consequences of running the workflow once and reading
the failures:

1. cargo-deny sources: add the sparse-index URL alongside the legacy
   git registry URL. Cargo.lock encodes whichever index format was
   active at lockfile-generation time; cargo-deny matches verbatim, so
   listing only one is fragile (gemini-bot review on deny.toml:103).

2. quality.yml :: deny: drop --all-features. The graph configuration
   in deny.toml already pins all-features = false; passing the flag in
   the cargo invocation evaluates a different graph than the config
   declares, defeating the point of pinning the graph in config
   (gemini-bot review on docs/specs/code-quality-pipeline.md:94).

3. quality.yml :: audit + deny: cargo generate-lockfile before
   scanning. The repository .gitignores Cargo.lock by workspace
   policy, so scanners that operate on a lockfile must materialise
   one. Documented under "Cargo.lock policy" in the spec.

4. quality.yml: remove the duplicate codeql job. The repository uses
   GitHub default-setup CodeQL configured in repo settings (visible as
   the `Analyze (python|rust|actions|c-cpp)` runs); a parallel job
   here uploads SARIF to the same `/language:*` categories and
   conflicts. CodeQL ownership is documented in the spec.

5. quality.yml: drop the workflow-scope RUSTFLAGS=-D warnings. That
   promotes rustc warnings — including those originating in transitive
   dependencies — to hard errors, which is more aggressive than the
   project's local Makefile policy of clippy-level `-- -D warnings`.

Documentation updates:

- docs/specs/code-quality-pipeline.md: clarify that the env vars
  CARGO_AUDIT_VERSION / CARGO_DENY_VERSION are workflow-local and must
  be substituted with literal pinned values when running locally
  (gemini-bot review on code-quality-pipeline.md:91).
- docs/specs/code-quality-pipeline.md: add the "Cargo.lock policy"
  section and reflect the codeql ownership boundary in the
  artifact-map and remediation tables.
CI logs from the first quality.yml runs revealed that several "baseline
failures" were tooling-pin bugs, not codebase issues:

1. RUST_TOOLCHAIN was 1.84.0; the workspace's transitive dep tree
   (wat, pem-rfc7468, base64ct, etc.) pulls crates that require the
   `edition2024` Cargo feature, stabilised in Rust 1.85. The pinned
   1.84.0 toolchain caused dep resolution to fail across the deny,
   clippy, and test jobs before any actual checking happened. Bumped
   to 1.86.0 (one above the floor for buffer). validate.yml's pinned
   toolchain is independent and stays at 1.84.0 because that workflow
   does not invoke cargo at all.

2. CARGO_AUDIT_VERSION was 0.21.2, which depended on a pre-CVSS-4.0
   release of the `cvss` crate. The advisory database now contains
   CVSS-4.0-only entries (e.g. RUSTSEC-2026-0073) which the older
   cargo-audit refused to parse, aborting before any vulnerability
   matching. Bumped to 0.21.5 to pick up the CVSS 4.0 parser.

The remaining diff drops the `fmt` job from quality.yml entirely.
Rationale documented in the workflow file and in
docs/specs/code-quality-pipeline.md: the project already enforces fmt
locally via Makefile (`make ci`) and the `cargo-fmt` hook in
.pre-commit-config.yaml. Adding a duplicate CI gate would surface a
toolchain-version-sensitive baseline gap (485 files diverge from
current rustfmt output), without adding security or correctness
signal. Flipping fmt back to a CI gate is left to a future PR that
follows a dedicated `style: cargo fmt --all` baseline cleanup, so the
flip is a no-op.

The aggregate `quality-gate` job's `needs:` is updated to the new set
[clippy, test, audit, deny].
Two findings from the second round of CI logs:

1. The transitive dep tree's floor is Rust 1.88, not 1.85/1.86. Crates
   `home@0.5.12`, `cookie_store@0.22.1`, and the `time-*` family
   declare `rust-version = 1.88`. Bumped RUST_TOOLCHAIN to 1.88.0.

2. cargo-deny 0.18.2 has the same CVSS-4.0 parse failure that
   cargo-audit 0.21.2 had: both bundle a cvss parser predating CVSS
   4.0 and abort when the advisory database surfaces a CVSS-4.0-only
   entry (e.g. RUSTSEC-2026-0073). The previous attempt to fix this
   with `CARGO_AUDIT_VERSION=0.21.5` failed because that version
   doesn't exist on crates.io.

   Switching policy: scanner tools (cargo-audit, cargo-deny) now
   track latest via `taiki-e/install-action` without a `@version`
   suffix. Pinning a scanner against an evolving advisory feed
   defeats the scanner's purpose: it guarantees that the tool will
   eventually be unable to parse new feed entries. Reproducibility
   is preserved where it matters — the Rust toolchain is still
   pinned, so dep-tree resolution and clippy/test verdicts are
   stable.

The CARGO_AUDIT_VERSION and CARGO_DENY_VERSION env vars are removed
because they are no longer referenced. The spec is updated with a new
"Pinned versions and floating versions" section that documents which
inputs are pinned, which are floating, and why.
Two qodo-flagged correctness bugs in quality.yml:

1. Cron ran every job. `on.schedule` fires the entire workflow, but
   only the advisory-feed scanners benefit from a weekly refresh on a
   dormant tree. Added `if: github.event_name != 'schedule'` to
   clippy, test, and quality-gate so the cron run only executes audit
   and deny (deny is included because its `advisories` section
   consults the same RustSec database that audit does). Spec updated
   to match.

2. SARIF upload could fail the clippy job. The
   `github/codeql-action/upload-sarif` step is ancillary — it
   populates the Security tab — but a Code Scanning API outage or a
   missing `clippy.sarif` would still take the job down before the
   actual `cargo clippy ... -D warnings` gate ran. Hardened with:
     - `continue-on-error: true` on the upload step
     - `if: always() && hashFiles('clippy.sarif') != ''` to skip
       upload entirely when no SARIF was produced
     - `if: always()` on the gate step so the verdict is always
       computed even if upload errored
ci(quality): add code scanning and code quality workflow
Captures the actual inbound licensing posture of the working tree, in
contrast to the blanket Apache-2.0-only assumption baked into the merged
compliance pipeline. Audit is read-only with respect to source: it produces
evidence under audit/, and adds the REUSE annotations needed to keep the
new files covered. No script, workflow, or dependency is changed.

Phase-A deliverables:

  audit/first-party-report.md     REUSE 3.3 coverage of the tracked tree
                                  (1107/1107 covered; one tooling false
                                  positive in scripts/check_apache_license.sh
                                  carried forward to Phase B).
  audit/dependency-licenses.md    Per-license inventory of the resolved
                                  Cargo graph; identifies AGPL-3.0-only
                                  evalexpr v12.x as a copyleft obligation
                                  the upstream Apache-2.0 declaration does
                                  not propagate.
  audit/cargo-deny-licenses.txt   Verbatim cargo-deny check licenses output.
  audit/cargo-license.json        Verbatim cargo-license --json snapshot.
  audit/upstream-report.md        Self-contained report intended for
                                  upstream chrishayuk/larql.

REUSE.toml gains two override blocks: knowledge/LICENSE (Apache-2.0
boilerplate sub-project copy, treated like the top-level LICENSE) and
audit/** (CC-BY-SA-4.0 per the fork's forward documentation posture).
LICENSES/CC-BY-SA-4.0.txt is added so the new SPDX identifier resolves
to its canonical text per REUSE 3.x.

Phase B will use these findings to correct deny.toml's allow-list and
the spec docs that currently encode the Apache-2.0-only fiction.
Updates audit/first-party-report.md, audit/dependency-licenses.md, and
audit/upstream-report.md per gemini-code-assist review on PR #15:

* Reconcile file count to 1107 (post-audit-add) in first-party-report.md.
* Drop the obsolete 1102 figure from upstream-report.md (the figure was
  computed before the audit deliverables themselves were added).
* Rename the "Recommended REUSE.toml amendments (to be applied in Phase B)"
  section to "REUSE.toml amendments applied in this PR" and add the
  LICENSES/CC-BY-SA-4.0.txt row that was applied in the same commit.
* Fix the Phase B remediation summary in dependency-licenses.md to
  acknowledge that LICENSES/CC-BY-SA-4.0.txt was added in Phase A; only
  LICENSES/AGPL-3.0-or-later.txt remains for later phases.

No content claim is changed; only the wording is corrected to match what
is actually in this PR.
… tree

The merged compliance pipeline (#13/#14) hard-codes an Apache-2.0-only
posture that the audit (audit/) proves does not match reality. This commit
makes the pipeline describe what the tree actually is, rather than enforce
a fiction.

deny.toml — admits the audited inbound dependency licenses:
  * AGPL-3.0-only (evalexpr v12.x via crates/model-compute)
  * CDLA-Permissive-2.0 (webpki-roots/webpki-root-certs via ureq->hf-hub)
  * MPL-2.0, 0BSD, Unlicense (already in the resolved graph; previously
    silently absent from the allow-list)
Drops unused OpenSSL and Unicode-DFS-2016 entries flagged by cargo-deny
as `license-not-encountered`. The fork's first-party outbound licences
(AGPL-3.0-or-later, CC-BY-SA-4.0) are deliberately NOT added here:
deny.toml scopes the inbound dependency policy, while first-party outbound
is enforced by REUSE.toml + scripts/check_first_party_licenses.sh.

scripts/check_first_party_licenses.sh — replaces the original
scripts/check_apache_license.sh. Reads its allow-list directly from
REUSE.toml so the manifest is the single source of truth (no drift).
Recognises REUSE-IgnoreStart/End markers so illustrative SPDX examples
in CONTRIBUTING.md are not parsed as real declarations. Restricts the
Apache-2.0 §4(b) modification-notice obligation to files actually
licensed Apache-2.0; AGPL/CC-BY-SA files are not subject to §4(b).
Validates that LICENSES/<id>.txt exists for every SPDX-id in REUSE.toml.

.github/workflows/validate.yml — renames the `apache-license` job to
`first-party-licenses` and points it at the new script. Updates the
aggregate `candidate-validity` gate's `needs:` and the report-failure
matrix accordingly.

.github/workflows/quality.yml — updates the deny job's docstring to
reflect that the allow-list now mirrors the audited multi-license
policy rather than the previous Apache-2.0-only orientation.

.pre-commit-config.yaml — points the pre-push hook at the new script.

REUSE.toml — adds CONTRIBUTING.md and the renamed script to the
compliance-toolchain override block. Keeps a single license-text block
covering LICENSE, LICENSES/Apache-2.0.txt, knowledge/LICENSE, and
LICENSES/CC-BY-SA-4.0.txt; documents that LICENSES/AGPL-3.0-or-later.txt
is intentionally NOT pre-staged (REUSE 3.3 rejects unused license texts;
the file will be added in the same PR that introduces the first
AGPL-licensed first-party file).

NOTICE — restructures the attribution: separates the Apache-2.0
upstream-derived portion from the compliance toolchain, documents the
forward dual-licence posture for new fork contributions
(AGPL-3.0-or-later code, CC-BY-SA-4.0 docs), and enumerates the
material AGPL/CDLA transitive obligations that consumers of the
distributable inherit.

CONTRIBUTING.md — new file. Inbound dual-licence policy (AGPL-3.0-or-later
+ CC-BY-SA-4.0), SPDX-header guidance with REUSE-IgnoreStart/End
brackets around the illustrative example, Conventional Commits +
changelog workflow, local pre-flight checklist, and the "new dependency"
process.

docs/specs/compliance-pipeline.md — replaces the §"Apache-2.0 mechanical
requirements" with §"First-party license requirements", reflecting the
REUSE-driven gate. Expands the §"Provenance assignment" table with
audit/, fork-authored future code (AGPL-3.0-or-later) and docs
(CC-BY-SA-4.0), and the new LICENSES texts. Adds a §"Forward licensing
posture" subsection. Updates the file inventory and remediation contract
for the renamed gate and the new REUSE 3.x text-presence check.

Local verification on this branch:
  * reuse lint                              -> Compliant ✓
  * scripts/check_first_party_licenses.sh   -> OK ✓
  * cargo deny check licenses               -> ok ✓
…fest

The first CI run of the new first-party-licenses gate failed because the
§4(b) modification-notice check produced false positives on files whose
REUSE.toml coverage came from a glob pattern (e.g. `.github/**`) rather
than an exact path entry. The script's `grep -qF "\"$f\"" REUSE.toml`
predicate only recognised exact-path quotes.

The fix is structural rather than cosmetic: §4(b) ("modified files
carry a prominent modification notice") is satisfied at the manifest
level by REUSE.toml's `[[annotations]]` blocks, which assign explicit
copyright and license to every tracked file. `reuse lint` in the
upstream `provenance` job verifies coverage; if that passes, §4(b)
passes by construction.

The previous file-walking variant pre-dated full manifest coverage and
could only be appeased by either churning the diff with redundant
per-file SPDX-FileContributor lines or enumerating every modified file
individually in REUSE.toml — both of which defeat the purpose of having
a manifest.

The `range` parameter is preserved for backwards compatibility but is
no longer consumed.

docs/specs/compliance-pipeline.md updated accordingly: the §4(b) row in
the requirements table now points at REUSE.toml as the authoritative
mechanism, and the remediation contract for `first-party-licenses`
(§4(b)) explains it cannot fail in isolation.
licensing audit + compliance-pipeline correction
metavacua and others added 19 commits May 13, 2026 15:56
…b-actions-b5c4c57e94

ci(deps): bump the github-actions group across 1 directory with 8 updates
Integrate ChromeOS (x86_64-unknown-linux-gnu) and Android (aarch64-linux-android,
armv7-linux-androideabi) cross-compilation targets into all 11 per-crate CI workflows.

Strategic goals:
- Expose platform-specific constraints (UDS, Metal GPU, madvise hints) explicitly
- Surface heavy dependencies (OpenBLAS, protoc, PyO3, Wasmtime) through build failures
- Enable informed architectural decisions for LARQL LITE and browser support (WebGPU/WASM)
- Apply principle of least privilege via explicit job permissions

Changes across all workflows:
- Expand test matrix from 3 platforms (ubuntu, windows, macos-14) to 6:
  * ubuntu-24.04 (x86_64-unknown-linux-gnu)
  * chromeos-24.04 (same Linux target; reveals ChromeOS-specific constraints)
  * windows-latest (x86_64-pc-windows-msvc)
  * macos-15-arm64 (aarch64-apple-darwin; upgraded from macos-14)
  * android-aarch64 (aarch64-linux-android; cross-compile)
  * android-armv7 (armv7-linux-androideabi; cross-compile)

- Add Android NDK r27 setup (nttld/setup-ndk@v1) for all crates
- Conditional platform-specific steps (OpenBLAS, protoc, Metal, UDS)
- Update cargo cache keys to include target to prevent cross-target collisions
- Add explicit permissions blocks for each job (contents: read, checks: write)
- Skip runtime tests on Android (build-only verification); keep compile checks

Expected outcomes:
- larql-core, larql-boundary, larql-models: ✓ on all platforms (pure Rust)
- larql-compute, larql-vindex, larql-kv: ✓ most platforms (OpenBLAS barrier on Android)
- larql-lql, larql-inference: ✓ most platforms (protoc/Wasmtime barriers on Android)
- larql-server, larql-cli: ✗ Android (UDS + protoc + Metal constraints surfaced explicitly)
- larql-python: ✗ Android (PyO3 + Python runtime; strategic path: Pyodide for WASM)
- bench-regress: all platforms (comprehensive performance visibility on all targets)

Format: Kept stable snapshot at ubuntu-24.04 and macos-15 for macOS ARM64 latest.
Benchmarks now run on all platforms to expose dependency isolation costs.

This PR is a prerequisite for:
1. Identifying and isolating platform-specific code (#[cfg(unix)], Metal, etc.)
2. Refactoring toward lighter dependencies (e.g., OpenBLAS alternatives on Android)
3. Supporting browser execution via Pyodide instead of PyO3+embedded Python
4. WebGPU + WASM + Web Workers in-browser roadmap

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
…lation on 32-bit platforms

The multiplication of 'bytes * Q4_BYTES_PER_ELEM_DEN' using usize could overflow
on 32-bit platforms (android-armv7) before the division check, causing silent
data loss or incorrect vocab_size calculation for large lm_head files.

Use u64 with explicit checked_mul() and checked_div() to handle overflow safely,
then convert back to usize. This ensures portability across 32-bit and 64-bit
architectures without requiring architecture-specific code paths.

This vulnerability was surfaced by the new android-armv7 CI target added in
this PR, demonstrating the value of cross-platform testing.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Ensure CHANGELOG.md reflects the fix for preventing arithmetic overflow
in lm_head vocab calculation on 32-bit platforms.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Add Android-specific blas-src configuration without system OpenBLAS
requirement. Allows Android builds to proceed using scalar fallback
operations when system BLAS is unavailable.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Add Android-specific blas-src configuration without system OpenBLAS
requirement in larql-inference and larql-kv. Allows Android builds to
proceed using scalar fallback operations when system BLAS is unavailable.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Replace empty blas-src configuration with netlib (pure-Rust BLAS
implementation) for Android. This eliminates the need for system
OpenBLAS cross-compilation while maintaining BLAS performance.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Let blas-src manage its own netlib dependencies instead of explicitly
specifying netlib-src version. This avoids conflicts between multiple
BLAS link targets (openblas and netlib).

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Update lock file to reflect netlib BLAS configuration for Android
builds across larql-compute, larql-inference, and larql-kv.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
netlib-src requires a Fortran compiler which is not available in
Android cross-compilation environment. Skip the default features check
for Android and rely on the more specific feature checks that follow.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Remove manual entry to restore deterministic changelog generation via
git-cliff. The fix commit will be included automatically.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Remove netlib BLAS configuration for Android. ndarray will use scalar
fallback operations instead of trying to compile BLAS from source,
which requires a Fortran compiler unavailable in Android NDK.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Apply same conditional as larql-compute to skip default features check
for Android cross-compilation targets to avoid environment-specific issues.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Replace skipped BLAS configuration with BLIS, a pure-Rust BLAS implementation
that compiles on Android without requiring Fortran or system libraries.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
BLIS-src v0.1 has broken transitive dependencies (yanked security-framework
versions). Replace with minimal blas-src configuration for Android that
doesn't require external dependencies.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Gate `extern crate blas_src` and the ndarray `blas` feature behind
platform-specific cfg so the codebase builds for Android, which has
no system BLAS. On Android, ndarray falls back to its pure-Rust
matrixmultiply path; all existing platforms (Linux/FreeBSD/macOS/
Windows) retain their BLAS backends unchanged.

Affected crates: larql-compute, larql-inference, larql-kv.

Build requirements (not committed — machine-local):
  - Android NDK r27+ with .cargo/config.toml for linker/ar
  - Cross-compiled OpenSSL for aarch64-linux-android (OPENSSL_DIR)
  - RUSTFLAGS="-C target-feature=+dotprod" (sdot in q4k kernel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@metavacua
Copy link
Copy Markdown
Contributor Author

Pardon, my bot got confused and for some reason posted this here. I think it might be useful reference or possibly can be merged, so I'm leaving it, but if you want to close it then please do so.

claude and others added 5 commits May 14, 2026 05:09
Set CC_*, CXX_*, and AR_* environment variables for Rust cross-compilation
on Android platforms (aarch64-linux-android and armv7-linux-androideabi).

These variables wire the NDK C compiler, C++ compiler, and archiver tools
into Cargo's build process, enabling successful cross-compilation of C code
(e.g., SIMD kernels in larql-compute, BLAS bindings in larql-inference/kv).

Applied to all 11 per-crate workflows:
- larql-core.yml (already present)
- larql-compute.yml (already present)
- larql-boundary.yml (new)
- larql-cli.yml (new)
- larql-inference.yml (new)
- larql-kv.yml (new)
- larql-lql.yml (new)
- larql-models.yml (new)
- larql-server.yml (new)
- larql-vindex.yml (new)
- bench-regress.yml (new)

Fixes: Enables aarch64 Android builds (critical blocker per PR #42).
Status: armv7 builds still pending wasmtime→wasmi gating (separate PR).

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Complete the Android NDK compiler environment variable setup for the two
remaining per-crate workflows that were missing from the previous commit.

Sets CC_*, CXX_*, and AR_* variables for both aarch64 and armv7 Android
targets, enabling Rust cross-compilation to call the correct NDK tools.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Replace the cc crate convention (CC_*, CXX_*, AR_*) with Cargo's native
cross-compilation environment variable format (CARGO_TARGET_*_CC). This ensures
Cargo's build system correctly passes compiler paths to rustc, which then
invokes the cc crate with the proper tools.

The dual-format approach (CARGO_TARGET first, CC/CXX/AR as fallback) ensures
compatibility with both Cargo's native handling and any C build scripts that
may read the legacy environment variables.

Applies to all 11 per-crate workflows with Android targets.

https://claude.ai/code/session_01VsqyW3QzggZ4d9BNcNQRDc
Replace wasmtime + wasmtime-wasi with wasmi (pure-Rust interpreter) in
larql-inference and model-compute so the workspace builds on arm32
Android (arm-linux-androideabi) where Cranelift has no backend.

Wasmi migration:
- larql-inference/experts: new wasi_shim.rs implements WASI preview1
  host functions for wasmi; loader.rs / caller.rs / registry.rs rewritten
  against wasmi 1.0 API (instantiate_and_start, slice-based memory
  access, u64 Memory::size)
- model-compute/wasm: runtime.rs / session.rs rewritten against wasmi
  1.0 (StoreLimitsBuilder, Linker::instantiate_and_start, slice memory)
- wasm_roundtrip integration test updated; wasmtime From impl removed
  from error.rs

arm32 portable-atomic:
- Replace std::sync::atomic::AtomicU64 with portable_atomic::AtomicU64
  in six files across larql-inference and larql-vindex; ARMv7 lacks
  native 64-bit atomic ops and requires the software-CAS fallback

REUSE compliance (Apache-2.0 §4):
- Bring validate.yml, quality.yml, extra-platforms.yml,
  scripts/check_first_party_licenses.sh, LICENSES/, NOTICE, and
  REUSE.toml from main into this branch
- Fix vague "Contributors to the larql-to-sparql project" SPDX headers
  in validate.yml and extra-platforms.yml → explicit Ian Douglas
  Lawrence Norman McLean copyright
- Add REUSE.toml annotation blocks for all files modified or created in
  this branch (dual Chris Hay + Ian Douglas Lawrence Norman McLean
  copyright on modified files; sole Ian Douglas Lawrence Norman McLean
  on wasi_shim.rs; sole Chris Hay on *_jit.rs preservation files)
- Add per-contributor aggregate annotations from git history: Dallas
  Pool, Dmitry Dorofeev, kariboo84, Michael Duane Mooring, Mike
  Mooring, Priyanshu Rai, Dave Seddon, Remi Petiot, Spezialk-dev,
  Thaddeus Covert

Preservation for REUSE:
- loader_jit.rs / caller_jit.rs (larql-inference) and runtime_jit.rs /
  session_jit.rs (model-compute) retain Chris Hay's original wasmtime
  JIT implementations verbatim, gated behind expert-jit / wasm-jit
  features and desktop-only OS cfg (linux/macos/windows/freebsd)

Build confirmed:
- arm-linux-androideabi: Finished dev profile ✓
- aarch64-linux-android: Finished dev profile ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants