blob: c81c28d86b10edccaeedd25af300303f6034a7e4 [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
Bruce Dawson6d0c2352023-08-06 02:38:51 +000015import time
Junji Watanabe607284d2023-04-20 03:14:52 +000016
17import gclient_paths
Ben Segall530d86d2023-05-29 16:11:00 +000018import reclient_metrics
Junji Watanabe607284d2023-04-20 03:14:52 +000019
20
21def find_reclient_bin_dir():
22 tools_path = gclient_paths.GetBuildtoolsPath()
23 if not tools_path:
24 return None
25
26 reclient_bin_dir = os.path.join(tools_path, 'reclient')
27 if os.path.isdir(reclient_bin_dir):
28 return reclient_bin_dir
29 return None
30
31
32def find_reclient_cfg():
33 tools_path = gclient_paths.GetBuildtoolsPath()
34 if not tools_path:
35 return None
36
37 reclient_cfg = os.path.join(tools_path, 'reclient_cfgs', 'reproxy.cfg')
38 if os.path.isfile(reclient_cfg):
39 return reclient_cfg
40 return None
41
42
43def run(cmd_args):
44 if os.environ.get('NINJA_SUMMARIZE_BUILD') == '1':
45 print(' '.join(cmd_args))
46 return subprocess.call(cmd_args)
47
48
49def start_reproxy(reclient_cfg, reclient_bin_dir):
50 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000051 os.path.join(reclient_bin_dir,
52 'bootstrap' + gclient_paths.GetExeSuffix()), '--re_proxy=' +
53 os.path.join(reclient_bin_dir, 'reproxy' + gclient_paths.GetExeSuffix()),
Junji Watanabe607284d2023-04-20 03:14:52 +000054 '--cfg=' + reclient_cfg
55 ])
56
57
58def stop_reproxy(reclient_cfg, reclient_bin_dir):
59 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000060 os.path.join(reclient_bin_dir,
61 'bootstrap' + gclient_paths.GetExeSuffix()), '--shutdown',
Junji Watanabe607284d2023-04-20 03:14:52 +000062 '--cfg=' + reclient_cfg
63 ])
64
65
66def find_ninja_out_dir(args):
67 # Ninja uses getopt_long, which allows to intermix non-option arguments.
68 # To leave non supported parameters untouched, we do not use getopt.
69 for index, arg in enumerate(args[1:]):
70 if arg == '-C':
71 # + 1 to get the next argument and +1 because we trimmed off args[0]
72 return args[index + 2]
73 if arg.startswith('-C'):
74 # Support -Cout/Default
75 return arg[2:]
76 return '.'
77
78
Ben Segall7a0fe8b2023-04-24 20:36:55 +000079def find_cache_dir(tmp_dir):
80 """Helper to find the correct cache directory for a build.
81
82 tmp_dir should be a build specific temp directory within the out directory.
83
84 If this is called from within a gclient checkout, the cache dir will be:
85 <gclient_root>/.reproxy_cache/md5(tmp_dir)/
86 If this is not called from within a gclient checkout, the cache dir will be:
87 tmp_dir/cache
88 """
89 gclient_root = gclient_paths.FindGclientRoot(os.getcwd())
90 if gclient_root:
91 return os.path.join(gclient_root, '.reproxy_cache',
92 hashlib.md5(tmp_dir.encode()).hexdigest())
93 return os.path.join(tmp_dir, 'cache')
94
95
Ben Segalle49349b2023-06-01 02:54:56 +000096def set_reproxy_metrics_flags(tool):
Ben Segall530d86d2023-05-29 16:11:00 +000097 """Helper to setup metrics collection flags for reproxy.
98
99 The following env vars are set if not already set:
100 RBE_metrics_project=chromium-reclient-metrics
101 RBE_invocation_id=$AUTONINJA_BUILD_ID
102 RBE_metrics_table=rbe_metrics.builds
Ben Segalle49349b2023-06-01 02:54:56 +0000103 RBE_metrics_labels=source=developer,tool={tool}
Ben Segall530d86d2023-05-29 16:11:00 +0000104 RBE_metrics_prefix=go.chromium.org
105 """
106 autoninja_id = os.environ.get("AUTONINJA_BUILD_ID")
107 if autoninja_id is not None:
108 os.environ.setdefault("RBE_invocation_id", autoninja_id)
109 os.environ.setdefault("RBE_metrics_project", "chromium-reclient-metrics")
110 os.environ.setdefault("RBE_metrics_table", "rbe_metrics.builds")
Ben Segalle49349b2023-06-01 02:54:56 +0000111 os.environ.setdefault("RBE_metrics_labels", "source=developer,tool=" + tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000112 os.environ.setdefault("RBE_metrics_prefix", "go.chromium.org")
113
114
Ben Segall3589c482023-07-24 17:42:55 +0000115def remove_mdproxy_from_path():
116 os.environ["PATH"] = os.pathsep.join(
117 d for d in os.environ.get("PATH", "").split(os.pathsep)
118 if "mdproxy" not in d)
119
120
Junji Watanabe607284d2023-04-20 03:14:52 +0000121def set_reproxy_path_flags(out_dir, make_dirs=True):
122 """Helper to setup the logs and cache directories for reclient.
123
124 Creates the following directory structure if make_dirs is true:
Ben Segall7a0fe8b2023-04-24 20:36:55 +0000125 If in a gclient checkout
126 out_dir/
127 .reproxy_tmp/
128 logs/
129 <gclient_root>
130 .reproxy_cache/
131 md5(out_dir/.reproxy_tmp)/
132
133 If not in a gclient checkout
Junji Watanabe607284d2023-04-20 03:14:52 +0000134 out_dir/
135 .reproxy_tmp/
136 logs/
137 cache/
138
139 The following env vars are set if not already set:
140 RBE_output_dir=out_dir/.reproxy_tmp/logs
141 RBE_proxy_log_dir=out_dir/.reproxy_tmp/logs
142 RBE_log_dir=out_dir/.reproxy_tmp/logs
143 RBE_cache_dir=out_dir/.reproxy_tmp/cache
144 *Nix Only:
145 RBE_server_address=unix://out_dir/.reproxy_tmp/reproxy.sock
146 Windows Only:
147 RBE_server_address=pipe://md5(out_dir/.reproxy_tmp)/reproxy.pipe
148 """
149 tmp_dir = os.path.abspath(os.path.join(out_dir, '.reproxy_tmp'))
150 log_dir = os.path.join(tmp_dir, 'logs')
Ben Segall04ca3832023-07-19 01:50:59 +0000151 racing_dir = os.path.join(tmp_dir, 'racing')
Ben Segall7a0fe8b2023-04-24 20:36:55 +0000152 cache_dir = find_cache_dir(tmp_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000153 if make_dirs:
Ben Segall2e673842023-06-21 14:23:37 +0000154 if os.path.exists(log_dir):
Ben Segall78998662023-07-24 20:42:37 +0000155 try:
156 # Clear log dir before each build to ensure correct metric aggregation.
157 shutil.rmtree(log_dir)
158 except OSError:
159 print(
160 "Couldn't clear logs because reproxy did "
161 "not shutdown after the last build",
162 file=sys.stderr)
Junji Watanabe607284d2023-04-20 03:14:52 +0000163 os.makedirs(tmp_dir, exist_ok=True)
164 os.makedirs(log_dir, exist_ok=True)
165 os.makedirs(cache_dir, exist_ok=True)
Ben Segall60b21dd2023-07-19 02:40:41 +0000166 os.makedirs(racing_dir, exist_ok=True)
Junji Watanabe607284d2023-04-20 03:14:52 +0000167 os.environ.setdefault("RBE_output_dir", log_dir)
168 os.environ.setdefault("RBE_proxy_log_dir", log_dir)
169 os.environ.setdefault("RBE_log_dir", log_dir)
170 os.environ.setdefault("RBE_cache_dir", cache_dir)
Ben Segall04ca3832023-07-19 01:50:59 +0000171 os.environ.setdefault("RBE_racing_tmp_dir", racing_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000172 if sys.platform.startswith('win'):
173 pipe_dir = hashlib.md5(tmp_dir.encode()).hexdigest()
174 os.environ.setdefault("RBE_server_address",
175 "pipe://%s/reproxy.pipe" % pipe_dir)
176 else:
Takuto Ikutabf67b232023-05-26 03:06:40 +0000177 # unix domain socket has path length limit, so use fixed size path here.
178 # ref: https://www.man7.org/linux/man-pages/man7/unix.7.html
179 os.environ.setdefault(
Takuto Ikutab665de62023-05-26 06:24:45 +0000180 "RBE_server_address", "unix:///tmp/reproxy_%s.sock" %
Takuto Ikutabf67b232023-05-26 03:06:40 +0000181 hashlib.sha256(tmp_dir.encode()).hexdigest())
Junji Watanabe607284d2023-04-20 03:14:52 +0000182
183
Ben Segalld3e43dd2023-07-25 02:32:31 +0000184def set_racing_defaults():
185 os.environ.setdefault("RBE_local_resource_fraction", "0.2")
186 os.environ.setdefault("RBE_racing_bias", "0.95")
187
Ben Segall04ca3832023-07-19 01:50:59 +0000188
Junji Watanabe607284d2023-04-20 03:14:52 +0000189@contextlib.contextmanager
Ben Segalle49349b2023-06-01 02:54:56 +0000190def build_context(argv, tool):
Junji Watanabe607284d2023-04-20 03:14:52 +0000191 # If use_remoteexec is set, but the reclient binaries or configs don't
192 # exist, display an error message and stop. Otherwise, the build will
193 # attempt to run with rewrapper wrapping actions, but will fail with
194 # possible non-obvious problems.
195 reclient_bin_dir = find_reclient_bin_dir()
196 reclient_cfg = find_reclient_cfg()
197 if reclient_bin_dir is None or reclient_cfg is None:
198 print(("Build is configured to use reclient but necessary binaries "
199 "or config files can't be found. Developer builds with "
200 "reclient are not yet supported. Try regenerating your "
201 "build with use_goma in place of use_remoteexec for now."),
202 file=sys.stderr)
203 yield 1
204 return
Ben Segall530d86d2023-05-29 16:11:00 +0000205
206 ninja_out = find_ninja_out_dir(argv)
207
Junji Watanabe607284d2023-04-20 03:14:52 +0000208 try:
Ben Segall530d86d2023-05-29 16:11:00 +0000209 set_reproxy_path_flags(ninja_out)
Junji Watanabe607284d2023-04-20 03:14:52 +0000210 except OSError:
211 print("Error creating reproxy_tmp in output dir", file=sys.stderr)
212 yield 1
213 return
Ben Segall530d86d2023-05-29 16:11:00 +0000214
215 if reclient_metrics.check_status(ninja_out):
Ben Segalle49349b2023-06-01 02:54:56 +0000216 set_reproxy_metrics_flags(tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000217
Fumitoshi Ukaidedeb882023-06-15 03:58:35 +0000218 if os.environ.get('RBE_instance', None):
219 print('WARNING: Using RBE_instance=%s\n' %
220 os.environ.get('RBE_instance', ''))
221
Ben Segall582edfa2023-08-03 14:03:48 +0000222 remote_disabled = os.environ.get('RBE_remote_disabled')
223 if remote_disabled not in ('1', 't', 'T', 'true', 'TRUE', 'True'):
224 set_racing_defaults()
Ben Segalld3e43dd2023-07-25 02:32:31 +0000225
Ben Segall3589c482023-07-24 17:42:55 +0000226 # TODO(b/292523514) remove this once a fix is landed in reproxy
227 remove_mdproxy_from_path()
228
Bruce Dawson6d0c2352023-08-06 02:38:51 +0000229 start = time.time()
Junji Watanabe607284d2023-04-20 03:14:52 +0000230 reproxy_ret_code = start_reproxy(reclient_cfg, reclient_bin_dir)
Bruce Dawson6d0c2352023-08-06 02:38:51 +0000231 elapsed = time.time() - start
232 print('%1.3f s to start reproxy' % elapsed)
Junji Watanabe607284d2023-04-20 03:14:52 +0000233 if reproxy_ret_code != 0:
234 yield reproxy_ret_code
235 return
236 try:
237 yield
238 finally:
239 print("Shutting down reproxy...", file=sys.stderr)
Bruce Dawson6d0c2352023-08-06 02:38:51 +0000240 start = time.time()
Junji Watanabe607284d2023-04-20 03:14:52 +0000241 stop_reproxy(reclient_cfg, reclient_bin_dir)
Bruce Dawson6d0c2352023-08-06 02:38:51 +0000242 elapsed = time.time() - start
243 print('%1.3f s to stop reproxy' % elapsed)