From 67d6bcd005e112c3d67b62ef47d584e7f0121ee9 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 19:13:17 -0400 Subject: [PATCH 01/13] Complete CMake library targets --- CMakeLists.txt | 116 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d10c279d..723eafb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,15 +6,23 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") set(CMAKE_CXX_STANDARD 11) -# The following setups the simd_* variables +include(GNUInstallDirs) + +# Clang-cl uses MSVC command-line and binary interfaces despite having a +# different compiler ID. Treat both toolchains alike when selecting targets. +set(REFLEX_MSVC_TOOLCHAIN FALSE) +if(MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + set(REFLEX_MSVC_TOOLCHAIN TRUE) +endif() + +# The following sets up the simd_* variables. include(SIMDTestAndSetup) # # Defining source variables # -set(lib_sources - lib/convert.cpp +set(libreflex_common_sources lib/debug.cpp lib/error.cpp lib/input.cpp @@ -22,13 +30,17 @@ set(lib_sources lib/matcher_avx2.cpp lib/matcher_avx512bw.cpp lib/pattern.cpp - lib/posix.cpp lib/simd.cpp lib/simd_avx2.cpp lib/simd_avx512bw.cpp - lib/unicode.cpp lib/utf8.cpp +) +set(libreflex_sources + ${libreflex_common_sources} + lib/convert.cpp + lib/posix.cpp + lib/unicode.cpp unicode/block_scripts.cpp unicode/language_scripts.cpp unicode/letter_scripts.cpp @@ -36,53 +48,80 @@ set(lib_sources unicode/composer.cpp ) +set(libreflexmin_sources + ${libreflex_common_sources} +) + set(bin_sources src/reflex.cpp ) -list(TRANSFORM lib_sources PREPEND ${PROJECT_SOURCE_DIR}/) -list(TRANSFORM bin_sources PREPEND ${PROJECT_SOURCE_DIR}/) - # # Defining targets section # -add_library(ReflexLib SHARED "") -target_sources(ReflexLib PRIVATE ${lib_sources}) -target_include_directories(ReflexLib PUBLIC - $ - $ -) -target_compile_definitions(ReflexLib PRIVATE ${simd_definitions}) -target_compile_options(ReflexLib PRIVATE ${simd_flags}) - -add_library(ReflexLibStatic STATIC "") -target_sources(ReflexLibStatic PRIVATE ${lib_sources}) -target_include_directories(ReflexLibStatic PUBLIC - $ - $ +# The public headers do not declare symbols with dllexport/dllimport, so an +# MSVC-style toolchain can only produce usable static libraries at present. +if(REFLEX_MSVC_TOOLCHAIN) + add_library(ReflexLib STATIC) + add_library(ReflexMinLib STATIC) + set(reflex_library_targets ReflexLib) + set(reflexmin_library_targets ReflexMinLib) +else() + add_library(ReflexLib SHARED) + add_library(ReflexLibStatic STATIC) + add_library(ReflexMinLib SHARED) + add_library(ReflexMinLibStatic STATIC) + set(reflex_library_targets ReflexLib ReflexLibStatic) + set(reflexmin_library_targets ReflexMinLib ReflexMinLibStatic) +endif() + +foreach(target IN LISTS reflex_library_targets) + target_sources(${target} PRIVATE ${libreflex_sources}) +endforeach() + +foreach(target IN LISTS reflexmin_library_targets) + target_sources(${target} PRIVATE ${libreflexmin_sources}) +endforeach() + +set(reflex_all_library_targets + ${reflex_library_targets} + ${reflexmin_library_targets} ) -target_compile_definitions(ReflexLibStatic PRIVATE ${simd_definitions}) -target_compile_options(ReflexLibStatic PRIVATE ${simd_flags}) -add_executable(Reflex "") -target_sources(Reflex PRIVATE ${bin_sources}) -target_link_libraries(Reflex PRIVATE ReflexLibStatic) +foreach(target IN LISTS reflex_all_library_targets) + target_include_directories(${target} PUBLIC + $ + $ + ) + target_compile_definitions(${target} PRIVATE ${simd_definitions}) + target_compile_options(${target} PRIVATE ${simd_flags}) +endforeach() + +add_executable(Reflex ${bin_sources}) +if(TARGET ReflexLibStatic) + target_link_libraries(Reflex PRIVATE ReflexLibStatic) +else() + target_link_libraries(Reflex PRIVATE ReflexLib) +endif() target_compile_definitions(Reflex PRIVATE ${simd_definitions}) target_compile_options(Reflex PRIVATE ${simd_flags}) -# Don't user target name as filename instead use lowercase name for backwards compatibility -set_target_properties(ReflexLibStatic PROPERTIES OUTPUT_NAME reflex_static_lib) -set_target_properties(ReflexLib PROPERTIES OUTPUT_NAME reflex_shared_lib) +foreach(target IN LISTS reflex_library_targets) + set_target_properties(${target} PROPERTIES OUTPUT_NAME reflex) +endforeach() + +foreach(target IN LISTS reflexmin_library_targets) + set_target_properties(${target} PROPERTIES OUTPUT_NAME reflexmin) +endforeach() + set_target_properties(Reflex PROPERTIES OUTPUT_NAME reflex) # # Exporting targets section # -include(GNUInstallDirs) - -install(TARGETS Reflex ReflexLib ReflexLibStatic +install(TARGETS Reflex ${reflex_all_library_targets} EXPORT ReflexTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -122,7 +161,6 @@ install(FILES set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") -set(REFLEX_PKGCONFIG_LIBRARY "reflex_shared_lib") if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") set(libdir "${CMAKE_INSTALL_LIBDIR}") @@ -136,14 +174,20 @@ else() set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() -# Compatibility note: CMake currently installs libreflex_shared_lib rather than -# libreflex, so its pkg-config file must use the CMake library name. +set(REFLEX_PKGCONFIG_LIBRARY reflex) configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" @ONLY ) +set(REFLEX_PKGCONFIG_LIBRARY reflexmin) +configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in + "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" + @ONLY +) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" + "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) From e29ed348b996dfd05b744ed6f29e9bad12d4f13c Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 19:19:48 -0400 Subject: [PATCH 02/13] Make CMake SIMD detection cross-build safe --- CMakeLists.txt | 38 +++++++-- cmake/SIMDTestAndSetup.cmake | 151 +++++++++++++++++++++-------------- 2 files changed, 121 insertions(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 723eafb7..2cc8eff6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,12 +27,8 @@ set(libreflex_common_sources lib/error.cpp lib/input.cpp lib/matcher.cpp - lib/matcher_avx2.cpp - lib/matcher_avx512bw.cpp lib/pattern.cpp lib/simd.cpp - lib/simd_avx2.cpp - lib/simd_avx512bw.cpp lib/utf8.cpp ) @@ -56,6 +52,36 @@ set(bin_sources src/reflex.cpp ) +set(reflex_simd_objects "") + +# These noninstalled PIC objects keep AVX2 instructions out of the baseline +# sources while allowing all static and shared libraries to reuse one build. +if(REFLEX_COMPILER_HAS_AVX2) + add_library(ReflexAvx2Objects OBJECT + lib/matcher_avx2.cpp + lib/simd_avx2.cpp + ) + set_target_properties(ReflexAvx2Objects PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(ReflexAvx2Objects PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_definitions(ReflexAvx2Objects PRIVATE ${simd_definitions}) + target_compile_options(ReflexAvx2Objects PRIVATE ${simd_avx2_flags}) + list(APPEND reflex_simd_objects $) +endif() + +# AVX512BW needs a distinct compiler option and uses the AVX2 objects above as +# its runtime fallback, so it is a separate private object target as well. +if(REFLEX_COMPILER_HAS_AVX512BW) + add_library(ReflexAvx512bwObjects OBJECT + lib/matcher_avx512bw.cpp + lib/simd_avx512bw.cpp + ) + set_target_properties(ReflexAvx512bwObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(ReflexAvx512bwObjects PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_definitions(ReflexAvx512bwObjects PRIVATE ${simd_definitions}) + target_compile_options(ReflexAvx512bwObjects PRIVATE ${simd_avx512bw_flags}) + list(APPEND reflex_simd_objects $) +endif() + # # Defining targets section # @@ -77,11 +103,11 @@ else() endif() foreach(target IN LISTS reflex_library_targets) - target_sources(${target} PRIVATE ${libreflex_sources}) + target_sources(${target} PRIVATE ${libreflex_sources} ${reflex_simd_objects}) endforeach() foreach(target IN LISTS reflexmin_library_targets) - target_sources(${target} PRIVATE ${libreflexmin_sources}) + target_sources(${target} PRIVATE ${libreflexmin_sources} ${reflex_simd_objects}) endforeach() set(reflex_all_library_targets diff --git a/cmake/SIMDTestAndSetup.cmake b/cmake/SIMDTestAndSetup.cmake index e82891d5..f665c4f1 100644 --- a/cmake/SIMDTestAndSetup.cmake +++ b/cmake/SIMDTestAndSetup.cmake @@ -1,96 +1,123 @@ -# SIMD intrinsics setup and test. -# Tests whether specifics intrinsics are available and then defines macro definitions -# Expose the simd_definitions and simd_flags when included from another cmake file. +# Test whether the compiler accepts each SIMD implementation with its required +# options. The resulting variables configure baseline and specialized targets. -include(CheckCXXSourceRuns) +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) option(USE_AVX512BW "Enable AVX512BW intrinsics (if available)" ON) option(USE_AVX2 "Enable AVX2 intrinsics (if available)" ON) option(USE_SSE2 "Enable SSE2 intrinsics (if available)" ON) option(USE_NEON "Enable NEON intrinsics (if available)" ON) -if (USE_AVX512BW) - check_cxx_source_runs(" - #include +if(REFLEX_MSVC_TOOLCHAIN) + set(reflex_sse2_flag "/arch:SSE2") + set(reflex_avx2_flag "/arch:AVX2") + set(reflex_avx512bw_flag "/arch:AVX512") +else() + set(reflex_sse2_flag "-msse2") + set(reflex_avx2_flag "-mavx2") + set(reflex_avx512bw_flag "-mavx512bw") +endif() + +if(USE_SSE2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_sse2_flag}") + check_cxx_source_compiles(" + #include int main() { - __m512 n = _mm512_set1_epi8(42); - (void)_mm512_cmpeq_epi8_mask(n, n); - return 0; + __m128i n = _mm_set1_epi8(42); + return _mm_cvtsi128_si32(n) != 42; } - " HAVE_AVX512BW) + " REFLEX_COMPILER_HAS_SSE2) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_SSE2 FALSE) endif() -if (USE_AVX2) - check_cxx_source_runs(" +# AVX implementations use SSE2 as their portable runtime fallback, so do not +# enable them when the baseline SSE2 implementation was explicitly disabled. +if(USE_AVX2 AND REFLEX_COMPILER_HAS_SSE2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_avx2_flag}") + check_cxx_source_compiles(" #include int main() { __m256i n = _mm256_set1_epi8(42); - (void)_mm256_movemask_epi8(_mm256_and_si256(n, n)); - return 0; + return _mm256_movemask_epi8(_mm256_and_si256(n, n)) == 0; } - " HAVE_AVX2) + " REFLEX_COMPILER_HAS_AVX2) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_AVX2 FALSE) endif() -if (USE_SSE2) - check_cxx_source_runs(" - #include +# The AVX512BW implementation dispatches to AVX2 on machines without AVX512BW, +# so only build it when the AVX2 implementation is also available. +if(USE_AVX512BW AND REFLEX_COMPILER_HAS_AVX2) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "${reflex_avx512bw_flag}") + check_cxx_source_compiles(" + #include int main() { - __m128i n = _mm_set1_epi8(42); - return 0; + __m512i n = _mm512_set1_epi8(42); + return _mm512_cmpeq_epi8_mask(n, n) == 0; } - " HAVE_SSE2) + " REFLEX_COMPILER_HAS_AVX512BW) + cmake_pop_check_state() +else() + set(REFLEX_COMPILER_HAS_AVX512BW FALSE) endif() -if (USE_NEON) - check_cxx_source_runs(" +if(USE_NEON AND NOT REFLEX_COMPILER_HAS_SSE2) + cmake_push_check_state(RESET) + check_cxx_source_compiles(" #include int main() { - uint64x2_t n; - uint64_t m = vgetq_lane_u64(n, 0); - return 0; + uint64x2_t n = vdupq_n_u64(42); + return vgetq_lane_u64(n, 0) != 42; } - " HAVE_NEON) + " REFLEX_COMPILER_HAS_NEON) + cmake_pop_check_state() + + if(NOT REFLEX_COMPILER_HAS_NEON AND NOT REFLEX_MSVC_TOOLCHAIN) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_FLAGS "-mfpu=neon") + check_cxx_source_compiles(" + #include + int main() { + uint64x2_t n = vdupq_n_u64(42); + return vgetq_lane_u64(n, 0) != 42; + } + " REFLEX_COMPILER_HAS_NEON_WITH_FLAG) + cmake_pop_check_state() + else() + set(REFLEX_COMPILER_HAS_NEON_WITH_FLAG FALSE) + endif() +else() + set(REFLEX_COMPILER_HAS_NEON FALSE) + set(REFLEX_COMPILER_HAS_NEON_WITH_FLAG FALSE) endif() set(simd_definitions "") set(simd_flags "") +set(simd_avx2_flags "") +set(simd_avx512bw_flags "") -if (${HAVE_AVX512BW}) +if(REFLEX_COMPILER_HAS_AVX512BW) list(APPEND simd_definitions HAVE_AVX512BW) - if (WIN32 AND MSVC) - list(APPEND simd_flags "/arch:AVX512") - else() - list(APPEND simd_flags "-mavx512bw") - endif() -endif() - -if (${HAVE_AVX2}) + list(APPEND simd_flags "${reflex_sse2_flag}") + list(APPEND simd_avx2_flags "${reflex_avx2_flag}") + list(APPEND simd_avx512bw_flags "${reflex_avx512bw_flag}") +elseif(REFLEX_COMPILER_HAS_AVX2) list(APPEND simd_definitions HAVE_AVX2) - if (WIN32 AND MSVC) - list(APPEND simd_flags "/arch:AVX2") - else() - list(APPEND simd_flags "-mavx2") - endif() -endif() - -if (${HAVE_SSE2}) + list(APPEND simd_flags "${reflex_sse2_flag}") + list(APPEND simd_avx2_flags "${reflex_avx2_flag}") +elseif(REFLEX_COMPILER_HAS_SSE2) list(APPEND simd_definitions HAVE_SSE2) - if (WIN32 AND MSVC) - if ("${CMAKE_GENERATOR_PLATFORM}" MATCHES "Win32") - # SSE2 is a given if the system is running x64 - list(APPEND simd_flags "/arch:SSE2") - endif() - else() - list(APPEND simd_flags "-msse2") - endif() -endif() - -if (${HAVE_NEON}) + list(APPEND simd_flags "${reflex_sse2_flag}") +elseif(REFLEX_COMPILER_HAS_NEON) list(APPEND simd_definitions HAVE_NEON) - if ((${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm64|aarch64") OR (WIN32 AND MSVC)) - # Arm64 compilers and MSVC runs NEON by default according to their docs - else() - list(APPEND simd_flags "-march=native" "-mfpu=neon") - endif() +elseif(REFLEX_COMPILER_HAS_NEON_WITH_FLAG) + list(APPEND simd_definitions HAVE_NEON) + list(APPEND simd_flags "-mfpu=neon") endif() - From 4787e0b5810198b0cfe84777d4ba07899a299b4e Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 20:43:08 -0400 Subject: [PATCH 03/13] Add rtest to the CMake build --- CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cc8eff6..7f58d2e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") set(CMAKE_CXX_STANDARD 11) include(GNUInstallDirs) +include(CTest) # Clang-cl uses MSVC command-line and binary interfaces despite having a # different compiler ID. Treat both toolchains alike when selecting targets. @@ -133,6 +134,18 @@ endif() target_compile_definitions(Reflex PRIVATE ${simd_definitions}) target_compile_options(Reflex PRIVATE ${simd_flags}) +if(BUILD_TESTING) + add_executable(rtest tests/rtest.cpp) + if(TARGET ReflexLibStatic) + target_link_libraries(rtest PRIVATE ReflexLibStatic) + else() + # MSVC-style builds expose ReflexLib itself as the static library because + # the public headers do not provide the exports required by a usable DLL. + target_link_libraries(rtest PRIVATE ReflexLib) + endif() + add_test(NAME rtest COMMAND rtest) +endif() + foreach(target IN LISTS reflex_library_targets) set_target_properties(${target} PROPERTIES OUTPUT_NAME reflex) endforeach() From 18b68556547d5ffed71f3904b4372a313b0b01df Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:03:07 -0400 Subject: [PATCH 04/13] Explain the CMake build structure --- CMakeLists.txt | 58 +++++++++++++++++++++++------------- cmake/Config.cmake.in | 6 +++- cmake/SIMDTestAndSetup.cmake | 12 ++++++++ 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f58d2e6..0d935712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,10 +2,14 @@ cmake_minimum_required(VERSION 3.15) project(reflex VERSION 6.3.0 LANGUAGES CXX) +# Make the project-specific modules under cmake/ available to include(). set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +# Ask CMake to compile all targets as C++11. set(CMAKE_CXX_STANDARD 11) +# GNUInstallDirs supplies portable install paths, while CTest defines the +# BUILD_TESTING option and enables test registration when requested. include(GNUInstallDirs) include(CTest) @@ -19,10 +23,8 @@ endif() # The following sets up the simd_* variables. include(SIMDTestAndSetup) -# -# Defining source variables -# - +# Keep the matcher implementation shared by the full and minimal libraries in +# one list so the two variants do not drift apart. set(libreflex_common_sources lib/debug.cpp lib/error.cpp @@ -33,6 +35,7 @@ set(libreflex_common_sources lib/utf8.cpp ) +# The full library adds pattern conversion, POSIX support, and Unicode tables. set(libreflex_sources ${libreflex_common_sources} lib/convert.cpp @@ -45,14 +48,17 @@ set(libreflex_sources unicode/composer.cpp ) +# The minimal library contains only the runtime needed by generated scanners. set(libreflexmin_sources ${libreflex_common_sources} ) +# The reflex scanner generator itself is built from a single translation unit. set(bin_sources src/reflex.cpp ) +# Collect optional optimized objects for reuse by every library variant. set(reflex_simd_objects "") # These noninstalled PIC objects keep AVX2 instructions out of the baseline @@ -83,10 +89,7 @@ if(REFLEX_COMPILER_HAS_AVX512BW) list(APPEND reflex_simd_objects $) endif() -# -# Defining targets section -# - +# Define the available static and shared library targets for this toolchain. # The public headers do not declare symbols with dllexport/dllimport, so an # MSVC-style toolchain can only produce usable static libraries at present. if(REFLEX_MSVC_TOOLCHAIN) @@ -103,19 +106,24 @@ else() set(reflexmin_library_targets ReflexMinLib ReflexMinLibStatic) endif() +# Set the source files contained in each full-library target. foreach(target IN LISTS reflex_library_targets) target_sources(${target} PRIVATE ${libreflex_sources} ${reflex_simd_objects}) endforeach() +# Set the smaller source set contained in each minimal-library target. foreach(target IN LISTS reflexmin_library_targets) target_sources(${target} PRIVATE ${libreflexmin_sources} ${reflex_simd_objects}) endforeach() +# Gather all library targets so their common build settings remain identical. set(reflex_all_library_targets ${reflex_library_targets} ${reflexmin_library_targets} ) +# Use source-tree headers while building and installed headers for consumers; +# SIMD definitions and options remain private implementation details. foreach(target IN LISTS reflex_all_library_targets) target_include_directories(${target} PUBLIC $ @@ -125,15 +133,22 @@ foreach(target IN LISTS reflex_all_library_targets) target_compile_options(${target} PRIVATE ${simd_flags}) endforeach() +# Link the generator to the static full library where it has a distinct target; +# on MSVC-style toolchains ReflexLib is already the static library. add_executable(Reflex ${bin_sources}) if(TARGET ReflexLibStatic) target_link_libraries(Reflex PRIVATE ReflexLibStatic) else() target_link_libraries(Reflex PRIVATE ReflexLib) endif() + +# The generator includes SIMD-aware public headers, so use the same baseline +# definitions and compiler options as the library implementation. target_compile_definitions(Reflex PRIVATE ${simd_definitions}) target_compile_options(Reflex PRIVATE ${simd_flags}) +# CTest defines BUILD_TESTING; when enabled, build and register the same rtest +# regression executable used by the Autotools build. if(BUILD_TESTING) add_executable(rtest tests/rtest.cpp) if(TARGET ReflexLibStatic) @@ -146,6 +161,8 @@ if(BUILD_TESTING) add_test(NAME rtest COMMAND rtest) endif() +# Target names are internal to CMake; give installed artifacts their traditional +# reflex and reflexmin basenames for compatibility with existing consumers. foreach(target IN LISTS reflex_library_targets) set_target_properties(${target} PROPERTIES OUTPUT_NAME reflex) endforeach() @@ -156,10 +173,8 @@ endforeach() set_target_properties(Reflex PROPERTIES OUTPUT_NAME reflex) -# -# Exporting targets section -# - +# Install the generator and public libraries, and record the library targets for +# the find_package export generated below. install(TARGETS Reflex ${reflex_all_library_targets} EXPORT ReflexTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} @@ -168,20 +183,20 @@ install(TARGETS Reflex ${reflex_all_library_targets} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) +# Install only the public C++ headers from the source tree. install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/reflex DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h" ) +# Install imported targets such as Reflex::ReflexLib for downstream CMake users. install(EXPORT ReflexTargets NAMESPACE Reflex:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) -# -# Packaging section (find_package support) -# - +# Generate and install the relocatable configuration loaded by +# find_package(Reflex CONFIG). include(CMakePackageConfigHelpers) configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in @@ -194,13 +209,13 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) -# -# Packaging section (pkg-config support) -# - +# Preserve pkg-config's variable references in the generated metadata instead +# of expanding every path at CMake configuration time. set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") +# GNUInstallDirs permits absolute or prefix-relative directories; normalize +# both forms into the paths expected by pkg-config files. if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") set(libdir "${CMAKE_INSTALL_LIBDIR}") else() @@ -213,6 +228,8 @@ else() set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() +# Generate metadata for both libraries from one template, substituting the +# appropriate library basename into each output file. set(REFLEX_PKGCONFIG_LIBRARY reflex) configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" @@ -225,6 +242,7 @@ configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in @ONLY ) +# Install both pkg-config descriptions alongside the libraries. install(FILES "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index 5cf14bf5..dbf74ad5 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -1,5 +1,9 @@ +# CMake expands this marker into helpers that make the installed package +# relocatable and provide check_required_components(). @PACKAGE_INIT@ +# Load the imported Reflex:: targets generated by the project's install(EXPORT). include("${CMAKE_CURRENT_LIST_DIR}/ReflexTargets.cmake") -check_required_components(Reflex) \ No newline at end of file +# Honor REQUIRED semantics if consumers request package components later. +check_required_components(Reflex) diff --git a/cmake/SIMDTestAndSetup.cmake b/cmake/SIMDTestAndSetup.cmake index f665c4f1..7eff3aac 100644 --- a/cmake/SIMDTestAndSetup.cmake +++ b/cmake/SIMDTestAndSetup.cmake @@ -1,14 +1,19 @@ # Test whether the compiler accepts each SIMD implementation with its required # options. The resulting variables configure baseline and specialized targets. +# These modules provide compile probes and isolate their temporary flags from +# the rest of the project configuration. include(CheckCXXSourceCompiles) include(CMakePushCheckState) +# Options let packagers disable a backend; an enabled backend must still pass +# its compiler probe before it is built. option(USE_AVX512BW "Enable AVX512BW intrinsics (if available)" ON) option(USE_AVX2 "Enable AVX2 intrinsics (if available)" ON) option(USE_SSE2 "Enable SSE2 intrinsics (if available)" ON) option(USE_NEON "Enable NEON intrinsics (if available)" ON) +# Select the SIMD option spelling understood by the active compiler interface. if(REFLEX_MSVC_TOOLCHAIN) set(reflex_sse2_flag "/arch:SSE2") set(reflex_avx2_flag "/arch:AVX2") @@ -19,6 +24,8 @@ else() set(reflex_avx512bw_flag "-mavx512bw") endif() +# Compile rather than run the probe so configuration also works when the build +# machine cannot execute binaries for the target architecture. if(USE_SSE2) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_FLAGS "${reflex_sse2_flag}") @@ -68,6 +75,8 @@ else() set(REFLEX_COMPILER_HAS_AVX512BW FALSE) endif() +# Probe NEON only when no x86 backend is available. AArch64 needs no extra +# option, while some 32-bit ARM compilers require -mfpu=neon. if(USE_NEON AND NOT REFLEX_COMPILER_HAS_SSE2) cmake_push_check_state(RESET) check_cxx_source_compiles(" @@ -98,11 +107,14 @@ else() set(REFLEX_COMPILER_HAS_NEON_WITH_FLAG FALSE) endif() +# These lists are consumed by the parent CMakeLists.txt when it defines targets. set(simd_definitions "") set(simd_flags "") set(simd_avx2_flags "") set(simd_avx512bw_flags "") +# Define only the strongest backend macro. Baseline targets stay at SSE2 while +# private AVX object targets receive the flags for their specialized code. if(REFLEX_COMPILER_HAS_AVX512BW) list(APPEND simd_definitions HAVE_AVX512BW) list(APPEND simd_flags "${reflex_sse2_flag}") From a232e6a33dee8008b4602e75923d6eb94bb4a021 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:39:50 -0400 Subject: [PATCH 05/13] Remove redundant CMake export metadata --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d935712..4ad58955 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,7 +180,6 @@ install(TARGETS Reflex ${reflex_all_library_targets} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) # Install only the public C++ headers from the source tree. From 7b2a9788d35892ef3e4a74d7923c396e3624de83 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:41:33 -0400 Subject: [PATCH 06/13] Keep library-private SIMD settings off reflex --- CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ad58955..def46bf3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,11 +142,6 @@ else() target_link_libraries(Reflex PRIVATE ReflexLib) endif() -# The generator includes SIMD-aware public headers, so use the same baseline -# definitions and compiler options as the library implementation. -target_compile_definitions(Reflex PRIVATE ${simd_definitions}) -target_compile_options(Reflex PRIVATE ${simd_flags}) - # CTest defines BUILD_TESTING; when enabled, build and register the same rtest # regression executable used by the Autotools build. if(BUILD_TESTING) From e05f2aceba680f7263485f13763d632eae531353 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:41:46 -0400 Subject: [PATCH 07/13] Simplify the reflex executable definition --- CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index def46bf3..993c2a2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,11 +53,6 @@ set(libreflexmin_sources ${libreflex_common_sources} ) -# The reflex scanner generator itself is built from a single translation unit. -set(bin_sources - src/reflex.cpp -) - # Collect optional optimized objects for reuse by every library variant. set(reflex_simd_objects "") @@ -135,7 +130,7 @@ endforeach() # Link the generator to the static full library where it has a distinct target; # on MSVC-style toolchains ReflexLib is already the static library. -add_executable(Reflex ${bin_sources}) +add_executable(Reflex src/reflex.cpp) if(TARGET ReflexLibStatic) target_link_libraries(Reflex PRIVATE ReflexLibStatic) else() From 42390bb45436bbeb10e5fa585ebbf49adb79b2ad Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:42:18 -0400 Subject: [PATCH 08/13] Share pkg-config templates between build systems --- CMakeLists.txt | 10 ++++------ cmake/reflex.pc.in | 11 ----------- 2 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 cmake/reflex.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 993c2a2e..e00d815f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,16 +217,14 @@ else() set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() -# Generate metadata for both libraries from one template, substituting the -# appropriate library basename into each output file. -set(REFLEX_PKGCONFIG_LIBRARY reflex) -configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in +# Reuse the Autotools templates, which expect the project version in VERSION. +set(VERSION "${PROJECT_VERSION}") +configure_file(${PROJECT_SOURCE_DIR}/reflex.pc.in "${CMAKE_CURRENT_BINARY_DIR}/reflex.pc" @ONLY ) -set(REFLEX_PKGCONFIG_LIBRARY reflexmin) -configure_file(${PROJECT_SOURCE_DIR}/cmake/reflex.pc.in +configure_file(${PROJECT_SOURCE_DIR}/reflexmin.pc.in "${CMAKE_CURRENT_BINARY_DIR}/reflexmin.pc" @ONLY ) diff --git a/cmake/reflex.pc.in b/cmake/reflex.pc.in deleted file mode 100644 index 0ef7c448..00000000 --- a/cmake/reflex.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: RE/flex -Description: high-performance C++ regex library and lexical analyzer generator -Version: @PROJECT_VERSION@ -Requires: -Libs: -L${libdir} -l@REFLEX_PKGCONFIG_LIBRARY@ -Cflags: -I${includedir} From 5f6008ad52fba0a6b246997766c488aed61246e3 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:43:35 -0400 Subject: [PATCH 09/13] Make CMake pkg-config files relocatable --- CMakeLists.txt | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e00d815f..c0347a70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,19 +198,25 @@ install(FILES DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) -# Preserve pkg-config's variable references in the generated metadata instead -# of expanding every path at CMake configuration time. -set(prefix "${CMAKE_INSTALL_PREFIX}") -set(exec_prefix "\${prefix}") - -# GNUInstallDirs permits absolute or prefix-relative directories; normalize -# both forms into the paths expected by pkg-config files. +# Make relative installations relocatable by deriving their prefix from the +# installed .pc file. Absolute library destinations retain their fixed paths. if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(prefix "${CMAKE_INSTALL_PREFIX}") + set(exec_prefix "\${prefix}") set(libdir "${CMAKE_INSTALL_LIBDIR}") else() + # Anchor both inputs in the native build directory because RELATIVE_PATH + # requires full paths; only their relative structure enters the .pc file. + file(RELATIVE_PATH reflex_pc_prefix_relative + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/pkgconfig" + "${CMAKE_CURRENT_BINARY_DIR}" + ) + set(prefix "\${pcfiledir}/${reflex_pc_prefix_relative}") + set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") endif() +# GNUInstallDirs also permits the header destination to be absolute. if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") else() From 0352dfed69889a4027e25c1824203285e25f417c Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 21:44:41 -0400 Subject: [PATCH 10/13] Install CMake package version metadata --- CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0347a70..e81dd669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,8 +193,17 @@ configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) +# Do not promise compatibility between releases until the project defines an +# explicit C++ API and ABI compatibility policy. +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY ExactVersion +) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/ReflexConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflex ) From cd1eea7ff6ab0bcf45102a23215c2abde5aaff64 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Mon, 3 Aug 2026 08:29:12 -0400 Subject: [PATCH 11/13] Simplify CMake SIMD source compilation --- CMakeLists.txt | 53 ++++++++++++------------------------ cmake/SIMDTestAndSetup.cmake | 6 ++-- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e81dd669..a183920a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,20 @@ set(libreflex_common_sources lib/utf8.cpp ) +# Use AVX2 flags for AVX2 sources. +if(REFLEX_COMPILER_HAS_AVX2) + set(reflex_avx2_sources lib/matcher_avx2.cpp lib/simd_avx2.cpp) + list(APPEND libreflex_common_sources ${reflex_avx2_sources}) + set_source_files_properties(${reflex_avx2_sources} PROPERTIES COMPILE_OPTIONS "${simd_avx2_flags}") +endif() + +# Use AVX512BW flags for AVX512BW wources. +if(REFLEX_COMPILER_HAS_AVX512BW) + set(reflex_avx512bw_sources lib/matcher_avx512bw.cpp lib/simd_avx512bw.cpp) + list(APPEND libreflex_common_sources ${reflex_avx512bw_sources}) + set_source_files_properties(${reflex_avx512bw_sources} PROPERTIES COMPILE_OPTIONS "${simd_avx512bw_flags}") +endif() + # The full library adds pattern conversion, POSIX support, and Unicode tables. set(libreflex_sources ${libreflex_common_sources} @@ -53,40 +67,7 @@ set(libreflexmin_sources ${libreflex_common_sources} ) -# Collect optional optimized objects for reuse by every library variant. -set(reflex_simd_objects "") - -# These noninstalled PIC objects keep AVX2 instructions out of the baseline -# sources while allowing all static and shared libraries to reuse one build. -if(REFLEX_COMPILER_HAS_AVX2) - add_library(ReflexAvx2Objects OBJECT - lib/matcher_avx2.cpp - lib/simd_avx2.cpp - ) - set_target_properties(ReflexAvx2Objects PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_include_directories(ReflexAvx2Objects PRIVATE ${PROJECT_SOURCE_DIR}/include) - target_compile_definitions(ReflexAvx2Objects PRIVATE ${simd_definitions}) - target_compile_options(ReflexAvx2Objects PRIVATE ${simd_avx2_flags}) - list(APPEND reflex_simd_objects $) -endif() - -# AVX512BW needs a distinct compiler option and uses the AVX2 objects above as -# its runtime fallback, so it is a separate private object target as well. -if(REFLEX_COMPILER_HAS_AVX512BW) - add_library(ReflexAvx512bwObjects OBJECT - lib/matcher_avx512bw.cpp - lib/simd_avx512bw.cpp - ) - set_target_properties(ReflexAvx512bwObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_include_directories(ReflexAvx512bwObjects PRIVATE ${PROJECT_SOURCE_DIR}/include) - target_compile_definitions(ReflexAvx512bwObjects PRIVATE ${simd_definitions}) - target_compile_options(ReflexAvx512bwObjects PRIVATE ${simd_avx512bw_flags}) - list(APPEND reflex_simd_objects $) -endif() - -# Define the available static and shared library targets for this toolchain. -# The public headers do not declare symbols with dllexport/dllimport, so an -# MSVC-style toolchain can only produce usable static libraries at present. +# Only build static libs for MSVC because the public headers do not declare symbols with dllexport/dllimport. if(REFLEX_MSVC_TOOLCHAIN) add_library(ReflexLib STATIC) add_library(ReflexMinLib STATIC) @@ -103,12 +84,12 @@ endif() # Set the source files contained in each full-library target. foreach(target IN LISTS reflex_library_targets) - target_sources(${target} PRIVATE ${libreflex_sources} ${reflex_simd_objects}) + target_sources(${target} PRIVATE ${libreflex_sources}) endforeach() # Set the smaller source set contained in each minimal-library target. foreach(target IN LISTS reflexmin_library_targets) - target_sources(${target} PRIVATE ${libreflexmin_sources} ${reflex_simd_objects}) + target_sources(${target} PRIVATE ${libreflexmin_sources}) endforeach() # Gather all library targets so their common build settings remain identical. diff --git a/cmake/SIMDTestAndSetup.cmake b/cmake/SIMDTestAndSetup.cmake index 7eff3aac..9d7e4f9c 100644 --- a/cmake/SIMDTestAndSetup.cmake +++ b/cmake/SIMDTestAndSetup.cmake @@ -1,5 +1,5 @@ # Test whether the compiler accepts each SIMD implementation with its required -# options. The resulting variables configure baseline and specialized targets. +# options. The resulting variables configure baseline and optimized sources. # These modules provide compile probes and isolate their temporary flags from # the rest of the project configuration. @@ -113,8 +113,8 @@ set(simd_flags "") set(simd_avx2_flags "") set(simd_avx512bw_flags "") -# Define only the strongest backend macro. Baseline targets stay at SSE2 while -# private AVX object targets receive the flags for their specialized code. +# Define only the strongest backend macro. Baseline sources stay at SSE2 while +# optimized AVX sources receive the flags required by their specialized code. if(REFLEX_COMPILER_HAS_AVX512BW) list(APPEND simd_definitions HAVE_AVX512BW) list(APPEND simd_flags "${reflex_sse2_flag}") From eaf68c39ed3d6d05362564fd2042e5fd1772ffde Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sat, 18 Jul 2026 16:41:18 -0400 Subject: [PATCH 12/13] Test on multiple architectures --- .github/workflows/c-cpp.yml | 123 ++++++++++++++++++++++++++++++++---- Makefile.am | 4 +- Makefile.in | 4 +- 3 files changed, 116 insertions(+), 15 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 701d2c86..b45db7ae 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -2,22 +2,123 @@ name: C/C++ CI on: push: - branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: Linux GCC + os: ubuntu-latest + arch: x86_64 + target: native + configure_args: "" + - name: Linux GCC (no SSE2/AVX2) + os: ubuntu-latest + arch: x86_64 + target: native + configure_args: --disable-sse2 --disable-avx2 + - name: Linux ARM GCC + os: ubuntu-24.04-arm + arch: aarch64 + target: native + configure_args: "" + - name: Linux ARM GCC (no NEON) + os: ubuntu-24.04-arm + arch: aarch64 + target: native + configure_args: --disable-neon + - name: Windows MinGW + os: windows-latest + arch: x86_64 + target: native + configure_args: "" + - name: Windows cross (MinGW) + os: ubuntu-latest + arch: x86_64 + target: windows-cross + configure_args: "" + - name: macOS ARM Clang + os: macos-latest + arch: arm64 + target: native + configure_args: "" steps: - - uses: actions/checkout@v3 - - name: Bootstrap autotools - run: autoreconf -i - - name: configure - run: ./configure - - name: make - run: make - - name: make test - run: make test + - name: Configure Windows line endings + if: runner.os == 'Windows' + shell: pwsh + run: git config --global core.autocrlf input + + - uses: actions/checkout@v6 + + - name: Set up MSYS2 + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + autoconf + automake + bison + make + mingw-w64-ucrt-x86_64-gcc + + - name: Install macOS build tools + if: runner.os == 'macOS' + env: + HOMEBREW_NO_AUTO_UPDATE: 1 + run: brew install autoconf automake + + - name: Install Windows cross tools + if: matrix.target == 'windows-cross' + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends --yes mingw-w64 wine + + - name: Build and test + if: runner.os != 'Windows' + shell: bash + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + autoreconf -i + if test "${{ matrix.target }}" = "windows-cross"; then + CC=x86_64-w64-mingw32-gcc \ + CXX=x86_64-w64-mingw32-g++ \ + AR=x86_64-w64-mingw32-ar \ + RANLIB=x86_64-w64-mingw32-ranlib \ + LDFLAGS="-static -static-libgcc -static-libstdc++" \ + ./configure --host=x86_64-w64-mingw32 ${{ matrix.configure_args }} + else + ./configure ${{ matrix.configure_args }} + fi + make -j2 + make -C tests + if test "${{ matrix.target }}" = "windows-cross"; then + WINEARCH=win64 WINEDEBUG=-all WINEPREFIX="$RUNNER_TEMP/wine" \ + wine ./tests/rtest.exe + else + ./tests/rtest + fi + + - name: Build and test + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + autoreconf -i + ./configure ${{ matrix.configure_args }} + make -j2 + make -C tests + ./tests/rtest diff --git a/Makefile.am b/Makefile.am index 87c260d8..6aeb062c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,7 @@ all-local: cp2bin .PHONY: cp2bin # to copy the reflex binary to reflex/bin -cp2bin: $(top_builddir)/src/reflex +cp2bin: $(top_builddir)/src/reflex$(EXEEXT) -mkdir -p $(top_builddir)/bin -cp -f $< $(top_builddir)/bin @echo @@ -50,5 +50,5 @@ install-data-hook: .PHONY: test -test: $(top_builddir)/src/reflex +test: $(top_builddir)/src/reflex$(EXEEXT) -cd tests; $(MAKE) && ./rtest diff --git a/Makefile.in b/Makefile.in index ef0c6cd0..32e7236a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -931,7 +931,7 @@ all-local: cp2bin .PHONY: cp2bin # to copy the reflex binary to reflex/bin -cp2bin: $(top_builddir)/src/reflex +cp2bin: $(top_builddir)/src/reflex$(EXEEXT) -mkdir -p $(top_builddir)/bin -cp -f $< $(top_builddir)/bin @echo @@ -965,7 +965,7 @@ install-data-hook: .PHONY: test -test: $(top_builddir)/src/reflex +test: $(top_builddir)/src/reflex$(EXEEXT) -cd tests; $(MAKE) && ./rtest # Tell versions [3.59,3.63) of GNU make to not export all variables. From c1afbbbfcb314e3eab87742ed37ab5856d1823d0 Mon Sep 17 00:00:00 2001 From: Benjamin Redelings Date: Sun, 2 Aug 2026 20:46:20 -0400 Subject: [PATCH 13/13] Test CMake builds across supported platforms --- .github/workflows/c-cpp-cmake.yml | 124 ++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 .github/workflows/c-cpp-cmake.yml diff --git a/.github/workflows/c-cpp-cmake.yml b/.github/workflows/c-cpp-cmake.yml new file mode 100644 index 00000000..9d23cc2a --- /dev/null +++ b/.github/workflows/c-cpp-cmake.yml @@ -0,0 +1,124 @@ +name: C/C++ CMake CI + +on: + push: + pull_request: + branches: [ "master" ] + +jobs: + build: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + include: + - name: Linux GCC + os: ubuntu-latest + arch: x86_64 + target: native + cmake_args: "" + - name: Linux GCC (no SSE2/AVX2) + os: ubuntu-latest + arch: x86_64 + target: native + cmake_args: "-DUSE_SSE2=OFF -DUSE_AVX2=OFF" + - name: Linux ARM GCC + os: ubuntu-24.04-arm + arch: aarch64 + target: native + cmake_args: "" + - name: Linux ARM GCC (no NEON) + os: ubuntu-24.04-arm + arch: aarch64 + target: native + cmake_args: "-DUSE_NEON=OFF" + - name: Windows MinGW + os: windows-latest + arch: x86_64 + target: native + cmake_args: "" + - name: Windows cross (MinGW) + os: ubuntu-latest + arch: x86_64 + target: windows-cross + cmake_args: "" + - name: macOS ARM Clang + os: macos-latest + arch: arm64 + target: native + cmake_args: "" + + steps: + - name: Configure Windows line endings + if: runner.os == 'Windows' + shell: pwsh + run: git config --global core.autocrlf input + + - uses: actions/checkout@v6 + + - name: Set up MSYS2 + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-gcc + mingw-w64-ucrt-x86_64-ninja + + - name: Install Windows cross tools + if: matrix.target == 'windows-cross' + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends --yes mingw-w64 wine + + - name: Build and test + if: runner.os != 'Windows' + shell: bash + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + cmake --version + if test "${{ matrix.target }}" = "windows-cross"; then + cmake -S . -B build \ + -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + -DCMAKE_CROSSCOMPILING_EMULATOR=wine \ + -DCMAKE_EXE_LINKER_FLAGS="-static -static-libgcc -static-libstdc++" \ + ${{ matrix.cmake_args }} + else + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + ${{ matrix.cmake_args }} + fi + cmake --build build --parallel 2 + if test "${{ matrix.target }}" = "windows-cross"; then + WINEARCH=win64 WINEDEBUG=-all WINEPREFIX="$RUNNER_TEMP/wine" \ + ctest --test-dir build --output-on-failure + else + ctest --test-dir build --output-on-failure + fi + cmake --install build --prefix "$RUNNER_TEMP/reflex-install" + + - name: Build and test + if: runner.os == 'Windows' + shell: msys2 {0} + run: | + test "$(uname -m)" = "${{ matrix.arch }}" + uname -a + c++ --version + cmake --version + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_TESTING=ON \ + ${{ matrix.cmake_args }} + cmake --build build --parallel 2 + ctest --test-dir build --output-on-failure + cmake --install build --prefix "$RUNNER_TEMP/reflex-install"