c333840baa
Problem: 'make clean', 'make test-c', and 'make check' use POSIX shell constructs (for loop, rm -f, rm -rf) that require sh.exe. On native Windows with WinLibs MinGW (no MSYS2, no Git Bash), there is no sh.exe on PATH. GNU Make falls back to cmd.exe, which can't parse 'for test in ...; do' or find 'rm', so these targets fail with 'test was unexpected' or 'CreateProcess error'. Root cause: the Makefile's recipe lines assumed a POSIX shell is always available. The IS_WIN detection (from #129) catches the platform but the shell-dependent targets were never made portable. Fix: replace the shell-dependent constructs with small Python helper scripts (Python is already a project dependency for test-python, convert, bench). This works from cmd.exe, PowerShell, Git Bash, and MSYS2 alike. Changes: - tools/run_tests.py (new): runs each C test binary, exits non-zero on the first failure. Replaces the 'for test in ...; do ./$test || exit 1; done' shell loop in test-c. - tools/clean.py (new): removes build artifacts and test binaries. Replaces 'rm -f' and 'rm -rf' in clean. Only removes executables (.exe) and known artifact names — never .c or .py source files. - Makefile: PYTHON defaults to 'python' on Windows (not 'python3'); test-c and clean now call the Python helpers instead of shell constructs. Verified from native PowerShell (no sh.exe): make clean removes 8-19 files/dirs, make test-c runs all 7 C test suites, source files survive. Also verified from Git Bash (sh.exe present): behavior unchanged. Co-authored-by: woolcoxm <13604288+woolcoxm@users.noreply.github.com>
229 lines
8.9 KiB
Makefile
229 lines
8.9 KiB
Makefile
# --- OS detection ---
|
|
# On Windows the OS env var is 'Windows_NT' in EVERY shell (cmd, PowerShell,
|
|
# MSYS2, Git-Bash), and GNU Make imports it. Detect Windows from it FIRST so
|
|
# `make` works from a native PowerShell/CMD shell — there `uname` is not on
|
|
# PATH, so the old uname-only check returned empty and fell through to the
|
|
# Linux branch (no .exe, no -static). We only call `uname` when NOT on Windows.
|
|
ifeq ($(OS),Windows_NT)
|
|
IS_WIN := 1
|
|
UNAME_S :=
|
|
UNAME_M :=
|
|
else
|
|
IS_WIN :=
|
|
UNAME_S := $(shell uname -s)
|
|
UNAME_M := $(shell uname -m)
|
|
endif
|
|
|
|
ifeq ($(UNAME_S),Darwin)
|
|
# --- macOS / Apple Silicon ---
|
|
# Apple clang non include il runtime OpenMP: se c'e' libomp di Homebrew lo usa
|
|
# (brew install libomp), altrimenti compila single-thread (i pragma omp sono ignorati).
|
|
# Niente -march: su arm64 NEON e' baseline (i kernel __ARM_NEON si attivano da soli).
|
|
CC = clang
|
|
OMPDIR := $(shell brew --prefix libomp 2>/dev/null)
|
|
ifneq ($(OMPDIR),)
|
|
OMPC = -Xclang -fopenmp -I$(OMPDIR)/include
|
|
OMPL = -L$(OMPDIR)/lib -lomp
|
|
else
|
|
$(warning libomp not found: building single-threaded. For multithreading: brew install libomp)
|
|
OMPC =
|
|
OMPL =
|
|
endif
|
|
CFLAGS = -O3 $(OMPC) -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
|
LDFLAGS = -lm $(OMPL)
|
|
EXE =
|
|
else ifneq ($(IS_WIN),)
|
|
# --- Windows 11 x86-64 (MinGW-w64 / MSYS2) ---
|
|
# GCC + libgomp + winpthreads: pthread, OpenMP, clock_gettime, opendir/readdir,
|
|
# AVX2 intrinsics - tutto gratis, nessun porting.
|
|
# ARCH default = x86-64-v3 (portable binary with AVX2). For max speed on THIS
|
|
# machine use ARCH=native: on AVX-VNNI CPUs (Intel Alder Lake+, Meteor Lake+)
|
|
# it also unlocks the 128-bit VPDPBUSD int8/int4 dot kernel (dot_i8i8/dot_i4i8),
|
|
# which the x86-64-v3 baseline does not define. The #ifdef guards in glm.c mean
|
|
# a v3 build simply compiles out the VNNI path - safe on any x86-64.
|
|
CC = gcc
|
|
ARCH ?= x86-64-v3
|
|
CFLAGS = -D_FILE_OFFSET_BITS=64 -O3 -march=$(ARCH) -fopenmp -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
|
LDFLAGS = -lm -fopenmp -static
|
|
EXE = .exe
|
|
else
|
|
UNAME_M := $(shell uname -m)
|
|
ifneq (,$(filter ppc64le ppc64,$(UNAME_M)))
|
|
# --- Linux PowerPC (POWER8/POWER9/POWER10) ---
|
|
# PowerPC GCC uses -mcpu, not -march. ARCH=native works on gcc >= 4.7.
|
|
# The AVX2/NEON kernels fall back to the portable scalar C path
|
|
# (validated token-exact vs the transformers oracle on a POWER8 S824).
|
|
CC = gcc
|
|
ARCH ?= native
|
|
CFLAGS = -O3 -mcpu=$(ARCH) -fopenmp -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
|
LDFLAGS = -lm -fopenmp
|
|
EXE =
|
|
else
|
|
# --- Linux x86-64 (percorso originale, invariato) ---
|
|
CC = gcc
|
|
# ARCH=native -> ottimizzato per QUESTA macchina (default, piu' veloce).
|
|
# ARCH=x86-64-v3 -> binario PORTABILE su qualsiasi x86-64 moderno con AVX2 (per distribuire).
|
|
# ARCH=x86-64 -> massima compatibilita' (niente AVX2: usa il path scalare di fallback).
|
|
ARCH ?= native
|
|
CFLAGS = -O3 -march=$(ARCH) -fopenmp -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
|
LDFLAGS = -lm -fopenmp
|
|
EXE =
|
|
endif
|
|
endif
|
|
|
|
# CUDA=1 adds an opt-in backend for resident tensors. The default build remains
|
|
# pure C and keeps the original zero-dependency runtime.
|
|
#
|
|
# Two paths:
|
|
# - Linux/macOS: CUDA=1 links backend_cuda.o directly (cudart via -l).
|
|
# - Windows: CUDA_DLL=1 builds a standalone coli_cuda.dll (nvcc+MSVC),
|
|
# then the host glm.exe loads it at runtime via backend_loader.c
|
|
# (LoadLibrary/GetProcAddress). MinGW gcc cannot compile .cu
|
|
# (nvcc needs cl.exe), and cross-linking MSVC objects into a
|
|
# gcc binary is fragile — the DLL split keeps the toolchains
|
|
# clean. See backend_loader.c and README "cuda-dll" below.
|
|
CUDA ?= 0
|
|
CUDA_DLL ?= 0
|
|
CUDA_HOME ?= /usr/local/cuda
|
|
NVCC ?= $(CUDA_HOME)/bin/nvcc
|
|
CUDA_ARCH ?= native
|
|
NVCCFLAGS ?= -O3 -std=c++17 -arch=$(CUDA_ARCH) -Xcompiler=-Wall,-Wextra
|
|
ifeq ($(IS_WIN),)
|
|
PYTHON ?= python3
|
|
else
|
|
PYTHON ?= python
|
|
endif
|
|
CUDA_OBJ =
|
|
TEST_BINS = tests/test_json$(EXE) tests/test_st$(EXE) tests/test_tier$(EXE) tests/test_grammar$(EXE) tests/test_decode_batch$(EXE) tests/test_idot$(EXE) tests/test_i4_acc512$(EXE)
|
|
|
|
# Windows CUDA DLL path: host links the loader, NOT cudart.
|
|
ifneq ($(IS_WIN),)
|
|
ifeq ($(CUDA_DLL),1)
|
|
CFLAGS += -DCOLI_CUDA
|
|
CUDA_OBJ = backend_loader.o
|
|
endif
|
|
endif
|
|
|
|
# Linux CUDA direct-link path (unchanged).
|
|
ifeq ($(CUDA),1)
|
|
ifeq ($(UNAME_S),Darwin)
|
|
$(error CUDA=1 is supported only on Linux)
|
|
endif
|
|
ifneq ($(IS_WIN),)
|
|
# On Windows use CUDA_DLL=1 (runtime DLL), not CUDA=1 (direct link).
|
|
$(error On Windows use: make CUDA_DLL=1 cuda-dll (see backend_loader.c))
|
|
endif
|
|
CFLAGS += -DCOLI_CUDA
|
|
LDFLAGS += -L$(CUDA_HOME)/lib64 -Wl,-rpath,$(CUDA_HOME)/lib64 -lcudart -lstdc++
|
|
CUDA_OBJ = backend_cuda.o
|
|
endif
|
|
|
|
# METAL=1 adds an opt-in Apple-GPU backend (macOS only). The shader is compiled at
|
|
# runtime, so no Xcode / offline metal compiler is required. Default build unchanged.
|
|
METAL ?= 0
|
|
METAL_OBJ =
|
|
METALXX = clang++ -x objective-c++ -fobjc-arc -O3
|
|
ifeq ($(METAL),1)
|
|
ifneq ($(UNAME_S),Darwin)
|
|
$(error METAL=1 is supported only on macOS)
|
|
endif
|
|
CFLAGS += -DCOLI_METAL
|
|
LDFLAGS += -framework Metal -framework Foundation -lc++
|
|
METAL_OBJ = backend_metal.o
|
|
endif
|
|
|
|
all: glm$(EXE)
|
|
|
|
# phony 'glm' → 'glm.exe' on Windows (so 'make glm' and 'coli build' work on every platform)
|
|
glm: glm$(EXE)
|
|
|
|
glm$(EXE): glm.c st.h json.h tok.h tok_unicode.h compat.h grammar.h $(CUDA_OBJ) $(METAL_OBJ)
|
|
$(CC) $(CFLAGS) glm.c $(CUDA_OBJ) $(METAL_OBJ) -o glm$(EXE) $(LDFLAGS)
|
|
|
|
# Windows runtime loader object: resolves coli_cuda_* from coli_cuda.dll.
|
|
backend_loader.o: backend_loader.c backend_cuda.h compat.h
|
|
$(CC) $(CFLAGS) -c backend_loader.c -o $@
|
|
|
|
# Windows CUDA DLL: compile backend_cuda.cu with nvcc (+MSVC cl.exe as host
|
|
# compiler, required by nvcc on Windows) into coli_cuda.dll. Run this from a
|
|
# shell that has the MSVC environment set (e.g. after vcvars64.bat, or from a
|
|
# "x64 Native Tools Command Prompt"). COLI_CUDA_BUILDING_DLL enables
|
|
# __declspec(dllexport) so the 11 API symbols are exported.
|
|
cuda-dll: backend_cuda.cu backend_cuda.h
|
|
@command -v $(NVCC) >/dev/null 2>&1 || { echo "nvcc not found: set CUDA_HOME or NVCC" >&2; exit 1; }
|
|
@command -v cl >/dev/null 2>&1 || { echo "cl.exe (MSVC) not in PATH — run vcvars64.bat first" >&2; exit 1; }
|
|
$(NVCC) $(NVCCFLAGS) -shared -DCOLI_CUDA_BUILDING_DLL \
|
|
-L"$(CUDA_HOME)/lib/x64" -lcudart \
|
|
backend_cuda.cu -o coli_cuda.dll
|
|
|
|
backend_cuda.o: backend_cuda.cu backend_cuda.h
|
|
@command -v $(NVCC) >/dev/null 2>&1 || { echo "nvcc not found: set CUDA_HOME or NVCC" >&2; exit 1; }
|
|
$(NVCC) $(NVCCFLAGS) -c backend_cuda.cu -o $@
|
|
|
|
backend_metal.o: backend_metal.mm backend_metal.h
|
|
$(METALXX) -c backend_metal.mm -o $@
|
|
|
|
metal-test: tests/test_backend_metal.mm backend_metal.mm backend_metal.h
|
|
$(METALXX) tests/test_backend_metal.mm backend_metal.mm -framework Metal -framework Foundation -o backend_metal_test
|
|
./backend_metal_test
|
|
|
|
cuda-test: backend_cuda.cu backend_cuda.h tests/test_backend_cuda.cu
|
|
@command -v $(NVCC) >/dev/null 2>&1 || { echo "nvcc not found: set CUDA_HOME or NVCC" >&2; exit 1; }
|
|
$(NVCC) $(NVCCFLAGS) backend_cuda.cu tests/test_backend_cuda.cu -o backend_cuda_test$(EXE)
|
|
./backend_cuda_test$(EXE)
|
|
|
|
cuda-bench: backend_cuda.cu backend_cuda.h tests/bench_tensor_core.cu
|
|
@command -v $(NVCC) >/dev/null 2>&1 || { echo "nvcc not found: set CUDA_HOME or NVCC" >&2; exit 1; }
|
|
$(NVCC) $(NVCCFLAGS) backend_cuda.cu tests/bench_tensor_core.cu -o backend_cuda_bench$(EXE)
|
|
./backend_cuda_bench$(EXE)
|
|
|
|
olmoe$(EXE): olmoe.c st.h json.h compat.h
|
|
$(CC) $(CFLAGS) olmoe.c -o olmoe$(EXE) $(LDFLAGS)
|
|
|
|
# binario portabile da distribuire su altre macchine x86-64
|
|
portable:
|
|
$(MAKE) glm$(EXE) ARCH=x86-64-v3
|
|
|
|
iobench$(EXE): iobench.c compat.h
|
|
$(CC) $(CFLAGS) iobench.c -o iobench$(EXE) $(LDFLAGS)
|
|
|
|
tests/test_json$(EXE): tests/test_json.c json.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_st$(EXE): tests/test_st.c st.h json.h compat.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_tier$(EXE): tests/test_tier.c tier.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_grammar$(EXE): tests/test_grammar.c grammar.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_decode_batch$(EXE): tests/test_decode_batch.c decode_batch.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_idot$(EXE): tests/test_idot.c glm.c st.h json.h tok.h tok_unicode.h compat.h grammar.h tier.h
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
tests/test_i4_acc512$(EXE): tests/test_i4_acc512.c
|
|
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
|
|
|
test-c: $(TEST_BINS)
|
|
$(PYTHON) tools/run_tests.py $(TEST_BINS)
|
|
|
|
test-python:
|
|
$(PYTHON) -m unittest discover -s tests -p 'test_*.py'
|
|
|
|
test: test-c test-python
|
|
|
|
# Local validation: one portable CPU build and dependency-free tests.
|
|
check:
|
|
$(MAKE) clean
|
|
$(MAKE) portable
|
|
$(MAKE) test
|
|
|
|
clean:
|
|
$(PYTHON) tools/clean.py
|
|
|
|
.PHONY: all glm cuda-test cuda-bench cuda-dll portable test-c test-python test check clean
|