-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathClang.cmake
711 lines (632 loc) · 21.4 KB
/
Clang.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
cmake_minimum_required(VERSION 3.20)
set(CLANG_PATH "" CACHE PATH "Path to Clang Toolset (if not in environment)")
function(json_escape_string)
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
""
"OUTPUT;INPUT"
""
)
set(_tmp "${_ARGS_INPUT}")
string(REPLACE "\\" "\\\\" _tmp "${_tmp}")
string(REPLACE "\"" "\\\"" _tmp "${_tmp}")
set(${_ARGS_OUTPUT} "${_tmp}" PARENT_SCOPE)
endfunction()
function(get_target_include_directories)
unset(_ARGS)
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
"INTERFACE"
"TARGET;OUTPUT"
"IGNORE"
)
# FIXME: For some reason, CMake claims that the target we depend on depends on the target we are.
# Might be something broken with LINK_LIBRARIES or similar?
set(out "")
set(ignore "${_ARGS_IGNORE}")
list(APPEND ignore "${_ARGS_TARGET}")
set(gtid "")
if(_ARGS_INTERFACE)
get_property(gtid TARGET ${current_target} PROPERTY INTERFACE_LINK_LIBRARIES)
else()
get_property(gtid TARGET ${current_target} PROPERTY LINK_LIBRARIES)
endif()
foreach(_tmp ${gtid})
list(APPEND ignore "${_tmp}")
if((NOT "${_tmp}" IN_LIST _ARGS_IGNORE) AND (NOT "${_tmp}" STREQUAL "${_ARGS_TARGET}") AND (TARGET "${_tmp}"))
get_target_include_directories(INTERFACE OUTPUT cc_src_command TARGET "${_tmp}" IGNORE ${ignore})
foreach(_tmp3 ${cc_src_command})
list(APPEND out "${_tmp3}")
endforeach()
endif()
endforeach()
set(gtid_source_dir "")
get_property(gtid_source_dir TARGET ${_ARGS_TARGET} PROPERTY SOURCE_DIR)
set(gtid "")
if(_ARGS_INTERFACE)
get_property(gtid TARGET ${_ARGS_TARGET} PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
else()
get_property(gtid TARGET ${_ARGS_TARGET} PROPERTY INCLUDE_DIRECTORIES)
endif()
foreach(_tmp ${gtid})
cmake_path(ABSOLUTE_PATH _tmp BASE_DIRECTORY "${gtid_source_dir}")
list(APPEND out "${_tmp}")
endforeach()
if(_ARGS_INTERFACE)
set(gtid "")
get_property(gtid TARGET ${_ARGS_TARGET} PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
foreach(_tmp ${gtid})
cmake_path(ABSOLUTE_PATH _tmp BASE_DIRECTORY "${gtid_source_dir}")
list(APPEND out "${_tmp}")
endforeach()
endif()
set(${_ARGS_OUTPUT} "${out}" PARENT_SCOPE)
endfunction()
function(get_target_definitions)
unset(_ARGS)
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
"INTERFACE"
"TARGET;OUTPUT"
"IGNORE"
)
# FIXME: For some reason, CMake claims that the target we depend on depends on the target we are.
# Might be something broken with LINK_LIBRARIES or similar?
set(out "")
set(ignore "${_ARGS_IGNORE}")
list(APPEND ignore "${_ARGS_TARGET}")
set(gtd "")
if(_ARGS_INTERFACE)
get_property(gtd TARGET ${current_target} PROPERTY INTERFACE_LINK_LIBRARIES)
else()
get_property(gtd TARGET ${current_target} PROPERTY LINK_LIBRARIES)
endif()
foreach(_tmp ${gtd})
list(APPEND ignore "${_tmp}")
if((NOT "${_tmp}" IN_LIST _ARGS_IGNORE) AND (NOT "${_tmp}" STREQUAL "${_ARGS_TARGET}") AND (TARGET "${_tmp}"))
get_target_definitions(INTERFACE OUTPUT cc_src_command TARGET "${_tmp}" IGNORE ${ignore})
foreach(_tmp3 ${cc_src_command})
list(APPEND out "${_tmp3}")
endforeach()
endif()
endforeach()
set(gtid "")
if(_ARGS_INTERFACES)
get_property(gtid TARGET ${_ARGS_TARGET} PROPERTY INTERFACE_COMPILE_DEFINITIONS)
else()
get_property(gtid TARGET ${_ARGS_TARGET} PROPERTY COMPILE_DEFINITIONS)
endif()
foreach(_tmp ${gtid})
list(APPEND out "${_tmp}")
endforeach()
set(${_ARGS_OUTPUT} "${out}" PARENT_SCOPE)
endfunction()
macro(cstd_to_flag output version default)
set(${output} "${default}")
if(MSVC)
if("${version}" GREATER_EQUAL 23)
set(${output} "/std:clatest")
elseif("${version}" GREATER_EQUAL 17)
set(${output} "/std:c17")
elseif("${version}" GREATER_EQUAL 11)
set(${output} "/std:c11")
endif()
else()
if("${version}" GREATER_EQUAL 23)
set(${output} "-std=c23")
elseif("${version}" GREATER_EQUAL 17)
set(${output} "-std=c17")
elseif("${version}" GREATER_EQUAL 11)
set(${output} "-std=c11")
elseif("${version}" GREATER_EQUAL 99)
set(${output} "-std=c99")
elseif("${version}" GREATER_EQUAL 90)
set(${output} "-std=c90")
endif()
endif()
endmacro()
macro(cxxstd_to_flag output version default)
set(${output} "${default}")
if(MSVC)
if("${version}" GREATER_EQUAL 21)
set(${output} "/std:c++latest")
elseif("${version}" GREATER_EQUAL 20)
set(${output} "/std:c++20")
elseif("${version}" GREATER_EQUAL 17)
set(${output} "/std:c++17")
elseif("${version}" GREATER_EQUAL 14)
set(${output} "/std:c++14")
endif()
else()
if("${version}" GREATER_EQUAL 23)
set(${output} "-std=c++2b")
elseif("${version}" GREATER_EQUAL 20)
set(${output} "-std=c++20")
elseif("${version}" GREATER_EQUAL 17)
set(${output} "-std=c++17")
elseif("${version}" GREATER_EQUAL 14)
set(${output} "-std=c++14")
elseif("${version}" GREATER_EQUAL 11)
set(${output} "-std=c++11")
elseif("${version}" GREATER_EQUAL 98)
set(${output} "-std=c++98")
endif()
endif()
endmacro()
function(generate_compile_commands_json)
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
""
"REGEX"
"TARGETS"
)
if(NOT _ARGS_REGEX)
set(_ARGS_REGEX "\\.(h|c)(|pp)$")
endif()
# If the generator itself can create the compile_commands.json file, don't create our own.
set(cc_generators
"Borland Makefiles"
"MSYS Makefiles"
"MinGW Makefiles"
"NMake Makefiles"
"NMake Makefiles JOM"
"Unix Makefiles"
"Watcom WMake"
"Ninja"
"Ninja Multi-Config"
)
if(CMAKE_GENERATOR IN_LIST cc_generators)
foreach(current_target ${_ARGS_TARGETS})
set_target_properties(${current_target} PROPERTIES
EXPORT_COMPILE_COMMANDS ON
)
endforeach()
return()
endif()
if(MSVC)
set(_define_prefix "/D")
set(_include_prefix "/I")
else()
set(_define_prefix "-D")
set(_include_prefix "-I")
endif()
# Is this generator able to have multiple configurations?
get_property(cc_multiconfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT cc_multiconfig)
set(cc_configurations ${CMAKE_BUILD_TYPE})
else()
set(cc_configurations ${CMAKE_CONFIGURATION_TYPES})
endif()
foreach(current_target ${_ARGS_TARGETS})
# For each target, generate a compile_commands.json file with all files.
# Get source and binary directory.
get_property(cc_tgt_source_dir TARGET ${current_target} PROPERTY SOURCE_DIR)
get_filename_component(cc_tgt_source_dir "${cc_tgt_source_dir}" ABSOLUTE)
get_property(cc_tgt_binary_dir TARGET ${current_target} PROPERTY BINARY_DIR)
get_filename_component(cc_tgt_binary_dir "${cc_tgt_binary_dir}" ABSOLUTE)
# C++ Standard
get_property(cc_tgt_std_CXX TARGET ${current_target} PROPERTY CXX_STANDARD)
cxxstd_to_flag(cc_tgt_std_CXX "${cc_tgt_std_CXX}" "")
# C standard
get_property(cc_tgt_std_C TARGET ${current_target} PROPERTY C_STANDARD)
cstd_to_flag(cc_tgt_std_C "${cc_tgt_std_C}" "")
# Include Directories
get_property(_tmp TARGET ${current_target} PROPERTY INCLUDE_DIRECTORIES)
foreach(cc_src_command ${_tmp})
cmake_path(ABSOLUTE_PATH cc_src_command BASE_DIRECTORY "${cc_tgt_source_dir}")
list(APPEND cc_tgt_includes "${cc_src_command}")
endforeach()
get_target_include_directories(OUTPUT _tmp TARGET ${current_target})
# Definitions, Options
get_target_definitions(OUTPUT cc_tgt_definitions TARGET ${current_target})
get_property(cc_tgt_options TARGET ${current_target} PROPERTY COMPILE_OPTIONS)
# Interface stuff from link dependencies
get_property(cc_tgt_depends TARGET ${current_target} PROPERTY LINK_LIBRARIES)
foreach(_tmp3 ${cc_tgt_depends})
if(TARGET ${_tmp3})
# - Interface Include Directories
get_property(_tmp TARGET ${_tmp3} PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
foreach(cc_src_command ${_tmp})
cmake_path(ABSOLUTE_PATH cc_src_command BASE_DIRECTORY "${cc_tgt_source_dir}")
list(APPEND cc_tgt_includes "${cc_src_command}")
endforeach()
get_property(_tmp TARGET ${_tmp3} PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
foreach(cc_src_command ${_tmp})
cmake_path(ABSOLUTE_PATH cc_src_command BASE_DIRECTORY "${cc_tgt_source_dir}")
list(APPEND cc_tgt_includes "${cc_src_command}")
endforeach()
# - Interface Defines, Options
get_property(_tmp TARGET ${current_target} PROPERTY INTERFACE_COMPILE_DEFINITIONS)
foreach(cc_src_command ${_tmp})
list(APPEND cc_tgt_definitions "${cc_src_command}")
endforeach()
get_property(_tmp TARGET ${current_target} PROPERTY INTERFACE_COMPILE_OPTIONS)
foreach(cc_src_command ${_tmp})
list(APPEND cc_tgt_options "${cc_src_command}")
endforeach()
endif()
endforeach()
# Figure out source files for this target.
set(cc_tgt_sources "")
get_target_property(cc_tgt_sources_raw ${current_target} SOURCES)
foreach(cc_tgt_source ${cc_tgt_sources_raw})
cmake_path(ABSOLUTE_PATH cc_tgt_source BASE_DIRECTORY "${cc_tgt_source_dir}")
list(APPEND cc_tgt_sources "${cc_tgt_source}")
endforeach()
list(FILTER cc_tgt_sources INCLUDE REGEX "${_ARGS_REGEX}")
# Generate a unique compile_commands.json.
set(cc_json_content "")
string(APPEND cc_json_content "[\n")
# Write entries for each file.
foreach(current_source ${cc_tgt_sources})
# Find the real location of the source file.
get_property(cc_src_location SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY LOCATION)
cmake_path(ABSOLUTE_PATH cc_src_location BASE_DIRECTORY "${cc_tgt_source_dir}")
# Try and figure out the language used.
get_property(cc_src_language SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY LANGUAGE)
if("${cc_src_language}" STREQUAL "")
get_filename_component(_tmp "${current_source}" EXT)
if((_tmp STREQUAL ".hpp") OR (_tmp STREQUAL ".cpp"))
set(cc_src_language "CXX")
else()
set(cc_src_language "C")
endif()
endif()
if(cc_src_language STREQUAL "CXX")
# C++ Standard
get_property(cc_src_std_CXX SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY CXX_STANDARD)
cxxstd_to_flag(cc_src_std "${cc_src_std_CXX}" "${cc_tgt_std_CXX}")
else()
# C standard
get_property(cc_src_std_C SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY C_STANDARD)
cstd_to_flag(cc_src_std "${cc_src_std_C}" "${cc_tgt_std_C}")
endif()
# Compile Options
set(cc_src_options "${cc_tgt_options}")
get_property(_tmp SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY COMPILE_OPTIONS)
foreach(cc_src_command ${_tmp})
list(APPEND cc_src_options "${cc_src_command}")
endforeach()
# Includes
set(cc_src_includex "${cc_tgt_includes}")
get_property(_tmp SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY INCLUDE_DIRECTORIES)
foreach(cc_src_command ${_tmp})
cmake_path(ABSOLUTE_PATH cc_src_command BASE_DIRECTORY "${cc_tgt_source_dir}")
list(APPEND cc_src_includex "${cc_src_command}")
endforeach()
# Defines
set(cc_src_defines "${cc_tgt_defines}")
get_property(_tmp SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY COMPILE_DEFINITIONS)
foreach(cc_src_command ${_tmp})
list(APPEND cc_src_defines "${cc_src_command}")
endforeach()
#get_property(_ SOURCE ${current_source} TARGET_DIRECTORY ${current_target} PROPERTY _)
# Generate JSON content.
set(cc_json_content_entry "{}")
# Working Directory
file(TO_CMAKE_PATH "${cc_tgt_source_dir}" _tmp)
json_escape_string(OUTPUT _tmp INPUT "${_tmp}")
string(JSON cc_json_content_entry SET ${cc_json_content_entry} "directory" \"${_tmp}\")
# Target File
file(TO_CMAKE_PATH "${cc_src_location}" _tmp)
json_escape_string(OUTPUT _tmp INPUT "${_tmp}")
string(JSON cc_json_content_entry SET ${cc_json_content_entry} "file" \"${_tmp}\")
if(ON) # Command
set(cc_src_command "")
# cl/cc difference
if(MSVC)
list(APPEND cc_src_command "cl")
else()
list(APPEND cc_src_command "cc")
endif()
# C/CXX Standard
if(NOT "${cc_src_std}" STREQUAL "")
list(APPEND cc_src_command "${cc_src_std}")
endif()
# Global Flags
if(MSVC)
separate_arguments(cc_src_flags WINDOWS_COMMAND ${CMAKE_${cc_src_language}_FLAGS})
else()
separate_arguments(cc_src_flags UNIX_COMMAND ${CMAKE_${cc_src_language}_FLAGS})
endif()
foreach(flag ${cc_src_flags})
if(NOT "${flag}" STREQUAL "")
list(APPEND cc_src_command "${flag}")
endif()
endforeach()
set(cc_src_command_config "")
foreach(current_config ${cc_configurations})
string(TOUPPER "${current_config}" current_config_upper)
list(APPEND cc_src_command_config "$<$<CONFIG:${current_config}>:${CMAKE_${cc_src_language}_FLAGS_${current_config_upper}}>")
endforeach()
list(JOIN cc_src_command_config "" cc_src_command_config)
list(APPEND cc_src_command "${cc_src_command_config}")
# Definitions
foreach(define ${cc_src_defines})
if(NOT "${define}" STREQUAL "")
list(APPEND cc_src_command "${_define_prefix}${define}")
endif()
endforeach()
# Other Options
foreach(option ${cc_src_options})
if(NOT "${option}" STREQUAL "")
list(APPEND cc_src_command "${option}")
endif()
endforeach()
# Include Directories
foreach(include ${cc_src_includex})
if(NOT "${include}" STREQUAL "")
file(TO_CMAKE_PATH "${include}" _tmp)
list(APPEND cc_src_command "${_include_prefix}${include}")
endif()
endforeach()
# File to compile
json_escape_string(OUTPUT cc_src_location INPUT "${cc_src_location}")
if(MSVC)
list(APPEND cc_src_command "\"${cc_src_location}\"")
else()
list(APPEND cc_src_command "-c \"${cc_src_location}\"")
endif()
# Build actual command entry.
list(JOIN cc_src_command " " cc_src_command)
json_escape_string(OUTPUT _tmp INPUT "${cc_src_command}")
string(JSON cc_json_content_entry SET ${cc_json_content_entry} "command" \"${_tmp}\")
endif()
string(APPEND cc_json_content "${cc_json_content_entry},\n")
endforeach()
# Close the array.
string(APPEND cc_json_content "]\n")
# Generate file
file(GENERATE
OUTPUT "$<TARGET_PROPERTY:${current_target},BINARY_DIR>/$<CONFIG>/compile_commands.json"
CONTENT "${cc_json_content}"
TARGET ${current_target}
FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_READ GROUP_EXECUTE
)
endforeach()
unset(_tmp)
unset(cc_src_command)
unset(current_target)
unset(current_source)
unset(current_config)
endfunction()
function(clang_format)
list(APPEND CMAKE_MESSAGE_INDENT "[clang-format] ")
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
"DEPENDENCY"
"REGEX;VERSION"
"TARGETS"
)
find_program(CLANG_FORMAT_BIN
NAMES
"clang-format"
HINTS
"${CLANG_PATH}"
PATHS
/bin
/sbin
/usr/bin
/usr/local/bin
PATH_SUFFIXES
bin
bin64
bin32
DOC "Path (or name) of the clang-format binary"
)
if(NOT CLANG_FORMAT_BIN)
message(WARNING "Clang for CMake: Could not find clang-format at path '${CLANG_FORMAT_BIN}', disabling clang-format...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
# Validate Version
if (_ARGS_VERSION)
set(_VERSION_RESULT "")
set(_VERSION_OUTPUT "")
execute_process(
COMMAND "${CLANG_FORMAT_BIN}" --version
WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
RESULT_VARIABLE _VERSION_RESULT
OUTPUT_VARIABLE _VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT _VERSION_RESULT EQUAL 0)
message(WARNING "Clang for CMake: Could not discover version, disabling clang-format...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
string(REGEX MATCH "([0-9]+\.[0-9]+\.[0-9]+)" _VERSION_MATCH ${_VERSION_OUTPUT})
if(NOT ${_VERSION_MATCH} VERSION_GREATER_EQUAL ${_ARGS_VERSION})
message(WARNING "Clang for CMake: Old version discovered, disabling clang-format...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
endif()
# Default Filter
if(NOT _ARGS_REGEX)
set(_ARGS_REGEX "\\.(h|hpp|c|cpp)$")
endif()
# Go through each target
foreach(current_target ${_ARGS_TARGETS})
set(designed_target "${current_target}_clang-format")
get_target_property(target_sources_rel ${current_target} SOURCES)
set(target_sources "")
foreach(_tmp ${target_sources_rel})
get_filename_component(_tmp "${_tmp}" ABSOLUTE)
file(TO_CMAKE_PATH "${_tmp}" _tmp)
list(APPEND target_sources "${_tmp}")
endforeach()
list(FILTER target_sources INCLUDE REGEX "${_ARGS_REGEX}")
unset(target_sources_rel)
get_target_property(target_source_dir_rel ${current_target} SOURCE_DIR)
get_filename_component(target_source_dir ${target_source_dir_rel} ABSOLUTE)
unset(target_source_dir_rel)
add_custom_target(${designed_target}
COMMAND "${CLANG_FORMAT_BIN}" -style=file -i ${target_sources}
COMMENT "clang-format: Formatting ${current_target}..."
WORKING_DIRECTORY "${target_source_dir}"
)
# IDE Folder & Label
get_target_property(folder ${current_target} FOLDER)
get_target_property(label ${current_target} PROJECT_LABEL)
if(folder)
set_target_properties(${designed_target} PROPERTIES FOLDER ${folder})
else()
set_target_properties(${designed_target} PROPERTIES FOLDER Clang)
endif()
if(label)
set_target_properties(${designed_target} PROPERTIES PROJECT_LABEL "${label} (clang-format)")
else()
set_target_properties(${designed_target} PROPERTIES PROJECT_LABEL "${current_target} (clang-format)")
endif()
if(_ARGS_DEPENDENCY)
add_dependencies(${current_target} ${designed_target})
endif()
if(NOT TARGET clang-format)
add_custom_target(clang-format
DEPENDS
${designed_target}
COMMENT
"clang-format: Formatting..."
)
set_target_properties(clang-format PROPERTIES
FOLDER Clang
)
endif()
add_dependencies(clang-format ${designed_target})
endforeach()
list(POP_BACK CMAKE_MESSAGE_INDENT)
endfunction()
function(clang_tidy)
list(APPEND CMAKE_MESSAGE_INDENT "[clang-tidy] ")
cmake_parse_arguments(
PARSE_ARGV 0
_ARGS
"DEPENDENCY"
"REGEX;VERSION"
"TARGETS"
)
find_program(CLANG_TIDY_BIN
NAMES
"clang-tidy"
HINTS
"${CLANG_PATH}"
PATHS
/bin
/sbin
/usr/bin
/usr/local/bin
PATH_SUFFIXES
bin
bin64
bin32
DOC "Path (or name) of the clang-tidy binary"
)
if(NOT CLANG_TIDY_BIN)
message(WARNING "Clang for CMake: Could not find clang-tidy at path '${CLANG_TIDY_BIN}', disabling clang-tidy...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
# Validate Version
if (_ARGS_VERSION)
set(_VERSION_RESULT "")
set(_VERSION_OUTPUT "")
execute_process(
COMMAND "${CLANG_TIDY_BIN}" --version
WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
RESULT_VARIABLE _VERSION_RESULT
OUTPUT_VARIABLE _VERSION_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(NOT _VERSION_RESULT EQUAL 0)
message(WARNING "Clang for CMake: Could not discover version, disabling clang-tidy...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
string(REGEX MATCH "([0-9]+\.[0-9]+\.[0-9]+)" _VERSION_MATCH ${_VERSION_OUTPUT})
if(NOT ${_VERSION_MATCH} VERSION_GREATER_EQUAL ${_ARGS_VERSION})
message(WARNING "Clang for CMake: Old version discovered, disabling clang-tidy...")
list(POP_BACK CMAKE_MESSAGE_INDENT)
return()
endif()
endif()
# Default Filter
if(NOT _ARGS_REGEX)
set(_ARGS_REGEX "\\.(h|hpp|c|cpp)$")
endif()
# Go through each target
foreach(current_target ${_ARGS_TARGETS})
set(designed_target "${current_target}_clang-tidy")
# Source Directory
get_target_property(cc_src_command ${current_target} SOURCE_DIR)
get_filename_component(target_source_dir ${cc_src_command} ABSOLUTE)
file(TO_CMAKE_PATH "${target_source_dir}" target_source_dir_nat)
unset(cc_src_command)
# Binary Directory
get_target_property(cc_src_command ${current_target} BINARY_DIR)
get_filename_component(target_binary_dir ${cc_src_command} ABSOLUTE)
file(TO_CMAKE_PATH "${target_binary_dir}" target_binary_dir_nat)
unset(cc_src_command)
# Sources
get_target_property(cc_src_command ${current_target} SOURCES)
set(target_sources "")
foreach(_tmp ${cc_src_command})
get_filename_component(_tmp ${_tmp} ABSOLUTE)
file(TO_CMAKE_PATH "${_tmp}" _tmp)
list(APPEND target_sources "${_tmp}")
endforeach()
list(FILTER target_sources INCLUDE REGEX "${_ARGS_REGEX}")
unset(cc_src_command)
add_custom_target(${designed_target}
COMMENT "clang-tidy: Tidying ${current_target}..."
WORKING_DIRECTORY "${target_binary_dir}"
VERBATIM
)
foreach(_tmp ${target_sources})
add_custom_command(
TARGET ${designed_target}
POST_BUILD
COMMAND "${CLANG_TIDY_BIN}"
ARGS --quiet -p="$<TARGET_PROPERTY:${current_target},BINARY_DIR>/$<CONFIG>" "${_tmp}"
WORKING_DIRECTORY "${target_binary_dir}"
COMMAND_EXPAND_LISTS
)
endforeach()
# IDE Folder & Label
get_target_property(folder ${current_target} FOLDER)
get_target_property(label ${current_target} PROJECT_LABEL)
if(folder)
set_target_properties(${designed_target} PROPERTIES FOLDER ${folder})
else()
set_target_properties(${designed_target} PROPERTIES FOLDER Clang)
endif()
if(label)
set_target_properties(${designed_target} PROPERTIES PROJECT_LABEL "${label} (clang-tidy)")
else()
set_target_properties(${designed_target} PROPERTIES PROJECT_LABEL "${current_target} (clang-tidy)")
endif()
if(_ARGS_DEPENDENCY)
add_dependencies(${current_target} ${designed_target})
endif()
if(NOT TARGET clang-tidy)
add_custom_target(clang-tidy
DEPENDS
${designed_target}
COMMENT
"clang-tiy: Tidying..."
)
set_target_properties(clang-tidy PROPERTIES
FOLDER Clang
)
endif()
add_dependencies(clang-tidy ${designed_target})
endforeach()
list(POP_BACK CMAKE_MESSAGE_INDENT)
endfunction()