Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 1 | # Copyright 2020 The Pigweed Authors |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 4 | # use this file except in compliance with the License. You may obtain a copy of |
| 5 | # the License at |
| 6 | # |
| 7 | # https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 12 | # License for the specific language governing permissions and limitations under |
| 13 | # the License. |
| 14 | include_guard(GLOBAL) |
| 15 | |
| 16 | # Create an empty, dummy source file for use by non-INTERFACE libraries, which |
| 17 | # require at least one source file. |
| 18 | set(_pw_empty_source_file "${CMAKE_BINARY_DIR}/pw_empty_source_file.cc") |
| 19 | file(WRITE "${_pw_empty_source_file}" "") |
| 20 | |
| 21 | # Automatically creates a library and test targets for the files in a module. |
| 22 | # This function is only suitable for simple modules that meet the following |
| 23 | # requirements: |
| 24 | # |
| 25 | # - The module exposes exactly one library. |
| 26 | # - All source files in the module directory are included in the library. |
Wyatt Hepler | c3a2d47 | 2020-01-09 12:24:24 -0800 | [diff] [blame] | 27 | # - Each test in the module has |
| 28 | # - exactly one source .cc file, |
| 29 | # - optionally, one .c source with the same base name as the .cc file, |
| 30 | # - only a dependency on the main module library. |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 31 | # - The module is not a facade. |
| 32 | # |
| 33 | # Modules that do not meet these requirements may not use |
| 34 | # pw_auto_add_simple_module. Instead, define the module's libraries and tests |
| 35 | # with pw_add_module_library, pw_add_facade, pw_add_test, and the standard CMake |
| 36 | # functions, such as add_library, target_link_libraries, etc. |
| 37 | # |
| 38 | # This function does the following: |
| 39 | # |
| 40 | # 1. Find all .c and .cc files in the module's root directory. |
| 41 | # 2. Create a library with the module name using pw_add_module_library with |
| 42 | # all source files that do not end with _test.cc. |
| 43 | # 3. Declare a test for each source file that ends with _test.cc. |
| 44 | # |
| 45 | # Args: |
| 46 | # IMPLEMENTS_FACADE: this module implements the specified facade |
Wyatt Hepler | 4981058 | 2020-01-24 11:26:35 -0800 | [diff] [blame^] | 47 | # PUBLIC_DEPS: public target_link_libraries arguments |
| 48 | # PRIVATE_DEPS: private target_link_libraries arguments |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 49 | # |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 50 | function(pw_auto_add_simple_module MODULE) |
Wyatt Hepler | 4981058 | 2020-01-24 11:26:35 -0800 | [diff] [blame^] | 51 | set(multi PUBLIC_DEPS PRIVATE_DEPS) |
| 52 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "IMPLEMENTS_FACADE" "${multi}") |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 53 | |
| 54 | file(GLOB all_sources *.cc *.c) |
| 55 | |
| 56 | # Create a library with all source files not ending in _test. |
| 57 | set(sources "${all_sources}") |
| 58 | list(FILTER sources EXCLUDE REGEX "_test.cc$") |
| 59 | |
| 60 | file(GLOB_RECURSE headers *.h) |
| 61 | |
| 62 | if(arg_IMPLEMENTS_FACADE) |
| 63 | set(groups backends) |
| 64 | set(deps PUBLIC_DEPS "${arg_IMPLEMENTS_FACADE}.facade") |
| 65 | else() |
| 66 | set(groups modules "${MODULE}") |
| 67 | endif() |
| 68 | |
| 69 | pw_add_module_library("${MODULE}" |
Wyatt Hepler | 4981058 | 2020-01-24 11:26:35 -0800 | [diff] [blame^] | 70 | PUBLIC_DEPS |
| 71 | ${arg_PUBLIC_DEPS} |
| 72 | PRIVATE_DEPS |
| 73 | ${arg_PRIVATE_DEPS} |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 74 | SOURCES |
| 75 | ${sources} |
| 76 | HEADERS |
| 77 | ${headers} |
| 78 | ${deps} |
| 79 | ) |
| 80 | |
| 81 | # Create a test for each source file ending in _test. Tests with mutliple .cc |
| 82 | # files or different dependencies than the module will not work correctly. |
| 83 | set(tests "${all_sources}") |
| 84 | list(FILTER tests INCLUDE REGEX "_test.cc$") |
| 85 | |
| 86 | foreach(test IN LISTS tests) |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 87 | get_filename_component(test_name "${test}" NAME_WE) |
Wyatt Hepler | c3a2d47 | 2020-01-09 12:24:24 -0800 | [diff] [blame] | 88 | |
| 89 | # Find a .c test corresponding with the test .cc file, if any. |
| 90 | list(FILTER c_test INCLUDE REGEX "^${test_name}.c$") |
| 91 | |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 92 | pw_add_test("${MODULE}.${test_name}" |
| 93 | SOURCES |
| 94 | "${test}" |
Wyatt Hepler | c3a2d47 | 2020-01-09 12:24:24 -0800 | [diff] [blame] | 95 | ${c_test} |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 96 | DEPS |
| 97 | "${MODULE}" |
Wyatt Hepler | 4981058 | 2020-01-24 11:26:35 -0800 | [diff] [blame^] | 98 | ${arg_PUBLIC_DEPS} |
| 99 | ${arg_PRIVATE_DEPS} |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 100 | GROUPS |
| 101 | "${groups}" |
| 102 | ) |
| 103 | endforeach() |
| 104 | endfunction(pw_auto_add_simple_module) |
| 105 | |
| 106 | # Creates a library in a module. The library has access to the public/ include |
| 107 | # directory. |
| 108 | # |
| 109 | # Args: |
| 110 | # SOURCES: source files for this library |
| 111 | # HEADERS: header files for this library |
| 112 | # PUBLIC_DEPS: public target_link_libraries arguments |
| 113 | # PRIVATE_DEPS: private target_link_libraries arguments |
| 114 | function(pw_add_module_library NAME) |
| 115 | set(list_args SOURCES HEADERS PUBLIC_DEPS PRIVATE_DEPS) |
| 116 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "${list_args}") |
| 117 | |
| 118 | # Check that the library's name is prefixed by the module name. |
| 119 | get_filename_component(module "${CMAKE_CURRENT_SOURCE_DIR}" NAME) |
| 120 | |
| 121 | if(NOT "${NAME}" MATCHES "^${module}(\\.[^\\.]+)?$") |
| 122 | message(FATAL_ERROR |
| 123 | "Module libraries must match the module name or be in the form " |
| 124 | "'MODULE_NAME.LIBRARY_NAME'. " |
| 125 | "The library '${NAME}' does not match the module '${module}'." |
| 126 | ) |
| 127 | endif() |
| 128 | |
| 129 | add_library("${NAME}" EXCLUDE_FROM_ALL ${arg_HEADERS} ${arg_SOURCES}) |
| 130 | target_include_directories("${NAME}" PUBLIC public/) |
| 131 | target_link_libraries("${NAME}" |
| 132 | PUBLIC |
| 133 | pw_build |
| 134 | ${arg_PUBLIC_DEPS} |
| 135 | PRIVATE |
| 136 | ${arg_PRIVATE_DEPS} |
| 137 | ) |
| 138 | |
| 139 | # Libraries require at least one source file. |
| 140 | if(NOT arg_SOURCES) |
| 141 | target_sources("${NAME}" PRIVATE "${_pw_empty_source_file}") |
| 142 | endif() |
| 143 | endfunction(pw_add_module_library) |
| 144 | |
| 145 | # Declares a module as a facade. |
| 146 | # |
| 147 | # Facades are declared as two libraries to avoid circular dependencies. |
| 148 | # Libraries that use the facade depend on a library named for the module. The |
| 149 | # module that implements the facade depends on a library named |
| 150 | # MODULE_NAME.facade. |
| 151 | # |
| 152 | # pw_add_facade accepts the same arguments as pw_add_module_library. |
| 153 | function(pw_add_facade MODULE) |
| 154 | pw_add_module_library("${MODULE}.facade" ${ARGN}) |
| 155 | |
| 156 | # Use a library with an empty source instead of an INTERFACE library so that |
| 157 | # the library can have a private dependency on the backend. |
| 158 | add_library("${MODULE}" OBJECT EXCLUDE_FROM_ALL "${_pw_empty_source_file}") |
| 159 | target_link_libraries("${MODULE}" |
| 160 | PUBLIC |
| 161 | "${MODULE}.facade" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 162 | "${MODULE}.backend" |
| 163 | ) |
| 164 | endfunction(pw_add_facade) |
| 165 | |
| 166 | # Declares a unit test. Creates two targets: |
| 167 | # |
| 168 | # - <TEST_NAME>: the test executable |
| 169 | # - <TEST_NAME>_run: builds and runs the test |
| 170 | # |
| 171 | # Args: |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 172 | # NAME: name to use for the target |
| 173 | # SOURCES: source files for this test |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 174 | # DEPS: libraries on which this test depends |
| 175 | # GROUPS: groups to which to add this test; if none are specified, the test is |
| 176 | # added to the default and all groups |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 177 | function(pw_add_test NAME) |
| 178 | cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "SOURCES;DEPS;GROUPS") |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 179 | |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 180 | add_executable("${NAME}" EXCLUDE_FROM_ALL ${arg_SOURCES}) |
| 181 | target_link_libraries("${NAME}" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 182 | PRIVATE |
| 183 | pw_unit_test |
| 184 | pw_unit_test.main |
| 185 | ${arg_DEPS} |
| 186 | ) |
| 187 | |
| 188 | # Define a target for running the test. The target creates a stamp file to |
| 189 | # indicate successful test completion. This allows running tests in parallel |
| 190 | # with Ninja's full dependency resolution. |
| 191 | add_custom_command( |
| 192 | COMMAND |
| 193 | # TODO(hepler): This only runs local test binaries. Execute a test runner |
| 194 | # instead to support device test runs. |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 195 | "$<TARGET_FILE:${NAME}>" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 196 | COMMAND |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 197 | "${CMAKE_COMMAND}" -E touch "${NAME}.stamp" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 198 | DEPENDS |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 199 | "${NAME}" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 200 | OUTPUT |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 201 | "${NAME}.stamp" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 202 | ) |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 203 | add_custom_target("${NAME}_run" DEPENDS "${NAME}.stamp") |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 204 | |
| 205 | # Always add tests to the "all" group. If no groups are provided, add the |
| 206 | # test to the "default" group. |
| 207 | if(arg_GROUPS) |
| 208 | set(groups all ${arg_GROUPS}) |
| 209 | else() |
| 210 | set(groups all default) |
| 211 | endif() |
| 212 | |
| 213 | list(REMOVE_DUPLICATES groups) |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 214 | pw_add_test_to_groups("${NAME}" ${groups}) |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 215 | endfunction(pw_add_test) |
| 216 | |
| 217 | # Adds a test target to the specified test groups. Test groups can be built with |
| 218 | # the pw_tests_GROUP_NAME target or executed with the pw_run_tests_GROUP_NAME |
| 219 | # target. |
| 220 | function(pw_add_test_to_groups TEST_NAME) |
| 221 | foreach(group IN LISTS ARGN) |
Wyatt Hepler | 6afac0a | 2020-01-08 14:23:26 -0800 | [diff] [blame] | 222 | if(NOT TARGET "pw_tests.${group}") |
| 223 | add_custom_target("pw_tests.${group}") |
| 224 | add_custom_target("pw_run_tests.${group}") |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 225 | endif() |
| 226 | |
Wyatt Hepler | 6afac0a | 2020-01-08 14:23:26 -0800 | [diff] [blame] | 227 | add_dependencies("pw_tests.${group}" "${TEST_NAME}") |
| 228 | add_dependencies("pw_run_tests.${group}" "${TEST_NAME}_run") |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 229 | endforeach() |
| 230 | endfunction(pw_add_test_to_groups) |
| 231 | |
| 232 | # Declare top-level targets for tests. |
Wyatt Hepler | 6afac0a | 2020-01-08 14:23:26 -0800 | [diff] [blame] | 233 | add_custom_target(pw_tests.default) |
| 234 | add_custom_target(pw_run_tests.default) |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 235 | |
Wyatt Hepler | 6afac0a | 2020-01-08 14:23:26 -0800 | [diff] [blame] | 236 | add_custom_target(pw_tests DEPENDS pw_tests.default) |
| 237 | add_custom_target(pw_run_tests DEPENDS pw_run_tests.default) |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 238 | |
| 239 | # Define the standard Pigweed compile options. |
| 240 | add_library(_pw_reduced_size_copts INTERFACE) |
| 241 | target_compile_options(_pw_reduced_size_copts |
| 242 | INTERFACE |
| 243 | "-fno-common" |
| 244 | "-fno-exceptions" |
| 245 | "-ffunction-sections" |
| 246 | "-fdata-sections" |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 247 | $<$<COMPILE_LANGUAGE:CXX>:-fno-rtti> |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 248 | ) |
| 249 | |
| 250 | add_library(_pw_strict_warnings_copts INTERFACE) |
| 251 | target_compile_options(_pw_strict_warnings_copts |
| 252 | INTERFACE |
| 253 | "-Wall" |
| 254 | "-Wextra" |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 255 | # Make all warnings errors, except for the exemptions below. |
| 256 | "-Werror" |
| 257 | "-Wno-error=cpp" # preprocessor #warning statement |
| 258 | "-Wno-error=deprecated-declarations" # [[deprecated]] attribute |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 259 | $<$<COMPILE_LANGUAGE:CXX>:-Wnon-virtual-dtor> |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 260 | ) |
| 261 | |
| 262 | add_library(_pw_cpp17_copts INTERFACE) |
| 263 | target_compile_options(_pw_cpp17_copts |
| 264 | INTERFACE |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 265 | $<$<COMPILE_LANGUAGE:CXX>:-std=c++17> |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 266 | # Allow uses of the register keyword, which may appear in C headers. |
Wyatt Hepler | a6a7cb2 | 2020-01-07 16:19:02 -0800 | [diff] [blame] | 267 | $<$<COMPILE_LANGUAGE:CXX>:-Wno-register> |
Wyatt Hepler | 0fbcdfc | 2020-01-02 07:53:39 -0800 | [diff] [blame] | 268 | ) |
| 269 | |
| 270 | # Target that specifies the standard Pigweed build options. |
| 271 | add_library(pw_build INTERFACE) |
| 272 | target_link_libraries(pw_build |
| 273 | INTERFACE |
| 274 | _pw_reduced_size_copts |
| 275 | _pw_strict_warnings_copts |
| 276 | _pw_cpp17_copts |
| 277 | ) |
Wyatt Hepler | 6afac0a | 2020-01-08 14:23:26 -0800 | [diff] [blame] | 278 | target_compile_options(pw_build |
| 279 | INTERFACE |
| 280 | # Force the compiler use colorized output. This is required for Ninja. |
| 281 | $<$<CXX_COMPILER_ID:Clang>:-fcolor-diagnostics> |
| 282 | $<$<CXX_COMPILER_ID:GNU>:-fdiagnostics-color=always> |
| 283 | ) |