1#
2# Arm SCP/MCP Software
3# Copyright (c) 2021-2024, Arm Limited and Contributors. All rights reserved.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7
8# cmake-lint: disable=C0301
9
10cmake_minimum_required(VERSION 3.18.3)
11
12list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
13
14#
15# Individual firmware targets are configured based on the 'Firmware.cmake'
16# living in the firmware source directory that the user provides to us, which
17# determines the default set of configuration options we are given, including
18# the toolchain file.
19#
20
21if(SCP_FIRMWARE_SOURCE_DIR)
22    get_filename_component(SCP_FIRMWARE_SOURCE_DIR "${SCP_FIRMWARE_SOURCE_DIR}"
23                           ABSOLUTE BASE_DIR "${CMAKE_SOURCE_DIR}/product")
24
25    include("${SCP_FIRMWARE_SOURCE_DIR}/Firmware.cmake")
26
27    if((NOT SCP_FIRMWARE) OR (NOT SCP_FIRMWARE_TARGET))
28        # cmake-format: off
29        message(FATAL_ERROR
30            "Insufficient firmware metadata provided.\n"
31
32            "Please ensure your 'Firmware.cmake' has set both `SCP_FIRMWARE` "
33            "and `SCP_FIRMWARE_TARGET` to the name of your firmware and the "
34            "firmware's CMake target respectively.")
35        # cmake-format: on
36    endif()
37
38    if(NOT SCP_FIRMWARE_BINARY_DIR)
39        #
40        # Derive the binary directory from the location of the source directory
41        # if it's in-tree.
42        #
43
44        file(RELATIVE_PATH SCP_FIRMWARE_BINARY_DIR "${CMAKE_SOURCE_DIR}"
45             "${SCP_FIRMWARE_SOURCE_DIR}")
46    endif()
47
48    if(SCP_FIRMWARE_BINARY_DIR MATCHES "\\.\\.")
49        # cmake-format: off
50        message(FATAL_ERROR
51            "Invalid firmware binary directory.\n"
52
53            "Please ensure your firmware binary directory is a relative path. "
54            "This path is used as the location within the project binary "
55            "directory that will be used for firmware artifacts.")
56        # cmake-format: on
57    endif()
58endif()
59
60#
61# Handle automatic selection of the toolchain. This only occurs if the user has
62# not explicitly provided the path to a toolchain file and the firmware has
63# given us a default preference.
64#
65
66if(SCP_TOOLCHAIN_INIT AND (NOT CMAKE_TOOLCHAIN_FILE))
67    #
68    # Let the firmware decide what its default toolchain should be, but allow
69    # the user to override it.
70    #
71
72    if(NOT SCP_TOOLCHAIN)
73        set(SCP_TOOLCHAIN "${SCP_TOOLCHAIN_INIT}")
74    endif()
75
76    set(CMAKE_TOOLCHAIN_FILE
77        "${SCP_FIRMWARE_SOURCE_DIR}/Toolchain-${SCP_TOOLCHAIN}.cmake")
78endif()
79
80if(SCP_TOOLCHAIN STREQUAL "Clang" AND NOT SCP_LLVM_SYSROOT_CC)
81    message(
82        FATAL_ERROR
83            "When Clang is set as toolchain SCP_LLVM_SYSROOT_CC must be defined"
84    )
85endif()
86
87project(
88    SCP
89    VERSION 2.15.0
90    DESCRIPTION "Arm SCP/MCP Software"
91    HOMEPAGE_URL
92        "https://developer.arm.com/tools-and-software/open-source-software/firmware/scp-firmware"
93    LANGUAGES C ASM)
94
95#
96# Configure the default build type to be "Release". We choose to default to a
97# release build as non-developer consumers will generally want a release build,
98# whereas developers who need a debug build are generally familiar with how to
99# enable it in the build system.
100#
101
102if((NOT CMAKE_BUILD_TYPE) AND (NOT CMAKE_CONFIGURATION_TYPES))
103    set(CMAKE_BUILD_TYPE
104        "Release"
105        CACHE STRING "Build type." FORCE)
106
107    set_property(
108        CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel"
109                                                "RelWithDebInfo")
110endif()
111
112#
113# Set the default C standard to C11 plus any compiler extensions.
114#
115
116set(CMAKE_C_STANDARD 11)
117set(CMAKE_C_STANDARD_REQUIRED TRUE)
118set(CMAKE_C_EXTENSIONS TRUE)
119
120#
121# We use `__declspec` when available, but Clang needs to explicitly enable
122# support for it before we can use them.
123#
124
125if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
126    string(APPEND CMAKE_C_FLAGS " -fms-extensions")
127endif()
128
129#
130# Enable inter-procedural optimization for all targets if we can.
131# `CheckIPOSupported` doesn't play particularly nicely with toolchains that
132# cannot build executables right off the bat, so we skip this check if we're
133# cross compiling.
134#
135
136include(CheckIPOSupported)
137include(CMakeDependentOption)
138
139check_ipo_supported(RESULT SCP_IPO_SUPPORTED)
140
141cmake_dependent_option(
142    SCP_ENABLE_IPO
143    "Enable the interprocedural optimization (IPO) if supported?"
144    "${SCP_ENABLE_IPO_INIT}" "DEFINED SCP_ENABLE_IPO_INIT" "${SCP_ENABLE_IPO}")
145
146if(CMAKE_CROSSCOMPILING
147   AND (CMAKE_C_COMPILER_ID STREQUAL "GNU")
148   AND SCP_ENABLE_IPO)
149    set(SCP_IPO_SUPPORTED TRUE)
150endif()
151
152cmake_dependent_option(
153    CMAKE_INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimization?"
154    TRUE "SCP_IPO_SUPPORTED;SCP_ENABLE_IPO" FALSE)
155
156#
157# Export a `compile_commands.json` file for generators that support it.
158#
159
160set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
161
162#
163# Set up Cppcheck if it's available and enabled.
164#
165if(NOT DISABLE_CPPCHECK)
166    find_program(CMAKE_C_CPPCHECK NAMES "cppcheck")
167
168    if(NOT CMAKE_C_CPPCHECK)
169        unset(CMAKE_C_CPPCHECK CACHE)
170    endif()
171
172    if(CMAKE_C_CPPCHECK)
173        # Prepend cppcheck_wrapper.py before binary to perform pre and post
174        # cppcheck operations.
175        set(CPPCHECK_WRAPPER ${CMAKE_SOURCE_DIR}/tools/cppcheck_wrapper.py)
176        # cmake-format: off
177
178        list(PREPEND CMAKE_C_CPPCHECK  ${CPPCHECK_WRAPPER})
179        list(APPEND CMAKE_C_CPPCHECK "--quiet")
180        list(APPEND CMAKE_C_CPPCHECK "--suppressions-list=${CMAKE_CURRENT_SOURCE_DIR}/tools/config/cppcheck/cppcheck_suppress_list.txt")
181        list(APPEND CMAKE_C_CPPCHECK "--enable=all")
182        list(APPEND CMAKE_C_CPPCHECK "--error-exitcode=1")
183
184        if(SCP_ARCHITECTURE STREQUAL "arm-m")
185            if(CMAKE_SYSTEM_PROCESSOR MATCHES "cortex-m(33|55|85)")
186                list(APPEND CMAKE_C_CPPCHECK "--library=${CMAKE_CURRENT_SOURCE_DIR}/tools/config/cppcheck/.armv8m.cppcheck.cfg")
187            elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "cortex-m(3|7)")
188                list(APPEND CMAKE_C_CPPCHECK "--library=${CMAKE_CURRENT_SOURCE_DIR}/tools/config/cppcheck/.armv7m.cppcheck.cfg")
189            endif()
190        endif()
191
192        if(CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
193            list(APPEND CMAKE_C_CPPCHECK "--library=${CMAKE_CURRENT_SOURCE_DIR}/tools/config/cppcheck/.armclang.cppcheck.cfg")
194        else()
195            list(APPEND CMAKE_C_CPPCHECK "--library=${CMAKE_CURRENT_SOURCE_DIR}/tools/config/cppcheck/.gnu.cppcheck.cfg")
196        endif()
197
198        if(COMMAND_OUTPUT_VERBOSE)
199             list(APPEND CMAKE_C_CPPCHECK "verbose")
200        endif()
201
202        # cmake-format: on
203    endif()
204endif()
205
206#
207# Try to locate Arm Compiler's 'fromelf' tool.
208#
209
210if(CMAKE_C_COMPILER_ID MATCHES "ARMClang")
211    get_filename_component(base ${CMAKE_C_COMPILER} DIRECTORY)
212
213    find_program(SCP_FROMELF fromelf ${base})
214
215    if(SCP_FROMELF)
216        mark_as_advanced(SCP_FROMELF)
217    endif()
218endif()
219
220#
221# Try to identify any standard libraries that require special treatment.
222#
223
224include(CheckSymbolExists)
225
226check_symbol_exists("__NEWLIB__" "sys/features.h" SCP_HAVE_NEWLIB)
227
228#
229# If the user asks for it, we can save them some time by creating a flat binary
230# from the firmware's compiled ELF image.
231#
232
233include(CMakeDependentOption)
234
235cmake_dependent_option(
236    SCP_GENERATE_FLAT_BINARY "Generate a flat binary (.bin) image?"
237    "${SCP_GENERATE_FLAT_BINARY_INIT}" "DEFINED SCP_GENERATE_FLAT_BINARY_INIT"
238    "${SCP_GENERATE_FLAT_BINARY}")
239
240#
241# If the firmware developer has given us initial values for these configuration
242# options, we can expose them to the user.
243#
244
245include(CMakeDependentOption)
246
247# Common build options
248
249if(SCP_ARCHITECTURE STREQUAL "optee")
250    set(SCP_ENABLE_SUB_SYSTEM_MODE_INIT TRUE)
251endif()
252
253set(SCP_ENABLE_OVERRIDE_FIRMWARE_NAME
254    ${SCP_ENABLE_OVERRIDE_FIRMWARE_NAME_INIT}
255    CACHE STRING "Override firmware binary name")
256
257cmake_dependent_option(
258    SCP_ENABLE_SUB_SYSTEM_MODE "Enable the execution as a sub-system?"
259    "${SCP_ENABLE_SUB_SYSTEM_MODE_INIT}"
260    "DEFINED SCP_ENABLE_SUB_SYSTEM_MODE_INIT"
261    "${SCP_ENABLE_SUB_SYSTEM_MODE}")
262
263cmake_dependent_option(
264    SCP_ENABLE_NOTIFICATIONS "Enable the notification subsystem?"
265    "${SCP_ENABLE_NOTIFICATIONS_INIT}" "DEFINED SCP_ENABLE_NOTIFICATIONS_INIT"
266    "${SCP_ENABLE_NOTIFICATIONS}")
267
268cmake_dependent_option(
269    SCP_ENABLE_OUTBAND_MSG_SUPPORT
270    "Enable Transport Out Band Message Support?"
271    "${SCP_ENABLE_OUTBAND_MSG_SUPPORT_INIT}"
272    "DEFINED SCP_ENABLE_OUTBAND_MSG_SUPPORT_INIT"
273    "${SCP_ENABLE_OUTBAND_MSG_SUPPORT}")
274
275cmake_dependent_option(
276    SCP_ENABLE_RESOURCE_PERMISSIONS
277    "Enable the resource permission support?"
278    "${SCP_ENABLE_RESOURCE_PERMISSIONS_INIT}"
279    "DEFINED SCP_ENABLE_RESOURCE_PERMISSIONS_INIT"
280    "${SCP_ENABLE_RESOURCE_PERMISSIONS}")
281
282cmake_dependent_option(
283    SCP_ENABLE_SCMI_NOTIFICATIONS
284    "Enable the SCMI notifications?"
285    "${SCP_ENABLE_SCMI_NOTIFICATIONS_INIT}"
286    "DEFINED SCP_ENABLE_SCMI_NOTIFICATIONS_INIT"
287    "${SCP_ENABLE_SCMI_NOTIFICATIONS}")
288
289cmake_dependent_option(
290    SCP_ENABLE_SCMI_SENSOR_EVENTS
291    "Enable the SCMI sensor events?"
292    "${SCP_ENABLE_SCMI_SENSOR_EVENTS_INIT}"
293    "DEFINED SCP_ENABLE_SCMI_SENSOR_EVENTS_INIT"
294    "${SCP_ENABLE_SCMI_SENSOR_EVENTS}")
295
296cmake_dependent_option(
297    SCP_ENABLE_FAST_CHANNELS
298    "Enable the transport Fast Channels?"
299    "${SCP_ENABLE_FAST_CHANNELS_INIT}"
300    "DEFINED SCP_ENABLE_FAST_CHANNELS_INIT"
301    "${SCP_ENABLE_FAST_CHANNELS}")
302
303# Include firmware specific build options
304include("${SCP_FIRMWARE_SOURCE_DIR}/Buildoptions.cmake" OPTIONAL)
305
306#
307# Wrap `add_executable` in a way that allows us to do some extra processing on
308# the firmware target.(e.g. flat binary generation) This is necessary almost
309# exclusively because  `add_custom_command` cannot be used with a target created
310# in a different directory.
311#
312
313# cmake-lint: disable=C0103,C0111
314
315macro(add_executable target)
316    _add_executable(${target} ${ARGN})
317
318    if("${target}" STREQUAL "${SCP_FIRMWARE_TARGET}")
319        if(SCP_GENERATE_FLAT_BINARY)
320            #
321            # Invoke 'objcopy' or 'fromelf', which we use to generate a flat
322            # binary for the generated firmware image. We make this optional
323            # because some binaries consist of disjoint sections far apart from
324            # one another, which can generate huge binaries.
325            #
326
327            if(SCP_FROMELF)
328                # cmake-format: off
329                add_custom_command(
330                    TARGET ${target} POST_BUILD
331                    COMMAND ${SCP_FROMELF}
332                    ARGS
333                        --bin "$<TARGET_FILE:${target}>"
334                        --output "$<TARGET_FILE_DIR:${target}>/$<TARGET_PROPERTY:${target},OUTPUT_NAME>.bin"
335                    COMMENT "Generating flat binary ${target}.bin")
336                # cmake-format: on
337            elseif(CMAKE_OBJCOPY)
338                # cmake-format: off
339                add_custom_command(
340                    TARGET ${target} POST_BUILD
341                    COMMAND ${CMAKE_OBJCOPY}
342                    ARGS -O binary
343                        "$<TARGET_FILE:${target}>"
344                        "$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_BASE_NAME:${target}>.bin"
345                    COMMENT "Generating flat binary ${target}.bin")
346                # cmake-format: on
347            endif()
348        endif()
349    endif()
350endmacro()
351
352if(SCP_FIRMWARE)
353    #
354    # Pull in the CMSIS package.
355    #
356
357    add_subdirectory("contrib/cmsis" EXCLUDE_FROM_ALL)
358
359    #
360    # Load in the firmware. We do this now so that the firmware list files can
361    # make adjustments to any modules it wishes to load in, configure the
362    # architecture library, etc.
363    #
364
365    add_subdirectory("${SCP_FIRMWARE_SOURCE_DIR}" "${SCP_FIRMWARE_BINARY_DIR}"
366                     EXCLUDE_FROM_ALL)
367
368    #
369    # Generate a map file for the firmware.
370    #
371
372    if(CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
373        target_link_options(
374            ${SCP_FIRMWARE_TARGET}
375            PRIVATE "LINKER:--map"
376                    "LINKER:--list=$<TARGET_FILE_DIR:${SCP_FIRMWARE_TARGET}>/$<TARGET_FILE_BASE_NAME:${SCP_FIRMWARE_TARGET}>.map")
377    else()
378        target_link_options(
379            ${SCP_FIRMWARE_TARGET}
380            PRIVATE "LINKER:--cref"
381                    "LINKER:-Map=$<TARGET_FILE_DIR:${SCP_FIRMWARE_TARGET}>/$<TARGET_FILE_BASE_NAME:${SCP_FIRMWARE_TARGET}>.map")
382    endif()
383
384    #
385    # Make the firmware target a part of the 'all' pseudo-target and give it a
386    # fixed output binary name so that different toolchains don't generate
387    # different names (which is desirable especially for higher-level build
388    # systems).
389    #
390
391    if("${SCP_ENABLE_OVERRIDE_FIRMWARE_NAME}" STREQUAL "")
392        set(SCP_FIRMWARE_NAME ${SCP_FIRMWARE_TARGET})
393    else()
394        set(SCP_FIRMWARE_NAME ${SCP_ENABLE_OVERRIDE_FIRMWARE_NAME})
395    endif()
396
397    set_target_properties(
398        ${SCP_FIRMWARE_TARGET}
399        PROPERTIES EXCLUDE_FROM_ALL FALSE
400                   OUTPUT_NAME "${SCP_FIRMWARE_NAME}"
401                   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
402
403    set_target_properties(
404        ${SCP_FIRMWARE_TARGET}
405        PROPERTIES EXCLUDE_FROM_ALL FALSE
406        SUFFIX ".elf")
407    #
408    # Load in the CLI debugger if it was requested.
409    #
410
411    cmake_dependent_option(
412        SCP_ENABLE_DEBUGGER "Enable the debugger-cli subsystem?"
413        "${SCP_ENABLE_DEBUGGER_INIT}" "DEFINED SCP_ENABLE_DEBUGGER_INIT"
414        "${SCP_ENABLE_DEBUGGER}")
415
416    if(SCP_ENABLE_DEBUGGER)
417        add_subdirectory("debugger" EXCLUDE_FROM_ALL)
418    endif()
419
420    #
421    # Load in the architecture support library.
422    #
423
424    add_subdirectory("arch" EXCLUDE_FROM_ALL)
425
426    #
427    # Load in the modules specified, plus any that need to be included to
428    # support other modules.
429    #
430
431    add_subdirectory("module" EXCLUDE_FROM_ALL)
432
433    #
434    # Load in the framework. This should be the last of the targets we need.
435    #
436
437    add_subdirectory("framework" EXCLUDE_FROM_ALL)
438
439    #
440    # Collect up all the source files used to build the various targets created
441    # above.
442    #
443
444    # cmake-format: off
445
446    string(APPEND scp_sources "$<TARGET_PROPERTY:framework,INTERFACE_SOURCES>$<SEMICOLON>")
447    string(APPEND scp_sources "$<TARGET_PROPERTY:framework,SOURCES>$<SEMICOLON>")
448    string(APPEND scp_sources "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},INTERFACE_SOURCES>$<SEMICOLON>")
449    string(APPEND scp_sources "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},SOURCES>$<SEMICOLON>")
450    string(APPEND scp_sources "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},INTERFACE_SOURCES>$<SEMICOLON>")
451    string(APPEND scp_sources "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},SOURCES>$<SEMICOLON>")
452
453    string(APPEND scp_includes "$<TARGET_PROPERTY:framework,INTERFACE_INCLUDE_DIRECTORIES>$<SEMICOLON>")
454    string(APPEND scp_includes "$<TARGET_PROPERTY:framework,INCLUDE_DIRECTORIES>$<SEMICOLON>")
455    string(APPEND scp_includes "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},INTERFACE_INCLUDE_DIRECTORIES>$<SEMICOLON>")
456    string(APPEND scp_includes "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},INCLUDE_DIRECTORIES>$<SEMICOLON>")
457    string(APPEND scp_includes "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},INTERFACE_INCLUDE_DIRECTORIES>$<SEMICOLON>")
458    string(APPEND scp_includes "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},INCLUDE_DIRECTORIES>$<SEMICOLON>")
459
460    string(APPEND scp_defines "$<TARGET_PROPERTY:framework,INTERFACE_COMPILE_DEFINITIONS>$<SEMICOLON>")
461    string(APPEND scp_defines "$<TARGET_PROPERTY:framework,COMPILE_DEFINITIONS>$<SEMICOLON>")
462    string(APPEND scp_defines "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},INTERFACE_COMPILE_DEFINITIONS>$<SEMICOLON>")
463    string(APPEND scp_defines "$<TARGET_PROPERTY:${SCP_ARCHITECTURE_TARGET},COMPILE_DEFINITIONS>$<SEMICOLON>" )
464    string(APPEND scp_defines "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},INTERFACE_COMPILE_DEFINITIONS>$<SEMICOLON>")
465    string(APPEND scp_defines "$<TARGET_PROPERTY:${SCP_FIRMWARE_TARGET},COMPILE_DEFINITIONS>$<SEMICOLON>")
466
467    list(APPEND scp_libraries "$<TARGET_FILE:framework>")
468    list(APPEND scp_libraries "$<TARGET_FILE:${SCP_ARCHITECTURE_TARGET}>")
469    list(APPEND scp_libraries "$<TARGET_FILE:${SCP_FIRMWARE_TARGET}>")
470
471    foreach(module IN LISTS SCP_MODULE_TARGETS)
472        string(APPEND scp_sources "$<TARGET_PROPERTY:${module},INTERFACE_SOURCES>$<SEMICOLON>")
473        string(APPEND scp_sources "$<TARGET_PROPERTY:${module},SOURCES>$<SEMICOLON>")
474
475        string(APPEND scp_includes "$<TARGET_PROPERTY:${module},INTERFACE_INCLUDE_DIRECTORIES>$<SEMICOLON>")
476        string(APPEND scp_includes "$<TARGET_PROPERTY:${module},INCLUDE_DIRECTORIES>$<SEMICOLON>")
477
478        string(APPEND scp_defines "$<TARGET_PROPERTY:${module},INTERFACE_COMPILE_DEFINITIONS>$<SEMICOLON>")
479        string(APPEND scp_defines "$<TARGET_PROPERTY:${module},COMPILE_DEFINITIONS>$<SEMICOLON>")
480
481        list(APPEND scp_libraries "$<TARGET_FILE:${module}>")
482    endforeach()
483
484    set(scp_sources $<REMOVE_DUPLICATES:${scp_sources}>)
485    set(scp_includes $<REMOVE_DUPLICATES:${scp_includes}>)
486    set(scp_defines $<REMOVE_DUPLICATES:${scp_defines}>)
487
488    # cmake-format: on
489
490    #
491    # Load in the documentation generation.
492    #
493    add_subdirectory("doc" EXCLUDE_FROM_ALL)
494endif()
495
496
497set(SCP_FIRMWARE_LIB "${SCP_FIRMWARE_TARGET}-all")
498
499# merged libraries is only built when `make ${SCP_FIRMWARE_LIB}` is issued
500add_custom_target(${SCP_FIRMWARE_LIB}
501    # COMMAND/COMMENT must be upper case
502        COMMAND ${CMAKE_AR} rcT $<TARGET_FILE_DIR:${SCP_FIRMWARE_TARGET}>/lib${SCP_FIRMWARE_LIB}.a ${scp_libraries}
503        COMMENT " Merging all libraries in a single lib${SCP_FIRMWARE_LIB}.a"
504)
505
506add_dependencies(${SCP_FIRMWARE_LIB} ${SCP_FIRMWARE_TARGET})
507
508#
509# Set up global inclusions and exclusions for source file quality assurance
510# tools. This is intended to filter in external directories (e.g. out-of-tree
511# modules) and filter out third-party directories.
512#
513
514list(APPEND glob_includes "${SCP_SOURCE_DIR}")
515
516list(APPEND glob_excludes "^${SCP_SOURCE_DIR}/contrib/cmsis/git")
517
518list(APPEND cmake_globs "CMakeLists.txt")
519list(APPEND cmake_globs "*.cmake")
520
521#
522# Glob sources and place them in their respective variables. Globs for source
523# types can be with specified with a `${type}_globs` variable, which should be a
524# list of globs to be fed into `file(GLOB_RECURSE ...)`.
525#
526
527# cmake-lint: disable=C0103
528
529foreach(type cmake)
530    unset(sources)
531
532    foreach(include IN LISTS glob_includes)
533        foreach(glob IN LISTS ${type}_globs)
534            file(GLOB_RECURSE _sources "${include}/${glob}")
535            list(APPEND sources ${_sources})
536        endforeach()
537    endforeach()
538
539    foreach(exclude IN LISTS glob_excludes)
540        list(FILTER sources EXCLUDE REGEX "${exclude}")
541    endforeach()
542
543    set(${type}_sources ${sources})
544endforeach()
545
546#
547# Configure cmake-format.
548#
549
550find_package(CMakeFormat OPTIONAL_COMPONENTS Format Lint)
551
552if(TARGET CMakeFormat::Format)
553    add_custom_target(
554        format-cmake
555        COMMAND CMakeFormat::Format -i "${cmake_sources}"
556        WORKING_DIRECTORY "${SCP_SOURCE_DIR}"
557        COMMENT "Formatting CMake sources..."
558        COMMAND_EXPAND_LISTS)
559
560    list(APPEND format_targets "format-cmake")
561
562    add_custom_target(
563        check-cmake
564        COMMAND CMakeFormat::Format --check "${cmake_sources}"
565        WORKING_DIRECTORY "${SCP_SOURCE_DIR}"
566        COMMENT "Checking CMake sources..."
567        COMMAND_EXPAND_LISTS)
568
569    list(APPEND check_targets "check-cmake")
570endif()
571
572if(TARGET CMakeFormat::Lint)
573    add_custom_target(
574        lint-cmake
575        COMMAND CMakeFormat::Lint --suppress-decorations "${cmake_sources}"
576        WORKING_DIRECTORY "${SCP_SOURCE_DIR}"
577        COMMENT "Linting CMake sources..."
578        COMMAND_EXPAND_LISTS)
579
580    list(APPEND lint_targets "lint-cmake")
581endif()
582
583#
584# Create the final check targets. These targets consist of miscellaneous quality
585# assurance tasks like linting and formatting, and act as dummy targets that
586# invoke the various tools we set up above.
587#
588
589add_custom_target(
590    format
591    DEPENDS ${format_targets}
592    COMMENT "Formatting all sources...")
593
594add_custom_target(
595    lint
596    DEPENDS ${lint_targets}
597    COMMENT "Linting all sources...")
598
599add_custom_target(
600    check
601    DEPENDS ${check_targets} lint
602    COMMENT "Checking lint...")
603