blob: 29d7eb75d62fb5d8038fe89f61f1a757f07db42c [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
12import subprocess
13import sys
14
15import gclient_paths
Ben Segall530d86d2023-05-29 16:11:00 +000016import reclient_metrics
Junji Watanabe607284d2023-04-20 03:14:52 +000017
18
19def 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
30def 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
41def 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
47def start_reproxy(reclient_cfg, reclient_bin_dir):
48 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000049 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 Watanabe607284d2023-04-20 03:14:52 +000052 '--cfg=' + reclient_cfg
53 ])
54
55
56def stop_reproxy(reclient_cfg, reclient_bin_dir):
57 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000058 os.path.join(reclient_bin_dir,
59 'bootstrap' + gclient_paths.GetExeSuffix()), '--shutdown',
Junji Watanabe607284d2023-04-20 03:14:52 +000060 '--cfg=' + reclient_cfg
61 ])
62
63
64def 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 Segall7a0fe8b2023-04-24 20:36:55 +000077def 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 Segalle49349b2023-06-01 02:54:56 +000094def set_reproxy_metrics_flags(tool):
Ben Segall530d86d2023-05-29 16:11:00 +000095 """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 Segalle49349b2023-06-01 02:54:56 +0000101 RBE_metrics_labels=source=developer,tool={tool}
Ben Segall530d86d2023-05-29 16:11:00 +0000102 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 Segalle49349b2023-06-01 02:54:56 +0000109 os.environ.setdefault("RBE_metrics_labels", "source=developer,tool=" + tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000110 os.environ.setdefault("RBE_metrics_prefix", "go.chromium.org")
111
112
Junji Watanabe607284d2023-04-20 03:14:52 +0000113def 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 Segall7a0fe8b2023-04-24 20:36:55 +0000117 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 Watanabe607284d2023-04-20 03:14:52 +0000126 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 Segall7a0fe8b2023-04-24 20:36:55 +0000143 cache_dir = find_cache_dir(tmp_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000144 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 Ikutabf67b232023-05-26 03:06:40 +0000157 # 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 Ikutab665de62023-05-26 06:24:45 +0000160 "RBE_server_address", "unix:///tmp/reproxy_%s.sock" %
Takuto Ikutabf67b232023-05-26 03:06:40 +0000161 hashlib.sha256(tmp_dir.encode()).hexdigest())
Junji Watanabe607284d2023-04-20 03:14:52 +0000162
163
164@contextlib.contextmanager
Ben Segalle49349b2023-06-01 02:54:56 +0000165def build_context(argv, tool):
Junji Watanabe607284d2023-04-20 03:14:52 +0000166 # 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 Segall530d86d2023-05-29 16:11:00 +0000180
181 ninja_out = find_ninja_out_dir(argv)
182
Junji Watanabe607284d2023-04-20 03:14:52 +0000183 try:
Ben Segall530d86d2023-05-29 16:11:00 +0000184 set_reproxy_path_flags(ninja_out)
Junji Watanabe607284d2023-04-20 03:14:52 +0000185 except OSError:
186 print("Error creating reproxy_tmp in output dir", file=sys.stderr)
187 yield 1
188 return
Ben Segall530d86d2023-05-29 16:11:00 +0000189
190 if reclient_metrics.check_status(ninja_out):
Ben Segalle49349b2023-06-01 02:54:56 +0000191 set_reproxy_metrics_flags(tool)
Ben Segall530d86d2023-05-29 16:11:00 +0000192
Junji Watanabe607284d2023-04-20 03:14:52 +0000193 reproxy_ret_code = start_reproxy(reclient_cfg, reclient_bin_dir)
194 if reproxy_ret_code != 0:
195 yield reproxy_ret_code
196 return
197 try:
198 yield
199 finally:
200 print("Shutting down reproxy...", file=sys.stderr)
201 stop_reproxy(reclient_cfg, reclient_bin_dir)