Monorepo for Aesthetic.Computer
aesthetic.computer
1# ac-native Makefile
2# Builds the native AC piece runner, optionally with QuickJS-ng
3
4SRCDIR := src
5BUILDDIR := build
6QJSDIR := $(BUILDDIR)/quickjs
7
8# Use musl for static linking if available, else gcc
9CC ?= gcc
10GIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
11BUILD_TS ?= $(shell date -u '+%Y-%m-%dT%H:%M')
12BUILD_NAME := $(shell scripts/build-name.sh --bump 2>/dev/null || echo "local-build")
13QUIRCDIR := lib/quirc/lib
14QRGENDIR := lib/qrcodegen
15CFLAGS := -O2 -Wall -Wextra -std=gnu11 -I$(QJSDIR) -I$(QUIRCDIR) -I$(QRGENDIR) -DAC_GIT_HASH=\"$(GIT_HASH)\" -DAC_BUILD_TS=\"$(BUILD_TS)\" -DAC_BUILD_NAME=\"$(BUILD_NAME)\"
16LDFLAGS := -Wl,--as-needed -lm -lpthread -lasound -lutil -ldl
17
18# Static build: only when musl-gcc is available (musl has bundled static libc)
19# On Fedora/standard gcc, dynamic linking is used and .so files are bundled in initramfs
20ifdef STATIC
21 ifeq ($(shell command -v musl-gcc 2>/dev/null),)
22 # musl-gcc not available — skip -static (dynamic linking, bundle .so in initramfs)
23 STATIC :=
24 else
25 LDFLAGS += -static
26 endif
27endif
28
29# DRM headers — use pkg-config if available
30DRM_CFLAGS := $(shell pkg-config --cflags libdrm 2>/dev/null || echo "-I/usr/include/libdrm -I/usr/include/drm")
31DRM_LIBS := $(shell pkg-config --libs libdrm 2>/dev/null || echo "-ldrm")
32ALSA_CFLAGS := $(shell pkg-config --cflags alsa 2>/dev/null)
33ALSA_LIBS := $(shell pkg-config --libs alsa 2>/dev/null || echo "-lasound")
34FLITE_LIBS := -lflite -lflite_cmulex -lflite_usenglish -lflite_cmu_us_slt -lflite_cmu_us_kal
35
36# ffmpeg libraries for video recording (optional: disabled if not found)
37HAVE_AVCODEC := $(shell pkg-config --exists libavcodec libavformat libavutil libswscale libswresample 2>/dev/null && echo 1)
38ifeq ($(HAVE_AVCODEC),1)
39 AV_CFLAGS := $(shell pkg-config --cflags libavcodec libavformat libavutil libswscale libswresample 2>/dev/null)
40 AV_LIBS := $(shell pkg-config --libs libavcodec libavformat libavutil libswscale libswresample 2>/dev/null)
41endif
42
43CFLAGS += $(DRM_CFLAGS) $(ALSA_CFLAGS)
44LDFLAGS += $(DRM_LIBS) $(ALSA_LIBS) $(FLITE_LIBS)
45ifeq ($(HAVE_AVCODEC),1)
46 CFLAGS += -DHAVE_AVCODEC $(AV_CFLAGS)
47 LDFLAGS += $(AV_LIBS)
48endif
49
50# SDL3 GPU-accelerated display: always compiled, loaded via dlopen at runtime.
51# Binary runs without SDL3 libs and falls back to DRM/fbdev.
52# No compile-time or link-time SDL dependency.
53
54# Wayland display backend (for running under cage compositor: make USE_WAYLAND=1)
55XDG_SHELL_XML := /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
56ifdef USE_WAYLAND
57 WL_CFLAGS := $(shell pkg-config --cflags wayland-client 2>/dev/null)
58 WL_LIBS := $(shell pkg-config --libs wayland-client 2>/dev/null || echo "-lwayland-client")
59 CFLAGS += -DUSE_WAYLAND $(WL_CFLAGS)
60 LDFLAGS += $(WL_LIBS)
61endif
62
63# Source files
64SSL_LIBS := $(shell pkg-config --libs openssl 2>/dev/null || echo "-lssl -lcrypto")
65LDFLAGS += $(SSL_LIBS)
66
67SRCS := $(SRCDIR)/ac-native.c \
68 $(SRCDIR)/drm-display.c \
69 $(SRCDIR)/framebuffer.c \
70 $(SRCDIR)/graph.c \
71 $(SRCDIR)/graph3d.c \
72 $(SRCDIR)/font.c \
73 $(SRCDIR)/color.c \
74 $(SRCDIR)/input.c \
75 $(SRCDIR)/audio.c \
76 $(SRCDIR)/usb-midi.c \
77 $(SRCDIR)/wifi.c \
78 $(SRCDIR)/tts.c \
79 $(SRCDIR)/ws-client.c \
80 $(SRCDIR)/udp-client.c \
81 $(SRCDIR)/camera.c \
82 $(SRCDIR)/pty.c \
83 $(SRCDIR)/machines.c \
84 $(SRCDIR)/swank-bridge.c \
85 $(SRCDIR)/js-bindings.c \
86 $(SRCDIR)/audio-decode.c \
87 $(SRCDIR)/recorder.c
88
89# Wayland display backend (conditional)
90ifdef USE_WAYLAND
91 SRCS += $(SRCDIR)/wayland-display.c
92endif
93
94OBJS := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS))
95
96# Wayland protocol generated objects (separate from SRCS pattern)
97ifdef USE_WAYLAND
98 WL_PROTO_OBJS := $(BUILDDIR)/xdg-shell-protocol.o
99else
100 WL_PROTO_OBJS :=
101endif
102
103# QuickJS-ng object files (compiled from source)
104QJS_SRCS := $(QJSDIR)/quickjs.c $(QJSDIR)/libunicode.c $(QJSDIR)/libregexp.c $(QJSDIR)/cutils.c $(QJSDIR)/libbf.c
105QJS_OBJS := $(patsubst $(QJSDIR)/%.c,$(BUILDDIR)/qjs-%.o,$(QJS_SRCS))
106
107# Quirc QR decoder (compiled from vendored source)
108QUIRC_SRCS := $(QUIRCDIR)/quirc.c $(QUIRCDIR)/decode.c $(QUIRCDIR)/identify.c $(QUIRCDIR)/version_db.c
109QUIRC_OBJS := $(patsubst $(QUIRCDIR)/%.c,$(BUILDDIR)/quirc-%.o,$(QUIRC_SRCS))
110
111# QR code generator (vendored from nayuki/QR-Code-generator, MIT licensed)
112QRGEN_SRCS := $(QRGENDIR)/qrcodegen.c
113QRGEN_OBJS := $(patsubst $(QRGENDIR)/%.c,$(BUILDDIR)/qrgen-%.o,$(QRGEN_SRCS))
114
115TARGET := $(BUILDDIR)/ac-native
116
117.PHONY: all clean quickjs test bpf
118
119all: quickjs $(TARGET)
120
121# BPF trace tools (ftrace fallback by default; full BPF: make bpf HAS_LIBBPF=1)
122bpf:
123 $(MAKE) -C bpf CC=$(CC)
124
125$(TARGET): $(OBJS) $(QJS_OBJS) $(QUIRC_OBJS) $(QRGEN_OBJS) $(WL_PROTO_OBJS)
126 $(CC) -o $@ $^ $(LDFLAGS)
127 @echo "Built: $@ ($(shell wc -c < $@ | tr -d ' ') bytes)"
128
129# All .c files depend on all project headers (simple but correct)
130HEADERS := $(wildcard $(SRCDIR)/*.h)
131
132# Track CFLAGS changes — force full rebuild when git hash, build name, or any flag changes.
133# Make only watches source timestamps, not compiler flags, so without this the binary
134# retains stale AC_BUILD_NAME / AC_GIT_HASH after new commits with no C source changes.
135CFLAGS_FILE := $(BUILDDIR)/.cflags
136CFLAGS_SIG := $(shell echo '$(CFLAGS)' | md5sum | cut -d' ' -f1)
137CFLAGS_PREV := $(shell cat $(CFLAGS_FILE) 2>/dev/null)
138ifneq ($(CFLAGS_SIG),$(CFLAGS_PREV))
139 $(shell mkdir -p $(BUILDDIR) && echo '$(CFLAGS_SIG)' > $(CFLAGS_FILE))
140 $(shell rm -f $(BUILDDIR)/*.o)
141endif
142
143$(BUILDDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) | $(BUILDDIR)
144 $(CC) $(CFLAGS) -c -o $@ $<
145
146$(BUILDDIR)/qjs-%.o: $(QJSDIR)/%.c | $(BUILDDIR)
147 $(CC) $(CFLAGS) -DCONFIG_VERSION=\"0.8.0\" -c -o $@ $<
148
149$(BUILDDIR)/qrgen-%.o: $(QRGENDIR)/%.c | $(BUILDDIR)
150 $(CC) -O2 -Wall -std=c99 -I$(QRGENDIR) -c -o $@ $<
151
152$(BUILDDIR)/quirc-%.o: $(QUIRCDIR)/%.c | $(BUILDDIR)
153 $(CC) -O2 -Wall -std=gnu11 -I$(QUIRCDIR) -c -o $@ $<
154
155$(BUILDDIR):
156 mkdir -p $(BUILDDIR)
157
158# Wayland protocol code generation (xdg-shell)
159ifdef USE_WAYLAND
160$(BUILDDIR)/xdg-shell-client-protocol.h: $(XDG_SHELL_XML) | $(BUILDDIR)
161 wayland-scanner client-header $< $@
162
163$(BUILDDIR)/xdg-shell-protocol.c: $(XDG_SHELL_XML) | $(BUILDDIR)
164 wayland-scanner private-code $< $@
165
166$(BUILDDIR)/xdg-shell-protocol.o: $(BUILDDIR)/xdg-shell-protocol.c $(BUILDDIR)/xdg-shell-client-protocol.h | $(BUILDDIR)
167 $(CC) -O2 -Wall -I$(BUILDDIR) -c -o $@ $<
168
169# wayland-display.c depends on generated protocol header
170$(BUILDDIR)/wayland-display.o: $(SRCDIR)/wayland-display.c $(BUILDDIR)/xdg-shell-client-protocol.h $(HEADERS) | $(BUILDDIR)
171 $(CC) $(CFLAGS) -I$(BUILDDIR) -c -o $@ $<
172endif
173
174# Download and prepare QuickJS-ng
175quickjs: $(QJSDIR)/quickjs.h
176
177QJS_VERSION := 0.8.0
178QJS_URL := https://github.com/nicoffee/nicoffee-nicoffee/archive/refs/tags/v$(QJS_VERSION).tar.gz
179
180$(QJSDIR)/quickjs.h:
181 @echo "Fetching QuickJS..."
182 mkdir -p $(QJSDIR)
183 curl -sL "https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz" -o $(BUILDDIR)/qjs.tar.xz && \
184 cd $(BUILDDIR) && tar xf qjs.tar.xz && \
185 cp quickjs-2024-01-13/*.c quickjs-2024-01-13/*.h quickjs/ || { \
186 echo "ERROR: Cannot fetch QuickJS." ; \
187 echo " curl -L https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz | tar xJ" ; \
188 exit 1 ; \
189 }
190
191clean:
192 rm -f $(BUILDDIR)/*.o $(BUILDDIR)/qjs-*.o $(TARGET) $(BUILDDIR)/qjs.tar.xz
193 rm -rf $(BUILDDIR)/quickjs $(BUILDDIR)/quickjs-*
194
195# Run test piece locally (needs DRM access or QEMU)
196test: $(TARGET)
197 @echo "Running hello.mjs test piece..."
198 $(TARGET) test-pieces/hello.mjs