1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (c) 2023, Basalte bv
4
5include(boards)
6include(git)
7include(extensions)
8include(west)
9
10find_program(CODECHECKER_EXE NAMES CodeChecker codechecker REQUIRED)
11message(STATUS "Found SCA: CodeChecker (${CODECHECKER_EXE})")
12
13# Get CodeChecker specific variables
14zephyr_get(CODECHECKER_ANALYZE_JOBS)
15zephyr_get(CODECHECKER_ANALYZE_OPTS)
16zephyr_get(CODECHECKER_CLEANUP)
17zephyr_get(CODECHECKER_CONFIG_FILE)
18zephyr_get(CODECHECKER_EXPORT)
19zephyr_get(CODECHECKER_NAME)
20zephyr_get(CODECHECKER_PARSE_EXIT_STATUS)
21zephyr_get(CODECHECKER_PARSE_OPTS)
22zephyr_get(CODECHECKER_PARSE_SKIP)
23zephyr_get(CODECHECKER_STORE)
24zephyr_get(CODECHECKER_STORE_OPTS)
25zephyr_get(CODECHECKER_STORE_TAG)
26zephyr_get(CODECHECKER_TRIM_PATH_PREFIX MERGE VAR CODECHECKER_TRIM_PATH_PREFIX WEST_TOPDIR)
27
28# Get twister runner specific variables
29zephyr_get(TC_RUNID)
30zephyr_get(TC_NAME)
31
32if(NOT CODECHECKER_NAME)
33  if(TC_NAME)
34    set(CODECHECKER_NAME "${BOARD}${BOARD_QUALIFIERS}:${TC_NAME}")
35  else()
36    set(CODECHECKER_NAME zephyr)
37  endif()
38endif()
39
40if(CODECHECKER_ANALYZE_JOBS)
41  set(CODECHECKER_ANALYZE_JOBS "--jobs;${CODECHECKER_ANALYZE_JOBS}")
42elseif(TC_RUNID)
43  set(CODECHECKER_ANALYZE_JOBS "--jobs;1")
44endif()
45
46if(CODECHECKER_CONFIG_FILE)
47  set(CODECHECKER_CONFIG_FILE "--config;${CODECHECKER_CONFIG_FILE}")
48endif()
49
50if(CODECHECKER_STORE_TAG)
51  set(CODECHECKER_STORE_TAG "--tag;${CODECHECKER_STORE_TAG}")
52else()
53  git_describe(${APPLICATION_SOURCE_DIR} app_version)
54  if(app_version)
55    set(CODECHECKER_STORE_TAG "--tag;${app_version}")
56  endif()
57endif()
58
59if(CODECHECKER_TRIM_PATH_PREFIX)
60  set(CODECHECKER_TRIM_PATH_PREFIX "--trim-path-prefix;${CODECHECKER_TRIM_PATH_PREFIX}")
61endif()
62
63# CodeChecker uses the compile_commands.json as input
64set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
65
66# Create an output directory for our tool
67set(output_dir ${CMAKE_BINARY_DIR}/sca/codechecker)
68file(MAKE_DIRECTORY ${output_dir})
69
70# Use a dummy file to let CodeChecker know we can start analyzing
71set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND
72  ${CMAKE_COMMAND} -E touch ${output_dir}/codechecker.ready)
73set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts
74  ${output_dir}/codechecker.ready)
75
76add_custom_target(codechecker ALL
77  COMMAND ${CODECHECKER_EXE} analyze
78    --keep-gcc-include-fixed
79    --keep-gcc-intrin
80    --output ${output_dir}/codechecker.plist
81    --name ${CODECHECKER_NAME} # Set a default metadata name
82    ${CODECHECKER_CONFIG_FILE}
83    ${CODECHECKER_ANALYZE_JOBS}
84    ${CODECHECKER_ANALYZE_OPTS}
85    ${CMAKE_BINARY_DIR}/compile_commands.json
86    || ${CMAKE_COMMAND} -E true # allow to continue processing results
87  DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json ${output_dir}/codechecker.ready
88  VERBATIM
89  USES_TERMINAL
90  COMMAND_EXPAND_LISTS
91)
92
93# Cleanup dummy file
94add_custom_command(
95  TARGET codechecker POST_BUILD
96  COMMAND ${CMAKE_COMMAND} -E rm ${output_dir}/codechecker.ready
97)
98
99if(CODECHECKER_CLEANUP)
100  add_custom_target(codechecker-cleanup ALL
101    COMMAND ${CMAKE_COMMAND} -E rm -r ${output_dir}/codechecker.plist
102  )
103else()
104  add_custom_target(codechecker-cleanup)
105endif()
106
107add_dependencies(codechecker-cleanup codechecker)
108
109# If 'codechecker parse' returns an exit status of '2', it means more than 0
110# issues were detected. Suppress the exit status by default, but permit opting
111# in to the failure.
112set(CODECHECKER_PARSE_OPTS ${CODECHECKER_PARSE_OPTS} || ${CMAKE_COMMAND} -E touch ${output_dir}/codechecker.failed)
113if(CODECHECKER_PARSE_EXIT_STATUS)
114  add_custom_target(codechecker-parse-check ALL
115    COMMAND ! ${CMAKE_COMMAND} -E rm ${output_dir}/codechecker.failed 2>/dev/null
116  )
117else()
118  add_custom_target(codechecker-parse-check ALL
119    COMMAND ${CMAKE_COMMAND} -E rm -f ${output_dir}/codechecker.failed 2>/dev/null
120  )
121endif()
122add_dependencies(codechecker-cleanup codechecker-parse-check)
123
124if(DEFINED CODECHECKER_EXPORT)
125  string(REPLACE "," ";" export_list ${CODECHECKER_EXPORT})
126
127  foreach(export_item IN LISTS export_list)
128    message(STATUS "CodeChecker export: ${CMAKE_BINARY_DIR}/codechecker.${export_item}")
129
130    add_custom_target(codechecker-report-${export_item} ALL
131      COMMAND ${CODECHECKER_EXE} parse
132        ${output_dir}/codechecker.plist
133        --export ${export_item}
134        --output ${output_dir}/codechecker.${export_item}
135        ${CODECHECKER_CONFIG_FILE}
136        ${CODECHECKER_TRIM_PATH_PREFIX}
137        ${CODECHECKER_PARSE_OPTS}
138      BYPRODUCTS ${output_dir}/codechecker.${export_item}
139      VERBATIM
140      USES_TERMINAL
141      COMMAND_EXPAND_LISTS
142    )
143    add_dependencies(codechecker-report-${export_item} codechecker)
144    add_dependencies(codechecker-parse-check codechecker-report-${export_item})
145  endforeach()
146endif()
147
148if(NOT CODECHECKER_PARSE_SKIP)
149  # Output parse results
150    add_custom_target(codechecker-parse ALL
151    COMMAND ${CODECHECKER_EXE} parse
152      ${output_dir}/codechecker.plist
153      ${CODECHECKER_CONFIG_FILE}
154      ${CODECHECKER_TRIM_PATH_PREFIX}
155      ${CODECHECKER_PARSE_OPTS}
156    VERBATIM
157    USES_TERMINAL
158    COMMAND_EXPAND_LISTS
159  )
160  add_dependencies(codechecker-parse codechecker)
161  add_dependencies(codechecker-parse-check codechecker-parse)
162endif()
163
164if(DEFINED CODECHECKER_STORE OR DEFINED CODECHECKER_STORE_OPTS)
165  add_custom_target(codechecker-store ALL
166    COMMAND ${CODECHECKER_EXE} store
167      ${CODECHECKER_CONFIG_FILE}
168      ${CODECHECKER_STORE_TAG}
169      ${CODECHECKER_TRIM_PATH_PREFIX}
170      ${CODECHECKER_STORE_OPTS}
171      ${output_dir}/codechecker.plist
172    VERBATIM
173    USES_TERMINAL
174    COMMAND_EXPAND_LISTS
175  )
176  add_dependencies(codechecker-store codechecker)
177  add_dependencies(codechecker-cleanup codechecker-store)
178endif()
179