Reputation: 2101
The goal here to help people catch some bugs on their embedded systems when developing with C++. As part of this I'm trying to get clang-tidy to work for small embedded targets.
I'm trying setup CMake to do the following:
However, clang-tidy fails with "unknown target CPU 'armv6-m"
--
The CMake script I've used is as follows:
ClangTidy.cmake
set(ENABLE_CLANG_TIDY ON CACHE BOOL "Add clang-tidy automatically to builds")
if (ENABLE_CLANG_TIDY)
find_program(CLANG_TIDY_EXE NAMES "C:\\Program Files\\LLVM\\bin\\clang-tidy.exe")
if (CLANG_TIDY_EXE)
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
set(CLANG_TIDY_CHECKS "-*,modernize-*")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};-checks=${CLANG_TIDY_CHECKS};-header-filter='${CMAKE_SOURCE_DIR}/*'"
CACHE STRING "" FORCE)
else()
message(AUTHOR_WARNING "clang-tidy not found!")
set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE) # delete it
endif()
endif()
armv6-m is part of the CMake script, but it is only used for GCC with the following being used to setup the GCC flags:
set(TARGET STM32F070x6)
set(ARCH armv6-m)
set(CORE cortex-m0)
set(CMAKE_CXX_FLAGS "-march=${ARCH} -mcpu=${CORE} -D${TARGET}")
And then the following to build:
#
# Including extra cmake rules
include(cmake/ClangTidy.cmake)
#Compile and link the exe :)
add_executable(${TARGET}.elf ${MAIN_SOURCE})
#Print the size of the .hex
add_custom_target(size ALL arm-none-eabi-size ${TARGET}.elf DEPENDS ${TARGET}.elf)
add_custom_target(${TARGET}.bin ALL DEPENDS ${TARGET}.elf COMMAND ${CMAKE_OBJCOPY} -Obinary ${TARGET}.elf ${TARGET}.bin)
The resulting build message from cmake build is:
[build] "C:\Program Files\CMake\bin\cmake.exe" -E __run_co_compile
--tidy="C:/Program Files/LLVM/bin/clang-tidy.exe;-checks=-*,modernize-*;-header-filter='G:/Files/Git/projectname/*'" --source=../src/main.cpp
-- C:\PROGRA~2\GNUTOO~1\82018-~1\bin\AR10B2~1.EXE -DHSE_VALUE=48000000 -DSTM32F070x6 -DTRACE -DUSE_STDPERIPH_DRIVER -I../include -I../system -I../library -I../library/usb -I../library/usb/HID -I../library/usb/LL -I../library/usb/Standard -I../library/usb/Types -I../library/common -I../library/common/SCPI -I../library/common/containers -I../library/peripherals -I../library/peripherals/Bitbanging -I../st-library/include -I../st-library/include/arm -I../st-library/include/cmsis -I../st-library/include/cortexm -I../st-library/include/diag -I../st-library/include/stm32f0-stdperiph -march=armv6-m -mcpu=cortex-m0 -DSTM32F070x6 -Os -mthumb -g2 -ggdb -pedantic -Wall -Wextra -Wfloat-equal -Wshadow -Wall -Wl,--gc-sections -fmessage-length=0 -ffunction-sections -fdata-sections -ffreestanding -fno-builtin -std=c++1z -fno-rtti -fno-exceptions -fno-use-cxa-atexit -fno-threadsafe-statics -ftemplate-backtrace-limit=0 -O2 -g -DNDEBUG -MD -MT CMakeFiles/STM32F070x6.elf.dir/src/main.cpp.obj -MF CMakeFiles\STM32F070x6.elf.dir\src\main.cpp.obj.d -o CMakeFiles/STM32F070x6.elf.dir/src/main.cpp.obj -c ../src/main.cpp
Immediately after the above the error occurs:
[build] error: unknown target CPU 'armv6-m' [clang-diagnostic-error]
[build] note: valid target CPU values are: nocona, core2, penryn, bonnell, atom, silvermont, slm, goldmont, goldmont-plus, tremont, nehalem, corei7, westmere, sandybridge, corei7-avx, ivybridge, core-avx-i, haswell, core-avx2, broadwell, skylake, skylake-avx512, skx, cascadelake, cannonlake, icelake-client, icelake-server, knl, knm, k8, athlon64, athlon-fx, opteron, k8-sse3, athlon64-sse3, opteron-sse3, amdfam10, barcelona, btver1, btver2, bdver1, bdver2, bdver3, bdver4, znver1, x86-64
The target ( armv6-m ) field appears to be the same one that GCC used. But I'm not sure why clang-tidy is being called with the target field.
Upvotes: 7
Views: 3869
Reputation: 11388
I had come up with this solution a couple of years ago but had to revisit it recently for another platform. For anyone who likes Python, here it is:
set(LLVM_ROOT /opt/llvm-14.0.6)
set(LLVM_BIN ${LLVM_ROOT}/bin)
set(CLANG_TIDY ${LLVM_BIN}/clang-tidy)
set(CLANG_TIDY_ARGS --use-color)
set(CLANG_TIDY_ON_ARM python3 ${CMAKE_SOURCE_DIR}/cmake/llvm/clang-tidy-on-arm.py)
if(NOT EXISTS ${CLANG_TIDY})
message(FATAL_ERROR
"Cannot find Clang Tidy executable at path: ${CLANG_TIDY}\n"
"Please make sure you have a working copy of LLVM installed to this directory: "
"${LLVM_ROOT}"
)
endif()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")
set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY_ON_ARM} ${CLANG_TIDY} ${CLANG_TIDY_ARGS})
else()
set(CMAKE_C_CLANG_TIDY ${CLANG_TIDY} ${CLANG_TIDY_ARGS})
endif()
# This is a wrapper script that converts a GCC compilation command for a Zynq
# target to a Clang-compatible command on ARM.
import os.path
import subprocess
import sys
args = list(sys.argv)
if len(args) < 2 or not args[1].endswith("clang-tidy"):
print(
"error: clang-tidy-on-arm.py: "
"expecting the first argument to be a path to Clang Tidy executable."
)
sys.exit(1)
clang_tidy = args[1]
if not os.path.exists(clang_tidy):
print(
"error: clang-tidy-on-arm.py: "
f"the path to Clang Tidy does not exist: {clang_tidy}."
)
sys.exit(1)
# Remove the first argument (which is the path to this script) because it is not
# to be passed to the underlying Clang Tidy invocation.
_ = args.pop(0)
# Clang does not support this argument: -march=armv7-a.
# error: unknown target CPU 'arm7v-a'
# So we replace it with a generic ARM target.
# See clang -print-targets for available targets.
march_idx = args.index('-march=armv7-a')
args[march_idx] = "--target=arm-none-eabi"
# Clang Tidy needs to access stdio.h and cpuset.h
# This include path seems to be substituted by RTEMS Toolchain automatically,
# but this script has to do it manually here because we are calling Clang Tidy
# which calls into Clang to parse the AST and the RTEMS Toolchain's GCC is not
# used.
args.insert(march_idx, "-I/opt/rtems-6-arm-toolchain/arm-rtems6/include")
print(f"clang-tidy-on-arm.py: checking file: {args[-1]}")
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
print(result.stdout)
if result.returncode != 0:
print(result.stderr)
print(f"clang-tidy-on-arm.py: Clang Tidy has finished with error code: {result.returncode}")
sys.exit(1)
Upvotes: 0
Reputation: 1
As others alluded to this is due to CMake passing all the flags to clang-tidy
and clang-tidy
not understanding all of those flags. In my case it stumbled over the RISC-V march
flag.
The workaround I've used is to generate an intermediate script that uses sed
to strip out the march
flag before passing it to clang-tidy
. This solution is entirely contained within CMake but putting the script on disk is also possible.
My solution (in your case use CMAKE_CXX_CLANG_TIDY
instead):
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy.sh
CONTENT "#!/usr/bin/env sh\nclang-tidy $(echo $@ | sed 's/-march=[^ ]* //')"
FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE)
set(CMAKE_C_CLANG_TIDY "${CMAKE_CURRENT_BINARY_DIR}/clang-tidy.sh")
Upvotes: 0
Reputation: 11
As a workaround you could write a small script which filters the parameters received and then call clang-tidy
with the filtered list of parameters. This script can then be handed to CMAKE_CXX_CLANG_TIDY
instead of the actual executable.
Upvotes: 1
Reputation: 9309
in the source code we can see:
static int HandleTidy(const std::string& runCmd, const std::string& sourceFile,
const std::vector<std::string>& orig_cmd)
{
// Construct the clang-tidy command line by taking what was given
// and adding our compiler command line. The clang-tidy tool will
// automatically skip over the compiler itself and extract the
// options.
int ret;
std::vector<std::string> tidy_cmd = cmExpandedList(runCmd, true);
tidy_cmd.push_back(sourceFile);
tidy_cmd.emplace_back("--");
cmAppend(tidy_cmd, orig_cmd);
So I would say, all compiler options i.e. CMAKE_CXX_FLAGS are also passed to clang-tidy by default...
Upvotes: 2