Consolidate build into one root Makefile; split run.sh into run-engine.sh/run-interpreter.sh

This commit is contained in:
ml
2026-07-05 16:35:18 +02:00
parent bd739ccfdf
commit 28558b4343
12 changed files with 300 additions and 199 deletions
+1 -2
View File
@@ -1,8 +1,7 @@
# Build output, including dist/user.properties which holds real birth # Build output, including dist/user.properties which holds real birth
# data once filled in — never commit it. # data once filled in — never commit it.
/dist /dist
/engine/build /build
/interpreter/build
*.o *.o
core core
+35 -22
View File
@@ -16,15 +16,17 @@ built so it can be linked straight into it later without rework (see
## Build & run ## Build & run
```bash ```bash
cd engine make # builds dist/{deck-engine,interpreter-cli}, dist/img/, dist/i18n/,
make # builds dist/deck-engine, dist/img/, dist/i18n/, dist/run.sh, dist/user.properties # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties
make test # builds and runs engine/tests/smoke_test.c make test # builds and runs engine/tests/smoke_test.c and interpreter/tests/*_test.c
make clean # removes build/ and the generated binary/img/i18n (never touches dist/user.properties) 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 A single `Makefile` at the repo root builds both `engine/` and
inside `engine/`). There is no `make install`; `dist/` is meant to be run `interpreter/` — there is no per-module Makefile, and no `cd` needed
in place. 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: 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 \ --birth-lat 52.5200 --birth-lon 13.4050 \
[--date YYYY-MM-DDTHH:MM] [--format text|html|json] [--lang en|de] [--i18n-dir <path>] [--date YYYY-MM-DDTHH:MM] [--format text|html|json] [--lang en|de] [--i18n-dir <path>]
# 2. dist/run.sh [text|html] [lang] — wraps the binary for daily use: seed # 2. dist/run-engine.sh [text|html|json] [lang] — wraps the binary for
# and --date come from the OS clock (today's date / current UTC time), # daily use: seed and --date come from the OS clock (today's date /
# birth data + default language are read from dist/user.properties # current UTC time), birth data + default language are read from
# next to the script; the optional [lang] arg overrides that language # dist/user.properties next to the script; the optional [lang] arg
# for a single run. # overrides that language for a single run.
``` ```
`dist/user.properties` is seeded once from `engine/scripts/user.properties.template` `dist/user.properties` is seeded once from `engine/scripts/user.properties.template`
with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never
overwritten by later builds** — `run.sh` refuses to run (with a clear overwritten by later builds** — `run-engine.sh`/`run-interpreter.sh`
error) until the placeholders are replaced with real values. 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()` 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, 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. main.c CLI entry point.
i18n/en.lang, i18n/de.lang Translation source files, one per language. i18n/en.lang, i18n/de.lang Translation source files, one per language.
tests/smoke_test.c tests/smoke_test.c
scripts/run.sh, scripts/user.properties.template scripts/run-engine.sh, scripts/run-interpreter.sh, scripts/user.properties.template
Makefile
interpreter/ Separate module + binary; see "Interpretation" below. interpreter/ Separate module + binary; see "Interpretation" below.
dist/ Build output (gitignored-style; see Build & run). dist/ Build output (gitignored-style; see Build & run).
Makefile Single root Makefile, builds both engine/ and interpreter/.
``` ```
### Data flow / the real API ### 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: binaries compose over that JSON, never by linking together:
```bash ```bash
cd engine && make # -> dist/deck-engine (unchanged; --format json is new) make # -> dist/deck-engine (unchanged; --format json is new) and dist/interpreter-cli
cd interpreter && make # -> dist/interpreter-cli make test # builds and runs both engine/tests/*_test.c and interpreter/tests/*_test.c
cd interpreter && make test # builds and runs both interpreter/tests/*_test.c
dist/deck-engine ... --format json | dist/interpreter-cli dist/deck-engine ... --format json | dist/interpreter-cli
# or: dist/deck-engine ... --format json > reading.json && dist/interpreter-cli reading.json # 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 - `interpret_daily_reading(reading, &out)` (`significance.h/.c`) scores
every aspect in `reading->transits.aspects[]`, keeps the **top 5** by every aspect in `reading->transits.aspects[]`, keeps the **top 5** by
score in `DailyInterpretation.top_items[]` (descending, evicting the 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. today"), makes the whole load fail rather than silently dropping data.
- **Deliberately header-only dependency on the engine, throughout**: - **Deliberately header-only dependency on the engine, throughout**:
`significance.h`/`reading_io.h` `#include engine/src/reading.h` for the `significance.h`/`reading_io.h` `#include engine/src/reading.h` for the
struct/enum *definitions*, but `interpreter/Makefile` never compiles or struct/enum *definitions*, but the root `Makefile` never compiles or
links any engine `.c` file (or the vendored Astronomy Engine) — every 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 test in `interpreter/tests/` runs against hand-built JSON text or
hand-built `DailyReading` values, with no real ephemeris/tarot-draw hand-built `DailyReading` values, with no real ephemeris/tarot-draw
call involved anywhere. Consequently `reading_io.c` (planet/aspect call involved anywhere. Consequently `reading_io.c` (planet/aspect
@@ -251,7 +264,7 @@ dist/deck-engine ... --format json | dist/interpreter-cli
## Licensing ## 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`. MIT, per the repo's top-level `LICENSE`.
- `engine/third_party/astronomy/`: vendored MIT code, unmodified — see - `engine/third_party/astronomy/`: vendored MIT code, unmodified — see
`VENDORED.md` for the pinned upstream commit. `VENDORED.md` for the pinned upstream commit.
+114
View File
@@ -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/<file> 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>.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 <code>.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.
+22 -17
View File
@@ -41,14 +41,15 @@ engine/
src/ Engine source (rng, tarot, astro, i18n, reading, CLI). src/ Engine source (rng, tarot, astro, i18n, reading, CLI).
i18n/ Translation files (en.lang, de.lang, ...). i18n/ Translation files (en.lang, de.lang, ...).
tests/smoke_test.c Determinism, ephemeris, and i18n regression checks. tests/smoke_test.c Determinism, ephemeris, and i18n regression checks.
scripts/ run.sh and the user.properties template. scripts/ run-engine.sh, run-interpreter.sh, and
Makefile the user.properties template.
dist/ Build output (gitignored) — see below. dist/ Build output (gitignored) — see below.
interpreter/ Separate module + binary (dist/interpreter-cli): interpreter/ Separate module + binary (dist/interpreter-cli):
scores how significant a day's transits are, scores how significant a day's transits are,
reading deck-engine's --format json output reading deck-engine's --format json output
(see CLAUDE.md). (see CLAUDE.md).
docs/ Architecture/dataflow diagrams, input/output format reference. 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 See [`CLAUDE.md`](CLAUDE.md) for the detailed architecture and the sharp
@@ -61,25 +62,25 @@ struct format reference
## Building ## Building
```bash ```bash
cd engine make # -> dist/deck-engine, dist/interpreter-cli, dist/img/, dist/i18n/,
make # -> dist/deck-engine, dist/img/, dist/i18n/, dist/run.sh, dist/user.properties # dist/run-engine.sh, dist/run-interpreter.sh, dist/user.properties
make test # builds and runs engine/tests/smoke_test.c 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. Requires only a C99 compiler and `make` — no other dependencies.
## Running ## Running
**Daily use**, via `dist/run.sh`: pulls today's date and the current UTC **Daily use**, via `dist/run-engine.sh`: pulls today's date and the
time from the OS clock (used as the tarot seed and the transit moment) current UTC time from the OS clock (used as the tarot seed and the
and reads your birth data from `dist/user.properties`. Edit that file transit moment) and reads your birth data from `dist/user.properties`.
first — it's seeded once from a placeholder template and is never Edit that file first — it's seeded once from a placeholder template and
overwritten by later builds: is never overwritten by later builds:
```bash ```bash
dist/run.sh # text output, language from user.properties (default en) dist/run-engine.sh # text output, language from user.properties (default en)
dist/run.sh html # HTML report with card art, open in a browser dist/run-engine.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 html de # override the language for this run
``` ```
**Direct CLI**, with every input explicit: **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] [--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 scores how significant a day's transits are, reading `deck-engine`'s
`--format json` output — the two binaries compose over that JSON, they're `--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 ```bash
dist/deck-engine ... --format json | dist/interpreter-cli dist/deck-engine ... --format json | dist/interpreter-cli
# or, for daily use:
dist/run-interpreter.sh
``` ```
## License ## License
The engine source code in this repository (`engine/src/`, `engine/scripts/`, The engine source code in this repository (`engine/src/`, `engine/scripts/`)
`engine/Makefile`) is released under the **MIT License** — see and the root `Makefile` are released under the **MIT License** — see
[`LICENSE`](LICENSE). [`LICENSE`](LICENSE).
`engine/third_party/astronomy/` is vendored from `engine/third_party/astronomy/` is vendored from
+17 -12
View File
@@ -33,10 +33,11 @@ know before changing the engine. This document is the visual overview.
- **`engine/i18n/`** holds the translation source files (`en.lang`, - **`engine/i18n/`** holds the translation source files (`en.lang`,
`de.lang`, ...), one `key=value` file per language — see `de.lang`, ...), one `key=value` file per language — see
[`CLAUDE.md`](../CLAUDE.md) for the key-naming convention. [`CLAUDE.md`](../CLAUDE.md) for the key-naming convention.
- **`dist/`** is generated by `make`: the `deck-engine` binary, a copy of - **`dist/`** is generated by the single root `Makefile`: the
the card art under `img/`, a copy of the translation files under `deck-engine` and `interpreter-cli` binaries, a copy of the card art
`i18n/`, `run.sh`, and a `user.properties` template that's seeded once under `img/`, a copy of the translation files under `i18n/`,
and never overwritten by later builds. `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 - **The Pebble watchapp does not exist yet.** When it's built, it will
call `reading_generate()` directly and walk the returned struct to lay call `reading_generate()` directly and walk the returned struct to lay
out its own screens — it has no reason to touch `reading_print_*`. 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 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 `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 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) the catalog from `--lang`/`--i18n-dir` (or `run-engine.sh`'s `lang=`
before calling `reading_generate`, so this is orthogonal to the three property) before calling `reading_generate`, so this is orthogonal to
computations above — it only affects how their results are rendered as the three computations above — it only affects how their results are
text, not the underlying `TarotCard`/`ZodiacSign`/etc. enum values. rendered as text, not the underlying `TarotCard`/`ZodiacSign`/etc. enum
values.
Both entry points (direct CLI flags, or `run.sh` pulling the seed/date Both entry points (direct CLI flags, or `run-engine.sh` pulling the
from the OS clock and birth data from `user.properties`) converge on the seed/date from the OS clock and birth data from `user.properties`)
same `main.c` argument parsing before calling `reading_generate` — there converge on the same `main.c` argument parsing before calling
is exactly one code path from parsed input to a reading. `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 ## Regenerating the diagrams
+16 -9
View File
@@ -1,8 +1,8 @@
# Input & output formats # Input & output formats
See [`architecture.md`](architecture.md) for how these fit together. See [`architecture.md`](architecture.md) for how these fit together.
There are two ways to provide input (direct CLI flags, or `run.sh` + There are two ways to provide input (direct CLI flags, or `run-engine.sh`
`user.properties`) and three output formats (`text`, `html`, `json`); the + `user.properties`) and three output formats (`text`, `html`, `json`); the
`DailyReading` struct is the programmatic form all three are rendered `DailyReading` struct is the programmatic form all three are rendered
from, and the one the future watchapp will consume directly. `json` is from, and the one the future watchapp will consume directly. `json` is
also `interpreter-cli`'s input format — see "The interpreter" at the also `interpreter-cli`'s input format — see "The interpreter" at the
@@ -34,9 +34,10 @@ deck-engine --seed <string> --birth-date YYYY-MM-DD --birth-time HH:MM
Birth latitude/longitude only affect the Ascendant/houses — planet signs Birth latitude/longitude only affect the Ascendant/houses — planet signs
are geocentric and location-independent. 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 - **Seed and `--date`** come from the OS clock: `date +%Y-%m-%d` (stable
all day) and `date -u +%Y-%m-%dT%H:%M`. 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` The file is seeded once from `engine/scripts/user.properties.template`
with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never with placeholder values (`YOUR_BIRTH_DATE_HERE`, etc.) and is **never
overwritten by later builds**. `run.sh` checks every birth field for an overwritten by later builds**. `run-engine.sh` checks every birth field
empty value or a leftover `YOUR_` placeholder and refuses to run with a for an empty value or a leftover `YOUR_` placeholder and refuses to run
clear error until the file is filled in; `lang` has no such check since with a clear error until the file is filled in; `lang` has no such
it always has a usable default (`en`). check since it always has a usable default (`en`).
- The optional second argument overrides `lang=` for a single run without - 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/`) ### Translations (`--lang`, `dist/i18n/`)
@@ -226,6 +231,8 @@ dist/deck-engine ... --format json | dist/interpreter-cli
# or: # or:
dist/deck-engine ... --format json > reading.json dist/deck-engine ... --format json > reading.json
dist/interpreter-cli 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 - **Input**: a file path argument, or stdin if no argument (or `-`) is
-74
View File
@@ -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/<file> 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>.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 <code>.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.
@@ -1,11 +1,12 @@
#!/bin/sh #!/bin/sh
# Runs deck-engine for "today": the tarot seed and the transit moment come # 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 # 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. # [lang] overrides user.properties' lang= for this run only (e.g.
# ./run.sh html de); omit it to use lang= from user.properties, which # ./run-engine.sh html de); omit it to use lang= from user.properties,
# itself defaults to "en" if unset. # which itself defaults to "en" if unset. See run-interpreter.sh for a
# significance report instead of the full reading.
set -eu set -eu
+84
View File
@@ -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"
+2 -1
View File
@@ -1,6 +1,7 @@
# deck-in-a-dash user properties # 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 # 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. # overwritten by later builds, so it's safe to edit in place.
# #
+5 -5
View File
@@ -137,13 +137,13 @@ static void test_reading_print_json_shape(void) {
printf("PASS test_reading_print_json_shape\n"); printf("PASS test_reading_print_json_shape\n");
} }
/* Run with cwd == engine/ (as `make test` does), so the shipped /* Run with cwd == repo root (as `make test` does), so the shipped
* engine/i18n/ .lang files are reachable as i18n/<code>.lang. */ * engine/i18n/ .lang files are reachable at that path. */
static void test_i18n_fallback_and_translation(void) { static void test_i18n_fallback_and_translation(void) {
/* No catalog loaded yet: every lookup returns the caller's fallback. */ /* No catalog loaded yet: every lookup returns the caller's fallback. */
assert(strcmp(i18n_get("does.not.exist", "fallback text"), "fallback text") == 0); 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(loaded && "expected engine/i18n/de.lang to exist and load");
assert(strcmp(i18n_get("body.sun", "Sun"), "Sonne") == 0); assert(strcmp(i18n_get("body.sun", "Sun"), "Sonne") == 0);
assert(strcmp(i18n_get("does.not.exist", "fallback text"), "fallback text") == 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 /* Loading a file that doesn't exist fails and leaves the previously
* loaded catalog in place, rather than silently clearing it. */ * 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); assert(strcmp(i18n_get("body.sun", "Sun"), "Sonne") == 0);
/* Loading a different language replaces the catalog wholesale. */ /* Loading a different language replaces the catalog wholesale. */
loaded = i18n_load("i18n/en.lang"); loaded = i18n_load("engine/i18n/en.lang");
assert(loaded); assert(loaded);
assert(strcmp(astro_body_name(PLANET_MOON), "Moon") == 0); assert(strcmp(astro_body_name(PLANET_MOON), "Moon") == 0);
-54
View File
@@ -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)