target_determinator.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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. # Need to quote targets for inclusion in another query.
  27. quoted_targets = "\n".join([f'"{t}"' for t in targets.splitlines()])
  28. with tempfile.NamedTemporaryFile(mode="w+") as tmp:
  29. query = (
  30. f"let t = set({quoted_targets}) in "
  31. "kind(rule, $t) except attr(tags, manual, $t)\n"
  32. )
  33. query_lines = query.splitlines()
  34. if len(query_lines) <= 10:
  35. query_snippet = "\n".join(query_lines)
  36. else:
  37. query_snippet = "\n".join(
  38. query_lines[:5] + ["..."] + query_lines[-5:]
  39. )
  40. log(f"Bazel query snippet:\n```\n{query_snippet}\n```")
  41. tmp.write(query)
  42. tmp.flush()
  43. try:
  44. p = subprocess.run(
  45. [str(bazel), "query", f"--query_file={tmp.name}"],
  46. stdout=subprocess.PIPE,
  47. stderr=subprocess.PIPE,
  48. check=True,
  49. encoding="utf-8",
  50. )
  51. return p.stdout
  52. except subprocess.CalledProcessError as err:
  53. log(err.stderr)
  54. log("Full query file:\n```")
  55. with open(tmp.name) as f:
  56. log(f.read())
  57. log("```")
  58. raise
  59. def main() -> None:
  60. parser = argparse.ArgumentParser(__doc__)
  61. parser.add_argument(
  62. "baseline", nargs=1, help="Git commit of the diff baseline."
  63. )
  64. parser.add_argument(
  65. "args",
  66. nargs="*",
  67. help="Remaining args to forward to the underlying tool.",
  68. )
  69. parsed_args = parser.parse_args()
  70. scripts_utils.chdir_repo_root()
  71. bazel = Path(scripts_utils.locate_bazel())
  72. target_determinator = scripts_utils.get_release(
  73. scripts_utils.Release.TARGET_DETERMINATOR
  74. )
  75. p = subprocess.run(
  76. [
  77. target_determinator,
  78. f"--bazel={bazel}",
  79. parsed_args.baseline[0],
  80. ]
  81. + parsed_args.args,
  82. check=True,
  83. stdout=subprocess.PIPE,
  84. encoding="utf-8",
  85. )
  86. targets = p.stdout
  87. if targets.strip() != "":
  88. targets = filter_targets(bazel, targets)
  89. log(f"Found {len(targets.splitlines())} impacted targets!")
  90. print(targets.rstrip())
  91. if __name__ == "__main__":
  92. main()