build_apps.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. #
  4. # ESP-IDF helper script to build multiple applications. Consumes the input of find_apps.py.
  5. #
  6. import argparse
  7. import sys
  8. import logging
  9. from find_build_apps import BuildItem, BuildError, setup_logging, BUILD_SYSTEMS
  10. def main():
  11. parser = argparse.ArgumentParser(description="ESP-IDF app builder")
  12. parser.add_argument(
  13. "-v",
  14. "--verbose",
  15. action="count",
  16. help="Increase the logging level of the script. Can be specified multiple times.",
  17. )
  18. parser.add_argument(
  19. "--build-verbose",
  20. action="store_true",
  21. help="Enable verbose output from build system.",
  22. )
  23. parser.add_argument(
  24. "--log-file",
  25. type=argparse.FileType("w"),
  26. help="Write the script log to the specified file, instead of stderr",
  27. )
  28. parser.add_argument(
  29. "--parallel-count",
  30. default=1,
  31. type=int,
  32. help="Number of parallel build jobs. Note that this script doesn't start the jobs, " +
  33. "it needs to be executed multiple times with same value of --parallel-count and " +
  34. "different values of --parallel-index.",
  35. )
  36. parser.add_argument(
  37. "--parallel-index",
  38. default=1,
  39. type=int,
  40. help="Index (1-based) of the job, out of the number specified by --parallel-count.",
  41. )
  42. parser.add_argument(
  43. "--format",
  44. default="json",
  45. choices=["json"],
  46. help="Format to read the list of builds",
  47. )
  48. parser.add_argument(
  49. "--dry-run",
  50. action="store_true",
  51. help="Don't actually build, only print the build commands",
  52. )
  53. parser.add_argument(
  54. "--keep-going",
  55. action="store_true",
  56. help="Don't exit immediately when a build fails.",
  57. )
  58. parser.add_argument(
  59. "--output-build-list",
  60. type=argparse.FileType("w"),
  61. help="If specified, the list of builds (with all the placeholders expanded) will be written to this file.",
  62. )
  63. parser.add_argument(
  64. "build_list",
  65. type=argparse.FileType("r"),
  66. nargs="?",
  67. default=sys.stdin,
  68. help="Name of the file to read the list of builds from. If not specified, read from stdin.",
  69. )
  70. args = parser.parse_args()
  71. setup_logging(args)
  72. build_items = [BuildItem.from_json(line) for line in args.build_list]
  73. if not build_items:
  74. logging.error("Empty build list!")
  75. raise SystemExit(1)
  76. num_builds = len(build_items)
  77. num_jobs = args.parallel_count
  78. job_index = args.parallel_index - 1 # convert to 0-based index
  79. num_builds_per_job = (num_builds + num_jobs - 1) // num_jobs
  80. min_job_index = num_builds_per_job * job_index
  81. if min_job_index >= num_builds:
  82. logging.warn("Nothing to do for job {} (build total: {}, per job: {})".format(
  83. job_index + 1, num_builds, num_builds_per_job))
  84. raise SystemExit(0)
  85. max_job_index = min(num_builds_per_job * (job_index + 1) - 1, num_builds - 1)
  86. logging.info("Total {} builds, max. {} builds per job, running builds {}-{}".format(
  87. num_builds, num_builds_per_job, min_job_index + 1, max_job_index + 1))
  88. builds_for_current_job = build_items[min_job_index:max_job_index + 1]
  89. for i, build_info in enumerate(builds_for_current_job):
  90. index = i + min_job_index + 1
  91. build_info.index = index
  92. build_info.dry_run = args.dry_run
  93. build_info.verbose = args.build_verbose
  94. build_info.keep_going = args.keep_going
  95. logging.debug(" Build {}: {}".format(index, repr(build_info)))
  96. if args.output_build_list:
  97. args.output_build_list.write(build_info.to_json_expanded() + "\n")
  98. failed_builds = []
  99. for build_info in builds_for_current_job:
  100. logging.info("Running build {}: {}".format(build_info.index, repr(build_info)))
  101. build_system_class = BUILD_SYSTEMS[build_info.build_system]
  102. try:
  103. build_system_class.build(build_info)
  104. except BuildError as e:
  105. logging.error(str(e))
  106. if args.keep_going:
  107. failed_builds.append(build_info)
  108. else:
  109. raise SystemExit(1)
  110. if failed_builds:
  111. logging.error("The following build have failed:")
  112. for build in failed_builds:
  113. logging.error(" {}".format(build))
  114. raise SystemExit(1)
  115. if __name__ == "__main__":
  116. main()