Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 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 | |
| 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 Pranke | 112a77f | 2019-03-12 01:50:42 +0000 | [diff] [blame] | 7 | # 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 Tambre | b946b23 | 2019-03-26 14:48:46 +0000 | [diff] [blame] | 10 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 11 | import functools |
Edward Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 12 | import logging |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 13 | import os |
Dirk Pranke | 112a77f | 2019-03-12 01:50:42 +0000 | [diff] [blame] | 14 | import sys |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 15 | |
Gavin Mak | 65c49b1 | 2023-08-24 18:06:42 +0000 | [diff] [blame] | 16 | import gclient_utils |
| 17 | import subprocess2 |
| 18 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 19 | # TODO: Should fix these warnings. |
| 20 | # pylint: disable=line-too-long |
| 21 | |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 22 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 23 | @functools.lru_cache |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 24 | def FindGclientRoot(from_dir, filename='.gclient'): |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 25 | """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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 33 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 34 | logging.info('Found gclient root at ' + path) |
Edward Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 35 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 36 | if path == real_from_dir: |
| 37 | return path |
Edward Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 38 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 39 | # 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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 52 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 53 | 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 Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 59 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 60 | 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 Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 66 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 67 | return None |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 68 | |
| 69 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 70 | @functools.lru_cache |
| 71 | def _GetPrimarySolutionPathInternal(cwd): |
| 72 | gclient_root = FindGclientRoot(cwd) |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 73 | 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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 77 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 78 | # Some projects might not use .gclient. Try to see whether we're in a git |
| 79 | # checkout that contains a 'buildtools' subdir. |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 80 | top_dir = cwd |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 81 | 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 Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 88 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 89 | if os.path.exists(os.path.join(top_dir, 'buildtools')): |
| 90 | return top_dir |
| 91 | return None |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 92 | |
| 93 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 94 | def GetPrimarySolutionPath(): |
| 95 | """Returns the full path to the primary solution. (gclient_root + src)""" |
| 96 | return _GetPrimarySolutionPathInternal(os.getcwd()) |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 97 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 98 | |
| 99 | @functools.lru_cache |
| 100 | def _GetBuildtoolsPathInternal(cwd, override): |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 101 | if override is not None: |
| 102 | return override |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 103 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 104 | 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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 118 | return None |
Edward Lemur | 84b5f9a | 2020-01-08 20:06:51 +0000 | [diff] [blame] | 119 | |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 120 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 121 | def 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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 130 | def GetBuildtoolsPlatformBinaryPath(): |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 131 | """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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 135 | |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 136 | 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 Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 145 | |
| 146 | |
| 147 | def GetExeSuffix(): |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 148 | """Returns '' or '.exe' depending on how executables work on this platform.""" |
| 149 | if sys.platform.startswith(('cygwin', 'win')): |
| 150 | return '.exe' |
| 151 | return '' |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 152 | |
| 153 | |
Andrew Grieve | 6e8c182 | 2023-10-03 20:36:59 +0000 | [diff] [blame] | 154 | @functools.lru_cache |
Nico Weber | 09e0b38 | 2019-03-11 16:54:07 +0000 | [diff] [blame] | 155 | def GetGClientPrimarySolutionName(gclient_root_dir_path): |
Mike Frysinger | 124bb8e | 2023-09-06 05:48:55 +0000 | [diff] [blame] | 156 | """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 |