blob: 3e4522a5a2f4b4bfd4eea839801edfe5b0143141 [file] [log] [blame]
Nico Weber09e0b382019-03-11 16:54:07 +00001# 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
5# This file is imported by various thin wrappers (around gn, clang-format, ...),
6# so it's meant to import very quickly. To keep it that way don't add more
Dirk Pranke112a77f2019-03-12 01:50:42 +00007# code, and even more importantly don't add more toplevel import statements,
8# particularly for modules that are not builtin (see sys.builtin_modules_names,
9# os isn't built in, but it's essential to this file).
Raul Tambreb946b232019-03-26 14:48:46 +000010
Andrew Grieve6e8c1822023-10-03 20:36:59 +000011import functools
Edward Lemur84b5f9a2020-01-08 20:06:51 +000012import logging
Nico Weber09e0b382019-03-11 16:54:07 +000013import os
Dirk Pranke112a77f2019-03-12 01:50:42 +000014import sys
Nico Weber09e0b382019-03-11 16:54:07 +000015
Gavin Mak65c49b12023-08-24 18:06:42 +000016import gclient_utils
17import subprocess2
18
Mike Frysinger124bb8e2023-09-06 05:48:55 +000019# TODO: Should fix these warnings.
20# pylint: disable=line-too-long
21
Nico Weber09e0b382019-03-11 16:54:07 +000022
Andrew Grieve6e8c1822023-10-03 20:36:59 +000023@functools.lru_cache
Nico Weber09e0b382019-03-11 16:54:07 +000024def FindGclientRoot(from_dir, filename='.gclient'):
Mike Frysinger124bb8e2023-09-06 05:48:55 +000025 """Tries to find the gclient root."""
26 real_from_dir = os.path.abspath(from_dir)
27 path = real_from_dir
28 while not os.path.exists(os.path.join(path, filename)):
29 split_path = os.path.split(path)
30 if not split_path[1]:
31 return None
32 path = split_path[0]
Nico Weber09e0b382019-03-11 16:54:07 +000033
Mike Frysinger124bb8e2023-09-06 05:48:55 +000034 logging.info('Found gclient root at ' + path)
Edward Lemur84b5f9a2020-01-08 20:06:51 +000035
Mike Frysinger124bb8e2023-09-06 05:48:55 +000036 if path == real_from_dir:
37 return path
Edward Lemur84b5f9a2020-01-08 20:06:51 +000038
Mike Frysinger124bb8e2023-09-06 05:48:55 +000039 # If we did not find the file in the current directory, make sure we are in
40 # a sub directory that is controlled by this configuration.
41 entries_filename = os.path.join(path, filename + '_entries')
42 if not os.path.exists(entries_filename):
43 # If .gclient_entries does not exist, a previous call to gclient sync
44 # might have failed. In that case, we cannot verify that the .gclient
45 # is the one we want to use. In order to not to cause too much trouble,
46 # just issue a warning and return the path anyway.
47 print(
48 "%s missing, %s file in parent directory %s might not be the file "
49 "you want to use." % (entries_filename, filename, path),
50 file=sys.stderr)
51 return path
Nico Weber09e0b382019-03-11 16:54:07 +000052
Mike Frysinger124bb8e2023-09-06 05:48:55 +000053 entries_content = gclient_utils.FileRead(entries_filename)
54 scope = {}
55 try:
56 exec(entries_content, scope)
57 except (SyntaxError, Exception) as e:
58 gclient_utils.SyntaxErrorToError(filename, e)
Edward Lemur84b5f9a2020-01-08 20:06:51 +000059
Mike Frysinger124bb8e2023-09-06 05:48:55 +000060 all_directories = scope['entries'].keys()
61 path_to_check = os.path.relpath(real_from_dir, path)
62 while path_to_check:
63 if path_to_check in all_directories:
64 return path
65 path_to_check = os.path.dirname(path_to_check)
Edward Lemur84b5f9a2020-01-08 20:06:51 +000066
Mike Frysinger124bb8e2023-09-06 05:48:55 +000067 return None
Nico Weber09e0b382019-03-11 16:54:07 +000068
69
Andrew Grieve6e8c1822023-10-03 20:36:59 +000070@functools.lru_cache
71def _GetPrimarySolutionPathInternal(cwd):
72 gclient_root = FindGclientRoot(cwd)
Mike Frysinger124bb8e2023-09-06 05:48:55 +000073 if gclient_root:
74 # Some projects' top directory is not named 'src'.
75 source_dir_name = GetGClientPrimarySolutionName(gclient_root) or 'src'
76 return os.path.join(gclient_root, source_dir_name)
Nico Weber09e0b382019-03-11 16:54:07 +000077
Mike Frysinger124bb8e2023-09-06 05:48:55 +000078 # Some projects might not use .gclient. Try to see whether we're in a git
79 # checkout that contains a 'buildtools' subdir.
Andrew Grieve6e8c1822023-10-03 20:36:59 +000080 top_dir = cwd
Mike Frysinger124bb8e2023-09-06 05:48:55 +000081 try:
82 top_dir = subprocess2.check_output(
83 ['git', 'rev-parse', '--show-toplevel'], stderr=subprocess2.DEVNULL)
84 top_dir = top_dir.decode('utf-8', 'replace')
85 top_dir = os.path.normpath(top_dir.strip())
86 except subprocess2.CalledProcessError:
87 pass
Edward Lemur84b5f9a2020-01-08 20:06:51 +000088
Mike Frysinger124bb8e2023-09-06 05:48:55 +000089 if os.path.exists(os.path.join(top_dir, 'buildtools')):
90 return top_dir
91 return None
Nico Weber09e0b382019-03-11 16:54:07 +000092
93
Andrew Grieve6e8c1822023-10-03 20:36:59 +000094def GetPrimarySolutionPath():
95 """Returns the full path to the primary solution. (gclient_root + src)"""
96 return _GetPrimarySolutionPathInternal(os.getcwd())
Nico Weber09e0b382019-03-11 16:54:07 +000097
Andrew Grieve6e8c1822023-10-03 20:36:59 +000098
99@functools.lru_cache
100def _GetBuildtoolsPathInternal(cwd, override):
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000101 if override is not None:
102 return override
Nico Weber09e0b382019-03-11 16:54:07 +0000103
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000104 primary_solution = GetPrimarySolutionPath()
105 if not primary_solution:
106 return None
107
108 buildtools_path = os.path.join(primary_solution, 'buildtools')
109 if os.path.exists(buildtools_path):
110 return buildtools_path
111
112 # buildtools may be in the gclient root.
113 gclient_root = FindGclientRoot(os.getcwd())
114 buildtools_path = os.path.join(gclient_root, 'buildtools')
115 if os.path.exists(buildtools_path):
116 return buildtools_path
117
Nico Weber09e0b382019-03-11 16:54:07 +0000118 return None
Edward Lemur84b5f9a2020-01-08 20:06:51 +0000119
Nico Weber09e0b382019-03-11 16:54:07 +0000120
Andrew Grieve6e8c1822023-10-03 20:36:59 +0000121def GetBuildtoolsPath():
122 """Returns the full path to the buildtools directory.
123 This is based on the root of the checkout containing the current directory."""
124 # Overriding the build tools path by environment is highly unsupported and
125 # may break without warning. Do not rely on this for anything important.
126 override = os.environ.get('CHROMIUM_BUILDTOOLS_PATH')
127 return _GetBuildtoolsPathInternal(os.getcwd(), override)
128
129
Nico Weber09e0b382019-03-11 16:54:07 +0000130def GetBuildtoolsPlatformBinaryPath():
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000131 """Returns the full path to the binary directory for the current platform."""
132 buildtools_path = GetBuildtoolsPath()
133 if not buildtools_path:
134 return None
Nico Weber09e0b382019-03-11 16:54:07 +0000135
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000136 if sys.platform.startswith(('cygwin', 'win')):
137 subdir = 'win'
138 elif sys.platform == 'darwin':
139 subdir = 'mac'
140 elif sys.platform.startswith('linux'):
141 subdir = 'linux64'
142 else:
143 raise gclient_utils.Error('Unknown platform: ' + sys.platform)
144 return os.path.join(buildtools_path, subdir)
Nico Weber09e0b382019-03-11 16:54:07 +0000145
146
147def GetExeSuffix():
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000148 """Returns '' or '.exe' depending on how executables work on this platform."""
149 if sys.platform.startswith(('cygwin', 'win')):
150 return '.exe'
151 return ''
Nico Weber09e0b382019-03-11 16:54:07 +0000152
153
Andrew Grieve6e8c1822023-10-03 20:36:59 +0000154@functools.lru_cache
Nico Weber09e0b382019-03-11 16:54:07 +0000155def GetGClientPrimarySolutionName(gclient_root_dir_path):
Mike Frysinger124bb8e2023-09-06 05:48:55 +0000156 """Returns the name of the primary solution in the .gclient file specified."""
157 gclient_config_file = os.path.join(gclient_root_dir_path, '.gclient')
158 gclient_config_contents = gclient_utils.FileRead(gclient_config_file)
159 env = {}
160 exec(gclient_config_contents, env)
161 solutions = env.get('solutions', [])
162 if solutions:
163 return solutions[0].get('name')
164 return None