# --- 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 (binario portabile con AVX2). Per max velocita' # su QUESTA macchina: make ARCH=native 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. CUDA ?= 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 PYTHON ?= python3 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) ifeq ($(CUDA),1) ifeq ($(UNAME_S),Darwin) $(error CUDA=1 is supported only on Linux) endif ifneq ($(IS_WIN),) # GPU: stub only in Phase 1 (G0). G1 builds coli_cuda.dll with MSVC+nvcc. $(error CUDA=1 on Windows requires G1: build coli_cuda.dll with MSVC+nvcc (see PORT_WINDOWS_PLAN.md ยง8)) 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) 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) @for test in $(TEST_BINS); do ./$$test || exit 1; done 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: rm -f olmoe$(EXE) glm$(EXE) iobench$(EXE) backend_cuda.o backend_cuda_test$(EXE) backend_cuda_bench$(EXE) backend_metal.o backend_metal_test $(TEST_BINS) rm -rf tests/__pycache__ .PHONY: all glm cuda-test cuda-bench portable test-c test-python test check clean