From 28558b434333b67437e60377ee4db9acc868d426 Mon Sep 17 00:00:00 2001 From: ml Date: Sun, 5 Jul 2026 16:35:18 +0200 Subject: [PATCH] Consolidate build into one root Makefile; split run.sh into run-engine.sh/run-interpreter.sh --- .gitignore | 3 +- CLAUDE.md | 57 +++++++----- Makefile | 114 +++++++++++++++++++++++ README.md | 39 ++++---- docs/architecture.md | 29 +++--- docs/input-output-format.md | 25 +++-- engine/Makefile | 74 --------------- engine/scripts/{run.sh => run-engine.sh} | 7 +- engine/scripts/run-interpreter.sh | 84 +++++++++++++++++ engine/scripts/user.properties.template | 3 +- engine/tests/smoke_test.c | 10 +- interpreter/Makefile | 54 ----------- 12 files changed, 300 insertions(+), 199 deletions(-) create mode 100644 Makefile delete mode 100644 engine/Makefile rename engine/scripts/{run.sh => run-engine.sh} (89%) create mode 100644 engine/scripts/run-interpreter.sh delete mode 100644 interpreter/Makefile diff --git a/.gitignore b/.gitignore index 340ac32..73186ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ # Build output, including dist/user.properties which holds real birth # data once filled in — never commit it. /dist -/engine/build -/interpreter/build +/build *.o core diff --git a/CLAUDE.md b/CLAUDE.md index bb4133c..aac73d0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,15 +16,17 @@ built so it can be linked straight into it later without rework (see ## Build & run ```bash -cd engine -make # builds dist/deck-engine, dist/img/, dist/i18n/, dist/run.sh, dist/user.properties -make test # builds and runs engine/tests/smoke_test.c -make clean # removes build/ and the generated binary/img/i18n (never touches dist/user.properties) +make # builds dist/{deck-engine,interpreter-cli}, dist/img/, dist/i18n/, + # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties +make test # builds and runs engine/tests/smoke_test.c and interpreter/tests/*_test.c +make clean # removes build/ and the generated binaries/img/i18n (never touches dist/user.properties) ``` -Build output goes to `dist/`, a sibling of `engine/` and `res/` (not -inside `engine/`). There is no `make install`; `dist/` is meant to be run -in place. +A single `Makefile` at the repo root builds both `engine/` and +`interpreter/` — there is no per-module Makefile, and no `cd` needed +before running `make`. Build output goes to `dist/`, a sibling of +`engine/`, `interpreter/`, and `res/`. There is no `make install`; +`dist/` is meant to be run in place. Two ways to run a reading: @@ -35,17 +37,22 @@ dist/deck-engine --seed "2026-07-03" \ --birth-lat 52.5200 --birth-lon 13.4050 \ [--date YYYY-MM-DDTHH:MM] [--format text|html|json] [--lang en|de] [--i18n-dir ] -# 2. dist/run.sh [text|html] [lang] — wraps the binary for daily use: seed -# and --date come from the OS clock (today's date / current UTC time), -# birth data + default language are read from dist/user.properties -# next to the script; the optional [lang] arg overrides that language -# for a single run. +# 2. dist/run-engine.sh [text|html|json] [lang] — wraps the binary for +# daily use: seed and --date come from the OS clock (today's date / +# current UTC time), birth data + default language are read from +# dist/user.properties next to the script; the optional [lang] arg +# overrides that language for a single run. ``` `dist/user.properties` is seeded once from `engine/scripts/user.properties.template` with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never -overwritten by later builds** — `run.sh` refuses to run (with a clear -error) until the placeholders are replaced with real values. +overwritten by later builds** — `run-engine.sh`/`run-interpreter.sh` +refuse to run (with a clear error) until the placeholders are replaced +with real values. `dist/run-interpreter.sh` is the equivalent daily-use +wrapper for the interpreter: it runs `run-engine.sh`'s same OS-clock +seed/date and `user.properties` birth data through `deck-engine --format +json`, piped straight into `dist/interpreter-cli` — see "Interpretation" +below. Run a single smoke test by editing `engine/tests/smoke_test.c`'s `main()` temporarily, or just read its assertions — there's no test filter flag, @@ -74,10 +81,10 @@ deck_in_a_dash/ main.c CLI entry point. i18n/en.lang, i18n/de.lang Translation source files, one per language. tests/smoke_test.c - scripts/run.sh, scripts/user.properties.template - Makefile + scripts/run-engine.sh, scripts/run-interpreter.sh, scripts/user.properties.template interpreter/ Separate module + binary; see "Interpretation" below. dist/ Build output (gitignored-style; see Build & run). + Makefile Single root Makefile, builds both engine/ and interpreter/. ``` ### Data flow / the real API @@ -193,14 +200,19 @@ deliberately unchanged by this beyond gaining `--format json`; the two binaries compose over that JSON, never by linking together: ```bash -cd engine && make # -> dist/deck-engine (unchanged; --format json is new) -cd interpreter && make # -> dist/interpreter-cli -cd interpreter && make test # builds and runs both interpreter/tests/*_test.c +make # -> dist/deck-engine (unchanged; --format json is new) and dist/interpreter-cli +make test # builds and runs both engine/tests/*_test.c and interpreter/tests/*_test.c dist/deck-engine ... --format json | dist/interpreter-cli # or: dist/deck-engine ... --format json > reading.json && dist/interpreter-cli reading.json +# or, for daily use (OS clock + user.properties, same as run-engine.sh): +dist/run-interpreter.sh ``` +The root `Makefile` builds both from one invocation, but keeps them as +separate compilation units throughout — no object file is ever shared +between the two binaries (see the next bullet). + - `interpret_daily_reading(reading, &out)` (`significance.h/.c`) scores every aspect in `reading->transits.aspects[]`, keeps the **top 5** by score in `DailyInterpretation.top_items[]` (descending, evicting the @@ -237,8 +249,9 @@ dist/deck-engine ... --format json | dist/interpreter-cli today"), makes the whole load fail rather than silently dropping data. - **Deliberately header-only dependency on the engine, throughout**: `significance.h`/`reading_io.h` `#include engine/src/reading.h` for the - struct/enum *definitions*, but `interpreter/Makefile` never compiles or - links any engine `.c` file (or the vendored Astronomy Engine) — every + struct/enum *definitions*, but the root `Makefile` never compiles or + links any engine `.c` file (or the vendored Astronomy Engine) into the + interpreter's binary or tests — every test in `interpreter/tests/` runs against hand-built JSON text or hand-built `DailyReading` values, with no real ephemeris/tarot-draw call involved anywhere. Consequently `reading_io.c` (planet/aspect @@ -251,7 +264,7 @@ dist/deck-engine ... --format json | dist/interpreter-cli ## Licensing -- Engine code (`engine/src/`, `engine/scripts/`, `engine/Makefile`): +- Engine code (`engine/src/`, `engine/scripts/`) and the root `Makefile`: MIT, per the repo's top-level `LICENSE`. - `engine/third_party/astronomy/`: vendored MIT code, unmodified — see `VENDORED.md` for the pinned upstream commit. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38d6878 --- /dev/null +++ b/Makefile @@ -0,0 +1,114 @@ +CC ?= cc +CFLAGS ?= -std=c99 -Wall -Wextra -O2 +LDLIBS := -lm + +BUILD_DIR := build +OBJ_DIR := $(BUILD_DIR)/obj +DIST_DIR := dist +DIST_IMG_DIR := $(DIST_DIR)/img +DIST_I18N_DIR := $(DIST_DIR)/i18n + +# ---- engine ---- + +ENGINE_SRC_DIR := engine/src +ENGINE_THIRD_PARTY := engine/third_party/astronomy/astronomy.c +ENGINE_TEST_DIR := engine/tests +ENGINE_I18N_DIR := engine/i18n +ENGINE_SCRIPTS_DIR := engine/scripts +RES_IMG_DIR := res/img + +# Engine logic shared by the CLI and the test binary (no main()). +ENGINE_SRCS := $(ENGINE_SRC_DIR)/rng.c $(ENGINE_SRC_DIR)/tarot.c $(ENGINE_SRC_DIR)/tarot_data.c \ + $(ENGINE_SRC_DIR)/astro.c $(ENGINE_SRC_DIR)/reading.c $(ENGINE_SRC_DIR)/i18n.c \ + $(ENGINE_THIRD_PARTY) +ENGINE_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(ENGINE_SRCS)) + +ENGINE_MAIN_OBJ := $(OBJ_DIR)/$(ENGINE_SRC_DIR)/main.o +ENGINE_TEST_OBJ := $(OBJ_DIR)/$(ENGINE_TEST_DIR)/smoke_test.o + +ENGINE_BINARY := $(DIST_DIR)/deck-engine +ENGINE_TEST_BINARY := $(BUILD_DIR)/smoke_test + +# ---- interpreter ---- + +# Only ever compiles this module's own sources - significance.h/ +# reading_io.h reach into engine/src/ for struct/enum *definitions* +# (plain #includes), but no engine/src/*.c (or the vendored Astronomy +# Engine) is compiled or linked here. That's what keeps this module (and +# its tests) independent of the engine's actual ephemeris/tarot-draw +# implementation - see significance.c's and reading_io.c's own comments +# for why each duplicates a small table instead of linking astro.c. +INTERP_SRC_DIR := interpreter/src +INTERP_TEST_DIR := interpreter/tests + +INTERP_LIB_SRCS := $(INTERP_SRC_DIR)/significance.c $(INTERP_SRC_DIR)/json.c $(INTERP_SRC_DIR)/reading_io.c +INTERP_LIB_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(INTERP_LIB_SRCS)) +INTERP_MAIN_OBJ := $(OBJ_DIR)/$(INTERP_SRC_DIR)/main.o + +INTERP_BINARY := $(DIST_DIR)/interpreter-cli + +SIGNIFICANCE_TEST_OBJ := $(OBJ_DIR)/$(INTERP_TEST_DIR)/significance_test.o +JSON_TEST_OBJ := $(OBJ_DIR)/$(INTERP_TEST_DIR)/json_test.o +SIGNIFICANCE_TEST_BIN := $(BUILD_DIR)/significance_test +JSON_TEST_BIN := $(BUILD_DIR)/json_test + +.PHONY: all test clean images i18n scripts + +all: $(ENGINE_BINARY) $(INTERP_BINARY) images i18n scripts + +$(ENGINE_BINARY): $(ENGINE_OBJS) $(ENGINE_MAIN_OBJ) + @mkdir -p $(DIST_DIR) + $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) + +$(INTERP_BINARY): $(INTERP_LIB_OBJS) $(INTERP_MAIN_OBJ) + @mkdir -p $(DIST_DIR) + $(CC) $(CFLAGS) -o $@ $^ + +# The HTML report expects card art at img/ next to deck-engine, so +# every build refreshes a copy alongside it (cheap: 22 small JPEGs). +images: + @mkdir -p $(DIST_IMG_DIR) + cp $(RES_IMG_DIR)/*.jpeg $(DIST_IMG_DIR)/ + +# deck-engine expects translation files at i18n/.lang next to it +# (default --i18n-dir), so every build refreshes a copy alongside it, +# same as images/. Add a new language by dropping another .lang +# file into engine/i18n/ - no code or Makefile change needed. +i18n: + @mkdir -p $(DIST_I18N_DIR) + cp $(ENGINE_I18N_DIR)/*.lang $(DIST_I18N_DIR)/ + +# run-engine.sh/run-interpreter.sh are refreshed every build. +# user.properties is only seeded once (from the placeholder template) so +# a rebuild never clobbers the user's own filled-in birth data. +scripts: + @mkdir -p $(DIST_DIR) + cp $(ENGINE_SCRIPTS_DIR)/run-engine.sh $(DIST_DIR)/run-engine.sh + chmod +x $(DIST_DIR)/run-engine.sh + cp $(ENGINE_SCRIPTS_DIR)/run-interpreter.sh $(DIST_DIR)/run-interpreter.sh + chmod +x $(DIST_DIR)/run-interpreter.sh + test -f $(DIST_DIR)/user.properties || \ + cp $(ENGINE_SCRIPTS_DIR)/user.properties.template $(DIST_DIR)/user.properties + +test: $(ENGINE_TEST_BINARY) $(SIGNIFICANCE_TEST_BIN) $(JSON_TEST_BIN) + ./$(ENGINE_TEST_BINARY) + ./$(SIGNIFICANCE_TEST_BIN) + ./$(JSON_TEST_BIN) + +$(ENGINE_TEST_BINARY): $(ENGINE_OBJS) $(ENGINE_TEST_OBJ) + $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) + +$(SIGNIFICANCE_TEST_BIN): $(OBJ_DIR)/$(INTERP_SRC_DIR)/significance.o $(SIGNIFICANCE_TEST_OBJ) + $(CC) $(CFLAGS) -o $@ $^ + +$(JSON_TEST_BIN): $(OBJ_DIR)/$(INTERP_SRC_DIR)/json.o $(OBJ_DIR)/$(INTERP_SRC_DIR)/reading_io.o $(JSON_TEST_OBJ) + $(CC) $(CFLAGS) -o $@ $^ + +$(OBJ_DIR)/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -rf $(BUILD_DIR) $(ENGINE_BINARY) $(INTERP_BINARY) $(DIST_IMG_DIR) $(DIST_I18N_DIR) \ + $(DIST_DIR)/run-engine.sh $(DIST_DIR)/run-interpreter.sh + # user.properties holds the user's own data - never removed by clean. diff --git a/README.md b/README.md index f8c12f1..518dd3b 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,15 @@ engine/ src/ Engine source (rng, tarot, astro, i18n, reading, CLI). i18n/ Translation files (en.lang, de.lang, ...). tests/smoke_test.c Determinism, ephemeris, and i18n regression checks. - scripts/ run.sh and the user.properties template. - Makefile + scripts/ run-engine.sh, run-interpreter.sh, and + the user.properties template. dist/ Build output (gitignored) — see below. interpreter/ Separate module + binary (dist/interpreter-cli): scores how significant a day's transits are, reading deck-engine's --format json output (see CLAUDE.md). docs/ Architecture/dataflow diagrams, input/output format reference. +Makefile Single root Makefile, builds both engine/ and interpreter/. ``` See [`CLAUDE.md`](CLAUDE.md) for the detailed architecture and the sharp @@ -61,25 +62,25 @@ struct format reference ## Building ```bash -cd engine -make # -> dist/deck-engine, dist/img/, dist/i18n/, dist/run.sh, dist/user.properties -make test # builds and runs engine/tests/smoke_test.c +make # -> dist/deck-engine, dist/interpreter-cli, dist/img/, dist/i18n/, + # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties +make test # builds and runs engine/tests/smoke_test.c and interpreter/tests/*_test.c ``` Requires only a C99 compiler and `make` — no other dependencies. ## Running -**Daily use**, via `dist/run.sh`: pulls today's date and the current UTC -time from the OS clock (used as the tarot seed and the transit moment) -and reads your birth data from `dist/user.properties`. Edit that file -first — it's seeded once from a placeholder template and is never -overwritten by later builds: +**Daily use**, via `dist/run-engine.sh`: pulls today's date and the +current UTC time from the OS clock (used as the tarot seed and the +transit moment) and reads your birth data from `dist/user.properties`. +Edit that file first — it's seeded once from a placeholder template and +is never overwritten by later builds: ```bash -dist/run.sh # text output, language from user.properties (default en) -dist/run.sh html # HTML report with card art, open in a browser -dist/run.sh html de # override the language for this run +dist/run-engine.sh # text output, language from user.properties (default en) +dist/run-engine.sh html # HTML report with card art, open in a browser +dist/run-engine.sh html de # override the language for this run ``` **Direct CLI**, with every input explicit: @@ -91,19 +92,23 @@ dist/deck-engine --seed "2026-07-03" \ [--date YYYY-MM-DDTHH:MM] [--format text|html|json] [--lang en|de] ``` -**Interpreter** (`interpreter/`, built separately with `cd interpreter && make`): +**Interpreter** (`interpreter/`, built by the same root `make`): scores how significant a day's transits are, reading `deck-engine`'s `--format json` output — the two binaries compose over that JSON, they're -never linked together: +never linked together. `dist/run-interpreter.sh` is the daily-use +equivalent of `run-engine.sh` (same OS clock + `user.properties` inputs), +piping straight into `interpreter-cli`: ```bash dist/deck-engine ... --format json | dist/interpreter-cli +# or, for daily use: +dist/run-interpreter.sh ``` ## License -The engine source code in this repository (`engine/src/`, `engine/scripts/`, -`engine/Makefile`) is released under the **MIT License** — see +The engine source code in this repository (`engine/src/`, `engine/scripts/`) +and the root `Makefile` are released under the **MIT License** — see [`LICENSE`](LICENSE). `engine/third_party/astronomy/` is vendored from diff --git a/docs/architecture.md b/docs/architecture.md index 612b211..ae0b46f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -33,10 +33,11 @@ know before changing the engine. This document is the visual overview. - **`engine/i18n/`** holds the translation source files (`en.lang`, `de.lang`, ...), one `key=value` file per language — see [`CLAUDE.md`](../CLAUDE.md) for the key-naming convention. -- **`dist/`** is generated by `make`: the `deck-engine` binary, a copy of - the card art under `img/`, a copy of the translation files under - `i18n/`, `run.sh`, and a `user.properties` template that's seeded once - and never overwritten by later builds. +- **`dist/`** is generated by the single root `Makefile`: the + `deck-engine` and `interpreter-cli` binaries, a copy of the card art + under `img/`, a copy of the translation files under `i18n/`, + `run-engine.sh`/`run-interpreter.sh`, and a `user.properties` template + that's seeded once and never overwritten by later builds. - **The Pebble watchapp does not exist yet.** When it's built, it will call `reading_generate()` directly and walk the returned struct to lay out its own screens — it has no reason to touch `reading_print_*`. @@ -70,15 +71,19 @@ Every display string produced along the way (`tarot_data.c`'s card and position text, `astro.c`'s body/sign/moon-phase/aspect names, and `reading.c`'s section headers) is looked up in `i18n.c`'s translation catalog, with the built-in English text as the fallback. `main.c` loads -the catalog from `--lang`/`--i18n-dir` (or `run.sh`'s `lang=` property) -before calling `reading_generate`, so this is orthogonal to the three -computations above — it only affects how their results are rendered as -text, not the underlying `TarotCard`/`ZodiacSign`/etc. enum values. +the catalog from `--lang`/`--i18n-dir` (or `run-engine.sh`'s `lang=` +property) before calling `reading_generate`, so this is orthogonal to +the three computations above — it only affects how their results are +rendered as text, not the underlying `TarotCard`/`ZodiacSign`/etc. enum +values. -Both entry points (direct CLI flags, or `run.sh` pulling the seed/date -from the OS clock and birth data from `user.properties`) converge on the -same `main.c` argument parsing before calling `reading_generate` — there -is exactly one code path from parsed input to a reading. +Both entry points (direct CLI flags, or `run-engine.sh` pulling the +seed/date from the OS clock and birth data from `user.properties`) +converge on the same `main.c` argument parsing before calling +`reading_generate` — there is exactly one code path from parsed input to +a reading. `run-interpreter.sh` reuses those same OS-clock/`user.properties` +inputs, but calls `deck-engine --format json` and pipes the result into +`interpreter-cli` instead of printing a reading directly. ## Regenerating the diagrams diff --git a/docs/input-output-format.md b/docs/input-output-format.md index d0cb4b0..a1be0f8 100644 --- a/docs/input-output-format.md +++ b/docs/input-output-format.md @@ -1,8 +1,8 @@ # Input & output formats See [`architecture.md`](architecture.md) for how these fit together. -There are two ways to provide input (direct CLI flags, or `run.sh` + -`user.properties`) and three output formats (`text`, `html`, `json`); the +There are two ways to provide input (direct CLI flags, or `run-engine.sh` ++ `user.properties`) and three output formats (`text`, `html`, `json`); the `DailyReading` struct is the programmatic form all three are rendered from, and the one the future watchapp will consume directly. `json` is also `interpreter-cli`'s input format — see "The interpreter" at the @@ -34,9 +34,10 @@ deck-engine --seed --birth-date YYYY-MM-DD --birth-time HH:MM Birth latitude/longitude only affect the Ascendant/houses — planet signs are geocentric and location-independent. -### `run.sh` + `user.properties` +### `run-engine.sh` + `user.properties` -`dist/run.sh [text|html] [lang]` wraps the binary for daily use: +`dist/run-engine.sh [text|html|json] [lang]` wraps the binary for daily +use: - **Seed and `--date`** come from the OS clock: `date +%Y-%m-%d` (stable all day) and `date -u +%Y-%m-%dT%H:%M`. @@ -55,12 +56,16 @@ are geocentric and location-independent. The file is seeded once from `engine/scripts/user.properties.template` with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never - overwritten by later builds**. `run.sh` checks every birth field for an - empty value or a leftover `YOUR_` placeholder and refuses to run with a - clear error until the file is filled in; `lang` has no such check since - it always has a usable default (`en`). + overwritten by later builds**. `run-engine.sh` checks every birth field + for an empty value or a leftover `YOUR_` placeholder and refuses to run + with a clear error until the file is filled in; `lang` has no such + check since it always has a usable default (`en`). - The optional second argument overrides `lang=` for a single run without - editing the file, e.g. `dist/run.sh html de`. + editing the file, e.g. `dist/run-engine.sh html de`. +- `dist/run-interpreter.sh` (no arguments) is the equivalent wrapper for + the interpreter: same OS clock + `user.properties` inputs, but calls + `deck-engine --format json` and pipes it straight into + `dist/interpreter-cli` — see "The interpreter" below. ### Translations (`--lang`, `dist/i18n/`) @@ -226,6 +231,8 @@ dist/deck-engine ... --format json | dist/interpreter-cli # or: dist/deck-engine ... --format json > reading.json dist/interpreter-cli reading.json +# or, for daily use (OS clock + user.properties, same inputs as run-engine.sh): +dist/run-interpreter.sh ``` - **Input**: a file path argument, or stdin if no argument (or `-`) is diff --git a/engine/Makefile b/engine/Makefile deleted file mode 100644 index 74115dd..0000000 --- a/engine/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -CC ?= cc -CFLAGS ?= -std=c99 -Wall -Wextra -O2 -LDLIBS := -lm - -SRC_DIR := src -THIRD_PARTY := third_party/astronomy/astronomy.c -TEST_DIR := tests -BUILD_DIR := build -OBJ_DIR := $(BUILD_DIR)/obj - -# Final build output lives in dist/, next to (not inside) engine/ and res/. -DIST_DIR := ../dist -DIST_IMG_DIR := $(DIST_DIR)/img -DIST_I18N_DIR := $(DIST_DIR)/i18n -RES_IMG_DIR := ../res/img -I18N_DIR := i18n -SCRIPTS_DIR := scripts - -# Engine logic shared by the CLI and the test binary (no main()). -ENGINE_SRCS := $(SRC_DIR)/rng.c $(SRC_DIR)/tarot.c $(SRC_DIR)/tarot_data.c \ - $(SRC_DIR)/astro.c $(SRC_DIR)/reading.c $(SRC_DIR)/i18n.c $(THIRD_PARTY) -ENGINE_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(ENGINE_SRCS)) - -MAIN_OBJ := $(OBJ_DIR)/$(SRC_DIR)/main.o -TEST_OBJ := $(OBJ_DIR)/$(TEST_DIR)/smoke_test.o - -BINARY := $(DIST_DIR)/deck-engine -TEST_BINARY := $(BUILD_DIR)/smoke_test - -.PHONY: all test clean images scripts i18n - -all: $(BINARY) images scripts i18n - -$(BINARY): $(ENGINE_OBJS) $(MAIN_OBJ) - @mkdir -p $(DIST_DIR) - $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) - -# The HTML report expects card art at img/ next to the binary, so -# every build refreshes a copy alongside it (cheap: 22 small JPEGs). -images: - @mkdir -p $(DIST_IMG_DIR) - cp $(RES_IMG_DIR)/*.jpeg $(DIST_IMG_DIR)/ - -# The CLI expects translation files at i18n/.lang next to the -# binary (default --i18n-dir), so every build refreshes a copy alongside -# it, same as images/. Add a new language by dropping another .lang -# file into engine/i18n/ - no code or Makefile change needed. -i18n: - @mkdir -p $(DIST_I18N_DIR) - cp $(I18N_DIR)/*.lang $(DIST_I18N_DIR)/ - -# run.sh is refreshed every build. user.properties is only seeded once -# (from the placeholder template) so a rebuild never clobbers the user's -# own filled-in birth data. -scripts: - @mkdir -p $(DIST_DIR) - cp $(SCRIPTS_DIR)/run.sh $(DIST_DIR)/run.sh - chmod +x $(DIST_DIR)/run.sh - test -f $(DIST_DIR)/user.properties || \ - cp $(SCRIPTS_DIR)/user.properties.template $(DIST_DIR)/user.properties - -test: $(TEST_BINARY) - ./$(TEST_BINARY) - -$(TEST_BINARY): $(ENGINE_OBJS) $(TEST_OBJ) - $(CC) $(CFLAGS) -o $@ $^ $(LDLIBS) - -$(OBJ_DIR)/%.o: %.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -rf $(BUILD_DIR) $(BINARY) $(DIST_IMG_DIR) $(DIST_I18N_DIR) $(DIST_DIR)/run.sh - # user.properties holds the user's own data - never removed by clean. diff --git a/engine/scripts/run.sh b/engine/scripts/run-engine.sh similarity index 89% rename from engine/scripts/run.sh rename to engine/scripts/run-engine.sh index f5514c1..ab331e2 100644 --- a/engine/scripts/run.sh +++ b/engine/scripts/run-engine.sh @@ -1,11 +1,12 @@ #!/bin/sh # Runs deck-engine for "today": the tarot seed and the transit moment come # from the OS clock, birth data comes from user.properties next to this -# script. Usage: ./run.sh [text|html] [lang] +# script. Usage: ./run-engine.sh [text|html|json] [lang] # # [lang] overrides user.properties' lang= for this run only (e.g. -# ./run.sh html de); omit it to use lang= from user.properties, which -# itself defaults to "en" if unset. +# ./run-engine.sh html de); omit it to use lang= from user.properties, +# which itself defaults to "en" if unset. See run-interpreter.sh for a +# significance report instead of the full reading. set -eu diff --git a/engine/scripts/run-interpreter.sh b/engine/scripts/run-interpreter.sh new file mode 100644 index 0000000..b659840 --- /dev/null +++ b/engine/scripts/run-interpreter.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# Runs deck-engine (--format json) piped straight into interpreter-cli for +# "today": same OS-clock seed/transit-moment and user.properties birth +# data as run-engine.sh, but reports how significant today's transits are +# instead of printing the full reading. Usage: ./run-interpreter.sh +# +# Requires both dist/deck-engine (built by engine/'s Makefile) and +# dist/interpreter-cli (built by interpreter/'s own Makefile) to already +# exist next to this script. + +set -eu + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ENGINE_BINARY="$SCRIPT_DIR/deck-engine" +INTERPRETER_BINARY="$SCRIPT_DIR/interpreter-cli" +PROPERTIES_FILE="$SCRIPT_DIR/user.properties" + +if [ ! -x "$ENGINE_BINARY" ]; then + echo "error: $ENGINE_BINARY not found or not executable (run 'make' in engine/ first)" >&2 + exit 1 +fi + +if [ ! -x "$INTERPRETER_BINARY" ]; then + echo "error: $INTERPRETER_BINARY not found or not executable (run 'make' in interpreter/ first)" >&2 + exit 1 +fi + +if [ ! -f "$PROPERTIES_FILE" ]; then + echo "error: $PROPERTIES_FILE not found" >&2 + exit 1 +fi + +birth_date="" +birth_time="" +birth_utc_offset="" +birth_lat="" +birth_lon="" + +while IFS='=' read -r key value || [ -n "$key" ]; do + key="$(printf '%s' "$key" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + value="$(printf '%s' "$value" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + case "$key" in + '' | '#'*) continue ;; + esac + case "$key" in + birth_date) birth_date="$value" ;; + birth_time) birth_time="$value" ;; + birth_utc_offset) birth_utc_offset="$value" ;; + birth_lat) birth_lat="$value" ;; + birth_lon) birth_lon="$value" ;; + esac +done <"$PROPERTIES_FILE" + +check_set() { + case "$2" in + '' | *YOUR_*) + echo "error: $PROPERTIES_FILE: '$1' is not set - edit the file and fill in your details." >&2 + exit 1 + ;; + esac +} +check_set birth_date "$birth_date" +check_set birth_time "$birth_time" +check_set birth_utc_offset "$birth_utc_offset" +check_set birth_lat "$birth_lat" +check_set birth_lon "$birth_lon" + +# OS-provided inputs: today's date is the tarot seed (stable all day, a +# new spread each day), current UTC time drives today's transits. The +# tarot seed doesn't matter for --format json (only "transits" is read +# downstream) but it's kept identical to run-engine.sh's for consistency. +seed="$(date +%Y-%m-%d)" +transit_date="$(date -u +%Y-%m-%dT%H:%M)" + +"$ENGINE_BINARY" \ + --seed "$seed" \ + --birth-date "$birth_date" \ + --birth-time "$birth_time" \ + --birth-utc-offset "$birth_utc_offset" \ + --birth-lat "$birth_lat" \ + --birth-lon "$birth_lon" \ + --date "$transit_date" \ + --format json \ + | exec "$INTERPRETER_BINARY" diff --git a/engine/scripts/user.properties.template b/engine/scripts/user.properties.template index 4834f39..4384567 100644 --- a/engine/scripts/user.properties.template +++ b/engine/scripts/user.properties.template @@ -1,6 +1,7 @@ # deck-in-a-dash user properties # -# Fill in your birth details below, then run ./run.sh (or ./run.sh html). +# Fill in your birth details below, then run ./run-engine.sh (or +# ./run-engine.sh html). # This file is only copied here once by the build - it will not be # overwritten by later builds, so it's safe to edit in place. # diff --git a/engine/tests/smoke_test.c b/engine/tests/smoke_test.c index 6d4cfee..d6da024 100644 --- a/engine/tests/smoke_test.c +++ b/engine/tests/smoke_test.c @@ -137,13 +137,13 @@ static void test_reading_print_json_shape(void) { printf("PASS test_reading_print_json_shape\n"); } -/* Run with cwd == engine/ (as `make test` does), so the shipped - * engine/i18n/ .lang files are reachable as i18n/.lang. */ +/* Run with cwd == repo root (as `make test` does), so the shipped + * engine/i18n/ .lang files are reachable at that path. */ static void test_i18n_fallback_and_translation(void) { /* No catalog loaded yet: every lookup returns the caller's fallback. */ assert(strcmp(i18n_get("does.not.exist", "fallback text"), "fallback text") == 0); - bool loaded = i18n_load("i18n/de.lang"); + bool loaded = i18n_load("engine/i18n/de.lang"); assert(loaded && "expected engine/i18n/de.lang to exist and load"); assert(strcmp(i18n_get("body.sun", "Sun"), "Sonne") == 0); assert(strcmp(i18n_get("does.not.exist", "fallback text"), "fallback text") == 0); @@ -158,11 +158,11 @@ static void test_i18n_fallback_and_translation(void) { /* Loading a file that doesn't exist fails and leaves the previously * loaded catalog in place, rather than silently clearing it. */ - assert(!i18n_load("i18n/does-not-exist.lang")); + assert(!i18n_load("engine/i18n/does-not-exist.lang")); assert(strcmp(i18n_get("body.sun", "Sun"), "Sonne") == 0); /* Loading a different language replaces the catalog wholesale. */ - loaded = i18n_load("i18n/en.lang"); + loaded = i18n_load("engine/i18n/en.lang"); assert(loaded); assert(strcmp(astro_body_name(PLANET_MOON), "Moon") == 0); diff --git a/interpreter/Makefile b/interpreter/Makefile deleted file mode 100644 index c4ab796..0000000 --- a/interpreter/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -CC ?= cc -CFLAGS ?= -std=c99 -Wall -Wextra -O2 - -SRC_DIR := src -TEST_DIR := tests -BUILD_DIR := build -OBJ_DIR := $(BUILD_DIR)/obj - -# Shares deck-engine's build output directory (see ../engine/Makefile), -# so a daily run can find both binaries in one place: -# dist/deck-engine ... --format json | dist/interpreter-cli -DIST_DIR := ../dist -BINARY := $(DIST_DIR)/interpreter-cli - -# Only ever compiles this module's own sources - significance.h/ -# reading_io.h reach into engine/src/ for struct/enum *definitions* -# (plain #includes), but no engine/src/*.c (or the vendored Astronomy -# Engine) is compiled or linked here. That's what keeps this module (and -# its tests) independent of the engine's actual ephemeris/tarot-draw -# implementation - see significance.c's and reading_io.c's own comments -# for why each duplicates a small table instead of linking astro.c. -LIB_SRCS := $(SRC_DIR)/significance.c $(SRC_DIR)/json.c $(SRC_DIR)/reading_io.c -LIB_OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(LIB_SRCS)) -MAIN_OBJ := $(OBJ_DIR)/$(SRC_DIR)/main.o - -SIGNIFICANCE_TEST_OBJ := $(OBJ_DIR)/$(TEST_DIR)/significance_test.o -JSON_TEST_OBJ := $(OBJ_DIR)/$(TEST_DIR)/json_test.o -SIGNIFICANCE_TEST_BIN := $(BUILD_DIR)/significance_test -JSON_TEST_BIN := $(BUILD_DIR)/json_test - -.PHONY: all test clean - -all: $(BINARY) - -$(BINARY): $(LIB_OBJS) $(MAIN_OBJ) - @mkdir -p $(DIST_DIR) - $(CC) $(CFLAGS) -o $@ $^ - -test: $(SIGNIFICANCE_TEST_BIN) $(JSON_TEST_BIN) - ./$(SIGNIFICANCE_TEST_BIN) - ./$(JSON_TEST_BIN) - -$(SIGNIFICANCE_TEST_BIN): $(OBJ_DIR)/$(SRC_DIR)/significance.o $(SIGNIFICANCE_TEST_OBJ) - $(CC) $(CFLAGS) -o $@ $^ - -$(JSON_TEST_BIN): $(OBJ_DIR)/$(SRC_DIR)/json.o $(OBJ_DIR)/$(SRC_DIR)/reading_io.o $(JSON_TEST_OBJ) - $(CC) $(CFLAGS) -o $@ $^ - -$(OBJ_DIR)/%.o: %.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) -c -o $@ $< - -clean: - rm -rf $(BUILD_DIR) $(BINARY)