@@ -2,16 +2,26 @@ CC ?= clang
22STD = c17
33AR = ar
44TARGET = rayforce
5- # Version is authoritative in include/rayforce.h — extract it here
6- VERSION_MAJOR := $(shell grep 'RAY_VERSION_MAJOR' include/rayforce.h | head -1 | awk '{print $$3}')
7- VERSION_MINOR := $(shell grep 'RAY_VERSION_MINOR' include/rayforce.h | head -1 | awk '{print $$3}')
8- VERSION_PATCH := $(shell grep 'RAY_VERSION_PATCH' include/rayforce.h | head -1 | awk '{print $$3}')
9- VERSION = $(VERSION_MAJOR ) .$(VERSION_MINOR ) .$(VERSION_PATCH )
5+ # Version: the git tag is the single source of truth. Precedence:
6+ # explicit override (CI passes RAY_VERSION=X.Y.Z from the release PR title)
7+ # > latest git tag (vX.Y.Z) > 0.0.0 dev default.
8+ # The value is injected into the build via -D below (see DEFS); nothing is
9+ # hand-edited in source to cut a release. See RELEASE.md.
10+ RAY_VERSION ?= $(shell git describe --tags --match 'v[0-9]* .[0-9]* .[0-9]* ' --abbrev=0 2>/dev/null | sed 's/^v//')
11+ ifeq ($(strip $(RAY_VERSION ) ) ,)
12+ RAY_VERSION := 0.0.0
13+ endif
14+ VERSION = $(RAY_VERSION )
15+ VERSION_MAJOR := $(word 1,$(subst ., ,$(RAY_VERSION ) ) )
16+ VERSION_MINOR := $(word 2,$(subst ., ,$(RAY_VERSION ) ) )
17+ VERSION_PATCH := $(word 3,$(subst ., ,$(RAY_VERSION ) ) )
1018GIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
1119BUILD_DATE := $(shell date -u +% Y-% m-% d)
1220
1321WARNS = -Wall -Wextra -Werror -Wstrict-prototypes -Wno-unused-parameter
14- DEFS = -DRAYFORCE_GIT_COMMIT=\"$(GIT_HASH ) \" -DRAYFORCE_BUILD_DATE=\"$(BUILD_DATE ) \"
22+ DEFS = -DRAYFORCE_GIT_COMMIT=\"$(GIT_HASH ) \" -DRAYFORCE_BUILD_DATE=\"$(BUILD_DATE ) \" \
23+ -DRAY_VERSION_MAJOR=$(VERSION_MAJOR ) -DRAY_VERSION_MINOR=$(VERSION_MINOR ) \
24+ -DRAY_VERSION_PATCH=$(VERSION_PATCH ) -DRAYFORCE_VERSION=\"$(RAY_VERSION ) \"
1525INCLUDES = -Iinclude -Isrc
1626# Header-dependency tracking: -MMD emits a .d makefile fragment next to
1727# each .o listing the headers it included (user headers only, not system);
@@ -101,6 +111,21 @@ lib: CFLAGS = $(RELEASE_CFLAGS)
101111lib : $(LIB_OBJ )
102112 $(AR ) rc lib$(TARGET ) .a $(LIB_OBJ )
103113
114+ # Release tarball: build the optimized binary and package it as
115+ # dist/rayforce-<version>-<os>-<arch>.tar.gz plus a SHA-256 checksum.
116+ # Used by .github/workflows/release.yml (which passes RAY_VERSION=X.Y.Z) and
117+ # runnable locally. VERSION comes from the tag/override resolved at the top.
118+ dist : release
119+ @mkdir -p dist
120+ @os=$$(uname -s | tr 'A-Z' 'a-z' ) ; arch=$$(uname -m ) ; \
121+ name=$(TARGET ) -$(VERSION ) -$$ os-$$ arch; stage=dist/$$ name; \
122+ mkdir -p $$ stage; \
123+ cp $(TARGET ) LICENSE README.md include/rayforce.h $$ stage/; \
124+ tar -czf dist/$$ name.tar.gz -C dist $$ name; \
125+ rm -rf $$ stage; \
126+ ( cd dist && { command -v sha256sum > /dev/null 2>&1 && sha256sum $$ name.tar.gz || shasum -a 256 $$ name.tar.gz; } > $$ name.tar.gz.sha256 ); \
127+ echo " built dist/$$ name.tar.gz"
128+
104129# Allocator micro-benchmark (release-optimized, linked against lib objects).
105130# Compile all sources fresh with RELEASE_CFLAGS so the benchmark measures
106131# the release allocator, not a sanitizer-instrumented debug build.
@@ -148,14 +173,22 @@ bench-join-dup:
148173 bench/join_dup/main.c $(LIB_SRC ) $(LIBS ) $(RELEASE_LDFLAGS )
149174 ./bench-join-dup
150175
176+ # Worker threads per process during tests. Without this the runtime
177+ # auto-sizes to ncpu-1, so on a many-core box the in-process harness AND
178+ # every server it spawns via .sys.exec each create ~ncpu-1 threads — a lot of
179+ # wasted CPU for tiny test inputs. RAYFORCE_CORES (honored by ray_pool_create)
180+ # caps it; children inherit the env. Override for a fuller parallel stress,
181+ # e.g. `make test TEST_CORES=0` (serial) or `make test TEST_CORES=8`.
182+ TEST_CORES ?= 2
183+
151184# Tests. Depends on $(TARGET) because test/rfl/system/ipc_diff.rfl
152185# spawns ./$(TARGET) as an IPC server via .sys.exec — both binaries
153186# must exist on disk and share the build flavour (sanitizers, coverage).
154187test : CFLAGS = $(DEBUG_CFLAGS )
155188test : LDFLAGS = $(DEBUG_LDFLAGS )
156189test : $(TARGET ) $(LIB_OBJ ) $(TEST_OBJ )
157190 $(CC ) $(CFLAGS ) -o $(TARGET ) .test $(LIB_OBJ ) $(TEST_OBJ ) $(LIBS ) $(LDFLAGS ) -Itest
158- ./$(TARGET ) .test
191+ RAYFORCE_CORES= $( TEST_CORES ) ./$(TARGET ) .test
159192
160193# Coverage report. Builds both binaries with clang source-based
161194# instrumentation, runs the test suite (writing one .profraw per
@@ -194,14 +227,14 @@ clean:
194227 -rm -f $(LIB_OBJ ) $(MAIN_OBJ ) $(TEST_OBJ )
195228 -rm -f $(DEPS )
196229 -rm -f $(TARGET ) $(TARGET ) .test lib$(TARGET ) .a
197- -rm -rf build build_release
230+ -rm -rf build build_release dist
198231 # Test-generated fixtures (see test/rfl/system/*.rfl) — should not linger after a run.
199232 -rm -f rf_test_* .csv
200233 # Coverage artefacts (see `make coverage`).
201234 -rm -f cov-* .profraw default.profraw coverage.profdata
202235 -rm -rf coverage_html
203236
204- .PHONY : default debug release lib bench-alloc bench-join-buildside bench-join-dup test coverage clean
237+ .PHONY : default debug release lib dist bench-alloc bench-join-buildside bench-join-dup test coverage clean
205238
206239# Header dependencies last: .d fragments only add prerequisites to the
207240# object targets above, and being last they can't hijack the default goal.
0 commit comments