PVS-Studio.cmake 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. # 2006-2008 (c) Viva64.com Team
  2. # 2008-2018 (c) OOO "Program Verification Systems"
  3. #
  4. # Version 12
  5. cmake_minimum_required(VERSION 2.8.12)
  6. cmake_policy(SET CMP0054 NEW)
  7. if (PVS_STUDIO_AS_SCRIPT)
  8. # This code runs at build time.
  9. # It executes pvs-studio-analyzer and propagates its return value.
  10. set(in_cl_params FALSE)
  11. set(additional_args)
  12. foreach (arg ${PVS_STUDIO_COMMAND})
  13. if (NOT in_cl_params)
  14. if ("${arg}" STREQUAL "--cl-params")
  15. set(in_cl_params TRUE)
  16. endif ()
  17. else ()
  18. # A workaround for macOS frameworks (e.g. QtWidgets.framework)
  19. # You can test this workaround on this project: https://github.com/easyaspi314/MidiEditor/tree/gba
  20. if (APPLE AND "${arg}" MATCHES "^-I(.*)\\.framework$")
  21. STRING(REGEX REPLACE "^-I(.*)\\.framework$" "\\1.framework" framework "${arg}")
  22. if (IS_ABSOLUTE "${framework}")
  23. get_filename_component(framework "${framework}" DIRECTORY)
  24. list(APPEND additional_args "-iframework")
  25. list(APPEND additional_args "${framework}")
  26. endif ()
  27. endif ()
  28. endif ()
  29. endforeach ()
  30. execute_process(COMMAND ${PVS_STUDIO_COMMAND} ${additional_args}
  31. ERROR_VARIABLE error
  32. RESULT_VARIABLE result)
  33. set(stderr_type "")
  34. if (result)
  35. set(stderr_type FATAL_ERROR)
  36. endif ()
  37. if (result OR error)
  38. message(${stderr_type} "${error}")
  39. endif ()
  40. return()
  41. endif ()
  42. if(__PVS_STUDIO_INCLUDED)
  43. return()
  44. endif()
  45. set(__PVS_STUDIO_INCLUDED TRUE)
  46. set(PVS_STUDIO_SCRIPT "${CMAKE_CURRENT_LIST_FILE}")
  47. function (pvs_studio_log TEXT)
  48. if (PVS_STUDIO_DEBUG)
  49. message("PVS-Studio: ${TEXT}")
  50. endif ()
  51. endfunction ()
  52. function (pvs_studio_relative_path VAR ROOT FILEPATH)
  53. set("${VAR}" "${FILEPATH}" PARENT_SCOPE)
  54. if ("${FILEPATH}" MATCHES "^/.*$" OR "${FILEPATH}" MATCHES "^.:/.*$")
  55. file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}")
  56. if (NOT "${RPATH}" MATCHES "^\\.\\..*$")
  57. set("${VAR}" "${RPATH}" PARENT_SCOPE)
  58. endif ()
  59. endif ()
  60. endfunction ()
  61. function (pvs_studio_join_path VAR DIR1 DIR2)
  62. if ("${DIR2}" MATCHES "^(/|~|.:/).*$" OR "${DIR1}" STREQUAL "")
  63. set("${VAR}" "${DIR2}" PARENT_SCOPE)
  64. else ()
  65. set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE)
  66. endif ()
  67. endfunction ()
  68. macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX)
  69. if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND")
  70. foreach (PROP ${PROPERTY})
  71. pvs_studio_join_path(PROP "${DIR}" "${PROP}")
  72. if (APPLE AND "${PREFIX}" STREQUAL "-I" AND IS_ABSOLUTE "${PROP}" AND "${PROP}" MATCHES "\\.framework$")
  73. get_filename_component(FRAMEWORK "${PROP}" DIRECTORY)
  74. list(APPEND "${CXX}" "-iframework")
  75. list(APPEND "${CXX}" "${FRAMEWORK}")
  76. list(APPEND "${C}" "-iframework")
  77. list(APPEND "${C}" "${FRAMEWORK}")
  78. pvs_studio_log("framework: ${FRAMEWORK}")
  79. elseif (NOT "${PROP}" STREQUAL "")
  80. list(APPEND "${CXX}" "${PREFIX}${PROP}")
  81. list(APPEND "${C}" "${PREFIX}${PROP}")
  82. endif()
  83. endforeach ()
  84. endif ()
  85. endmacro ()
  86. macro (pvs_studio_append_standard_flag FLAGS STANDARD)
  87. if ("${STANDARD}" MATCHES "^(99|11|14|17)$")
  88. if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang")
  89. list(APPEND "${FLAGS}" "-std=c++${STANDARD}")
  90. endif ()
  91. endif ()
  92. endmacro ()
  93. function (pvs_studio_set_directory_flags DIRECTORY CXX C)
  94. set(CXX_FLAGS "${${CXX}}")
  95. set(C_FLAGS "${${C}}")
  96. get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES)
  97. pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I")
  98. get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS)
  99. pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D")
  100. set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE)
  101. set("${C}" "${C_FLAGS}" PARENT_SCOPE)
  102. endfunction ()
  103. function (pvs_studio_set_target_flags TARGET CXX C)
  104. set(CXX_FLAGS "${${CXX}}")
  105. set(C_FLAGS "${${C}}")
  106. set(prop_incdirs "$<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>")
  107. list(APPEND CXX_FLAGS "$<$<BOOL:${prop_incdirs}>:-I$<JOIN:${prop_incdirs},$<SEMICOLON>-I>>")
  108. list(APPEND C_FLAGS "$<$<BOOL:${prop_incdirs}>:-I$<JOIN:${prop_incdirs},$<SEMICOLON>-I>>")
  109. set(prop_compdefs "$<TARGET_PROPERTY:${TARGET},COMPILE_DEFINITIONS>")
  110. list(APPEND CXX_FLAGS "$<$<BOOL:${prop_compdefs}>:-D$<JOIN:${prop_compdefs},$<SEMICOLON>-D>>")
  111. list(APPEND C_FLAGS "$<$<BOOL:${prop_compdefs}>:-D$<JOIN:${prop_compdefs},$<SEMICOLON>-D>>")
  112. set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE)
  113. set("${C}" "${C_FLAGS}" PARENT_SCOPE)
  114. endfunction ()
  115. function (pvs_studio_set_source_file_flags SOURCE)
  116. set(LANGUAGE "")
  117. string(TOLOWER "${SOURCE}" SOURCE_LOWER)
  118. if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$")
  119. if ("${SOURCE}" MATCHES "^.*\\.c$")
  120. set(LANGUAGE C)
  121. else ()
  122. set(LANGUAGE CXX)
  123. endif ()
  124. endif ()
  125. if ("${LANGUAGE}" STREQUAL "C")
  126. set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO)
  127. elseif ("${LANGUAGE}" STREQUAL "CXX")
  128. set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO)
  129. endif ()
  130. set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE)
  131. set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE)
  132. endfunction ()
  133. function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR)
  134. set(PLOGS ${PVS_STUDIO_PLOGS})
  135. pvs_studio_set_source_file_flags("${SOURCE}")
  136. get_filename_component(SOURCE "${SOURCE}" REALPATH)
  137. get_source_file_property(PROPERTY "${SOURCE}" HEADER_FILE_ONLY)
  138. if (PROPERTY)
  139. return()
  140. endif ()
  141. pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}")
  142. pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}")
  143. set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog")
  144. get_filename_component(LOG "${LOG}" REALPATH)
  145. get_filename_component(PARENT_DIR "${LOG}" DIRECTORY)
  146. if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "")
  147. # A workaround to support implicit dependencies for ninja generators.
  148. set(depPvsArg)
  149. set(depCommandArg)
  150. if (CMAKE_VERSION VERSION_GREATER 3.6 AND "${CMAKE_GENERATOR}" STREQUAL "Ninja")
  151. pvs_studio_relative_path(relLog "${CMAKE_BINARY_DIR}" "${LOG}")
  152. set(depPvsArg --dep-file "${LOG}.d" --dep-file-target "${relLog}")
  153. set(depCommandArg DEPFILE "${LOG}.d")
  154. endif ()
  155. # https://public.kitware.com/Bug/print_bug_page.php?bug_id=14353
  156. # https://public.kitware.com/Bug/file/5436/expand_command.cmake
  157. #
  158. # It is a workaround to expand generator expressions.
  159. set(cmdline "${PVS_STUDIO_BIN}" analyze
  160. --output-file "${LOG}"
  161. --source-file "${SOURCE}"
  162. ${depPvsArg}
  163. ${PVS_STUDIO_ARGS}
  164. --cl-params "${PVS_STUDIO_CL_PARAMS}" "${SOURCE}")
  165. string(REPLACE ";" "$<SEMICOLON>" cmdline "${cmdline}")
  166. set(pvscmd "${CMAKE_COMMAND}"
  167. -D PVS_STUDIO_AS_SCRIPT=TRUE
  168. -D "PVS_STUDIO_COMMAND=${cmdline}"
  169. -P "${PVS_STUDIO_SCRIPT}"
  170. )
  171. add_custom_command(OUTPUT "${LOG}"
  172. COMMAND "${CMAKE_COMMAND}" -E make_directory "${PARENT_DIR}"
  173. COMMAND "${CMAKE_COMMAND}" -E remove_directory "${LOG}"
  174. COMMAND ${pvscmd}
  175. WORKING_DIRECTORY "${BINARY_DIR}"
  176. DEPENDS "${SOURCE}" "${PVS_STUDIO_CONFIG}" "${PVS_STUDIO_SUPPRESS_BASE}"
  177. IMPLICIT_DEPENDS "${PVS_STUDIO_LANGUAGE}" "${SOURCE}"
  178. ${depCommandArg}
  179. VERBATIM
  180. COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}")
  181. list(APPEND PLOGS "${LOG}")
  182. endif ()
  183. set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE)
  184. endfunction ()
  185. function (pvs_studio_analyze_target TARGET DIR)
  186. set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}")
  187. set(PVS_STUDIO_TARGET_CXX_FLAGS "")
  188. set(PVS_STUDIO_TARGET_C_FLAGS "")
  189. get_target_property(PROPERTY "${TARGET}" SOURCES)
  190. pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}")
  191. if ("${BINARY_DIR}" MATCHES "^/.*$")
  192. pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}")
  193. else ()
  194. pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}")
  195. endif ()
  196. file(MAKE_DIRECTORY "${BINARY_DIR}")
  197. pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS)
  198. pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS)
  199. if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND")
  200. foreach (SOURCE ${PROPERTY})
  201. pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}")
  202. pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}")
  203. endforeach ()
  204. endif ()
  205. set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE)
  206. endfunction ()
  207. set(PVS_STUDIO_RECURSIVE_TARGETS)
  208. set(PVS_STUDIO_RECURSIVE_TARGETS_NEW)
  209. macro(pvs_studio_get_recursive_targets TARGET)
  210. get_target_property(libs "${TARGET}" LINK_LIBRARIES)
  211. foreach (lib IN LISTS libs)
  212. list(FIND PVS_STUDIO_RECURSIVE_TARGETS "${lib}" index)
  213. if (TARGET "${lib}" AND "${index}" STREQUAL -1)
  214. get_target_property(target_type "${lib}" TYPE)
  215. if (NOT "${target_type}" STREQUAL "INTERFACE_LIBRARY")
  216. list(APPEND PVS_STUDIO_RECURSIVE_TARGETS "${lib}")
  217. list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${lib}")
  218. pvs_studio_get_recursive_targets("${lib}")
  219. endif ()
  220. endif ()
  221. endforeach ()
  222. endmacro()
  223. option(PVS_STUDIO_DISABLE OFF "Disable PVS-Studio targets")
  224. option(PVS_STUDIO_DEBUG OFF "Add debug info")
  225. # pvs_studio_add_target
  226. # Target options:
  227. # ALL add PVS-Studio target to default build (default: off)
  228. # TARGET target name of analysis target (default: pvs)
  229. # ANALYZE targets... targets to analyze
  230. # RECURSIVE analyze target's dependencies (requires CMake 3.5+)
  231. # COMPILE_COMMANDS use compile_commands.json instead of targets (specified by the 'ANALYZE' option) to determine files for analysis
  232. # (set CMAKE_EXPORT_COMPILE_COMMANDS, available only for Makefile and Ninja generators)
  233. #
  234. # Output options:
  235. # OUTPUT prints report to stdout
  236. # LOG path path to report (default: ${CMAKE_CURRENT_BINARY_DIR}/PVS-Studio.log)
  237. # FORMAT format format of report
  238. # MODE mode analyzers/levels filter (default: GA:1,2)
  239. # HIDE_HELP do not print help message
  240. #
  241. # Analyzer options:
  242. # PLATFORM name linux32/linux64 (default: linux64)
  243. # PREPROCESSOR name preprocessor type: gcc/clang (default: auto detected)
  244. # LICENSE path path to PVS-Studio.lic (default: ~/.config/PVS-Studio/PVS-Studio.lic)
  245. # CONFIG path path to PVS-Studio.cfg
  246. # CFG_TEXT text embedded PVS-Studio.cfg
  247. # SUPPRESS_BASE path to suppress base file
  248. # KEEP_COMBINED_PLOG do not delete combined plog file *.pvs.raw for further processing with plog-converter
  249. #
  250. # Misc options:
  251. # DEPENDS targets.. additional target dependencies
  252. # SOURCES path... list of source files to analyze
  253. # BIN path path to pvs-studio-analyzer (Unix) or CompilerCommandsAnalyzer.exe (Windows)
  254. # CONVERTER path path to plog-converter (Unix) or HtmlGenerator.exe (Windows)
  255. # C_FLAGS flags... additional C_FLAGS
  256. # CXX_FLAGS flags... additional CXX_FLAGS
  257. # ARGS args... additional pvs-studio-analyzer/CompilerCommandsAnalyzer.exe flags
  258. function (pvs_studio_add_target)
  259. macro (default VAR VALUE)
  260. if ("${${VAR}}" STREQUAL "")
  261. set("${VAR}" "${VALUE}")
  262. endif ()
  263. endmacro ()
  264. set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang|visualcpp")
  265. if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
  266. set(DEFAULT_PREPROCESSOR "clang")
  267. elseif (MSVC)
  268. set(DEFAULT_PREPROCESSOR "visualcpp")
  269. else ()
  270. set(DEFAULT_PREPROCESSOR "gcc")
  271. endif ()
  272. set(OPTIONAL OUTPUT ALL RECURSIVE HIDE_HELP KEEP_COMBINED_PLOG COMPILE_COMMANDS)
  273. set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT SUPPRESS_BASE)
  274. set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE MODE)
  275. cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN})
  276. if ("${PVS_STUDIO_CONFIG}" STREQUAL "" OR NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "")
  277. set(PVS_STUDIO_EMPTY_CONFIG ON)
  278. else ()
  279. set(PVS_STUDIO_EMPTY_CONFIG OFF)
  280. endif ()
  281. default(PVS_STUDIO_CFG_TEXT "analysis-mode=31")
  282. default(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg")
  283. default(PVS_STUDIO_C_FLAGS "")
  284. default(PVS_STUDIO_CXX_FLAGS "")
  285. default(PVS_STUDIO_TARGET "pvs")
  286. default(PVS_STUDIO_LOG "PVS-Studio.log")
  287. set(PATHS)
  288. if (WIN32)
  289. set(ROOT "PROGRAMFILES(X86)")
  290. set(ROOT "$ENV{${ROOT}}/PVS-Studio")
  291. string(REPLACE \\ / ROOT "${ROOT}")
  292. if (EXISTS "${ROOT}")
  293. set(PATHS "${ROOT}")
  294. endif ()
  295. default(PVS_STUDIO_BIN "CompilerCommandsAnalyzer.exe")
  296. default(PVS_STUDIO_CONVERTER "HtmlGenerator.exe")
  297. else ()
  298. default(PVS_STUDIO_BIN "pvs-studio-analyzer")
  299. default(PVS_STUDIO_CONVERTER "plog-converter")
  300. endif ()
  301. find_program(PVS_STUDIO_BIN_PATH "${PVS_STUDIO_BIN}" ${PATHS})
  302. set(PVS_STUDIO_BIN "${PVS_STUDIO_BIN_PATH}")
  303. if (NOT EXISTS "${PVS_STUDIO_BIN}")
  304. message(FATAL_ERROR "pvs-studio-analyzer is not found")
  305. endif ()
  306. find_program(PVS_STUDIO_CONVERTER_PATH "${PVS_STUDIO_CONVERTER}" ${PATHS})
  307. set(PVS_STUDIO_CONVERTER "${PVS_STUDIO_CONVERTER_PATH}")
  308. if (NOT EXISTS "${PVS_STUDIO_CONVERTER}")
  309. message(FATAL_ERROR "plog-converter is not found")
  310. endif ()
  311. default(PVS_STUDIO_MODE "GA:1,2")
  312. default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}")
  313. if (WIN32)
  314. default(PVS_STUDIO_PLATFORM "x64")
  315. else ()
  316. default(PVS_STUDIO_PLATFORM "linux64")
  317. endif ()
  318. string(REPLACE ";" "+" PVS_STUDIO_MODE "${PVS_STUDIO_MODE}")
  319. if (PVS_STUDIO_EMPTY_CONFIG)
  320. set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}")
  321. else ()
  322. set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_CONFIG}")
  323. endif ()
  324. add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}"
  325. COMMAND ${PVS_STUDIO_CONFIG_COMMAND}
  326. WORKING_DIRECTORY "${BINARY_DIR}"
  327. COMMENT "Generating PVS-Studio.cfg")
  328. if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$")
  329. message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.")
  330. endif ()
  331. pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}")
  332. pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS)
  333. if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "")
  334. pvs_studio_join_path(PVS_STUDIO_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_LICENSE}")
  335. list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}")
  336. endif ()
  337. list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}"
  338. --platform "${PVS_STUDIO_PLATFORM}"
  339. --preprocessor "${PVS_STUDIO_PREPROCESSOR}")
  340. if (NOT "${PVS_STUDIO_SUPPRESS_BASE}" STREQUAL "")
  341. pvs_studio_join_path(PVS_STUDIO_SUPPRESS_BASE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_SUPPRESS_BASE}")
  342. list(APPEND PVS_STUDIO_ARGS --suppress-file "${PVS_STUDIO_SUPPRESS_BASE}")
  343. endif ()
  344. if (NOT "${CMAKE_CXX_COMPILER}" STREQUAL "")
  345. list(APPEND PVS_STUDIO_ARGS --cxx "${CMAKE_CXX_COMPILER}")
  346. endif ()
  347. if (NOT "${CMAKE_C_COMPILER}" STREQUAL "")
  348. list(APPEND PVS_STUDIO_ARGS --cc "${CMAKE_C_COMPILER}")
  349. endif ()
  350. set(PVS_STUDIO_PLOGS "")
  351. set(PVS_STUDIO_RECURSIVE_TARGETS_NEW)
  352. if (${PVS_STUDIO_RECURSIVE})
  353. foreach (TARGET IN LISTS PVS_STUDIO_ANALYZE)
  354. list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${TARGET}")
  355. pvs_studio_get_recursive_targets("${TARGET}")
  356. endforeach ()
  357. endif ()
  358. set(inc_path)
  359. foreach (TARGET ${PVS_STUDIO_ANALYZE})
  360. set(DIR "${CMAKE_CURRENT_SOURCE_DIR}")
  361. string(FIND "${TARGET}" ":" DELIM)
  362. if ("${DELIM}" GREATER "-1")
  363. math(EXPR DELIMI "${DELIM}+1")
  364. string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR)
  365. string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET)
  366. pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}")
  367. else ()
  368. get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR)
  369. if (EXISTS "${TARGET_SOURCE_DIR}")
  370. set(DIR "${TARGET_SOURCE_DIR}")
  371. endif ()
  372. endif ()
  373. pvs_studio_analyze_target("${TARGET}" "${DIR}")
  374. list(APPEND PVS_STUDIO_DEPENDS "${TARGET}")
  375. if ("${inc_path}" STREQUAL "")
  376. set(inc_path "$<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>")
  377. else ()
  378. set(inc_path "${inc_path}$<SEMICOLON>$<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>")
  379. endif ()
  380. endforeach ()
  381. foreach (TARGET ${PVS_STUDIO_RECURSIVE_TARGETS_NEW})
  382. set(DIR "${CMAKE_CURRENT_SOURCE_DIR}")
  383. get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR)
  384. if (EXISTS "${TARGET_SOURCE_DIR}")
  385. set(DIR "${TARGET_SOURCE_DIR}")
  386. endif ()
  387. pvs_studio_analyze_target("${TARGET}" "${DIR}")
  388. list(APPEND PVS_STUDIO_DEPENDS "${TARGET}")
  389. endforeach ()
  390. set(PVS_STUDIO_TARGET_CXX_FLAGS "")
  391. set(PVS_STUDIO_TARGET_C_FLAGS "")
  392. foreach (SOURCE ${PVS_STUDIO_SOURCES})
  393. pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
  394. endforeach ()
  395. if (PVS_STUDIO_COMPILE_COMMANDS)
  396. set(COMPILE_COMMANDS_LOG "${PVS_STUDIO_LOG}.pvs.analyzer.raw")
  397. if (NOT CMAKE_EXPORT_COMPILE_COMMANDS)
  398. message(FATAL_ERROR "You should set CMAKE_EXPORT_COMPILE_COMMANDS to TRUE")
  399. endif ()
  400. add_custom_command(
  401. OUTPUT "${COMPILE_COMMANDS_LOG}"
  402. COMMAND "${PVS_STUDIO_BIN}" analyze -i
  403. --output-file "${COMPILE_COMMANDS_LOG}.always"
  404. ${PVS_STUDIO_ARGS}
  405. COMMENT "Analyzing with PVS-Studio"
  406. WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
  407. DEPENDS "${PVS_STUDIO_CONFIG}" "${PVS_STUDIO_SUPPRESS_BASE}"
  408. )
  409. list(APPEND PVS_STUDIO_PLOGS_LOGS "${COMPILE_COMMANDS_LOG}.always")
  410. list(APPEND PVS_STUDIO_PLOGS_DEPENDENCIES "${COMPILE_COMMANDS_LOG}")
  411. endif ()
  412. pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}")
  413. if (PVS_STUDIO_PLOGS OR PVS_STUDIO_COMPILE_COMMANDS)
  414. if (WIN32)
  415. string(REPLACE / \\ PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}")
  416. endif ()
  417. if (WIN32)
  418. set(COMMANDS COMMAND type ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>nul)
  419. else ()
  420. set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}")
  421. endif ()
  422. set(COMMENT "Generating ${LOG_RELATIVE}")
  423. if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT)
  424. if ("${PVS_STUDIO_FORMAT}" STREQUAL "")
  425. set(PVS_STUDIO_FORMAT "errorfile")
  426. endif ()
  427. list(APPEND COMMANDS
  428. COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw"
  429. COMMAND "${CMAKE_COMMAND}" -E rename "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw"
  430. COMMAND "${PVS_STUDIO_CONVERTER}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" -a "${PVS_STUDIO_MODE}"
  431. )
  432. if(NOT PVS_STUDIO_KEEP_COMBINED_PLOG)
  433. list(APPEND COMMANDS COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw")
  434. endif()
  435. endif ()
  436. else ()
  437. set(COMMANDS COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_LOG}")
  438. set(COMMENT "Generating ${LOG_RELATIVE}: no sources found")
  439. endif ()
  440. if (WIN32)
  441. string(REPLACE / \\ PVS_STUDIO_LOG "${PVS_STUDIO_LOG}")
  442. endif ()
  443. add_custom_command(OUTPUT "${PVS_STUDIO_LOG}"
  444. ${COMMANDS}
  445. COMMENT "${COMMENT}"
  446. DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES}
  447. WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
  448. if (PVS_STUDIO_ALL)
  449. set(ALL "ALL")
  450. else ()
  451. set(ALL "")
  452. endif ()
  453. if (PVS_STUDIO_OUTPUT)
  454. if (PVS_STUDIO_HIDE_HELP AND NOT WIN32)
  455. set(COMMANDS COMMAND grep -v " error: Help:" ${PVS_STUDIO_LOG} 1>&2 || exit 0)
  456. elseif (WIN32)
  457. set(COMMANDS COMMAND type "${PVS_STUDIO_LOG}" 1>&2)
  458. else ()
  459. set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2)
  460. endif()
  461. else ()
  462. set(COMMANDS "")
  463. endif ()
  464. add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${PVS_STUDIO_DEPENDS} "${PVS_STUDIO_LOG}")
  465. # A workaround to add implicit dependencies of source files from include directories
  466. set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES INCLUDE_DIRECTORIES "${inc_path}")
  467. endfunction ()