CMake with Clang Tools and Doxygen for Windows

Hanz
5 min readApr 24, 2020

--

What is Clang?

Clang(ˈklæŋ),是一種基於LLVM的Compiler,開發者為Apple及Google。LLVM為現今Mac OS X及iOS開發工具的一部分,是一個open-source的Compiler infrastructure。Clang包含了Clang-tidy 靜態分析工具、Clang-format 格式檢查工具,其強大的檢查及修正功能也應用在許多軟體專案裡,這邊提供GoogleCloudCpp給大家做參考

What is Doxygen?

是一種文件生成器,可提取檔案從原始碼的註解/程式碼,輸出成HTML作為系統手冊。CMake 也整合了Doxygen module,只要安裝好Doxygen,可使用其中的語法設定。

Setup

Install CLANG & Ninja:

Install Doxygen & GraphViz(optional)

Check System Environment:

First, make sure LLVM, Doxygen, GraphViz in system Path variable
add a DEPOT_TOOLS_WIN_TOOLCHAIN system variable and set it to 0.
For Clang-format, add CHROMIUM_BUILDTOOLS_PATH = …\src\buildtools

3. Generate Configuration Files by commands

clang-tidy: 預設會尋找 .clang-tidy 檔案讀取設定

clang-tidy -checks=* -warnings-as-errors=* -header-filter '.*' -dump-config > .clang-tidy

.clang-tidy file sample:

---
# Configure clang-tidy for this project.
# Here is an explanation for why some of the checks are disabled:
#
# -google-readability-namespace-comments: the *_CLIENT_NS is a macro, and
# clang-tidy fails to match it against the initial value.
#
# -modernize-use-trailing-return-type: clang-tidy recommends using
# `auto Foo() -> std::string { return ...; }`, we think the code is less
# readable in this form.
#
# -modernize-return-braced-init-list: We think removing typenames and using
# only braced-init can hurt readability.
#
# -performance-move-const-arg: This warning requires the developer to
# know/care more about the implementation details of types/functions than
# should be necessary. For example, `A a; F(std::move(a));` will trigger a
# warning IFF `A` is a trivial type (and therefore the move is
# meaningless). It would also warn if `F` accepts by `const&`, which is
# another detail that the caller need not care about.
#
# -readability-redundant-declaration: A friend declaration inside a class
# counts as a declaration, so if we also declare that friend outside the
# class in order to document it as part of the public API, that will
# trigger a redundant declaration warning from this check.
#
Checks: >
-*,
bugprone-*,
google-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
-google-readability-braces-around-statements,
-google-readability-namespace-comments,
-google-runtime-references,
-misc-non-private-member-variables-in-classes,
-modernize-return-braced-init-list,
-performance-move-const-arg,
-readability-named-parameter,
-modernize-use-trailing-return-type,
-readability-braces-around-statements,
-readability-redundant-declaration
# Turn all the warnings from the checks above into errors.
WarningsAsErrors: "*"
# TODO(#205) - Enable clang-tidy checks in our headers.
# HeaderFilterRegex: "google/cloud/.*"
CheckOptions:
- { key: readability-identifier-naming.NamespaceCase, value: lower_case }
- { key: readability-identifier-naming.ClassCase, value: CamelCase }
- { key: readability-identifier-naming.StructCase, value: CamelCase }
- { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase }
- { key: readability-identifier-naming.FunctionCase, value: aNy_CasE }
- { key: readability-identifier-naming.VariableCase, value: lower_case }
- { key: readability-identifier-naming.ClassMemberCase, value: lower_case }
- { key: readability-identifier-naming.ClassMemberSuffix, value: _ }
- { key: readability-identifier-naming.PrivateMemberSuffix, value: _ }
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
- { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }
- { key: readability-identifier-naming.EnumConstantCase, value: CamelCase }
- { key: readability-identifier-naming.EnumConstantPrefix, value: k }
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: k }
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
- { key: readability-identifier-naming.MemberConstantCase, value: CamelCase }
- { key: readability-identifier-naming.MemberConstantPrefix, value: k }
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
- { key: readability-identifier-naming.StaticConstantPrefix, value: k }
- { key: readability-implicit-bool-conversion.AllowIntegerConditions, value: 1 }
- { key: readability-implicit-bool-conversion.AllowPointerConditions, value: 1 }

clang-format: 在command裡 指定 -style=file 後,會尋找 .clang-format 檔案讀取設定,一樣可先dump config出來:

