-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
259 lines (207 loc) · 14.3 KB
/
Copy pathMakefile
File metadata and controls
259 lines (207 loc) · 14.3 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
# blep — developer tasks. Run `make` (or `make help`) for the list.
# Toolchain (JDK + Gradle) comes from mise; the Android SDK from $(ANDROID_HOME).
ANDROID_HOME ?= $(HOME)/android-sdk
ADB := $(ANDROID_HOME)/platform-tools/adb
SDKMANAGER := $(ANDROID_HOME)/cmdline-tools/latest/bin/sdkmanager
AVDMANAGER := $(ANDROID_HOME)/cmdline-tools/latest/bin/avdmanager
EMULATOR := $(ANDROID_HOME)/emulator/emulator
GRADLE := mise exec -- ./gradlew
APP_ID := fyi.blep
AVD ?= blep
DEVICE ?= # target adb serial; auto-picks when only one is attached
SYSIMG ?= system-images;android-35;google_apis;x86_64
# ── Play publishing ──────────────────────────────────────────────────────────
# Service-account key (gitignored) + the tracks each form factor releases to.
# Override on the CLI, e.g. `make ship PLAY_COMMIT=1 PLAY_KEY=~/secrets/sa.json`.
PLAY_KEY ?= sa.json
PLAY_PHONE_TRACK ?= alpha # phone closed-test track (the one with testers)
PLAY_WEAR_TRACK ?= wear:blep # Wear OS form-factor track (API prefixes it `wear:`)
PHONE_AAB := app/androidApp/build/outputs/bundle/release/androidApp-release.aab
WEAR_AAB := app/wearApp/build/outputs/bundle/release/wearApp-release.aab
PUBLISH_PY := .venv-publish/bin/python
NOTES_DIR ?= store/release-notes # <locale>.txt "what's new" (auto-drafted by `make release-notes`)
# Dry-run unless PLAY_COMMIT is set (so a bare `make publish-*` only prints the plan).
COMMIT_FLAG := $(if $(PLAY_COMMIT),--commit,)
.DEFAULT_GOAL := help
.PHONY: help setup doctor test sim sim-gps sim-safety scenarios chaos robustness build apk aab \
install install-wear run demo uninstall devices logcat \
emulator-setup emulator screenshots screenshots-i18n screenshots-wear promo \
publish-setup publish-listing publish-store bump-version release-notes release-build publish-release release ship ble-trackers \
bridge bridge-motion bridge-rssi web web-build web-icons \
sync-emu sync-emu-up sync-emu-down sync-emu-apps sync-emu-companion sync-emu-pair sync-emu-verify sync-emu-status \
ci apple clean
help: ## Show this help
@awk 'BEGIN{FS=":.*##"; printf "\nblep — make targets\n\n"} /^[a-zA-Z0-9_-]+:.*##/ {printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
@echo ""
# ───────────────────────────── setup ──────────────────────────────
setup: ## Install toolchain (mise tools) and website deps
mise install
cd web && npm install
doctor: ## Print tool versions and connected devices
@mise exec -- java -version 2>&1 | head -1
@mise exec -- ./app/gradlew --version 2>/dev/null | grep Gradle || true
@echo "ANDROID_HOME=$(ANDROID_HOME)"
@$(ADB) devices
# ─────────────────────────── app: build ───────────────────────────
test: ## Run core unit tests on the JVM (no Android SDK needed)
cd app && $(GRADLE) :core:jvmTest -Pblep.android=false
# Pull the println'd table out of a sim test's captured stdout.
define print_sim
@python3 -c "import re,glob; f=sorted(glob.glob('app/core/build/test-results/jvmTest/*TrackingSimulation*.xml'))[-1]; s=open(f).read(); m=re.search(r'CDATA\[(.*?)\]\]',s,re.S); print(m.group(1).strip() if m else '(no output)')"
endef
sim: ## Run the closed-loop tracking simulation + print the scenario table
-cd app && $(GRADLE) :core:jvmTest --tests '*TrackingSimulationTest.simulation_suite' -Pblep.android=false --rerun-tasks -q
$(print_sim)
scenarios: sim ## Alias for `sim`
sim-gps: ## GPS-fusion reference: mean closest-approach by drift × GPS accuracy
-cd app && $(GRADLE) :core:jvmTest --tests '*TrackingSimulationTest.simulation_gps_suite' -Pblep.android=false --rerun-tasks -q
$(print_sim)
sim-safety: ## Anti-tracking detection scenarios (follower vs routine) — printed table
-cd app && $(GRADLE) :core:jvmTest --tests '*SafetySimulationTest.scenario_suite' -Pblep.android=false --rerun-tasks -q
@python3 -c "import re,glob; f=sorted(glob.glob('app/core/build/test-results/jvmTest/*SafetySimulation*.xml'))[-1]; s=open(f).read(); m=re.search(r'CDATA\[(.*?)\]\]',s,re.S); print(m.group(1).strip() if m else '(no output)')"
chaos: ## Random-search every tuning knob (CHAOS_N=60, override on the CLI)
-cd app && CHAOS_N=$${CHAOS_N:-60} $(GRADLE) :core:jvmTest --tests '*TrackingSimulationTest.chaos*' -Pblep.android=false --rerun-tasks -q
$(print_sim)
robustness: ## Held-out generalisation over random worlds (ROBUST_N, ROBUST_SEED)
-cd app && $(GRADLE) :core:jvmTest --tests '*TrackingSimulationTest.robustness*' -Pblep.android=false --rerun-tasks -q
$(print_sim)
build: ## Build the phone + Wear debug APKs
cd app && $(GRADLE) :androidApp:assembleDebug :wearApp:assembleDebug
@echo "APKs:"
@find app -path '*outputs/apk/debug/*.apk'
apk: build ## Alias for `build`
aab: ## Build the signed release AAB for Play (needs app/keystore.properties)
cd app && $(GRADLE) :androidApp:bundleRelease
@echo "AAB: app/androidApp/build/outputs/bundle/release/androidApp-release.aab"
clean: ## Clean Gradle + web build outputs
cd app && $(GRADLE) clean
rm -rf web/dist
# ─────────────────────── app: device / phone ──────────────────────
# Resolve the target device into ANDROID_SERIAL (honoured by both adb and Gradle).
# DEVICE=<serial> picks one explicitly; otherwise auto-pick when exactly one is
# attached, else list them and ask. Use inside a recipe: `@$(call with_device,<cmd>)`.
define with_device
serial="$(DEVICE)"; \
if [ -z "$$serial" ]; then \
list=$$($(ADB) devices | awk '$$2=="device"{print $$1}' | grep -v '^emulator-'); \
cnt=$$(printf '%s\n' "$$list" | sed '/^$$/d' | wc -l); \
if [ "$$cnt" -eq 0 ]; then echo "No physical device (emulators are ignored unless you set DEVICE=<serial>). Attached:"; $(ADB) devices | awk '$$2=="device"{print " "$$1}'; exit 1; fi; \
if [ "$$cnt" -gt 1 ]; then echo "Multiple devices — pick one with DEVICE=<serial>:"; printf ' %s\n' $$list; exit 1; fi; \
serial="$$list"; \
fi; \
export ANDROID_SERIAL="$$serial"; echo "› device $$serial"; $(1)
endef
devices: ## List connected devices (adb)
$(ADB) devices
install: ## Build + install the phone app (DEVICE=<serial> to pick when several attached)
@$(call with_device,cd app && $(GRADLE) :androidApp:installDebug)
run: install ## Install and launch blep on the phone
@$(call with_device,$(ADB) shell am start -n $(APP_ID)/.MainActivity)
install-wear: ## Build + install the Wear OS app (DEVICE=<serial> for the watch)
@$(call with_device,cd app && $(GRADLE) :wearApp:installDebug)
demo: ## Install + launch the app in demo mode (scripted data, no BLE needed)
cd app && $(GRADLE) :androidApp:assembleDebug -q
@$(call with_device,$(ADB) install -r app/androidApp/build/outputs/apk/debug/androidApp-debug.apk \
&& $(ADB) shell am start -n $(APP_ID)/.MainActivity --ez demo true)
uninstall: ## Remove blep from the connected device
-@$(call with_device,$(ADB) uninstall $(APP_ID))
logcat: ## Tail blep logs from the device (app must be running)
@$(call with_device,$(ADB) logcat --pid=$$($(ADB) shell pidof -s $(APP_ID)))
# ───────────────────────── app: emulator ──────────────────────────
# Note: the emulator has NO motion sensors, so REAL pointer tracking can't run
# there — use a real phone for that. But `make demo`/`make screenshots` feed
# scripted data, so every screen (incl. the radar) renders on the emulator. BLE
# *advertisements* CAN be injected via netsim — see `make ble-trackers`.
emulator-setup: ## Install emulator + system image and (re)create the AVD
yes | $(SDKMANAGER) "platform-tools" "emulator" "$(SYSIMG)"
echo "no" | $(AVDMANAGER) create avd -n $(AVD) -k "$(SYSIMG)" -d pixel_6 --force
emulator: ## Boot the blep emulator
ANDROID_HOME=$(ANDROID_HOME) ANDROID_SDK_ROOT=$(ANDROID_HOME) $(EMULATOR) -avd $(AVD) -netdelay none -netspeed full
screenshots: ## Regenerate the captioned store screenshots from demo mode
tools/screenshots.sh
promo: ## Generate the branded promo video (screenshots/promo.mp4) for YouTube/Play
tools/promo-video.sh
screenshots-i18n: ## Localized phone screenshots + feature graphic per app language
tools/screenshots-i18n.sh
screenshots-wear: ## Localized Wear OS screenshots per app language
tools/screenshots-wear.sh
# ───────────────────────────── Play publishing ─────────────────────────────
# All publish targets are DRY-RUN by default — they print the plan and upload
# nothing. Set PLAY_COMMIT=1 to actually push. Needs the venv (`make publish-setup`)
# and a service-account key (PLAY_KEY, gitignored). See store/listing/README.md.
publish-setup: ## Create the Python venv for the Play publishing tools
python3 -m venv .venv-publish && .venv-publish/bin/pip install -q -r tools/requirements-publish.txt
publish-listing: ## Push store text + screenshots to Play (PLAY_COMMIT=1 to apply)
$(PUBLISH_PY) tools/publish-listing.py --key $(PLAY_KEY) $(COMMIT_FLAG)
# ── Target A: regenerate screenshots, then update the whole store listing ──
publish-store: ## Regen localized screenshots, then push listing text + images (PLAY_COMMIT=1 to apply; needs a running emulator)
$(MAKE) screenshots-i18n
$(MAKE) screenshots-wear
$(MAKE) publish-listing
@echo "✓ store updated$(if $(PLAY_COMMIT),, (dry run — set PLAY_COMMIT=1 to push))"
bump-version: ## Increment each module's versionCode (phone 1xxx, wear 2xxx band)
tools/bump-version.sh
release-build: ## Build the signed phone + Wear release AABs
cd app && $(GRADLE) :androidApp:bundleRelease :wearApp:bundleRelease
release-notes: ## Draft the English "what's new" from feat/fix commits since the last release
tools/release-notes.sh
publish-release: release-build ## Upload + release the built AABs to their tracks (no bump; PLAY_COMMIT=1 to apply)
$(PUBLISH_PY) tools/publish-release.py --key $(PLAY_KEY) --aab $(PHONE_AAB) --track $(PLAY_PHONE_TRACK) --notes-dir $(NOTES_DIR) $(COMMIT_FLAG)
$(PUBLISH_PY) tools/publish-release.py --key $(PLAY_KEY) --aab $(WEAR_AAB) --track $(PLAY_WEAR_TRACK) --notes-dir $(NOTES_DIR) $(COMMIT_FLAG)
# ── Target B: bump build numbers, build, upload, release ──
release: ## Bump versionCode → build signed AABs → upload + release to tracks (PLAY_COMMIT=1 to apply)
$(MAKE) bump-version
$(MAKE) publish-release
@echo "✓ release done$(if $(PLAY_COMMIT),, (dry run — versionCodes bumped, nothing uploaded; set PLAY_COMMIT=1 to ship))"
ship: ## Everything: publish-store + release (PLAY_COMMIT=1 to apply; needs a running emulator)
$(MAKE) publish-store
$(MAKE) release
ble-trackers: ## Inject fake AirTag/Tile/SmartTag adverts into the emulator (netsim+Bumble)
@test -x tools/ble-netsim/venv/bin/python || \
(python3 -m venv tools/ble-netsim/venv && tools/ble-netsim/venv/bin/pip install -q bumble)
@port=$$(sed -n 's/^grpc.port=//p' "$${TMPDIR:-/tmp}"/netsim.ini 2>/dev/null); \
tools/ble-netsim/venv/bin/python tools/ble-netsim/advertise.py $$port
# ── Phone↔watch sync test rig (two emulators on one netsim radio) ─────────────
# Validates the Wear Data Layer sync (favourites/names/mutes/settings + relay) that
# unit tests can't reach. Pairing the emulators needs the sideloaded Wear companion +
# a watch in pairing mode (a freshly-wiped watch AVD); the rest is scripted. See
# scripts/sync-emu.sh + scripts/verify-sync.sh.
sync-emu: ## Full sync rig: images → AVDs → boot → install → companion → pair → verify
scripts/sync-emu.sh all
sync-emu-up: ## Create AVDs (if missing) + boot phone + Wear emulators on netsim
scripts/sync-emu.sh avds && scripts/sync-emu.sh up
sync-emu-down: ## Kill both emulators
scripts/sync-emu.sh down
sync-emu-apps: ## Build + install the phone & wear debug APKs
scripts/sync-emu.sh apps
sync-emu-companion: ## Fetch (cached) + sideload the Wear OS companion app onto the phone
scripts/sync-emu.sh companion
sync-emu-pair: ## Drive the companion pairing wizard (watch must be in pairing mode)
scripts/sync-emu.sh pair
sync-emu-verify: ## Prove a favourite syncs phone→watch over the Data Layer
scripts/sync-emu.sh verify
sync-emu-status: ## Show emulator + netsim radios + pairing state
scripts/sync-emu.sh status
# On-device "bridge" checks: validate the real Android sensor/BLE glue the JVM
# sims bypass. Need a running emulator (`make emulator`). Path-finding *logic*
# stays in `make sim` — netsim has no RSSI gradient, so the hunt can't be e2e'd.
bridge-motion: ## Motion bridge: fused sensors → heading (smoke + injected-yaw tracking)
cd app && $(GRADLE) :core:connectedDebugAndroidTest
tools/ble-netsim/check_motion_bridge.sh
bridge-rssi: ## RSSI bridge: netsim advert → AndroidBleScanner.rssi() → tracking screen
@test -x tools/ble-netsim/venv/bin/python || \
(python3 -m venv tools/ble-netsim/venv && tools/ble-netsim/venv/bin/pip install -q bumble)
tools/ble-netsim/check_rssi_bridge.sh
bridge: bridge-motion bridge-rssi ## Run both on-device bridge checks
# ───────────────────────────── website ────────────────────────────
web: ## Run the website dev server
cd web && npm run dev
web-build: ## Build the static website into web/dist
cd web && npm run build
web-icons: ## Regenerate PWA icons from logo.svg
cd web && npm run gen:icons
# ───────────────────────────── ci-ish ─────────────────────────────
ci: test build ## Run locally what CI checks on Linux (tests + Android build)
apple: ## Compile the iOS/watchOS Kotlin targets (requires macOS + Xcode)
@uname -s | grep -qi darwin || { echo "Apple targets need macOS + Xcode."; exit 1; }
cd app && $(GRADLE) :core:compileKotlinIosSimulatorArm64 :core:compileKotlinWatchosSimulatorArm64 :composeApp:compileKotlinIosSimulatorArm64