cmake_minimum_required(VERSION 3.14) project(glazejson CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(ENV{MAKEFLAGS} "--no-print-directory") # ---- Detect availability ---- # Some toolchains (e.g. GCC 12's libstdc++, or Clang against an older system # libstdc++) don't ship a working header, which Glaze otherwise # includes by default (see glaze/core/write_chars.hpp). Probe for it here and # tell Glaze to fall back to -based float formatting when missing. include(CheckIncludeFileCXX) set(CMAKE_REQUIRED_FLAGS "-std=c++23") check_include_file_cxx("format" GLZ_HAVE_STD_FORMAT) unset(CMAKE_REQUIRED_FLAGS) # ---- Fetch glaze ---- include(FetchContent) FetchContent_Declare( glaze GIT_REPOSITORY https://github.com/stephenberry/glaze.git GIT_TAG v7.7.1 GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(glaze) # ---- Erlang include paths ---- execute_process( COMMAND erl -noshell -noinput -eval "io:format(\"~ts/erts-~ts/include\", [code:root_dir(), erlang:system_info(version)]), halt(0)." OUTPUT_VARIABLE ERL_ERTS_INCLUDE OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND erl -noshell -noinput -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)]), halt(0)." OUTPUT_VARIABLE ERL_EI_INCLUDE OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process( COMMAND erl -noshell -noinput -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)]), halt(0)." OUTPUT_VARIABLE ERL_EI_LIB OUTPUT_STRIP_TRAILING_WHITESPACE ) # ---- NIF shared library ---- add_library(glazejson SHARED glaze_nif.cpp) target_include_directories(glazejson PRIVATE ${ERL_ERTS_INCLUDE} ${ERL_EI_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_directories(glazejson PRIVATE ${ERL_EI_LIB}) target_link_libraries(glazejson PRIVATE glaze::glaze ei) # NIF symbols (enif_*) are resolved at load time by the BEAM, not at link # time. On Linux this is fine by default (lazy symbol binding), but macOS's # linker rejects undefined symbols in shared libraries unless told otherwise. if(APPLE) target_link_options(glazejson PRIVATE -undefined dynamic_lookup) endif() if(NOT GLZ_HAVE_STD_FORMAT) target_compile_definitions(glazejson PRIVATE GLZ_USE_STD_FORMAT_FLOAT=0) endif() include(CheckCXXCompilerFlag) set(CMAKE_REQUIRED_FLAGS "-std=c++23") check_cxx_compiler_flag("-march=native" GLZ_HAVE_MARCH_NATIVE) check_cxx_compiler_flag("-mtune=native" GLZ_HAVE_MTUNE_NATIVE) unset(CMAKE_REQUIRED_FLAGS) set(GLZ_NATIVE_FLAGS "") if(GLZ_HAVE_MARCH_NATIVE) list(APPEND GLZ_NATIVE_FLAGS -march=native) endif() if(GLZ_HAVE_MTUNE_NATIVE) list(APPEND GLZ_NATIVE_FLAGS -mtune=native) endif() # GCC's "-flto=auto" produces objects with GNU LTO sections that GNU ld/gold # can link directly. Upstream Clang on Linux instead emits LLVM bitcode, which # requires lld (or another LLVM-aware linker) to consume — plain GNU ld fails # with "file format not recognized". Use Clang's own thin-LTO + lld pairing # there. AppleClang uses Apple's ld64 (which understands its own LTO objects # via libLTO), and doesn't ship/accept "-fuse-ld=lld", so leave it on its # default LTO handling. if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(GLZ_LTO_FLAGS -flto=thin) target_link_options(glazejson PRIVATE -fuse-ld=lld) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(GLZ_LTO_FLAGS -flto=thin) else() set(GLZ_LTO_FLAGS -flto=auto -fno-fat-lto-objects) endif() target_compile_options(glazejson PRIVATE -fPIC -Wall -finline-functions $<$:-O3 -DNDEBUG ${GLZ_LTO_FLAGS} ${GLZ_NATIVE_FLAGS}> $<$:-O0 -g> ) # Remove the "lib" prefix so Erlang can load it as "glazejson.so" # Erlang loads NIF libraries with a ".so" extension on every platform # (including macOS, where CMake's shared-library default is ".dylib"). set_target_properties(glazejson PROPERTIES PREFIX "" SUFFIX ".so" OUTPUT_NAME "glazejson" ) # ---- Output to priv/ ---- if(DEFINED PRIV_DIR) set_target_properties(glazejson PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PRIV_DIR}" ) else() set_target_properties(glazejson PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../priv" ) endif()