// version variable that will be substituted by cmake // This shows an example using the @ variable type constchar* ver = "${cf_example_VERSION}"; constchar* path = "@CMAKE_SOURCE_DIR@";
// version variable that will be substituted by cmake // This shows an example using the @ variable type constchar* ver = "0.2.1"; constchar* path = "~/code/cmake-examples/03-code-generation/configure-files";
// version variable that will be substituted by cmake // This shows an example using the @ variable type constchar* ver = "${cf_example_VERSION}"; constchar* path = "~/code/cmake-examples/03-code-generation/configure-files";
#endif
静态分析
ClangFormat 代码格式化
ClangFormat 基本使用
Clang Format 是非常常用的代码格式化工具,个人以为其命令行使用比 CMake 链接重要。
# Get the path to this file get_filename_component(_clangcheckpath ${CMAKE_CURRENT_LIST_FILE} PATH) # have at least one here by default set(CHANGED_FILE_EXTENSIONS ".cpp") foreach(EXTENSION ${CLANG_FORMAT_CXX_FILE_EXTENSIONS}) set(CHANGED_FILE_EXTENSIONS "${CHANGED_FILE_EXTENSIONS},${EXTENSION}" ) endforeach()
iflen(excludes) > 0: for exclude in excludes: if exclude in directory: directory_excluded = False return output
for root, _, files in os.walk(directory): for file in files: filename = os.path.join(root, file) if check_file(filename, excludes, extensions): print("Will check file [{}]".format(filename)) output.append(filename) return output
defclean_git_filename(line): """ Takes a line from git status --porcelain and returns the filename """ file = None git_status = line[:2] # Not an exhaustive list of git status output but should # be enough for this case # check if this is a delete if'D'in git_status: returnNone # ignored file if'!'in git_status: returnNone # Covers renamed files if'->'in line: file = line[3:].split('->')[-1].strip() else: file = line[3:].strip()
return file
defget_changed_files(git_bin, excludes, file_extensions): """ Run git status and return the list of changed files """ extensions = file_extensions.split(",") # arguments coming from cmake will be *.xx. We want to remove the * for i, extension inenumerate(extensions): if extension[0] == '*': extensions[i] = extension[1:]
defrun_clang_format(clang_format_bin, changed_files): """ Run clang format on a list of files @return 0 if formatted correctly. """ iflen(changed_files) == 0: return0 cmd = [clang_format_bin, "-style=file", "-output-replacements-xml"] + changed_files # print("clang-format cmd = {}".format(cmd)) try: cmd_output = subprocess.check_output(cmd).decode() if"replacement offset"in cmd_output: print("ERROR: Changed files don't match format") return1 except subprocess.CalledProcessError as e: print("Error calling clang-format [{}]".format(e)) return e.returncode
return0
defcli(): # global params parser = argparse.ArgumentParser(prog='clang-format-check-changed', description='Checks if files chagned in git match the .clang-format specification') parser.add_argument("--file-extensions", type=str, default=".cpp,.h,.cxx,.hxx,.hpp,.cc,.ipp", help="Comma separated list of file extensions to check") parser.add_argument('--exclude', action='append', default=[], help='Will not match the files / directories with these in the name') parser.add_argument('--clang-format-bin', type=str, default="clang-format", help="The clang format binary") parser.add_argument('--git-bin', type=str, default="git", help="The git binary") args = parser.parse_args()
# Run gcovr to get the .gcda files form .gcno changed_files, returncode = get_changed_files( args.git_bin, args.exclude, args.file_extensions) if returncode != 0: return returncode
# Locate cppcheck # # This module defines # CPPCHECK_BIN, where to find cppcheck # # To help find the binary you can set CPPCHECK_ROOT_DIR to search a custom path # Exported argumets include # CPPCHECK_FOUND, if false, do not try to link to cppcheck --- if (CPPCHECK_FOUND) # # CPPCHECK_THREADS_ARG - Number of threads to use (e.g. -j 3) # CPPCHECK_PROJECT_ARG - The project to use (compile_comands.json) # CPPCHECK_BUILD_DIR_ARG - The build output directory # CPPCHECK_ERROR_EXITCODE_ARG - The exit code if an error is found # CPPCHECK_SUPPRESSIONS - A suppressiosn file to use # CPPCHECK_CHECKS_ARGS - The checks to run # CPPCHECK_OTHER_ARGS - Any other arguments # CPPCHECK_COMMAND - The full command to run the default cppcheck configuration # CPPCHECK_EXCLUDES - A list of files or folders to exclude from the scan. Must be the full path # # if CPPCHECK_XML_OUTPUT is set before calling this. CppCheck will create an xml file with that name # find the cppcheck binary
# if custom path check there first if(CPPCHECK_ROOT_DIR) find_program(CPPCHECK_BIN NAMES cppcheck PATHS "${CPPCHECK_ROOT_DIR}" NO_DEFAULT_PATH) endif()
set(CPPCHECK_THREADS_ARG "-j4" CACHE STRING"The number of threads to use") set(CPPCHECK_PROJECT_ARG "--project=${PROJECT_BINARY_DIR}/compile_commands.json") set(CPPCHECK_BUILD_DIR_ARG "--cppcheck-build-dir=${PROJECT_BINARY_DIR}/analysis/cppcheck" CACHE STRING"The build directory to use") # Don't show these errors if(EXISTS"${CMAKE_SOURCE_DIR}/.cppcheck_suppressions") set(CPPCHECK_SUPPRESSIONS "--suppressions-list=${CMAKE_SOURCE_DIR}/.cppcheck_suppressions" CACHE STRING"The suppressions file to use") else() set(CPPCHECK_SUPPRESSIONS "" CACHE STRING"The suppressions file to use") endif()
# Show these errors but don't fail the build # These are mainly going to be from the "warning" category that is enabled by default later if(EXISTS"${CMAKE_SOURCE_DIR}/.cppcheck_exitcode_suppressions") set(CPPCHECK_EXITCODE_SUPPRESSIONS "--exitcode-suppressions=${CMAKE_SOURCE_DIR}/.cppcheck_exitcode_suppressions" CACHE STRING"The exitcode suppressions file to use") else() set(CPPCHECK_EXITCODE_SUPPRESSIONS "" CACHE STRING"The exitcode suppressions file to use") endif()
set(CPPCHECK_ERROR_EXITCODE_ARG "--error-exitcode=1" CACHE STRING"The exitcode to use if an error is found") set(CPPCHECK_CHECKS_ARGS "--enable=warning" CACHE STRING"Arguments for the checks to run") set(CPPCHECK_OTHER_ARGS "" CACHE STRING"Other arguments") set(_CPPCHECK_EXCLUDES)
# handle the QUIETLY and REQUIRED arguments and set YAMLCPP_FOUND to TRUE if all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS( CPPCHECK DEFAULT_MSG CPPCHECK_BIN)
if(CPPCHECK_FOUND) file(MAKE_DIRECTORY${CMAKE_BINARY_DIR}/analysis/cppcheck) add_custom_target(cppcheck-analysis COMMAND${CPPCHECK_COMMAND}) message("cppcheck found. Use cppccheck-analysis targets to run it") else() message("cppcheck not found. No cppccheck-analysis targets") endif()
// ----------- catch.hpp:8162 ---------- // 32kb for the alternate stack seems to be sufficient. However, this value // is experimentally determined, so that's not guaranteed. // --------------- 修改前 --------------- constexprstatic std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; // --------------- 修改后 --------------- constexprstatic std::size_t sigStackSize = 32768;
# Prepare "Catch2" library for other executables add_library(Catch2 INTERFACE) add_library(Catch2::Test ALIAS Catch2) target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
运行单元测试与调试
凡是 cmake 构建的测试程序调试方式其实都一样。
1 2 3 4 5 6 7 8 9 10 11 12
# 创建 build 目录 mkdir -p build cd build
# 构建可调试测试程序 cmake .. -D CMAKE_BUILD_TYPE=Debug make
make test# 使用 CTest 测试, 确保测试已注册 ./unit_tests # 直接运行测试 gdb unit_tests # 使用 gdb / lldb / etc. 调试 # ... # 或者使用 IDE 测试 (推荐, 自己配置或者使用 CMake 插件)
书写 Catch2 单元测试
需要注意的是单元测试框架都不能使用 main 函数,因为框架本质就是为用户提供支持测试的 main 函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#define CATCH_CONFIG_MAIN // 为用户生成 main 函数 #include"catch2/catch.hpp"
# set a project version set (deb_example_VERSION_MAJOR 0) set (deb_example_VERSION_MINOR 2) set (deb_example_VERSION_PATCH 2) set (deb_example_VERSION "${deb_example_VERSION_MAJOR}.${deb_example_VERSION_MINOR}.${deb_example_VERSION_PATCH}")
############################################################ # Create a library ############################################################
#Generate the shared library from the library sources add_library(cmake_examples_deb SHARED src/Hello.cpp)
target_include_directories(cmake_examples_deb PUBLIC ${PROJECT_SOURCE_DIR}/include ) ############################################################ # Create an executable ############################################################
# Add an executable with the above sources add_executable(cmake_examples_deb_bin src/main.cpp)
# link the new hello_library target with the hello_binary target target_link_libraries( cmake_examples_deb_bin PUBLIC cmake_examples_deb )