1 # Copyright 2020 The Pigweed Authors
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
7 # https://www.apache.org/licenses/LICENSE-2.0
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
16 # Wrapper around cmake_parse_arguments that fails with an error if any arguments
18 macro(_pw_parse_argv_strict function start_arg options one multi)
19 cmake_parse_arguments(PARSE_ARGV
20 "${start_arg}" arg "${options}" "${one}" "${multi}"
22 if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "")
23 set(_all_args ${options} ${one} ${multi})
25 "Unexpected arguments to ${function}: ${arg_UNPARSED_ARGUMENTS}\n"
26 "Valid arguments: ${_all_args}"
31 # Automatically creates a library and test targets for the files in a module.
32 # This function is only suitable for simple modules that meet the following
35 # - The module exposes exactly one library.
36 # - All source files in the module directory are included in the library.
37 # - Each test in the module has
38 # - exactly one source .cc file,
39 # - optionally, one .c source with the same base name as the .cc file,
40 # - only a dependency on the main module library.
41 # - The module is not a facade.
43 # Modules that do not meet these requirements may not use
44 # pw_auto_add_simple_module. Instead, define the module's libraries and tests
45 # with pw_add_module_library, pw_add_facade, pw_add_test, and the standard CMake
46 # functions, such as add_library, target_link_libraries, etc.
48 # This function does the following:
50 # 1. Find all .c and .cc files in the module's root directory.
51 # 2. Create a library with the module name using pw_add_module_library with
52 # all source files that do not end with _test.cc.
53 # 3. Declare a test for each source file that ends with _test.cc.
57 # IMPLEMENTS_FACADE - this module implements the specified facade
58 # PUBLIC_DEPS - public target_link_libraries arguments
59 # PRIVATE_DEPS - private target_link_libraries arguments
61 function(pw_auto_add_simple_module MODULE)
62 _pw_parse_argv_strict(pw_auto_add_simple_module 1
65 "PUBLIC_DEPS;PRIVATE_DEPS;TEST_DEPS"
68 file(GLOB all_sources *.cc *.c)
70 # Create a library with all source files not ending in _test.
71 set(sources "${all_sources}")
72 list(FILTER sources EXCLUDE REGEX "_test(\\.cc|(_c)?\\.c)$")
73 list(FILTER sources EXCLUDE REGEX "_fuzzer\\.cc$")
75 file(GLOB_RECURSE headers *.h)
77 if(arg_IMPLEMENTS_FACADE)
80 set(groups modules "${MODULE}")
83 pw_add_module_library("${MODULE}"
85 ${arg_IMPLEMENTS_FACADE}
96 if(arg_IMPLEMENTS_FACADE)
97 target_include_directories("${MODULE}" PUBLIC public_overrides)
100 pw_auto_add_module_tests("${MODULE}"
108 endfunction(pw_auto_add_simple_module)
110 # Creates a test for each source file ending in _test. Tests with mutliple .cc
111 # files or different dependencies than the module will not work correctly.
115 # PRIVATE_DEPS - dependencies to apply to all tests
116 # GROUPS - groups in addition to MODULE to which to add these tests
118 function(pw_auto_add_module_tests MODULE)
119 _pw_parse_argv_strict(pw_auto_add_module_tests 1
122 "PRIVATE_DEPS;GROUPS"
125 file(GLOB cc_tests *_test.cc)
127 foreach(test IN LISTS cc_tests)
128 get_filename_component(test_name "${test}" NAME_WE)
130 # Find a .c test corresponding with the test .cc file, if any.
131 file(GLOB c_test "${test_name}.c" "${test_name}_c.c")
133 pw_add_test("${MODULE}.${test_name}"
138 "$<TARGET_NAME_IF_EXISTS:${MODULE}>"
145 endfunction(pw_auto_add_module_tests)
147 # Sets the provided variable to the common library arguments.
148 macro(_pw_library_args variable)
149 set("${variable}" SOURCES HEADERS PUBLIC_DEPS PRIVATE_DEPS ${ARGN})
152 # Creates a library in a module. The library has access to the public/ include
157 # SOURCES - source files for this library
158 # HEADERS - header files for this library
159 # PUBLIC_DEPS - public target_link_libraries arguments
160 # PRIVATE_DEPS - private target_link_libraries arguments
161 # IMPLEMENTS_FACADES - which facades this library implements
163 function(pw_add_module_library NAME)
164 _pw_library_args(list_args IMPLEMENTS_FACADES)
165 _pw_parse_argv_strict(pw_add_module_library 1 "" "" "${list_args}")
167 # Check that the library's name is prefixed by the module name.
168 get_filename_component(module "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
170 if(NOT "${NAME}" MATCHES "${module}(\\.[^\\.]+)?(\\.facade)?$")
172 "Module libraries must match the module name or be in the form "
173 "'MODULE_NAME.LIBRARY_NAME'. The library '${NAME}' does not match."
177 add_library("${NAME}" EXCLUDE_FROM_ALL ${arg_HEADERS} ${arg_SOURCES})
178 target_include_directories("${NAME}" PUBLIC public)
179 target_link_libraries("${NAME}"
184 pw_build.strict_warnings
185 pw_build.extra_strict_warnings
189 if(NOT "${arg_IMPLEMENTS_FACADES}" STREQUAL "")
190 target_include_directories("${NAME}" PUBLIC public_overrides)
191 set(facades ${arg_IMPLEMENTS_FACADES})
192 list(TRANSFORM facades APPEND ".facade")
193 target_link_libraries("${NAME}" PUBLIC ${facades})
196 # Libraries require at least one source file.
198 target_sources("${NAME}" PRIVATE $<TARGET_PROPERTY:pw_build.empty,SOURCES>)
200 endfunction(pw_add_module_library)
202 # Declares a module as a facade.
204 # Facades are declared as two libraries to avoid circular dependencies.
205 # Libraries that use the facade depend on a library named for the module. The
206 # module that implements the facade depends on a library named
207 # MODULE_NAME.facade.
209 # pw_add_facade accepts the same arguments as pw_add_module_library, except for
210 # IMPLEMENTS_FACADES. It also accepts the following argument:
212 # DEFAULT_BACKEND - which backend to use by default
214 function(pw_add_facade NAME)
215 _pw_library_args(list_args)
216 _pw_parse_argv_strict(pw_add_facade 1 "" "DEFAULT_BACKEND" "${list_args}")
218 # If no backend is set, a script that displays an error message is used
219 # instead. If the facade is used in the build, it fails with this error.
220 add_custom_target("${NAME}._no_backend_set_message"
222 python "$ENV{PW_ROOT}/pw_build/py/pw_build/null_backend.py" "${NAME}"
224 add_library("${NAME}.NO_BACKEND_SET" INTERFACE)
225 add_dependencies("${NAME}.NO_BACKEND_SET" "${NAME}._no_backend_set_message")
227 # Set the default backend to the error message if no default is specified.
228 if("${arg_DEFAULT_BACKEND}" STREQUAL "")
229 set(arg_DEFAULT_BACKEND "${NAME}.NO_BACKEND_SET")
232 # Declare the backend variable for this facade.
233 set("${NAME}_BACKEND" "${arg_DEFAULT_BACKEND}" CACHE STRING
234 "Backend for ${NAME}")
236 # Define the facade library, which is used by the backend to avoid circular
238 add_library("${NAME}.facade" INTERFACE)
239 target_include_directories("${NAME}.facade" INTERFACE public)
240 target_link_libraries("${NAME}.facade" INTERFACE ${arg_PUBLIC_DEPS})
242 # Define the public-facing library for this facade, which depends on the
243 # header files in .facade target and exposes the dependency on the backend.
244 pw_add_module_library("${NAME}"
253 endfunction(pw_add_facade)
255 # Sets which backend to use for the given facade.
256 function(pw_set_backend FACADE BACKEND)
257 set("${FACADE}_BACKEND" "${BACKEND}" CACHE STRING "Backend for ${NAME}" FORCE)
258 endfunction(pw_set_backend)
260 # Declares a unit test. Creates two targets:
262 # * <TEST_NAME> - the test executable
263 # * <TEST_NAME>.run - builds and runs the test
267 # NAME: name to use for the target
268 # SOURCES: source files for this test
269 # DEPS: libraries on which this test depends
270 # GROUPS: groups to which to add this test; if none are specified, the test is
271 # added to the 'default' and 'all' groups
273 function(pw_add_test NAME)
274 _pw_parse_argv_strict(pw_add_test 1 "" "" "SOURCES;DEPS;GROUPS")
276 add_executable("${NAME}" EXCLUDE_FROM_ALL ${arg_SOURCES})
277 target_link_libraries("${NAME}"
284 # Define a target for running the test. The target creates a stamp file to
285 # indicate successful test completion. This allows running tests in parallel
286 # with Ninja's full dependency resolution.
289 # TODO(hepler): This only runs local test binaries. Execute a test runner
290 # instead to support device test runs.
291 "$<TARGET_FILE:${NAME}>"
293 "${CMAKE_COMMAND}" -E touch "${NAME}.stamp"
299 add_custom_target("${NAME}.run" DEPENDS "${NAME}.stamp")
301 # Always add tests to the "all" group. If no groups are provided, add the
302 # test to the "default" group.
304 set(groups all ${arg_GROUPS})
306 set(groups all default)
309 list(REMOVE_DUPLICATES groups)
310 pw_add_test_to_groups("${NAME}" ${groups})
311 endfunction(pw_add_test)
313 # Adds a test target to the specified test groups. Test groups can be built with
314 # the pw_tests_GROUP_NAME target or executed with the pw_run_tests_GROUP_NAME
316 function(pw_add_test_to_groups TEST_NAME)
317 foreach(group IN LISTS ARGN)
318 if(NOT TARGET "pw_tests.${group}")
319 add_custom_target("pw_tests.${group}")
320 add_custom_target("pw_run_tests.${group}")
323 add_dependencies("pw_tests.${group}" "${TEST_NAME}")
324 add_dependencies("pw_run_tests.${group}" "${TEST_NAME}.run")
326 endfunction(pw_add_test_to_groups)