Browse code

Add project

Robert Cranston authored on 17/05/2022 21:36:24
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,392 @@
1
+# https://git.rcrnstn.net/rcrnstn/cmake-common
2
+
3
+## CMake modules
4
+include(FetchContent)
5
+include(CMakePackageConfigHelpers)
6
+include(CheckIPOSupported)
7
+include(GNUInstallDirs)
8
+include(CTest)
9
+if(NOT CPack_CMake_INCLUDED)
10
+    include(CPack)
11
+endif()
12
+
13
+function(common)
14
+    ## Arguments
15
+    set(OPTIONS
16
+        DISABLE_WSHADOW
17
+        DISABLE_SANITIZERS
18
+        DISABLE_CPPCHECK
19
+        DISABLE_CLANG_TIDY
20
+        DISABLE_INCLUDE_WHAT_YOU_USE
21
+    )
22
+    set(ONE_VALUE_ARGS
23
+        CXX_STANDARD
24
+    )
25
+    set(MULTI_VALUE_ARGS
26
+        PACKAGES
27
+        FETCHCONTENT
28
+        DEPENDENCIES_PUBLIC
29
+        DEPENDENCIES_PRIVATE
30
+        DEPENDENCIES_TESTS
31
+        DEFINES
32
+    )
33
+    cmake_parse_arguments(PARSE_ARGV 0 ARG
34
+        "${OPTIONS}"
35
+        "${ONE_VALUE_ARGS}"
36
+        "${MULTI_VALUE_ARGS}"
37
+    )
38
+
39
+    ## Variables
40
+    set(TEST_TARGETS)
41
+    set(PROJECT_IS_DEBUG)
42
+    set(PROJECT_IS_INTERFACE_LIBRARY)
43
+    set(PROJECT_IS_TOP_LEVEL)
44
+    string(TOLOWER "${CMAKE_BUILD_TYPE}" PROJECT_BUILD_TYPE)
45
+    get_target_property(PROJECT_TYPE ${PROJECT_NAME} TYPE)
46
+    if("${PROJECT_BUILD_TYPE}" STREQUAL "debug")
47
+        set(PROJECT_IS_DEBUG TRUE)
48
+    endif()
49
+    if("${PROJECT_TYPE}" STREQUAL "INTERFACE_LIBRARY")
50
+        set(PROJECT_IS_INTERFACE_LIBRARY TRUE)
51
+    endif()
52
+    if("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}")
53
+        set(PROJECT_IS_TOP_LEVEL TRUE)
54
+    endif()
55
+
56
+    ## Packages
57
+    foreach(PACKAGE ${ARG_PACKAGES})
58
+        find_package(${PACKAGE} REQUIRED)
59
+    endforeach()
60
+
61
+    ## Fetch content
62
+    foreach(FETCHCONTENT ${ARG_FETCHCONTENT})
63
+        get_filename_component(FETCHCONTENT_NAME ${FETCHCONTENT} NAME)
64
+        FetchContent_Declare(${FETCHCONTENT_NAME}
65
+            GIT_REPOSITORY ${FETCHCONTENT}
66
+            GIT_SHALLOW TRUE
67
+            GIT_PROGRESS TRUE
68
+        )
69
+        FetchContent_MakeAvailable(${FETCHCONTENT_NAME})
70
+    endforeach()
71
+
72
+    ## Main target
73
+    file(GLOB_RECURSE SRC CONFIGURE_DEPENDS
74
+        RELATIVE "${PROJECT_SOURCE_DIR}"
75
+        src/*
76
+    )
77
+    file(GLOB_RECURSE INCLUDE CONFIGURE_DEPENDS
78
+        RELATIVE "${PROJECT_SOURCE_DIR}"
79
+        include/*
80
+    )
81
+    if(NOT ("${SRC}" STREQUAL "" AND "${INCLUDE}" STREQUAL ""))
82
+        if(PROJECT_IS_INTERFACE_LIBRARY)
83
+            target_include_directories(${PROJECT_NAME}
84
+                INTERFACE
85
+                    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
86
+                    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
87
+            )
88
+        else()
89
+            target_sources(${PROJECT_NAME}
90
+                PRIVATE
91
+                    ${SRC}
92
+                    ${INCLUDE}
93
+            )
94
+            target_include_directories(${PROJECT_NAME}
95
+                PUBLIC
96
+                    "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
97
+                    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
98
+                PRIVATE
99
+                    src
100
+            )
101
+            target_link_libraries(${PROJECT_NAME}
102
+                PUBLIC
103
+                    ${ARG_DEPENDENCIES_PUBLIC}
104
+                PRIVATE
105
+                    "$<BUILD_INTERFACE:${ARG_DEPENDENCIES_PRIVATE}>"
106
+            )
107
+        endif()
108
+        if(PROJECT_IS_TOP_LEVEL)
109
+            install(
110
+                DIRECTORY
111
+                    include/
112
+                DESTINATION
113
+                    ${CMAKE_INSTALL_INCLUDEDIR}
114
+            )
115
+            install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME})
116
+            install(EXPORT ${PROJECT_NAME}
117
+                FILE ${PROJECT_NAME}Config.cmake
118
+                DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
119
+            )
120
+            write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake
121
+                COMPATIBILITY SameMajorVersion
122
+            )
123
+            install(
124
+                FILES ${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
125
+                DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
126
+            )
127
+        endif()
128
+    endif()
129
+
130
+    ## Test targets
131
+    if(PROJECT_IS_TOP_LEVEL AND BUILD_TESTING)
132
+        file(GLOB_RECURSE TESTS_COMMON CONFIGURE_DEPENDS
133
+            tests/common/*
134
+        )
135
+        file(GLOB TESTS CONFIGURE_DEPENDS
136
+            LIST_DIRECTORIES FALSE
137
+            tests/*
138
+        )
139
+        foreach(TEST ${TESTS})
140
+            get_filename_component(TEST_NAME "${TEST}" NAME_WE)
141
+            set(TEST_TARGET "${PROJECT_NAME}-test-${TEST_NAME}")
142
+            add_executable(${TEST_TARGET}
143
+                ${TEST}
144
+                ${TESTS_COMMON}
145
+            )
146
+            target_include_directories(${TEST_TARGET}
147
+                PRIVATE
148
+                    tests
149
+            )
150
+            target_link_libraries(${TEST_TARGET}
151
+                PRIVATE
152
+                    ${PROJECT_NAME}
153
+                    ${ARG_DEPENDENCIES_TESTS}
154
+            )
155
+            add_test(
156
+                NAME    ${TEST_TARGET}
157
+                COMMAND ${TEST_TARGET}
158
+            )
159
+            list(APPEND TEST_TARGETS ${TEST_TARGET})
160
+        endforeach()
161
+    endif()
162
+
163
+    ## Assets
164
+    file(REMOVE_RECURSE
165
+        ${CMAKE_BINARY_DIR}/assets
166
+    )
167
+    file(GLOB_RECURSE ASSETS CONFIGURE_DEPENDS
168
+        RELATIVE "${PROJECT_SOURCE_DIR}"
169
+        assets/*
170
+    )
171
+    if(NOT "${ASSETS}" STREQUAL "")
172
+        add_custom_target(${PROJECT_NAME}-assets)
173
+        add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-assets)
174
+    endif()
175
+    foreach(ASSET ${ASSETS})
176
+        get_filename_component(ASSET_DIR ${ASSET} DIRECTORY)
177
+        add_custom_command(
178
+            DEPENDS
179
+                ${PROJECT_SOURCE_DIR}/${ASSET}
180
+            OUTPUT
181
+                ${CMAKE_BINARY_DIR}/${ASSET}
182
+            COMMAND
183
+                ${CMAKE_COMMAND} -E make_directory
184
+                    ${CMAKE_BINARY_DIR}/${ASSET_DIR}
185
+            COMMAND
186
+                ${CMAKE_COMMAND} -E create_symlink
187
+                    ${PROJECT_SOURCE_DIR}/${ASSET}
188
+                    ${CMAKE_BINARY_DIR}/${ASSET}
189
+            VERBATIM
190
+        )
191
+        set_property(TARGET ${PROJECT_NAME}-assets APPEND PROPERTY
192
+            SOURCES ${CMAKE_BINARY_DIR}/${ASSET}
193
+        )
194
+        if(NOT "${ASSET}" MATCHES "/tests/")
195
+            install(
196
+                FILES
197
+                    ${PROJECT_SOURCE_DIR}/${ASSET}
198
+                DESTINATION
199
+                    ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/${ASSET_DIR}
200
+            )
201
+        endif()
202
+    endforeach()
203
+
204
+    ## Documentation
205
+    if(PROJECT_IS_TOP_LEVEL)
206
+        file(GLOB_RECURSE DOCS CONFIGURE_DEPENDS
207
+            RELATIVE "${PROJECT_SOURCE_DIR}"
208
+            doc/*
209
+        )
210
+        file(GLOB DOCS_ROOT CONFIGURE_DEPENDS
211
+            RELATIVE "${PROJECT_SOURCE_DIR}"
212
+            README*
213
+            LICENSE*
214
+            COPYING*
215
+            CHANGELOG*
216
+            CHANGES*
217
+            HISTORY*
218
+            NEWS*
219
+            RELEASES*
220
+            AUTHORS*
221
+            ACKNOWLEDGMENTS*
222
+            CONTRIBUTORS*
223
+            CONTRIBUTING*
224
+            CODE_OF_CONDUCT*
225
+            SECURITY*
226
+            SUPPORT*
227
+        )
228
+        list(APPEND DOCS ${DOCS_ROOT})
229
+        foreach(DOC ${DOCS})
230
+            get_filename_component(DOC_DIR ${DOC} DIRECTORY)
231
+            install(
232
+                FILES
233
+                    ${PROJECT_SOURCE_DIR}/${DOC}
234
+                DESTINATION
235
+                    ${CMAKE_INSTALL_DOCDIR}/${DOC_DIR}
236
+            )
237
+        endforeach()
238
+    endif()
239
+
240
+    ## Man pages
241
+    if(PROJECT_IS_TOP_LEVEL)
242
+        file(GLOB_RECURSE MANS CONFIGURE_DEPENDS
243
+            RELATIVE "${PROJECT_SOURCE_DIR}"
244
+            man/*
245
+        )
246
+        foreach(MAN ${MANS})
247
+            get_filename_component(MAN_DIR ${MAN} DIRECTORY)
248
+            install(
249
+                FILES
250
+                    ${PROJECT_SOURCE_DIR}/${MAN}
251
+                DESTINATION
252
+                    ${CMAKE_INSTALL_MANDIR}/${MAN_DIR}
253
+            )
254
+        endforeach()
255
+    endif()
256
+
257
+    ## Preprocessor defines
258
+    if(PROJECT_IS_TOP_LEVEL AND NOT PROJECT_IS_INTERFACE_LIBRARY)
259
+        set_property(TARGET ${PROJECT_NAME} ${TEST_TARGETS} APPEND PROPERTY
260
+            COMPILE_DEFINITIONS "${ARG_DEFINES}"
261
+        )
262
+    endif()
263
+
264
+    ## Language version
265
+    if(PROJECT_IS_TOP_LEVEL AND NOT PROJECT_IS_INTERFACE_LIBRARY)
266
+        set_target_properties(${PROJECT_NAME} ${TEST_TARGETS} PROPERTIES
267
+            CXX_STANDARD          "${ARG_CXX_STANDARD}"
268
+            CXX_STANDARD_REQUIRED ON
269
+            CXX_EXTENSIONS        OFF
270
+        )
271
+    endif()
272
+
273
+    ## Build options
274
+    if(PROJECT_IS_TOP_LEVEL AND NOT PROJECT_IS_INTERFACE_LIBRARY)
275
+        if(MSVC)
276
+            set(COMPILE_OPTIONS
277
+                /permissive-
278
+                /WX
279
+                /W4
280
+            )
281
+        elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|.*Clang")
282
+            set(SANITIZE_OPTIONS)
283
+            if(PROJECT_IS_DEBUG AND NOT ARG_DISABLE_SANITIZERS)
284
+                set(SANITIZE_OPTIONS
285
+                    $<$<CONFIG:Debug>:
286
+                        -g -fno-omit-frame-pointer
287
+                        -fsanitize=address,leak,undefined
288
+                        -fsanitize=float-divide-by-zero
289
+                        -fsanitize=float-cast-overflow
290
+                        # -fsanitize=pointer-compare
291
+                        # -fsanitize=pointer-subtract
292
+                        # -fsanitize=implicit-conversion
293
+                        -fno-sanitize-recover=all
294
+                    >
295
+                )
296
+            endif()
297
+            set(WSHADOW_OPTION)
298
+            if(NOT ARG_DISABLE_WSHADOW)
299
+                set(WSHADOW_OPTION -Wshadow=local)
300
+            endif()
301
+            set(COMPILE_OPTIONS
302
+                -pedantic
303
+                -Werror -Wfatal-errors
304
+                -Wall -Wextra
305
+                -Wno-missing-braces -Wmissing-field-initializers
306
+                -Wconversion -Wsign-conversion
307
+                -Wdouble-promotion
308
+                -Wimplicit-fallthrough
309
+                -Wvla
310
+                -Wzero-as-null-pointer-constant
311
+                -Weffc++
312
+                ${WSHADOW_OPTION}
313
+                ${SANITIZE_OPTIONS}
314
+            )
315
+            set(LINK_OPTIONS
316
+                ${SANITIZE_OPTIONS}
317
+            )
318
+            check_ipo_supported(RESULT INTERPROCEDURAL_OPTIMIZATION)
319
+        endif()
320
+        set_property(TARGET ${PROJECT_NAME} ${TEST_TARGETS} APPEND PROPERTY
321
+            COMPILE_OPTIONS "${COMPILE_OPTIONS}"
322
+        )
323
+        set_property(TARGET ${PROJECT_NAME} ${TEST_TARGETS} APPEND PROPERTY
324
+            LINK_OPTIONS "${LINK_OPTIONS}"
325
+        )
326
+        set_property(TARGET ${PROJECT_NAME} ${TEST_TARGETS} APPEND PROPERTY
327
+            INTERPROCEDURAL_OPTIMIZATION "${INTERPROCEDURAL_OPTIMIZATION}"
328
+        )
329
+    endif()
330
+
331
+    ## Sanitizer environment variables
332
+    set(SAN_STRIP_PATH_PREFIX strip_path_prefix=${PROJECT_BINARY_DIR})
333
+    set(ASAN_OPTIONS  "${SAN_STRIP_PATH_PREFIX}")
334
+    set(LSAN_OPTIONS  "${SAN_STRIP_PATH_PREFIX}")
335
+    set(UBSAN_OPTIONS "${SAN_STRIP_PATH_PREFIX}")
336
+    file(GLOB ASAN_SUPP CONFIGURE_DEPENDS
337
+        .asan.supp
338
+    )
339
+    file(GLOB LSAN_SUPP CONFIGURE_DEPENDS
340
+        .lsan.supp
341
+    )
342
+    file(GLOB UBSAN_SUPP CONFIGURE_DEPENDS
343
+        .ubsan.supp
344
+    )
345
+    if(NOT "${ASAN_SUPP}" STREQUAL "")
346
+        set(ASAN_OPTIONS "${ASAN_OPTIONS},suppressions=${ASAN_SUPP}")
347
+    endif()
348
+    if(NOT "${LSAN_SUPP}" STREQUAL "")
349
+        set(LSAN_OPTIONS "${LSAN_OPTIONS},suppressions=${LSAN_SUPP}")
350
+    endif()
351
+    if(NOT "${UBSAN_SUPP}" STREQUAL "")
352
+        set(UBSAN_OPTIONS "${UBSAN_OPTIONS},suppressions=${UBSAN_SUPP}")
353
+    endif()
354
+    # set(ASAN_OPTIONS "${ASAN_OPTIONS},detect_leaks=1")
355
+    # set(UBSAN_OPTIONS "${UBSAN_OPTIONS},print_stacktrace=1")
356
+    set_property(TEST ${TEST_TARGETS} APPEND PROPERTY
357
+        ENVIRONMENT
358
+            ASAN_OPTIONS=${ASAN_OPTIONS}
359
+            LSAN_OPTIONS=${LSAN_OPTIONS}
360
+            UBSAN_OPTIONS=${UBSAN_OPTIONS}
361
+    )
362
+
363
+    ## Tools
364
+    if(PROJECT_IS_TOP_LEVEL AND PROJECT_IS_DEBUG)
365
+        find_program(CPPCHECK             cppcheck)
366
+        find_program(CLANG_TIDY           clang-tidy)
367
+        find_program(INCLUDE_WHAT_YOU_USE include-what-you-use)
368
+        if(CPPCHECK AND NOT ARG_DISABLE_CPPCHECK)
369
+            set(CXX_CPPCHECK ${CPPCHECK}
370
+                --enable=warning,style
371
+                --inline-suppr
372
+                --template "{file}:{line}: ({severity} {id}) {message}"
373
+                --error-exitcode=1
374
+            )
375
+        endif()
376
+        if(CLANG_TIDY AND NOT ARG_DISABLE_CLANG_TIDY)
377
+            set(CXX_CLANG_TIDY ${CLANG_TIDY}
378
+                --extra-arg=-Wno-error=unknown-warning-option
379
+                --extra-arg=-Wno-error=ignored-optimization-argument
380
+            )
381
+        endif()
382
+        if(INCLUDE_WHAT_YOU_USE AND NOT ARG_DISABLE_INCLUDE_WHAT_YOU_USE)
383
+            set(CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE})
384
+        endif()
385
+        set_target_properties(${PROJECT_NAME} ${TEST_TARGETS} PROPERTIES
386
+            EXPORT_COMPILE_COMMANDS  ON
387
+            CXX_CPPCHECK             "${CXX_CPPCHECK}"
388
+            CXX_CLANG_TIDY           "${CXX_CLANG_TIDY}"
389
+            CXX_INCLUDE_WHAT_YOU_USE "${CXX_INCLUDE_WHAT_YOU_USE}"
390
+        )
391
+    endif()
392
+endfunction()