1# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and
2# other BLT Project Developers. See the top-level COPYRIGHT file for details
3#
4# SPDX-License-Identifier: (BSD-3-Clause)
5#------------------------------------------------------------------------------
6# Targets related to source code checks (formatting, static analysis, etc)
7#------------------------------------------------------------------------------
8
9add_custom_target(${BLT_CODE_CHECK_TARGET_NAME})
10add_custom_target(${BLT_CODE_STYLE_TARGET_NAME})
11
12if(ASTYLE_FOUND)
13    # targets for verifying formatting
14    add_custom_target(astyle_check)
15    add_dependencies(${BLT_CODE_CHECK_TARGET_NAME} astyle_check)
16
17    # targets for modifying formatting
18    add_custom_target(astyle_style)
19    add_dependencies(${BLT_CODE_STYLE_TARGET_NAME} astyle_style)
20endif()
21
22if(CLANGFORMAT_FOUND)
23    # targets for verifying formatting
24    add_custom_target(clangformat_check)
25    add_dependencies(${BLT_CODE_CHECK_TARGET_NAME} clangformat_check)
26
27    # targets for modifying formatting
28    add_custom_target(clangformat_style)
29    add_dependencies(${BLT_CODE_STYLE_TARGET_NAME} clangformat_style)
30endif()
31
32if(UNCRUSTIFY_FOUND)
33    # targets for verifying formatting
34    add_custom_target(uncrustify_check)
35    add_dependencies(${BLT_CODE_CHECK_TARGET_NAME} uncrustify_check)
36
37    # targets for modifying formatting
38    add_custom_target(uncrustify_style)
39    add_dependencies(${BLT_CODE_STYLE_TARGET_NAME} uncrustify_style)
40endif()
41
42if(CPPCHECK_FOUND)
43    add_custom_target(cppcheck_check)
44    add_dependencies(${BLT_CODE_CHECK_TARGET_NAME} cppcheck_check)
45endif()
46
47if(CLANGQUERY_FOUND)
48    # note: interactive_clang_query_check
49    # is for the use of code developers who
50    # want to check specific attributes of
51    # specific targets, and does not make
52    # sense as a dependency of check
53    add_custom_target(clang_query_check)
54    add_custom_target(interactive_clang_query_check)
55    add_dependencies(${BLT_CODE_CHECK_TARGET_NAME} clang_query_check)
56endif()
57
58# Code check targets should only be run on demand
59foreach(target
60        check uncrustify_check astyle_check clangformat_check cppcheck_check
61        style uncrustify_style astyle_style clangformat_style
62        clang_query_check interactive_clang_query_check)
63    if(TARGET ${target})
64        set_property(TARGET ${target} PROPERTY EXCLUDE_FROM_ALL TRUE)
65        set_property(TARGET ${target} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
66    endif()
67endforeach()
68
69
70##------------------------------------------------------------------------------
71## blt_add_code_checks( PREFIX               <Base name used for created targets>
72##                      SOURCES              [source1 [source2 ...]]
73##                      ASTYLE_CFG_FILE      <Path to AStyle config file>
74##                      CLANGFORMAT_CFG_FILE <Path to ClangFormat config file>
75##                      UNCRUSTIFY_CFG_FILE  <Path to Uncrustify config file>
76##                      CPPCHECK_FLAGS       <List of flags added to Cppcheck>)
77##
78## This macro adds all enabled code check targets for the given SOURCES. It
79## filters checks based on file extensions.
80##------------------------------------------------------------------------------
81
82macro(blt_add_code_checks)
83
84    set(options )
85    set(singleValueArgs PREFIX ASTYLE_CFG_FILE CLANGFORMAT_CFG_FILE UNCRUSTIFY_CFG_FILE)
86    set(multiValueArgs SOURCES CPPCHECK_FLAGS)
87
88    cmake_parse_arguments(arg
89        "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN})
90
91    if (NOT DEFINED arg_PREFIX)
92        message(FATAL_ERROR "blt_add_code_checks requires the parameter PREFIX.")
93    endif()
94
95    if (NOT DEFINED arg_SOURCES)
96        message(FATAL_ERROR "blt_add_code_checks requires the parameter SOURCES.")
97    endif()
98
99    # Make the sources relative to the bin directory
100    set(_rel_sources)
101    foreach(_file ${arg_SOURCES})
102        # Get full path
103        if(IS_ABSOLUTE ${_file})
104            set(_full_path ${_file})
105        else()
106            set(_full_path ${CMAKE_CURRENT_SOURCE_DIR}/${_file})
107        endif()
108
109        file(RELATIVE_PATH _rel_path ${CMAKE_BINARY_DIR} ${_full_path})
110        list(APPEND _rel_sources ${_rel_path})
111    endforeach()
112
113    # Generate source lists based on language
114    set(_c_sources)
115    set(_f_sources)
116    blt_split_source_list_by_language(SOURCES      ${_rel_sources}
117                                      C_LIST       _c_sources
118                                      Fortran_LIST _f_sources)
119
120    # Check that at most one formatting config file was supplied
121    if (DEFINED arg_UNCRUSTIFY_CFG_FILE AND DEFINED arg_ASTYLE_CFG_FILE)
122        message(FATAL_ERROR
123          "blt_add_code_checks macro does not support multiple "
124          "style config parameters within the same invocation. "
125          "Both UNCRUSTIFY_CFG_FILE and ASTYLE_CFG_FILE were supplied.")
126    endif()
127
128    # Add code checks
129    set(_error_msg "blt_add_code_checks tried to create an already existing target with given PREFIX: ${arg_PREFIX}. ")
130
131    if (ASTYLE_FOUND AND DEFINED arg_ASTYLE_CFG_FILE)
132        set(_check_target_name ${arg_PREFIX}_astyle_check)
133        blt_error_if_target_exists(${_check_target_name} ${_error_msg})
134        set(_style_target_name ${arg_PREFIX}_astyle_style)
135        blt_error_if_target_exists(${_style_target_name} ${_error_msg})
136
137        blt_add_astyle_target( NAME              ${_check_target_name}
138                               MODIFY_FILES      FALSE
139                               CFG_FILE          ${arg_ASTYLE_CFG_FILE}
140                               WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
141                               SRC_FILES         ${_c_sources} )
142
143        blt_add_astyle_target( NAME              ${_style_target_name}
144                               MODIFY_FILES      TRUE
145                               CFG_FILE          ${arg_ASTYLE_CFG_FILE}
146                               WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
147                               SRC_FILES         ${_c_sources} )
148    endif()
149
150    if (CLANGFORMAT_FOUND AND DEFINED arg_CLANGFORMAT_CFG_FILE)
151        set(_check_target_name ${arg_PREFIX}_clangformat_check)
152        blt_error_if_target_exists(${_check_target_name} ${_error_msg})
153        set(_style_target_name ${arg_PREFIX}_clangformat_style)
154        blt_error_if_target_exists(${_style_target_name} ${_error_msg})
155
156        blt_add_clangformat_target( NAME              ${_check_target_name}
157                                    MODIFY_FILES      FALSE
158                                    CFG_FILE          ${arg_CLANGFORMAT_CFG_FILE}
159                                    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
160                                    SRC_FILES         ${_c_sources} )
161
162        blt_add_clangformat_target( NAME              ${_style_target_name}
163                                    MODIFY_FILES      TRUE
164                                    CFG_FILE          ${arg_CLANGFORMAT_CFG_FILE}
165                                    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
166                                    SRC_FILES         ${_c_sources} )
167    endif()
168
169    if (UNCRUSTIFY_FOUND AND DEFINED arg_UNCRUSTIFY_CFG_FILE)
170        set(_check_target_name ${arg_PREFIX}_uncrustify_check)
171        blt_error_if_target_exists(${_check_target_name} ${_error_msg})
172        set(_style_target_name ${arg_PREFIX}_uncrustify_style)
173        blt_error_if_target_exists(${_style_target_name} ${_error_msg})
174
175        blt_add_uncrustify_target( NAME              ${_check_target_name}
176                                   MODIFY_FILES      FALSE
177                                   CFG_FILE          ${arg_UNCRUSTIFY_CFG_FILE}
178                                   WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
179                                   SRC_FILES         ${_c_sources} )
180
181        blt_add_uncrustify_target( NAME              ${_style_target_name}
182                                   MODIFY_FILES      TRUE
183                                   CFG_FILE          ${arg_UNCRUSTIFY_CFG_FILE}
184                                   WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
185                                   SRC_FILES         ${_c_sources} )
186    endif()
187
188    if (CPPCHECK_FOUND)
189        set(_cppcheck_target_name ${arg_PREFIX}_cppcheck_check)
190        blt_error_if_target_exists(${_cppcheck_target_name} ${_error_msg})
191
192        blt_add_cppcheck_target( NAME              ${_cppcheck_target_name}
193                                 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
194                                 SRC_FILES         ${_c_sources}
195                                 PREPEND_FLAGS     ${arg_CPPCHECK_FLAGS})
196    endif()
197
198    if (CLANGQUERY_FOUND)
199        set(_clang_query_target_name ${arg_PREFIX}_clang_query_check)
200        blt_error_if_target_exists(${_clang_query_target_name} ${_error_msg})
201        blt_add_clang_query_target( NAME              ${_clang_query_target_name}
202                                    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
203                                    SRC_FILES         ${_c_sources})
204    endif()
205
206endmacro(blt_add_code_checks)
207
208##-----------------------------------------------------------------------------
209## blt_add_clang_query_target( NAME              <Created Target Name>
210##                             WORKING_DIRECTORY <Working Directory>
211##                             COMMENT           <Additional Comment for Target Invocation>
212##                             CHECKERS          <If specified, requires a specific set of checkers>
213##                             DIE_ON_MATCH      <If true, matches stop the build>
214##                             SRC_FILES         [FILE1 [FILE2 ...]] )
215##
216## Creates a new target with the given NAME for running clang_query over the given SRC_FILES
217##-----------------------------------------------------------------------------
218macro(blt_add_clang_query_target)
219    if(CLANGQUERY_FOUND)
220
221        ## parse the arguments to the macro
222        set(options)
223        set(singleValueArgs NAME COMMENT WORKING_DIRECTORY DIE_ON_MATCH)
224        set(multiValueArgs SRC_FILES CHECKERS)
225
226        cmake_parse_arguments(arg
227            "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} )
228
229        # Check required parameters
230        if(NOT DEFINED arg_NAME)
231             message(FATAL_ERROR "blt_add_clang_query_target requires a NAME parameter")
232        endif()
233
234        if(NOT DEFINED arg_SRC_FILES)
235            message(FATAL_ERROR "blt_add_clang_query_target requires a SRC_FILES parameter")
236        endif()
237
238        if(DEFINED arg_WORKING_DIRECTORY)
239            set(_wd ${arg_WORKING_DIRECTORY})
240        else()
241            set(_wd ${CMAKE_CURRENT_SOURCE_DIR})
242        endif()
243
244        set(interactive_target_name interactive_${arg_NAME})
245        set(CLANG_QUERY_HELPER_SCRIPT ${BLT_ROOT_DIR}/cmake/clang-query-wrapper.py)
246        set(CLANG_QUERY_HELPER_COMMAND python ${CLANG_QUERY_HELPER_SCRIPT} --clang-query ${CLANGQUERY_EXECUTABLE} --checker-directories ${BLT_CLANG_QUERY_CHECKER_DIRECTORIES} --compilation-database-path ${CMAKE_BINARY_DIR})
247
248        if(arg_DIE_ON_MATCH)
249            set(CLANG_QUERY_HELPER_COMMAND ${CLANG_QUERY_HELPER_COMMAND} --die-on-match)
250        endif()
251
252        if(DEFINED arg_CHECKERS)
253            STRING(REGEX REPLACE " " ":" CHECKER_ARG_STRING ${arg_CHECKERS})
254            add_custom_target(${arg_NAME}
255              COMMAND ${CLANG_QUERY_HELPER_COMMAND} -i --checkers=${CHECKER_ARG_STRING} ${arg_SRC_FILES}
256                    WORKING_DIRECTORY ${_wd}
257                    COMMENT "${arg_COMMENT}Running specified clang_query source code static analysis checks.")
258        else() #DEFINED CHECKERS
259            add_custom_target(${arg_NAME}
260              COMMAND ${CLANG_QUERY_HELPER_COMMAND} ${arg_SRC_FILES}
261                    WORKING_DIRECTORY ${_wd}
262                    COMMENT "${arg_COMMENT}Running all clang_query source code static analysis checks.")
263        endif()
264
265        add_custom_target(${interactive_target_name}
266          COMMAND ${CLANG_QUERY_HELPER_COMMAND} -i ${arg_SRC_FILES}
267                WORKING_DIRECTORY ${_wd}
268                COMMENT "${arg_COMMENT}Running clang_query source code static analysis checks.")
269
270        # hook our new target into the proper dependency chain
271        add_dependencies(clang_query_check ${arg_NAME})
272        add_dependencies(interactive_clang_query_check ${interactive_target_name})
273
274        # Code check targets should only be run on demand
275        set_property(TARGET ${interactive_target_name} PROPERTY EXCLUDE_FROM_ALL TRUE)
276        set_property(TARGET ${interactive_target_name} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
277        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_ALL TRUE)
278        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
279    endif()
280endmacro(blt_add_clang_query_target)
281
282
283##-----------------------------------------------------------------------------
284## blt_add_cppcheck_target( NAME                <Created Target Name>
285##                          WORKING_DIRECTORY   <Working Directory>
286##                          PREPEND_FLAGS       <additional flags for cppcheck>
287##                          APPEND_FLAGS        <additional flags for cppcheck>
288##                          COMMENT             <Additional Comment for Target Invocation>
289##                          SRC_FILES           [FILE1 [FILE2 ...]] )
290##
291## Creates a new target with the given NAME for running cppcheck over the given SRC_FILES
292##-----------------------------------------------------------------------------
293macro(blt_add_cppcheck_target)
294
295    ## parse the arguments to the macro
296    set(options)
297    set(singleValueArgs NAME COMMENT WORKING_DIRECTORY)
298    set(multiValueArgs SRC_FILES PREPEND_FLAGS APPEND_FLAGS)
299
300    cmake_parse_arguments(arg
301        "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} )
302
303    # Check required parameters
304    if(NOT DEFINED arg_NAME)
305        message(FATAL_ERROR "blt_add_cppcheck_target requires a NAME parameter")
306    endif()
307
308    if(NOT DEFINED arg_SRC_FILES)
309        message(FATAL_ERROR "blt_add_cppcheck_target requires a SRC_FILES parameter")
310    endif()
311
312    if(DEFINED arg_WORKING_DIRECTORY)
313        set(_wd ${arg_WORKING_DIRECTORY})
314    else()
315        set(_wd ${CMAKE_CURRENT_SOURCE_DIR})
316    endif()
317
318    add_custom_target(${arg_NAME}
319            COMMAND ${CPPCHECK_EXECUTABLE} ${arg_PREPEND_FLAGS} ${arg_SRC_FILES} ${arg_APPEND_FLAGS}
320            WORKING_DIRECTORY ${_wd}
321            COMMENT "${arg_COMMENT}Running cppcheck source code static analysis checks.")
322
323    # hook our new target into the proper dependency chain
324    add_dependencies(cppcheck_check ${arg_NAME})
325
326    # Code check targets should only be run on demand
327    set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_ALL TRUE)
328    set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
329endmacro(blt_add_cppcheck_target)
330
331
332##------------------------------------------------------------------------------
333## blt_add_astyle_target( NAME              <Created Target Name>
334##                        MODIFY_FILES      [TRUE | FALSE (default)]
335##                        CFG_FILE          <AStyle Configuration File>
336##                        PREPEND_FLAGS     <Additional Flags to AStyle>
337##                        APPEND_FLAGS      <Additional Flags to AStyle>
338##                        COMMENT           <Additional Comment for Target Invocation>
339##                        WORKING_DIRECTORY <Working Directory>
340##                        SRC_FILES         [FILE1 [FILE2 ...]] )
341##
342## Creates a new target with the given NAME for running astyle over the given SRC_FILES.
343##------------------------------------------------------------------------------
344macro(blt_add_astyle_target)
345
346    ## parse the arguments to the macro
347    set(options)
348    set(singleValueArgs NAME MODIFY_FILES CFG_FILE COMMENT WORKING_DIRECTORY)
349    set(multiValueArgs SRC_FILES PREPEND_FLAGS APPEND_FLAGS)
350
351    cmake_parse_arguments(arg
352        "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} )
353
354    # Check/Set required parameters
355    if(NOT DEFINED arg_NAME)
356        message(FATAL_ERROR "blt_add_astyle_target requires a NAME parameter")
357    endif()
358
359    if(NOT DEFINED arg_CFG_FILE)
360        message(FATAL_ERROR "blt_add_astyle_target requires a CFG_FILE parameter")
361    endif()
362
363    if(NOT DEFINED arg_SRC_FILES)
364        message(FATAL_ERROR "blt_add_astyle_target requires a SRC_FILES parameter")
365    endif()
366
367    if(NOT DEFINED arg_MODIFY_FILES)
368        set(arg_MODIFY_FILES FALSE)
369    endif()
370
371    if(DEFINED arg_WORKING_DIRECTORY)
372        set(_wd ${arg_WORKING_DIRECTORY})
373    else()
374        set(_wd ${CMAKE_CURRENT_SOURCE_DIR})
375    endif()
376
377    set(_generate_target TRUE)
378
379    if(${arg_MODIFY_FILES})
380        set(MODIFY_FILES_FLAG --suffix=none)
381    else()
382        set(MODIFY_FILES_FLAG --dry-run)
383
384        # Check the version -- output is of the form "Artistic Style Version X.Y.Z"
385        execute_process(
386            COMMAND ${ASTYLE_EXECUTABLE} --version
387            OUTPUT_VARIABLE _version_str
388            ERROR_VARIABLE  _version_str
389            OUTPUT_STRIP_TRAILING_WHITESPACE )
390        string(REGEX MATCH "([0-9]+(\\.)?)+$" _astyle_version ${_version_str})
391
392        # Skip 'check' target if version is not high enough
393        if(_astyle_version VERSION_LESS 2.05)
394            set(_generate_target FALSE)
395            message(WARNING "blt_add_astyle_target requires AStyle v2.05 or greater "
396                            " for style check targets. "
397                            " Current AStyle executable: '${ASTYLE_EXECUTABLE}' "
398                            " Current AStyle version is: ${_astyle_version}."    )
399        endif()
400    endif()
401
402    if(_generate_target)
403
404        # AStyle doesn't report failure when there are files that require formatting.
405        # Fix this with a wrapper script that parses the output.
406        set(wrapped_astyle_script ${CMAKE_CURRENT_BINARY_DIR}/WrapAstyle_${arg_NAME}.cmake)
407
408        configure_file(
409            ${BLT_ROOT_DIR}/cmake/WrapAstyle.cmake.in
410            ${wrapped_astyle_script}
411            @ONLY )
412
413        add_custom_target(
414            ${arg_NAME}
415            COMMAND ${CMAKE_COMMAND} -P ${wrapped_astyle_script}
416            WORKING_DIRECTORY ${_wd}
417            COMMENT "${arg_COMMENT}Running AStyle source code formatting checks.")
418
419        # Hook our new target into the proper dependency chain
420        if(${arg_MODIFY_FILES})
421            add_dependencies(astyle_style ${arg_NAME})
422        else()
423            add_dependencies(astyle_check ${arg_NAME})
424        endif()
425
426        # Code formatting targets should only be run on demand
427        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_ALL TRUE)
428        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
429    endif()
430endmacro(blt_add_astyle_target)
431
432##------------------------------------------------------------------------------
433## blt_add_clangformat_target( NAME              <Created Target Name>
434##                             MODIFY_FILES      [TRUE | FALSE (default)]
435##                             CFG_FILE          <ClangFormat Configuration File>
436##                             PREPEND_FLAGS     <Additional Flags to ClangFormat>
437##                             APPEND_FLAGS      <Additional Flags to ClangFormat>
438##                             COMMENT           <Additional Comment for Target Invocation>
439##                             WORKING_DIRECTORY <Working Directory>
440##                             SRC_FILES         [FILE1 [FILE2 ...]] )
441##
442## Creates a new target with the given NAME for running ClangFormat over the given SRC_FILES.
443##------------------------------------------------------------------------------
444macro(blt_add_clangformat_target)
445
446    ## parse the arguments to the macro
447    set(options)
448    set(singleValueArgs NAME MODIFY_FILES CFG_FILE COMMENT WORKING_DIRECTORY)
449    set(multiValueArgs SRC_FILES PREPEND_FLAGS APPEND_FLAGS)
450
451    cmake_parse_arguments(arg
452        "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} )
453
454    # Check/Set required parameters
455    if(NOT DEFINED arg_NAME)
456        message(FATAL_ERROR "blt_add_clangformat_target requires a NAME parameter")
457    endif()
458
459    if(NOT DEFINED arg_CFG_FILE)
460        message(FATAL_ERROR "blt_add_clangformat_target requires a CFG_FILE parameter")
461    endif()
462
463    if(NOT DEFINED arg_SRC_FILES)
464        message(FATAL_ERROR "blt_add_clangformat_target requires a SRC_FILES parameter")
465    endif()
466
467    if(NOT DEFINED arg_MODIFY_FILES)
468        set(arg_MODIFY_FILES FALSE)
469    endif()
470
471    if(DEFINED arg_WORKING_DIRECTORY)
472        set(_wd ${arg_WORKING_DIRECTORY})
473    else()
474        set(_wd ${CMAKE_CURRENT_SOURCE_DIR})
475    endif()
476
477    set(_generate_target TRUE)
478
479    # Copy config file to given working directory since ClangFormat doesn't support pointing to one
480    configure_file(${arg_CFG_FILE} ${arg_WORKING_DIRECTORY}/.clang-format COPYONLY)
481
482    if(_generate_target)
483        # ClangFormat does not support --dry-run until version 10 which isn't on many machines.
484        # For now, use run-clang-format for dry running purposes.
485        if(${arg_MODIFY_FILES})
486            add_custom_target(${arg_NAME}
487                    COMMAND  ${CLANGFORMAT_EXECUTABLE} ${arg_PREPEND_FLAGS}
488                        --style=file -i ${arg_SRC_FILES} ${arg_APPEND_FLAGS}
489                    WORKING_DIRECTORY ${_wd}
490                    COMMENT "${arg_COMMENT}Running ClangFormat source code formatting checks.")
491        else()
492            set(_run_clangformat "${BLT_ROOT_DIR}/cmake/run-clang-format.py" --clang-format-executable ${CLANGFORMAT_EXECUTABLE})
493            add_custom_target(${arg_NAME}
494                    COMMAND ${_run_clangformat} -j1 ${arg_SRC_FILES}
495                    WORKING_DIRECTORY ${_wd}
496                    COMMENT "${arg_COMMENT}Running ClangFormat source code formatting checks.")
497
498        endif()
499
500        # Hook our new target into the proper dependency chain
501        if(${arg_MODIFY_FILES})
502            add_dependencies(clangformat_style ${arg_NAME})
503        else()
504            add_dependencies(clangformat_check ${arg_NAME})
505        endif()
506
507        # Code formatting targets should only be run on demand
508        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_ALL TRUE)
509        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
510    endif()
511endmacro(blt_add_clangformat_target)
512
513##------------------------------------------------------------------------------
514## blt_add_uncrustify_target( NAME              <Created Target Name>
515##                            MODIFY_FILES      [TRUE | FALSE (default)]
516##                            CFG_FILE          <Uncrustify Configuration File>
517##                            PREPEND_FLAGS     <Additional Flags to Uncrustify>
518##                            APPEND_FLAGS      <Additional Flags to Uncrustify>
519##                            COMMENT           <Additional Comment for Target Invocation>
520##                            WORKING_DIRECTORY <Working Directory>
521##                            SRC_FILES         [FILE1 [FILE2 ...]] )
522##
523## Creates a new target with the given NAME for running uncrustify over the given SRC_FILES.
524##------------------------------------------------------------------------------
525macro(blt_add_uncrustify_target)
526
527    ## parse the arguments to the macro
528    set(options)
529    set(singleValueArgs NAME MODIFY_FILES CFG_FILE COMMENT WORKING_DIRECTORY)
530    set(multiValueArgs SRC_FILES PREPEND_FLAGS APPEND_FLAGS)
531
532    cmake_parse_arguments(arg
533        "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN} )
534
535    # Check/Set required parameters
536    if(NOT DEFINED arg_NAME)
537        message(FATAL_ERROR "blt_add_uncrustify_target requires a NAME parameter")
538    endif()
539
540    if(NOT DEFINED arg_CFG_FILE)
541        message(FATAL_ERROR "blt_add_uncrustify_target requires a CFG_FILE parameter")
542    endif()
543
544    if(NOT DEFINED arg_SRC_FILES)
545        message(FATAL_ERROR "blt_add_uncrustify_target requires a SRC_FILES parameter")
546    endif()
547
548    if(NOT DEFINED arg_MODIFY_FILES)
549        set(arg_MODIFY_FILES FALSE)
550    endif()
551
552    if(DEFINED arg_WORKING_DIRECTORY)
553        set(_wd ${arg_WORKING_DIRECTORY})
554    else()
555        set(_wd ${CMAKE_CURRENT_SOURCE_DIR})
556    endif()
557
558    set(_generate_target TRUE)
559
560    if(${arg_MODIFY_FILES})
561        set(MODIFY_FILES_FLAG --replace;--no-backup)
562    else()
563        set(MODIFY_FILES_FLAG "--check")
564
565        # Check the version -- output is of the form "uncrustify X.Y.Z"
566        execute_process(
567            COMMAND ${UNCRUSTIFY_EXECUTABLE} --version
568            OUTPUT_VARIABLE _version_str
569            OUTPUT_STRIP_TRAILING_WHITESPACE )
570        string(REGEX MATCH "([0-9]+(\\.)?)+(_[a-zA-Z])?" _uncrustify_version ${_version_str})
571
572        # Skip 'check' target if version is not high enough
573        if(_uncrustify_version VERSION_LESS 0.61)
574            set(_generate_target FALSE)
575            message(WARNING "blt_add_uncrustify_target requires uncrustify v0.61 or greater "
576                            " for style check targets. "
577                            " Current uncrustify executable: '${UNCRUSTIFY_EXECUTABLE}' "
578                            " Current uncrustify version is: ${_uncrustify_version}."    )
579        endif()
580    endif()
581
582    if(_generate_target)
583        add_custom_target(${arg_NAME}
584                COMMAND ${UNCRUSTIFY_EXECUTABLE} ${arg_PREPEND_FLAGS}
585                    -c ${arg_CFG_FILE} ${MODIFY_FILES_FLAG} ${arg_SRC_FILES} ${arg_APPEND_FLAGS}
586                WORKING_DIRECTORY ${_wd}
587                COMMENT "${arg_COMMENT}Running uncrustify source code formatting checks.")
588
589        # hook our new target into the proper dependency chain
590        if(${arg_MODIFY_FILES})
591            add_dependencies(uncrustify_style ${arg_NAME})
592        else()
593            add_dependencies(uncrustify_check ${arg_NAME})
594        endif()
595
596        # Code formatting targets should only be run on demand
597        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_ALL TRUE)
598        set_property(TARGET ${arg_NAME} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD TRUE)
599    endif()
600
601endmacro(blt_add_uncrustify_target)
602