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 180function(_pw_rebase_paths VAR OUT_DIR ROOT FILES EXTENSIONS) 181 foreach(file IN LISTS FILES) 182 get_filename_component(file "${file}" ABSOLUTE) 183 file(RELATIVE_PATH file "${ROOT}" "${file}") 184 185 if ("${EXTENSIONS}" STREQUAL "") 186 list(APPEND mirrored_files "${OUT_DIR}/${file}") 187 else() 188 foreach(ext IN LISTS EXTENSIONS) 189 get_filename_component(dir "${file}" DIRECTORY) 190 get_filename_component(name "${file}" NAME_WE) 191 list(APPEND mirrored_files "${OUT_DIR}/${dir}/${name}${ext}") 192 endforeach() 193 endif() 194 endforeach() 195 196 set("${VAR}" "${mirrored_files}" PARENT_SCOPE) 197endfunction(_pw_rebase_paths) 198 199# Internal function that invokes protoc through generate_protos.py. 200function(_pw_generate_protos TARGET LANGUAGE) 201 pw_parse_arguments( 202 NUM_POSITIONAL_ARGS 203 2 204 ONE_VALUE_ARGS 205 PLUGIN 206 INCLUDE_FILE 207 OUT_DIR 208 MULTI_VALUE_ARGS 209 OUTPUT_EXTS 210 SOURCES 211 INPUTS 212 DEPENDS 213 ) 214 215 # Determine the names of the compiled output files. 216 _pw_rebase_paths(outputs 217 "${arg_OUT_DIR}/${LANGUAGE}" "${arg_OUT_DIR}/sources" "${arg_SOURCES}" 218 "${arg_OUTPUT_EXTS}") 219 220 # Export the output files to the caller's scope so it can use them if needed. 221 set(generated_outputs "${outputs}" PARENT_SCOPE) 222 223 if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 224 foreach(source_file IN LISTS SOURCES) 225 get_filename_component(dir "${source_file}" DIRECTORY) 226 get_filename_component(name "${source_file}" NAME_WE) 227 set(arg_PLUGIN "${dir}/${name}.bat") 228 endforeach() 229 endif() 230 231 set(script "$ENV{PW_ROOT}/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py") 232 add_custom_command( 233 COMMAND 234 python3 235 "${script}" 236 --language "${LANGUAGE}" 237 --plugin-path "${arg_PLUGIN}" 238 --include-file "${arg_INCLUDE_FILE}" 239 --compile-dir "${arg_OUT_DIR}/sources" 240 --out-dir "${arg_OUT_DIR}/${LANGUAGE}" 241 --sources ${arg_SOURCES} 242 DEPENDS 243 ${script} 244 ${arg_SOURCES} 245 ${arg_INPUTS} 246 ${arg_DEPENDS} 247 OUTPUT 248 ${outputs} 249 ) 250 add_custom_target("${TARGET}._generate.${LANGUAGE}" DEPENDS ${outputs}) 251 add_dependencies("${TARGET}._generate.${LANGUAGE}" "${TARGET}._sources") 252endfunction(_pw_generate_protos) 253 254# Internal function that creates a pwpb proto library. 255function(_pw_pwpb_library NAME) 256 pw_parse_arguments( 257 NUM_POSITIONAL_ARGS 258 1 259 ONE_VALUE_ARGS 260 INCLUDE_FILE 261 OUT_DIR 262 MULTI_VALUE_ARGS 263 SOURCES 264 INPUTS 265 DEPS 266 ) 267 268 list(TRANSFORM arg_DEPS APPEND .pwpb) 269 270 _pw_generate_protos("${NAME}" pwpb 271 PLUGIN 272 "$ENV{PW_ROOT}/pw_protobuf/py/pw_protobuf/plugin.py" 273 OUTPUT_EXTS 274 ".pwpb.h" 275 INCLUDE_FILE 276 "${arg_INCLUDE_FILE}" 277 OUT_DIR 278 "${arg_OUT_DIR}" 279 SOURCES 280 ${arg_SOURCES} 281 INPUTS 282 ${arg_INPUTS} 283 DEPENDS 284 ${arg_DEPS} 285 ) 286 287 # Create the library with the generated source files. 288 pw_add_library_generic("${NAME}.pwpb" INTERFACE 289 PUBLIC_INCLUDES 290 "${arg_OUT_DIR}/pwpb" 291 PUBLIC_DEPS 292 pw_build 293 pw_protobuf 294 pw_span 295 pw_string.string 296 ${arg_DEPS} 297 ) 298 add_dependencies("${NAME}.pwpb" "${NAME}._generate.pwpb") 299endfunction(_pw_pwpb_library) 300 301# Internal function that creates a pwpb_rpc library. 302function(_pw_pwpb_rpc_library NAME) 303 pw_parse_arguments( 304 NUM_POSITIONAL_ARGS 305 1 306 ONE_VALUE_ARGS 307 INCLUDE_FILE 308 OUT_DIR 309 MULTI_VALUE_ARGS 310 SOURCES 311 INPUTS 312 DEPS 313 ) 314 315 # Determine the names of the output files. 316 list(TRANSFORM arg_DEPS APPEND .pwpb_rpc) 317 318 _pw_generate_protos("${NAME}" pwpb_rpc 319 PLUGIN 320 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_pwpb.py" 321 OUTPUT_EXTS 322 ".rpc.pwpb.h" 323 INCLUDE_FILE 324 "${arg_INCLUDE_FILE}" 325 OUT_DIR 326 "${arg_OUT_DIR}" 327 SOURCES 328 ${arg_SOURCES} 329 INPUTS 330 ${arg_INPUTS} 331 DEPENDS 332 ${arg_DEPS} 333 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 334 ) 335 336 # Create the library with the generated source files. 337 pw_add_library_generic("${NAME}.pwpb_rpc" INTERFACE 338 PUBLIC_INCLUDES 339 "${arg_OUT_DIR}/pwpb_rpc" 340 PUBLIC_DEPS 341 "${NAME}.pwpb" 342 pw_build 343 pw_rpc.pwpb.client_api 344 pw_rpc.pwpb.server_api 345 pw_rpc.server 346 ${arg_DEPS} 347 ) 348 add_dependencies("${NAME}.pwpb_rpc" "${NAME}._generate.pwpb_rpc") 349endfunction(_pw_pwpb_rpc_library) 350 351# Internal function that creates a raw_rpc proto library. 352function(_pw_raw_rpc_library NAME) 353 pw_parse_arguments( 354 NUM_POSITIONAL_ARGS 355 1 356 ONE_VALUE_ARGS 357 INCLUDE_FILE 358 OUT_DIR 359 MULTI_VALUE_ARGS 360 SOURCES 361 INPUTS 362 DEPS 363 ) 364 365 list(TRANSFORM arg_DEPS APPEND .raw_rpc) 366 367 _pw_generate_protos("${NAME}" raw_rpc 368 PLUGIN 369 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_raw.py" 370 OUTPUT_EXTS 371 ".raw_rpc.pb.h" 372 INCLUDE_FILE 373 "${arg_INCLUDE_FILE}" 374 OUT_DIR 375 "${arg_OUT_DIR}" 376 SOURCES 377 ${arg_SOURCES} 378 INPUTS 379 ${arg_INPUTS} 380 DEPENDS 381 ${arg_DEPS} 382 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 383 ) 384 385 # Create the library with the generated source files. 386 pw_add_library_generic("${NAME}.raw_rpc" INTERFACE 387 PUBLIC_INCLUDES 388 "${arg_OUT_DIR}/raw_rpc" 389 PUBLIC_DEPS 390 pw_build 391 pw_rpc.raw.server_api 392 pw_rpc.raw.client_api 393 pw_rpc.server 394 ${arg_DEPS} 395 ) 396 add_dependencies("${NAME}.raw_rpc" "${NAME}._generate.raw_rpc") 397endfunction(_pw_raw_rpc_library) 398 399# Internal function that creates a nanopb proto library. 400function(_pw_nanopb_library NAME) 401 pw_parse_arguments( 402 NUM_POSITIONAL_ARGS 403 1 404 ONE_VALUE_ARGS 405 INCLUDE_FILE 406 OUT_DIR 407 MULTI_VALUE_ARGS 408 SOURCES 409 INPUTS 410 DEPS 411 ) 412 413 list(TRANSFORM arg_DEPS APPEND .nanopb) 414 415 if("${dir_pw_third_party_nanopb}" STREQUAL "") 416 add_custom_target("${NAME}._generate.nanopb") # Nothing to do 417 pw_add_error_target("${NAME}.nanopb" 418 MESSAGE 419 "Attempting to use pw_proto_library, but dir_pw_third_party_nanopb is " 420 "not set. Set dir_pw_third_party_nanopb to the path to the Nanopb " 421 "repository." 422 ) 423 else() 424 # When compiling with the Nanopb plugin, the nanopb.proto file is already 425 # compiled internally, so skip recompiling it with protoc. 426 if("${arg_SOURCES}" MATCHES "nanopb\\.proto") 427 add_custom_target("${NAME}._generate.nanopb") # Nothing to do 428 pw_add_library_generic("${NAME}.nanopb" INTERFACE 429 PUBLIC_DEPS 430 pw_build 431 pw_third_party.nanopb 432 ${arg_DEPS} 433 ) 434 else() 435 _pw_generate_protos("${NAME}" nanopb 436 PLUGIN 437 "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb" 438 OUTPUT_EXTS 439 ".pb.h" 440 ".pb.c" 441 INCLUDE_FILE 442 "${arg_INCLUDE_FILE}" 443 OUT_DIR 444 "${arg_OUT_DIR}" 445 SOURCES 446 ${arg_SOURCES} 447 INPUTS 448 ${arg_INPUTS} 449 DEPENDS 450 ${arg_DEPS} 451 ) 452 453 # Create the library with the generated source files. 454 pw_add_library_generic("${NAME}.nanopb" STATIC 455 SOURCES 456 ${generated_outputs} 457 PUBLIC_INCLUDES 458 "${arg_OUT_DIR}/nanopb" 459 PUBLIC_DEPS 460 pw_build 461 pw_third_party.nanopb 462 ${arg_DEPS} 463 ) 464 endif() 465 466 add_dependencies("${NAME}.nanopb" "${NAME}._generate.nanopb") 467 468 # Ensure that nanopb_pb2.py is generated to avoid race conditions. 469 add_dependencies("${NAME}._generate.nanopb" 470 pw_third_party.nanopb.generate_proto 471 ) 472 endif() 473endfunction(_pw_nanopb_library) 474 475# Internal function that creates a nanopb_rpc library. 476function(_pw_nanopb_rpc_library NAME) 477 pw_parse_arguments( 478 NUM_POSITIONAL_ARGS 479 1 480 ONE_VALUE_ARGS 481 INCLUDE_FILE 482 OUT_DIR 483 MULTI_VALUE_ARGS 484 SOURCES 485 INPUTS 486 DEPS 487 ) 488 489 # Determine the names of the output files. 490 list(TRANSFORM arg_DEPS APPEND .nanopb_rpc) 491 492 _pw_generate_protos("${NAME}" nanopb_rpc 493 PLUGIN 494 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_nanopb.py" 495 OUTPUT_EXTS 496 ".rpc.pb.h" 497 INCLUDE_FILE 498 "${arg_INCLUDE_FILE}" 499 OUT_DIR 500 "${arg_OUT_DIR}" 501 SOURCES 502 ${arg_SOURCES} 503 INPUTS 504 ${arg_INPUTS} 505 DEPENDS 506 ${arg_DEPS} 507 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin.py" 508 ) 509 510 # Create the library with the generated source files. 511 pw_add_library_generic("${NAME}.nanopb_rpc" INTERFACE 512 PUBLIC_INCLUDES 513 "${arg_OUT_DIR}/nanopb_rpc" 514 PUBLIC_DEPS 515 "${NAME}.nanopb" 516 pw_build 517 pw_rpc.nanopb.client_api 518 pw_rpc.nanopb.server_api 519 pw_rpc.server 520 ${arg_DEPS} 521 ) 522 add_dependencies("${NAME}.nanopb_rpc" "${NAME}._generate.nanopb_rpc") 523endfunction(_pw_nanopb_rpc_library) 524