• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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