run_bazel.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. #!/usr/bin/env python3
  2. """Runs bazel on arguments.
  3. This is provided for other scripts to run bazel without requiring it be
  4. manually installed.
  5. """
  6. __copyright__ = """
  7. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  8. Exceptions. See /LICENSE for license information.
  9. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  10. """
  11. import argparse
  12. import subprocess
  13. import time
  14. import scripts_utils
  15. def main() -> None:
  16. parser = argparse.ArgumentParser(description="Runs bazel.")
  17. parser.add_argument(
  18. "--attempts",
  19. metavar="COUNT",
  20. type=int,
  21. default=1,
  22. help="The number of attempts to execute the command, automatically "
  23. "retrying errors that may be transient.",
  24. )
  25. parser.add_argument(
  26. "--jobs-on-last-attempt",
  27. metavar="COUNT",
  28. type=int,
  29. help="Sets the number of jobs in user.bazelrc on the last attempt. If "
  30. "there is only one attempt, this will be set immediately.",
  31. )
  32. script_args, bazel_args = parser.parse_known_args()
  33. bazel = scripts_utils.locate_bazel()
  34. attempt = 0
  35. while True:
  36. attempt += 1
  37. if attempt == script_args.attempts and script_args.jobs_on_last_attempt:
  38. with open("user.bazelrc", "a") as bazelrc:
  39. bazelrc.write(
  40. f"build --jobs={script_args.jobs_on_last_attempt}\n"
  41. )
  42. p = subprocess.run([bazel] + bazel_args)
  43. # If this was the last attempt, we're done.
  44. if attempt == script_args.attempts:
  45. exit(p.returncode)
  46. # Several error codes are reliably permanent, break immediately.
  47. # `0` -- Success.
  48. # `1` -- The build failed.
  49. # `2` -- Command line or environment problem.
  50. # `3` -- Tests failed or timed out, we don't retry at this layer
  51. # on execution timeout.
  52. # `4` -- Test command but no tests found.
  53. # `8` -- Explicitly interrupted build.
  54. #
  55. # Note that `36` is documented as "likely permanent", but we retry
  56. # it as most of our transient failures actually produce that error
  57. # code.
  58. if p.returncode in (0, 1, 2, 3, 4, 8):
  59. exit(p.returncode)
  60. print("Retrying a failure because it may be transient...")
  61. # Also sleep a bit to try to skip over transient machine load.
  62. time.sleep(attempt)
  63. if __name__ == "__main__":
  64. main()