# SPDX-License-Identifier: Apache-2.0 # # Copyright (c) 2023, Basalte bv include(boards) include(git) include(extensions) include(west) find_program(CODECHECKER_EXE NAMES CodeChecker codechecker REQUIRED) message(STATUS "Found SCA: CodeChecker (${CODECHECKER_EXE})") # Get CodeChecker specific variables zephyr_get(CODECHECKER_ANALYZE_JOBS) zephyr_get(CODECHECKER_ANALYZE_OPTS) zephyr_get(CODECHECKER_CLEANUP) zephyr_get(CODECHECKER_CONFIG_FILE) zephyr_get(CODECHECKER_EXPORT) zephyr_get(CODECHECKER_NAME) zephyr_get(CODECHECKER_PARSE_EXIT_STATUS) zephyr_get(CODECHECKER_PARSE_OPTS) zephyr_get(CODECHECKER_PARSE_SKIP) zephyr_get(CODECHECKER_STORE) zephyr_get(CODECHECKER_STORE_OPTS) zephyr_get(CODECHECKER_STORE_TAG) zephyr_get(CODECHECKER_TRIM_PATH_PREFIX MERGE VAR CODECHECKER_TRIM_PATH_PREFIX WEST_TOPDIR) # Get twister runner specific variables zephyr_get(TC_RUNID) zephyr_get(TC_NAME) if(NOT CODECHECKER_NAME) if(TC_NAME) set(CODECHECKER_NAME "${BOARD}${BOARD_QUALIFIERS}:${TC_NAME}") else() set(CODECHECKER_NAME zephyr) endif() endif() if(CODECHECKER_ANALYZE_JOBS) set(CODECHECKER_ANALYZE_JOBS "--jobs;${CODECHECKER_ANALYZE_JOBS}") elseif(TC_RUNID) set(CODECHECKER_ANALYZE_JOBS "--jobs;1") endif() if(CODECHECKER_CONFIG_FILE) set(CODECHECKER_CONFIG_FILE "--config;${CODECHECKER_CONFIG_FILE}") endif() if(CODECHECKER_STORE_TAG) set(CODECHECKER_STORE_TAG "--tag;${CODECHECKER_STORE_TAG}") else() git_describe(${APPLICATION_SOURCE_DIR} app_version) if(app_version) set(CODECHECKER_STORE_TAG "--tag;${app_version}") endif() endif() if(CODECHECKER_TRIM_PATH_PREFIX) set(CODECHECKER_TRIM_PATH_PREFIX "--trim-path-prefix;${CODECHECKER_TRIM_PATH_PREFIX}") endif() # CodeChecker uses the compile_commands.json as input set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Create an output directory for our tool set(output_dir ${CMAKE_BINARY_DIR}/sca/codechecker) file(MAKE_DIRECTORY ${output_dir}) # Use a dummy file to let CodeChecker know we can start analyzing set_property(GLOBAL APPEND PROPERTY extra_post_build_commands COMMAND ${CMAKE_COMMAND} -E touch ${output_dir}/codechecker.ready) set_property(GLOBAL APPEND PROPERTY extra_post_build_byproducts ${output_dir}/codechecker.ready) add_custom_target(codechecker ALL COMMAND ${CODECHECKER_EXE} analyze --keep-gcc-include-fixed --keep-gcc-intrin --output ${output_dir}/codechecker.plist --name ${CODECHECKER_NAME} # Set a default metadata name ${CODECHECKER_CONFIG_FILE} ${CODECHECKER_ANALYZE_JOBS} ${CODECHECKER_ANALYZE_OPTS} ${CMAKE_BINARY_DIR}/compile_commands.json || ${CMAKE_COMMAND} -E true # allow to continue processing results DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json ${output_dir}/codechecker.ready VERBATIM USES_TERMINAL COMMAND_EXPAND_LISTS ) # Cleanup dummy file add_custom_command( TARGET codechecker POST_BUILD COMMAND ${CMAKE_COMMAND} -E rm ${output_dir}/codechecker.ready ) if(CODECHECKER_CLEANUP) add_custom_target(codechecker-cleanup ALL COMMAND ${CMAKE_COMMAND} -E rm -r ${output_dir}/codechecker.plist ) else() add_custom_target(codechecker-cleanup) endif() add_dependencies(codechecker-cleanup codechecker) # If 'codechecker parse' returns an exit status of '2', it means more than 0 # issues were detected. Suppress the exit status by default, but permit opting # in to the failure. if(NOT CODECHECKER_PARSE_EXIT_STATUS) set(CODECHECKER_PARSE_OPTS ${CODECHECKER_PARSE_OPTS} || ${CMAKE_COMMAND} -E true) endif() if(DEFINED CODECHECKER_EXPORT) string(REPLACE "," ";" export_list ${CODECHECKER_EXPORT}) foreach(export_item IN LISTS export_list) message(STATUS "CodeChecker export: ${CMAKE_BINARY_DIR}/codechecker.${export_item}") add_custom_target(codechecker-report-${export_item} ALL COMMAND ${CODECHECKER_EXE} parse ${output_dir}/codechecker.plist --export ${export_item} --output ${output_dir}/codechecker.${export_item} ${CODECHECKER_CONFIG_FILE} ${CODECHECKER_TRIM_PATH_PREFIX} ${CODECHECKER_PARSE_OPTS} BYPRODUCTS ${output_dir}/codechecker.${export_item} VERBATIM USES_TERMINAL COMMAND_EXPAND_LISTS ) add_dependencies(codechecker-report-${export_item} codechecker) add_dependencies(codechecker-cleanup codechecker-report-${export_item}) endforeach() elseif(NOT CODECHECKER_PARSE_SKIP) # Output parse results add_custom_target(codechecker-parse ALL COMMAND ${CODECHECKER_EXE} parse ${output_dir}/codechecker.plist ${CODECHECKER_CONFIG_FILE} ${CODECHECKER_TRIM_PATH_PREFIX} ${CODECHECKER_PARSE_OPTS} VERBATIM USES_TERMINAL COMMAND_EXPAND_LISTS ) add_dependencies(codechecker-parse codechecker) add_dependencies(codechecker-cleanup codechecker-parse) endif() if(DEFINED CODECHECKER_STORE OR DEFINED CODECHECKER_STORE_OPTS) add_custom_target(codechecker-store ALL COMMAND ${CODECHECKER_EXE} store ${CODECHECKER_CONFIG_FILE} ${CODECHECKER_STORE_TAG} ${CODECHECKER_TRIM_PATH_PREFIX} ${CODECHECKER_STORE_OPTS} ${output_dir}/codechecker.plist VERBATIM USES_TERMINAL COMMAND_EXPAND_LISTS ) add_dependencies(codechecker-store codechecker) add_dependencies(codechecker-cleanup codechecker-store) endif()