Browse code

Add project

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