replace pxl8.sh with Makefile, port client and server to Windows
This commit is contained in:
parent
8d491612ab
commit
8f9317f751
9 changed files with 725 additions and 878 deletions
499
Makefile
Normal file
499
Makefile
Normal file
|
|
@ -0,0 +1,499 @@
|
||||||
|
# pxl8 - Makefile
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# make Build everything (debug)
|
||||||
|
# make release Build everything (release)
|
||||||
|
# make run Build and run (CART=script.fnl)
|
||||||
|
# make server Build pxl8d only
|
||||||
|
# make client Build pxl8 only
|
||||||
|
# make install Install to ~/.local/bin (release)
|
||||||
|
# make profile Profile with perf + flamegraph (Linux)
|
||||||
|
# make ase Aseprite tools (ASE_CMD=package|clean)
|
||||||
|
# make clean Remove build artifacts
|
||||||
|
# make clean-all Remove build artifacts and dependencies
|
||||||
|
# make clean-deps Remove only dependencies
|
||||||
|
# make clean-cache Clear ccache
|
||||||
|
# make update Fetch/update all dependencies
|
||||||
|
# make vendor-sdl Vendor SDL3 from source
|
||||||
|
# make help Show available targets
|
||||||
|
#
|
||||||
|
# Variables:
|
||||||
|
# MODE=release Build in release mode (default: debug)
|
||||||
|
# CART=demo Cart/script to run (default: demo)
|
||||||
|
# PROFILE_DURATION=30 Perf recording seconds (default: 30)
|
||||||
|
# ASE_CMD= Aseprite subcommand (package, clean, ...)
|
||||||
|
|
||||||
|
export PATH := $(PATH):/usr/bin:/bin
|
||||||
|
|
||||||
|
# -- Platform detection -------------------------------------------------------
|
||||||
|
|
||||||
|
UNAME := $(shell uname)
|
||||||
|
PLATFORM := posix
|
||||||
|
|
||||||
|
ifneq (,$(findstring MINGW,$(UNAME)))
|
||||||
|
PLATFORM := windows
|
||||||
|
endif
|
||||||
|
ifneq (,$(findstring MSYS,$(UNAME)))
|
||||||
|
PLATFORM := windows
|
||||||
|
endif
|
||||||
|
ifneq (,$(findstring CYGWIN,$(UNAME)))
|
||||||
|
PLATFORM := windows
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -- Toolchain ----------------------------------------------------------------
|
||||||
|
|
||||||
|
CC := clang
|
||||||
|
CCACHE := $(shell command -v ccache 2>/dev/null)
|
||||||
|
ifdef CCACHE
|
||||||
|
CC := ccache $(CC)
|
||||||
|
endif
|
||||||
|
|
||||||
|
NPROC := $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
||||||
|
|
||||||
|
# -- Directories --------------------------------------------------------------
|
||||||
|
|
||||||
|
BUILDDIR = .build/$(MODE)
|
||||||
|
OBJDIR = $(BUILDDIR)/obj
|
||||||
|
BINDIR = bin/$(MODE)
|
||||||
|
SHADERDIR = $(BUILDDIR)/shaders/cpu
|
||||||
|
|
||||||
|
# -- Mode (debug/release) ----------------------------------------------------
|
||||||
|
|
||||||
|
MODE ?= debug
|
||||||
|
|
||||||
|
ifeq ($(MODE),release)
|
||||||
|
CFLAGS = -std=c23 -Wall -Wextra -Wno-missing-braces \
|
||||||
|
-O3 -flto -ffast-math -funroll-loops \
|
||||||
|
-fno-unwind-tables -fno-asynchronous-unwind-tables
|
||||||
|
LDFLAGS = -flto
|
||||||
|
else
|
||||||
|
CFLAGS = -std=c23 -Wall -Wextra -Wno-missing-braces -g -O1 -DDEBUG
|
||||||
|
LDFLAGS =
|
||||||
|
endif
|
||||||
|
|
||||||
|
DEP_CFLAGS = -O3 -funroll-loops -MMD -MP
|
||||||
|
CFLAGS += -MMD -MP
|
||||||
|
|
||||||
|
# -- Linker -------------------------------------------------------------------
|
||||||
|
|
||||||
|
MOLD := $(shell command -v mold 2>/dev/null)
|
||||||
|
ifdef MOLD
|
||||||
|
LDFLAGS += -fuse-ld=mold
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM),windows)
|
||||||
|
LIBS = -lws2_32
|
||||||
|
PXL8_DEF = $(BUILDDIR)/pxl8.def
|
||||||
|
EXE_EXT = .exe
|
||||||
|
else ifeq ($(UNAME),Darwin)
|
||||||
|
LIBS = -lm
|
||||||
|
export MACOSX_DEPLOYMENT_TARGET := $(shell sw_vers -productVersion | cut -d '.' -f 1)
|
||||||
|
else
|
||||||
|
LIBS = -lm
|
||||||
|
LDFLAGS += -rdynamic
|
||||||
|
LIBS += -ldl
|
||||||
|
endif
|
||||||
|
|
||||||
|
EXE_EXT ?=
|
||||||
|
|
||||||
|
# -- SDL3 detection -----------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM),windows)
|
||||||
|
# Windows: always vendor SDL3, ship SDL3.dll alongside the .exe
|
||||||
|
SDL3_BUILD_DIR := $(firstword $(wildcard lib/SDL/build/Release lib/SDL/build/Debug))
|
||||||
|
ifneq ($(SDL3_BUILD_DIR),)
|
||||||
|
SDL3_CFLAGS = -Ilib/SDL/include
|
||||||
|
SDL3_LIBS = -L$(SDL3_BUILD_DIR) -lSDL3
|
||||||
|
HAS_SDL3 = 1
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
# Unix: check vendored build, then system
|
||||||
|
ifneq (,$(wildcard lib/SDL/build/libSDL3.*))
|
||||||
|
SDL3_CFLAGS = -Ilib/SDL/include
|
||||||
|
SDL3_LIBS = -Llib/SDL/build -lSDL3 -Wl,-rpath,$(CURDIR)/lib/SDL/build
|
||||||
|
HAS_SDL3 = 1
|
||||||
|
else
|
||||||
|
SDL3_CFLAGS := $(shell pkg-config --cflags sdl3 2>/dev/null)
|
||||||
|
SDL3_LIBS := $(shell pkg-config --libs sdl3 2>/dev/null)
|
||||||
|
ifneq ($(SDL3_LIBS),)
|
||||||
|
HAS_SDL3 = 1
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS += $(SDL3_CFLAGS)
|
||||||
|
LIBS += $(SDL3_LIBS)
|
||||||
|
|
||||||
|
# -- Linenoise ----------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM),windows)
|
||||||
|
LINENOISE_DIR = lib/linenoise-win32
|
||||||
|
LINENOISE_SRCS = $(LINENOISE_DIR)/linenoise.c $(LINENOISE_DIR)/stringbuf.c $(LINENOISE_DIR)/utf8.c
|
||||||
|
CFLAGS += -DPXL8_WIN32_LINENOISE -D_CRT_SECURE_NO_WARNINGS
|
||||||
|
else
|
||||||
|
LINENOISE_DIR = lib/linenoise
|
||||||
|
LINENOISE_SRCS = $(LINENOISE_DIR)/linenoise.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -- LuaJIT ------------------------------------------------------------------
|
||||||
|
|
||||||
|
LUAJIT_LIB = lib/luajit/src/libluajit.a
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM),windows)
|
||||||
|
LUAJIT_BUILD = $(MAKE) -C lib/luajit -j$(NPROC) \
|
||||||
|
CC="$(firstword $(CC))" HOST_CC="$(firstword $(CC))" \
|
||||||
|
TARGET_SYS=Windows BUILDMODE=static \
|
||||||
|
MINILUA_LIBS= TARGET_XLIBS= Q=
|
||||||
|
else
|
||||||
|
LUAJIT_BUILD = $(MAKE) -C lib/luajit -j$(NPROC) \
|
||||||
|
CC="$(firstword $(CC))" HOST_CC="$(firstword $(CC))" Q= > /dev/null 2>&1
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -- Include paths ------------------------------------------------------------
|
||||||
|
|
||||||
|
INCLUDES = \
|
||||||
|
-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal \
|
||||||
|
-Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/shader \
|
||||||
|
-Isrc/sfx -Isrc/sim -Isrc/world \
|
||||||
|
-I$(LINENOISE_DIR) -Ilib/luajit/src -Ilib/miniz -I.build/shaders/c
|
||||||
|
|
||||||
|
# -- Source files -------------------------------------------------------------
|
||||||
|
|
||||||
|
LIB_SRCS = $(LINENOISE_SRCS) lib/miniz/miniz.c
|
||||||
|
|
||||||
|
PXL8_SRCS = \
|
||||||
|
src/asset/pxl8_ase.c \
|
||||||
|
src/asset/pxl8_cart.c \
|
||||||
|
src/asset/pxl8_save.c \
|
||||||
|
src/bsp/pxl8_bsp.c \
|
||||||
|
src/bsp/pxl8_bsp_render.c \
|
||||||
|
src/core/pxl8.c \
|
||||||
|
src/core/pxl8_bytes.c \
|
||||||
|
src/core/pxl8_io.c \
|
||||||
|
src/core/pxl8_log.c \
|
||||||
|
src/core/pxl8_replay.c \
|
||||||
|
src/core/pxl8_rng.c \
|
||||||
|
src/gfx/pxl8_3d_camera.c \
|
||||||
|
src/gfx/pxl8_anim.c \
|
||||||
|
src/gfx/pxl8_atlas.c \
|
||||||
|
src/gfx/pxl8_blit.c \
|
||||||
|
src/gfx/pxl8_colormap.c \
|
||||||
|
src/gfx/pxl8_dither.c \
|
||||||
|
src/gfx/pxl8_render.c \
|
||||||
|
src/gfx/pxl8_shader_registry.c \
|
||||||
|
src/gfx/pxl8_shader_runtime.c \
|
||||||
|
src/gfx/pxl8_font.c \
|
||||||
|
src/gfx/pxl8_gfx.c \
|
||||||
|
src/gfx/pxl8_glows.c \
|
||||||
|
src/gfx/pxl8_lightmap.c \
|
||||||
|
src/gfx/pxl8_lights.c \
|
||||||
|
src/gfx/pxl8_mesh.c \
|
||||||
|
src/gfx/pxl8_palette.c \
|
||||||
|
src/gfx/pxl8_particles.c \
|
||||||
|
src/gfx/pxl8_tilemap.c \
|
||||||
|
src/gfx/pxl8_tilesheet.c \
|
||||||
|
src/gfx/pxl8_transition.c \
|
||||||
|
src/gui/pxl8_gui.c \
|
||||||
|
src/hal/pxl8_hal_sdl3.c \
|
||||||
|
src/hal/pxl8_thread_sdl3.c \
|
||||||
|
src/math/pxl8_math.c \
|
||||||
|
src/math/pxl8_noise.c \
|
||||||
|
src/net/pxl8_net.c \
|
||||||
|
src/net/pxl8_protocol.c \
|
||||||
|
src/procgen/pxl8_graph.c \
|
||||||
|
src/script/pxl8_repl.c \
|
||||||
|
src/script/pxl8_script.c \
|
||||||
|
src/sfx/pxl8_sfx.c \
|
||||||
|
src/sim/pxl8_sim.c \
|
||||||
|
src/world/pxl8_entity.c \
|
||||||
|
src/world/pxl8_world.c \
|
||||||
|
src/world/pxl8_world_chunk.c \
|
||||||
|
src/world/pxl8_world_chunk_cache.c
|
||||||
|
|
||||||
|
ifeq ($(HAS_SDL3),1)
|
||||||
|
PXL8_SRCS += src/hal/pxl8_io_sdl3.c src/hal/pxl8_mem_sdl3.c
|
||||||
|
else
|
||||||
|
PXL8_SRCS += src/hal/pxl8_mem.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
# -- Object files -------------------------------------------------------------
|
||||||
|
|
||||||
|
LIB_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(LIB_SRCS)))
|
||||||
|
PXL8_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(notdir $(PXL8_SRCS)))
|
||||||
|
ALL_OBJS = $(LIB_OBJS) $(PXL8_OBJS)
|
||||||
|
|
||||||
|
# -- Shader objects -----------------------------------------------------------
|
||||||
|
|
||||||
|
SHADER_SRCS = $(wildcard src/gfx/shaders/cpu/*.c)
|
||||||
|
SHADER_OBJS = $(patsubst src/gfx/shaders/cpu/%.c,$(SHADERDIR)/obj/%.o,$(SHADER_SRCS))
|
||||||
|
SHADER_INCLUDES = -Isrc/core -Isrc/gfx -Isrc/math
|
||||||
|
|
||||||
|
# -- Targets ------------------------------------------------------------------
|
||||||
|
|
||||||
|
CLIENT = $(BINDIR)/pxl8$(EXE_EXT)
|
||||||
|
SERVER = $(BINDIR)/pxl8d$(EXE_EXT)
|
||||||
|
|
||||||
|
CART ?= demo
|
||||||
|
PROFILE_DURATION ?= 30
|
||||||
|
ASE_CMD ?=
|
||||||
|
|
||||||
|
.PHONY: all release client server run clean clean-all clean-deps clean-cache \
|
||||||
|
update vendor-sdl deps luajit install ase profile help
|
||||||
|
|
||||||
|
all: client server
|
||||||
|
|
||||||
|
release:
|
||||||
|
$(MAKE) MODE=release all
|
||||||
|
|
||||||
|
# -- Dependencies -------------------------------------------------------------
|
||||||
|
|
||||||
|
DEPS_OK = lib/luajit/src/luajit.c lib/miniz/miniz.c lib/fennel/fennel.lua $(LINENOISE_DIR)/linenoise.c
|
||||||
|
|
||||||
|
deps: $(DEPS_OK)
|
||||||
|
|
||||||
|
lib/luajit/src/luajit.c:
|
||||||
|
@echo "[INFO] Fetching LuaJIT"
|
||||||
|
@rm -rf lib/luajit
|
||||||
|
@git clone --quiet --branch v2.1 https://github.com/LuaJIT/LuaJIT.git lib/luajit
|
||||||
|
|
||||||
|
lib/linenoise/linenoise.c:
|
||||||
|
@mkdir -p lib/linenoise
|
||||||
|
@echo "[INFO] Fetching linenoise"
|
||||||
|
@curl -sL -o lib/linenoise/linenoise.c https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.c
|
||||||
|
@curl -sL -o lib/linenoise/linenoise.h https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.h
|
||||||
|
|
||||||
|
lib/linenoise-win32/linenoise.c:
|
||||||
|
@echo "[INFO] Fetching linenoise-win32"
|
||||||
|
@rm -rf lib/linenoise-win32
|
||||||
|
@git clone --quiet --depth 1 https://github.com/msteveb/linenoise.git lib/linenoise-win32
|
||||||
|
|
||||||
|
lib/miniz/miniz.c:
|
||||||
|
@mkdir -p lib/miniz
|
||||||
|
@echo "[INFO] Fetching miniz"
|
||||||
|
@curl -sL -o /tmp/miniz.zip "https://github.com/richgel999/miniz/releases/download/3.1.0/miniz-3.1.0.zip"
|
||||||
|
@unzip -qjo /tmp/miniz.zip miniz.c miniz.h -d lib/miniz/
|
||||||
|
@rm -f /tmp/miniz.zip
|
||||||
|
|
||||||
|
lib/fennel/fennel.lua:
|
||||||
|
@mkdir -p lib/fennel
|
||||||
|
@echo "[INFO] Fetching Fennel"
|
||||||
|
@curl -sL -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-1.6.1.lua"
|
||||||
|
|
||||||
|
update:
|
||||||
|
@rm -f lib/linenoise/linenoise.c lib/miniz/miniz.c lib/fennel/fennel.lua
|
||||||
|
@rm -rf lib/luajit lib/linenoise-win32
|
||||||
|
$(MAKE) deps
|
||||||
|
|
||||||
|
vendor-sdl:
|
||||||
|
@echo "[INFO] Fetching SDL3"
|
||||||
|
@if [ -d lib/SDL/.git ]; then cd lib/SDL && git pull --quiet origin main; \
|
||||||
|
else rm -rf lib/SDL && git clone --quiet https://github.com/libsdl-org/SDL.git lib/SDL; fi
|
||||||
|
@echo "[INFO] Building SDL3"
|
||||||
|
@mkdir -p lib/SDL/build && cd lib/SDL/build && \
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release && \
|
||||||
|
cmake --build . --config Release --parallel $(NPROC)
|
||||||
|
@echo "[INFO] Built SDL3"
|
||||||
|
|
||||||
|
# -- LuaJIT build ------------------------------------------------------------
|
||||||
|
|
||||||
|
luajit: $(LUAJIT_LIB)
|
||||||
|
|
||||||
|
$(LUAJIT_LIB): lib/luajit/src/luajit.c
|
||||||
|
@echo "[INFO] Building LuaJIT"
|
||||||
|
@$(LUAJIT_BUILD)
|
||||||
|
@echo "[INFO] Built LuaJIT"
|
||||||
|
|
||||||
|
# -- Server (Rust) ------------------------------------------------------------
|
||||||
|
|
||||||
|
server: $(SERVER)
|
||||||
|
|
||||||
|
$(SERVER): $(wildcard pxl8d/src/*.rs pxl8d/Cargo.toml)
|
||||||
|
@mkdir -p $(BINDIR)
|
||||||
|
@echo "[INFO] Building pxl8d ($(MODE) mode)"
|
||||||
|
ifeq ($(MODE),release)
|
||||||
|
@cd pxl8d && PATH="$$(echo $$PATH | tr ':' '\n' | grep -v '/usr/bin' | tr '\n' ':')" cargo build --release --quiet
|
||||||
|
@cp pxl8d/target/release/pxl8d$(EXE_EXT) $(SERVER)
|
||||||
|
else
|
||||||
|
@cd pxl8d && PATH="$$(echo $$PATH | tr ':' '\n' | grep -v '/usr/bin' | tr '\n' ':')" cargo build --quiet
|
||||||
|
@cp pxl8d/target/debug/pxl8d$(EXE_EXT) $(SERVER)
|
||||||
|
endif
|
||||||
|
@echo "[INFO] Built pxl8d"
|
||||||
|
|
||||||
|
# -- Client (C23) -------------------------------------------------------------
|
||||||
|
|
||||||
|
client: deps $(CLIENT)
|
||||||
|
|
||||||
|
$(CLIENT): $(ALL_OBJS) $(SHADER_OBJS) $(LUAJIT_LIB)
|
||||||
|
@mkdir -p $(BINDIR)
|
||||||
|
@echo "[INFO] Linking pxl8"
|
||||||
|
ifeq ($(PLATFORM),windows)
|
||||||
|
@(echo EXPORTS && llvm-nm --defined-only $(ALL_OBJS) $(SHADER_OBJS) | grep ' T pxl8_\| T SDL' | awk '{print $$3}') > $(PXL8_DEF)
|
||||||
|
@MSYS_NO_PATHCONV=1 $(CC) $(LDFLAGS) $(ALL_OBJS) $(SHADER_OBJS) $(LUAJIT_LIB) $(LIBS) -Wl,/DEF:$(PXL8_DEF) -o $@
|
||||||
|
@cp -u $(SDL3_BUILD_DIR)/SDL3.dll $(BINDIR)/
|
||||||
|
else
|
||||||
|
@$(CC) $(LDFLAGS) $(ALL_OBJS) $(SHADER_OBJS) $(LUAJIT_LIB) $(LIBS) -o $@
|
||||||
|
endif
|
||||||
|
@echo "[INFO] Built pxl8 -> $@"
|
||||||
|
|
||||||
|
# -- Compile rules ------------------------------------------------------------
|
||||||
|
|
||||||
|
# Lib sources (dep flags, no warnings)
|
||||||
|
$(OBJDIR)/linenoise.o: $(LINENOISE_DIR)/linenoise.c | $(OBJDIR)
|
||||||
|
@echo "[CC] $<"
|
||||||
|
@$(CC) -c $(DEP_CFLAGS) $(INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/stringbuf.o: $(LINENOISE_DIR)/stringbuf.c | $(OBJDIR)
|
||||||
|
@echo "[CC] $<"
|
||||||
|
@$(CC) -c $(DEP_CFLAGS) $(INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/utf8.o: $(LINENOISE_DIR)/utf8.c | $(OBJDIR)
|
||||||
|
@echo "[CC] $<"
|
||||||
|
@$(CC) -c $(DEP_CFLAGS) $(INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
$(OBJDIR)/miniz.o: lib/miniz/miniz.c | $(OBJDIR)
|
||||||
|
@echo "[CC] $<"
|
||||||
|
@$(CC) -c $(DEP_CFLAGS) $(INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
# Pattern rules for pxl8 sources by directory
|
||||||
|
define PXL8_COMPILE_RULE
|
||||||
|
$(OBJDIR)/%.o: $(1)/%.c | $(OBJDIR)
|
||||||
|
@echo "[CC] $$<"
|
||||||
|
@$$(CC) -c $$(CFLAGS) $$(INCLUDES) $$< -o $$@
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach dir,src/asset src/bsp src/core src/gfx src/gui src/hal src/math src/net src/procgen src/script src/shader src/sfx src/sim src/world,$(eval $(call PXL8_COMPILE_RULE,$(dir))))
|
||||||
|
|
||||||
|
# pxl8_script.o depends on embedded Lua/Fennel sources
|
||||||
|
LUA_SRCS = $(wildcard src/lua/*.lua src/lua/pxl8/*.lua) lib/fennel/fennel.lua
|
||||||
|
$(OBJDIR)/pxl8_script.o: $(LUA_SRCS)
|
||||||
|
|
||||||
|
# Shader objects
|
||||||
|
$(SHADERDIR)/obj/%.o: src/gfx/shaders/cpu/%.c | $(SHADERDIR)/obj
|
||||||
|
@echo "[SHADER] $<"
|
||||||
|
@$(CC) -c -O2 $(SHADER_INCLUDES) $< -o $@
|
||||||
|
|
||||||
|
# -- Directory creation -------------------------------------------------------
|
||||||
|
|
||||||
|
$(OBJDIR) $(SHADERDIR)/obj:
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
# -- Gitignore bookkeeping ---------------------------------------------------
|
||||||
|
|
||||||
|
$(shell mkdir -p bin .build lib 2>/dev/null)
|
||||||
|
$(shell [ -f bin/.gitignore ] || echo '*' > bin/.gitignore)
|
||||||
|
$(shell [ -f .build/.gitignore ] || echo '*' > .build/.gitignore)
|
||||||
|
$(shell [ -f lib/.gitignore ] || echo '*' > lib/.gitignore)
|
||||||
|
|
||||||
|
# -- Run ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
run: all
|
||||||
|
@$(BINDIR)/pxl8d$(EXE_EXT) & SERVER_PID=$$!; \
|
||||||
|
sleep 0.5; \
|
||||||
|
$(BINDIR)/pxl8$(EXE_EXT) $(CART) || true; \
|
||||||
|
kill $$SERVER_PID 2>/dev/null; wait $$SERVER_PID 2>/dev/null || true
|
||||||
|
|
||||||
|
# -- Clean --------------------------------------------------------------------
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .build/debug .build/release bin/debug bin/release .build/shaders
|
||||||
|
@if [ -d pxl8d ]; then cd pxl8d && cargo clean 2>/dev/null; fi || true
|
||||||
|
|
||||||
|
clean-all: clean
|
||||||
|
rm -rf lib
|
||||||
|
|
||||||
|
clean-deps:
|
||||||
|
rm -rf lib
|
||||||
|
|
||||||
|
clean-cache:
|
||||||
|
@if command -v ccache >/dev/null 2>&1; then ccache -C; else echo "ccache not found"; fi
|
||||||
|
|
||||||
|
# -- Install ------------------------------------------------------------------
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(MAKE) MODE=release client
|
||||||
|
@mkdir -p $(HOME)/.local/bin
|
||||||
|
@cp bin/release/pxl8$(EXE_EXT) $(HOME)/.local/bin/pxl8$(EXE_EXT)
|
||||||
|
@chmod +x $(HOME)/.local/bin/pxl8$(EXE_EXT)
|
||||||
|
@echo "[INFO] Installed pxl8 to $(HOME)/.local/bin/pxl8$(EXE_EXT)"
|
||||||
|
|
||||||
|
# -- Aseprite tools -----------------------------------------------------------
|
||||||
|
|
||||||
|
ase:
|
||||||
|
@bash tools/aseprite/pxl8-ase.sh $(ASE_CMD)
|
||||||
|
|
||||||
|
# -- Profile (Linux) ----------------------------------------------------------
|
||||||
|
|
||||||
|
profile: client
|
||||||
|
@if [ "$$(uname)" != "Linux" ]; then echo "[ERROR] Profiling requires Linux + perf"; exit 1; fi
|
||||||
|
@if ! command -v perf >/dev/null 2>&1; then echo "[ERROR] perf not found"; exit 1; fi
|
||||||
|
@if [ ! -d lib/FlameGraph ]; then \
|
||||||
|
echo "[INFO] Fetching FlameGraph"; \
|
||||||
|
git clone --quiet https://github.com/brendangregg/FlameGraph.git lib/FlameGraph; \
|
||||||
|
fi
|
||||||
|
@mkdir -p .build/$(MODE)/profile; \
|
||||||
|
TIMESTAMP=$$(date +%Y%m%d_%H%M%S); \
|
||||||
|
PDIR=.build/$(MODE)/profile; \
|
||||||
|
echo "[INFO] Starting server..."; \
|
||||||
|
$(BINDIR)/pxl8d$(EXE_EXT) & SERVER_PID=$$!; \
|
||||||
|
sleep 0.5; \
|
||||||
|
echo "[INFO] Profiling for $(PROFILE_DURATION)s (Ctrl+C to stop early)..."; \
|
||||||
|
perf record -F 99 -g --call-graph dwarf -o $$PDIR/perf_$$TIMESTAMP.data -- \
|
||||||
|
timeout $(PROFILE_DURATION)s $(BINDIR)/pxl8$(EXE_EXT) $(CART) 2>/dev/null || true; \
|
||||||
|
kill $$SERVER_PID 2>/dev/null; wait $$SERVER_PID 2>/dev/null || true; \
|
||||||
|
echo "[INFO] Generating flamegraph..."; \
|
||||||
|
perf script -i $$PDIR/perf_$$TIMESTAMP.data > $$PDIR/perf_$$TIMESTAMP.perf; \
|
||||||
|
lib/FlameGraph/stackcollapse-perf.pl $$PDIR/perf_$$TIMESTAMP.perf > $$PDIR/perf_$$TIMESTAMP.folded; \
|
||||||
|
lib/FlameGraph/flamegraph.pl --cp --colors orange --title "pxl8 profile" \
|
||||||
|
$$PDIR/perf_$$TIMESTAMP.folded > $$PDIR/flamegraph_$$TIMESTAMP.svg; \
|
||||||
|
rm -f $$PDIR/perf_$$TIMESTAMP.data $$PDIR/perf_$$TIMESTAMP.perf $$PDIR/perf_$$TIMESTAMP.folded; \
|
||||||
|
echo "[INFO] Flamegraph: $$PDIR/flamegraph_$$TIMESTAMP.svg"; \
|
||||||
|
command -v xdg-open >/dev/null 2>&1 && xdg-open $$PDIR/flamegraph_$$TIMESTAMP.svg 2>/dev/null &
|
||||||
|
|
||||||
|
# -- Help ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "pxl8 - framework build system"
|
||||||
|
@echo ""
|
||||||
|
@echo "TARGETS:"
|
||||||
|
@echo " make Build everything (debug)"
|
||||||
|
@echo " make release Build everything (release)"
|
||||||
|
@echo " make run Build and run (CART=\"demo --repl\")"
|
||||||
|
@echo " make client Build pxl8 only"
|
||||||
|
@echo " make server Build pxl8d only"
|
||||||
|
@echo " make install Install to ~/.local/bin (release)"
|
||||||
|
@echo " make profile Profile with perf (PROFILE_DURATION=30, Linux only)"
|
||||||
|
@echo " make ase Aseprite tools (ASE_CMD=package|clean)"
|
||||||
|
@echo " make clean Remove build artifacts"
|
||||||
|
@echo " make clean-all Remove artifacts + dependencies"
|
||||||
|
@echo " make clean-deps Remove only dependencies"
|
||||||
|
@echo " make clean-cache Clear ccache"
|
||||||
|
@echo " make update Fetch/update all dependencies"
|
||||||
|
@echo " make vendor-sdl Vendor SDL3 from source"
|
||||||
|
@echo ""
|
||||||
|
@echo "VARIABLES:"
|
||||||
|
@echo " MODE=release Build mode (default: debug)"
|
||||||
|
@echo " CART=demo Cart or script to run"
|
||||||
|
|
||||||
|
# -- Auto-generated header dependencies --------------------------------------
|
||||||
|
|
||||||
|
-include $(ALL_OBJS:.o=.d)
|
||||||
|
-include $(SHADER_OBJS:.o=.d)
|
||||||
|
|
||||||
|
# -- compile_commands.json ----------------------------------------------------
|
||||||
|
|
||||||
|
compile_commands.json: $(LIB_SRCS) $(PXL8_SRCS)
|
||||||
|
@echo "[INFO] Generating compile_commands.json"
|
||||||
|
@echo '[' > $@.tmp
|
||||||
|
@first=true; \
|
||||||
|
for f in $(LIB_SRCS); do \
|
||||||
|
if $$first; then first=false; else printf ',\n' >> $@.tmp; fi; \
|
||||||
|
printf ' {"directory":"%s","command":"clang -c %s %s","file":"%s"}' \
|
||||||
|
"$(CURDIR)" "$(DEP_CFLAGS) $(INCLUDES)" "$$f" "$$f" >> $@.tmp; \
|
||||||
|
done; \
|
||||||
|
for f in $(PXL8_SRCS); do \
|
||||||
|
if $$first; then first=false; else printf ',\n' >> $@.tmp; fi; \
|
||||||
|
printf ' {"directory":"%s","command":"clang -c %s %s","file":"%s"}' \
|
||||||
|
"$(CURDIR)" "$(CFLAGS) $(INCLUDES)" "$$f" "$$f" >> $@.tmp; \
|
||||||
|
done
|
||||||
|
@printf '\n]\n' >> $@.tmp
|
||||||
|
@mv $@.tmp $@
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
### Quick Start
|
### Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./pxl8.sh build # Build pxl8 system
|
make # Build pxl8 system
|
||||||
./pxl8.sh install # Install pxl8 binary to ~/.local/bin
|
make install # Install pxl8 binary to ~/.local/bin
|
||||||
./pxl8.sh run [game.cart | project_dir] # Run pxl8 demo (or a specific game)
|
make run # Run pxl8 demo
|
||||||
./pxl8.sh run [game.cart | project_dir] --repl # Run pxl8 demo (or a specific game) with a REPL
|
make run CART="game.cart --repl" # Run a specific game with a REPL
|
||||||
```
|
```
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
- **Clang 19+** (or GCC 15+) - Required for C23 `#embed` support
|
- **Clang 19+** (or GCC 15+) - Required for C23 `#embed` support
|
||||||
- **Rust nightly** (edition 2024) - Required for [pxl8d](pxl8d) server
|
- **Rust nightly** (edition 2024) - Required for [pxl8d](pxl8d) server
|
||||||
- **SDL3** - System package or auto-vendored
|
- **SDL3** - System package or auto-vendored
|
||||||
|
- **MSYS2** (Windows only) - Provides `make` and Unix tools (`scoop install msys2`)
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|
|
||||||
818
pxl8.sh
818
pxl8.sh
|
|
@ -1,818 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
CC="${CC:-clang}"
|
|
||||||
|
|
||||||
if command -v ccache >/dev/null 2>&1; then
|
|
||||||
CC="ccache $CC"
|
|
||||||
fi
|
|
||||||
|
|
||||||
CFLAGS="-std=c23 -Wall -Wextra -Wno-missing-braces"
|
|
||||||
LIBS="-lm"
|
|
||||||
MODE="debug"
|
|
||||||
BUILDDIR=".build"
|
|
||||||
BINDIR="bin"
|
|
||||||
|
|
||||||
if command -v mold >/dev/null 2>&1; then
|
|
||||||
LINKER_FLAGS="-fuse-ld=mold"
|
|
||||||
else
|
|
||||||
LINKER_FLAGS=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$(uname)" in
|
|
||||||
Linux)
|
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
|
||||||
LIBS="$LIBS -ldl"
|
|
||||||
;;
|
|
||||||
Darwin)
|
|
||||||
export MACOSX_DEPLOYMENT_TARGET="$(sw_vers -productVersion | cut -d '.' -f 1)"
|
|
||||||
;;
|
|
||||||
MINGW*|MSYS*|CYGWIN*)
|
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -Wl,--export-all-symbols"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -rdynamic"
|
|
||||||
LIBS="$LIBS -ldl"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
BOLD='\033[1m'
|
|
||||||
GREEN='\033[38;2;184;187;38m'
|
|
||||||
NC='\033[0m'
|
|
||||||
RED='\033[38;2;251;73;52m'
|
|
||||||
YELLOW='\033[38;2;250;189;47m'
|
|
||||||
|
|
||||||
build_luajit() {
|
|
||||||
if [[ ! -f "lib/luajit/src/libluajit.a" ]]; then
|
|
||||||
print_info "Building LuaJIT"
|
|
||||||
cd lib/luajit
|
|
||||||
|
|
||||||
make clean >/dev/null 2>&1 || true
|
|
||||||
make -j$(nproc 2>/dev/null || echo 4) > /dev/null 2>&1
|
|
||||||
cd - > /dev/null
|
|
||||||
print_info "Built LuaJIT"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
build_server() {
|
|
||||||
local mode="$1"
|
|
||||||
local server_bin
|
|
||||||
if [[ "$mode" == "release" ]]; then
|
|
||||||
server_bin="pxl8d/target/release/pxl8d"
|
|
||||||
else
|
|
||||||
server_bin="pxl8d/target/debug/pxl8d"
|
|
||||||
fi
|
|
||||||
if [[ -d "pxl8d" ]]; then
|
|
||||||
print_info "Building pxl8d ($mode mode)"
|
|
||||||
cd pxl8d
|
|
||||||
if [[ "$mode" == "release" ]]; then
|
|
||||||
cargo build --release --quiet
|
|
||||||
else
|
|
||||||
cargo build --quiet
|
|
||||||
fi
|
|
||||||
local status=$?
|
|
||||||
cd - > /dev/null
|
|
||||||
if [[ $status -eq 0 ]]; then
|
|
||||||
mkdir -p "$BINDIR"
|
|
||||||
cp "$server_bin" "$BINDIR/pxl8d"
|
|
||||||
print_info "Built pxl8d"
|
|
||||||
else
|
|
||||||
print_error "pxl8d build failed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
start_server() {
|
|
||||||
local server_bin="$BINDIR/pxl8d"
|
|
||||||
if [[ -f "$server_bin" ]]; then
|
|
||||||
print_info "Starting server..."
|
|
||||||
./$server_bin &
|
|
||||||
SERVER_PID=$!
|
|
||||||
print_info "Server started with PID $SERVER_PID"
|
|
||||||
sleep 0.5
|
|
||||||
else
|
|
||||||
print_error "pxl8d binary not found: $server_bin"
|
|
||||||
print_error "Build first with: ./pxl8.sh build"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_server() {
|
|
||||||
if [[ -n "$SERVER_PID" ]]; then
|
|
||||||
print_info "Stopping server"
|
|
||||||
kill $SERVER_PID 2>/dev/null || true
|
|
||||||
wait $SERVER_PID 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
clean_server() {
|
|
||||||
if [[ -d "pxl8d" ]]; then
|
|
||||||
print_info "Cleaning pxl8d"
|
|
||||||
cd pxl8d
|
|
||||||
cargo clean 2>/dev/null || true
|
|
||||||
cd - > /dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
build_sdl() {
|
|
||||||
if [[ ! -f "lib/SDL/build/libSDL3.so" ]] && [[ ! -f "lib/SDL/build/libSDL3.a" ]] && [[ ! -f "lib/SDL/build/libSDL3.dylib" ]]; then
|
|
||||||
print_info "Building SDL3"
|
|
||||||
mkdir -p lib/SDL/build
|
|
||||||
cd lib/SDL/build
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=Release > /dev/null 2>&1
|
|
||||||
cmake --build . --parallel $(nproc 2>/dev/null || echo 4) > /dev/null 2>&1
|
|
||||||
cd - > /dev/null
|
|
||||||
print_info "Built SDL3"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix_output() {
|
|
||||||
local in_warning=false
|
|
||||||
while IFS= read -r line; do
|
|
||||||
if [[ "$line" == *": warning:"* ]] || [[ "$line" == *": note:"* ]]; then
|
|
||||||
in_warning=true
|
|
||||||
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
|
||||||
elif [[ "$line" == *": error:"* ]] || [[ "$line" == *": fatal error:"* ]]; then
|
|
||||||
in_warning=false
|
|
||||||
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $line" >&2
|
|
||||||
elif [[ "$line" =~ ^[0-9]+\ (warning|error)s?\ generated ]] || [[ -z "$line" ]]; then
|
|
||||||
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
|
||||||
elif $in_warning; then
|
|
||||||
echo -e "${YELLOW}${BOLD}[$(timestamp) WARN]${NC} $line" >&2
|
|
||||||
else
|
|
||||||
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $line" >&2
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
compile_source_file() {
|
|
||||||
local src_file="$1"
|
|
||||||
local obj_file="$2"
|
|
||||||
local compile_flags="$3"
|
|
||||||
|
|
||||||
print_info "Compiling: $src_file"
|
|
||||||
local output
|
|
||||||
local exit_code
|
|
||||||
output=$($CC -c $compile_flags "$src_file" -o "$obj_file" 2>&1) || exit_code=$?
|
|
||||||
if [[ -n "$output" ]]; then
|
|
||||||
echo "$output" | prefix_output
|
|
||||||
fi
|
|
||||||
if [[ -n "$exit_code" ]]; then
|
|
||||||
print_error "Compilation failed for $src_file"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_compile_commands() {
|
|
||||||
local project_dir
|
|
||||||
project_dir="$(pwd)"
|
|
||||||
local tmpfile
|
|
||||||
tmpfile=$(mktemp)
|
|
||||||
local first=true
|
|
||||||
|
|
||||||
echo "[" > "$tmpfile"
|
|
||||||
|
|
||||||
for src_file in $LIB_SOURCE_FILES; do
|
|
||||||
src_file="${src_file## }"
|
|
||||||
src_file="${src_file%% }"
|
|
||||||
[[ -z "$src_file" ]] && continue
|
|
||||||
if $first; then first=false; else printf ",\n" >> "$tmpfile"; fi
|
|
||||||
printf ' {\n "directory": "%s",\n "command": "clang -c %s %s",\n "file": "%s"\n }' \
|
|
||||||
"$project_dir" "$DEP_COMPILE_FLAGS" "$src_file" "$src_file" >> "$tmpfile"
|
|
||||||
done
|
|
||||||
|
|
||||||
for src_file in $PXL8_SOURCE_FILES; do
|
|
||||||
src_file="${src_file## }"
|
|
||||||
src_file="${src_file%% }"
|
|
||||||
[[ -z "$src_file" ]] && continue
|
|
||||||
if $first; then first=false; else printf ",\n" >> "$tmpfile"; fi
|
|
||||||
printf ' {\n "directory": "%s",\n "command": "clang -c %s %s",\n "file": "%s"\n }' \
|
|
||||||
"$project_dir" "$COMPILE_FLAGS" "$src_file" "$src_file" >> "$tmpfile"
|
|
||||||
done
|
|
||||||
|
|
||||||
printf "\n]\n" >> "$tmpfile"
|
|
||||||
mv "$tmpfile" "compile_commands.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
compile_shaders() {
|
|
||||||
local build_mode="$1"
|
|
||||||
local shader_dir="src/gfx/shaders/cpu"
|
|
||||||
local so_dir=".build/$build_mode/shaders/cpu"
|
|
||||||
local obj_dir=".build/$build_mode/shaders/cpu/obj"
|
|
||||||
|
|
||||||
if [[ ! -d "$shader_dir" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ "$build_mode" == "debug" ]] && mkdir -p "$so_dir"
|
|
||||||
mkdir -p "$obj_dir"
|
|
||||||
|
|
||||||
local SHADER_INCLUDES="-Isrc/core -Isrc/gfx -Isrc/math"
|
|
||||||
case "$(uname)" in
|
|
||||||
Darwin) SO_EXT="dylib" ;;
|
|
||||||
*) SO_EXT="so" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Compile C shaders directly
|
|
||||||
for shader in $(find "$shader_dir" -maxdepth 1 -name "*.c" 2>/dev/null); do
|
|
||||||
local shader_name=$(basename "${shader%.c}")
|
|
||||||
local so_file="$so_dir/${shader_name}.$SO_EXT"
|
|
||||||
local obj_file="$obj_dir/${shader_name}.o"
|
|
||||||
|
|
||||||
# Debug: compile to .so for hot-reload
|
|
||||||
if [[ "$build_mode" == "debug" ]]; then
|
|
||||||
if [[ "$shader" -nt "$so_file" ]] || [[ ! -f "$so_file" ]]; then
|
|
||||||
$CC -shared -fPIC -O2 $SHADER_INCLUDES "$shader" -o "$so_file" 2>&1 | prefix_output || {
|
|
||||||
print_error "Shader build failed: $shader_name"
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Compile to .o for release static linking
|
|
||||||
if [[ "$shader" -nt "$obj_file" ]] || [[ ! -f "$obj_file" ]]; then
|
|
||||||
$CC -c -O2 $SHADER_INCLUDES "$shader" -o "$obj_file" 2>&1 | prefix_output || {
|
|
||||||
print_error "Shader compile failed: $shader_name"
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Export shader object files for linking
|
|
||||||
SHADER_OBJECTS=""
|
|
||||||
for obj in $(find "$obj_dir" -name "*.o" 2>/dev/null); do
|
|
||||||
SHADER_OBJECTS="$SHADER_OBJECTS $obj"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
make_lib_dirs() {
|
|
||||||
mkdir -p lib/linenoise lib/fennel lib/miniz
|
|
||||||
}
|
|
||||||
|
|
||||||
print_error() {
|
|
||||||
echo -e "${RED}${BOLD}[$(timestamp) ERROR]${NC} $1" >&2
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info() {
|
|
||||||
echo -e "${GREEN}${BOLD}[$(timestamp) INFO]${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_usage() {
|
|
||||||
echo -e "${BOLD}pxl8${NC} - framework build tool"
|
|
||||||
echo
|
|
||||||
echo -e "${BOLD}USAGE:${NC}"
|
|
||||||
echo " ./pxl8.sh <command> [options]"
|
|
||||||
echo
|
|
||||||
echo -e "${BOLD}COMMANDS:${NC}"
|
|
||||||
echo " ase Aseprite tools (use 'ase help' for subcommands)"
|
|
||||||
echo " build Build pxl8"
|
|
||||||
echo " clean Remove build artifacts"
|
|
||||||
echo " help Show this help message"
|
|
||||||
echo " install Install pxl8 to ~/.local/bin"
|
|
||||||
echo " profile Profile with perf and generate flamegraph (Linux)"
|
|
||||||
echo " run Build and run pxl8 (optional: cart.pxc or folder)"
|
|
||||||
echo " update Download/update all dependencies"
|
|
||||||
echo " vendor Fetch source for dependencies (ex. SDL3)"
|
|
||||||
echo
|
|
||||||
echo -e "${BOLD}OPTIONS:${NC}"
|
|
||||||
echo " --all Clean both build artifacts and dependencies"
|
|
||||||
echo " --cache Clear ccache (use with clean)"
|
|
||||||
echo " --deps Clean only dependencies"
|
|
||||||
echo " --duration=N Profile duration in seconds (default: 30)"
|
|
||||||
echo " --release Build/run/clean in release mode (default: debug)"
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_sdl3() {
|
|
||||||
if [[ -d "lib/SDL/build" ]]; then
|
|
||||||
SDL3_CFLAGS="-Ilib/SDL/include"
|
|
||||||
SDL3_LIBS="-Llib/SDL/build -lSDL3"
|
|
||||||
SDL3_RPATH="-Wl,-rpath,$(pwd)/lib/SDL/build"
|
|
||||||
print_info "Using vendored SDL3"
|
|
||||||
else
|
|
||||||
SDL3_CFLAGS=$(pkg-config --cflags sdl3 2>/dev/null || echo "")
|
|
||||||
SDL3_LIBS=$(pkg-config --libs sdl3 2>/dev/null || echo "")
|
|
||||||
SDL3_RPATH=""
|
|
||||||
|
|
||||||
if [[ -z "$SDL3_LIBS" ]]; then
|
|
||||||
case "$(uname)" in
|
|
||||||
Darwin)
|
|
||||||
if [[ -f "/usr/local/include/SDL3/SDL.h" ]]; then
|
|
||||||
SDL3_CFLAGS="-I/usr/local/include/SDL3"
|
|
||||||
SDL3_LIBS="-L/usr/local/lib -lSDL3"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
Linux)
|
|
||||||
if [[ -f "/usr/include/SDL3/SDL.h" ]]; then
|
|
||||||
SDL3_CFLAGS="-I/usr/include/SDL3"
|
|
||||||
SDL3_LIBS="-lSDL3"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
MINGW*|MSYS*|CYGWIN*)
|
|
||||||
if [[ -f "/mingw64/include/SDL3/SDL.h" ]]; then
|
|
||||||
SDL3_CFLAGS="-I/mingw64/include/SDL3"
|
|
||||||
SDL3_LIBS="-lSDL3"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$SDL3_LIBS" ]]; then
|
|
||||||
print_info "System SDL3 not found, vendoring SDL3..."
|
|
||||||
update_sdl
|
|
||||||
build_sdl
|
|
||||||
SDL3_CFLAGS="-Ilib/SDL/include"
|
|
||||||
SDL3_LIBS="-Llib/SDL/build -lSDL3"
|
|
||||||
SDL3_RPATH="-Wl,-rpath,$(pwd)/lib/SDL/build"
|
|
||||||
print_info "Using vendored SDL3"
|
|
||||||
else
|
|
||||||
print_info "Using system SDL3"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
CFLAGS="$CFLAGS $SDL3_CFLAGS"
|
|
||||||
LIBS="$LIBS $SDL3_LIBS $SDL3_RPATH"
|
|
||||||
HAS_SDL3=1
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp() {
|
|
||||||
date +"%H:%M:%S"
|
|
||||||
}
|
|
||||||
|
|
||||||
update_fennel() {
|
|
||||||
print_info "Fetching Fennel"
|
|
||||||
|
|
||||||
local version="1.6.1"
|
|
||||||
|
|
||||||
if curl -sL --max-time 5 -o lib/fennel/fennel.lua "https://fennel-lang.org/downloads/fennel-${version}.lua" 2>/dev/null; then
|
|
||||||
if [[ -f "lib/fennel/fennel.lua" ]] && [[ -s "lib/fennel/fennel.lua" ]]; then
|
|
||||||
print_info "Updated Fennel (${version})"
|
|
||||||
else
|
|
||||||
print_error "Downloaded file is empty or missing"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_error "Failed to download Fennel"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
update_flamegraph() {
|
|
||||||
print_info "Fetching FlameGraph"
|
|
||||||
|
|
||||||
if [[ -d "lib/FlameGraph/.git" ]]; then
|
|
||||||
cd lib/FlameGraph && git pull --quiet origin master
|
|
||||||
cd - > /dev/null
|
|
||||||
else
|
|
||||||
rm -rf lib/FlameGraph
|
|
||||||
git clone --quiet https://github.com/brendangregg/FlameGraph.git lib/FlameGraph
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Updated FlameGraph"
|
|
||||||
}
|
|
||||||
|
|
||||||
update_linenoise() {
|
|
||||||
print_info "Fetching linenoise"
|
|
||||||
|
|
||||||
if curl -sL --max-time 5 -o lib/linenoise/linenoise.c https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.c 2>/dev/null && \
|
|
||||||
curl -sL --max-time 5 -o lib/linenoise/linenoise.h https://raw.githubusercontent.com/antirez/linenoise/master/linenoise.h 2>/dev/null; then
|
|
||||||
print_info "Updated linenoise"
|
|
||||||
else
|
|
||||||
print_error "Failed to download linenoise"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
update_luajit() {
|
|
||||||
print_info "Fetching LuaJIT"
|
|
||||||
|
|
||||||
local version="2.1"
|
|
||||||
local branch="v${version}"
|
|
||||||
|
|
||||||
if [[ -d "lib/luajit/.git" ]]; then
|
|
||||||
cd lib/luajit && git pull --quiet origin ${branch}
|
|
||||||
cd - > /dev/null
|
|
||||||
else
|
|
||||||
rm -rf lib/luajit
|
|
||||||
git clone --quiet --branch ${branch} https://github.com/LuaJIT/LuaJIT.git lib/luajit
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Updated LuaJIT (${version})"
|
|
||||||
}
|
|
||||||
|
|
||||||
update_miniz() {
|
|
||||||
print_info "Fetching miniz"
|
|
||||||
|
|
||||||
local version="3.1.0"
|
|
||||||
|
|
||||||
if curl -sL --max-time 5 -o /tmp/miniz.zip "https://github.com/richgel999/miniz/releases/download/${version}/miniz-${version}.zip" 2>/dev/null; then
|
|
||||||
unzip -qjo /tmp/miniz.zip miniz.c miniz.h -d lib/miniz/ 2>/dev/null
|
|
||||||
rm -f /tmp/miniz.zip
|
|
||||||
|
|
||||||
if [[ -f "lib/miniz/miniz.c" ]] && [[ -f "lib/miniz/miniz.h" ]]; then
|
|
||||||
print_info "Updated miniz (${version})"
|
|
||||||
else
|
|
||||||
print_error "Failed to extract miniz files"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_error "Failed to download miniz"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
update_sdl() {
|
|
||||||
print_info "Fetching SDL3"
|
|
||||||
|
|
||||||
if [[ -d "lib/SDL/.git" ]]; then
|
|
||||||
cd lib/SDL && git pull --quiet origin main
|
|
||||||
cd - > /dev/null
|
|
||||||
else
|
|
||||||
rm -rf lib/SDL
|
|
||||||
git clone --quiet https://github.com/libsdl-org/SDL.git lib/SDL
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Updated SDL3"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
COMMAND="$1"
|
|
||||||
shift || true
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case $arg in
|
|
||||||
--release)
|
|
||||||
MODE="release"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$MODE" = "release" ]; then
|
|
||||||
CFLAGS="$CFLAGS -O3 -flto -ffast-math -funroll-loops -fno-unwind-tables -fno-asynchronous-unwind-tables"
|
|
||||||
LINKER_FLAGS="$LINKER_FLAGS -flto"
|
|
||||||
BUILDDIR="$BUILDDIR/release"
|
|
||||||
BINDIR="$BINDIR/release"
|
|
||||||
else
|
|
||||||
CFLAGS="$CFLAGS -g -O1 -DDEBUG"
|
|
||||||
BUILDDIR="$BUILDDIR/debug"
|
|
||||||
BINDIR="$BINDIR/debug"
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEP_CFLAGS="-O3 -funroll-loops"
|
|
||||||
|
|
||||||
case "$COMMAND" in
|
|
||||||
build)
|
|
||||||
mkdir -p "$BUILDDIR"
|
|
||||||
mkdir -p "$BINDIR"
|
|
||||||
|
|
||||||
if [[ ! -d "lib/luajit" ]] || \
|
|
||||||
[[ ! -f "lib/linenoise/linenoise.c" ]] || \
|
|
||||||
[[ ! -f "lib/miniz/miniz.c" ]] || \
|
|
||||||
[[ ! -f "lib/fennel/fennel.lua" ]]; then
|
|
||||||
print_info "Missing dependencies, fetching..."
|
|
||||||
|
|
||||||
make_lib_dirs
|
|
||||||
update_fennel
|
|
||||||
update_linenoise
|
|
||||||
update_luajit
|
|
||||||
update_miniz
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "bin/.gitignore" ]]; then
|
|
||||||
echo "*" > "bin/.gitignore"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f ".build/.gitignore" ]]; then
|
|
||||||
echo "*" > ".build/.gitignore"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "lib/.gitignore" ]]; then
|
|
||||||
echo "*" > "lib/.gitignore"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -d "lib/luajit" ]]; then
|
|
||||||
build_luajit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -d "lib/SDL" ]]; then
|
|
||||||
build_sdl
|
|
||||||
fi
|
|
||||||
|
|
||||||
build_server "$MODE"
|
|
||||||
|
|
||||||
setup_sdl3
|
|
||||||
print_info "Building pxl8 ($MODE mode)"
|
|
||||||
|
|
||||||
if [[ "$CC" == ccache* ]]; then
|
|
||||||
print_info "Compiler cache: ccache enabled"
|
|
||||||
fi
|
|
||||||
|
|
||||||
INCLUDES="-Isrc/asset -Isrc/bsp -Isrc/core -Isrc/gfx -Isrc/gui -Isrc/hal -Isrc/math -Isrc/net -Isrc/procgen -Isrc/script -Isrc/shader -Isrc/sfx -Isrc/sim -Isrc/world -Ilib/linenoise -Ilib/luajit/src -Ilib/miniz -I.build/shaders/c"
|
|
||||||
COMPILE_FLAGS="$CFLAGS $INCLUDES"
|
|
||||||
DEP_COMPILE_FLAGS="$DEP_CFLAGS $INCLUDES"
|
|
||||||
|
|
||||||
EXECUTABLE="$BINDIR/pxl8"
|
|
||||||
|
|
||||||
LIB_SOURCE_FILES="lib/linenoise/linenoise.c lib/miniz/miniz.c"
|
|
||||||
|
|
||||||
PXL8_SOURCE_FILES="
|
|
||||||
src/asset/pxl8_ase.c
|
|
||||||
src/asset/pxl8_cart.c
|
|
||||||
src/asset/pxl8_save.c
|
|
||||||
src/bsp/pxl8_bsp.c
|
|
||||||
src/bsp/pxl8_bsp_render.c
|
|
||||||
src/core/pxl8.c
|
|
||||||
src/core/pxl8_bytes.c
|
|
||||||
src/core/pxl8_io.c
|
|
||||||
src/core/pxl8_log.c
|
|
||||||
src/core/pxl8_replay.c
|
|
||||||
src/core/pxl8_rng.c
|
|
||||||
src/gfx/pxl8_3d_camera.c
|
|
||||||
src/gfx/pxl8_anim.c
|
|
||||||
src/gfx/pxl8_atlas.c
|
|
||||||
src/gfx/pxl8_blit.c
|
|
||||||
src/gfx/pxl8_colormap.c
|
|
||||||
src/gfx/pxl8_dither.c
|
|
||||||
src/gfx/pxl8_render.c
|
|
||||||
src/gfx/pxl8_shader_registry.c
|
|
||||||
src/gfx/pxl8_shader_runtime.c
|
|
||||||
src/gfx/pxl8_font.c
|
|
||||||
src/gfx/pxl8_gfx.c
|
|
||||||
src/gfx/pxl8_glows.c
|
|
||||||
src/gfx/pxl8_lightmap.c
|
|
||||||
src/gfx/pxl8_lights.c
|
|
||||||
src/gfx/pxl8_mesh.c
|
|
||||||
src/gfx/pxl8_palette.c
|
|
||||||
src/gfx/pxl8_particles.c
|
|
||||||
src/gfx/pxl8_tilemap.c
|
|
||||||
src/gfx/pxl8_tilesheet.c
|
|
||||||
src/gfx/pxl8_transition.c
|
|
||||||
src/gui/pxl8_gui.c
|
|
||||||
src/hal/pxl8_hal_sdl3.c
|
|
||||||
src/hal/pxl8_thread_sdl3.c
|
|
||||||
src/math/pxl8_math.c
|
|
||||||
src/math/pxl8_noise.c
|
|
||||||
src/net/pxl8_net.c
|
|
||||||
src/net/pxl8_protocol.c
|
|
||||||
src/procgen/pxl8_graph.c
|
|
||||||
src/script/pxl8_repl.c
|
|
||||||
src/script/pxl8_script.c
|
|
||||||
src/sfx/pxl8_sfx.c
|
|
||||||
src/sim/pxl8_sim.c
|
|
||||||
src/world/pxl8_entity.c
|
|
||||||
src/world/pxl8_world.c
|
|
||||||
src/world/pxl8_world_chunk.c
|
|
||||||
src/world/pxl8_world_chunk_cache.c
|
|
||||||
"
|
|
||||||
|
|
||||||
if [[ "$HAS_SDL3" -eq 1 ]]; then
|
|
||||||
PXL8_SOURCE_FILES="$PXL8_SOURCE_FILES src/hal/pxl8_io_sdl3.c src/hal/pxl8_mem_sdl3.c"
|
|
||||||
else
|
|
||||||
PXL8_SOURCE_FILES="$PXL8_SOURCE_FILES src/hal/pxl8_mem.c"
|
|
||||||
fi
|
|
||||||
|
|
||||||
generate_compile_commands
|
|
||||||
|
|
||||||
LUAJIT_LIB="lib/luajit/src/libluajit.a"
|
|
||||||
OBJECT_DIR="$BUILDDIR/obj"
|
|
||||||
mkdir -p "$OBJECT_DIR"
|
|
||||||
|
|
||||||
OBJECTS=""
|
|
||||||
NEED_LINK=false
|
|
||||||
SOURCES_COMPILED=""
|
|
||||||
|
|
||||||
for src_file in $LIB_SOURCE_FILES; do
|
|
||||||
obj_name=$(basename "$src_file" .c).o
|
|
||||||
obj_file="$OBJECT_DIR/$obj_name"
|
|
||||||
OBJECTS="$OBJECTS $obj_file"
|
|
||||||
|
|
||||||
if [[ "$src_file" -nt "$obj_file" ]]; then
|
|
||||||
NEED_LINK=true
|
|
||||||
compile_source_file "$src_file" "$obj_file" "$DEP_COMPILE_FLAGS"
|
|
||||||
SOURCES_COMPILED="yes"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
compile_shaders "$MODE"
|
|
||||||
|
|
||||||
for src_file in $PXL8_SOURCE_FILES; do
|
|
||||||
obj_name=$(basename "$src_file" .c).o
|
|
||||||
obj_file="$OBJECT_DIR/$obj_name"
|
|
||||||
OBJECTS="$OBJECTS $obj_file"
|
|
||||||
|
|
||||||
NEEDS_REBUILD=false
|
|
||||||
if [[ "$src_file" -nt "$obj_file" ]] || \
|
|
||||||
[[ "src/core/pxl8_types.h" -nt "$obj_file" ]] || \
|
|
||||||
[[ "src/core/pxl8_macros.h" -nt "$obj_file" ]] || \
|
|
||||||
[[ "src/net/pxl8_protocol.h" -nt "$obj_file" ]]; then
|
|
||||||
NEEDS_REBUILD=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$src_file" == "src/script/pxl8_script.c" ]]; then
|
|
||||||
for lua_file in src/lua/*.lua src/lua/pxl8/*.lua lib/fennel/fennel.lua; do
|
|
||||||
if [[ -f "$lua_file" ]] && [[ "$lua_file" -nt "$obj_file" ]]; then
|
|
||||||
NEEDS_REBUILD=true
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$NEEDS_REBUILD" == true ]]; then
|
|
||||||
NEED_LINK=true
|
|
||||||
compile_source_file "$src_file" "$obj_file" "$COMPILE_FLAGS"
|
|
||||||
SOURCES_COMPILED="yes"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ "$LUAJIT_LIB" -nt "$EXECUTABLE" ]] || [[ "$NEED_LINK" == true ]]; then
|
|
||||||
print_info "Linking executable"
|
|
||||||
if ! $CC $LINKER_FLAGS $OBJECTS $SHADER_OBJECTS $LUAJIT_LIB $LIBS -o "$EXECUTABLE"; then
|
|
||||||
print_error "Linking failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
elif [[ -z "$SOURCES_COMPILED" ]]; then
|
|
||||||
print_info "All files are up to date, skipping build"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Build complete"
|
|
||||||
print_info "Binary: $EXECUTABLE"
|
|
||||||
;;
|
|
||||||
|
|
||||||
run)
|
|
||||||
"$0" build "$@" || exit 1
|
|
||||||
|
|
||||||
CART=""
|
|
||||||
EXTRA_ARGS=""
|
|
||||||
for arg in "$@"; do
|
|
||||||
if [[ "$arg" == "--release" ]]; then
|
|
||||||
MODE="release"
|
|
||||||
elif [[ "$arg" == "--repl" ]]; then
|
|
||||||
EXTRA_ARGS="$EXTRA_ARGS --repl"
|
|
||||||
elif [[ -z "$CART" ]]; then
|
|
||||||
CART="$arg"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
start_server "$MODE"
|
|
||||||
trap stop_server EXIT
|
|
||||||
|
|
||||||
if [[ -z "$CART" ]]; then
|
|
||||||
"$BINDIR/pxl8" demo $EXTRA_ARGS
|
|
||||||
else
|
|
||||||
"$BINDIR/pxl8" "$CART" $EXTRA_ARGS
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
clean)
|
|
||||||
CLEAN_ALL=false
|
|
||||||
CLEAN_CACHE=false
|
|
||||||
CLEAN_DEPS=false
|
|
||||||
CLEAN_RELEASE=false
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--all) CLEAN_ALL=true ;;
|
|
||||||
--cache) CLEAN_CACHE=true ;;
|
|
||||||
--deps) CLEAN_DEPS=true ;;
|
|
||||||
--release) CLEAN_RELEASE=true ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ "$CLEAN_CACHE" == true ]] && command -v ccache >/dev/null 2>&1; then
|
|
||||||
print_info "Clearing ccache"
|
|
||||||
ccache -C >/dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$CLEAN_RELEASE" == true ]]; then
|
|
||||||
BUILD_PATH=".build/release"
|
|
||||||
BIN_PATH="bin/release"
|
|
||||||
MODE="release"
|
|
||||||
else
|
|
||||||
BUILD_PATH=".build/debug"
|
|
||||||
BIN_PATH="bin/debug"
|
|
||||||
MODE="debug"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$CLEAN_ALL" == true ]]; then
|
|
||||||
print_info "Removing build artifacts and dependencies"
|
|
||||||
rm -rf "$BUILD_PATH" "$BIN_PATH" .build/shaders lib
|
|
||||||
clean_server
|
|
||||||
print_info "Cleaned all"
|
|
||||||
elif [[ "$CLEAN_DEPS" == true ]]; then
|
|
||||||
print_info "Removing dependencies"
|
|
||||||
rm -rf lib
|
|
||||||
print_info "Cleaned dependencies"
|
|
||||||
else
|
|
||||||
print_info "Removing build artifacts"
|
|
||||||
rm -rf "$BUILD_PATH" "$BIN_PATH" .build/shaders
|
|
||||||
clean_server
|
|
||||||
print_info "Cleaned"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
update)
|
|
||||||
make_lib_dirs
|
|
||||||
update_fennel
|
|
||||||
update_linenoise
|
|
||||||
update_luajit
|
|
||||||
update_miniz
|
|
||||||
print_info "All dependencies updated"
|
|
||||||
;;
|
|
||||||
|
|
||||||
vendor)
|
|
||||||
update_sdl
|
|
||||||
;;
|
|
||||||
|
|
||||||
install)
|
|
||||||
"$0" build --release || exit 1
|
|
||||||
|
|
||||||
INSTALL_DIR="${HOME}/.local/bin"
|
|
||||||
mkdir -p "$INSTALL_DIR"
|
|
||||||
|
|
||||||
cp "bin/release/pxl8" "$INSTALL_DIR/pxl8"
|
|
||||||
chmod +x "$INSTALL_DIR/pxl8"
|
|
||||||
|
|
||||||
print_info "Installed pxl8 to $INSTALL_DIR/pxl8"
|
|
||||||
|
|
||||||
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
|
|
||||||
print_info "Note: Add $INSTALL_DIR to your PATH if not already"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
ase)
|
|
||||||
bash tools/aseprite/pxl8-ase.sh "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
profile)
|
|
||||||
if [[ "$(uname)" != "Linux" ]]; then
|
|
||||||
print_error "Profiling with perf is only supported on Linux"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v perf >/dev/null 2>&1; then
|
|
||||||
print_error "perf not found. Install linux-tools or perf package."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -d "lib/FlameGraph" ]]; then
|
|
||||||
mkdir -p lib
|
|
||||||
update_flamegraph
|
|
||||||
fi
|
|
||||||
|
|
||||||
"$0" build || exit 1
|
|
||||||
|
|
||||||
PROFILE_DIR=".build/debug/profile"
|
|
||||||
mkdir -p "$PROFILE_DIR"
|
|
||||||
|
|
||||||
CART=""
|
|
||||||
PERF_DURATION=30
|
|
||||||
for arg in "$@"; do
|
|
||||||
if [[ "$arg" =~ ^--duration=([0-9]+)$ ]]; then
|
|
||||||
PERF_DURATION="${BASH_REMATCH[1]}"
|
|
||||||
elif [[ "$arg" != "--release" ]] && [[ -z "$CART" ]]; then
|
|
||||||
CART="$arg"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
[[ -z "$CART" ]] && CART="demo"
|
|
||||||
|
|
||||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
|
||||||
PERF_DATA="$PROFILE_DIR/perf_${TIMESTAMP}.data"
|
|
||||||
PERF_SCRIPT="$PROFILE_DIR/perf_${TIMESTAMP}.perf"
|
|
||||||
FOLDED="$PROFILE_DIR/perf_${TIMESTAMP}.folded"
|
|
||||||
SVG="$PROFILE_DIR/flamegraph_${TIMESTAMP}.svg"
|
|
||||||
|
|
||||||
print_info "Starting server..."
|
|
||||||
./bin/debug/pxl8d &
|
|
||||||
SERVER_PID=$!
|
|
||||||
sleep 0.5
|
|
||||||
|
|
||||||
trap "kill $SERVER_PID 2>/dev/null; wait $SERVER_PID 2>/dev/null" EXIT
|
|
||||||
|
|
||||||
print_info "Profiling pxl8 for ${PERF_DURATION}s (Ctrl+C to stop early)..."
|
|
||||||
perf record -F 99 -g --call-graph dwarf -o "$PERF_DATA" -- \
|
|
||||||
timeout "${PERF_DURATION}s" ./bin/debug/pxl8 "$CART" 2>/dev/null || true
|
|
||||||
|
|
||||||
print_info "Processing profile data..."
|
|
||||||
perf script -i "$PERF_DATA" > "$PERF_SCRIPT"
|
|
||||||
|
|
||||||
print_info "Generating flamegraph..."
|
|
||||||
lib/FlameGraph/stackcollapse-perf.pl "$PERF_SCRIPT" > "$FOLDED"
|
|
||||||
lib/FlameGraph/flamegraph.pl --cp --colors orange --title "pxl8 profile" "$FOLDED" > "$SVG"
|
|
||||||
|
|
||||||
rm -f "$PERF_DATA" "$PERF_SCRIPT" "$FOLDED"
|
|
||||||
|
|
||||||
print_info "Flamegraph: $SVG"
|
|
||||||
|
|
||||||
if command -v xdg-open >/dev/null 2>&1; then
|
|
||||||
xdg-open "$SVG" 2>/dev/null &
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
help|--help|-h|"")
|
|
||||||
print_usage
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
print_error "Unknown command: $COMMAND"
|
|
||||||
print_usage
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
@ -26,7 +26,7 @@ fn get_time_ns() -> u64 {
|
||||||
static mut FREQ: i64 = 0;
|
static mut FREQ: i64 = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
if FREQ == 0 {
|
if FREQ == 0 {
|
||||||
QueryPerformanceFrequency(&mut FREQ);
|
QueryPerformanceFrequency(&raw mut FREQ);
|
||||||
}
|
}
|
||||||
let mut count: i64 = 0;
|
let mut count: i64 = 0;
|
||||||
QueryPerformanceCounter(&mut count);
|
QueryPerformanceCounter(&mut count);
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ mod sys {
|
||||||
pub type RawSocket = c_int;
|
pub type RawSocket = c_int;
|
||||||
pub const INVALID_SOCKET: RawSocket = -1;
|
pub const INVALID_SOCKET: RawSocket = -1;
|
||||||
|
|
||||||
|
pub fn init() {}
|
||||||
|
|
||||||
pub fn socket() -> RawSocket {
|
pub fn socket() -> RawSocket {
|
||||||
unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }
|
unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }
|
||||||
}
|
}
|
||||||
|
|
@ -139,6 +141,8 @@ mod sys {
|
||||||
pub type RawSocket = c_int;
|
pub type RawSocket = c_int;
|
||||||
pub const INVALID_SOCKET: RawSocket = -1;
|
pub const INVALID_SOCKET: RawSocket = -1;
|
||||||
|
|
||||||
|
pub fn init() {}
|
||||||
|
|
||||||
pub fn socket() -> RawSocket {
|
pub fn socket() -> RawSocket {
|
||||||
unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }
|
unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }
|
||||||
}
|
}
|
||||||
|
|
@ -195,59 +199,66 @@ mod sys {
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod sys {
|
mod sys {
|
||||||
use windows_sys::Win32::Networking::WinSock::*;
|
use windows_sys::Win32::Networking::WinSock as ws;
|
||||||
|
|
||||||
pub type RawSocket = SOCKET;
|
pub type RawSocket = ws::SOCKET;
|
||||||
pub const INVALID_SOCKET_VAL: RawSocket = INVALID_SOCKET;
|
pub const INVALID_SOCKET: RawSocket = ws::INVALID_SOCKET;
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
unsafe {
|
||||||
|
let mut wsa: ws::WSADATA = core::mem::zeroed();
|
||||||
|
ws::WSAStartup(0x0202, &mut wsa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn socket() -> RawSocket {
|
pub fn socket() -> RawSocket {
|
||||||
unsafe { socket(AF_INET as i32, SOCK_DGRAM as i32, 0) }
|
unsafe { ws::socket(ws::AF_INET as i32, ws::SOCK_DGRAM as i32, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(sock: RawSocket, port: u16) -> i32 {
|
pub fn bind(sock: RawSocket, port: u16) -> i32 {
|
||||||
let addr = SOCKADDR_IN {
|
let addr = ws::SOCKADDR_IN {
|
||||||
sin_family: AF_INET,
|
sin_family: ws::AF_INET,
|
||||||
sin_port: port.to_be(),
|
sin_port: port.to_be(),
|
||||||
sin_addr: IN_ADDR { S_un: IN_ADDR_0 { S_addr: u32::from_be_bytes([127, 0, 0, 1]).to_be() } },
|
sin_addr: ws::IN_ADDR { S_un: ws::IN_ADDR_0 { S_addr: 0 } },
|
||||||
sin_zero: [0; 8],
|
sin_zero: [0; 8],
|
||||||
};
|
};
|
||||||
unsafe { bind(sock, &addr as *const _ as *const SOCKADDR, core::mem::size_of::<SOCKADDR_IN>() as i32) }
|
unsafe { ws::bind(sock, &addr as *const _ as *const ws::SOCKADDR, core::mem::size_of::<ws::SOCKADDR_IN>() as i32) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_nonblocking(sock: RawSocket) -> i32 {
|
pub fn set_nonblocking(sock: RawSocket) -> i32 {
|
||||||
let mut nonblocking: u32 = 1;
|
let mut nonblocking: u32 = 1;
|
||||||
unsafe { ioctlsocket(sock, FIONBIO as i32, &mut nonblocking) }
|
unsafe { ws::ioctlsocket(sock, ws::FIONBIO as i32, &mut nonblocking) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recvfrom(sock: RawSocket, buf: &mut [u8], addr: &mut SOCKADDR_IN) -> i32 {
|
pub fn recvfrom(sock: RawSocket, buf: &mut [u8], addr: &mut ws::SOCKADDR_IN) -> i32 {
|
||||||
let mut addr_len = core::mem::size_of::<SOCKADDR_IN>() as i32;
|
let mut addr_len = core::mem::size_of::<ws::SOCKADDR_IN>() as i32;
|
||||||
unsafe {
|
unsafe {
|
||||||
recvfrom(
|
ws::recvfrom(
|
||||||
sock,
|
sock,
|
||||||
buf.as_mut_ptr(),
|
buf.as_mut_ptr(),
|
||||||
buf.len() as i32,
|
buf.len() as i32,
|
||||||
0,
|
0,
|
||||||
addr as *mut _ as *mut SOCKADDR,
|
addr as *mut _ as *mut ws::SOCKADDR,
|
||||||
&mut addr_len,
|
&mut addr_len,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sendto(sock: RawSocket, buf: &[u8], addr: &SOCKADDR_IN) -> i32 {
|
pub fn sendto(sock: RawSocket, buf: &[u8], addr: &ws::SOCKADDR_IN) -> i32 {
|
||||||
unsafe {
|
unsafe {
|
||||||
sendto(
|
ws::sendto(
|
||||||
sock,
|
sock,
|
||||||
buf.as_ptr(),
|
buf.as_ptr(),
|
||||||
buf.len() as i32,
|
buf.len() as i32,
|
||||||
0,
|
0,
|
||||||
addr as *const _ as *const SOCKADDR,
|
addr as *const _ as *const ws::SOCKADDR,
|
||||||
core::mem::size_of::<SOCKADDR_IN>() as i32,
|
core::mem::size_of::<ws::SOCKADDR_IN>() as i32,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(sock: RawSocket) {
|
pub fn close(sock: RawSocket) {
|
||||||
unsafe { closesocket(sock) };
|
unsafe { ws::closesocket(sock) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,6 +268,7 @@ type SockAddr = libc::sockaddr_in;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
type SockAddr = windows_sys::Win32::Networking::WinSock::SOCKADDR_IN;
|
type SockAddr = windows_sys::Win32::Networking::WinSock::SOCKADDR_IN;
|
||||||
|
|
||||||
|
|
||||||
pub struct Transport {
|
pub struct Transport {
|
||||||
client_addr: SockAddr,
|
client_addr: SockAddr,
|
||||||
has_client: bool,
|
has_client: bool,
|
||||||
|
|
@ -267,6 +279,7 @@ pub struct Transport {
|
||||||
|
|
||||||
impl Transport {
|
impl Transport {
|
||||||
pub fn bind(port: u16) -> Option<Self> {
|
pub fn bind(port: u16) -> Option<Self> {
|
||||||
|
sys::init();
|
||||||
let sock = sys::socket();
|
let sock = sys::socket();
|
||||||
if sock == sys::INVALID_SOCKET {
|
if sock == sys::INVALID_SOCKET {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
#include "pxl8_shader_runtime.h"
|
#include "pxl8_shader_runtime.h"
|
||||||
#include "pxl8_log.h"
|
#include "pxl8_log.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -11,10 +17,45 @@ struct pxl8_shader_lib {
|
||||||
char path[256];
|
char path[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void* lib_open(const char* path) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (void*)LoadLibraryA(path);
|
||||||
|
#else
|
||||||
|
return dlopen(path, RTLD_NOW);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lib_close(void* handle) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
FreeLibrary((HMODULE)handle);
|
||||||
|
#else
|
||||||
|
dlclose(handle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* lib_sym(void* handle, const char* name) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (void*)GetProcAddress((HMODULE)handle, name);
|
||||||
|
#else
|
||||||
|
return dlsym(handle, name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* lib_error(void) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
static char buf[256];
|
||||||
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
||||||
|
0, buf, sizeof(buf), NULL);
|
||||||
|
return buf;
|
||||||
|
#else
|
||||||
|
return dlerror();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
pxl8_shader_lib* pxl8_shader_lib_load(const char* path) {
|
pxl8_shader_lib* pxl8_shader_lib_load(const char* path) {
|
||||||
void* handle = dlopen(path, RTLD_NOW);
|
void* handle = lib_open(path);
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
pxl8_error("Failed to load shader library: %s - %s", path, dlerror());
|
pxl8_error("Failed to load shader library: %s - %s", path, lib_error());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +70,7 @@ pxl8_shader_lib* pxl8_shader_lib_load(const char* path) {
|
||||||
void pxl8_shader_lib_unload(pxl8_shader_lib* lib) {
|
void pxl8_shader_lib_unload(pxl8_shader_lib* lib) {
|
||||||
if (!lib) return;
|
if (!lib) return;
|
||||||
if (lib->handle) {
|
if (lib->handle) {
|
||||||
dlclose(lib->handle);
|
lib_close(lib->handle);
|
||||||
}
|
}
|
||||||
free(lib);
|
free(lib);
|
||||||
}
|
}
|
||||||
|
|
@ -38,12 +79,12 @@ bool pxl8_shader_lib_reload(pxl8_shader_lib* lib) {
|
||||||
if (!lib) return false;
|
if (!lib) return false;
|
||||||
|
|
||||||
if (lib->handle) {
|
if (lib->handle) {
|
||||||
dlclose(lib->handle);
|
lib_close(lib->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
lib->handle = dlopen(lib->path, RTLD_NOW);
|
lib->handle = lib_open(lib->path);
|
||||||
if (!lib->handle) {
|
if (!lib->handle) {
|
||||||
pxl8_error("Failed to reload shader library: %s - %s", lib->path, dlerror());
|
pxl8_error("Failed to reload shader library: %s - %s", lib->path, lib_error());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,9 +97,9 @@ pxl8_shader_fn pxl8_shader_lib_get(pxl8_shader_lib* lib, const char* name) {
|
||||||
char symbol[128];
|
char symbol[128];
|
||||||
snprintf(symbol, sizeof(symbol), "pxl8_shader_%s", name);
|
snprintf(symbol, sizeof(symbol), "pxl8_shader_%s", name);
|
||||||
|
|
||||||
void* fn = dlsym(lib->handle, symbol);
|
void* fn = lib_sym(lib->handle, symbol);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
pxl8_error("Failed to find shader: %s - %s", symbol, dlerror());
|
pxl8_error("Failed to find shader: %s - %s", symbol, lib_error());
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
typedef SOCKET socket_t;
|
typedef SOCKET socket_t;
|
||||||
|
typedef int ssize_t;
|
||||||
#define INVALID_SOCK INVALID_SOCKET
|
#define INVALID_SOCK INVALID_SOCKET
|
||||||
#define close_socket closesocket
|
#define close_socket closesocket
|
||||||
#else
|
#else
|
||||||
|
|
@ -108,7 +109,7 @@ pxl8_result pxl8_net_connect(pxl8_net* net) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int rcvbuf = 1024 * 1024;
|
int rcvbuf = 1024 * 1024;
|
||||||
setsockopt(net->sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
|
setsockopt(net->sock, SOL_SOCKET, SO_RCVBUF, (const char*)&rcvbuf, sizeof(rcvbuf));
|
||||||
|
|
||||||
memset(&net->server_addr, 0, sizeof(net->server_addr));
|
memset(&net->server_addr, 0, sizeof(net->server_addr));
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
#include "pxl8_repl.h"
|
#include "pxl8_repl.h"
|
||||||
|
|
||||||
#include <linenoise.h>
|
#include <linenoise.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <poll.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "pxl8_log.h"
|
#include "pxl8_log.h"
|
||||||
#include "pxl8_mem.h"
|
#include "pxl8_mem.h"
|
||||||
|
|
@ -37,38 +40,71 @@ struct pxl8_repl {
|
||||||
|
|
||||||
static pxl8_repl* g_repl = NULL;
|
static pxl8_repl* g_repl = NULL;
|
||||||
|
|
||||||
static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) {
|
static const char* fennel_keywords[] = {
|
||||||
const char* fennel_keywords[] = {
|
"fn", "let", "var", "set", "global", "local",
|
||||||
"fn", "let", "var", "set", "global", "local",
|
"if", "when", "do", "while", "for", "each",
|
||||||
"if", "when", "do", "while", "for", "each",
|
"lambda", "λ", "partial", "macro", "macros",
|
||||||
"lambda", "λ", "partial", "macro", "macros",
|
"require", "include", "import-macros",
|
||||||
"require", "include", "import-macros",
|
"values", "select", "table", "length",
|
||||||
"values", "select", "table", "length",
|
".", "..", ":", "->", "->>", "-?>", "-?>>",
|
||||||
".", "..", ":", "->", "->>", "-?>", "-?>>",
|
"doto", "match", "case", "pick-values",
|
||||||
"doto", "match", "case", "pick-values",
|
"collect", "icollect", "accumulate"
|
||||||
"collect", "icollect", "accumulate"
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const char* pxl8_functions[] = {
|
static const char* pxl8_functions[] = {
|
||||||
"pxl8.clr", "pxl8.pixel", "pxl8.get_pixel",
|
"pxl8.clr", "pxl8.pixel", "pxl8.get_pixel",
|
||||||
"pxl8.line", "pxl8.rect", "pxl8.rect_fill",
|
"pxl8.line", "pxl8.rect", "pxl8.rect_fill",
|
||||||
"pxl8.circle", "pxl8.circle_fill", "pxl8.text",
|
"pxl8.circle", "pxl8.circle_fill", "pxl8.text",
|
||||||
"pxl8.get_screen", "pxl8.info", "pxl8.warn",
|
"pxl8.get_screen", "pxl8.info", "pxl8.warn",
|
||||||
"pxl8.error", "pxl8.debug", "pxl8.trace"
|
"pxl8.error", "pxl8.debug", "pxl8.trace"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef PXL8_WIN32_LINENOISE
|
||||||
|
|
||||||
|
static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc, void* userdata) {
|
||||||
|
(void)userdata;
|
||||||
usize buf_len = strlen(buf);
|
usize buf_len = strlen(buf);
|
||||||
|
|
||||||
for (usize i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
|
for (usize i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
|
||||||
if (strncmp(buf, fennel_keywords[i], buf_len) == 0) {
|
if (strncmp(buf, fennel_keywords[i], buf_len) == 0)
|
||||||
linenoiseAddCompletion(lc, fennel_keywords[i]);
|
linenoiseAddCompletion(lc, fennel_keywords[i]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (usize i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
|
for (usize i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
|
||||||
if (strncmp(buf, pxl8_functions[i], buf_len) == 0) {
|
if (strncmp(buf, pxl8_functions[i], buf_len) == 0)
|
||||||
|
linenoiseAddCompletion(lc, pxl8_functions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* pxl8_repl_hints(const char* buf, int* color, int* bold, void* userdata) {
|
||||||
|
(void)userdata;
|
||||||
|
if (strncmp(buf, "pxl8.", 5) == 0 && strlen(buf) == 5) {
|
||||||
|
*color = 35;
|
||||||
|
*bold = 0;
|
||||||
|
return "clr|pixel|line|rect|circle|text|get_screen";
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "(fn") == 0) {
|
||||||
|
*color = 36;
|
||||||
|
*bold = 0;
|
||||||
|
return " [args] body)";
|
||||||
|
}
|
||||||
|
if (strcmp(buf, "(let") == 0) {
|
||||||
|
*color = 36;
|
||||||
|
*bold = 0;
|
||||||
|
return " [bindings] body)";
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void pxl8_repl_completion(const char* buf, linenoiseCompletions* lc) {
|
||||||
|
usize buf_len = strlen(buf);
|
||||||
|
for (usize i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
|
||||||
|
if (strncmp(buf, fennel_keywords[i], buf_len) == 0)
|
||||||
|
linenoiseAddCompletion(lc, fennel_keywords[i]);
|
||||||
|
}
|
||||||
|
for (usize i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
|
||||||
|
if (strncmp(buf, pxl8_functions[i], buf_len) == 0)
|
||||||
linenoiseAddCompletion(lc, pxl8_functions[i]);
|
linenoiseAddCompletion(lc, pxl8_functions[i]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,22 +114,21 @@ static char* pxl8_repl_hints(const char* buf, int* color, int* bold) {
|
||||||
*bold = 0;
|
*bold = 0;
|
||||||
return "clr|pixel|line|rect|circle|text|get_screen";
|
return "clr|pixel|line|rect|circle|text|get_screen";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(buf, "(fn") == 0) {
|
if (strcmp(buf, "(fn") == 0) {
|
||||||
*color = 36;
|
*color = 36;
|
||||||
*bold = 0;
|
*bold = 0;
|
||||||
return " [args] body)";
|
return " [args] body)";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(buf, "(let") == 0) {
|
if (strcmp(buf, "(let") == 0) {
|
||||||
*color = 36;
|
*color = 36;
|
||||||
*bold = 0;
|
*bold = 0;
|
||||||
return " [bindings] body)";
|
return " [bindings] body)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void pxl8_repl_flush_logs(pxl8_repl* repl) {
|
static void pxl8_repl_flush_logs(pxl8_repl* repl) {
|
||||||
u32 log_read_idx = atomic_load(&repl->log_read_idx);
|
u32 log_read_idx = atomic_load(&repl->log_read_idx);
|
||||||
u32 log_write_idx = atomic_load(&repl->log_write_idx);
|
u32 log_write_idx = atomic_load(&repl->log_write_idx);
|
||||||
|
|
@ -106,6 +141,68 @@ static void pxl8_repl_flush_logs(pxl8_repl* repl) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
static int pxl8_repl_thread(void* arg) {
|
||||||
|
pxl8_repl* repl = (pxl8_repl*)arg;
|
||||||
|
|
||||||
|
printf("[pxl8 REPL] Fennel 1.6.0 - Tab for completion, Ctrl-Z to exit\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
while (!atomic_load(&repl->should_quit)) {
|
||||||
|
pxl8_repl_flush_logs(repl);
|
||||||
|
|
||||||
|
const char* prompt = (repl->accumulator[0] != '\0') ? ".. " : ">> ";
|
||||||
|
char* line = linenoise(prompt);
|
||||||
|
|
||||||
|
if (line == NULL) {
|
||||||
|
atomic_store(&repl->should_quit, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_multiline = (repl->accumulator[0] != '\0');
|
||||||
|
|
||||||
|
if (strlen(line) > 0 || in_multiline) {
|
||||||
|
if (!in_multiline) {
|
||||||
|
linenoiseHistoryAdd(line);
|
||||||
|
linenoiseHistorySave(".pxl8_history");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repl->accumulator[0] != '\0') {
|
||||||
|
strncat(repl->accumulator, "\n",
|
||||||
|
PXL8_MAX_REPL_COMMAND_SIZE - strlen(repl->accumulator) - 1);
|
||||||
|
}
|
||||||
|
strncat(repl->accumulator, line,
|
||||||
|
PXL8_MAX_REPL_COMMAND_SIZE - strlen(repl->accumulator) - 1);
|
||||||
|
|
||||||
|
u32 write_idx = atomic_load(&repl->cmd_write_idx);
|
||||||
|
u32 read_idx = atomic_load(&repl->cmd_read_idx);
|
||||||
|
u32 next_write = (write_idx + 1) % PXL8_REPL_QUEUE_SIZE;
|
||||||
|
|
||||||
|
if (next_write != read_idx) {
|
||||||
|
strncpy(repl->commands[write_idx], repl->accumulator, PXL8_MAX_REPL_COMMAND_SIZE - 1);
|
||||||
|
repl->commands[write_idx][PXL8_MAX_REPL_COMMAND_SIZE - 1] = '\0';
|
||||||
|
atomic_store(&repl->cmd_write_idx, next_write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
while (!atomic_load(&repl->should_quit) &&
|
||||||
|
(atomic_load(&repl->cmd_write_idx) != atomic_load(&repl->cmd_read_idx) ||
|
||||||
|
!atomic_load(&repl->cmd_complete))) {
|
||||||
|
pxl8_repl_flush_logs(repl);
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
atomic_store(&repl->cmd_complete, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pxl8_repl_flush_logs(repl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
static int pxl8_repl_thread(void* arg) {
|
static int pxl8_repl_thread(void* arg) {
|
||||||
pxl8_repl* repl = (pxl8_repl*)arg;
|
pxl8_repl* repl = (pxl8_repl*)arg;
|
||||||
|
|
||||||
|
|
@ -217,6 +314,8 @@ static int pxl8_repl_thread(void* arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
pxl8_repl* pxl8_repl_create(void) {
|
pxl8_repl* pxl8_repl_create(void) {
|
||||||
pxl8_repl* repl = (pxl8_repl*)pxl8_calloc(1, sizeof(pxl8_repl));
|
pxl8_repl* repl = (pxl8_repl*)pxl8_calloc(1, sizeof(pxl8_repl));
|
||||||
if (!repl) return NULL;
|
if (!repl) return NULL;
|
||||||
|
|
@ -231,8 +330,13 @@ pxl8_repl* pxl8_repl_create(void) {
|
||||||
|
|
||||||
linenoiseHistorySetMaxLen(100);
|
linenoiseHistorySetMaxLen(100);
|
||||||
linenoiseSetMultiLine(1);
|
linenoiseSetMultiLine(1);
|
||||||
|
#ifdef PXL8_WIN32_LINENOISE
|
||||||
|
linenoiseSetCompletionCallback(pxl8_repl_completion, NULL);
|
||||||
|
linenoiseSetHintsCallback(pxl8_repl_hints, NULL);
|
||||||
|
#else
|
||||||
linenoiseSetCompletionCallback(pxl8_repl_completion);
|
linenoiseSetCompletionCallback(pxl8_repl_completion);
|
||||||
linenoiseSetHintsCallback(pxl8_repl_hints);
|
linenoiseSetHintsCallback(pxl8_repl_hints);
|
||||||
|
#endif
|
||||||
linenoiseHistoryLoad(".pxl8_history");
|
linenoiseHistoryLoad(".pxl8_history");
|
||||||
|
|
||||||
g_repl = repl;
|
g_repl = repl;
|
||||||
|
|
@ -264,7 +368,9 @@ void pxl8_repl_destroy(pxl8_repl* repl) {
|
||||||
g_repl = NULL;
|
g_repl = NULL;
|
||||||
pxl8_log_set_handler(NULL);
|
pxl8_log_set_handler(NULL);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
system("stty sane 2>/dev/null");
|
system("stty sane 2>/dev/null");
|
||||||
|
#endif
|
||||||
pxl8_free(repl);
|
pxl8_free(repl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
#define PATH_MAX 260
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lualib.h>
|
#include <lualib.h>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue