Skip to content

Commit bf33860

Browse files
authored
Merge pull request #46 from lefticus/static_analysis_updates
2 parents a1c1815 + bea3226 commit bf33860

6 files changed

Lines changed: 211 additions & 69 deletions

File tree

README.md

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,23 @@ Similar to `target_include_directories`, but it suppresses the warnings. It is u
125125

126126
## Changing the project_options parameters dynamically
127127

128-
It might be useful to change the test and development options on the fly (e.g., to enable sanitizers when running tests). To do this, you can include the `GlobalOptions.cmake`, which adds global options for the arguments of `project_options` function.
128+
It might be useful to change the test and development options on the fly (e.g., to enable sanitizers when running tests). To do this, you can include the `DynamicOptions.cmake`, which adds `dynamic_project_options`. `dynamic_project_options` provides a recommended set of defaults (all static analysis and runtime analysis enabled for platforms where that is possible) while also providing a high level option `ENABLE_DEVELOPER_MODE` (defaulted to `ON`) which can be turned off for easy use by non-developers.
129+
130+
The goal of the `dynamic_project_options` is to give a safe and well analyzed environment to the developer by default, while simultaneously making it easy for a user of the project to compile while not having to worry about clang-tidy, sanitizers, cppcheck, etc.
131+
132+
The defaults presented to the user can be modified with
133+
134+
* `set(<featurename>_DEFAULT value)` - for user and developer builds
135+
* `set(<featurename>_USER_DEFAULT value)` - for user builds
136+
* `set(<featureoptionname>_DEVELOPER_DEFAULT value)` - for developer builds
137+
138+
If you need to fix a setting for the sake of a command-line configuration, you can use:
139+
140+
```
141+
cmake -DOPT_<featurename>:BOOL=value
142+
```
143+
129144

130-
⚠️ It is highly recommended to keep the build declarative and reproducible by using the function arguments as explained above. The user of your code should not need to pass any special flags to build the library for normal usage. Please do not use this for anything other than test and development.
131145

