2
0

platformio_generator.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import os
  2. import hashlib
  3. import pathlib
  4. from platformio import fs
  5. Import("env")
  6. try:
  7. import protobuf
  8. except ImportError:
  9. env.Execute(
  10. env.VerboseAction(
  11. # We need to speicify protobuf version. In other case got next (on Ubuntu 20.04):
  12. # Requirement already satisfied: protobuf in /usr/lib/python3/dist-packages (3.6.1)
  13. '$PYTHONEXE -m pip install "protobuf>=3.19.1"',
  14. "Installing Protocol Buffers dependencies",
  15. )
  16. )
  17. try:
  18. import grpc_tools.protoc
  19. except ImportError:
  20. env.Execute(
  21. env.VerboseAction(
  22. '$PYTHONEXE -m pip install "grpcio-tools>=1.43.0"',
  23. "Installing GRPC dependencies",
  24. )
  25. )
  26. nanopb_root = os.path.join(os.getcwd(), '..')
  27. project_dir = env.subst("$PROJECT_DIR")
  28. build_dir = env.subst("$BUILD_DIR")
  29. generated_src_dir = os.path.join(build_dir, 'nanopb', 'generated-src')
  30. generated_build_dir = os.path.join(build_dir, 'nanopb', 'generated-build')
  31. md5_dir = os.path.join(build_dir, 'nanopb', 'md5')
  32. nanopb_protos = env.GetProjectOption("custom_nanopb_protos", "")
  33. nanopb_plugin_options = env.GetProjectOption("custom_nanopb_options", "")
  34. if not nanopb_protos:
  35. print("[nanopb] No generation needed.")
  36. else:
  37. if isinstance(nanopb_plugin_options, (list, tuple)):
  38. nanopb_plugin_options = " ".join(nanopb_plugin_options)
  39. nanopb_plugin_options = nanopb_plugin_options.split()
  40. protos_files = fs.match_src_files(project_dir, nanopb_protos)
  41. if not len(protos_files):
  42. print("[nanopb] ERROR: No files matched pattern:")
  43. print(f"custom_nanopb_protos: {nanopb_protos}")
  44. exit(1)
  45. protoc_generator = os.path.join(nanopb_root, 'generator', 'protoc')
  46. nanopb_options = ""
  47. nanopb_options += f" --nanopb_out={generated_src_dir}"
  48. for opt in nanopb_plugin_options:
  49. nanopb_options += (" --nanopb_opt=" + opt)
  50. try:
  51. os.makedirs(generated_src_dir)
  52. except FileExistsError:
  53. pass
  54. try:
  55. os.makedirs(md5_dir)
  56. except FileExistsError:
  57. pass
  58. # Collect include dirs based on
  59. proto_include_dirs = set()
  60. for proto_file in protos_files:
  61. proto_file_abs = os.path.join(project_dir, proto_file)
  62. proto_dir = os.path.dirname(proto_file_abs)
  63. proto_include_dirs.add(proto_dir)
  64. for proto_include_dir in proto_include_dirs:
  65. nanopb_options += (" --proto_path=" + proto_include_dir)
  66. nanopb_options += (" --nanopb_opt=-I" + proto_include_dir)
  67. for proto_file in protos_files:
  68. proto_file_abs = os.path.join(project_dir, proto_file)
  69. proto_file_path_abs = os.path.dirname(proto_file_abs)
  70. proto_file_basename = os.path.basename(proto_file_abs)
  71. proto_file_without_ext = os.path.splitext(proto_file_basename)[0]
  72. proto_file_md5_abs = os.path.join(md5_dir, proto_file_basename + '.md5')
  73. proto_file_current_md5 = hashlib.md5(pathlib.Path(proto_file_abs).read_bytes()).hexdigest()
  74. options_file = proto_file_without_ext + ".options"
  75. options_file_abs = os.path.join(proto_file_path_abs, options_file)
  76. options_file_md5_abs = None
  77. options_file_current_md5 = None
  78. if pathlib.Path(options_file_abs).exists():
  79. options_file_md5_abs = os.path.join(md5_dir, options_file + '.md5')
  80. options_file_current_md5 = hashlib.md5(pathlib.Path(options_file_abs).read_bytes()).hexdigest()
  81. else:
  82. options_file = None
  83. header_file = proto_file_without_ext + ".pb.h"
  84. source_file = proto_file_without_ext + ".pb.c"
  85. header_file_abs = os.path.join(generated_src_dir, source_file)
  86. source_file_abs = os.path.join(generated_src_dir, header_file)
  87. need_generate = False
  88. # Check proto file md5
  89. try:
  90. last_md5 = pathlib.Path(proto_file_md5_abs).read_text()
  91. if last_md5 != proto_file_current_md5:
  92. need_generate = True
  93. except FileNotFoundError:
  94. need_generate = True
  95. if options_file:
  96. # Check options file md5
  97. try:
  98. last_md5 = pathlib.Path(options_file_md5_abs).read_text()
  99. if last_md5 != options_file_current_md5:
  100. need_generate = True
  101. except FileNotFoundError:
  102. need_generate = True
  103. options_info = f"{options_file}" if options_file else "no options"
  104. if not need_generate:
  105. print(f"[nanopb] Skipping '{proto_file}' ({options_info})")
  106. else:
  107. print(f"[nanopb] Processing '{proto_file}' ({options_info})")
  108. cmd = protoc_generator + " " + nanopb_options + " " + proto_file_basename
  109. result = env.Execute(cmd)
  110. if result != 0:
  111. print(f"[nanopb] ERROR: ({result}) processing cmd: '{cmd}'")
  112. exit(1)
  113. pathlib.Path(proto_file_md5_abs).write_text(proto_file_current_md5)
  114. if options_file:
  115. pathlib.Path(options_file_md5_abs).write_text(options_file_current_md5)
  116. #
  117. # Add generated includes and sources to build environment
  118. #
  119. env.Append(CPPPATH=[generated_src_dir])
  120. env.BuildSources(generated_build_dir, generated_src_dir)