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.
112if(NOT CODECHECKER_PARSE_EXIT_STATUS)
113  set(CODECHECKER_PARSE_OPTS ${CODECHECKER_PARSE_OPTS} || ${CMAKE_COMMAND} -E true)
114endif()
115
116if(DEFINED CODECHECKER_EXPORT)
117  string(REPLACE "," ";" export_list ${CODECHECKER_EXPORT})
118
119  foreach(export_item IN LISTS export_list)
120    message(STATUS "CodeChecker export: ${CMAKE_BINARY_DIR}/codechecker.${export_item}")
121
122    add_custom_target(codechecker-report-${export_item} ALL
123      COMMAND ${CODECHECKER_EXE} parse
124        ${output_dir}/codechecker.plist
125        --export ${export_item}
126        --output ${output_dir}/codechecker.${export_item}
127        ${CODECHECKER_CONFIG_FILE}
128        ${CODECHECKER_TRIM_PATH_PREFIX}
129        ${CODECHECKER_PARSE_OPTS}
130      BYPRODUCTS ${output_dir}/codechecker.${export_item}
131      VERBATIM
132      USES_TERMINAL
133      COMMAND_EXPAND_LISTS
134    )
135    add_dependencies(codechecker-report-${export_item} codechecker)
136    add_dependencies(codechecker-cleanup codechecker-report-${export_item})
137  endforeach()
138elseif(NOT CODECHECKER_PARSE_SKIP)
139  # Output parse results
140    add_custom_target(codechecker-parse ALL
141    COMMAND ${CODECHECKER_EXE} parse
142      ${output_dir}/codechecker.plist
143      ${CODECHECKER_CONFIG_FILE}
144      ${CODECHECKER_TRIM_PATH_PREFIX}
145      ${CODECHECKER_PARSE_OPTS}
146    VERBATIM
147    USES_TERMINAL
148    COMMAND_EXPAND_LISTS
149  )
150  add_dependencies(codechecker-parse codechecker)
151  add_dependencies(codechecker-cleanup codechecker-parse)
152endif()
153
154if(DEFINED CODECHECKER_STORE OR DEFINED CODECHECKER_STORE_OPTS)
155  add_custom_target(codechecker-store ALL
156    COMMAND ${CODECHECKER_EXE} store
157      ${CODECHECKER_CONFIG_FILE}
158      ${CODECHECKER_STORE_TAG}
159      ${CODECHECKER_TRIM_PATH_PREFIX}
160      ${CODECHECKER_STORE_OPTS}
161      ${output_dir}/codechecker.plist
162    VERBATIM
163    USES_TERMINAL
164    COMMAND_EXPAND_LISTS
165  )
166  add_dependencies(codechecker-store codechecker)
167  add_dependencies(codechecker-cleanup codechecker-store)
168endif()
169