-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
546 lines (484 loc) · 22.5 KB
/
Copy pathMakefile
File metadata and controls
546 lines (484 loc) · 22.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# Formulon convenience targets.
#
# Long-running commands (lint, test, full builds) should redirect output to a
# file and grep the file — never pipe directly into grep.
BUILD_DIR ?= build
GENERATOR ?=
CMAKE ?= cmake
CTEST ?= ctest
CLANG_FORMAT ?= clang-format
CLANG_TIDY ?= clang-tidy
# Node.js binary. Overridable so CI can dodge emsdk's bundled non-executable
# `node` shim (see release.yml's `make CMAKE=... NODE=...` pattern).
NODE ?= node
# Python formatter/linter. Two config scopes, resolved by ruff's
# closest-config discovery:
# packages/python -> packages/python/pyproject.toml ([tool.ruff])
# tools/, tests/ -> ruff.toml at the repo root
# uvx resolves a cached ruff (first run downloads it); override RUFF to
# point at a pinned install.
RUFF ?= uvx ruff
# Repo-tooling Python trees governed by the root ruff.toml. `tools`
# covers oracle/catalog/ci/codegen/dev/bench/jis0208; `tests` picks up
# the standalone .py drivers (tests/parity/run_parity.py,
# tests/codegen/*.py) -- ruff only visits Python files, so the C++ and
# .mjs trees underneath are ignored. Excludes for build*/venv/vendored
# dirs live in ruff.toml.
RUFF_TOOL_PATHS := tools tests
# JS/TS formatter/linter for the .mjs scripts and hand-written .d.ts files.
# Config lives in biome.json at the repo root; the major is pinned to match
# that config's schema. npx serves it from the npm cache after the first run.
BIOME ?= npx --yes @biomejs/biome@2
# Trees biome covers (biome.json's files.includes narrows these to the
# editable .mjs / .d.ts files and excludes the staged dist/ copies).
BIOME_PATHS := packages/npm packages/npm-native tests/wasm src/wasm
# Default test parallelism. Override with `make test CTEST_JOBS=N`. CTest
# infers a reasonable number when the variable is empty (`-j 0` lets it
# pick), but defaulting to the host CPU count keeps the fast loop near
# 2 minutes on a modern workstation rather than the 10-15 minute serial
# walk through the ~10k oracle parametric tests.
CTEST_JOBS ?= 0
SRC_DIRS := src tests
CPP_GLOB := $(shell find $(SRC_DIRS) -type f \( -name '*.cpp' -o -name '*.h' \) 2>/dev/null)
.PHONY: all build release test test-slow test-all format format-check lint clean \
wasm wasm-debug wasm-capi test-wasm test-python size-check \
npm-package npm-test npm-pack npm-check-dts \
node-native node-package node-test \
python-package python-test python-wheel \
parity-test \
oracle-setup oracle-setup-mac oracle-setup-wsl \
oracle-gen oracle-gen-cf oracle-gen-workbook \
oracle-verify oracle-contribute oracle-contribute-list \
ironcalc-import ironcalc-verify \
fuzz-parser fuzz-xlsx fuzz-eval bench coverage mutation \
function-status behavior-status
all: build
build:
$(CMAKE) -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=Debug
$(CMAKE) --build $(BUILD_DIR) --parallel
release:
$(CMAKE) -B build-release -DCMAKE_BUILD_TYPE=Release
$(CMAKE) --build build-release --parallel
test:
(cd $(BUILD_DIR) && $(CTEST) -LE "SLOW|BENCH|TSAN" -j $(CTEST_JOBS) --output-on-failure --timeout 30)
test-slow:
(cd $(BUILD_DIR) && $(CTEST) -LE "BENCH|TSAN" -j $(CTEST_JOBS) --output-on-failure --timeout 120)
test-all:
(cd $(BUILD_DIR) && $(CTEST) -j $(CTEST_JOBS) --output-on-failure --timeout 300)
# `format` is the single auto-fix entry point and must cover every tree
# with editable source. Current coverage:
# src/ tests/ -> clang-format (Google style, .clang-format)
# packages/python -> ruff format + ruff check --fix
# (config: packages/python/pyproject.toml)
# packages/npm -> biome check --write over the .mjs scripts/tests
# packages/npm-native -> biome check --write over .mjs + hand-written
# index.d.ts
# tests/wasm, src/wasm -> biome check --write over run.mjs and the
# canonical hand-written formulon.d.ts
# (config: biome.json at the repo root)
# tools/, tests/**/*.py -> ruff format + ruff check --fix over the
# repo-tooling Python (oracle, catalog, ci,
# codegen, dev, bench, jis0208, parity runner)
# (config: ruff.toml at the repo root)
format:
@find $(SRC_DIRS) -type f \( -name '*.cpp' -o -name '*.h' \) -print0 \
| xargs -0 -r $(CLANG_FORMAT) -i
$(RUFF) format packages/python
$(RUFF) check --fix packages/python
$(RUFF) format $(RUFF_TOOL_PATHS)
$(RUFF) check --fix $(RUFF_TOOL_PATHS)
$(BIOME) check --write $(BIOME_PATHS)
format-check:
@find $(SRC_DIRS) -type f \( -name '*.cpp' -o -name '*.h' \) -print0 \
| xargs -0 -r $(CLANG_FORMAT) --dry-run --Werror
$(RUFF) format --check packages/python
$(RUFF) check packages/python
$(RUFF) format --check $(RUFF_TOOL_PATHS)
$(RUFF) check $(RUFF_TOOL_PATHS)
$(BIOME) check $(BIOME_PATHS)
lint:
@if [ ! -f $(BUILD_DIR)/compile_commands.json ]; then \
echo "compile_commands.json missing; run 'make build' first."; exit 1; \
fi
@find src -type f \( -name '*.cpp' -o -name '*.h' \) \
-exec $(CLANG_TIDY) -p $(BUILD_DIR) {} +
clean:
rm -rf $(BUILD_DIR) build-release build-relwithdebinfo \
build-asan build-ubsan build-tsan build-coverage \
$(WASM_BUILD_DIR) $(WASM_DEBUG_BUILD_DIR) $(WASM_CAPI_BUILD_DIR)
# -- WASM build / smoke-test targets --------------------------------------
# `make wasm` -> Release-mode formulon.{js,wasm} under build-wasm/.
# `make wasm-debug` -> Debug-mode artifact (with assertions) under
# build-wasm-debug/.
# `make test-wasm` -> Node-based smoke tests against build-wasm/formulon.js.
#
# All three short-circuit cleanly when the host lacks emscripten so the
# native CI path stays green without an Emscripten install.
EM_CMAKE := emcmake cmake
WASM_BUILD_DIR ?= build-wasm
WASM_DEBUG_BUILD_DIR ?= build-wasm-debug
WASM_CAPI_BUILD_DIR ?= build-wasm-capi
wasm:
@if ! command -v emcmake >/dev/null 2>&1; then \
echo "wasm: emscripten toolchain not found in PATH"; \
echo " Install: https://emscripten.org/docs/getting_started/downloads.html"; \
exit 1; \
fi
$(EM_CMAKE) -B $(WASM_BUILD_DIR) -DCMAKE_BUILD_TYPE=Release \
-DFM_BUILD_WASM=ON -DFM_BUILD_TESTING=OFF -DFM_BUILD_CLI=OFF
$(CMAKE) --build $(WASM_BUILD_DIR) --parallel --target formulon_wasm
@echo ""
@echo "wasm artifacts:"
@ls -la $(WASM_BUILD_DIR)/formulon.wasm $(WASM_BUILD_DIR)/formulon.js 2>/dev/null || \
echo " (artifacts not found; check build log above)"
wasm-debug:
@if ! command -v emcmake >/dev/null 2>&1; then \
echo "wasm-debug: emscripten toolchain not found in PATH"; \
exit 1; \
fi
$(EM_CMAKE) -B $(WASM_DEBUG_BUILD_DIR) -DCMAKE_BUILD_TYPE=Debug \
-DFM_BUILD_WASM=ON -DFM_BUILD_TESTING=OFF -DFM_BUILD_CLI=OFF
$(CMAKE) --build $(WASM_DEBUG_BUILD_DIR) --parallel --target formulon_wasm
# capi WASM: standalone reactor build consumed by wasmtime-py. No JS
# glue, no pthread, exports the curated fm_* list from
# tools/wasm/capi_exports.txt. Drives the PyPI distribution.
wasm-capi:
@if ! command -v emcmake >/dev/null 2>&1; then \
echo "wasm-capi: emscripten toolchain not found in PATH"; \
exit 1; \
fi
$(EM_CMAKE) -B $(WASM_CAPI_BUILD_DIR) -DCMAKE_BUILD_TYPE=Release \
-DFM_BUILD_WASM=ON -DFM_WASM_VARIANT=capi \
-DFM_BUILD_TESTING=OFF -DFM_BUILD_CLI=OFF
$(CMAKE) --build $(WASM_CAPI_BUILD_DIR) --parallel --target formulon_wasm
@echo ""
@echo "wasm-capi artifact:"
@ls -la $(WASM_CAPI_BUILD_DIR)/formulon_capi.wasm 2>/dev/null || \
echo " (artifact not found; check build log above)"
test-wasm:
@if [ ! -f $(WASM_BUILD_DIR)/formulon.js ]; then \
echo "test-wasm: $(WASM_BUILD_DIR)/formulon.js missing; run 'make wasm' first"; \
exit 1; \
fi
@if ! command -v $(NODE) >/dev/null 2>&1; then \
echo "test-wasm: '$(NODE)' not found"; \
exit 1; \
fi
$(NODE) tests/wasm/run.mjs
# -- npm packaging targets ------------------------------------------------
# `make npm-package` -> stage build-wasm/formulon.{js,wasm} + the
# hand-written .d.ts into packages/npm/dist/.
# `make npm-test` -> run node:test smoke tests against the staged
# package (catches staging mistakes that running
# against build-wasm/ would mask).
# `make npm-pack` -> produce a publishable .tgz under build-wasm/.
#
# None of these run automatically as part of `make build` / `make test`.
# Publishing is a manual, out-of-band step.
NPM_PKG_DIR := packages/npm
npm-package: wasm
@if ! command -v $(NODE) >/dev/null 2>&1; then \
echo "npm-package: '$(NODE)' not found"; \
exit 1; \
fi
$(NODE) $(NPM_PKG_DIR)/scripts/stage.mjs \
--build-dir $(WASM_BUILD_DIR) \
--out-dir $(NPM_PKG_DIR)/dist
# `make npm-check-dts` -> fail if the staged dist/formulon.d.ts has
# drifted from the canonical src/wasm/formulon.d.ts.
# Needs no WASM build, so it is cheap to run in CI and locally.
npm-check-dts:
@if ! command -v $(NODE) >/dev/null 2>&1; then \
echo "npm-check-dts: '$(NODE)' not found"; \
exit 1; \
fi
$(NODE) $(NPM_PKG_DIR)/scripts/check-dts.mjs
npm-test: npm-package npm-check-dts
@if ! command -v $(NODE) >/dev/null 2>&1; then \
echo "npm-test: '$(NODE)' not found"; \
exit 1; \
fi
(cd $(NPM_PKG_DIR) && $(NODE) --test 'test/*.test.mjs')
npm-pack: npm-package
@if ! command -v npm >/dev/null 2>&1; then \
echo "npm-pack: npm not found in PATH"; \
exit 1; \
fi
(cd $(NPM_PKG_DIR) && npm pack --pack-destination ../../$(WASM_BUILD_DIR)/)
@echo ""
@echo "npm tarball:"
@ls -la $(WASM_BUILD_DIR)/libraz-formulon-*.tgz 2>/dev/null | tail -1 || \
echo " (tarball not found; check log above)"
# -- Python packaging targets --------------------------------------------
# `make python-package` -> stage build-wasm-capi/formulon_capi.wasm into
# packages/python/formulon/_wasm/.
# `make python-test` -> run the unittest smoke suite against the
# staged source tree (catches stage / packaging
# errors).
# `make python-wheel` -> produce build-py/dist/formulon-*-py3-none-any.whl.
#
# The Python distribution is pure-Python with a bundled WebAssembly
# module. The WASM is built by `make wasm-capi` (Emscripten toolchain
# required); the wheel itself is py3-none-any and works on every OS
# and arch wasmtime supports.
PY_PKG_DIR := packages/python
PY_BUILD_DIR ?= build-py
PYTHON ?= $(shell python3 -c 'import sys; print(sys.executable)')
python-package: wasm-capi
@if ! command -v $(PYTHON) >/dev/null 2>&1; then \
echo "python-package: python3 not found in PATH"; \
exit 1; \
fi
$(PYTHON) $(PY_PKG_DIR)/scripts/stage.py \
--build-dir $(WASM_CAPI_BUILD_DIR)
python-test: python-package
@if ! $(PYTHON) -c "import wasmtime" >/dev/null 2>&1; then \
echo "python-test: wasmtime runtime missing; install with:"; \
echo " $(PYTHON) -m pip install wasmtime"; \
exit 1; \
fi
@(cd $(PY_PKG_DIR) && $(PYTHON) -m unittest discover -v tests)
python-wheel: python-package
@if ! $(PYTHON) -m pip --version >/dev/null 2>&1; then \
echo "python-wheel: pip not available for python3"; \
exit 1; \
fi
@if ! $(PYTHON) -c "import setuptools" >/dev/null 2>&1; then \
echo "python-wheel: Python package 'setuptools' not installed"; \
exit 1; \
fi
@if ! $(PYTHON) -c "import wheel" >/dev/null 2>&1; then \
echo "python-wheel: Python package 'wheel' not installed"; \
exit 1; \
fi
mkdir -p $(PY_BUILD_DIR)/dist
(cd $(PY_PKG_DIR) && $(PYTHON) setup.py bdist_wheel \
--dist-dir ../../$(PY_BUILD_DIR)/dist/)
@echo ""
@echo "python wheel:"
@ls -la $(PY_BUILD_DIR)/dist/formulon-*.whl 2>/dev/null | tail -1 || \
echo " (wheel not found; check log above)"
# -- Node native (N-API) packaging targets ----------------------------
# `make node-native` -> build formulon.node via FM_BUILD_NODE_ADDON=ON.
# `make node-package` -> stage build/bin/formulon.node + JS shim into
# packages/npm-native/dist/.
# `make node-test` -> node:test smoke against the staged package.
NODE_NATIVE_PKG_DIR := packages/npm-native
NODE_NATIVE_BUILD_DIR ?= build
node-native:
$(CMAKE) -B $(NODE_NATIVE_BUILD_DIR) -DCMAKE_BUILD_TYPE=Release -DFM_BUILD_NODE_ADDON=ON
$(CMAKE) --build $(NODE_NATIVE_BUILD_DIR) --target formulon_node --parallel
node-package: node-native
@if ! command -v $(NODE) >/dev/null 2>&1; then \
echo "node-package: '$(NODE)' not found"; exit 1; \
fi
$(NODE) $(NODE_NATIVE_PKG_DIR)/scripts/stage.mjs \
--build-dir $(NODE_NATIVE_BUILD_DIR) \
--out-dir $(NODE_NATIVE_PKG_DIR)/dist
node-test: node-package
(cd $(NODE_NATIVE_PKG_DIR) && $(NODE) --test 'test/*.test.mjs')
# -- Cross-channel parity gate ------------------------------------------------
# `make parity-test` -> evaluate fixtures.json on every available channel
# (CLI / npm / Python wheel) and assert all channels
# agree at %.15g-canonicalized IEEE-754 bit
# granularity. Fewer than two active channels exits
# with CTest's conventional skip code (77); otherwise
# any disagreement, failed channel, or unmet fixture
# expectation fails (exit 1).
#
# Intentionally has no make-level dependency on `npm-package`, `python-package`,
# or the native build: the runner itself decides which channels to exercise
# based on what is on disk. Driving the prerequisite builds is a CI-side
# concern.
parity-test:
@command -v python3 >/dev/null 2>&1 || { \
echo "parity-test: python3 not found"; exit 1; }
python3 tests/parity/run_parity.py
# Alias kept for backward compatibility with `make test-python`.
test-python: python-test
# Standalone .wasm size report. Reads the artifact built by `make wasm`
# and gates against the milestone size ceiling. See
# tools/bench/wasm_size_report.sh and CLAUDE.md "WASM Size Policy".
size-check:
@sh tools/bench/wasm_size_report.sh $(WASM_BUILD_DIR)/formulon.wasm
# -- Oracle targets --------------------------------------------------------
# Drive Mac Excel 365 to generate golden JSON (oracle-gen), and verify
# Formulon's output against committed goldens (oracle-verify). The
# generator is macOS-only; the verifier runs on any platform that can
# build the test binary because it only reads committed JSON.
#
# Python tooling is managed by rye (https://rye.astral.sh/):
# tools/oracle/pyproject.toml project + deps
# tools/oracle/requirements.lock pinned versions (committed)
# tools/oracle/.venv/ local venv (gitignored)
#
# See tools/oracle/README.md.
ORACLE_DIR := tools/oracle
ORACLE_VENV := $(ORACLE_DIR)/.venv
ORACLE_PY := $(ORACLE_VENV)/bin/python
ORACLE_GEN := $(ORACLE_PY) tools/oracle/oracle_gen.py
# All `oracle-gen*` targets below run an M365 sentinel at startup and
# abort with a clear error on Office 2019 / pre-M365 Excel. The check is
# implemented by OracleDriver.assert_m365_or_abort (formula + workbook
# tracks) and a parallel open-coded probe in cf_oracle_gen (CF track).
# Shared venv guard for the oracle-gen* targets. Each target prefixes
# this with `@$(call require_oracle_venv,<target-name>)` so the error
# message names the actual target the user invoked.
define require_oracle_venv
if [ ! -x "$(ORACLE_PY)" ]; then \
echo "$(1): run 'make oracle-setup' first"; exit 1; \
fi
endef
oracle-setup:
@uname_s=$$(uname -s); \
if [ "$$uname_s" = "Darwin" ]; then \
$(MAKE) oracle-setup-mac; \
elif [ "$$uname_s" = "Linux" ] && grep -qi microsoft /proc/version 2>/dev/null; then \
$(MAKE) oracle-setup-wsl; \
else \
echo "oracle-setup: unsupported host $$uname_s"; \
echo " supported: macOS (Darwin) for primary, WSL2 (Linux+Microsoft) for windows variant"; \
exit 1; \
fi
oracle-setup-mac:
@if ! command -v rye >/dev/null 2>&1; then \
echo "oracle-setup-mac: rye not found in PATH."; \
echo " Install from https://rye.astral.sh/ (curl -sSf https://rye.astral.sh/get | bash)"; \
echo " or: brew install rye"; \
exit 1; \
fi
@(cd $(ORACLE_DIR) && rye sync)
@echo "oracle-setup-mac: venv ready at $(ORACLE_VENV)"
@echo "Grant Automation permission: System Settings -> Privacy & Security"
@echo " -> Automation -> (your terminal) -> Microsoft Excel"
@$(ORACLE_VENV)/bin/python tools/oracle/cli.py setup --target mac-365-ja_JP || true
oracle-setup-wsl:
@if ! command -v rye >/dev/null 2>&1; then \
echo "oracle-setup-wsl: rye not found. Install: curl -sSf https://rye.astral.sh/get | bash"; \
exit 1; \
fi
@(cd $(ORACLE_DIR) && rye sync)
@echo "oracle-setup-wsl: venv ready at $(ORACLE_VENV)"
@echo ""
@echo "Next steps for the Windows side (run in PowerShell, one-time):"
@echo " winget install Python.Python.3.12"
@echo " py -m pip install xlwings pywin32 pyyaml"
@echo ""
@echo "Then point the bridge at the Windows-side python.exe (preferred):"
@echo " export FORMULON_WIN_PYTHON=\"/mnt/c/Users/<you>/AppData/Local/Programs/Python/Python312/python.exe\""
@echo " # the cli.py setup step below auto-discovers candidates and prints"
@echo " # the exact export line; copy that into your shell rc."
@echo " # (Editing tools/oracle/targets.yaml is only for private forks — the"
@echo " # env var keeps per-machine paths out of committed files.)"
@$(ORACLE_VENV)/bin/python tools/oracle/cli.py setup --target win-365-ja_JP || true
oracle-gen:
@$(call require_oracle_venv,oracle-gen)
@$(ORACLE_GEN) $(if $(SUITE),--suite $(SUITE),) $(if $(TARGET),--target $(TARGET),)
# CF (conditional-formatting) track. macOS-only; see cf_oracle_gen.py for
# the supported rule subset.
oracle-gen-cf:
@$(call require_oracle_venv,oracle-gen-cf)
@$(ORACLE_PY) tools/oracle/cf_oracle_gen.py $(if $(SUITE),--suite $(SUITE),)
# Workbook track (pivot tables + print areas). Target is auto-detected
# from the host OS; pass TARGET=<name> to override.
oracle-gen-workbook:
@$(call require_oracle_venv,oracle-gen-workbook)
@$(ORACLE_PY) tools/oracle/cli.py workbook \
$(if $(TARGET),--target $(TARGET),) $(if $(SUITE),--suite $(SUITE),)
# oracle-verify shells to `ctest -L oracle`, which already selects the
# workbook oracle binary: formulon_workbook_oracle_tests carries the
# "oracle" CTest label, so the workbook track is verified alongside the
# formula and CF tracks with no extra selector.
oracle-verify:
@if [ ! -f $(BUILD_DIR)/CMakeCache.txt ]; then \
echo "oracle-verify: run 'make build' first"; exit 1; \
fi
@$(CMAKE) --build $(BUILD_DIR) --target formulon_oracle_tests --parallel
@$(CMAKE) --build $(BUILD_DIR) --target formulon_workbook_oracle_tests --parallel
@# gtest_discover_tests caches the parameter list keyed by binary
@# timestamp, but our goldens aren't a build input. Force
@# rediscovery so newly regenerated golden*/*.golden.json files land
@# in the ctest test list on the next run -- for both the formula
@# track (golden/) and the workbook track (golden_wb/).
@rm -f $(BUILD_DIR)/tests/oracle/formulon_oracle_tests*_tests.cmake
@rm -f $(BUILD_DIR)/tests/oracle/formulon_workbook_oracle_tests*_tests.cmake
@(cd $(BUILD_DIR) && $(CTEST) -L oracle --output-on-failure --timeout 60)
# One-command contributor onramp. Runs oracle-setup when the venv is
# missing, then dispatches to cli.py contribute, which probes Excel for
# version + locale, picks (or maps) the target, skips generation if the
# committed golden already covers the contributor's Excel build, and
# otherwise runs preflight + oracle_gen + push instructions.
#
# Override the auto-detected target with TARGET=<name> when needed.
oracle-contribute:
@if [ ! -x "$(ORACLE_PY)" ]; then \
echo "oracle-contribute: venv missing -- bootstrapping via oracle-setup..."; \
$(MAKE) oracle-setup; \
fi
@$(ORACLE_PY) tools/oracle/cli.py contribute $(if $(TARGET),--target $(TARGET),)
# Lists every contribution target known to `tools/oracle/targets.yaml`
# along with the current host's runnable subset.
oracle-contribute-list:
@if [ ! -x "$(ORACLE_PY)" ]; then \
echo "oracle-contribute-list: venv missing -- bootstrapping via oracle-setup..."; \
$(MAKE) oracle-setup; \
fi
@$(ORACLE_PY) tools/oracle/cli.py list
# -- IronCalc secondary oracle --------------------------------------------
# Imports xlsx fixtures vendored from IronCalc (dual MIT / Apache-2.0)
# into Formulon's golden JSON schema, then runs the secondary verifier
# registered under the `ironcalc` CTest label.
ironcalc-import:
@if [ ! -x "$(ORACLE_PY)" ]; then \
echo "ironcalc-import: run 'make oracle-setup' first (or 'cd tools/oracle && rye sync')"; \
exit 1; \
fi
@$(ORACLE_PY) tools/oracle/ironcalc_import.py
ironcalc-verify:
@if [ ! -f $(BUILD_DIR)/CMakeCache.txt ]; then \
echo "ironcalc-verify: run 'make build' first"; exit 1; \
fi
@$(CMAKE) --build $(BUILD_DIR) --target formulon_ironcalc_oracle_tests --parallel
@# Force ctest to rediscover parameter list; the goldens aren't a
@# build input so the cache would otherwise stick.
@rm -f $(BUILD_DIR)/tests/oracle/formulon_ironcalc_oracle_tests*_tests.cmake
@(cd $(BUILD_DIR) && $(CTEST) -L ironcalc --output-on-failure --timeout 60)
fuzz-parser:
@echo "fuzz-parser: not yet implemented (planned for M8)"
@exit 0
fuzz-xlsx:
@echo "fuzz-xlsx: not yet implemented (planned for M8)"
@exit 0
fuzz-eval:
@echo "fuzz-eval: not yet implemented (planned for M8)"
@exit 0
bench:
@echo "bench: not yet implemented (planned for M9)"
@exit 0
# Local coverage diagnostic. Builds with the gcov-instrumented preset,
# runs fast tests, and prints a per-area report plus the list of files
# the fast suite never touches. Targets in the report (util/value/eval
# 95% / functions 98% / io 90%) are aspirational; this is *not* a CI
# gate. Set FORMULON_COV_STRICT=1 for ad-hoc local enforcement.
# Driver: tools/dev/run_coverage.sh.
coverage:
bash tools/dev/run_coverage.sh
# Mutation testing report. mull-runner-cxx mutates the C++ source under
# the configured filter, runs the test binary against each mutant, and
# reports the kill rate. Local diagnostic only. CI does NOT gate on
# mutation score; thresholds drift with refactors.
# Set FORMULON_MUT_STRICT=1 for ad-hoc local enforcement (~70% target).
# Driver: tools/dev/run_mutation.sh.
mutation:
bash tools/dev/run_mutation.sh
# Function implementation coverage report. Scans src/eval/ for registered
# and lazy-dispatched function names and diffs them against the canonical
# catalog at tools/catalog/functions.txt. No build step required.
function-status:
@python3 tools/catalog/status.py
# Behaviour-vocabulary report. Parallels function-status but operates on
# function sub-behaviours (TEXT format codes, DATEVALUE era strings, CP932
# CHAR / CODE ranges, SEARCH / FIND wildcards). See
# tools/catalog/behaviors.yaml.
behavior-status:
@python3 tools/catalog/behaviors.py --report