• 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
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