gclient_paths.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. # Copyright 2019 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. # This file is imported by various thin wrappers (around gn, clang-format, ...),
  5. # so it's meant to import very quickly. To keep it that way don't add more
  6. # code, and even more importantly don't add more toplevel import statements,
  7. # particularly for modules that are not builtin (see sys.builtin_modules_names,
  8. # os isn't built in, but it's essential to this file).
  9. import functools
  10. import logging
  11. import os
  12. import sys
  13. import gclient_utils
  14. import subprocess2
  15. # TODO: Should fix these warnings.
  16. # pylint: disable=line-too-long
  17. @functools.lru_cache
  18. def FindGclientRoot(from_dir, filename='.gclient'):
  19. """Tries to find the gclient root."""
  20. real_from_dir = os.path.abspath(from_dir)
  21. path = real_from_dir
  22. while not os.path.exists(os.path.join(path, filename)):
  23. split_path = os.path.split(path)
  24. if not split_path[1]:
  25. return None
  26. path = split_path[0]
  27. logging.info('Found gclient root at ' + path)
  28. if path == real_from_dir:
  29. return path
  30. # If we did not find the file in the current directory, make sure we are in
  31. # a sub directory that is controlled by this configuration.
  32. entries_filename = os.path.join(path, filename + '_entries')
  33. if not os.path.exists(entries_filename):
  34. # If .gclient_entries does not exist, a previous call to gclient sync
  35. # might have failed. In that case, we cannot verify that the .gclient
  36. # is the one we want to use. In order to not to cause too much trouble,
  37. # just issue a warning and return the path anyway.
  38. print(
  39. "%s missing, %s file in parent directory %s might not be the file "
  40. "you want to use." % (entries_filename, filename, path),
  41. file=sys.stderr)
  42. return path
  43. entries_content = gclient_utils.FileRead(entries_filename)
  44. scope = {}
  45. try:
  46. exec(entries_content, scope)
  47. except (SyntaxError, Exception) as e:
  48. gclient_utils.SyntaxErrorToError(filename, e)
  49. all_directories = set(
  50. os.path.relpath(os.path.realpath(os.path.join(path, *k.split('/'))),
  51. start=os.path.realpath(path))
  52. for k in scope['entries'].keys())
  53. path_to_check = os.path.relpath(os.path.realpath(real_from_dir),
  54. start=os.path.realpath(path))
  55. while path_to_check:
  56. if path_to_check in all_directories:
  57. return path
  58. path_to_check = os.path.dirname(path_to_check)
  59. return None
  60. @functools.lru_cache
  61. def _GetPrimarySolutionPathInternal(cwd):
  62. gclient_root = FindGclientRoot(cwd)
  63. if gclient_root:
  64. # Some projects' top directory is not named 'src'.
  65. source_dir_name = GetGClientPrimarySolutionName(gclient_root) or 'src'
  66. return os.path.join(gclient_root, source_dir_name)
  67. # Some projects might not use .gclient. Try to see whether we're in a git
  68. # checkout that contains a 'buildtools' subdir.
  69. top_dir = cwd
  70. try:
  71. top_dir = subprocess2.check_output(
  72. ['git', 'rev-parse', '--show-toplevel'], stderr=subprocess2.DEVNULL)
  73. top_dir = top_dir.decode('utf-8', 'replace')
  74. top_dir = os.path.normpath(top_dir.strip())
  75. except subprocess2.CalledProcessError:
  76. pass
  77. if os.path.exists(os.path.join(top_dir, 'buildtools')):
  78. return top_dir
  79. return None
  80. def GetPrimarySolutionPath():
  81. """Returns the full path to the primary solution. (gclient_root + src)"""
  82. return _GetPrimarySolutionPathInternal(os.getcwd())
  83. @functools.lru_cache
  84. def _GetBuildtoolsPathInternal(cwd, override):
  85. if override is not None:
  86. return override
  87. primary_solution = GetPrimarySolutionPath()
  88. if not primary_solution:
  89. return None
  90. buildtools_path = os.path.join(primary_solution, 'buildtools')
  91. if os.path.exists(buildtools_path):
  92. return buildtools_path
  93. # buildtools may be in the gclient root.
  94. gclient_root = FindGclientRoot(os.getcwd())
  95. buildtools_path = os.path.join(gclient_root, 'buildtools')
  96. if os.path.exists(buildtools_path):
  97. return buildtools_path
  98. return None
  99. def GetBuildtoolsPath():
  100. """Returns the full path to the buildtools directory.
  101. This is based on the root of the checkout containing the current directory."""
  102. # Overriding the build tools path by environment is highly unsupported and
  103. # may break without warning. Do not rely on this for anything important.
  104. override = os.environ.get('CHROMIUM_BUILDTOOLS_PATH')
  105. return _GetBuildtoolsPathInternal(os.getcwd(), override)
  106. def GetBuildtoolsPlatformBinaryPath():
  107. """Returns the full path to the binary directory for the current platform."""
  108. buildtools_path = GetBuildtoolsPath()
  109. if not buildtools_path:
  110. return None
  111. if sys.platform.startswith(('cygwin', 'win')):
  112. subdir = 'win'
  113. elif sys.platform == 'darwin':
  114. subdir = 'mac'
  115. elif sys.platform.startswith('linux'):
  116. subdir = 'linux64'
  117. else:
  118. raise gclient_utils.Error('Unknown platform: ' + sys.platform)
  119. return os.path.join(buildtools_path, subdir)
  120. def GetExeSuffix():
  121. """Returns '' or '.exe' depending on how executables work on this platform."""
  122. if sys.platform.startswith(('cygwin', 'win')):
  123. return '.exe'
  124. return ''
  125. @functools.lru_cache
  126. def _GetGClientSolutions(gclient_root_dir_path):
  127. gclient_config_file = os.path.join(gclient_root_dir_path, '.gclient')
  128. gclient_config_contents = gclient_utils.FileRead(gclient_config_file)
  129. env = {}
  130. exec(gclient_config_contents, env)
  131. return env.get('solutions', [])
  132. def GetGClientPrimarySolutionName(gclient_root_dir_path):
  133. """Returns the name of the primary solution in the .gclient file specified."""
  134. solutions = _GetGClientSolutions(gclient_root_dir_path)
  135. if solutions:
  136. return solutions[0].get('name')
  137. return None
  138. def GetGClientPrimarySolutionURL(gclient_root_dir_path):
  139. """Returns the URL of the primary solution in the .gclient file specified."""
  140. solutions = _GetGClientSolutions(gclient_root_dir_path)
  141. if solutions:
  142. return solutions[0].get('url')
  143. return None