| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- include(CheckCCompilerFlag)
- include(CMakePackageConfigHelpers)
- include(GenerateExportHeader)
- include(GNUInstallDirs)
- # Developer options
- option(REPROC_DEVELOP "Enable all developer options" $ENV{REPROC_DEVELOP})
- option(REPROC_TEST "Build tests" ${REPROC_DEVELOP})
- option(REPROC_EXAMPLES "Build examples" ${REPROC_DEVELOP})
- option(REPROC_WARNINGS "Enable compiler warnings" ${REPROC_DEVELOP})
- option(REPROC_TIDY "Run clang-tidy when building" ${REPROC_DEVELOP})
- option(
- REPROC_SANITIZERS
- "Build with sanitizers on configurations that support it"
- ${REPROC_DEVELOP}
- )
- option(
- REPROC_WARNINGS_AS_ERRORS
- "Add -Werror or equivalent to the compile flags and clang-tidy"
- )
- mark_as_advanced(
- REPROC_TIDY
- REPROC_SANITIZERS
- REPROC_WARNINGS_AS_ERRORS
- )
- # Installation options
- option(REPROC_OBJECT_LIBRARIES "Build CMake object libraries" ${REPROC_DEVELOP})
- if(NOT REPROC_OBJECT_LIBRARIES)
- set(REPROC_INSTALL_DEFAULT ON)
- endif()
- option(REPROC_INSTALL "Generate installation rules" ${REPROC_INSTALL_DEFAULT})
- option(REPROC_INSTALL_PKGCONFIG "Install pkg-config files" ON)
- set(
- REPROC_INSTALL_CMAKECONFIGDIR
- ${CMAKE_INSTALL_LIBDIR}/cmake
- CACHE STRING "CMake config files installation directory"
- )
- set(
- REPROC_INSTALL_PKGCONFIGDIR
- ${CMAKE_INSTALL_LIBDIR}/pkgconfig
- CACHE STRING "pkg-config files installation directory"
- )
- mark_as_advanced(
- REPROC_OBJECT_LIBRARIES
- REPROC_INSTALL
- REPROC_INSTALL_PKGCONFIG
- REPROC_INSTALL_CMAKECONFIGDIR
- REPROC_INSTALL_PKGCONFIGDIR
- )
- # Testing
- if(REPROC_TEST)
- enable_testing()
- endif()
- # Build type
- if(REPROC_DEVELOP AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
- set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE)
- set_property(
- CACHE CMAKE_BUILD_TYPE
- PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo
- )
- endif()
- # clang-tidy
- if(REPROC_TIDY)
- find_program(REPROC_TIDY_PROGRAM clang-tidy)
- mark_as_advanced(REPROC_TIDY_PROGRAM)
- if(NOT REPROC_TIDY_PROGRAM)
- message(FATAL_ERROR "clang-tidy not found")
- endif()
- if(REPROC_WARNINGS_AS_ERRORS)
- set(REPROC_TIDY_PROGRAM ${REPROC_TIDY_PROGRAM} -warnings-as-errors=*)
- endif()
- endif()
- # Functions
- function(reproc_common TARGET LANGUAGE NAME DIRECTORY)
- if(LANGUAGE STREQUAL C)
- set(STANDARD 99)
- target_compile_features(${TARGET} PUBLIC c_std_99)
- else()
- # clang-tidy uses the MSVC standard library instead of MinGW's standard
- # library so we have to use C++14 (because MSVC headers use C++14).
- if(MINGW AND REPROC_TIDY)
- set(STANDARD 14)
- else()
- set(STANDARD 11)
- endif()
- target_compile_features(${TARGET} PUBLIC cxx_std_11)
- endif()
- set_target_properties(${TARGET} PROPERTIES
- ${LANGUAGE}_STANDARD ${STANDARD}
- ${LANGUAGE}_STANDARD_REQUIRED ON
- ${LANGUAGE}_EXTENSIONS OFF
- OUTPUT_NAME "${NAME}"
- RUNTIME_OUTPUT_DIRECTORY "${DIRECTORY}"
- ARCHIVE_OUTPUT_DIRECTORY "${DIRECTORY}"
- LIBRARY_OUTPUT_DIRECTORY "${DIRECTORY}"
- )
- if(REPROC_TIDY AND REPROC_TIDY_PROGRAM)
- set_property(
- TARGET ${TARGET}
- # `REPROC_TIDY_PROGRAM` is a list so we surround it with quotes to pass it
- # as a single argument.
- PROPERTY ${LANGUAGE}_CLANG_TIDY "${REPROC_TIDY_PROGRAM}"
- )
- endif()
- # Common development flags (warnings + sanitizers + colors)
- if(REPROC_WARNINGS)
- if(MSVC)
- check_c_compiler_flag(/permissive- REPROC_HAVE_PERMISSIVE)
- target_compile_options(${TARGET} PRIVATE
- /nologo # Silence MSVC compiler version output.
- $<$<BOOL:${REPROC_WARNINGS_AS_ERRORS}>:/WX> # -Werror
- $<$<BOOL:${REPROC_HAVE_PERMISSIVE}>:/permissive->
- )
- if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15.0)
- # CMake 3.15 does not add /W3 to the compiler flags by default anymore
- # so we add /W4 instead.
- target_compile_options(${TARGET} PRIVATE /W4)
- endif()
- if(LANGUAGE STREQUAL C)
- # Disable MSVC warnings that flag C99 features as non-standard.
- target_compile_options(${TARGET} PRIVATE /wd4204 /wd4221)
- endif()
- else()
- target_compile_options(${TARGET} PRIVATE
- -Wall
- -Wextra
- -pedantic
- -Wconversion
- -Wsign-conversion
- $<$<BOOL:${REPROC_WARNINGS_AS_ERRORS}>:-Werror>
- $<$<BOOL:${REPROC_WARNINGS_AS_ERRORS}>:-pedantic-errors>
- )
- if(LANGUAGE STREQUAL C OR CMAKE_CXX_COMPILER_ID MATCHES Clang)
- target_compile_options(${TARGET} PRIVATE -Wmissing-prototypes)
- endif()
- endif()
- if(WIN32)
- target_compile_definitions(${TARGET} PRIVATE _CRT_SECURE_NO_WARNINGS)
- endif()
- target_compile_options(${TARGET} PRIVATE
- $<$<${LANGUAGE}_COMPILER_ID:GNU>:-fdiagnostics-color>
- $<$<${LANGUAGE}_COMPILER_ID:Clang>:-fcolor-diagnostics>
- )
- endif()
- if(REPROC_SANITIZERS AND NOT MSVC AND NOT MINGW)
- if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15.0)
- set_property(
- TARGET ${TARGET}
- PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded
- )
- endif()
- target_compile_options(${TARGET} PRIVATE
- -fsanitize=address,undefined
- -fno-omit-frame-pointer
- )
- target_link_libraries(${TARGET} PRIVATE -fsanitize=address,undefined)
- endif()
- endfunction()
- function(reproc_library TARGET LANGUAGE)
- if(REPROC_OBJECT_LIBRARIES)
- add_library(${TARGET} OBJECT)
- else()
- add_library(${TARGET})
- endif()
- reproc_common(${TARGET} ${LANGUAGE} "" lib)
- if(BUILD_SHARED_LIBS AND NOT REPROC_OBJECT_LIBRARIES)
- # Enable -fvisibility=hidden and -fvisibility-inlines-hidden.
- set_target_properties(${TARGET} PROPERTIES
- ${LANGUAGE}_VISIBILITY_PRESET hidden
- VISIBILITY_INLINES_HIDDEN true
- )
- # clang-tidy errors with: unknown argument: '-fno-keep-inline-dllexport'
- # when enabling `VISIBILITY_INLINES_HIDDEN` on MinGW so we disable it when
- # running clang-tidy on MinGW.
- if(MINGW AND REPROC_TIDY)
- set_property(TARGET ${TARGET} PROPERTY VISIBILITY_INLINES_HIDDEN false)
- endif()
- # Disable CMake's default export definition.
- set_property(TARGET ${TARGET} PROPERTY DEFINE_SYMBOL "")
- string(TOUPPER ${TARGET} TARGET_UPPER)
- string(REPLACE + X TARGET_SANITIZED ${TARGET_UPPER})
- target_compile_definitions(${TARGET} PRIVATE ${TARGET_SANITIZED}_BUILDING)
- if(WIN32)
- target_compile_definitions(${TARGET} PUBLIC ${TARGET_SANITIZED}_SHARED)
- endif()
- endif()
- # Make sure we follow the popular naming convention for shared libraries on
- # UNIX systems.
- set_target_properties(${TARGET} PROPERTIES
- VERSION ${PROJECT_VERSION}
- SOVERSION ${PROJECT_VERSION_MAJOR}
- )
- # Only use the headers from the repository when building. When installing we
- # want to use the install location of the headers (e.g. /usr/include) as the
- # include directory instead.
- target_include_directories(${TARGET} PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
- )
- # Adapted from https://codingnest.com/basic-cmake-part-2/.
- # Each library is installed separately (with separate config files).
- if(REPROC_INSTALL)
- # Headers
- install(
- DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
- DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
- COMPONENT ${TARGET}-development
- )
- # Library
- install(
- TARGETS ${TARGET}
- EXPORT ${TARGET}-targets
- RUNTIME
- DESTINATION ${CMAKE_INSTALL_BINDIR}
- COMPONENT ${TARGET}-runtime
- LIBRARY
- DESTINATION ${CMAKE_INSTALL_LIBDIR}
- COMPONENT ${TARGET}-runtime
- NAMELINK_COMPONENT ${TARGET}-development
- ARCHIVE
- DESTINATION ${CMAKE_INSTALL_LIBDIR}
- COMPONENT ${TARGET}-development
- INCLUDES
- DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
- )
- if(NOT APPLE)
- set_property(
- TARGET ${TARGET}
- PROPERTY INSTALL_RPATH $ORIGIN
- )
- endif()
- # CMake config
- configure_package_config_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}-config.cmake.in
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-config.cmake
- INSTALL_DESTINATION ${REPROC_INSTALL_CMAKECONFIGDIR}/${TARGET}
- )
- write_basic_package_version_file(
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-config-version.cmake
- COMPATIBILITY SameMajorVersion
- )
- install(
- FILES
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-config.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-config-version.cmake
- DESTINATION ${REPROC_INSTALL_CMAKECONFIGDIR}/${TARGET}
- COMPONENT ${TARGET}-development
- )
- install(
- EXPORT ${TARGET}-targets
- DESTINATION ${REPROC_INSTALL_CMAKECONFIGDIR}/${TARGET}
- COMPONENT ${TARGET}-development
- )
- # pkg-config
- if(REPROC_INSTALL_PKGCONFIG)
- configure_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}.pc.in
- ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.pc
- @ONLY
- )
- install(
- FILES ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.pc
- DESTINATION ${REPROC_INSTALL_PKGCONFIGDIR}
- COMPONENT ${TARGET}-development
- )
- endif()
- endif()
- endfunction()
- function(reproc_test TARGET NAME LANGUAGE)
- if(NOT REPROC_TEST)
- return()
- endif()
- if(LANGUAGE STREQUAL C)
- set(EXTENSION c)
- else()
- set(EXTENSION cpp)
- endif()
- add_executable(${TARGET}-test-${NAME} test/${NAME}.${EXTENSION})
- reproc_common(${TARGET}-test-${NAME} ${LANGUAGE} ${NAME} test)
- target_link_libraries(${TARGET}-test-${NAME} PRIVATE ${TARGET})
- if(MINGW)
- target_compile_definitions(${TARGET}-test-${NAME} PRIVATE
- __USE_MINGW_ANSI_STDIO=1 # Add %zu on Mingw
- )
- endif()
- add_test(NAME ${TARGET}-test-${NAME} COMMAND ${TARGET}-test-${NAME})
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/resources/${NAME}.c)
- target_compile_definitions(${TARGET}-test-${NAME} PRIVATE
- RESOURCE_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/resources"
- )
- if (NOT TARGET ${TARGET}-resource-${NAME})
- add_executable(${TARGET}-resource-${NAME} resources/${NAME}.c)
- reproc_common(${TARGET}-resource-${NAME} C ${NAME} resources)
- endif()
- # Make sure the test resource is available when running the test.
- add_dependencies(${TARGET}-test-${NAME} ${TARGET}-resource-${NAME})
- endif()
- endfunction()
- function(reproc_example TARGET NAME LANGUAGE)
- cmake_parse_arguments(OPT "" "" "ARGS;DEPENDS" ${ARGN})
- if(NOT REPROC_EXAMPLES)
- return()
- endif()
- if(LANGUAGE STREQUAL C)
- set(EXTENSION c)
- else()
- set(EXTENSION cpp)
- endif()
- add_executable(${TARGET}-example-${NAME} examples/${NAME}.${EXTENSION})
- reproc_common(${TARGET}-example-${NAME} ${LANGUAGE} ${NAME} examples)
- target_link_libraries(${TARGET}-example-${NAME} PRIVATE ${TARGET} ${OPT_DEPENDS})
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/resources/${NAME}.c)
- target_compile_definitions(${TARGET}-example-${NAME} PRIVATE
- RESOURCE_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/resources"
- )
- if (NOT TARGET ${TARGET}-resource-${NAME})
- add_executable(${TARGET}-resource-${NAME} resources/${NAME}.c)
- reproc_common(${TARGET}-resource-${NAME} C ${NAME} resources)
- endif()
- # Make sure the example resource is available when running the example.
- add_dependencies(${TARGET}-example-${NAME} ${TARGET}-resource-${NAME})
- endif()
- if(REPROC_TEST)
- if(NOT DEFINED OPT_ARGS)
- set(OPT_ARGS cmake --help)
- endif()
- add_test(
- NAME ${TARGET}-example-${NAME}
- COMMAND ${TARGET}-example-${NAME} ${OPT_ARGS}
- )
- endif()
- endfunction()
|