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 16include($ENV{PW_ROOT}/pw_build/pigweed.cmake) 17 18# Declares a protocol buffers library. This function creates a library for each 19# supported protocol buffer implementation: 20# 21# ${NAME}.pwpb - pw_protobuf generated code 22# ${NAME}.nanopb - Nanopb generated code (requires Nanopb) 23# 24# This function also creates libraries for generating pw_rpc code: 25# 26# ${NAME}.pwpb_rpc - generates pw_protobuf pw_rpc code 27# ${NAME}.nanopb_rpc - generates Nanopb pw_rpc code 28# ${NAME}.raw_rpc - generates raw pw_rpc (no protobuf library) code 29# 30# Args: 31# 32# NAME - the base name of the libraries to create 33# SOURCES - .proto source files 34# DEPS - dependencies on other pw_proto_library targets 35# PREFIX - prefix add to the proto files 36# STRIP_PREFIX - prefix to remove from the proto files 37# INPUTS - files to include along with the .proto files (such as Nanopb 38# .options files) 39# 40function(pw_proto_library NAME) 41 pw_parse_arguments( 42 NUM_POSITIONAL_ARGS 43 1 44 ONE_VALUE_ARGS 45 STRIP_PREFIX 46 PREFIX 47 MULTI_VALUE_ARGS 48 SOURCES 49 INPUTS 50 DEPS 51 REQUIRED_ARGS 52 SOURCES 53 ) 54 55 set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}") 56 57 # Use INTERFACE libraries to track the proto include paths that are passed to 58 # protoc. 59 set(include_deps "${arg_DEPS}") 60 list(TRANSFORM include_deps APPEND ._includes) 61 62 pw_add_library_generic("${NAME}._includes" INTERFACE 63 PUBLIC_INCLUDES 64 "${out_dir}/sources" 65 PUBLIC_DEPS 66 ${include_deps} 67 ) 68 69 # Generate a file with all include paths needed by protoc. Use the include 70 # directory paths and replace ; with \n. 71 set(include_file "${out_dir}/include_paths.txt") 72 file(GENERATE OUTPUT "${include_file}" 73 CONTENT 74 "$<JOIN:$<TARGET_PROPERTY:${NAME}._includes,INTERFACE_INCLUDE_DIRECTORIES>,\n>") 75 76 if("${arg_STRIP_PREFIX}" STREQUAL "") 77 set(arg_STRIP_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}") 78 else() 79 get_filename_component(arg_STRIP_PREFIX "${arg_STRIP_PREFIX}" ABSOLUTE) 80 endif() 81 82 foreach(path IN LISTS arg_SOURCES arg_INPUTS) 83 get_filename_component(abspath "${path}" ABSOLUTE) 84 list(APPEND files_to_mirror "${abspath}") 85 endforeach() 86 87 # Mirror the sources to the output directory with the specified prefix. 88 pw_rebase_paths( 89 sources "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" 90 "${arg_SOURCES}" "") 91 pw_rebase_paths( 92 inputs "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" 93 "${arg_INPUTS}" "") 94 95 add_custom_command( 96 COMMAND 97 python3 98 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 99 --source-root "${arg_STRIP_PREFIX}" 100 --directory "${out_dir}/sources/${arg_PREFIX}" 101 ${files_to_mirror} 102 DEPENDS 103 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 104 ${files_to_mirror} 105 OUTPUT 106 ${sources} ${inputs} 107 ) 108 add_custom_target("${NAME}._sources" DEPENDS ${sources} ${inputs}) 109 110 set(sources_deps "${arg_DEPS}") 111 list(TRANSFORM sources_deps APPEND ._sources) 112 113 if(sources_deps) 114 add_dependencies("${NAME}._sources" ${sources_deps}) 115 endif() 116 117 # Create a protobuf target for each supported protobuf library. 118 _pw_pwpb_library("${NAME}" 119 SOURCES 120 ${sources} 121 INPUTS 122 ${inputs} 123 DEPS 124 ${arg_DEPS} 125 INCLUDE_FILE 126 "${include_file}" 127 OUT_DIR 128 "${out_dir}" 129 ) 130 _pw_pwpb_rpc_library("${NAME}" 131 SOURCES 132 ${sources} 133 INPUTS 134 ${inputs} 135 DEPS 136 ${arg_DEPS} 137 INCLUDE_FILE 138 "${include_file}" 139 OUT_DIR 140 "${out_dir}" 141 ) 142 _pw_raw_rpc_library("${NAME}" 143 SOURCES 144 ${sources} 145 INPUTS 146 ${inputs} 147 DEPS 148 ${arg_DEPS} 149 INCLUDE_FILE 150 "${include_file}" 151 OUT_DIR 152 "${out_dir}" 153 ) 154 _pw_nanopb_library("${NAME}" 155 SOURCES 156 ${sources} 157 INPUTS 158 ${inputs} 159 DEPS 160 ${arg_DEPS} 161 INCLUDE_FILE 162 "${include_file}" 163 OUT_DIR 164 "${out_dir}" 165 ) 166 _pw_nanopb_rpc_library("${NAME}" 167 SOURCES 168 ${sources} 169 INPUTS 170 ${inputs} 171 DEPS 172 ${arg_DEPS} 173 INCLUDE_FILE 174 "${include_file}" 175 OUT_DIR 176 "${out_dir}" 177 ) 178endfunction(pw_proto_library) 179 180# Args for generate_protos.py 181set(pw_protobuf_compiler_GENERATE_PROTOS_ARGS "" CACHE STRING "Args to generate_protos.py") 182 183# Internal function that invokes protoc through generate_protos.py. 184function(_pw_generate_protos TARGET LANGUAGE) 185 pw_parse_arguments( 186 NUM_POSITIONAL_ARGS 187 2 188 ONE_VALUE_ARGS 189 PLUGIN 190 INCLUDE_FILE 191 OUT_DIR 192 MULTI_VALUE_ARGS 193 OUTPUT_EXTS 194 SOURCES 195 INPUTS 196 DEPENDS 197 ) 198 199 # Determine the names of the compiled output files. 200 pw_rebase_paths(outputs 201 "${arg_OUT_DIR}/${LANGUAGE}" "${arg_OUT_DIR}/sources" "${arg_SOURCES}" 202 "${arg_OUTPUT_EXTS}") 203 204 # Export the output files to the caller's scope so it can use them if needed. 205 set(generated_outputs "${outputs}" PARENT_SCOPE) 206 207 if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 208 foreach(source_file IN LISTS SOURCES) 209 get_filename_component(dir "${source_file}" DIRECTORY) 210 get_filename_component(name "${source_file}" NAME_WE) 211 set(arg_PLUGIN "${dir}/${name}.bat") 212 endforeach() 213 endif() 214 215 set(script "$ENV{PW_ROOT}/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py") 216 add_custom_command( 217 COMMAND 218 python3 219 "${script}" 220 --language "${LANGUAGE}" 221 --plugin-path "${arg_PLUGIN}" 222 --include-file "${arg_INCLUDE_FILE}" 223 --compile-dir "${arg_OUT_DIR}/sources" 224 --out-dir "${arg_OUT_DIR}/${LANGUAGE}" 225 --sources ${arg_SOURCES} 226 "${pw_protobuf_compiler_GENERATE_PROTOS_ARGS}" 227 DEPENDS 228 ${script} 229 ${arg_SOURCES} 230 ${arg_INPUTS} 231 ${arg_DEPENDS} 232 OUTPUT 233 ${outputs} 234 ) 235 add_custom_target("${TARGET}._generate.${LANGUAGE}" DEPENDS ${outputs}) 236 add_dependencies("${TARGET}._generate.${LANGUAGE}" "${TARGET}._sources") 237endfunction(_pw_generate_protos) 238 239# Internal function that creates a pwpb proto library. 240function(_pw_pwpb_library NAME) 241 pw_parse_arguments( 242 NUM_POSITIONAL_ARGS 243 1 244 ONE_VALUE_ARGS 245 INCLUDE_FILE 246 OUT_DIR 247 MULTI_VALUE_ARGS 248 SOURCES 249 INPUTS 250 DEPS 251 ) 252 253 list(TRANSFORM arg_DEPS APPEND .pwpb) 254 255 _pw_generate_protos("${NAME}" pwpb 256 PLUGIN 257 "$ENV{PW_ROOT}/pw_protobuf/py/pw_protobuf/plugin.py" 258 OUTPUT_EXTS 259 ".pwpb.h" 260 INCLUDE_FILE 261 "${arg_INCLUDE_FILE}" 262 OUT_DIR 263 "${arg_OUT_DIR}" 264 SOURCES 265 ${arg_SOURCES} 266 INPUTS 267 ${arg_INPUTS} 268 DEPENDS 269 ${arg_DEPS} 270 ) 271 272 # Create the library with the generated source files. 273 pw_add_library_generic("${NAME}.pwpb" INTERFACE 274 PUBLIC_INCLUDES 275 "${arg_OUT_DIR}/pwpb" 276 PUBLIC_DEPS 277 pw_build 278 pw_protobuf 279 pw_span 280 pw_string.string 281 ${arg_DEPS} 282 ) 283 add_dependencies("${NAME}.pwpb" "${NAME}._generate.pwpb") 284endfunction(_pw_pwpb_library) 285 286# Internal function that creates a pwpb_rpc library. 287function(_pw_pwpb_rpc_library NAME) 288 pw_parse_arguments( 289 NUM_POSITIONAL_ARGS 290 1 291 ONE_VALUE_ARGS 292 INCLUDE_FILE 293 OUT_DIR 294 MULTI_VALUE_ARGS 295 SOURCES 296 INPUTS 297 DEPS 298 ) 299 300 # Determine the names of the output files. 301 list(TRANSFORM arg_DEPS APPEND .pwpb_rpc) 302 303 _pw_generate_protos("${NAME}" pwpb_rpc 304 PLUGIN 305 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_pwpb.py" 306 OUTPUT_EXTS 307 ".rpc.pwpb.h" 308 INCLUDE_FILE 309 "${arg_INCLUDE_FILE}" 310 OUT_DIR 311 "${arg_OUT_DIR}" 312 SOURCES 313 ${arg_SOURCES} 314 INPUTS 315 ${arg_INPUTS} 316 DEPENDS 317 ${arg_DEPS} 318 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 319 ) 320 321 # Create the library with the generated source files. 322 pw_add_library_generic("${NAME}.pwpb_rpc" INTERFACE 323 PUBLIC_INCLUDES 324 "${arg_OUT_DIR}/pwpb_rpc" 325 PUBLIC_DEPS 326 "${NAME}.pwpb" 327 pw_build 328 pw_rpc.pwpb.client_api 329 pw_rpc.pwpb.server_api 330 pw_rpc.server 331 ${arg_DEPS} 332 ) 333 add_dependencies("${NAME}.pwpb_rpc" "${NAME}._generate.pwpb_rpc") 334endfunction(_pw_pwpb_rpc_library) 335 336# Internal function that creates a raw_rpc proto library. 337function(_pw_raw_rpc_library NAME) 338 pw_parse_arguments( 339 NUM_POSITIONAL_ARGS 340 1 341 ONE_VALUE_ARGS 342 INCLUDE_FILE 343 OUT_DIR 344 MULTI_VALUE_ARGS 345 SOURCES 346 INPUTS 347 DEPS 348 ) 349 350 list(TRANSFORM arg_DEPS APPEND .raw_rpc) 351 352 _pw_generate_protos("${NAME}" raw_rpc 353 PLUGIN 354 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_raw.py" 355 OUTPUT_EXTS 356 ".raw_rpc.pb.h" 357 INCLUDE_FILE 358 "${arg_INCLUDE_FILE}" 359 OUT_DIR 360 "${arg_OUT_DIR}" 361 SOURCES 362 ${arg_SOURCES} 363 INPUTS 364 ${arg_INPUTS} 365 DEPENDS 366 ${arg_DEPS} 367 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 368 ) 369 370 # Create the library with the generated source files. 371 pw_add_library_generic("${NAME}.raw_rpc" INTERFACE 372 PUBLIC_INCLUDES 373 "${arg_OUT_DIR}/raw_rpc" 374 PUBLIC_DEPS 375 pw_build 376 pw_rpc.raw.server_api 377 pw_rpc.raw.client_api 378 pw_rpc.server 379 ${arg_DEPS} 380 ) 381 add_dependencies("${NAME}.raw_rpc" "${NAME}._generate.raw_rpc") 382endfunction(_pw_raw_rpc_library) 383 384# Internal function that creates a nanopb proto library. 385function(_pw_nanopb_library NAME) 386 pw_parse_arguments( 387 NUM_POSITIONAL_ARGS 388 1 389 ONE_VALUE_ARGS 390 INCLUDE_FILE 391 OUT_DIR 392 MULTI_VALUE_ARGS 393 SOURCES 394 INPUTS 395 DEPS 396 ) 397 398 list(TRANSFORM arg_DEPS APPEND .nanopb) 399 400 if("${dir_pw_third_party_nanopb}" STREQUAL "") 401 add_custom_target("${NAME}._generate.nanopb") # Nothing to do 402 pw_add_error_target("${NAME}.nanopb" 403 MESSAGE 404 "Attempting to use pw_proto_library, but dir_pw_third_party_nanopb is " 405 "not set. Set dir_pw_third_party_nanopb to the path to the Nanopb " 406 "repository." 407 ) 408 else() 409 # When compiling with the Nanopb plugin, the nanopb.proto file is already 410 # compiled internally, so skip recompiling it with protoc. 411 if("${arg_SOURCES}" MATCHES "nanopb\\.proto") 412 add_custom_target("${NAME}._generate.nanopb") # Nothing to do 413 pw_add_library_generic("${NAME}.nanopb" INTERFACE 414 PUBLIC_DEPS 415 pw_build 416 pw_third_party.nanopb 417 ${arg_DEPS} 418 ) 419 else() 420 _pw_generate_protos("${NAME}" nanopb 421 PLUGIN 422 "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb" 423 OUTPUT_EXTS 424 ".pb.h" 425 ".pb.c" 426 INCLUDE_FILE 427 "${arg_INCLUDE_FILE}" 428 OUT_DIR 429 "${arg_OUT_DIR}" 430 SOURCES 431 ${arg_SOURCES} 432 INPUTS 433 ${arg_INPUTS} 434 DEPENDS 435 ${arg_DEPS} 436 ) 437 438 # Create the library with the generated source files. 439 pw_add_library_generic("${NAME}.nanopb" STATIC 440 SOURCES 441 ${generated_outputs} 442 PUBLIC_INCLUDES 443 "${arg_OUT_DIR}/nanopb" 444 PUBLIC_DEPS 445 pw_build 446 pw_third_party.nanopb 447 ${arg_DEPS} 448 ) 449 endif() 450 451 add_dependencies("${NAME}.nanopb" "${NAME}._generate.nanopb") 452 453 # Ensure that nanopb_pb2.py is generated to avoid race conditions. 454 add_dependencies("${NAME}._generate.nanopb" 455 pw_third_party.nanopb.generate_proto 456 ) 457 endif() 458endfunction(_pw_nanopb_library) 459 460# Internal function that creates a nanopb_rpc library. 461function(_pw_nanopb_rpc_library NAME) 462 pw_parse_arguments( 463 NUM_POSITIONAL_ARGS 464 1 465 ONE_VALUE_ARGS 466 INCLUDE_FILE 467 OUT_DIR 468 MULTI_VALUE_ARGS 469 SOURCES 470 INPUTS 471 DEPS 472 ) 473 474 # Determine the names of the output files. 475 list(TRANSFORM arg_DEPS APPEND .nanopb_rpc) 476 477 _pw_generate_protos("${NAME}" nanopb_rpc 478 PLUGIN 479 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_nanopb.py" 480 OUTPUT_EXTS 481 ".rpc.pb.h" 482 INCLUDE_FILE 483 "${arg_INCLUDE_FILE}" 484 OUT_DIR 485 "${arg_OUT_DIR}" 486 SOURCES 487 ${arg_SOURCES} 488 INPUTS 489 ${arg_INPUTS} 490 DEPENDS 491 ${arg_DEPS} 492 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 493 ) 494 495 # Create the library with the generated source files. 496 pw_add_library_generic("${NAME}.nanopb_rpc" INTERFACE 497 PUBLIC_INCLUDES 498 "${arg_OUT_DIR}/nanopb_rpc" 499 PUBLIC_DEPS 500 "${NAME}.nanopb" 501 pw_build 502 pw_rpc.nanopb.client_api 503 pw_rpc.nanopb.server_api 504 pw_rpc.server 505 ${arg_DEPS} 506 ) 507 add_dependencies("${NAME}.nanopb_rpc" "${NAME}._generate.nanopb_rpc") 508endfunction(_pw_nanopb_rpc_library) 509