clang-format -style=Google -dump-config > .clang-format

.clang-format file sample:

# Use the Google style in this project.
BasedOnStyle: Google
# Some folks prefer to write "int& foo" while others prefer "int &foo". The
# Google Style Guide only asks for consistency within a project, we chose
# "int& foo" for this project:
DerivePointerAlignment: false
PointerAlignment: Left
IncludeBlocks: Merge
IncludeCategories:
# Matches common headers first, but sorts them after project includes
- Regex: '^\"google/cloud/(internal/|grpc_utils/|testing_util/|[^/]+\.h)'
Priority: 1500
- Regex: '^\"google/cloud/' # project includes should sort first
Priority: 500
- Regex: '^\"'
Priority: 1000
- Regex: '^<grpc/'
Priority: 2000
- Regex: '^<google/*'
Priority: 3000
- Regex: '^<.*/.*'
Priority: 4000
- Regex: '^<[^/]*>'
Priority: 5000
# Format raw string literals with a `pb` or `proto` tag as proto.
RawStringFormats:
- Language: TextProto
Delimiters:
- 'pb'
- 'proto'
BasedOnStyle: Google
CommentPragmas: '(@copydoc)'

doxygen: 一樣可以用command產生config file,也可以直接在cmake script中設定相關數值

doxygen -g doxygen_config.in

最基礎的設定值有兩個,分別為 INPUT 和 OUTPUT_DIRECTORY :

# 要輸出說明文件到哪裏 (因為build的時候會進到build folder,這邊指定放在build上層根目錄的doc folder內)
OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/../doc
# 輸入(要處理哪些資料夾底下的檔案) (parse 根目錄的src folder)
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../src

CMake Scripts

Clang-Tidy:

# cmake3.6後,增加了CXX_CLANG_TIDY屬性,透過set_target_properties設定好clang-tidy程式路徑,在建置時就會自動執行clang-tidy掃描這個target對象
set_target_properties(${target} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_PROGRAM}")

Doxygen

# check if Doxygen is installed
find_package(Doxygen)
# Config設定方式1:讀.in檔案
# set input and output files
set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/res/doxygen_config.in)
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/doc/doxygen_config)
# request to configure the file
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
# Config設定方式2:直接設定cmake內建的相關參數,詳情請參考https://github.com/googleapis/google-cloud-cpp/blob/master/cmake/GoogleCloudCppCommon.cmake#95
set(DOXYGEN_RECURSIVE YES)
set(DOXYGEN_FILE_PATTERNS *.h *.cc *.proto *.dox)
set(DOXYGEN_EXAMPLE_RECURSIVE YES)
set(DOXYGEN_EXCLUDE "third_party" "cmake-build-debug" "cmake-out")
set(DOXYGEN_EXCLUDE_SYMLINKS YES)
...
#執行方式1:設定custom_target
# note the option ALL which allows to build the docs together with the application
add_custom_target( doc_doxygen
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM )
#執行方式2:call doxygen_add_docs加入subproject,並透過add_dependencies設定command
# Find out the name of the subproject.
get_filename_component(CPP_SUBPROJECT
"${CMAKE_CURRENT_SOURCE_DIR}" NAME)
doxygen_add_docs(
${CPP_SUBPROJECT}-docs ${CMAKE_CURRENT_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT
"Generate HTML documentation")
add_dependencies(doxygen-docs ${CPP_SUBPROJECT}-docs)
# subproject中自行設定exclude的files
set(DOXYGEN_PROJECT_NAME "audience encode module")
set(DOXYGEN_PROJECT_BRIEF "...encoder...")
set(DOXYGEN_EXCLUDE_PATTERNS
"*/encode/tests/*")

Build Command:

mkdir build
cd build
cmake ..
cmake --build . --target doxygen-docs

Work with IDE (Visual Studio)

首先在Visual Studio Installer勾選C++ Clang工具

vs installer

VS在開啟CMake Project會產生CMakeSettings.json文件,設定CMakeSettings.json指定Generator & Compiler,請注意前面提到的CXX_CLANG_TIDY語法只能在Ninja/Makefile下有效:

使用Visual Studio作為Generator時,可以加上enableClangTidyCodeAnalysis 開啟ClangTidy功能

CMakeLists.txt > Build

Visual Studio上會出現掃描的結果及建議

當專案裡有.clang-format檔案時,開啟Visual Studio時會詢問是否套用,也可以在Tools > Options > Text Editor > C/C++ > Formatting > General 中設定

--

--