2026-07-22 18:17:43 +02:00
# env/makefile.env — single cross-platform build environment for Klammertext.
2026-07-18 18:48:23 +02:00
#
# Included identically by every component Makefile:
2026-07-22 18:17:43 +02:00
# include $(KLAMMERTEXT_HOME)/env/makefile.env
2026-07-18 18:48:23 +02:00
#
# The platform is auto-detected with uname; the compiler is chosen with
# make COMPILER=gcc (default)
# make COMPILER=clang
# There is no per-host/per-OS file and no DESKTOP_SESSION/HOST/SITE selector.
ifndef KLAMMERTEXT_HOME
2026-07-22 18:17:43 +02:00
$( error KLAMMERTEXT_HOME is not set -- source env/runtime.env first)
2026-07-18 18:48:23 +02:00
endif
2026-07-22 18:17:43 +02:00
include $( KLAMMERTEXT_HOME) /env/optimize.env
2026-07-18 18:48:23 +02:00
UNAME_S := $( shell uname -s)
CPP_VERSION := c++20
# Default compiler: clang on macOS, gcc elsewhere. gcc-built Klammertext
# binaries crash at runtime on macOS (a documented gcc/macOS codegen issue), so
2026-09-22 22:53:13 +02:00
# gcc there is only a compile-time conformance check (via `./dev/rebuild.sh
2026-07-18 18:48:23 +02:00
# gcc`, which compiles under g++ then rebuilds with clang). Override with
# `make COMPILER=...`.
ifeq ( $( UNAME_S) ,Darwin)
COMPILER ?= clang # gcc | clang
else
COMPILER ?= gcc # gcc | clang
endif
2026-09-25 00:32:55 +02:00
# Python: version- and platform-agnostic, no hardcoded paths. On Windows the
# interpreter is python.org's (the one PyPI wheels -- OpenImageIO -- are built
# for); it ships no python3-config, so its prefix is asked of the interpreter
# itself and its import library named by version (libs/python312.lib).
ifneq ( ,$( filter MINGW% MSYS%,$( UNAME_S)) )
PYTHON := python
PYTHON_PREFIX := $( shell cygpath -m " $$ ( $( PYTHON) -c 'import sys; print(sys.base_prefix)')" )
2026-09-26 16:17:59 +02:00
# The interpreter "python" resolves to must be python.org's, which has an
# import library under libs/. MSYS2's own python (installed as a dependency
# of poppler, through glib2) sits in /ucrt64/bin ahead of it and has none:
# a build against it compiles nothing (no Include/Python.h) or embeds an
# interpreter without the user's pip packages. env/runtime.env puts
# python.org's directory first; refuse rather than build the wrong thing.
# (Asked of the interpreter, not $(wildcard): the prefix "C:/Program Files"
# has a space, on which wildcard splits.)
ifeq ( ,$( NOPYTHON) )
ifneq ( yes,$( shell $( PYTHON) -c 'import os, sys; print("yes" if os.path.isdir(os.path.join(sys.base_prefix, "libs")) else "no")' ) )
$( error "python" is $( PYTHON_PREFIX) /python, not python.org's interpreter (no libs/ there); source env/runtime.env, which puts python.org' s Python first on PATH)
endif
endif
2026-09-25 00:32:55 +02:00
PYTHON_INC := -I" $( PYTHON_PREFIX) /Include"
PYTHON_LIB := -L" $( PYTHON_PREFIX) /libs" -l$( shell $( PYTHON) -c 'import sys; print("python%d%d" % sys.version_info[:2])' )
else
PYTHON := python3
2026-07-18 18:48:23 +02:00
PYTHON_INC := $( shell python3-config --includes)
PYTHON_LIB := $( shell python3-config --ldflags --embed)
2026-09-25 00:32:55 +02:00
endif
2026-07-18 18:48:23 +02:00
# Flags common to all platforms.
CPPFLAGS = -I$( KLAMMERTEXT_HOME) /mac $( PYTHON_INC)
CXXFLAGS = -Wall -Wextra -Weffc++ -Wshadow -std= $( CPP_VERSION) -fPIC $( OPTIMIZE) $( SANITIZE)
ifeq ( $( UNAME_S) ,Darwin)
# ---------- macOS (Apple Silicon) ----------
# Package-manager prefix, auto-detected: Homebrew (/opt/homebrew) or MacPorts
# (/opt/local). Override with `make MACOS_PREFIX=...`. Only the prefix differs
# between the two: the compiler is Apple Clang either way, and all Python flags
# come from python3-config (prefix-agnostic), so nothing else is PM-specific.
MACOS_PREFIX ?= $( shell test -d /opt/homebrew && echo /opt/homebrew || ( test -d /opt/local && echo /opt/local || echo /opt/homebrew) )
# Newest optional gcc (conformance check only): Homebrew g++-NN or MacPorts g++-mp-NN.
GCC := $( shell g = $$ ( ls $( MACOS_PREFIX) /bin/g++-[ 0-9] * $( MACOS_PREFIX) /bin/g++-mp-[ 0-9] * 2>/dev/null | sort -V | tail -1) ; echo $$ { g:-g++})
CLANG := clang++ # Apple clang (or brew/port llvm)
CPPFLAGS += -I$( MACOS_PREFIX) /include
LDFLAGS = -L$( MACOS_PREFIX) /lib
LDLIBS = $(if $( NOPYTHON) ,,$( PYTHON_LIB))
SHARED = -dynamiclib
SONAME = -install_name @rpath/$( notdir $@ )
ORIGIN := @loader_path
EXPORT_DYNAMIC = -Wl,-export_dynamic
2026-09-25 18:43:22 +02:00
EXE :=
DSO := .so
KLAMMERTEXT_LIBFILE := libklammertext.so
2026-09-25 00:32:55 +02:00
else ifneq ( ,$( filter MINGW% MSYS%,$( UNAME_S)) )
# ---------- Windows (MSYS2, UCRT64 environment; native PE binaries) ----------
# uname says MINGW64_NT-... under UCRT64 too. The compilers come from the
# environment's PATH (/ucrt64/bin). libdl.a is the mingw-w64-dlfcn shim over
# LoadLibrary, linked statically so no libdl.dll travels with the products.
# PE has no soname and no rpath; a DLL is found through PATH or
2026-09-25 18:43:22 +02:00
# the executable's own directory.
2026-09-25 00:32:55 +02:00
GCC := g++
CLANG := clang++
LDFLAGS =
LDLIBS = -l:libdl.a $(if $( NOPYTHON) ,,$( PYTHON_LIB))
SHARED = -shared
SONAME =
ORIGIN := $$ ORIGIN
EXPORT_DYNAMIC = -Wl,--export-all-symbols
2026-09-25 18:43:22 +02:00
EXE := .exe
DSO := .dll
KLAMMERTEXT_LIBFILE := klammertext.dll
2026-09-25 00:32:55 +02:00
# A Windows program finds a DLL through PATH or its own directory, never an
# rpath. So every directory that holds a program or a Python extension also
2026-09-25 18:43:22 +02:00
# holds the DLLs it needs: klammertext.dll and the three gcc runtime DLLs
2026-09-25 00:32:55 +02:00
# the toolchain links (from the compiler's own bin/). com/ and kpy/ copy them.
GCC_BIN := $( dir $( shell cygpath -m " $$ (command -v g++)" ))
GCC_RUNTIME_DLLS := libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll
2026-07-18 18:48:23 +02:00
else
# ---------- Linux ----------
2026-09-25 18:43:22 +02:00
EXE :=
DSO := .so
KLAMMERTEXT_LIBFILE := libklammertext.so
2026-07-18 18:48:23 +02:00
GCC := /usr/bin/g++
CLANG := $( shell command -v clang++-18 2>/dev/null || command -v clang++ 2>/dev/null)
LDFLAGS = -L/usr/lib/x86_64-linux-gnu
LDLIBS = -ldl $(if $( NOPYTHON) ,,$( PYTHON_LIB))
SHARED = -shared
SONAME = -Wl,-soname,$( notdir $@ )
ORIGIN := $$ ORIGIN
EXPORT_DYNAMIC = -rdynamic
endif
2026-09-25 18:43:22 +02:00
# Product names (decided 2026-09-25: Windows users get Windows names). The
# three variables above are set per platform: EXE is the executable suffix
# (".exe" on Windows, none elsewhere), DSO the suffix of a shared object
# loaded at run time (".dll" / ".so": document$(DSO), reflect$(DSO)), and
# KLAMMERTEXT_LIBFILE the library's file name (klammertext.dll /
# libklammertext.so). A make target carries the suffix so that the file the
# compiler writes IS the target: bin/ktext never existed on Windows -- gcc
# wrote ktext.exe -- and every make relinked the three commands. The engine's
# own copy of the DSO suffix is shared_library_suffix in mac/file.h.
#
# One link form everywhere: GNU ld's -l search list on PE ends with
# "<name>.dll", so -lklammertext finds klammertext.dll as it finds
# libklammertext.so on Linux (it did NOT find a DLL called libklammertext.so,
# which needed the exact-name -l: form).
KLAMMERTEXT_LIB = -lklammertext
2026-07-18 18:48:23 +02:00
# Resolve the compiler choice (gcc by default; clang with COMPILER=clang).
CXX := $(if $( filter clang,$( COMPILER)) ,$( CLANG) ,$( GCC))