# # The relative source and module paths seem to work inconsistently on Windows # and Nix (flakes). This file used to live around the build/in and # build/in/cmake directories. For now, it seems easiest to leave it here, as # one megafile, in the project's root directory. # cmake_policy(SET CMP0048 NEW) # For VERSION in project() cmake_policy(SET CMP0069 NEW) # For better IPO support cmake_minimum_required(VERSION 3.9) project( wtr.watcher VERSION 0.13.2 # hook: tool/release DESCRIPTION "watcher: a filesystem watcher" HOMEPAGE_URL "github.com/e-dant/watcher" LANGUAGES CXX C ) # # Options, Variable & Constants # option(BUILD_LIB "Create targets for the watcher-c libraries" ON) option(BUILD_BIN "Create targets for the CLI binaries" ON) option(BUILD_HDR "Create targets for the headers (both the C++ single-header library and the watcher-c library header)" ON) option(BUILD_TESTING "Create targets for the test programs" OFF) option(BUILD_SAN "Mega-option to allow sanitizers" OFF) option(BUILD_ASAN "Create targets address-sanitized libraries and binaries" OFF) option(BUILD_MSAN "Create targets memory-sanitized libraries and binaries" OFF) option(BUILD_TSAN "Create targets thread-sanitized libraries and binaries" OFF) option(BUILD_UBSAN "Create targets undefined-behavior-sanitized libraries and binaries" OFF) set(WTR_WATCHER_CXX_STD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(IS_CC_CLANG 0) set(IS_CC_ANYCLANG 0) set(IS_CC_APPLECLANG 0) set(IS_CC_GCC 0) set(IS_CC_MSVC 0) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") set(IS_CC_MSVC 1) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(IS_CC_GCC 1) elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(IS_CC_ANYCLANG 1) if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set(IS_CC_APPLECLANG 1) else() set(IS_CC_CLANG 1) endif() endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() if(NOT IS_CC_MSVC) # It's not that we don't want these, it's just that I hate Windows. # Also, MSVC doesn't support some of these arguments, so it's not possible. set(COMPILE_OPTIONS "${COMPILE_OPTIONS}" "-Wall" "-Wextra" "-Werror" "-Wno-unused-function" "-Wno-unneeded-internal-declaration" ) endif() if(NOT WIN32 AND NOT IS_CC_MSVC) # TODO: strict-aliasing set(COMPILE_OPTIONS "${COMPILE_OPTIONS}" "-Wno-ignored-optimization-argument" # For Android's clang "-Wno-unused-command-line-argument" # For clang-11 "-fno-exceptions" "-fno-rtti" "-fstrict-enums" "-fstrict-overflow" ) if(IS_CC_CLANG) set(COMPILE_OPTIONS "${COMPILE_OPTIONS}" "-fstrict-return" "-fstrict-float-cast-overflow" ) endif() if(NOT IS_CC_APPLECLANG) set(COMPILE_OPTIONS "${COMPILE_OPTIONS}" "-fexpensive-optimizations" ) endif() endif() if(ANDROID) # Android's stdlib ("bionic") doesn't need to be linked with (p)threads. set(LINK_LIBRARIES "${LINK_LIBRARIES}") else() find_package(Threads REQUIRED) set(LINK_LIBRARIES "${LINK_LIBRARIES}" "Threads::Threads" ) if(APPLE) list(APPEND LINK_LIBRARIES "-framework CoreFoundation" "-framework CoreServices" ) endif() endif() set(INCLUDE_PATH_DEVEL "devel/include") set(ALLOWED_asan 0) set(ALLOWED_msan 0) set(ALLOWED_tsan 0) set(ALLOWED_ubsan 0) set(ALLOWED_stacksan 0) set(ALLOWED_dataflowsan 0) set(ALLOWED_cfisan 0) set(ALLOWED_kcfisan 0) if(NOT WIN32 AND (BUILD_ASAN OR BUILD_SAN)) set(ALLOWED_asan 1) endif() if(IS_CC_CLANG AND NOT ANDROID AND (BUILD_MSAN OR BUILD_SAN)) set(ALLOWED_msan 1) endif() if(NOT ANDROID AND NOT WIN32 AND (BUILD_TSAN OR BUILD_SAN)) set(ALLOWED_tsan 1) endif() if(NOT WIN32 AND (BUILD_UBSAN OR BUILD_SAN)) set(ALLOWED_ubsan 1) endif() set(SAN_NAMES "asan" "msan" "tsan" "ubsan") set(CCLL_EXTOPT_SET_ASAN "-fno-omit-frame-pointer" "-fsanitize=address") set(CCLL_EXTOPT_SET_MSAN "-fno-omit-frame-pointer" "-fsanitize=memory") set(CCLL_EXTOPT_SET_TSAN "-fno-omit-frame-pointer" "-fsanitize=thread") set(CCLL_EXTOPT_SET_UBSAN "-fno-omit-frame-pointer" "-fsanitize=undefined") set(CCLL_EXTOPT_SET_STACKSAN "-fno-omit-frame-pointer" "-fsanitize=safe-stack") set(CCLL_EXTOPT_SET_DATAFLOWSAN "-fno-omit-frame-pointer" "-fsanitize=dataflow") set(CCLL_EXTOPT_SET_CFISAN "-fno-omit-frame-pointer" "-fsanitize=cfi") set(CCLL_EXTOPT_SET_KCFISAN "-fno-omit-frame-pointer" "-fsanitize=kcfi") set(SAN_SUPPORTED) foreach(SAN ${SAN_NAMES}) if(ALLOWED_${SAN}) list(APPEND SAN_SUPPORTED ${SAN}) endif() endforeach() message(STATUS "Supported sanitizers on ${CMAKE_SYSTEM}/${CMAKE_CXX_COMPILER_ID}: ${SAN_SUPPORTED}") # # Functions # function(wtr_add_bin_target NAME BIN_COMPONENT_NAME IS_TEST SRC_SET CC_OPT_SET LL_OPT_SET INCLUDE_PATH LLIB_SET) if(NOT IS_TEST AND NOT BUILD_BIN) message(STATUS "${NAME}: Skipped (BUILD_BIN=${BUILD_BIN})") return() elseif(NOT IS_TEST) message(STATUS "${NAME}: Added (BUILD_BIN=${BUILD_BIN})") endif() if(IS_TEST AND NOT BUILD_TESTING) message(STATUS "${NAME}: Skipped (BUILD_TESTING=${BUILD_TESTING})") return() elseif(IS_TEST) message(STATUS "${NAME}: Added (BUILD_TESTING=${BUILD_TESTING})") endif() include(CheckIPOSupported) include(FetchContent) include(GNUInstallDirs) add_executable("${NAME}" "${SRC_SET}") set_property(TARGET "${NAME}" PROPERTY CXX_STANDARD "${WTR_WATCHER_CXX_STD}") if(NOT WIN32 AND NOT IS_CC_MSVC AND NOT IS_CC_APPLECLANG) target_compile_options("${NAME}" PRIVATE "${CC_OPT_SET};-fwhole-program") else() target_compile_options("${NAME}" PRIVATE "${CC_OPT_SET}") endif() target_link_options("${NAME}" PRIVATE "${LL_OPT_SET}") target_include_directories("${NAME}" PUBLIC "${INCLUDE_PATH}") target_link_libraries("${NAME}" PRIVATE "${LLIB_SET}") check_ipo_supported(RESULT ipo_supported) if(ipo_supported) set_property(TARGET "${NAME}" PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) endif() if(APPLE) set_property( TARGET "${NAME}" PROPERTY XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "org.${NAME}" ) endif() if(IS_TEST) if(DEFINED ENV{WTR_WATCHER_USE_SYSTEM_SNITCH}) find_package(snitch REQUIRED) else() FetchContent_Declare( snitch GIT_REPOSITORY https://github.com/cschreib/snitch.git # v1.1.1 Doesn't show time as nice as v1.0.0 # Tuesday, June 29th, 2023 @ v1.1.1 GIT_TAG 5ad2fffebf31f3e6d56c2c0ab27bc45d01da2f05 # Friday, January 20th, 2023 @ v1.0.0 # GIT_TAG ea200a0830394f8e0ef732064f0935a77c003bd6 # Saturday, January 7th, 2023 @ main # GIT_TAG 8165d6c85353f9c302ce05f1c1c47dcfdc6aeb2c # Tuesday, December 18th, 2022 @ v0.1.3 # GIT_TAG f313bccafe98aaef617af3bf457d091d8d50cdcd # Friday, December 2nd, 2022 @ main # GIT_TAG c0b6ac4efe4019e4846e8967fe21de864b0cc1ed ) FetchContent_MakeAvailable(snitch) endif() endif() if(BIN_COMPONENT_NAME) install( TARGETS "${NAME}" DESTINATION "${CMAKE_INSTALL_PREFIX}/bin" COMPONENT "${BIN_COMPONENT_NAME}" ) endif() endfunction() function(wtr_add_lib_target NAME OUTPUT_NAME SRC_SET INC_SET LIB_TYPE) if(NOT BUILD_LIB) message(STATUS "${NAME}: Skipped (BUILD_LIB=${BUILD_LIB})") return() endif() message(STATUS "${NAME}: Added (BUILD_LIB=${BUILD_LIB})") include(GNUInstallDirs) add_library("${NAME}" "${LIB_TYPE}" "${SRC_SET}") target_include_directories("${NAME}" PRIVATE "${INC_SET}") set_property(TARGET "${NAME}" PROPERTY CXX_STANDARD "${WTR_WATCHER_CXX_STD}") set_property(TARGET "${NAME}" PROPERTY VERSION "${PROJECT_VERSION}") set_property(TARGET "${NAME}" PROPERTY SOVERSION "${PROJECT_VERSION_MAJOR}") set_property(TARGET "${NAME}" PROPERTY POSITION_INDEPENDENT_CODE ON) set_property(TARGET "${NAME}" PROPERTY OUTPUT_NAME "${OUTPUT_NAME}") target_compile_options("${NAME}" PRIVATE "${COMPILE_OPTIONS}") if("${NAME}" STREQUAL "watcher-c-shared" AND LINUX) message(STATUS "watcher-c-shared: Adding version script") target_link_options("${NAME}" PRIVATE "${LINK_OPTIONS};-Wl,--version-script=${CMAKE_SOURCE_DIR}/watcher-c/libwatcher-c.version") else() target_link_options("${NAME}" PRIVATE "${LINK_OPTIONS}") endif() target_link_libraries("${NAME}" PRIVATE "${LINK_LIBRARIES}") if(APPLE) set_property(TARGET "${NAME}" PROPERTY INSTALL_RPATH "/usr/local/lib") set_property(TARGET "${NAME}" PROPERTY BUILD_RPATH "/usr/local/lib") endif() install( TARGETS "${NAME}" DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT "lib" ) endfunction() function(wtr_add_hdr_target NAME HDR_SET) if(NOT BUILD_HDR) message(STATUS "${NAME}: Skipped (BUILD_HDR=${BUILD_HDR})") return() endif() message(STATUS "${NAME}: Added (BUILD_HDR=${BUILD_HDR})") include(GNUInstallDirs) add_library("${NAME}" INTERFACE "${HDR_SET}") target_include_directories("${NAME}" INTERFACE "${INCLPATH}") install(FILES "${HDR_SET}" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/wtr" COMPONENT "include") endfunction() function(wtr_add_rel_bin_target NAME SRC_SET) wtr_add_bin_target( "${NAME}" "bin" "OFF" # is test "${SRC_SET}" "${COMPILE_OPTIONS}" "${LINK_OPTIONS}" "include" "${LINK_LIBRARIES}" ) endfunction() function(wtr_add_test_bin_target NAME SRC_SET) wtr_add_bin_target( "${NAME}" "test-bin" "ON" # is test "${SRC_SET}" "${COMPILE_OPTIONS}" "${LINK_OPTIONS}" "${INCLUDE_PATH_DEVEL}" "${LINK_LIBRARIES};snitch::snitch" ) endfunction() function(wtr_add_san_rel_bin_target NAME SRC_SET SAN) wtr_add_bin_target( "${NAME}.${SAN}" "test-bin" "OFF" # is test "${SRC_SET}" "${COMPILE_OPTIONS};${CCLL_EXTOPT_SET_${SAN}}" "${LINK_OPTIONS};${CCLL_EXTOPT_SET_${SAN}}" "${INCLUDE_PATH_DEVEL}" "${LINK_LIBRARIES}" ) endfunction() function(wtr_add_san_test_bin_target NAME SRC_SET SAN) wtr_add_bin_target( "${NAME}.${SAN}" "test-bin" "ON" # is test "${SRC_SET}" "${COMPILE_OPTIONS};${CCLL_EXTOPT_SET_${SAN}}" "${LINK_OPTIONS};${CCLL_EXTOPT_SET_${SAN}}" "${INCLUDE_PATH_DEVEL}" "${LINK_LIBRARIES};snitch::snitch" ) endfunction() function(wtr_add_autosan_bin_target NAME SRC_SET IS_TEST) if(IS_TEST) wtr_add_test_bin_target("${NAME}" "${SRC_SET}") else() wtr_add_rel_bin_target("${NAME}" "${SRC_SET}") endif() foreach(SAN ${SAN_NAMES}) string(TOUPPER ${SAN} UPPER_SAN) if(NOT BUILD_SAN AND NOT BUILD_${UPPER_SAN}) message(STATUS "${NAME}.${SAN}: Skipped (BUILD_SAN=${BUILD_SAN}, BUILD_${UPPER_SAN}=${BUILD_${UPPER_SAN}})") elseif(NOT ALLOWED_${SAN}) message(STATUS "${NAME}.${SAN}: Skipped (Unsupported on ${CMAKE_SYSTEM}/${CMAKE_CXX_COMPILER_ID})") elseif(IS_TEST) wtr_add_san_test_bin_target("${NAME}" "${SRC_SET}" "${SAN}") else() wtr_add_san_rel_bin_target("${NAME}" "${SRC_SET}" "${SAN}") endif() endforeach() endfunction() function(wtr_add_san_lib_target NAME OUTPUT_NAME SRC_SET INC_SET LIB_TYPE SAN) string(TOUPPER "${SAN}" UPPER_SAN) if (NOT BUILD_LIB) message(STATUS "${NAME}: Skipped (BUILD_LIB=${BUILD_LIB})") return() elseif (NOT BUILD_SAN AND NOT BUILD_${UPPER_SAN}) message(STATUS "${NAME}: Skipped (BUILD_SAN=${BUILD_SAN}, BUILD_${UPPER_SAN}=${BUILD_${UPPER_SAN}})") return() elseif (NOT ALLOWED_${SAN}) message(STATUS "${NAME}: Skipped (Unsupported on ${CMAKE_SYSTEM}/${CMAKE_CXX_COMPILER_ID})") return() endif() message(STATUS "${NAME}: Added (BUILD_SAN=${BUILD_SAN}, BUILD_${UPPER_SAN}=${BUILD_${UPPER_SAN}}, BUILD_LIB=${BUILD_LIB})") wtr_add_lib_target("${NAME}" "${OUTPUT_NAME}" "${SRC_SET}" "${INC_SET}" "${LIB_TYPE}") target_compile_options("${NAME}" PRIVATE "${COMPILE_OPTIONS};${CCLL_EXTOPT_SET_${UPPER_SAN}}") target_link_options("${NAME}" PRIVATE "${LINK_OPTIONS};${CCLL_EXTOPT_SET_${UPPER_SAN}}") endfunction() function(wtr_add_autosan_lib_target NAME OUTPUT_NAME SRC_SET INC_SET LIB_TYPE) wtr_add_lib_target("${NAME}" "${OUTPUT_NAME}" "${SRC_SET}" "${INC_SET}" "${LIB_TYPE}") foreach(SAN ${SAN_NAMES}) string(TOUPPER ${SAN} UPPER_SAN) wtr_add_san_lib_target("${NAME}.${SAN}" "${OUTPUT_NAME}.${SAN}" "${SRC_SET}" "${INC_SET}" "${LIB_TYPE}" "${SAN}") endforeach() endfunction() # # Actual work # wtr_add_hdr_target( "wtr.hdr_watcher" "include/wtr/watcher.hpp" ) wtr_add_hdr_target( "watcher-c-hdr" "watcher-c/include/wtr/watcher-c.h" ) wtr_add_autosan_lib_target( "watcher-c-shared" "watcher-c" "watcher-c/src/watcher-c.cpp" "watcher-c/include;${CMAKE_SOURCE_DIR}/include" "SHARED" ) wtr_add_autosan_lib_target( "watcher-c-static" "watcher-c" "watcher-c/src/watcher-c.cpp" "watcher-c/include;${CMAKE_SOURCE_DIR}/include" "STATIC" ) wtr_add_autosan_bin_target( "wtr.watcher" "src/wtr/watcher/main.cpp" OFF ) wtr_add_autosan_bin_target( "tw" "src/wtr/tiny_watcher/main.cpp" OFF ) set(WTR_TEST_WATCHER_SOURCE_SET "devel/src/wtr/test_watcher/test_concurrency.cpp" "devel/src/wtr/test_watcher/test_event_targets.cpp" "devel/src/wtr/test_watcher/test_new_directories.cpp" "devel/src/wtr/test_watcher/test_simple.cpp" "devel/src/wtr/test_watcher/test_performance.cpp" "devel/src/wtr/test_watcher/test_openclose.cpp" ) wtr_add_autosan_bin_target( "wtr.test_watcher" "${WTR_TEST_WATCHER_SOURCE_SET}" ON ) # Used in the tool/test suite. Platforms vary # in their mv(1) implementations. We smooth # over that by using the `rename` system call, # which doesn't vary much at all. wtr_add_bin_target( "portable-destructive-rename" "test-bin" ON "devel/src/portable-destructive-rename/main.c" "" "" "" "" )