build_apps.py 5.0 KB

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