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. 14include_guard(GLOBAL) 15 16cmake_minimum_required(VERSION 3.20) 17 18# The PW_ROOT environment variable should be set in bootstrap. If it is not set, 19# set it to the root of the Pigweed repository. 20if("$ENV{PW_ROOT}" STREQUAL "") 21 get_filename_component(pw_root "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) 22 message("The PW_ROOT environment variable is not set; " 23 "using ${pw_root} within CMake") 24 set(ENV{PW_ROOT} "${pw_root}") 25endif() 26 27# TOOD(ewout, hepler): Remove this legacy include once all users pull in 28# pw_unit_test/test.cmake for test functions and variables instead of relying 29# on them to be provided by pw_build/pigweed.cmake. 30include("$ENV{PW_ROOT}/pw_unit_test/test.cmake") 31 32# Wrapper around cmake_parse_arguments that fails with an error if any arguments 33# remained unparsed or a required argument was not provided. 34# 35# All parsed arguments are prefixed with "arg_". This helper can only be used 36# by functions, not macros. 37# 38# Required Arguments: 39# 40# NUM_POSITIONAL_ARGS - PARSE_ARGV <N> arguments for 41# cmake_parse_arguments 42# 43# Optional Args: 44# 45# OPTION_ARGS - <option> arguments for cmake_parse_arguments 46# ONE_VALUE_ARGS - <one_value_keywords> arguments for cmake_parse_arguments 47# MULTI_VALUE_ARGS - <multi_value_keywords> arguments for 48# cmake_parse_arguments 49# REQUIRED_ARGS - required arguments which must be set, these may any 50# argument type (<option>, <one_value_keywords>, and/or 51# <multi_value_keywords>) 52# 53macro(pw_parse_arguments) 54 if(POLICY CMP0174) 55 cmake_policy(SET CMP0174 NEW) # Remove when CMake 3.31 or newer is required. 56 endif() 57 cmake_parse_arguments( 58 pw_parse_arg "" "NUM_POSITIONAL_ARGS" 59 "OPTION_ARGS;ONE_VALUE_ARGS;MULTI_VALUE_ARGS;REQUIRED_ARGS" 60 ${ARGN} 61 ) 62 pw_require_args("pw_parse_arguments" "pw_parse_arg_" NUM_POSITIONAL_ARGS) 63 if(NOT "${pw_parse_arg_UNPARSED_ARGUMENTS}" STREQUAL "") 64 message(FATAL_ERROR "Unexpected arguments to pw_parse_arguments: " 65 "${pw_parse_arg_UNPARSED_ARGUMENTS}") 66 endif() 67 68 # Now that we have the macro's arguments, process the caller's arguments. 69 cmake_parse_arguments(PARSE_ARGV 70 "${pw_parse_arg_NUM_POSITIONAL_ARGS}" 71 arg 72 "${pw_parse_arg_OPTION_ARGS}" 73 "${pw_parse_arg_ONE_VALUE_ARGS}" 74 "${pw_parse_arg_MULTI_VALUE_ARGS}" 75 ) 76 if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") 77 set(_all_args 78 ${pw_parse_arg_OPTION_ARGS} 79 ${pw_parse_arg_ONE_VALUE_ARGS} 80 ${pw_parse_arg_MULTI_VALUE_ARGS}) 81 message(FATAL_ERROR 82 "Unexpected arguments to ${CMAKE_CURRENT_FUNCTION}: " 83 "${arg_UNPARSED_ARGUMENTS}\n" 84 "Valid arguments: ${_all_args}" 85 ) 86 endif() 87 pw_require_args("${CMAKE_CURRENT_FUNCTION}" "arg_" 88 ${pw_parse_arg_REQUIRED_ARGS}) 89endmacro() 90 91# Checks that one or more variables are set. This is used to check that 92# arguments were provided to a function. Fails with FATAL_ERROR if 93# ${ARG_PREFIX}${name} is empty. The FUNCTION_NAME is used in the error message. 94# If FUNCTION_NAME is "", it is set to CMAKE_CURRENT_FUNCTION. 95# 96# Usage: 97# 98# pw_require_args(FUNCTION_NAME ARG_PREFIX ARG_NAME [ARG_NAME ...]) 99# 100# Examples: 101# 102# # Checks that arg_FOO is non-empty, using the current function name. 103# pw_require_args("" arg_ FOO) 104# 105# # Checks that FOO and BAR are non-empty, using function name "do_the_thing". 106# pw_require_args(do_the_thing "" FOO BAR) 107# 108macro(pw_require_args FUNCTION_NAME ARG_PREFIX) 109 if("${FUNCTION_NAME}" STREQUAL "") 110 set(_pw_require_args_FUNCTION_NAME "${CMAKE_CURRENT_FUNCTION}") 111 else() 112 set(_pw_require_args_FUNCTION_NAME "${FUNCTION_NAME}") 113 endif() 114 115 foreach(name IN ITEMS ${ARGN}) 116 if("${${ARG_PREFIX}${name}}" STREQUAL "") 117 message(FATAL_ERROR "A value must be provided for ${name} in " 118 "${_pw_require_args_FUNCTION_NAME}.") 119 endif() 120 endforeach() 121endmacro() 122 123# pw_target_link_targets: CMake target only form of target_link_libraries. 124# 125# Helper wrapper around target_link_libraries which only supports CMake targets 126# and detects when the target does not exist. 127# 128# NOTE: Generator expressions are not supported. 129# 130# Due to the processing order of list files, the list of targets has to be 131# checked at the end of the root CMake list file. Instead of requiring all 132# list files to be modified, a DEFER CALL is used. 133# 134# Required Args: 135# 136# <name> - The library target to add the TARGET link dependencies to. 137# 138# Optional Args: 139# 140# INTERFACE - interface target_link_libraries arguments which are all TARGETs. 141# PUBLIC - public target_link_libraries arguments which are all TARGETs. 142# PRIVATE - private target_link_libraries arguments which are all TARGETs. 143function(pw_target_link_targets NAME) 144 set(types INTERFACE PUBLIC PRIVATE ) 145 pw_parse_arguments( 146 NUM_POSITIONAL_ARGS 147 1 148 MULTI_VALUE_ARGS 149 ${types} 150 ) 151 152 if(NOT TARGET "${NAME}") 153 message(FATAL_ERROR "\"${NAME}\" must be a TARGET library") 154 endif() 155 156 foreach(type IN LISTS types) 157 foreach(library IN LISTS arg_${type}) 158 target_link_libraries(${NAME} ${type} ${library}) 159 if(NOT TARGET ${library}) 160 # It's possible the target has not yet been defined due to the ordering 161 # of add_subdirectory. Ergo defer the call until the end of the 162 # configuration phase. 163 164 # cmake_language(DEFER ...) evaluates arguments at the time the deferred 165 # call is executed, ergo wrap it in a cmake_language(EVAL CODE ...) to 166 # evaluate the arguments now. The arguments are wrapped in brackets to 167 # avoid re-evaluation at the deferred call. 168 cmake_language(EVAL CODE 169 "cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL 170 _pw_target_link_targets_deferred_check 171 [[${NAME}]] [[${type}]] ${library})" 172 ) 173 endif() 174 endforeach() 175 endforeach() 176endfunction() 177 178# Runs any deferred library checks for pw_target_link_targets. 179# 180# Required Args: 181# 182# <name> - The name of the library target to add the link dependencies to. 183# <type> - The type of the library (INTERFACE, PUBLIC, PRIVATE). 184# <library> - The library to check to assert it's a TARGET. 185function(_pw_target_link_targets_deferred_check NAME TYPE LIBRARY) 186 if(NOT TARGET ${LIBRARY}) 187 message(FATAL_ERROR 188 "${NAME}'s ${TYPE} dep \"${LIBRARY}\" is not a target.") 189 endif() 190endfunction() 191 192# Sets the provided variable to the multi_value_keywords from pw_add_library. 193macro(_pw_add_library_multi_value_args variable) 194 set("${variable}" SOURCES HEADERS 195 PUBLIC_DEPS PRIVATE_DEPS 196 PUBLIC_INCLUDES PRIVATE_INCLUDES 197 PUBLIC_DEFINES PRIVATE_DEFINES 198 PUBLIC_COMPILE_OPTIONS PRIVATE_COMPILE_OPTIONS 199 PUBLIC_LINK_OPTIONS PRIVATE_LINK_OPTIONS "${ARGN}") 200endmacro() 201 202# pw_add_library_generic: Creates a CMake library target. 203# 204# Required Args: 205# 206# <name> - The name of the library target to be created. 207# <type> - The library type which must be INTERFACE, OBJECT, STATIC, or 208# SHARED. 209# 210# Optional Args: 211# 212# SOURCES - source files for this library 213# HEADERS - header files for this library 214# PUBLIC_DEPS - public pw_target_link_targets arguments 215# PRIVATE_DEPS - private pw_target_link_targets arguments 216# PUBLIC_INCLUDES - public target_include_directories argument 217# PRIVATE_INCLUDES - public target_include_directories argument 218# PUBLIC_DEFINES - public target_compile_definitions arguments 219# PRIVATE_DEFINES - private target_compile_definitions arguments 220# PUBLIC_COMPILE_OPTIONS - public target_compile_options arguments 221# PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments 222# PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE - private target_compile_options BEFORE 223# arguments from the specified deps's INTERFACE_COMPILE_OPTIONS. Note that 224# these deps are not pulled in as target_link_libraries. This should not be 225# exposed by the non-generic API. 226# PUBLIC_LINK_OPTIONS - public target_link_options arguments 227# PRIVATE_LINK_OPTIONS - private target_link_options arguments 228function(pw_add_library_generic NAME TYPE) 229 set(supported_library_types INTERFACE OBJECT STATIC SHARED) 230 if(NOT "${TYPE}" IN_LIST supported_library_types) 231 message(FATAL_ERROR "\"${TYPE}\" is not a valid library type for ${NAME}. " 232 "Must be INTERFACE, OBJECT, STATIC, or SHARED.") 233 endif() 234 235 _pw_add_library_multi_value_args(multi_value_args) 236 pw_parse_arguments( 237 NUM_POSITIONAL_ARGS 238 2 239 MULTI_VALUE_ARGS 240 ${multi_value_args} 241 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 242 ) 243 244 # CMake 3.22 does not have a notion of target_headers yet, so in the mean 245 # time we ask for headers to be specified for consistency with GN & Bazel and 246 # to improve the IDE experience. However, we do want to ensure all the headers 247 # which are otherwise ignored by CMake are present. 248 # 249 # See https://gitlab.kitware.com/cmake/cmake/-/issues/22468 for adding support 250 # to CMake to associate headers with targets properly for CMake 3.23. 251 foreach(header IN ITEMS ${arg_HEADERS}) 252 get_filename_component(header "${header}" ABSOLUTE) 253 if(NOT EXISTS ${header}) 254 message(FATAL_ERROR "Header not found: \"${header}\"") 255 endif() 256 endforeach() 257 258 # Applies public or private attributes to a target. This fills a role similar 259 # to GN's public_configs and configs, but adds the attributes directly to the 260 # target to avoid creating extra internal targets. 261 macro(apply_library_config NAME TYPE VISIBILITY) 262 target_include_directories(${NAME} ${TYPE} ${arg_${VISIBILITY}_INCLUDES}) 263 target_compile_definitions(${NAME} ${TYPE} ${arg_${VISIBILITY}_DEFINES}) 264 target_compile_options(${NAME} ${TYPE} ${arg_${VISIBILITY}_COMPILE_OPTIONS}) 265 target_link_options(${NAME} ${TYPE} ${arg_${VISIBILITY}_LINK_OPTIONS}) 266 pw_target_link_targets(${NAME} ${TYPE} ${arg_${VISIBILITY}_DEPS}) 267 endmacro() 268 269 if("${TYPE}" STREQUAL "INTERFACE") 270 if(NOT "${arg_SOURCES}" STREQUAL "") 271 message( 272 SEND_ERROR "${NAME} cannot have sources as it's an INTERFACE library") 273 endif(NOT "${arg_SOURCES}" STREQUAL "") 274 275 add_library("${NAME}" INTERFACE EXCLUDE_FROM_ALL) 276 target_sources("${NAME}" PRIVATE ${arg_HEADERS}) 277 apply_library_config("${NAME}" INTERFACE PUBLIC) 278 elseif(("${TYPE}" STREQUAL "STATIC") OR ("${TYPE}" STREQUAL "SHARED")) 279 if("${arg_SOURCES}" STREQUAL "") 280 message( 281 SEND_ERROR "${NAME} must have SOURCES as it's not an INTERFACE library") 282 endif("${arg_SOURCES}" STREQUAL "") 283 284 add_library("${NAME}" "${TYPE}" EXCLUDE_FROM_ALL) 285 target_sources("${NAME}" PRIVATE ${arg_HEADERS} ${arg_SOURCES}) 286 apply_library_config("${NAME}" PUBLIC PUBLIC) 287 apply_library_config("${NAME}" PRIVATE PRIVATE) 288 foreach(compile_option_dep IN LISTS arg_PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE) 289 # This will fail at build time if the target does not exist. 290 target_compile_options("${NAME}" BEFORE PRIVATE 291 $<TARGET_PROPERTY:${compile_option_dep},INTERFACE_COMPILE_OPTIONS> 292 ) 293 endforeach() 294 elseif("${TYPE}" STREQUAL "OBJECT") 295 if("${arg_SOURCES}" STREQUAL "") 296 message( 297 SEND_ERROR "${NAME} must have SOURCES as it's not an INTERFACE library") 298 endif("${arg_SOURCES}" STREQUAL "") 299 300 # In order to support OBJECT libraries while maintaining transitive 301 # linking dependencies, the library has to be split up into two where the 302 # outer interface library forwards not only the internal object library 303 # but also its TARGET_OBJECTS. 304 add_library("${NAME}._pw_object" OBJECT EXCLUDE_FROM_ALL) 305 target_sources("${NAME}._pw_object" PRIVATE ${arg_SOURCES}) 306 apply_library_config("${NAME}._pw_object" PRIVATE PUBLIC) 307 apply_library_config("${NAME}._pw_object" PRIVATE PRIVATE) 308 foreach(compile_option_dep IN LISTS arg_PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE) 309 # This will fail at build time if the target does not exist. 310 target_compile_options("${NAME}._pw_object" BEFORE PRIVATE 311 $<TARGET_PROPERTY:${compile_option_dep},INTERFACE_COMPILE_OPTIONS> 312 ) 313 endforeach() 314 315 add_library("${NAME}" INTERFACE EXCLUDE_FROM_ALL) 316 target_sources("${NAME}" PRIVATE ${arg_HEADERS}) 317 pw_target_link_targets("${NAME}" 318 INTERFACE 319 "${NAME}._pw_object" 320 ) 321 apply_library_config("${NAME}" INTERFACE PUBLIC) 322 target_link_libraries("${NAME}" 323 INTERFACE 324 $<TARGET_OBJECTS:${NAME}._pw_object> 325 ) 326 else() 327 message(FATAL_ERROR "Unsupported libary type: ${TYPE}") 328 endif() 329endfunction(pw_add_library_generic) 330 331# Checks that the library's name is prefixed by the relative path with dot 332# separators instead of forward slashes. Ignores paths not under the root 333# directory. 334# 335# Optional Args: 336# 337# REMAP_PREFIXES - support remapping a prefix for checks 338# 339function(_pw_check_name_is_relative_to_root NAME ROOT) 340 pw_parse_arguments( 341 NUM_POSITIONAL_ARGS 342 2 343 MULTI_VALUE_ARGS 344 REMAP_PREFIXES 345 ) 346 347 file(RELATIVE_PATH rel_path "${ROOT}" "${CMAKE_CURRENT_SOURCE_DIR}") 348 if("${rel_path}" MATCHES "^\\.\\.") 349 return() # Ignore paths not under ROOT 350 endif() 351 352 list(LENGTH arg_REMAP_PREFIXES remap_arg_count) 353 if("${remap_arg_count}" EQUAL 2) 354 list(GET arg_REMAP_PREFIXES 0 from_prefix) 355 list(GET arg_REMAP_PREFIXES 1 to_prefix) 356 string(REGEX REPLACE "^${from_prefix}" "${to_prefix}" rel_path "${rel_path}") 357 elseif(NOT "${remap_arg_count}" EQUAL 0) 358 message(FATAL_ERROR 359 "If REMAP_PREFIXES is specified, exactly two arguments must be given.") 360 endif() 361 362 if(NOT "${rel_path}" MATCHES "^\\.\\..*") 363 string(REPLACE "/" "." dot_rel_path "${rel_path}") 364 if(NOT "${NAME}" MATCHES "^${dot_rel_path}(\\.[^\\.]+)?(\\.facade)?$") 365 message(FATAL_ERROR 366 "Module libraries under ${ROOT} must match the module name or be in " 367 "the form 'PATH_TO.THE_TARGET.NAME'. The library '${NAME}' does not " 368 "match. Expected ${dot_rel_path}.LIBRARY_NAME" 369 ) 370 endif() 371 endif() 372endfunction(_pw_check_name_is_relative_to_root) 373 374# Creates a pw module library. 375# 376# Required Args: 377# 378# <name> - The name of the library target to be created. 379# <type> - The library type which must be INTERFACE, OBJECT, STATIC or SHARED. 380# 381# Optional Args: 382# 383# SOURCES - source files for this library 384# HEADERS - header files for this library 385# PUBLIC_DEPS - public pw_target_link_targets arguments 386# PRIVATE_DEPS - private pw_target_link_targets arguments 387# PUBLIC_INCLUDES - public target_include_directories argument 388# PRIVATE_INCLUDES - public target_include_directories argument 389# PUBLIC_DEFINES - public target_compile_definitions arguments 390# PRIVATE_DEFINES - private target_compile_definitions arguments 391# PUBLIC_COMPILE_OPTIONS - public target_compile_options arguments 392# PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments 393# PUBLIC_LINK_OPTIONS - public target_link_options arguments 394# PRIVATE_LINK_OPTIONS - private target_link_options arguments 395# 396function(pw_add_library NAME TYPE) 397 _pw_add_library_multi_value_args(pw_add_library_generic_multi_value_args) 398 pw_parse_arguments( 399 NUM_POSITIONAL_ARGS 400 2 401 MULTI_VALUE_ARGS 402 ${pw_add_library_generic_multi_value_args} 403 ) 404 405 _pw_check_name_is_relative_to_root("${NAME}" "$ENV{PW_ROOT}" 406 REMAP_PREFIXES 407 third_party pw_third_party 408 ) 409 410 pw_add_library_generic(${NAME} ${TYPE} 411 SOURCES 412 ${arg_SOURCES} 413 HEADERS 414 ${arg_HEADERS} 415 PUBLIC_DEPS 416 # TODO: b/232141950 - Apply compilation options that affect ABI 417 # globally in the CMake build instead of injecting them into libraries. 418 pw_build 419 ${arg_PUBLIC_DEPS} 420 PRIVATE_DEPS 421 ${arg_PRIVATE_DEPS} 422 PUBLIC_INCLUDES 423 ${arg_PUBLIC_INCLUDES} 424 PRIVATE_INCLUDES 425 ${arg_PRIVATE_INCLUDES} 426 PUBLIC_DEFINES 427 ${arg_PUBLIC_DEFINES} 428 PRIVATE_DEFINES 429 ${arg_PRIVATE_DEFINES} 430 PUBLIC_COMPILE_OPTIONS 431 ${arg_PUBLIC_COMPILE_OPTIONS} 432 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 433 pw_build.warnings 434 PRIVATE_COMPILE_OPTIONS 435 ${arg_PRIVATE_COMPILE_OPTIONS} 436 PUBLIC_LINK_OPTIONS 437 ${arg_PUBLIC_LINK_OPTIONS} 438 PRIVATE_LINK_OPTIONS 439 ${arg_PRIVATE_LINK_OPTIONS} 440 ) 441endfunction(pw_add_library) 442 443# Declares a module as a facade. 444# 445# Facades are declared as two libraries to avoid circular dependencies. 446# Libraries that use the facade depend on a library named for the module. The 447# module that implements the facade depends on a library named 448# MODULE_NAME.facade. 449# 450# pw_add_facade accepts the same arguments as pw_add_library. 451# It also accepts the following argument: 452# 453# BACKEND - The name of the facade's backend variable. 454function(pw_add_facade NAME TYPE) 455 _pw_add_library_multi_value_args(multi_value_args) 456 pw_parse_arguments( 457 NUM_POSITIONAL_ARGS 458 2 459 ONE_VALUE_ARGS 460 BACKEND 461 MULTI_VALUE_ARGS 462 ${multi_value_args} 463 ) 464 465 _pw_check_name_is_relative_to_root("${NAME}" "$ENV{PW_ROOT}" 466 REMAP_PREFIXES 467 third_party pw_third_party 468 ) 469 470 pw_add_facade_generic("${NAME}" "${TYPE}" 471 BACKEND 472 ${arg_BACKEND} 473 SOURCES 474 ${arg_SOURCES} 475 HEADERS 476 ${arg_HEADERS} 477 PUBLIC_DEPS 478 # TODO: b/232141950 - Apply compilation options that affect ABI 479 # globally in the CMake build instead of injecting them into libraries. 480 pw_build 481 ${arg_PUBLIC_DEPS} 482 PRIVATE_DEPS 483 ${arg_PRIVATE_DEPS} 484 PUBLIC_INCLUDES 485 ${arg_PUBLIC_INCLUDES} 486 PRIVATE_INCLUDES 487 ${arg_PRIVATE_INCLUDES} 488 PUBLIC_DEFINES 489 ${arg_PUBLIC_DEFINES} 490 PRIVATE_DEFINES 491 ${arg_PRIVATE_DEFINES} 492 PUBLIC_COMPILE_OPTIONS 493 ${arg_PUBLIC_COMPILE_OPTIONS} 494 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 495 pw_build.warnings 496 PRIVATE_COMPILE_OPTIONS 497 ${arg_PRIVATE_COMPILE_OPTIONS} 498 PUBLIC_LINK_OPTIONS 499 ${arg_PUBLIC_LINK_OPTIONS} 500 PRIVATE_LINK_OPTIONS 501 ${arg_PRIVATE_LINK_OPTIONS} 502 ) 503endfunction(pw_add_facade) 504 505# pw_add_facade_generic: Creates a CMake facade library target. 506# 507# Facades are declared as two libraries to avoid circular dependencies. 508# Libraries that use the facade depend on the <name> of this target. The 509# libraries that implement this facade have to depend on an internal library 510# named <name>.facade. 511# 512# Required Args: 513# 514# <name> - The name for the public facade target (<name>) for all users and 515# the suffixed facade target for backend implementers (<name.facade). 516# <type> - The library type which must be INTERFACE, OBJECT, STATIC, or 517# SHARED. 518# BACKEND - The name of the facade's backend variable. 519# 520# Optional Args: 521# 522# SOURCES - source files for this library 523# HEADERS - header files for this library 524# PUBLIC_DEPS - public pw_target_link_targets arguments 525# PRIVATE_DEPS - private pw_target_link_targets arguments 526# PUBLIC_INCLUDES - public target_include_directories argument 527# PRIVATE_INCLUDES - public target_include_directories argument 528# PUBLIC_DEFINES - public target_compile_definitions arguments 529# PRIVATE_DEFINES - private target_compile_definitions arguments 530# PUBLIC_COMPILE_OPTIONS - public target_compile_options arguments 531# PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE - private target_compile_options BEFORE 532# arguments from the specified deps's INTERFACE_COMPILE_OPTIONS. Note that 533# these deps are not pulled in as target_link_libraries. This should not be 534# exposed by the non-generic API. 535# PRIVATE_COMPILE_OPTIONS - private target_compile_options arguments 536# PUBLIC_LINK_OPTIONS - public target_link_options arguments 537# PRIVATE_LINK_OPTIONS - private target_link_options arguments 538function(pw_add_facade_generic NAME TYPE) 539 set(supported_library_types INTERFACE OBJECT STATIC SHARED) 540 if(NOT "${TYPE}" IN_LIST supported_library_types) 541 message(FATAL_ERROR "\"${TYPE}\" is not a valid library type for ${NAME}. " 542 "Must be INTERFACE, OBJECT, STATIC, or SHARED.") 543 endif() 544 545 _pw_add_library_multi_value_args(multi_value_args) 546 pw_parse_arguments( 547 NUM_POSITIONAL_ARGS 548 2 549 ONE_VALUE_ARGS 550 BACKEND 551 MULTI_VALUE_ARGS 552 ${multi_value_args} 553 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 554 REQUIRED_ARGS 555 BACKEND 556 ) 557 558 if(NOT DEFINED "${arg_BACKEND}") 559 message(FATAL_ERROR "${NAME}'s backend variable ${arg_BACKEND} has not " 560 "been defined, you may be missing a pw_add_backend_variable or " 561 "the *.cmake import to that file.") 562 endif() 563 string(REGEX MATCH ".+_BACKEND" backend_ends_in_backend "${arg_BACKEND}") 564 if(NOT backend_ends_in_backend) 565 message(FATAL_ERROR "The ${NAME} pw_add_generic_facade's BACKEND argument " 566 "(${arg_BACKEND}) must end in _BACKEND (${name_ends_in_backend})") 567 endif() 568 569 set(backend_target "${${arg_BACKEND}}") 570 if ("${backend_target}" STREQUAL "") 571 # If no backend is set, a script that displays an error message is used 572 # instead. If the facade is used in the build, it fails with this error. 573 pw_add_error_target("${NAME}.NO_BACKEND_SET" 574 MESSAGE 575 "Attempted to build the ${NAME} facade with no backend set. " 576 "Configure the ${NAME} backend using pw_set_backend or remove all " 577 "dependencies on it. See https://pigweed.dev/pw_build." 578 ) 579 580 set(backend_target "${NAME}.NO_BACKEND_SET") 581 endif() 582 583 # Define the facade library, which is used by the backend to avoid circular 584 # dependencies. 585 pw_add_library_generic("${NAME}.facade" INTERFACE 586 HEADERS 587 ${arg_HEADERS} 588 PUBLIC_INCLUDES 589 ${arg_PUBLIC_INCLUDES} 590 PUBLIC_DEPS 591 ${arg_PUBLIC_DEPS} 592 PUBLIC_DEFINES 593 ${arg_PUBLIC_DEFINES} 594 PUBLIC_COMPILE_OPTIONS 595 ${arg_PUBLIC_COMPILE_OPTIONS} 596 PUBLIC_LINK_OPTIONS 597 ${arg_PUBLIC_LINK_OPTIONS} 598 ) 599 600 # Define the public-facing library for this facade, which depends on the 601 # header files and public interface aspects from the .facade target and 602 # exposes the dependency on the backend along with the private library 603 # target components. 604 pw_add_library_generic("${NAME}" "${TYPE}" 605 PUBLIC_DEPS 606 "${NAME}.facade" 607 "${backend_target}" 608 SOURCES 609 ${arg_SOURCES} 610 PRIVATE_INCLUDES 611 ${arg_PRIVATE_INCLUDES} 612 PRIVATE_DEPS 613 ${arg_PRIVATE_DEPS} 614 PRIVATE_DEFINES 615 ${arg_PRIVATE_DEFINES} 616 PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE 617 ${arg_PRIVATE_COMPILE_OPTIONS_DEPS_BEFORE} 618 PRIVATE_COMPILE_OPTIONS 619 ${arg_PRIVATE_COMPILE_OPTIONS} 620 PRIVATE_LINK_OPTIONS 621 ${arg_PRIVATE_LINK_OPTIONS} 622 ) 623endfunction(pw_add_facade_generic) 624 625# Declare a facade's backend variables which can be overriden later by using 626# pw_set_backend. 627# 628# Required Arguments: 629# NAME - Name of the facade's backend variable. 630# 631# Optional Arguments: 632# DEFAULT_BACKEND - Optional default backend selection for the facade. 633# 634function(pw_add_backend_variable NAME) 635 pw_parse_arguments( 636 NUM_POSITIONAL_ARGS 637 1 638 ONE_VALUE_ARGS 639 DEFAULT_BACKEND 640 ) 641 642 string(REGEX MATCH ".+_BACKEND" name_ends_in_backend "${NAME}") 643 if(NOT name_ends_in_backend) 644 message(FATAL_ERROR "The ${NAME} pw_add_backend_variable's NAME argument " 645 "must end in _BACKEND") 646 endif() 647 648 set("${NAME}" "${arg_DEFAULT_BACKEND}" CACHE STRING 649 "${NAME} backend variable for a facade") 650endfunction() 651 652# Sets which backend to use for the given facade's backend variable. 653function(pw_set_backend NAME BACKEND) 654 # TODO(ewout, hepler): Deprecate this temporarily support which permits the 655 # direct facade name directly, instead of the facade's backend variable name. 656 # Also update this to later assert the variable is DEFINED to catch typos. 657 string(REGEX MATCH ".+_BACKEND" name_ends_in_backend "${NAME}") 658 if(NOT name_ends_in_backend) 659 set(NAME "${NAME}_BACKEND") 660 endif() 661 if(NOT DEFINED "${NAME}") 662 message(WARNING "${NAME} was not defined when pw_set_backend was invoked, " 663 "you may be missing a pw_add_backend_variable or the *.cmake " 664 "import to that file.") 665 endif() 666 667 set("${NAME}" "${BACKEND}" CACHE STRING "backend variable for a facade" FORCE) 668endfunction(pw_set_backend) 669 670# Zephyr specific wrapper for pw_set_backend. 671function(pw_set_zephyr_backend_ifdef COND FACADE BACKEND BACKEND_DECL) 672 if(${${COND}}) 673 if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${BACKEND_DECL}") 674 message(FATAL_ERROR 675 "Can't find backend declaration file '${CMAKE_CURRENT_LIST_DIR}/${BACKEND_DECL}'") 676 endif() 677 include("${CMAKE_CURRENT_LIST_DIR}/${BACKEND_DECL}") 678 pw_set_backend("${FACADE}" "${BACKEND}") 679 endif() 680endfunction() 681 682# Zephyr specific wrapper to convert a pw library to a Zephyr library 683function(pw_zephyrize_libraries_ifdef COND) 684 if(DEFINED Zephyr_FOUND) 685 if(${${COND}}) 686 zephyr_link_libraries(${ARGN}) 687 foreach(lib ${ARGN}) 688 target_link_libraries(${lib} INTERFACE zephyr_interface) 689 endforeach() 690 endif() 691 endif() 692endfunction() 693 694# Zephyr function allowing conversion of Kconfig values to Pigweed configs 695function(pw_set_config_from_zephyr ZEPHYR_CONFIG PW_CONFIG) 696 if(${ZEPHYR_CONFIG}) 697 add_compile_definitions(${PW_CONFIG}=${${ZEPHYR_CONFIG}}) 698 endif() 699endfunction() 700 701# Set up the default pw_build_DEFAULT_MODULE_CONFIG. 702set("pw_build_DEFAULT_MODULE_CONFIG" pw_build.empty CACHE STRING 703 "Default implementation for all Pigweed module configurations.") 704 705# Declares a module configuration variable for module libraries to depend on. 706# Configs should be set to libraries which can be used to provide defines 707# directly or though included header files. 708# 709# The configs can be selected either through the pw_set_module_config function 710# to set the pw_build_DEFAULT_MODULE_CONFIG used by default for all Pigweed 711# modules or by selecting a specific one for the given NAME'd configuration. 712# 713# Args: 714# 715# NAME: name to use for the target which can be depended on for the config. 716function(pw_add_module_config NAME) 717 pw_parse_arguments(NUM_POSITIONAL_ARGS 1) 718 719 # Declare the module configuration variable for this module. 720 set("${NAME}" "${pw_build_DEFAULT_MODULE_CONFIG}" 721 CACHE STRING "Module configuration for ${NAME}") 722endfunction(pw_add_module_config) 723 724# Sets which config library to use for the given module. 725# 726# This can be used to set a specific module configuration or the default 727# module configuration used for all Pigweed modules: 728# 729# pw_set_module_config(pw_build_DEFAULT_MODULE_CONFIG my_config) 730# pw_set_module_config(pw_foo_CONFIG my_foo_config) 731function(pw_set_module_config NAME LIBRARY) 732 pw_parse_arguments(NUM_POSITIONAL_ARGS 2) 733 734 # Update the module configuration variable. 735 set("${NAME}" "${LIBRARY}" CACHE STRING "Config for ${NAME}" FORCE) 736endfunction(pw_set_module_config) 737 738# Adds compiler options to all targets built by CMake. Flags may be added any 739# time after this function is defined. The effect is global; all targets added 740# before or after a pw_add_global_compile_options call will be built with the 741# flags, regardless of where the files are located. 742# 743# pw_add_global_compile_options takes one optional named argument: 744# 745# LANGUAGES: Which languages (ASM, C, CXX) to apply the options to. Flags 746# apply to all languages by default. 747# 748# All other arguments are interpreted as compiler options. 749function(pw_add_global_compile_options) 750 cmake_parse_arguments(PARSE_ARGV 0 args "" "" "LANGUAGES") 751 752 set(supported_build_languages ASM C CXX) 753 754 if(NOT args_LANGUAGES) 755 set(args_LANGUAGES ${supported_build_languages}) 756 endif() 757 758 # Check the selected language. 759 foreach(lang IN LISTS args_LANGUAGES) 760 if(NOT "${lang}" IN_LIST supported_build_languages) 761 message(FATAL_ERROR "'${lang}' is not a supported language. " 762 "Supported languages: ${supported_build_languages}") 763 endif() 764 endforeach() 765 766 # Enumerate which flags variables to set. 767 foreach(lang IN LISTS args_LANGUAGES) 768 list(APPEND cmake_flags_variables "CMAKE_${lang}_FLAGS") 769 endforeach() 770 771 # Set each flag for each specified flags variable. 772 foreach(variable IN LISTS cmake_flags_variables) 773 foreach(flag IN LISTS args_UNPARSED_ARGUMENTS) 774 set(${variable} "${${variable}} ${flag}" CACHE INTERNAL "" FORCE) 775 endforeach() 776 endforeach() 777endfunction(pw_add_global_compile_options) 778 779# pw_add_error_target: Creates a CMake target which fails to build and prints a 780# message 781# 782# This function prints a message and causes a build failure only if you attempt 783# to build the target. This is useful when FATAL_ERROR messages cannot be used 784# to catch problems during the CMake configuration phase. 785# 786# Args: 787# 788# NAME: name to use for the target 789# MESSAGE: The message to print, prefixed with "ERROR: ". The message may be 790# composed of multiple pieces by passing multiple strings. 791# 792function(pw_add_error_target NAME) 793 pw_parse_arguments( 794 NUM_POSITIONAL_ARGS 795 1 796 MULTI_VALUE_ARGS 797 MESSAGE 798 ) 799 800 # In case the message is comprised of multiple strings, stitch them together. 801 set(message "ERROR: ") 802 foreach(line IN LISTS arg_MESSAGE) 803 string(APPEND message "${line}") 804 endforeach() 805 806 add_custom_target("${NAME}._error_message" 807 COMMAND 808 "${CMAKE_COMMAND}" -E echo "${message}" 809 COMMAND 810 "${CMAKE_COMMAND}" -E false 811 ) 812 813 # A static library is provided, in case this rule nominally provides a 814 # compiled output, e.g. to enable $<TARGET_FILE:"${NAME}">. 815 pw_add_library_generic("${NAME}" STATIC 816 SOURCES 817 $<TARGET_PROPERTY:pw_build.empty,SOURCES> 818 ) 819 add_dependencies("${NAME}" "${NAME}._error_message") 820endfunction(pw_add_error_target) 821 822# Rebases a set of files to a new root path and optionally appends extensions 823# to them. This is particularly useful for file generators. 824# 825# Required Arguments: 826# 827# <var> - Variable to store the rebased file list in. 828# <new_root> - The new root to rebase file paths onto. 829# <root> - The current root to rebase off of. 830# <files> - The list of files to rebase. 831# <extensions> - List of extensions to replace the existing file extensions 832# with. 833# 834# Examples: 835# 836# list(APPEND files "public/proj/foo.def" "public/proj/bar.def") 837# 838# pw_rebase_paths(out_files "/tmp" "${CMAKE_CURRENT_SOURCE_DIR}/public" 839# ${files} "") 840# out_files => [ "/tmp/proj/foo.def", "/tmp/proj/bar.def" ] 841# 842# pw_rebase_paths(out_files "/tmp" "${CMAKE_CURRENT_SOURCE_DIR}/public" 843# ${files} ".h") 844# out_files => [ "/tmp/proj/foo.h", "/tmp/proj/bar.h" ] 845# 846# list (APPEND exts ".h" ".cc") 847# pw_rebase_paths(out_files "/tmp" "${CMAKE_CURRENT_SOURCE_DIR}/public" 848# ${files} ${exts}) 849# out_files => [ "/tmp/proj/foo.h", "/tmp/proj/bar.h", 850# "/tmp/proj/foo.cc", "/tmp/proj/bar.cc" ] 851function(pw_rebase_paths VAR NEW_ROOT ROOT FILES EXTENSIONS) 852 foreach(file IN LISTS FILES) 853 get_filename_component(file "${file}" ABSOLUTE) 854 file(RELATIVE_PATH file "${ROOT}" "${file}") 855 856 if("${EXTENSIONS}" STREQUAL "") 857 list(APPEND mirrored_files "${NEW_ROOT}/${file}") 858 else() 859 foreach(ext IN LISTS EXTENSIONS) 860 get_filename_component(dir "${file}" DIRECTORY) 861 get_filename_component(name "${file}" NAME_WE) 862 list(APPEND mirrored_files "${NEW_ROOT}/${dir}/${name}${ext}") 863 endforeach() 864 endif() 865 endforeach() 866 867 set("${VAR}" 868 "${mirrored_files}" 869 PARENT_SCOPE) 870endfunction(pw_rebase_paths) 871