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