diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec1eb7be..f284e6903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,14 @@ cmake_minimum_required(VERSION 3.14) -include(cmake/util/PreventInSourceBuilds.cmake) +include(cmake/PreventInSourceBuilds.cmake) + +# ---- Initialize Project ---- + +# used to support find_package +set(package_name "patomic") + +# create base project project( patomic VERSION 0.5.1 @@ -11,35 +18,43 @@ project( ) # don't change include order, OptionVariables checks if project is top level -include(cmake/util/ProjectIsTopLevel.cmake) -include(cmake/util/OptionVariables.cmake) +include(cmake/ProjectIsTopLevel.cmake) +include(cmake/OptionVariables.cmake) # ---- Declare Library ---- -add_library( - patomic_patomic - ${build_type} +# target that we can modify (can't modify ALIAS targets) +# target name should not be the same as ${PROJECT_NAME}, causes add_subdirectory issues +set(target_name "patomic-patomic") +add_library(${target_name} ${build_type}) + +# alias to cause error at configuration time instead of link time if target is missing +add_library(patomic::patomic ALIAS ${target_name}) + +# add /include files to target +# unfortunately can't have CML file in /include +target_sources( + ${target_name} PRIVATE # include include/patomic/patomic.h - # src - src/patomic.c ) -# alias to cause error at configuration time instead of link time if target is missing -add_library(patomic::patomic ALIAS patomic_patomic) +# add /src files to target +add_subdirectory(src) # ---- Generate Build Info Headers ---- # used in export header generated below if(NOT PATOMIC_BUILD_SHARED) - target_compile_definitions(patomic_patomic PUBLIC PATOMIC_STATIC_DEFINE) + target_compile_definitions(${target_name} PUBLIC PATOMIC_STATIC_DEFINE) endif() +# generate header file with export macros prefixed with BASE_NAME include(GenerateExportHeader) generate_export_header( - patomic_patomic + ${target_name} BASE_NAME patomic EXPORT_FILE_NAME include/patomic/patomic_export.h ) @@ -47,44 +62,55 @@ generate_export_header( # ---- Library Properties ---- +# hide all symbols by default +# use SameMajorVersion versioning for shared library lookup set_target_properties( - patomic_patomic PROPERTIES + ${target_name} PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN YES VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}" - EXPORT_NAME patomic - OUTPUT_NAME patomic + EXPORT_NAME "patomic" + OUTPUT_NAME "patomic" ) # header files generated by CMake target_include_directories( - patomic_patomic SYSTEM - PUBLIC + ${target_name} SYSTEM PUBLIC "$" ) # header files from /include target_include_directories( - patomic_patomic ${warning_guard} - PUBLIC + ${target_name} ${warning_guard} PUBLIC "$" ) -target_compile_features(patomic_patomic PUBLIC c_std_90) +# require C90 compiler support +target_compile_features(${target_name} PUBLIC c_std_90) # ---- Install Rules ---- if(NOT CMAKE_SKIP_INSTALL_RULES) - include(cmake/util/InstallRules.cmake) + include(cmake/InstallRules.cmake) endif() # ---- Setup Tests ---- if(PATOMIC_BUILD_TESTING) + # need to enable testing in case BUILD_TESTING is disabled - enable_testing() + if(PROJECT_IS_TOP_LEVEL) + enable_testing() + endif() + + # tell unit tests where our files are + set(PATOMIC_BINARY_DIR "${PROJECT_BINARY_DIR}") + set(PATOMIC_SOURCE_DIR "${PROJECT_SOURCE_DIR}") + + # include test project add_subdirectory(test) + endif() diff --git a/cmake/InstallRules.cmake b/cmake/InstallRules.cmake new file mode 100644 index 000000000..b8c683c91 --- /dev/null +++ b/cmake/InstallRules.cmake @@ -0,0 +1,66 @@ +include(CMakePackageConfigHelpers) + +# copy header files to CMAKE_INSTALL_INCLUDEDIR +install( + DIRECTORY + "${PROJECT_SOURCE_DIR}/include/" # our header files + "${PROJECT_BINARY_DIR}/include/" # generated header files + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT ${package_name}-development +) + +# copy target build output artifacts to OS dependent locations +# (except includes, that just sets a compiler flag with the path) +install( + TARGETS ${target_name} + EXPORT ${package_name}-targets + RUNTIME # + COMPONENT ${package_name}-runtime + LIBRARY # + COMPONENT ${package_name}-runtime + NAMELINK_COMPONENT ${package_name}-development + ARCHIVE # + COMPONENT ${package_name}-development + INCLUDES # + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) + +# create config file that points to targets file +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/in/patomic-config.cmake.in" + "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" + @ONLY +) + +# copy config file for find_package to find +install( + FILES "${PROJECT_BINARY_DIR}/cmake/${package_name}-config.cmake" + DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# create version file for consumer to check version in CMake +write_basic_package_version_file( + "${package_name}-config-version.cmake" + COMPATIBILITY SameMajorVersion # a.k.a. SemVer +) + +# copy version file for find_package to find for version check +install( + FILES "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake" + DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# create targets file included by config file with targets for consumers +install( + EXPORT ${package_name}-targets + NAMESPACE patomic:: + DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" + COMPONENT ${package_name}-development +) + +# support packaging library +if(PROJECT_IS_TOP_LEVEL) + include(CPack) +endif() diff --git a/cmake/OptionVariables.cmake b/cmake/OptionVariables.cmake new file mode 100644 index 000000000..a7480567a --- /dev/null +++ b/cmake/OptionVariables.cmake @@ -0,0 +1,116 @@ +# included further down to avoid interfering with our cache variables +# include(GNUInstallDirs) + + +# ---- Options Summary ---- + +# ------------------------------------------------------------------------------------------------ +# | Option | Availability | Default | +# |==============================|===============|===============================================| +# | BUILD_SHARED_LIBS | Top-Level | OFF | +# | BUILD_TESTING | Top-Level | OFF | +# | CMAKE_INSTALL_INCLUDEDIR | Top-Level | include/${package_name}-${PROJECT_VERSION} | +# |------------------------------|---------------|-----------------------------------------------| +# | PATOMIC_BUILD_SHARED | Always | ${BUILD_SHARED_LIBS} | +# | PATOMIC_BUILD_TESTING | Always | ${BUILD_TESTING} | +# | PATOMIC_INCLUDES_WITH_SYSTEM | Not Top-Level | ON | +# | PATOMIC_INSTALL_CMAKEDIR | Always | ${CMAKE_INSTALL_LIBDIR}/cmake/${package_name} | +# ------------------------------------------------------------------------------------------------ + + +# ---- Build Shared ---- + +# Sometimes it's useful to be able to single out a dependency to be built as +# static or shared, even if obtained from source. +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_SHARED_LIBS "Build shared libs" OFF) +endif() +option( + PATOMIC_BUILD_SHARED + "Override BUILD_SHARED_LIBS for ${package_name} library" + ${BUILD_SHARED_LIBS} +) +mark_as_advanced(PATOMIC_BUILD_SHARED) +set(build_type STATIC) +if(PATOMIC_BUILD_SHARED) + set(build_type SHARED) +endif() + + +# ---- Warning Guard ---- + +# target_include_directories with SYSTEM modifier will request the compiler to +# omit warnings from the provided paths, if the compiler supports that. +# This is to provide a user experience similar to find_package when +# add_subdirectory or FetchContent is used to consume this project. +set(warning_guard "") +if(NOT PROJECT_IS_TOP_LEVEL) + option( + PATOMIC_INCLUDES_WITH_SYSTEM + "Use SYSTEM modifier for ${package_name}'s includes, disabling warnings" + ON + ) + mark_as_advanced(PATOMIC_INCLUDES_WITH_SYSTEM) + if(PATOMIC_INCLUDES_WITH_SYSTEM) + set(warning_guard SYSTEM) + endif() +endif() + + +# ---- Enable Testing ---- + +# By default tests aren't enabled even with BUILD_TESTING unless the library is +# built as a top level project. +# This is in order to cut down on unnecessary compile times, since it's unlikely +# for users to want to run the tests of their dependencies. +if(PROJECT_IS_TOP_LEVEL) + option(BUILD_TESTING "Build tests" OFF) +endif() +option( + PATOMIC_BUILD_TESTING + "Override BUILD_TESTING for ${package_name} library" + ${BUILD_TESTING} +) +mark_as_advanced(PATOMIC_BUILD_TESTING) + + +# ---- Install Include Directory ---- + +# Adds an extra directory to the include path by default, so that when you link +# against the target, you get `/include/-X.Y.Z` added to your +# include paths rather than `) call for consumers to find this project -set(package patomic) - -# copy header files to CMAKE_INSTALL_INCLUDEDIR -install( - DIRECTORY - "${PROJECT_SOURCE_DIR}/include/" - "${PROJECT_BINARY_DIR}/include/" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - COMPONENT patomic_Development -) - -# copy target build output artifacts to OS dependent locations -install( - TARGETS patomic_patomic - EXPORT patomicTargets - RUNTIME # - COMPONENT patomic_Runtime - LIBRARY # - COMPONENT patomic_Runtime - NAMELINK_COMPONENT patomic_Development - ARCHIVE # - COMPONENT patomic_Development - INCLUDES # - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" -) - -# copy config file for find_package to find -install( - FILES "${PROJECT_SOURCE_DIR}/cmake/util/InstallConfig.cmake" - DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" - RENAME "${package}Config.cmake" - COMPONENT patomic_Development -) - -# create version file for consumer to check version in CMake -write_basic_package_version_file( - "${package}ConfigVersion.cmake" - COMPATIBILITY SameMajorVersion -) - -# copy version file for find_package to find -install( - FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" - DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" - COMPONENT patomic_Development -) - -# create core configuration file detailing targets for consumer -install( - EXPORT patomicTargets - NAMESPACE patomic:: - DESTINATION "${PATOMIC_INSTALL_CMAKEDIR}" - COMPONENT patomic_Development -) - -# support packaging library -if(PROJECT_IS_TOP_LEVEL) - include(CPack) -endif() diff --git a/cmake/util/OptionVariables.cmake b/cmake/util/OptionVariables.cmake deleted file mode 100644 index 0b5528f08..000000000 --- a/cmake/util/OptionVariables.cmake +++ /dev/null @@ -1,78 +0,0 @@ -# ---- Build Shared ---- - -# Sometimes it's useful to be able to single out a dependency to be built as -# static or shared, even if obtained from source. -if(PROJECT_IS_TOP_LEVEL) - option(BUILD_SHARED_LIBS "Build shared libs" OFF) -endif() -option( - PATOMIC_BUILD_SHARED - "Override BUILD_SHARED_LIBS for patomic library" - ${BUILD_SHARED_LIBS} -) -mark_as_advanced(PATOMIC_BUILD_SHARED) -set(build_type STATIC) -if(PATOMIC_BUILD_SHARED) - set(build_type SHARED) -endif() - - -# ---- Warning Guard ---- - -# target_include_directories with SYSTEM modifier will request the compiler to -# omit warnings from the provided paths, if the compiler supports that. -# This is to provide a user experience similar to find_package when -# add_subdirectory or FetchContent is used to consume this project. -set(warning_guard "") -if(NOT PROJECT_IS_TOP_LEVEL) - option( - PATOMIC_INCLUDES_WITH_SYSTEM - "Use SYSTEM modifier for patomic's includes, disabling warnings" - ON - ) - mark_as_advanced(PATOMIC_INCLUDES_WITH_SYSTEM) - if(PATOMIC_INCLUDES_WITH_SYSTEM) - set(warning_guard SYSTEM) - endif() -endif() - - -# ---- Enable Testing ---- - -# By default tests aren't enabled even with BUILD_TESTING unless the library is -# built as a top level project. -# This is in order to cut down on unnecessary compile times, since it's unlikely -# for users to want to run the tests of their dependencies. -if(PROJECT_IS_TOP_LEVEL) - option(BUILD_TESTING "Build tests" OFF) -endif() -option( - PATOMIC_BUILD_TESTING - "Override BUILD_TESTING for patomic library" - ${BUILD_TESTING} -) -mark_as_advanced(PATOMIC_BUILD_TESTING) - - -# ---- Install Include Directory ---- - -# Adds an extra directory to the include path by default, so that when you link -# against the target, you get `/include/patomic-X.Y.Z` added to your -# include paths rather than ` patomic-test-${kind}-${name} (e.g. patomic-test-bt-SomeExample) +# - executable name -> ${name} (e.g. SomeExample on Unix or SomeExample.exe on Windows) +# - install directory -> ${CMAKE_INSTALL_TESTDIR}/${kind} (e.g. share/test/bt) +# +# Hierarchy: +# - patomic-test -> base custom target & component (build/install) for all tests +# - patomic-test-${kind} -> custom target & component (build install) for all tests of a specific kind +# - patomic-test-${kind}-${name} -> executable target for a single test +# +# _create_test( # BT|UT # [INCLUDE ...] # [SOURCE ...] # [LINK ...] # ) -function(create_test) +function(_create_test) # setup what arguments we expect - set(all_kinds "BT;UT") # list we can iterate over - set(all_kinds_option "BT|UT") # string to use in debug message + set(all_kinds "BT;UT") # list of all kinds we can iterate over + set(all_kinds_opt_msg "BT|UT") # string to use in debug message cmake_parse_arguments( "ARG" @@ -32,9 +39,9 @@ function(create_test) # check what test kinds are passed set(kind "") # -> "bt" - set(name "") # -> "${ARG_BT}" - # we turn kind into a list so that we can check its length (should be 1) - # and name is just the last name we process + set(name "") # -> value of "${ARG_BT}" + # we turn 'kind' into a list so that we can check its length (which should be 1) + # 'name' is just the last name we process (there should only be 1 name) foreach(ak IN LISTS all_kinds) # if(ARG_BT) if (ARG_${ak}) @@ -49,63 +56,101 @@ function(create_test) # validate arguments + # setup set(args_valid TRUE) - set(func_name "create_test") - set(LENGTH kind kinds_count) + set(func_name "create_test") # CMAKE_CURRENT_FUNCTION is CMake 3.17+ + set(LENGTH kind kinds_count) # expected value is 1 + # go through all possible issues with arguments if(kinds_count EQUAL 0) - message(WARNING "${all_kinds_option} option needs to be specified when invoking '${func_name}'") + message(WARNING "'${all_kinds_opt_msg}' option must be specified invoking '${func_name}'") set(args_valid FALSE) elseif(kinds_count GREATER 1) - message(WARNING "Only a single ${all_kinds_option} option may be specified when invoking '${func_name}'") + message(WARNING "Only a single '${all_kinds_opt_msg}' option may be specified when invoking '${func_name}'") set(args_valid FALSE) elseif(TARGET ${name}) - message(WARNING "Test name must not be an existing target when invoking '${func_name}', was passed: ${name}") + message(WARNING "Test name must not be an existing target when invoking '${func_name}', was passed: '${name}'") set(args_valid FALSE) elseif("${name}" STREQUAL "") message(WARNING "Test name must not be empty when invoking '${func_name}'") + set(args_valid FALSE) endif() + # check there are no leftover arguments if(DEFINED ARG_UNPARSED_ARGUMENTS) message(WARNING "The following arguments were not recognised when invoking '${func_name}': ${ARG_UNPARSED_ARGUMENTS}") set(args_valid FALSE) endif() - if (NOT args_valid) + # abort if validation failed + if(NOT args_valid) message(FATAL_ERROR "Aborting '${func_name}' due to invalid arguments") endif() - # setup target + # create test target - set(parent_target patomic_${kind}) - set(target ${parent_target}_${name}) - set(target_deps patomic::patomic GTest::gtest_main ${ARG_LINK}) - set(output_name ${name}) + # setup + set(base_target patomic-test) # patomic-test + set(parent_target ${base_target}-${kind}) # patomic-test-bt + set(target ${parent_target}-${name}) # patomic-test-bt-SomeExample - if (NOT "${target}" MATCHES ${PATOMIC_CREATE_TEST_TARGETS_MATCHING}) - message(DEBUG "Skipping creation of test target ${target} (matches ${PATOMIC_CREATE_TEST_TARGETS_MATCHING})") - return() + # check target name matches pattern + if(NOT "${target}" MATCHES "${PATOMIC_CREATE_TEST_TARGETS_MATCHING}") + message(VERBOSE "Skipping creation of test target '${target}' (does not match ${PATOMIC_CREATE_TEST_TARGETS_MATCHING})") endif() + # create target with sources add_executable( ${target} - ${ARG_INCLUDE} ${ARG_SOURCE} ) + # add include directories + target_include_directories( + ${target} PRIVATE + ${ARG_INCLUDE} + ) + + # update dependencies list directly because we use it in Windows PATH stuff later + if("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.20.0") + list(APPEND ARG_LINK GTest::gtest_main) + # TODO: this prevents test lookup on Windows; fix once pipeline exists + # TODO: see https://github.com/google/googletest/issues/2157 + # if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.23.0") + # list(APPEND ARG_LINK GTest::gmock) + # endif() + else() + list(APPEND ARG_LINK GTest::Main GTest::GTest) + endif() + + # link dependencies (all tests use GTest framework) target_link_libraries( - ${target} - PRIVATE - ${target_deps} + ${target} PRIVATE + ${ARG_LINK} ) - target_compile_features(${target} PRIVATE cxx_std_14) + # require C++14 as minimum + target_compile_features( + ${target} PRIVATE + cxx_std_14 + ) - set_target_properties(${target} PROPERTIES OUTPUT_NAME ${output_name}) + # set macro to know which test kind code is part of + string(TOUPPER "${kind}" kind_upper) + target_compile_definitions( + ${target} PRIVATE + "PATOMIC_${kind_upper}" + ) + # set binary name instead of using default + set_target_properties( + ${target} PROPERTIES + OUTPUT_NAME "${name}" + ) - # register tests with CTest + + # register test with GTest/CTest and parent target # must be run in same directory scope as target gtest_add_tests( @@ -113,84 +158,154 @@ function(create_test) TEST_LIST added_tests ) + add_dependencies(${parent_target} ${target}) + - # deal with Windows runtime linker issues for tests (with and without CTest) + # deal with Windows runtime linker issues - if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") # check we actually care about Windows PATH stuff - if (NOT PATOMIC_WINDOWS_SET_CTEST_PATH_ENV AND NOT PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE) + if(NOT PATOMIC_WINDOWS_SET_CTEST_PATH_ENV AND + NOT PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE) return() endif() # get paths to all shared library dependencies (DLLs) - windows_deps_path( - deps_path - ${target_deps} + windows_deps_paths( + deps_paths + ${ARG_LINK} ) - # set environment variable for the each test so that CTest works automatically - if (deps_path AND PATOMIC_WINDOWS_SET_CTEST_PATH_ENV) + # set environment variable for each test so that CTest works + if(deps_paths AND PATOMIC_WINDOWS_SET_CTEST_PATH_ENV) foreach(test IN LISTS added_tests) set_property( TEST "${test}" - PROPERTY ENVIRONMENT "PATH=${deps_path}" + PROPERTY ENVIRONMENT "PATH=${deps_paths}" ) endforeach() endif() # make dependencies accessible from parent target - if (PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE) + # so that we can create single file for all tests in kind + if(PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE) set_property( TARGET ${parent_target} - APPEND PROPERTY WIN_DEP_TARGETS ${target_deps} + APPEND PROPERTY WIN_DEPS_TARGETS "${ARG_LINK}" ) endif() + endif() # setup install of target if(NOT CMAKE_SKIP_INSTALL_RULES) - # install as part of patomic_${kind} component + + # install as part of patomic-test-${kind} component install( TARGETS ${target} RUNTIME # - COMPONENT "${parent_target}" - DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}" + COMPONENT ${parent_target} + DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}/" EXCLUDE_FROM_ALL ) - # install as part of patomic_test component + # install as part of patomic-test component install( TARGETS ${target} RUNTIME # - COMPONENT patomic_test - DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}" + COMPONENT ${base_target} + DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}/" EXCLUDE_FROM_ALL ) + endif() +endfunction() - # attach to parent target - add_dependencies(${parent_target} ${target}) +# Creates target patomic-test-bt-${name} corresponding to BT test executable. +# +# create_bt( +# NAME +# [INCLUDE ...] +# [SOURCE ...] +# [LINK ...] +# ) +function(create_bt) + + cmake_parse_arguments( + "ARG" + "" + "NAME" + "INCLUDE;SOURCE;LINK" + ${ARGN} + ) + + _create_test( + ${ARG_UNPARSED_ARGUMENTS} + BT ${ARG_NAME} + INCLUDE ${ARG_INCLUDE} + SOURCE ${ARG_SOURCE} + LINK + patomic::patomic + ${ARG_LINK} + ) + +endfunction() + + +# Creates target patomic-test-ut-${name} corresponding to UT test executable. +# +# create_ut( +# NAME +# [INCLUDE ...] +# [SOURCE ...] +# ) +function(create_ut) + + cmake_parse_arguments( + "ARG" + "" + "NAME" + "INCLUDE;SOURCE" + ${ARGN} + ) + + _create_test( + ${ARG_UNPARSED_ARGUMENTS} + UT ${ARG_NAME} + SOURCE ${ARG_SOURCE} + INCLUDE + "$" + "$" + "$" + ${ARG_INCLUDE} + ) + + # visibility macros will break UTs on Windows + set(target_name patomic-test-ut-${ARG_NAME}) + target_compile_definitions(${target_name} PRIVATE PATOMIC_STATIC_DEFINE) endfunction() # ---- Create Test Dependency File ---- -# Creates a file containing the output of windows_deps_path for all tests of +# Creates a file containing the output of windows_deps_paths for all tests of # the given kind registered so far. -# Expects a target named patomic_${kind} to exist -# E.g. if you call it as create_test_win_deps_path_file(BT) then patomic_bt must +# The file path will be "${CMAKE_CURRENT_BINARY_DIR}/windows_dependencies_path.txt". +# Expects a target named patomic-test-${kind} to exist +# E.g. if you call it as create_test_win_deps_paths_file(BT) then patomic-bt must # exist. +# This function has no effect when not running on Windows. # -# create_test_win_deps_path_file( +# create_test_win_deps_paths_file( # BT|UT # ) -function(create_test_win_deps_path_file ARG_KIND) +function(create_test_win_deps_paths_file ARG_KIND) # check we actually want to generate file @@ -201,10 +316,10 @@ function(create_test_win_deps_path_file ARG_KIND) # check KIND is valid - set(all_kinds_option "BT|UT") - set(func_name "create_test_win_deps_path_file") + set(all_kinds_opt_msg "BT|UT") + set(func_name "create_test_win_deps_paths_file") - if (NOT ARG_KIND MATCHES "^(${all_kinds_option})$") + if(NOT ARG_KIND MATCHES "^(${all_kinds_option})$") message(WARNING "${all_kinds_option} option needs to be specified when invoking '${func_name}'") message(FATAL_ERROR "Aborting '${func_name}' due to invalid arguments") endif() @@ -214,19 +329,19 @@ function(create_test_win_deps_path_file ARG_KIND) # create and install file with dependencies path - if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") # get dependencies set by create_test from target - get_target_property(dep_targets patomic_${kind} WIN_DEP_TARGETS) + get_target_property(dep_targets patomic-test-${kind} WIN_DEP_TARGETS) if("${dep_targets}" STREQUAL "dep_targets-NOTFOUND") - message(DEBUG "Skipping creation of Windows dependencies PATH file for ${ARG_KIND}; no relevant test targets created") + message(VERBOSE "Skipping creation of Windows dependencies PATH file for ${ARG_KIND}; no relevant test targets created") return() endif() list(REMOVE_DUPLICATES dep_targets) # get paths to all shared library dependencies (DLLs) - windows_deps_path( - deps_path + windows_deps_paths( + deps_paths ${dep_targets} ) @@ -234,27 +349,30 @@ function(create_test_win_deps_path_file ARG_KIND) set(file_path "${CMAKE_CURRENT_BINARY_DIR}/windows_dependencies_path.txt") file(GENERATE OUTPUT ${file_path} - CONTENT "${deps_path}" + CONTENT "${deps_paths}" ) # copy file to install location if(NOT CMAKE_SKIP_INSTALL_RULES) - # install as part of patomic_${kind} component + + # install as part of patomic-test-${kind} component install( FILES ${file_path} - COMPONENT patomic_${kind} + COMPONENT patomic-test-${kind} DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}" EXCLUDE_FROM_ALL ) - # install as part of patomic_test component + # install as part of patomic-test component install( FILES ${file_path} - COMPONENT patomic_test + COMPONENT patomic-test DESTINATION "${CMAKE_INSTALL_TESTDIR}/patomic/${kind}" EXCLUDE_FROM_ALL ) + endif() + endif() endfunction() diff --git a/test/cmake/OptionVariables.cmake b/test/cmake/OptionVariables.cmake index e2c8211e4..81a860ad1 100644 --- a/test/cmake/OptionVariables.cmake +++ b/test/cmake/OptionVariables.cmake @@ -1,28 +1,46 @@ +# ---- Options Summary ---- + +# -------------------------------------------------------------------- +# | Option | Availability | Default | +# |======================================|==============|============| +# | CMAKE_INSTALL_TESTDIR (unofficial) | Always | share/test | +# |--------------------------------------|--------------|------------| +# | PATOMIC_CREATE_TEST_TARGETS_MATCHING | Always | ^(.*)$ | +# | PATOMIC_WINDOWS_SET_CTEST_PATH_ENV | Always | ON | +# | PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE | Always | OFF | +# -------------------------------------------------------------------- + + # ---- Test Install Directory ---- # Normally tests would be install in CMAKE_INSTALL_BINDIR by default since # they're executables. # This is undesirable, so this variable exists to override the install location # of test binaries separately. -# It's not prefixed with patomic_test because it's ok for it to be shared and +# It's not prefixed with PATOMIC_ because it's ok for it to be shared and # overridden by parent projects. # Note: this is not an official CMake variable +# The variable type is STRING rather than PATH, because otherwise passing +# -DCMAKE_INSTALL_TESTDIR=share/test on the command line would expand to an +# absolute path with the base being the current CMake directory, leading to +# unexpected errors. set( CMAKE_INSTALL_TESTDIR "share/test" - CACHE PATH "(unofficial) Default test install location" + CACHE STRING "(unofficial) Default test install location" ) # ---- Test Build Selection ---- -# This option provides a way to selectively disable/enable tests based on target -# name (not suite/case name, or name passed to create_test). +# This option provides a way to selectively disable/enable building tests based +# on target name (not suite/case name, or name passed to create_test). # Regex must be written according to CMake Regex Specification. # By default all test targets are enabled. set( PATOMIC_CREATE_TEST_TARGETS_MATCHING "^(.*)$" CACHE STRING "Only test targets matching regex are created and registered with CTest" ) +mark_as_advanced(PATOMIC_CREATE_TEST_TARGETS_MATCHING) # ---- Windows Tests Path ---- @@ -30,8 +48,9 @@ set( # By default we set PATH for tests run with CTest on Windows in order to prevent # linker errors. # Due to limitations in CMake, we can only completely override the PATH, rather -# than prepend to it. +# than prepend or append to it. # This gives users the option to disable this behaviour. +# This option has no effect when not running on Windows. option( PATOMIC_WINDOWS_SET_CTEST_PATH_ENV "Set PATH environment variable for tests when run CTest on Windows" @@ -42,13 +61,16 @@ mark_as_advanced(PATOMIC_WINDOWS_SET_CTEST_PATH_ENV) # ---- Windows Path File ---- -# By default on Windows we generate a file per test kind that contains a string -# that can be prepended to PATH before running tests, in order to ensure that +# On Windows we need to set PATH for tests but may not want to have the PATH be +# completely overridden, like with PATOMIC_WINDOWS_SET_CTEST_PATH_ENV. +# Instead we can generated a file per test kind that contains a string that can +# be manually prepended to PATH before running tests, in order to ensure that # runtime dependencies can be found. # Most of the time we don't need this file (since CTest will take care of that -# for us), so we don't need to generate it. +# for us), so we don't generate it by default. # Additionally disabled by default because it contains potentially private # information about the target platform. +# This option has no effect when not running on Windows. option( PATOMIC_WINDOWS_CREATE_PATH_ENV_FILE "Create file with PATH environment variables for tests on Windows" diff --git a/test/cmake/WindowsDependenciesPath.cmake b/test/cmake/WindowsDependenciesPath.cmake index 4671253a7..c3cedb418 100644 --- a/test/cmake/WindowsDependenciesPath.cmake +++ b/test/cmake/WindowsDependenciesPath.cmake @@ -1,33 +1,39 @@ # ---- Windows Path Prefix ---- # Windows doesn't support rpath, so when linking dynamically the libraries need -# to either be in the same directory or on PATH. -# This function sets a variable to a GENERATOR string that may be prepended to -# path in order to find linked dependencies +# to either be in the same directory or in PATH. +# This function sets a variable to a list of GENERATOR strings which resolve to +# the paths of the linked dependencies. # See: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order -# Usage: windows_deps_path( ...) -function(windows_deps_path ARG_VAR) - if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") +# Usage: windows_deps_paths( ...) +function(windows_deps_paths ARG_VAR) + + # check that we're on Windows + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") set(${ARG_VAR} "" PARENT_SCOPE) return() endif() + # create a list of paths to dependency shared library locations set(paths "") foreach(target IN LISTS ARGN) - # This will fail if passed a link option that isn't a target - # This is intentional; don't do that + # This will fail if passed a link option that isn't a target. + # This is intentional; don't do that. # Instead, create an IMPORTED library, and set its target properties # such as IMPORTED_LOCATION for the library (.a .so etc.) path and # set INTERFACE_INCLUDE_DIRECTORIES to the directory containing any - # necessary header files + # necessary header files. get_target_property(type "${target}" TYPE) if(type STREQUAL "SHARED_LIBRARY") list(APPEND paths "$") endif() endforeach() + # remove duplicates + # makes generated file more human readable (if generated) list(REMOVE_DUPLICATES paths) + # concatenate list into string with same format as PATH on Windows set(path "") set(glue "") foreach(p IN LISTS paths) @@ -35,5 +41,6 @@ function(windows_deps_path ARG_VAR) set(glue "\;") # backslash is important endforeach() + # "return" string with PATH data set(${ARG_VAR} "${path}" PARENT_SCOPE) endfunction() diff --git a/test/ut/CMakeLists.txt b/test/ut/CMakeLists.txt index 20b7cd490..efdd8230e 100644 --- a/test/ut/CMakeLists.txt +++ b/test/ut/CMakeLists.txt @@ -1,17 +1,16 @@ -# create_test requires windows_deps_path include(../cmake/CreateTest.cmake) include(../cmake/WindowsDependenciesPath.cmake) # ---- Setup Tests ---- -add_custom_target(patomic_ut) -add_dependencies(patomic_test patomic_ut) +add_custom_target(patomic-test-ut) +add_dependencies(patomic-test patomic-test-ut) -create_test(UT example_add SOURCE example_add.cpp) -create_test(UT example_sub SOURCE example_sub.cpp) +create_ut(NAME example_add SOURCE example_add.cpp "${PATOMIC_SOURCE_DIR}/src/patomic.c") +create_ut(NAME example_sub SOURCE example_sub.cpp "${PATOMIC_SOURCE_DIR}/src/patomic.c") # ---- Windows Path Issues ---- -create_test_win_deps_path_file(UT) +create_test_win_deps_paths_file(UT)