Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 1 | # Copyright 2023 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 helper provides a build context that handles |
| 5 | the reclient lifecycle safely. It will automatically start |
| 6 | reproxy before running ninja and stop reproxy when build stops |
| 7 | for any reason e.g. build completion, keyboard interrupt etc.""" |
| 8 | |
| 9 | import contextlib |
| 10 | import hashlib |
| 11 | import os |
| 12 | import subprocess |
| 13 | import sys |
| 14 | |
| 15 | import gclient_paths |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 16 | import reclient_metrics |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 17 | |
| 18 | |
| 19 | def find_reclient_bin_dir(): |
| 20 | tools_path = gclient_paths.GetBuildtoolsPath() |
| 21 | if not tools_path: |
| 22 | return None |
| 23 | |
| 24 | reclient_bin_dir = os.path.join(tools_path, 'reclient') |
| 25 | if os.path.isdir(reclient_bin_dir): |
| 26 | return reclient_bin_dir |
| 27 | return None |
| 28 | |
| 29 | |
| 30 | def find_reclient_cfg(): |
| 31 | tools_path = gclient_paths.GetBuildtoolsPath() |
| 32 | if not tools_path: |
| 33 | return None |
| 34 | |
| 35 | reclient_cfg = os.path.join(tools_path, 'reclient_cfgs', 'reproxy.cfg') |
| 36 | if os.path.isfile(reclient_cfg): |
| 37 | return reclient_cfg |
| 38 | return None |
| 39 | |
| 40 | |
| 41 | def run(cmd_args): |
| 42 | if os.environ.get('NINJA_SUMMARIZE_BUILD') == '1': |
| 43 | print(' '.join(cmd_args)) |
| 44 | return subprocess.call(cmd_args) |
| 45 | |
| 46 | |
| 47 | def start_reproxy(reclient_cfg, reclient_bin_dir): |
| 48 | return run([ |
Ben Segall | cdefe67 | 2023-05-17 13:44:04 +0000 | [diff] [blame] | 49 | os.path.join(reclient_bin_dir, |
| 50 | 'bootstrap' + gclient_paths.GetExeSuffix()), '--re_proxy=' + |
| 51 | os.path.join(reclient_bin_dir, 'reproxy' + gclient_paths.GetExeSuffix()), |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 52 | '--cfg=' + reclient_cfg |
| 53 | ]) |
| 54 | |
| 55 | |
| 56 | def stop_reproxy(reclient_cfg, reclient_bin_dir): |
| 57 | return run([ |
Ben Segall | cdefe67 | 2023-05-17 13:44:04 +0000 | [diff] [blame] | 58 | os.path.join(reclient_bin_dir, |
| 59 | 'bootstrap' + gclient_paths.GetExeSuffix()), '--shutdown', |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 60 | '--cfg=' + reclient_cfg |
| 61 | ]) |
| 62 | |
| 63 | |
| 64 | def find_ninja_out_dir(args): |
| 65 | # Ninja uses getopt_long, which allows to intermix non-option arguments. |
| 66 | # To leave non supported parameters untouched, we do not use getopt. |
| 67 | for index, arg in enumerate(args[1:]): |
| 68 | if arg == '-C': |
| 69 | # + 1 to get the next argument and +1 because we trimmed off args[0] |
| 70 | return args[index + 2] |
| 71 | if arg.startswith('-C'): |
| 72 | # Support -Cout/Default |
| 73 | return arg[2:] |
| 74 | return '.' |
| 75 | |
| 76 | |
Ben Segall | 7a0fe8b | 2023-04-24 20:36:55 +0000 | [diff] [blame] | 77 | def find_cache_dir(tmp_dir): |
| 78 | """Helper to find the correct cache directory for a build. |
| 79 | |
| 80 | tmp_dir should be a build specific temp directory within the out directory. |
| 81 | |
| 82 | If this is called from within a gclient checkout, the cache dir will be: |
| 83 | <gclient_root>/.reproxy_cache/md5(tmp_dir)/ |
| 84 | If this is not called from within a gclient checkout, the cache dir will be: |
| 85 | tmp_dir/cache |
| 86 | """ |
| 87 | gclient_root = gclient_paths.FindGclientRoot(os.getcwd()) |
| 88 | if gclient_root: |
| 89 | return os.path.join(gclient_root, '.reproxy_cache', |
| 90 | hashlib.md5(tmp_dir.encode()).hexdigest()) |
| 91 | return os.path.join(tmp_dir, 'cache') |
| 92 | |
| 93 | |
Ben Segall | e49349b | 2023-06-01 02:54:56 +0000 | [diff] [blame] | 94 | def set_reproxy_metrics_flags(tool): |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 95 | """Helper to setup metrics collection flags for reproxy. |
| 96 | |
| 97 | The following env vars are set if not already set: |
| 98 | RBE_metrics_project=chromium-reclient-metrics |
| 99 | RBE_invocation_id=$AUTONINJA_BUILD_ID |
| 100 | RBE_metrics_table=rbe_metrics.builds |
Ben Segall | e49349b | 2023-06-01 02:54:56 +0000 | [diff] [blame] | 101 | RBE_metrics_labels=source=developer,tool={tool} |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 102 | RBE_metrics_prefix=go.chromium.org |
| 103 | """ |
| 104 | autoninja_id = os.environ.get("AUTONINJA_BUILD_ID") |
| 105 | if autoninja_id is not None: |
| 106 | os.environ.setdefault("RBE_invocation_id", autoninja_id) |
| 107 | os.environ.setdefault("RBE_metrics_project", "chromium-reclient-metrics") |
| 108 | os.environ.setdefault("RBE_metrics_table", "rbe_metrics.builds") |
Ben Segall | e49349b | 2023-06-01 02:54:56 +0000 | [diff] [blame] | 109 | os.environ.setdefault("RBE_metrics_labels", "source=developer,tool=" + tool) |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 110 | os.environ.setdefault("RBE_metrics_prefix", "go.chromium.org") |
| 111 | |
| 112 | |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 113 | def set_reproxy_path_flags(out_dir, make_dirs=True): |
| 114 | """Helper to setup the logs and cache directories for reclient. |
| 115 | |
| 116 | Creates the following directory structure if make_dirs is true: |
Ben Segall | 7a0fe8b | 2023-04-24 20:36:55 +0000 | [diff] [blame] | 117 | If in a gclient checkout |
| 118 | out_dir/ |
| 119 | .reproxy_tmp/ |
| 120 | logs/ |
| 121 | <gclient_root> |
| 122 | .reproxy_cache/ |
| 123 | md5(out_dir/.reproxy_tmp)/ |
| 124 | |
| 125 | If not in a gclient checkout |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 126 | out_dir/ |
| 127 | .reproxy_tmp/ |
| 128 | logs/ |
| 129 | cache/ |
| 130 | |
| 131 | The following env vars are set if not already set: |
| 132 | RBE_output_dir=out_dir/.reproxy_tmp/logs |
| 133 | RBE_proxy_log_dir=out_dir/.reproxy_tmp/logs |
| 134 | RBE_log_dir=out_dir/.reproxy_tmp/logs |
| 135 | RBE_cache_dir=out_dir/.reproxy_tmp/cache |
| 136 | *Nix Only: |
| 137 | RBE_server_address=unix://out_dir/.reproxy_tmp/reproxy.sock |
| 138 | Windows Only: |
| 139 | RBE_server_address=pipe://md5(out_dir/.reproxy_tmp)/reproxy.pipe |
| 140 | """ |
| 141 | tmp_dir = os.path.abspath(os.path.join(out_dir, '.reproxy_tmp')) |
| 142 | log_dir = os.path.join(tmp_dir, 'logs') |
Ben Segall | 7a0fe8b | 2023-04-24 20:36:55 +0000 | [diff] [blame] | 143 | cache_dir = find_cache_dir(tmp_dir) |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 144 | if make_dirs: |
| 145 | os.makedirs(tmp_dir, exist_ok=True) |
| 146 | os.makedirs(log_dir, exist_ok=True) |
| 147 | os.makedirs(cache_dir, exist_ok=True) |
| 148 | os.environ.setdefault("RBE_output_dir", log_dir) |
| 149 | os.environ.setdefault("RBE_proxy_log_dir", log_dir) |
| 150 | os.environ.setdefault("RBE_log_dir", log_dir) |
| 151 | os.environ.setdefault("RBE_cache_dir", cache_dir) |
| 152 | if sys.platform.startswith('win'): |
| 153 | pipe_dir = hashlib.md5(tmp_dir.encode()).hexdigest() |
| 154 | os.environ.setdefault("RBE_server_address", |
| 155 | "pipe://%s/reproxy.pipe" % pipe_dir) |
| 156 | else: |
Takuto Ikuta | bf67b23 | 2023-05-26 03:06:40 +0000 | [diff] [blame] | 157 | # unix domain socket has path length limit, so use fixed size path here. |
| 158 | # ref: https://www.man7.org/linux/man-pages/man7/unix.7.html |
| 159 | os.environ.setdefault( |
Takuto Ikuta | b665de6 | 2023-05-26 06:24:45 +0000 | [diff] [blame] | 160 | "RBE_server_address", "unix:///tmp/reproxy_%s.sock" % |
Takuto Ikuta | bf67b23 | 2023-05-26 03:06:40 +0000 | [diff] [blame] | 161 | hashlib.sha256(tmp_dir.encode()).hexdigest()) |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 162 | |
| 163 | |
| 164 | @contextlib.contextmanager |
Ben Segall | e49349b | 2023-06-01 02:54:56 +0000 | [diff] [blame] | 165 | def build_context(argv, tool): |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 166 | # If use_remoteexec is set, but the reclient binaries or configs don't |
| 167 | # exist, display an error message and stop. Otherwise, the build will |
| 168 | # attempt to run with rewrapper wrapping actions, but will fail with |
| 169 | # possible non-obvious problems. |
| 170 | reclient_bin_dir = find_reclient_bin_dir() |
| 171 | reclient_cfg = find_reclient_cfg() |
| 172 | if reclient_bin_dir is None or reclient_cfg is None: |
| 173 | print(("Build is configured to use reclient but necessary binaries " |
| 174 | "or config files can't be found. Developer builds with " |
| 175 | "reclient are not yet supported. Try regenerating your " |
| 176 | "build with use_goma in place of use_remoteexec for now."), |
| 177 | file=sys.stderr) |
| 178 | yield 1 |
| 179 | return |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 180 | |
| 181 | ninja_out = find_ninja_out_dir(argv) |
| 182 | |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 183 | try: |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 184 | set_reproxy_path_flags(ninja_out) |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 185 | except OSError: |
| 186 | print("Error creating reproxy_tmp in output dir", file=sys.stderr) |
| 187 | yield 1 |
| 188 | return |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 189 | |
| 190 | if reclient_metrics.check_status(ninja_out): |
Ben Segall | e49349b | 2023-06-01 02:54:56 +0000 | [diff] [blame] | 191 | set_reproxy_metrics_flags(tool) |
Ben Segall | 530d86d | 2023-05-29 16:11:00 +0000 | [diff] [blame] | 192 | |
Fumitoshi Ukai | dedeb88 | 2023-06-15 03:58:35 +0000 | [diff] [blame] | 193 | if os.environ.get('RBE_instance', None): |
| 194 | print('WARNING: Using RBE_instance=%s\n' % |
| 195 | os.environ.get('RBE_instance', '')) |
| 196 | |
Junji Watanabe | 607284d | 2023-04-20 03:14:52 +0000 | [diff] [blame] | 197 | reproxy_ret_code = start_reproxy(reclient_cfg, reclient_bin_dir) |
| 198 | if reproxy_ret_code != 0: |
| 199 | yield reproxy_ret_code |
| 200 | return |
| 201 | try: |
| 202 | yield |
| 203 | finally: |
| 204 | print("Shutting down reproxy...", file=sys.stderr) |
| 205 | stop_reproxy(reclient_cfg, reclient_bin_dir) |