132146
<details>
133147
<summary>Click to show the example:</summary>
@@ -148,8 +162,8 @@ FetchContent_Declare(_project_options URL https://github.com/cpp-best-practices/
148162
FetchContent_MakeAvailable(_project_options)
149163
include(${_project_options_SOURCE_DIR}/Index.cmake)
150164
151-
# ❗ Add global CMake options
152-
include(${_project_options_SOURCE_DIR}/src/GlobalOptions.cmake)
165+
# ❗ Add dynamic CMake options
166+
include(${_project_options_SOURCE_DIR}/src/DynamicOptions.cmake)
153167
154168
# uncomment to enable vcpkg:
155169
# # Setup vcpkg - should be called before defining project()
@@ -158,43 +172,16 @@ include(${_project_options_SOURCE_DIR}/src/GlobalOptions.cmake)
158172
# Set the project name and language
159173
project(myproject LANGUAGES CXX)
160174
161-
# ❗ enable sanitizers if running the tests
162-
option(FEATURE_TESTS "Enable the tests" OFF)
163-
if(FEATURE_TESTS)
164-
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
165-
set(ENABLE_SANITIZER_ADDRESS OFF)
166-
set(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR OFF)
167-
else()
168-
set(ENABLE_SANITIZER_ADDRESS "ENABLE_SANITIZER_ADDRESS")
169-
set(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "ENABLE_SANITIZER_UNDEFINED_BEHAVIOR")
170-
endif()
171-
endif()
175+
# Set PCH to be on by default for all non-Developer Mode Builds
176+
# (this is just intended as an example of what is possible)
177+
set(ENABLE_PCH_USER_DEFAULT ON)
172178
173179
# Initialize project_options variable related to this project
174180
# This overwrites `project_options` and sets `project_warnings`
175181
# uncomment the options to enable them:
176-
project_options(
177-
ENABLE_CACHE
178-
ENABLE_CPPCHECK
179-
ENABLE_CLANG_TIDY
180-
# WARNINGS_AS_ERRORS
181-
# ENABLE_CONAN
182-
# ENABLE_IPO
183-
# ENABLE_INCLUDE_WHAT_YOU_USE
184-
# ENABLE_COVERAGE
185-
# ENABLE_PCH
186-
# PCH_HEADERS
187-
# ENABLE_DOXYGEN
188-
# ENABLE_IPO
189-
# ENABLE_USER_LINKER
190-
# ENABLE_BUILD_WITH_TIME_TRACE
191-
# ENABLE_UNITY
192-
# ❗ Now, the address and undefined behavior sanitizers are enabled through CMake options
193-
${ENABLE_SANITIZER_ADDRESS}
194-
${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}
195-
# ENABLE_SANITIZER_LEAK
196-
# ENABLE_SANITIZER_THREAD
197-
# ENABLE_SANITIZER_MEMORY
182+
dynamic_project_options(
183+
# set PCH headers you want enabled. Format can be slow, so this might be helpful
184+
PCH_HEADERS <vector> <string> <fmt/format.h>
198185
)
199186
# add your executables, libraries, etc. here:
200187

src/DynamicProjectOptions.cmake

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# ENABLE_DEVELOPER_MODE: sets defaults appropriate for developers, this is defaulted to ON
2+
# * WARNINGS_AS_ERRORS: ON
3+
# * ENABLE_SANITIZER_ADDRESS: ON
4+
# * ENABLE_CLANG_TIDY: ON for Ninja/Makefiles
5+
# * ENABLE_SANITIZER_UNDEFINED: ON for Compilers that support it
6+
# * ENABLE_CPPCHECK: ON for Ninja/Makefiles
7+
8+
# For non-developer builds
9+
# -DENABLE_DEVELOPER_MODE:BOOL=OFF
10+
# Is recommended
11+
12+
# In developer mode, all features have options that show up in the CMake GUI tools
13+
14+
# dynamic_project_options() macro enables all recommended defaults with appropriately
15+
# applied options from the GUI which are set
16+
17+
# Any default can be overridden
18+
# set(<feature_name>_DEFAULT <value>) - set default for both user and developer modes
19+
# set(<feature_name>_DEVELOPER_DEFAULT <value>) - set default for developer mode
20+
# set(<feature_name>_USER_DEFAULT <value>) - set default for user mode
21+
22+
option(ENABLE_DEVELOPER_MODE "Set up defaults for a developer of the project, and let developer change options" ON)
23+
24+
if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32)
25+
set(SUPPORTS_UBSAN ON)
26+
else()
27+
set(SUPPORTS_UBSAN OFF)
28+
endif()
29+
30+
if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND WIN32)
31+
set(SUPPORTS_ASAN OFF)
32+
else()
33+
set(SUPPORTS_ASAN ON)
34+
endif()
35+
36+
# ccache, clang-tidy, cppcheck are only supported with Ninja and Makefile based generators
37+
# note that it is possible to use Ninja with cl, so this still allows clang-tidy on Windows
38+
# with CL.
39+
#
40+
# We are only setting the default options here. If the user attempts to enable
41+
# these tools on a platform with unknown support, they are on their own.
42+
#
43+
# Also note, cppcheck has an option to be run on VCproj files, so we should investigate that
44+
# Further note: MSVC2022 has builtin support for clang-tidy, but I can find
45+
# no way to enable that via CMake
46+
if(CMAKE_GENERATOR MATCHES ".*Makefile*." OR CMAKE_GENERATOR MATCHES ".*Ninja*")
47+
set(MAKEFILE_OR_NINJA ON)
48+
else()
49+
set(MAKEFILE_OR_NINJA OFF)
50+
endif()
51+
52+
include(CMakeDependentOption)
53+
54+
# <option name>;<user mode default>;<developer mode default>;<description>
55+
set(options
56+
"ENABLE_CACHE\;${MAKEFILE_OR_NINJA}\;${MAKEFILE_OR_NINJA}\;Enable ccache on Unix"
57+
"WARNINGS_AS_ERRORS\;OFF\;ON\;Treat warnings as Errors"
58+
"ENABLE_CLANG_TIDY\;OFF\;${MAKEFILE_OR_NINJA}\;Enable clang-tidy analysis during compilation"
59+
"ENABLE_CONAN\;ON\;ON\;Automatically integrate Conan for package management"
60+
"ENABLE_COVERAGE\;OFF\;OFF\;Analyze and report on coverage"
61+
"ENABLE_SANITIZER_ADDRESS\;OFF\;${SUPPORTS_ASAN}\;Make memory errors into hard runtime errors (windows/linux/macos)"
62+
"ENABLE_SANITIZER_UNDEFINED_BEHAVIOR\;OFF\;${SUPPORTS_UBSAN}\;Make certain types (numeric mostly) of undefined behavior into runtime errors"
63+
"ENABLE_CPPCHECK\;OFF\;${MAKEFILE_OR_NINJA}\;Enable cppcheck analysis during compilation"
64+
"ENABLE_IPO\;OFF\;OFF\;Enable whole-program optimization"
65+
"ENABLE_INCLUDE_WHAT_YOU_USE\;OFF\;OFF\;Enable include-what-you-use analysis during compilation"
66+
"ENABLE_PCH\;OFF\;OFF\;Enable pre-compiled-headers support"
67+
"ENABLE_DOXYGEN\;OFF\;OFF\;Build documentation with Doxygen"
68+
"ENABLE_USER_LINKER\;OFF\;OFF\;Allow custom linker settings"
69+
"ENABLE_BUILD_WITH_TIME_TRACE\;OFF\;OFF\;Generates report of where compile-time is spent"
70+
"ENABLE_UNITY\;OFF\;OFF\;Merge C++ files into larger C++ files, can speed up compilation sometimes"
71+
"ENABLE_SANITIZER_LEAK\;OFF\;OFF\;Make memory leaks into hard runtime errors"
72+
"ENABLE_SANITIZER_THREAD\;OFF\;OFF\;Make thread race conditions into hard runtime errors"
73+
"ENABLE_SANITIZER_MEMORY\;OFF\;OFF\;Make other memory errors into runtime errors")
74+
75+
foreach(option ${options})
76+
list(
77+
GET
78+
option
79+
0
80+
option_name)
81+
list(
82+
GET
83+
option
84+
1
85+
option_user_default)
86+
list(
87+
GET
88+
option
89+
2
90+
option_developer_default)
91+
list(
92+
GET
93+
option
94+
3
95+
option_description)
96+
97+
if(DEFINED ${option_name}_DEFAULT)
98+
if(DEFINED ${option_name}_DEVELOPER_DEFAULT OR DEFINED ${option_name}_USER_DEFAULT)
99+
message(
100+
SEND_ERROR
101+
"You have separately defined user/developer defaults and general defaults for ${option_name}. Please either provide a general default OR separate developer/user overrides"
102+
)
103+
endif()
104+
105+
set(option_user_default ${${option_name}_DEFAULT})
106+
set(option_developer_default ${${option_name}_DEFAULT})
107+
endif()
108+
109+
if(DEFINED ${option_name}_USER_DEFAULT)
110+
set(option_user_default ${${option_name}_USER_DEFAULT})
111+
endif()
112+
113+
if(DEFINED ${option_name}_DEVELOPER_DEFAULT)
114+
set(option_developer_default ${${option_name}_DEVELOPER_DEFAULT})
115+
endif()
116+
117+
cmake_dependent_option(
118+
OPT_${option_name}
119+
"${option_description}"
120+
${option_developer_default}
121+
ENABLE_DEVELOPER_MODE
122+
${option_user_default})
123+
124+
if(OPT_${option_name})
125+
set(${option_name}_VALUE ${option_name})
126+
else()
127+
unset(${option_name}_VALUE)
128+
endif()
129+
endforeach()
130+
131+
macro(dynamic_project_options)
132+
project_options(
133+
${ENABLE_CONAN_VALUE}
134+
${ENABLE_CACHE_VALUE}
135+
${WARNINGS_AS_ERRORS_VALUE}
136+
${ENABLE_CPPCHECK_VALUE}
137+
${ENABLE_CLANG_TIDY_VALUE}
138+
${ENABLE_COVERAGE_VALUE}
139+
${ENABLE_IPO_VALUE}
140+
${ENABLE_INCLUDE_WHAT_YOU_USE_VALUE}
141+
${ENABLE_PCH_VALUE}
142+
${ENABLE_DOXYGEN_VALUE}
143+
${ENABLE_USER_LINKER_VALUE}
144+
${ENABLE_BUILD_WITH_TIME_TRACE_VALUE}
145+
${ENABLE_UNITY_VALUE}
146+
${ENABLE_SANITIZER_ADDRESS_VALUE}
147+
${ENABLE_SANITIZER_LEAK_VALUE}
148+
${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR_VALUE}
149+
${ENABLE_SANITIZER_THREAD_VALUE}
150+
${ENABLE_SANITIZER_MEMORY_VALUE}
151+
${ARGN})
152+
endmacro()

