ninja.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env python3
  2. # Copyright 2022 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """This script is a wrapper around the ninja binary that is pulled to
  6. third_party as part of gclient sync. It will automatically find the ninja
  7. binary when run inside a gclient source tree, so users can just type
  8. "ninja" on the command line."""
  9. import os
  10. import subprocess
  11. import sys
  12. import gclient_paths
  13. import gn_helper
  14. def find_ninja_in_path():
  15. env_path = os.getenv("PATH")
  16. if not env_path:
  17. return
  18. exe = "ninja"
  19. if sys.platform in ("win32", "cygwin"):
  20. exe += ".exe"
  21. for bin_dir in env_path.split(os.pathsep):
  22. bin_dir = bin_dir.rstrip(os.sep)
  23. if os.path.basename(bin_dir) == "depot_tools":
  24. # skip depot_tools to avoid calling ninja.py infinitely.
  25. continue
  26. ninja_path = os.path.join(bin_dir, exe)
  27. if os.path.isfile(ninja_path):
  28. return ninja_path
  29. def check_out_dir(ninja_args):
  30. out_dir = "."
  31. tool = ""
  32. for i, arg in enumerate(ninja_args):
  33. if arg == "-t":
  34. tool = ninja_args[i + 1]
  35. elif arg.startswith("-t"):
  36. tool = arg[2:]
  37. elif arg == "-C":
  38. out_dir = ninja_args[i + 1]
  39. elif arg.startswith("-C"):
  40. out_dir = arg[2:]
  41. if tool in ["list", "commands", "inputs", "targets"]:
  42. # These tools are just inspect ninja rules and not modify out dir.
  43. # TODO: b/339320220 - implement these in siso
  44. return
  45. siso_marker = os.path.join(out_dir, ".siso_deps")
  46. if os.path.exists(siso_marker):
  47. print("depot_tools/ninja.py: %s contains Siso state file.\n"
  48. "Use `autoninja` to choose appropriate build tool,\n"
  49. "or run `gn clean %s` to switch from siso to ninja\n" %
  50. (out_dir, out_dir),
  51. file=sys.stderr)
  52. sys.exit(1)
  53. if os.environ.get("AUTONINJA_BUILD_ID"):
  54. # autoninja sets AUTONINJA_BUILD_ID
  55. return
  56. for k, v in gn_helper.args(out_dir):
  57. if k == "use_remoteexec" and v == "true":
  58. print(
  59. "depot_tools/ninja.py: detect use_remoteexec=true\n"
  60. "Use `autoninja` to choose appropriate build tool\n",
  61. file=sys.stderr)
  62. sys.exit(1)
  63. def fallback(ninja_args):
  64. # Try to find ninja in PATH.
  65. ninja_path = find_ninja_in_path()
  66. if ninja_path:
  67. check_out_dir(ninja_args)
  68. return subprocess.call([ninja_path] + ninja_args)
  69. print(
  70. "depot_tools/ninja.py: Could not find Ninja in the third_party of "
  71. "the current project, nor in your PATH.\n"
  72. "Please take one of the following actions to install Ninja:\n"
  73. "- If your project has DEPS, add a CIPD Ninja dependency to DEPS.\n"
  74. "- Otherwise, add Ninja to your PATH *after* depot_tools.",
  75. file=sys.stderr,
  76. )
  77. return 1
  78. def main(args):
  79. # On Windows the ninja.bat script passes along the arguments enclosed in
  80. # double quotes. This prevents multiple levels of parsing of the special '^'
  81. # characters needed when compiling a single file. When this case is
  82. # detected, we need to split the argument. This means that arguments
  83. # containing actual spaces are not supported by ninja.bat, but that is not a
  84. # real limitation.
  85. if sys.platform.startswith("win") and len(args) == 2:
  86. args = args[:1] + args[1].split()
  87. # macOS's python sets CPATH, LIBRARY_PATH, SDKROOT implicitly.
  88. # https://openradar.appspot.com/radar?id=5608755232243712
  89. #
  90. # Removing those environment variables to avoid affecting clang's behaviors.
  91. if sys.platform == "darwin":
  92. os.environ.pop("CPATH", None)
  93. os.environ.pop("LIBRARY_PATH", None)
  94. os.environ.pop("SDKROOT", None)
  95. # Get gclient root + src.
  96. primary_solution_path = gclient_paths.GetPrimarySolutionPath()
  97. gclient_root_path = gclient_paths.FindGclientRoot(os.getcwd())
  98. gclient_src_root_path = None
  99. if gclient_root_path:
  100. gclient_src_root_path = os.path.join(gclient_root_path, "src")
  101. for base_path in set(
  102. [primary_solution_path, gclient_root_path, gclient_src_root_path]):
  103. if not base_path:
  104. continue
  105. ninja_path = os.path.join(
  106. base_path,
  107. "third_party",
  108. "ninja",
  109. "ninja" + gclient_paths.GetExeSuffix(),
  110. )
  111. if os.path.isfile(ninja_path):
  112. check_out_dir(args[1:])
  113. return subprocess.call([ninja_path] + args[1:])
  114. return fallback(args[1:])
  115. if __name__ == "__main__":
  116. try:
  117. sys.exit(main(sys.argv))
  118. except KeyboardInterrupt:
  119. sys.exit(1)