blob: 9a2c617c4a03be4254833a7ce65d7d5fe7d31852 [file] [log] [blame]
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -08001# 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.
14include_guard(GLOBAL)
15
Wyatt Heplerb18f8f92021-01-05 15:20:40 -080016# Wrapper around cmake_parse_arguments that fails with an error if any arguments
17# remained unparsed.
18macro(_pw_parse_argv_strict function start_arg options one multi)
19 cmake_parse_arguments(PARSE_ARGV
20 "${start_arg}" arg "${options}" "${one}" "${multi}"
21 )
22 if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "")
23 set(_all_args ${options} ${one} ${multi})
24 message(FATAL_ERROR
25 "Unexpected arguments to ${function}: ${arg_UNPARSED_ARGUMENTS}\n"
26 "Valid arguments: ${_all_args}"
27 )
28 endif()
29endmacro()
30
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080031# 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
33# requirements:
34#
35# - The module exposes exactly one library.
36# - All source files in the module directory are included in the library.
Wyatt Heplerc3a2d472020-01-09 12:24:24 -080037# - 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.
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080041# - The module is not a facade.
42#
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.
47#
48# This function does the following:
49#
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.
54#
55# Args:
Wyatt Hepler0a6f7632020-10-29 09:08:19 -070056#
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
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080060#
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080061function(pw_auto_add_simple_module MODULE)
Wyatt Heplerb18f8f92021-01-05 15:20:40 -080062 _pw_parse_argv_strict(pw_auto_add_simple_module 1
63 ""
64 "IMPLEMENTS_FACADE"
65 "PUBLIC_DEPS;PRIVATE_DEPS;TEST_DEPS"
66 )
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080067
68 file(GLOB all_sources *.cc *.c)
69
70 # Create a library with all source files not ending in _test.
71 set(sources "${all_sources}")
Wyatt Heplerdfa75ee2021-05-13 16:46:04 -070072 list(FILTER sources EXCLUDE REGEX "_test(\\.cc|(_c)?\\.c)$") # *_test.cc
73 list(FILTER sources EXCLUDE REGEX "^test(\\.cc|(_c)?\\.c)$") # test.cc
Wyatt Hepler0a6f7632020-10-29 09:08:19 -070074 list(FILTER sources EXCLUDE REGEX "_fuzzer\\.cc$")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080075
76 file(GLOB_RECURSE headers *.h)
77
78 if(arg_IMPLEMENTS_FACADE)
79 set(groups backends)
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080080 else()
81 set(groups modules "${MODULE}")
82 endif()
83
84 pw_add_module_library("${MODULE}"
Wyatt Heplerf8a3bf72021-01-05 12:45:25 -080085 IMPLEMENTS_FACADES
86 ${arg_IMPLEMENTS_FACADE}
Wyatt Hepler49810582020-01-24 11:26:35 -080087 PUBLIC_DEPS
88 ${arg_PUBLIC_DEPS}
89 PRIVATE_DEPS
90 ${arg_PRIVATE_DEPS}
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080091 SOURCES
92 ${sources}
93 HEADERS
94 ${headers}
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -080095 )
96
Wyatt Hepler0a6f7632020-10-29 09:08:19 -070097 if(arg_IMPLEMENTS_FACADE)
98 target_include_directories("${MODULE}" PUBLIC public_overrides)
99 endif()
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800100
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700101 pw_auto_add_module_tests("${MODULE}"
102 PRIVATE_DEPS
103 ${arg_PUBLIC_DEPS}
104 ${arg_PRIVATE_DEPS}
105 ${arg_TEST_DEPS}
106 GROUPS
107 ${groups}
108 )
109endfunction(pw_auto_add_simple_module)
110
111# Creates a test for each source file ending in _test. Tests with mutliple .cc
112# files or different dependencies than the module will not work correctly.
113#
114# Args:
115#
116# PRIVATE_DEPS - dependencies to apply to all tests
117# GROUPS - groups in addition to MODULE to which to add these tests
118#
119function(pw_auto_add_module_tests MODULE)
Wyatt Heplerb18f8f92021-01-05 15:20:40 -0800120 _pw_parse_argv_strict(pw_auto_add_module_tests 1
121 ""
122 ""
123 "PRIVATE_DEPS;GROUPS"
124 )
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700125
126 file(GLOB cc_tests *_test.cc)
127
128 foreach(test IN LISTS cc_tests)
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800129 get_filename_component(test_name "${test}" NAME_WE)
Wyatt Heplerc3a2d472020-01-09 12:24:24 -0800130
131 # Find a .c test corresponding with the test .cc file, if any.
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700132 file(GLOB c_test "${test_name}.c" "${test_name}_c.c")
Wyatt Heplerc3a2d472020-01-09 12:24:24 -0800133
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800134 pw_add_test("${MODULE}.${test_name}"
135 SOURCES
136 "${test}"
Wyatt Heplerc3a2d472020-01-09 12:24:24 -0800137 ${c_test}
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800138 DEPS
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700139 "$<TARGET_NAME_IF_EXISTS:${MODULE}>"
Wyatt Hepler49810582020-01-24 11:26:35 -0800140 ${arg_PRIVATE_DEPS}
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800141 GROUPS
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700142 "${MODULE}"
143 ${arg_GROUPS}
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800144 )
145 endforeach()
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700146endfunction(pw_auto_add_module_tests)
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800147
Wyatt Heplerb18f8f92021-01-05 15:20:40 -0800148# Sets the provided variable to the common library arguments.
149macro(_pw_library_args variable)
150 set("${variable}" SOURCES HEADERS PUBLIC_DEPS PRIVATE_DEPS ${ARGN})
Wyatt Heplerf8a3bf72021-01-05 12:45:25 -0800151endmacro()
152
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800153# Creates a library in a module. The library has access to the public/ include
154# directory.
155#
156# Args:
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700157#
158# SOURCES - source files for this library
159# HEADERS - header files for this library
160# PUBLIC_DEPS - public target_link_libraries arguments
161# PRIVATE_DEPS - private target_link_libraries arguments
Wyatt Heplerf8a3bf72021-01-05 12:45:25 -0800162# IMPLEMENTS_FACADES - which facades this library implements
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700163#
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800164function(pw_add_module_library NAME)
Wyatt Heplerb18f8f92021-01-05 15:20:40 -0800165 _pw_library_args(list_args IMPLEMENTS_FACADES)
Wyatt Heplerdf9b6122021-01-05 14:29:57 -0800166 _pw_parse_argv_strict(pw_add_module_library 1 "" "" "${list_args}")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800167
168 # Check that the library's name is prefixed by the module name.
169 get_filename_component(module "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
170
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700171 if(NOT "${NAME}" MATCHES "${module}(\\.[^\\.]+)?(\\.facade)?$")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800172 message(FATAL_ERROR
173 "Module libraries must match the module name or be in the form "
Wyatt Hepler6639c452020-05-06 11:43:07 -0700174 "'MODULE_NAME.LIBRARY_NAME'. The library '${NAME}' does not match."
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800175 )
176 endif()
177
178 add_library("${NAME}" EXCLUDE_FROM_ALL ${arg_HEADERS} ${arg_SOURCES})
Wyatt Heplerdf9b6122021-01-05 14:29:57 -0800179 target_include_directories("${NAME}" PUBLIC public)
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800180 target_link_libraries("${NAME}"
181 PUBLIC
182 pw_build
183 ${arg_PUBLIC_DEPS}
184 PRIVATE
Wyatt Hepler8aa5ed52020-11-12 13:38:37 -0800185 pw_build.strict_warnings
186 pw_build.extra_strict_warnings
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800187 ${arg_PRIVATE_DEPS}
188 )
189
Wyatt Heplerf8a3bf72021-01-05 12:45:25 -0800190 if(NOT "${arg_IMPLEMENTS_FACADES}" STREQUAL "")
191 target_include_directories("${NAME}" PUBLIC public_overrides)
192 set(facades ${arg_IMPLEMENTS_FACADES})
193 list(TRANSFORM facades APPEND ".facade")
194 target_link_libraries("${NAME}" PUBLIC ${facades})
195 endif()
196
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800197 # Libraries require at least one source file.
198 if(NOT arg_SOURCES)
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700199 target_sources("${NAME}" PRIVATE $<TARGET_PROPERTY:pw_build.empty,SOURCES>)
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800200 endif()
201endfunction(pw_add_module_library)
202
203# Declares a module as a facade.
204#
205# Facades are declared as two libraries to avoid circular dependencies.
206# Libraries that use the facade depend on a library named for the module. The
207# module that implements the facade depends on a library named
208# MODULE_NAME.facade.
209#
Wyatt Heplerdf9b6122021-01-05 14:29:57 -0800210# pw_add_facade accepts the same arguments as pw_add_module_library, except for
211# IMPLEMENTS_FACADES. It also accepts the following argument:
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700212#
213# DEFAULT_BACKEND - which backend to use by default
214#
Wyatt Hepler6639c452020-05-06 11:43:07 -0700215function(pw_add_facade NAME)
Wyatt Heplerb18f8f92021-01-05 15:20:40 -0800216 _pw_library_args(list_args)
217 _pw_parse_argv_strict(pw_add_facade 1 "" "DEFAULT_BACKEND" "${list_args}")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800218
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700219 # If no backend is set, a script that displays an error message is used
220 # instead. If the facade is used in the build, it fails with this error.
221 add_custom_target("${NAME}._no_backend_set_message"
222 COMMAND
Wyatt Heplerb59d2c92021-05-11 11:20:43 -0700223 "${CMAKE_COMMAND}" -E echo
224 "ERROR: Attempted to build the ${NAME} facade with no backend."
225 "Configure the ${NAME} backend using pw_set_backend or remove all dependencies on it."
226 "See https://pigweed.dev/pw_build."
227 COMMAND
228 "${CMAKE_COMMAND}" -E false
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700229 )
230 add_library("${NAME}.NO_BACKEND_SET" INTERFACE)
231 add_dependencies("${NAME}.NO_BACKEND_SET" "${NAME}._no_backend_set_message")
232
233 # Set the default backend to the error message if no default is specified.
234 if("${arg_DEFAULT_BACKEND}" STREQUAL "")
235 set(arg_DEFAULT_BACKEND "${NAME}.NO_BACKEND_SET")
236 endif()
237
238 # Declare the backend variable for this facade.
239 set("${NAME}_BACKEND" "${arg_DEFAULT_BACKEND}" CACHE STRING
240 "Backend for ${NAME}")
241
Wyatt Hepler8026d8d2021-05-11 11:37:29 -0700242 # This target is never used; it simply tests that the specified backend
243 # actually exists in the build. The generator expression will fail to evaluate
244 # if the target is not defined.
245 add_custom_target(_pw_check_that_backend_for_${NAME}_is_defined
246 COMMAND
247 ${CMAKE_COMMAND} -E echo "$<TARGET_PROPERTY:${${NAME}_BACKEND},TYPE>"
248 )
249
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700250 # Define the facade library, which is used by the backend to avoid circular
251 # dependencies.
Wyatt Heplerdf9b6122021-01-05 14:29:57 -0800252 add_library("${NAME}.facade" INTERFACE)
253 target_include_directories("${NAME}.facade" INTERFACE public)
254 target_link_libraries("${NAME}.facade" INTERFACE ${arg_PUBLIC_DEPS})
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700255
256 # Define the public-facing library for this facade, which depends on the
257 # header files in .facade target and exposes the dependency on the backend.
Wyatt Heplerdf9b6122021-01-05 14:29:57 -0800258 pw_add_module_library("${NAME}"
259 SOURCES
260 ${arg_SOURCES}
261 HEADERS
262 ${arg_HEADERS}
263 PUBLIC_DEPS
Wyatt Hepler6639c452020-05-06 11:43:07 -0700264 "${NAME}.facade"
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700265 "${${NAME}_BACKEND}"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800266 )
267endfunction(pw_add_facade)
268
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700269# Sets which backend to use for the given facade.
270function(pw_set_backend FACADE BACKEND)
271 set("${FACADE}_BACKEND" "${BACKEND}" CACHE STRING "Backend for ${NAME}" FORCE)
272endfunction(pw_set_backend)
273
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800274# Declares a unit test. Creates two targets:
275#
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700276# * <TEST_NAME> - the test executable
Alexei Frolov69dccfd2020-11-13 12:10:24 -0800277# * <TEST_NAME>.run - builds and runs the test
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800278#
279# Args:
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700280#
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800281# NAME: name to use for the target
282# SOURCES: source files for this test
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800283# DEPS: libraries on which this test depends
284# GROUPS: groups to which to add this test; if none are specified, the test is
Wyatt Hepler0a6f7632020-10-29 09:08:19 -0700285# added to the 'default' and 'all' groups
286#
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800287function(pw_add_test NAME)
Wyatt Heplerb18f8f92021-01-05 15:20:40 -0800288 _pw_parse_argv_strict(pw_add_test 1 "" "" "SOURCES;DEPS;GROUPS")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800289
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800290 add_executable("${NAME}" EXCLUDE_FROM_ALL ${arg_SOURCES})
291 target_link_libraries("${NAME}"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800292 PRIVATE
293 pw_unit_test
294 pw_unit_test.main
295 ${arg_DEPS}
296 )
297
298 # Define a target for running the test. The target creates a stamp file to
299 # indicate successful test completion. This allows running tests in parallel
300 # with Ninja's full dependency resolution.
301 add_custom_command(
302 COMMAND
303 # TODO(hepler): This only runs local test binaries. Execute a test runner
304 # instead to support device test runs.
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800305 "$<TARGET_FILE:${NAME}>"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800306 COMMAND
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800307 "${CMAKE_COMMAND}" -E touch "${NAME}.stamp"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800308 DEPENDS
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800309 "${NAME}"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800310 OUTPUT
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800311 "${NAME}.stamp"
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800312 )
Alexei Frolov69dccfd2020-11-13 12:10:24 -0800313 add_custom_target("${NAME}.run" DEPENDS "${NAME}.stamp")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800314
315 # Always add tests to the "all" group. If no groups are provided, add the
316 # test to the "default" group.
317 if(arg_GROUPS)
318 set(groups all ${arg_GROUPS})
319 else()
320 set(groups all default)
321 endif()
322
323 list(REMOVE_DUPLICATES groups)
Wyatt Heplera6a7cb22020-01-07 16:19:02 -0800324 pw_add_test_to_groups("${NAME}" ${groups})
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800325endfunction(pw_add_test)
326
327# Adds a test target to the specified test groups. Test groups can be built with
328# the pw_tests_GROUP_NAME target or executed with the pw_run_tests_GROUP_NAME
329# target.
330function(pw_add_test_to_groups TEST_NAME)
331 foreach(group IN LISTS ARGN)
Wyatt Hepler6afac0a2020-01-08 14:23:26 -0800332 if(NOT TARGET "pw_tests.${group}")
333 add_custom_target("pw_tests.${group}")
334 add_custom_target("pw_run_tests.${group}")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800335 endif()
336
Wyatt Hepler6afac0a2020-01-08 14:23:26 -0800337 add_dependencies("pw_tests.${group}" "${TEST_NAME}")
Alexei Frolov69dccfd2020-11-13 12:10:24 -0800338 add_dependencies("pw_run_tests.${group}" "${TEST_NAME}.run")
Wyatt Hepler0fbcdfc2020-01-02 07:53:39 -0800339 endforeach()
340endfunction(pw_add_test_to_groups)