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 endif() 69 70 foreach(path IN LISTS arg_SOURCES arg_INPUTS) 71 get_filename_component(abspath "${path}" ABSOLUTE) 72 list(APPEND files_to_mirror "${abspath}") 73 endforeach() 74 75 # Mirror the sources to the output directory with the specified prefix. 76 _pw_rebase_paths( 77 sources "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_SOURCES}" "") 78 _pw_rebase_paths( 79 inputs "${out_dir}/sources/${arg_PREFIX}" "${arg_STRIP_PREFIX}" "${arg_INPUTS}" "") 80 81 add_custom_command( 82 COMMAND 83 python3 84 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 85 --source-root "${arg_STRIP_PREFIX}" 86 --directory "${out_dir}/sources/${arg_PREFIX}" 87 ${files_to_mirror} 88 DEPENDS 89 "$ENV{PW_ROOT}/pw_build/py/pw_build/mirror_tree.py" 90 ${files_to_mirror} 91 OUTPUT 92 ${sources} ${inputs} 93 ) 94 add_custom_target("${NAME}._sources" DEPENDS ${sources} ${inputs}) 95 96 set(sources_deps "${arg_DEPS}") 97 list(TRANSFORM sources_deps APPEND ._sources) 98 99 if(sources_deps) 100 add_dependencies("${NAME}._sources" ${sources_deps}) 101 endif() 102 103 # Create a protobuf target for each supported protobuf library. 104 _pw_pwpb_library( 105 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 106 _pw_raw_rpc_library( 107 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 108 _pw_nanopb_library( 109 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 110 _pw_nanopb_rpc_library( 111 "${NAME}" "${sources}" "${inputs}" "${arg_DEPS}" "${include_file}" "${out_dir}") 112endfunction(pw_proto_library) 113 114function(_pw_rebase_paths VAR OUT_DIR ROOT FILES EXTENSIONS) 115 foreach(file IN LISTS FILES) 116 get_filename_component(file "${file}" ABSOLUTE) 117 file(RELATIVE_PATH file "${ROOT}" "${file}") 118 119 if ("${EXTENSIONS}" STREQUAL "") 120 list(APPEND mirrored_files "${OUT_DIR}/${file}") 121 else() 122 foreach(ext IN LISTS EXTENSIONS) 123 get_filename_component(dir "${file}" DIRECTORY) 124 get_filename_component(name "${file}" NAME_WE) 125 list(APPEND mirrored_files "${OUT_DIR}/${dir}/${name}${ext}") 126 endforeach() 127 endif() 128 endforeach() 129 130 set("${VAR}" "${mirrored_files}" PARENT_SCOPE) 131endfunction(_pw_rebase_paths) 132 133# Internal function that invokes protoc through generate_protos.py. 134function(_pw_generate_protos 135 TARGET LANGUAGE PLUGIN OUTPUT_EXTS INCLUDE_FILE OUT_DIR SOURCES INPUTS DEPS) 136 # Determine the names of the compiled output files. 137 _pw_rebase_paths(outputs 138 "${OUT_DIR}/${LANGUAGE}" "${OUT_DIR}/sources" "${SOURCES}" "${OUTPUT_EXTS}") 139 140 # Export the output files to the caller's scope so it can use them if needed. 141 set(generated_outputs "${outputs}" PARENT_SCOPE) 142 143 if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 144 get_filename_component(dir "${source_file}" DIRECTORY) 145 get_filename_component(name "${source_file}" NAME_WE) 146 set(PLUGIN "${dir}/${name}.bat") 147 endif() 148 149 set(script "$ENV{PW_ROOT}/pw_protobuf_compiler/py/pw_protobuf_compiler/generate_protos.py") 150 add_custom_command( 151 COMMAND 152 python3 153 "${script}" 154 --language "${LANGUAGE}" 155 --plugin-path "${PLUGIN}" 156 --include-file "${INCLUDE_FILE}" 157 --compile-dir "${OUT_DIR}/sources" 158 --out-dir "${OUT_DIR}/${LANGUAGE}" 159 --sources ${SOURCES} 160 DEPENDS 161 ${script} 162 ${SOURCES} 163 ${INPUTS} 164 ${DEPS} 165 OUTPUT 166 ${outputs} 167 ) 168 add_custom_target("${TARGET}" DEPENDS ${outputs}) 169endfunction(_pw_generate_protos) 170 171# Internal function that creates a pwpb proto library. 172function(_pw_pwpb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 173 list(TRANSFORM DEPS APPEND .pwpb) 174 175 _pw_generate_protos("${NAME}._generate.pwpb" 176 pwpb 177 "$ENV{PW_ROOT}/pw_protobuf/py/pw_protobuf/plugin.py" 178 ".pwpb.h" 179 "${INCLUDE_FILE}" 180 "${OUT_DIR}" 181 "${SOURCES}" 182 "${INPUTS}" 183 "${DEPS}" 184 ) 185 186 # Create the library with the generated source files. 187 add_library("${NAME}.pwpb" INTERFACE) 188 target_include_directories("${NAME}.pwpb" INTERFACE "${OUT_DIR}/pwpb") 189 target_link_libraries("${NAME}.pwpb" INTERFACE pw_protobuf ${DEPS}) 190 add_dependencies("${NAME}.pwpb" "${NAME}._generate.pwpb") 191endfunction(_pw_pwpb_library) 192 193# Internal function that creates a raw_rpc proto library. 194function(_pw_raw_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 195 list(TRANSFORM DEPS APPEND .raw_rpc) 196 197 _pw_generate_protos("${NAME}._generate.raw_rpc" 198 raw_rpc 199 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_raw.py" 200 ".raw_rpc.pb.h" 201 "${INCLUDE_FILE}" 202 "${OUT_DIR}" 203 "${SOURCES}" 204 "${INPUTS}" 205 "${DEPS}" 206 ) 207 208 # Create the library with the generated source files. 209 add_library("${NAME}.raw_rpc" INTERFACE) 210 target_include_directories("${NAME}.raw_rpc" INTERFACE "${OUT_DIR}/raw_rpc") 211 target_link_libraries("${NAME}.raw_rpc" 212 INTERFACE 213 pw_rpc.raw 214 pw_rpc.server 215 ${DEPS} 216 ) 217 add_dependencies("${NAME}.raw_rpc" "${NAME}._generate.raw_rpc") 218endfunction(_pw_raw_rpc_library) 219 220# Internal function that creates a nanopb proto library. 221function(_pw_nanopb_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 222 list(TRANSFORM DEPS APPEND .nanopb) 223 224 if("${dir_pw_third_party_nanopb}" STREQUAL "") 225 add_custom_target("${NAME}._generate.nanopb" 226 cmake -E echo 227 ERROR: Attempting to use pw_proto_library, but 228 dir_pw_third_party_nanopb is not set. Set dir_pw_third_party_nanopb 229 to the path to the Nanopb repository. 230 COMMAND 231 cmake -E false 232 DEPENDS 233 ${DEPS} 234 SOURCES 235 ${SOURCES} 236 ) 237 set(generated_outputs $<TARGET_PROPERTY:pw_build.empty,SOURCES>) 238 else() 239 _pw_generate_protos("${NAME}._generate.nanopb" 240 nanopb 241 "${dir_pw_third_party_nanopb}/generator/protoc-gen-nanopb" 242 ".pb.h;.pb.c" 243 "${INCLUDE_FILE}" 244 "${OUT_DIR}" 245 "${SOURCES}" 246 "${INPUTS}" 247 "${DEPS}" 248 ) 249 endif() 250 251 # Create the library with the generated source files. 252 add_library("${NAME}.nanopb" EXCLUDE_FROM_ALL ${generated_outputs}) 253 target_include_directories("${NAME}.nanopb" PUBLIC "${OUT_DIR}/nanopb") 254 target_link_libraries("${NAME}.nanopb" PUBLIC pw_third_party.nanopb ${DEPS}) 255 add_dependencies("${NAME}.nanopb" "${NAME}._generate.nanopb") 256endfunction(_pw_nanopb_library) 257 258# Internal function that creates a nanopb_rpc library. 259function(_pw_nanopb_rpc_library NAME SOURCES INPUTS DEPS INCLUDE_FILE OUT_DIR) 260 # Determine the names of the output files. 261 list(TRANSFORM DEPS APPEND .nanopb_rpc) 262 263 _pw_generate_protos("${NAME}._generate.nanopb_rpc" 264 nanopb_rpc 265 "$ENV{PW_ROOT}/pw_rpc/py/pw_rpc/plugin_nanopb.py" 266 ".rpc.pb.h" 267 "${INCLUDE_FILE}" 268 "${OUT_DIR}" 269 "${SOURCES}" 270 "${INPUTS}" 271 "${DEPS}" 272 ) 273 274 # Create the library with the generated source files. 275 add_library("${NAME}.nanopb_rpc" INTERFACE) 276 target_include_directories("${NAME}.nanopb_rpc" 277 INTERFACE 278 "${OUT_DIR}/nanopb_rpc" 279 ) 280 target_link_libraries("${NAME}.nanopb_rpc" 281 INTERFACE 282 "${NAME}.nanopb" 283 pw_rpc.nanopb.method_union 284 pw_rpc.server 285 ${DEPS} 286 ) 287 add_dependencies("${NAME}.nanopb_rpc" "${NAME}._generate.nanopb_rpc") 288endfunction(_pw_nanopb_rpc_library) 289