src/GlobalOptions.cmake

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/Index.cmake

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,7 @@ macro(project_options)
155155
enable_include_what_you_use()
156156
endif()
157157

158-
# Very basic PCH example
159158
if(${ProjectOptions_ENABLE_PCH})
160-
# This sets a global PCH parameter, each project will build its own PCH, which is a good idea
161-
# if any #define's change consider breaking this out per project as necessary
162159
if(NOT ProjectOptions_PCH_HEADERS)
163160
set(ProjectOptions_PCH_HEADERS
164161
<vector>

src/StaticAnalyzers.cmake

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
macro(enable_cppcheck)
22
find_program(CPPCHECK cppcheck)
33
if(CPPCHECK)
4+
# Enable all warnings that are actionable by the user of this toolset
5+
# style should enable the other 3, but we'll be explicit just in case
46
set(CMAKE_CXX_CPPCHECK
57
${CPPCHECK}
6-
--suppress=missingInclude
7-
--enable=all
8+
--enable=style,performance,warning,portability
89
--inline-suppr
910
--inconclusive)
1011
if(${CMAKE_CXX_STANDARD})
@@ -21,6 +22,10 @@ endmacro()
2122
macro(enable_clang_tidy)
2223
find_program(CLANGTIDY clang-tidy)
2324
if(CLANGTIDY)
25+
if(NOT CMAKE_CXX_COMPILER_ID MATCHES ".*Clang" AND ${ProjectOptions_ENABLE_PCH})
26+
message(SEND_ERROR "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file")
27+
endif()
28+
2429
set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option)
2530
if(${CMAKE_CXX_STANDARD})
2631
set(CMAKE_CXX_CLANG_TIDY ${CMAKE_CXX_CLANG_TIDY} -extra-arg=-std=c++${CMAKE_CXX_STANDARD})

src/Utilities.cmake

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,31 @@ function(set_env_from_string env_string)
8282
endif()
8383
endforeach()
8484
endfunction()
85+
86+
87+
function(get_all_targets var)
88+
set(targets)
89+
get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR})
90+
set(${var}
91+
${targets}
92+
PARENT_SCOPE)
93+
endfunction()
94+
95+
macro(get_all_targets_recursive targets dir)
96+
get_property(
97+
subdirectories
98+
DIRECTORY ${dir}
99+
PROPERTY SUBDIRECTORIES)
100+
foreach(subdir ${subdirectories})
101+
get_all_targets_recursive(${targets} ${subdir})
102+
endforeach()
103+
104+
get_property(
105+
current_targets
106+
DIRECTORY ${dir}
107+
PROPERTY BUILDSYSTEM_TARGETS)
108+
list(APPEND ${targets} ${current_targets})
109+
endmacro()
110+
111+
112+

0 commit comments

Comments
 (0)