Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
6.0 KiB

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. import argparse
  18. import re
  19. import shlex
  20. import configparser
  21. import collections
  22. Build = collections.namedtuple("Build", "env extends build_flags build_src_flags")
  23. def expand_variables(cfg, value):
  24. RE_VARS = re.compile("\$\{.*?\}")
  25. for var in RE_VARS.findall(value):
  26. section, option = var.replace("${", "").replace("}", "").split(".", 1)
  27. value = value.replace(var, expand_variables(cfg, cfg.get(section, option)))
  28. return value
  29. def get_builds(cfg):
  30. RE_NEWLINE = re.compile("\r\n|\n")
  31. BASE_BUILD_FLAGS = set(
  32. shlex.split(expand_variables(cfg, cfg.get("env", "build_flags")))
  33. )
  34. for section in cfg.sections():
  35. if (not section.startswith("env:")) or (
  36. section.startswith("env:esp8266-") and section.endswith("-base")
  37. ):
  38. continue
  39. build_flags = None
  40. build_src_flags = None
  41. try:
  42. build_flags = cfg.get(section, "build_flags")
  43. build_flags = RE_NEWLINE.sub(" ", build_flags).strip()
  44. build_flags = " ".join(
  45. BASE_BUILD_FLAGS ^ set(shlex.split(expand_variables(cfg, build_flags)))
  46. )
  47. except configparser.NoOptionError:
  48. pass
  49. try:
  50. build_src_flags = cfg.get(section, "build_src_flags")
  51. build_src_flags = RE_NEWLINE.sub(" ", build_src_flags).strip()
  52. build_src_flags = expand_variables(cfg, build_src_flags)
  53. except configparser.NoOptionError:
  54. pass
  55. yield Build(
  56. section.replace("env:", ""),
  57. cfg.get(section, "extends").replace("env:", ""),
  58. build_flags,
  59. build_src_flags,
  60. )
  61. def find_any(string, values):
  62. for value in values:
  63. if value in string:
  64. return True
  65. return False
  66. def generate_lines(builds, ignore):
  67. minimal = []
  68. generic = []
  69. for build in builds:
  70. if find_any(build.env, ignore):
  71. continue
  72. flags = []
  73. if build.build_flags:
  74. flags.append('PLATFORMIO_BUILD_FLAGS="{}"'.format(build.build_flags))
  75. if build.build_src_flags:
  76. flags.append('ESPURNA_FLAGS="{}"'.format(build.build_src_flags))
  77. flags.append('ESPURNA_BUILD_NAME="{env}"'.format(env=build.env))
  78. cmd = ["env"]
  79. cmd.extend(flags)
  80. cmd.extend(["pio", "run", "-e", build.extends, "-s", "-t", "build-and-copy"])
  81. line = " ".join(cmd)
  82. # push minimal variants to the front as they definetly include global build_flags
  83. output = generic
  84. if "ESPURNA_MINIMAL" in build.build_src_flags:
  85. output = minimal
  86. output.append(line)
  87. return minimal + generic
  88. def every(seq, nth, total):
  89. index = 0
  90. for value in seq:
  91. if index == nth:
  92. yield value
  93. index = (index + 1) % total
  94. def parse_args():
  95. parser = argparse.ArgumentParser()
  96. parser.add_argument(
  97. "--destination", help="Where to place the resulting .bin", required=True
  98. )
  99. parser.add_argument(
  100. "--single-source",
  101. help="Combine .cpp files into one to speed up compilation",
  102. action="store_true",
  103. default=True,
  104. )
  105. parser.add_argument(
  106. "--ignore", help="Do not build envs that contain the string(s)", action="append"
  107. )
  108. builder_thread = parser.add_argument_group(
  109. title="Builder thread control for CI parallel builds"
  110. )
  111. builder_thread.add_argument("--builder-thread", type=int, required=True)
  112. builder_thread.add_argument("--builder-total-threads", type=int, required=True)
  113. full_version = parser.add_argument_group(
  114. title="Fully replace the version string for the build system"
  115. )
  116. full_version.add_argument("--full-version")
  117. version_parts = parser.add_argument_group(
  118. "Replace parts of the version string that would have been detected by the build system"
  119. )
  120. version_parts.add_argument("--version")
  121. version_parts.add_argument("--revision")
  122. version_parts.add_argument("--suffix")
  123. return parser.parse_args()
  124. if __name__ == "__main__":
  125. args = parse_args()
  126. Config = configparser.ConfigParser()
  127. with open("platformio.ini", "r") as f:
  128. Config.read_file(f)
  129. builder_total_threads = args.builder_total_threads
  130. builder_thread = args.builder_thread
  131. if builder_thread >= builder_total_threads:
  132. raise ValueError("* Builder thread index out of range *")
  133. builds = every(get_builds(Config), builder_thread, builder_total_threads)
  134. print("#!/bin/bash")
  135. print("set -e -x")
  136. variables = [
  137. ["ESPURNA_BUILD_DESTINATION", args.destination],
  138. ["ESPURNA_BUILD_SINGLE_SOURCE", int(args.single_source)],
  139. ["ESPURNA_BUILD_FULL_VERSION", args.full_version],
  140. ["ESPURNA_BUILD_VERSION", args.version],
  141. ["ESPURNA_BUILD_REVISION", args.revision],
  142. ["ESPURNA_BUILD_VERSION_SUFFIX", args.suffix],
  143. ]
  144. for var, value in variables:
  145. if value or not value is None:
  146. print('export {}="{}"'.format(var, value))
  147. print('trap "ls -R ${ESPURNA_BUILD_DESTINATION}" EXIT')
  148. print(
  149. 'echo "Selected thread #{} out of {}"'.format(
  150. builder_thread + 1, builder_total_threads
  151. )
  152. )
  153. for line in generate_lines(builds, args.ignore or ()):
  154. print(line)