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 16# Declares a protocol buffers library. This function creates a library for each 17# supported protocol buffer implementation: 18# 19# ${NAME}.pwpb - pw_protobuf generated code 20# ${NAME}.nanopb - Nanopb generated code (requires Nanopb) 21# 22# This function also creates libraries for generating pw_rpc code: 23# 24# ${NAME}.nanopb_rpc - generates Nanopb pw_rpc code 25# ${NAME}.raw_rpc - generates raw pw_rpc (no protobuf library) code 26# ${NAME}.pwpb_rpc - (Not implemented) generates pw_protobuf pw_rpc code 27# 28# Args: 29# 30# NAME - the base name of the libraries to create 31# SOURCES - .proto source files 32# DEPS - dependencies on other pw_proto_library targets 33# PREFIX - prefix add to the proto files 34# STRIP_PREFIX - prefix to remove from the proto files 35# INPUTS - files to include along with the .proto files (such as Nanopb 36# .options files) 37# 38function(pw_proto_library NAME) 39 cmake_parse_arguments(PARSE_ARGV 1 arg "" "STRIP_PREFIX;PREFIX" 40 "SOURCES;INPUTS;DEPS") 41 42 if("${arg_SOURCES}" STREQUAL "") 43 message(FATAL_ERROR 44 "pw_proto_library requires at least one .proto file in SOURCES. No " 45 "SOURCES were listed for ${NAME}.") 46 endif() 47 48 set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}") 49 50 # Use INTERFACE libraries to track the proto include paths that are passed to 51 # protoc. 52 set(include_deps "${arg_DEPS}") 53 list(TRANSFORM include_deps APPEND ._includes) 54 55 add_library("${NAME}._includes" INTERFACE) 56 target_include_directories("${NAME}._includes" INTERFACE "${out_dir}/sources") 57 target_link_libraries("${NAME}._includes" INTERFACE ${include_deps}) 58 59 # Generate a file with all include paths needed by protoc. Use the include 60 # directory paths and replace ; with \n. 61 set(include_file "${out_dir}/include_paths.txt") 62 file(GENERATE OUTPUT "${include_file}" 63 CONTENT 64 "$<JOIN:$<TARGET_PROPERTY:${NAME}._includes,INTERFACE_INCLUDE_DIRECTORIES>,\n>") 65 66 if("${arg_STRIP_PREFIX}" STREQUAL "") 67 set(arg_STRIP_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}") 68 else() 69 get_filename_component(arg_STRIP_PREFIX "${arg_STRIP_PREFIX}" ABSOLUTE) 70 endif() 71 72 foreach(path IN LISTS arg_SOURCES arg_INPUTS) 73 get_filename_component(abspath "${path}" ABSOLUTE) 74 list(APPEND files_to_mirror "${abspath}") 75 endforeach() 76 77 # Mirror the sources to the output directory with the specified prefix. 78 _pw_rebase_paths( 79 sources "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_SOURCES}" "") 80 _pw_rebase_paths( 81 inputs "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_INPUTS}" "") 82 83 add_custom_command( 84 COMMAND 85 python3 86 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 87 --source-root "${arg_STRIP_PREFIX}" 88 --directory "${out_dir}/sources/${arg_PREFIX}" 89 ${files_to_mirror} 90 DEPENDS 91 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 92 ${files_to_mirror} 93 OUTPUT 94 ${sources} ${inputs} 95 ) 96 add_custom_target("${NAME}._sources" DEPENDS ${sources} ${inputs}) 97 98 set(sources_deps "${arg_DEPS}") 99 list(TRANSFORM sources_deps APPEND ._sources) 100 101 if(sources_deps) 102 add_dependencies("${NAME}._sources" ${sources_deps}) 103 endif() 104 105 # Create a protobuf target for each supported protobuf library. 106 _pw_pwpb_library( 107 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 108 _pw_raw_rpc_library( 109 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 110 _pw_nanopb_library( 111 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 112 _pw_nanopb_rpc_library( 113 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 114endfunction(pw_proto_library) 115 116function(_pw_rebase_paths VAR OUT_DIR ROOT FILES EXTENSIONS) 117 foreach(file IN LISTS FILES) 118 get_filename_component(file "${file}" ABSOLUTE) 119 file(RELATIVE_PATH file "${ROOT}" "${file}") 120 121 if ("${EXTENSIONS}" STREQUAL "") 122 list(APPEND mirrored_files "${OUT_DIR}/${file}") 123 else() 124 foreach(ext IN LISTS EXTENSIONS) 125 get_filename_component(dir "${file}" DIRECTORY) 126 get_filename_component(name "${file}" NAME_WE) 127 list(APPEND mirrored_files "${OUT_DIR}/${dir}/${name}${ext}") 128 endforeach() 129 endif() 130 endforeach() 131 132 set("${VAR}" "${mirrored_files}" PARENT_SCOPE) 133endfunction(_pw_rebase_paths) 134 135# Internal function that invokes protoc through generate_protos.py. 136function(_pw_generate_protos 137 TARGET LANGUAGE PLUGIN OUTPUT_EXTS INCLUDE_FILE OUT_DIR SOURCES INPUTS DEPS) 138 # Determine the names of the compiled output files. 139 _pw_rebase_paths(outputs 140 "${OUT_DIR}/${LANGUAGE}" "${OUT_DIR}/sources" "${SOURCES}" "${OUTPUT_EXTS}") 141 142 # Export the output files to the caller's scope so it can use them if needed. 143 set(generated_outputs "${outputs}" PARENT_SCOPE) 144 145 if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 146 get_filename_component(dir "${source_file}" DIRECTORY) 147 get_filename_component(name "${source_file}" NAME_WE) 148 set(PLUGIN "${dir}/${name}.bat") 149 endif() 150 151 set(script "$ENV{PW_ROOT}/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py") 152 add_custom_command( 153 COMMAND 154 python3 155 "${script}" 156 --language "${LANGUAGE}" 157 --plugin-path "${PLUGIN}" 158 --include-file "${INCLUDE_FILE}" 159 --compile-dir "${OUT_DIR}/sources" 160 --out-dir "${OUT_DIR}/${LANGUAGE}" 161 --sources ${SOURCES} 162 DEPENDS 163 ${script} 164 ${SOURCES} 165 ${INPUTS} 166 ${DEPS} 167 OUTPUT 168 ${outputs} 169 ) 170 add_custom_target("${TARGET}._generate.${LANGUAGE}" DEPENDS ${outputs}) 171 add_dependencies("${TARGET}._generate.${LANGUAGE}" "${TARGET}._sources") 172endfunction(_pw_generate_protos) 173 174# Internal function that creates a pwpb proto library. 175function(_pw_pwpb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 176 list(TRANSFORM DEPS APPEND .pwpb) 177 178 _pw_generate_protos("${NAME}" 179 pwpb 180 "$ENV{PW_ROOT}/pw_protobuf/py/pw_protobuf/plugin.py" 181 ".pwpb.h" 182 "${INCLUDE_FILE}" 183 "${OUT_DIR}" 184 "${SOURCES}" 185 "${INPUTS}" 186 "${DEPS}" 187 ) 188 189 # Create the library with the generated source files. 190 add_library("${NAME}.pwpb" INTERFACE) 191 target_include_directories("${NAME}.pwpb" INTERFACE "${OUT_DIR}/pwpb") 192 target_link_libraries("${NAME}.pwpb" 193 INTERFACE 194 pw_build 195 pw_polyfill.cstddef 196 pw_polyfill.span 197 pw_protobuf 198 ${DEPS} 199 ) 200 add_dependencies("${NAME}.pwpb" "${NAME}._generate.pwpb") 201endfunction(_pw_pwpb_library) 202 203# Internal function that creates a raw_rpc proto library. 204function(_pw_raw_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 205 list(TRANSFORM DEPS APPEND .raw_rpc) 206 207 _pw_generate_protos("${NAME}" 208 raw_rpc 209 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_raw.py" 210 ".raw_rpc.pb.h" 211 "${INCLUDE_FILE}" 212 "${OUT_DIR}" 213 "${SOURCES}" 214 "${INPUTS}" 215 "${DEPS}" 216 ) 217 218 # Create the library with the generated source files. 219 add_library("${NAME}.raw_rpc" INTERFACE) 220 target_include_directories("${NAME}.raw_rpc" INTERFACE "${OUT_DIR}/raw_rpc") 221 target_link_libraries("${NAME}.raw_rpc" 222 INTERFACE 223 pw_build 224 pw_rpc.raw 225 pw_rpc.server 226 ${DEPS} 227 ) 228 add_dependencies("${NAME}.raw_rpc" "${NAME}._generate.raw_rpc") 229endfunction(_pw_raw_rpc_library) 230 231# Internal function that creates a nanopb proto library. 232function(_pw_nanopb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 233 list(TRANSFORM DEPS APPEND .nanopb) 234 235 if("${dir_pw_third_party_nanopb}" STREQUAL "") 236 add_custom_target("${NAME}._generate.nanopb" 237 "${CMAKE_COMMAND}" -E echo 238 ERROR: Attempting to use pw_proto_library, but 239 dir_pw_third_party_nanopb is not set. Set dir_pw_third_party_nanopb 240 to the path to the Nanopb repository. 241 COMMAND 242 "${CMAKE_COMMAND}" -E false 243 DEPENDS 244 ${DEPS} 245 SOURCES 246 ${SOURCES} 247 ) 248 set(generated_outputs $<TARGET_PROPERTY:pw_build.empty,SOURCES>) 249 else() 250 # When compiling with the Nanopb plugin, the nanopb.proto file is already 251 # compiled internally, so skip recompiling it with protoc. 252 if("${SOURCES}" MATCHES "nanopb\\.proto") 253 add_custom_target("${NAME}._generate.nanopb") # Nothing to do 254 add_library("${NAME}.nanopb" INTERFACE) 255 target_link_libraries("${NAME}.nanopb" 256 INTERFACE 257 pw_build 258 pw_third_party.nanopb 259 ${DEPS} 260 ) 261 else() 262 _pw_generate_protos("${NAME}" 263 nanopb 264 "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb" 265 ".pb.h;.pb.c" 266 "${INCLUDE_FILE}" 267 "${OUT_DIR}" 268 "${SOURCES}" 269 "${INPUTS}" 270 "${DEPS}" 271 ) 272 273 # Create the library with the generated source files. 274 add_library("${NAME}.nanopb" EXCLUDE_FROM_ALL ${generated_outputs}) 275 target_include_directories("${NAME}.nanopb" PUBLIC "${OUT_DIR}/nanopb") 276 target_link_libraries("${NAME}.nanopb" PUBLIC pw_build pw_third_party.nanopb ${DEPS}) 277 endif() 278 279 add_dependencies("${NAME}.nanopb" "${NAME}._generate.nanopb") 280 281 # Ensure that nanopb_pb2.py is generated to avoid race conditions. 282 add_dependencies("${NAME}._generate.nanopb" 283 pw_third_party.nanopb.generate_proto 284 ) 285 endif() 286endfunction(_pw_nanopb_library) 287 288# Internal function that creates a nanopb_rpc library. 289function(_pw_nanopb_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 290 # Determine the names of the output files. 291 list(TRANSFORM DEPS APPEND .nanopb_rpc) 292 293 _pw_generate_protos("${NAME}" 294 nanopb_rpc 295 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_nanopb.py" 296 ".rpc.pb.h" 297 "${INCLUDE_FILE}" 298 "${OUT_DIR}" 299 "${SOURCES}" 300 "${INPUTS}" 301 "${DEPS}" 302 ) 303 304 # Create the library with the generated source files. 305 add_library("${NAME}.nanopb_rpc" INTERFACE) 306 target_include_directories("${NAME}.nanopb_rpc" 307 INTERFACE 308 "${OUT_DIR}/nanopb_rpc" 309 ) 310 target_link_libraries("${NAME}.nanopb_rpc" 311 INTERFACE 312 "${NAME}.nanopb" 313 pw_build 314 pw_rpc.nanopb.client 315 pw_rpc.nanopb.method_union 316 pw_rpc.server 317 ${DEPS} 318 ) 319 add_dependencies("${NAME}.nanopb_rpc" "${NAME}._generate.nanopb_rpc") 320endfunction(_pw_nanopb_rpc_library) 321