blob: 6b4ec29118b99629a30af134060ac3388e95b37c [file] [log] [blame]
Junji Watanabe607284d2023-04-20 03:14:52 +00001# 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
5the reclient lifecycle safely. It will automatically start
6reproxy before running ninja and stop reproxy when build stops
7for any reason e.g. build completion, keyboard interrupt etc."""
8
9import contextlib
10import hashlib
11import os
Ben Segall2e673842023-06-21 14:23:37 +000012import shutil
Junji Watanabe607284d2023-04-20 03:14:52 +000013import subprocess
14import sys
15
16import gclient_paths
Ben Segall530d86d2023-05-29 16:11:00 +000017import reclient_metrics
Junji Watanabe607284d2023-04-20 03:14:52 +000018
19
20def find_reclient_bin_dir():
21 tools_path = gclient_paths.GetBuildtoolsPath()
22 if not tools_path:
23 return None
24
25 reclient_bin_dir = os.path.join(tools_path, 'reclient')
26 if os.path.isdir(reclient_bin_dir):
27 return reclient_bin_dir
28 return None
29
30
31def find_reclient_cfg():
32 tools_path = gclient_paths.GetBuildtoolsPath()
33 if not tools_path:
34 return None
35
36 reclient_cfg = os.path.join(tools_path, 'reclient_cfgs', 'reproxy.cfg')
37 if os.path.isfile(reclient_cfg):
38 return reclient_cfg
39 return None
40
41
42def run(cmd_args):
43 if os.environ.get('NINJA_SUMMARIZE_BUILD') == '1':
44 print(' '.join(cmd_args))
45 return subprocess.call(cmd_args)
46
47
48def start_reproxy(reclient_cfg, reclient_bin_dir):
49 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000050 os.path.join(reclient_bin_dir,
51 'bootstrap' + gclient_paths.GetExeSuffix()), '--re_proxy=' +
52 os.path.join(reclient_bin_dir, 'reproxy' + gclient_paths.GetExeSuffix()),
Junji Watanabe607284d2023-04-20 03:14:52 +000053 '--cfg=' + reclient_cfg
54 ])
55
56
57def stop_reproxy(reclient_cfg, reclient_bin_dir):
58 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000059 os.path.join(reclient_bin_dir,
60 'bootstrap' + gclient_paths.GetExeSuffix()), '--shutdown',
Junji Watanabe607284d2023-04-20 03:14:52 +000061 '--cfg=' + reclient_cfg
62 ])
63
64
65def find_ninja_out_dir(args):
66 # Ninja uses getopt_long, which allows to intermix non-option arguments.
67 # To leave non supported parameters untouched, we do not use getopt.
68 for index, arg in enumerate(args[1:]):
69 if arg == '-C':
70 # + 1 to get the next argument and +1 because we trimmed off args[0]
71 return args[index + 2]
72 if arg.startswith('-C'):
73 # Support -Cout/Default
74 return arg[2:]
75 return '.'
76
77
Ben Segall7a0fe8b2023-04-24 20:36:55 +000078def find_cache_dir(tmp_dir):
79 """Helper to find the correct cache directory for a build.
80
81 tmp_dir should be a build specific temp directory within the out directory.
82
83 If this is called from within a gclient checkout, the cache dir will be:
84 <gclient_root>/.reproxy_cache/md5(tmp_dir)/
85 If this is not called from within a gclient checkout, the cache dir will be:
86 tmp_dir/cache
87 """
88 gclient_root = gclient_paths.FindGclientRoot(os.getcwd())
89 if gclient_root:
90 return os.path.join(gclient_root, '.reproxy_cache',
91 hashlib.md5(tmp_dir.encode()).hexdigest())
92 return os.path.join(tmp_dir, 'cache')
93
94
Ben Segalle49349b2023-06-01 02:54:56 +000095def set_reproxy_metrics_flags(tool):
Ben Segall530d86d2023-05-29 16:11:00 +000096 """Helper to setup metrics collection flags for reproxy.
97
98 The following env vars are set if not already set:
99 RBE_metrics_project=chromium-reclient-metrics
100 RBE_invocation_id=$AUTONINJA_BUILD_ID
101 RBE_metrics_table=rbe_metrics.builds
Ben Segalle49349b2023-06-01 02:54:56 +0000102 RBE_metrics_labels=source=developer,tool={tool}
Ben Segall530d86d2023-05-29 16:11:00 +0000103 RBE_metrics_prefix=go.chromium.org
104 """
105 autoninja_id = os.environ.get("AUTONINJA_BUILD_ID")
106 if autoninja_id is not None:
107 os.environ.setdefault("RBE_invocation_id", autoninja_id)
108 os.environ.setdefault("RBE_metrics_project", "chromium-reclient-metrics")
109 os.environ.setdefault("RBE_metrics_table", "rbe_metrics.builds")
Ben Segalle49349b2023-06-01 02:54:56 +0000110 os.environ.setdefault("RBE_metrics_labels", "source=developer,tool=" + tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000111 os.environ.setdefault("RBE_metrics_prefix", "go.chromium.org")
112
113
Ben Segall3589c482023-07-24 17:42:55 +0000114def remove_mdproxy_from_path():
115 os.environ["PATH"] = os.pathsep.join(
116 d for d in os.environ.get("PATH", "").split(os.pathsep)
117 if "mdproxy" not in d)
118
119
Junji Watanabe607284d2023-04-20 03:14:52 +0000120def set_reproxy_path_flags(out_dir, make_dirs=True):
121 """Helper to setup the logs and cache directories for reclient.
122
123 Creates the following directory structure if make_dirs is true:
Ben Segall7a0fe8b2023-04-24 20:36:55 +0000124 If in a gclient checkout
125 out_dir/
126 .reproxy_tmp/
127 logs/
128 <gclient_root>
129 .reproxy_cache/
130 md5(out_dir/.reproxy_tmp)/
131
132 If not in a gclient checkout
Junji Watanabe607284d2023-04-20 03:14:52 +0000133 out_dir/
134 .reproxy_tmp/
135 logs/
136 cache/
137
138 The following env vars are set if not already set:
139 RBE_output_dir=out_dir/.reproxy_tmp/logs
140 RBE_proxy_log_dir=out_dir/.reproxy_tmp/logs
141 RBE_log_dir=out_dir/.reproxy_tmp/logs
142 RBE_cache_dir=out_dir/.reproxy_tmp/cache
143 *Nix Only:
144 RBE_server_address=unix://out_dir/.reproxy_tmp/reproxy.sock
145 Windows Only:
146 RBE_server_address=pipe://md5(out_dir/.reproxy_tmp)/reproxy.pipe
147 """
148 tmp_dir = os.path.abspath(os.path.join(out_dir, '.reproxy_tmp'))
149 log_dir = os.path.join(tmp_dir, 'logs')
Ben Segall04ca3832023-07-19 01:50:59 +0000150 racing_dir = os.path.join(tmp_dir, 'racing')
Ben Segall7a0fe8b2023-04-24 20:36:55 +0000151 cache_dir = find_cache_dir(tmp_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000152 if make_dirs:
Ben Segall2e673842023-06-21 14:23:37 +0000153 if os.path.exists(log_dir):
Ben Segall78998662023-07-24 20:42:37 +0000154 try:
155 # Clear log dir before each build to ensure correct metric aggregation.
156 shutil.rmtree(log_dir)
157 except OSError:
158 print(
159 "Couldn't clear logs because reproxy did "
160 "not shutdown after the last build",
161 file=sys.stderr)
Junji Watanabe607284d2023-04-20 03:14:52 +0000162 os.makedirs(tmp_dir, exist_ok=True)
163 os.makedirs(log_dir, exist_ok=True)
164 os.makedirs(cache_dir, exist_ok=True)
Ben Segall60b21dd2023-07-19 02:40:41 +0000165 os.makedirs(racing_dir, exist_ok=True)
Junji Watanabe607284d2023-04-20 03:14:52 +0000166 os.environ.setdefault("RBE_output_dir", log_dir)
167 os.environ.setdefault("RBE_proxy_log_dir", log_dir)
168 os.environ.setdefault("RBE_log_dir", log_dir)
169 os.environ.setdefault("RBE_cache_dir", cache_dir)
Ben Segall04ca3832023-07-19 01:50:59 +0000170 os.environ.setdefault("RBE_racing_tmp_dir", racing_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000171 if sys.platform.startswith('win'):
172 pipe_dir = hashlib.md5(tmp_dir.encode()).hexdigest()
173 os.environ.setdefault("RBE_server_address",
174 "pipe://%s/reproxy.pipe" % pipe_dir)
175 else:
Takuto Ikutabf67b232023-05-26 03:06:40 +0000176 # unix domain socket has path length limit, so use fixed size path here.
177 # ref: https://www.man7.org/linux/man-pages/man7/unix.7.html
178 os.environ.setdefault(
Takuto Ikutab665de62023-05-26 06:24:45 +0000179 "RBE_server_address", "unix:///tmp/reproxy_%s.sock" %
Takuto Ikutabf67b232023-05-26 03:06:40 +0000180 hashlib.sha256(tmp_dir.encode()).hexdigest())
Junji Watanabe607284d2023-04-20 03:14:52 +0000181
182
Ben Segalld3e43dd2023-07-25 02:32:31 +0000183def set_racing_defaults():
184 os.environ.setdefault("RBE_local_resource_fraction", "0.2")
185 os.environ.setdefault("RBE_racing_bias", "0.95")
186
Ben Segall04ca3832023-07-19 01:50:59 +0000187
Junji Watanabe607284d2023-04-20 03:14:52 +0000188@contextlib.contextmanager
Ben Segalle49349b2023-06-01 02:54:56 +0000189def build_context(argv, tool):
Junji Watanabe607284d2023-04-20 03:14:52 +0000190 # If use_remoteexec is set, but the reclient binaries or configs don't
191 # exist, display an error message and stop. Otherwise, the build will
192 # attempt to run with rewrapper wrapping actions, but will fail with
193 # possible non-obvious problems.
194 reclient_bin_dir = find_reclient_bin_dir()
195 reclient_cfg = find_reclient_cfg()
196 if reclient_bin_dir is None or reclient_cfg is None:
197 print(("Build is configured to use reclient but necessary binaries "
198 "or config files can't be found. Developer builds with "
199 "reclient are not yet supported. Try regenerating your "
200 "build with use_goma in place of use_remoteexec for now."),
201 file=sys.stderr)
202 yield 1
203 return
Ben Segall530d86d2023-05-29 16:11:00 +0000204
205 ninja_out = find_ninja_out_dir(argv)
206
Junji Watanabe607284d2023-04-20 03:14:52 +0000207 try:
Ben Segall530d86d2023-05-29 16:11:00 +0000208 set_reproxy_path_flags(ninja_out)
Junji Watanabe607284d2023-04-20 03:14:52 +0000209 except OSError:
210 print("Error creating reproxy_tmp in output dir", file=sys.stderr)
211 yield 1
212 return
Ben Segall530d86d2023-05-29 16:11:00 +0000213
214 if reclient_metrics.check_status(ninja_out):
Ben Segalle49349b2023-06-01 02:54:56 +0000215 set_reproxy_metrics_flags(tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000216
Fumitoshi Ukaidedeb882023-06-15 03:58:35 +0000217 if os.environ.get('RBE_instance', None):
218 print('WARNING: Using RBE_instance=%s\n' %
219 os.environ.get('RBE_instance', ''))
220
Ben Segall582edfa2023-08-03 14:03:48 +0000221 remote_disabled = os.environ.get('RBE_remote_disabled')
222 if remote_disabled not in ('1', 't', 'T', 'true', 'TRUE', 'True'):
223 set_racing_defaults()
Ben Segalld3e43dd2023-07-25 02:32:31 +0000224
Ben Segall3589c482023-07-24 17:42:55 +0000225 # TODO(b/292523514) remove this once a fix is landed in reproxy
226 remove_mdproxy_from_path()
227
Junji Watanabe607284d2023-04-20 03:14:52 +0000228 reproxy_ret_code = start_reproxy(reclient_cfg, reclient_bin_dir)
229 if reproxy_ret_code != 0:
230 yield reproxy_ret_code
231 return
232 try:
233 yield
234 finally:
235 print("Shutting down reproxy...", file=sys.stderr)
236 stop_reproxy(reclient_cfg, reclient_bin_dir)