cmake_minimum_required(VERSION 3.20) project(chess_spectral C) set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC")) add_compile_options(/W4 /O2) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) else() add_compile_options(-Wall -Wextra -O2) endif() # libm: GNU/Linux requires explicit -lm to link sqrt/fabs/etc. MSVC links # the math runtime implicitly; macOS Clang resolves libm via libSystem # (linking -lm there is a harmless no-op). We expose a single # CS_MATH_LIB variable and link it from each executable that uses # . if(UNIX) set(CS_MATH_LIB m) else() set(CS_MATH_LIB "") endif() # Vendored miniz (public domain, RFC 1950/1951). Only the low-level # deflate/inflate streaming APIs are needed — ZIP archive support # (miniz_zip.c) is intentionally excluded. add_library(miniz STATIC vendor/miniz/miniz.c vendor/miniz/miniz_tdef.c vendor/miniz/miniz_tinfl.c ) target_include_directories(miniz PUBLIC vendor/miniz) target_compile_definitions(miniz PUBLIC MINIZ_NO_ARCHIVE_APIS) if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC")) # Third-party code: don't fail our /W4 warnings on it. target_compile_options(miniz PRIVATE /W0) endif() add_library(cs_core STATIC src/cs_signal.c src/cs_d4.c src/cs_fiber.c src/cs_encoder.c src/cs_tables_data.c ) target_include_directories(cs_core PUBLIC include) add_executable(spectral src/main.c src/cs_fen.c src/cs_frame.c src/cs_gzip.c src/cs_temporal.c ) target_link_libraries(spectral PRIVATE cs_core miniz ${CS_MATH_LIB}) # Bake the absolute path of the PGN bridge into the binary so `spectral` # can auto-invoke it without the user wiring up PATH / env vars. Can be # overridden at runtime with the CS_PGN_BRIDGE environment variable. target_compile_definitions(spectral PRIVATE CS_BRIDGE_SCRIPT_PATH="${CMAKE_SOURCE_DIR}/bridge/pgn_bridge.py" ) add_executable(cs_test test/test_main.c test/test_encoder.c test/test_channels_extra.c test/test_d4.c test/test_temporal.c test/test_gzip.c test/test_frame_v5.c src/cs_temporal.c src/cs_frame.c src/cs_frame_v5.c src/cs_gzip.c ) target_link_libraries(cs_test PRIVATE cs_core miniz ${CS_MATH_LIB}) target_include_directories(cs_test PRIVATE test) enable_testing() add_test(NAME spectral_tests COMMAND cs_test) add_custom_target(codegen COMMAND ${CMAKE_COMMAND} -E echo "Running codegen scripts..." COMMAND python ${CMAKE_SOURCE_DIR}/codegen/emit_tables.py COMMAND python ${CMAKE_SOURCE_DIR}/codegen/emit_test_vectors.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Regenerate C tables and reference test vectors from Python" ) # ─── 4D encoder (v1.1 port) ───────────────────────────────────────────────── # Separate static lib + binary; zero shared TUs with the 2D paths. # All 11 channels are real (verified by python/tests/test_c_py_parity_4d.py # at TOL=1e-10). cs_fen_4d.c (v1.2.4) parses the FEN4 v1 placement literal # documented in docs/FEN4_FORMAT.md. add_library(cs_core_4d STATIC src/cs_signal_4d.c src/cs_a1_4d.c src/cs_std4_4d.c src/cs_diag_4d.c src/cs_pawn_anti_4d.c src/cs_fiber_sym_4d.c src/cs_encoder_4d.c src/cs_fen_4d.c src/cs_frame_4d.c src/cs_tables_data_4d.c # 1.18.0+ pure-phase integer encoder (JPL-compliant). Same float # public API surface, parallel int32 output. Tables generated by # codegen/emit_tables_4d.py alongside the float ones. src/cs_pure_phase_signal_4d.c src/cs_pure_phase_a1_4d.c src/cs_pure_phase_std4_4d.c src/cs_pure_phase_fiber_sym_4d.c src/cs_pure_phase_pawn_w_4d.c src/cs_pure_phase_pawn_y_4d.c src/cs_pure_phase_diag_4d.c src/cs_encoder_pure_phase_4d.c src/cs_pure_phase_tables_data_4d.c ) target_include_directories(cs_core_4d PUBLIC include) add_executable(spectral_4d src/main_4d.c src/cs_gzip.c ) target_link_libraries(spectral_4d PRIVATE cs_core_4d miniz ${CS_MATH_LIB}) add_custom_target(codegen_4d COMMAND ${CMAKE_COMMAND} -E echo "Running 4D codegen scripts..." COMMAND python ${CMAKE_SOURCE_DIR}/codegen/emit_tables_4d.py COMMAND python ${CMAKE_SOURCE_DIR}/codegen/emit_test_vectors_4d.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Regenerate 4D C tables and reference test vectors from Python" ) # ─── 1.7.0 D2 M2.1 — native fast-path for spatial_4d.Bitboard4D ──────────── # Shared library (not an executable) compiled from src/cs_bitboard4d.c. # Loaded at import time by chess_spectral._native_bitboard4d via ctypes. # Pure-Python Bitboard4D continues to work as fallback when this .so/.dll # isn't present (sdist install without C toolchain, Pyodide WASM-less # install, etc.). add_library(cs_bitboard4d SHARED src/cs_bitboard4d.c) target_include_directories(cs_bitboard4d PUBLIC include) # NOTE (1.7.0 M2.2): we deliberately do NOT set # C_VISIBILITY_PRESET=hidden here. The previous M2.1 commit set it # expecting the cs_bb4_* API to keep "default" visibility while # internal helpers got hidden — but the only "internal" symbols in # this TU are `static inline` helpers (popcount64, ctz64) which are # never linker-visible regardless. With C_VISIBILITY_PRESET=hidden, # the entire cs_bb4_* API ended up hidden too, dlsym() returned NULL # at Python ctypes load time, and HAS_NATIVE silently stayed False. # The native-tests in tests/test_native_bitboard4d.py noticed via # their `if not HAS_NATIVE: skip` guards, but the wheel shipped a # .so that couldn't actually be called. Default visibility (CMake's # default) exports the cs_bb4_* API correctly; the static-inline # helpers stay private without any extra annotation. # Force the standard `lib` prefix off on Windows (DLL convention) so # the file is `cs_bitboard4d.dll`, matching what the ctypes wrapper # looks for. POSIX gets the conventional `libcs_bitboard4d.so` / # `libcs_bitboard4d.dylib` from CMake defaults. if(WIN32) set_target_properties(cs_bitboard4d PROPERTIES PREFIX "") endif() # MSVC + Windows SDK 10.0.26100 dialect mismatch: every /std:c* mode # the MSVC C frontend supports (c11, c17, clatest) chokes on # function-like macro expansion in the SDK's SAL annotations # AND the compiler's own SIMD intrinsic prototypes — # both transitively pulled in by / . Errors look # like `'_Ptr': undeclared identifier`, `'_Memory': undeclared # identifier`, `'__x': undeclared identifier` (the macro parameters # leak when the strict-C frontend decides the macro body is malformed). # # The C++ frontend handles these headers cleanly because it has full # C++23 conformance and a more capable preprocessor. The cs_bb4_* # source is plain bit-twiddling — no C99-only syntax, no compound # literals, no designated initializers — so compiling it as C++ on # MSVC is a one-line workaround. The header already wraps its API in # `extern "C"`, so callers don't see any difference. # # Linux / macOS / Pyodide still build cs_bitboard4d as C (gcc / clang # compile the strict-C17 source unchanged). if(MSVC) set_source_files_properties(src/cs_bitboard4d.c PROPERTIES LANGUAGE CXX) endif() # ─── 1.18.0 — native shared library for the pure-phase 4D encoder ───────── # Loaded at import time by chess_spectral._native_pure_phase_4d via # ctypes. Mirrors the cs_bitboard4d pattern: separate SHARED .so/.dll # alongside the static cs_core_4d, with default symbol visibility so # every cs_encode_pure_phase_4d / cs_channel_pure_phase_*_4d entry # point is dlsym-visible from Python. add_library(cs_encoder_pure_phase_4d SHARED src/cs_pure_phase_signal_4d.c src/cs_pure_phase_a1_4d.c src/cs_pure_phase_std4_4d.c src/cs_pure_phase_fiber_sym_4d.c src/cs_pure_phase_pawn_w_4d.c src/cs_pure_phase_pawn_y_4d.c src/cs_pure_phase_diag_4d.c src/cs_encoder_pure_phase_4d.c src/cs_pure_phase_tables_data_4d.c # ORBIT_OFFSETS / ORBIT_MEMBERS / PIECE_ADJ_* symbols live in the # float-table TU; pure-phase A_1 + fiber-sym channels use them. # Linked statically into the shared lib (compiled twice — once # for cs_core_4d, once here — but the data is const and the cost # is ~5 MB of binary, acceptable for the ctypes path). src/cs_tables_data_4d.c ) target_include_directories(cs_encoder_pure_phase_4d PUBLIC include) # WINDOWS_EXPORT_ALL_SYMBOLS asks CMake to auto-generate the # __declspec(dllexport) annotations for every public function on # MSVC, so the C public API survives the C → DLL build without # every header needing a manual decoration. Linux / macOS export # everything by default; this is a Windows-only need. Mirrors the # pattern the cs_bitboard4d shared lib relies on under the hood # (it builds as C++ which has different export defaults; we keep # pure-phase strict-C and use the explicit CMake property). set_target_properties(cs_encoder_pure_phase_4d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) if(WIN32) set_target_properties(cs_encoder_pure_phase_4d PROPERTIES PREFIX "") endif() # Plain C17 build (the cs_bitboard4d C++-as-C trick doesn't work here # because the pure-phase TUs include cs_types_4d.h which uses # _Static_assert — a C-only keyword. The shared TUs already build # clean as C in cs_core_4d so the same source files build as C here # without further MSVC workarounds.) # ─── 1.19.0 — native shared library for the pure-phase 2D encoder ───────── # Loaded at import time by chess_spectral._native_pure_phase_2d via # ctypes. Mirror of the 4D port pattern: separate SHARED .so/.dll # alongside the static cs_core (which is the float 2D encoder), with # default symbol visibility so every cs_encode_pure_phase_2d / # cs_channel_pure_phase_*_2d entry point is dlsym-visible from Python. # # Closes the chess2d / chess4d C-port parity gap noted in the 1.18.0 # CHANGELOG: 4D shipped a JPL-compliant pure-phase C port; 2D was # Python-only. With 1.19.0 both encoders have parallel C paths. add_library(cs_encoder_pure_phase_2d SHARED src/cs_pure_phase_signal_2d.c src/cs_pure_phase_d4_irreps_2d.c src/cs_pure_phase_fiber_sym_2d.c src/cs_pure_phase_fa_2d.c src/cs_pure_phase_diag_2d.c src/cs_encoder_pure_phase_2d.c src/cs_pure_phase_tables_data_2d.c # D4_PERMS / CHARS symbols live in the float-table TU; # pure-phase D4 channel uses them. Linked statically into the # shared lib (compiled twice — once for cs_core, once here — # but the data is const and small). src/cs_tables_data.c ) target_include_directories(cs_encoder_pure_phase_2d PUBLIC include) # Same WINDOWS_EXPORT_ALL_SYMBOLS pattern as the 4D pure-phase lib; # auto-generate __declspec(dllexport) annotations on MSVC so the # strict-C17 sources don't need manual decoration. set_target_properties(cs_encoder_pure_phase_2d PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) if(WIN32) set_target_properties(cs_encoder_pure_phase_2d PROPERTIES PREFIX "") endif() # Plain C17 build. The 2D pure-phase headers use _Static_assert which # is a C-only keyword; the C++-as-C trick used for cs_bitboard4d does # not apply here. # ─── scikit-build-core wheel install ──────────────────────────────────────── # When building under scikit-build-core (i.e. `pip install .` or # `python -m build`), SKBUILD is defined and we install spectral + # spectral_4d binaries inside the wheel under chess_spectral/_native/. # The Python `_find_native_binary` helper (cli.py) checks that path # before falling back to repo-build paths. if(DEFINED SKBUILD) install(TARGETS spectral spectral_4d RUNTIME DESTINATION chess_spectral/_native COMPONENT chess_spectral_native) # 1.7.0 D2 M2.1 — also ship the bitboard shared library. # RUNTIME for Windows DLLs, LIBRARY for POSIX .so/.dylib. install(TARGETS cs_bitboard4d RUNTIME DESTINATION chess_spectral/_native LIBRARY DESTINATION chess_spectral/_native COMPONENT chess_spectral_native) # 1.19.0 — pure-phase 2D shared library. install(TARGETS cs_encoder_pure_phase_2d RUNTIME DESTINATION chess_spectral/_native LIBRARY DESTINATION chess_spectral/_native COMPONENT chess_spectral_native) endif()