target_determinator.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  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. try:
  41. p = subprocess.run(
  42. [str(bazel), "query", f"--query_file={tmp.name}"],
  43. stdout=subprocess.PIPE,
  44. stderr=subprocess.PIPE,
  45. check=True,
  46. encoding="utf-8",
  47. )
  48. return p.stdout
  49. except subprocess.CalledProcessError as err:
  50. log(err.stderr)
  51. raise
  52. def main() -> None:
  53. parser = argparse.ArgumentParser(__doc__)
  54. parser.add_argument(
  55. "baseline", nargs=1, help="Git commit of the diff baseline."
  56. )
  57. parser.add_argument(
  58. "args",
  59. nargs="*",
  60. help="Remaining args to forward to the underlying tool.",
  61. )
  62. parsed_args = parser.parse_args()
  63. scripts_utils.chdir_repo_root()
  64. bazel = Path(scripts_utils.locate_bazel())
  65. target_determinator = scripts_utils.get_target_determinator()
  66. p = subprocess.run(
  67. [
  68. target_determinator,
  69. f"--bazel={bazel}",
  70. parsed_args.baseline[0],
  71. ]
  72. + parsed_args.args,
  73. check=True,
  74. stdout=subprocess.PIPE,
  75. encoding="utf-8",
  76. )
  77. targets = p.stdout
  78. if targets.strip() != "":
  79. targets = filter_targets(bazel, targets)
  80. log(f"Found {len(targets.splitlines())} impacted targets!")
  81. print(targets.rstrip())
  82. if __name__ == "__main__":
  83. main()