target_determinator.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #!/usr/bin/env python3
  2. """Computes the potentially differing rules from some git commit.
  3. Wraps the "target-determinator" Go program here:
  4. https://github.com/bazel-contrib/target-determinator
  5. The purpose is to compute the potentially impacted set of targets from some
  6. provided Git commit to the current checkout.
  7. This script will ensure a cached version of the latest release is available, and
  8. then forward a limited set of flags to it. This script also filters the
  9. resulting targets using `bazel query` to make it the most relevant list for
  10. continuous integration.
  11. """
  12. __copyright__ = """
  13. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  14. Exceptions. See /LICENSE for license information.
  15. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  16. """
  17. import subprocess
  18. import argparse
  19. import tempfile
  20. import sys
  21. from pathlib import Path
  22. import scripts_utils
  23. def log(s: str) -> None:
  24. print(s, file=sys.stderr)
  25. def filter_targets(bazel: Path, targets: str) -> str:
  26. with tempfile.NamedTemporaryFile(mode="w+") as tmp:
  27. query = (
  28. f"let t = set({targets}) in "
  29. "kind(rule, $t) except attr(tags, manual, $t)\n"
  30. )
  31. query_lines = query.splitlines()
  32. if len(query_lines) <= 10:
  33. query_snippet = "\n".join(query_lines)
  34. else:
  35. query_snippet = "\n".join(
  36. query_lines[:5] + ["..."] + query_lines[-5:]
  37. )
  38. log(f"Bazel query snippet:\n```\n{query_snippet}\n```")
  39. tmp.write(query)
  40. tmp.flush()
  41. try:
  42. p = subprocess.run(
  43. [str(bazel), "query", f"--query_file={tmp.name}"],
  44. stdout=subprocess.PIPE,
  45. stderr=subprocess.PIPE,
  46. check=True,
  47. encoding="utf-8",
  48. )
  49. return p.stdout
  50. except subprocess.CalledProcessError as err:
  51. log(err.stderr)
  52. log("Full query file:\n```")
  53. with open(tmp.name) as f:
  54. log(f.read())
  55. log("```")
  56. raise
  57. def main() -> None:
  58. parser = argparse.ArgumentParser(__doc__)
  59. parser.add_argument(
  60. "baseline", nargs=1, help="Git commit of the diff baseline."
  61. )
  62. parser.add_argument(
  63. "args",
  64. nargs="*",
  65. help="Remaining args to forward to the underlying tool.",
  66. )
  67. parsed_args = parser.parse_args()
  68. scripts_utils.chdir_repo_root()
  69. bazel = Path(scripts_utils.locate_bazel())
  70. target_determinator = scripts_utils.get_release(
  71. scripts_utils.Release.TARGET_DETERMINATOR
  72. )
  73. p = subprocess.run(
  74. [
  75. target_determinator,
  76. f"--bazel={bazel}",
  77. parsed_args.baseline[0],
  78. ]
  79. + parsed_args.args,
  80. check=True,
  81. stdout=subprocess.PIPE,
  82. encoding="utf-8",
  83. )
  84. targets = p.stdout
  85. if targets.strip() != "":
  86. targets = filter_targets(bazel, targets)
  87. log(f"Found {len(targets.splitlines())} impacted targets!")
  88. print(targets.rstrip())
  89. if __name__ == "__main__":
  90. main()