blob: 108343df15c64cb42a1e5ffd8ece94a85845b907 [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
16
17
18def find_reclient_bin_dir():
19 tools_path = gclient_paths.GetBuildtoolsPath()
20 if not tools_path:
21 return None
22
23 reclient_bin_dir = os.path.join(tools_path, 'reclient')
24 if os.path.isdir(reclient_bin_dir):
25 return reclient_bin_dir
26 return None
27
28
29def find_reclient_cfg():
30 tools_path = gclient_paths.GetBuildtoolsPath()
31 if not tools_path:
32 return None
33
34 reclient_cfg = os.path.join(tools_path, 'reclient_cfgs', 'reproxy.cfg')
35 if os.path.isfile(reclient_cfg):
36 return reclient_cfg
37 return None
38
39
40def run(cmd_args):
41 if os.environ.get('NINJA_SUMMARIZE_BUILD') == '1':
42 print(' '.join(cmd_args))
43 return subprocess.call(cmd_args)
44
45
46def start_reproxy(reclient_cfg, reclient_bin_dir):
47 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000048 os.path.join(reclient_bin_dir,
49 'bootstrap' + gclient_paths.GetExeSuffix()), '--re_proxy=' +
50 os.path.join(reclient_bin_dir, 'reproxy' + gclient_paths.GetExeSuffix()),
Junji Watanabe607284d2023-04-20 03:14:52 +000051 '--cfg=' + reclient_cfg
52 ])
53
54
55def stop_reproxy(reclient_cfg, reclient_bin_dir):
56 return run([
Ben Segallcdefe672023-05-17 13:44:04 +000057 os.path.join(reclient_bin_dir,
58 'bootstrap' + gclient_paths.GetExeSuffix()), '--shutdown',
Junji Watanabe607284d2023-04-20 03:14:52 +000059 '--cfg=' + reclient_cfg
60 ])
61
62
63def find_ninja_out_dir(args):
64 # Ninja uses getopt_long, which allows to intermix non-option arguments.
65 # To leave non supported parameters untouched, we do not use getopt.
66 for index, arg in enumerate(args[1:]):
67 if arg == '-C':
68 # + 1 to get the next argument and +1 because we trimmed off args[0]
69 return args[index + 2]
70 if arg.startswith('-C'):
71 # Support -Cout/Default
72 return arg[2:]
73 return '.'
74
75
Ben Segall7a0fe8b2023-04-24 20:36:55 +000076def find_cache_dir(tmp_dir):
77 """Helper to find the correct cache directory for a build.
78
79 tmp_dir should be a build specific temp directory within the out directory.
80
81 If this is called from within a gclient checkout, the cache dir will be:
82 <gclient_root>/.reproxy_cache/md5(tmp_dir)/
83 If this is not called from within a gclient checkout, the cache dir will be:
84 tmp_dir/cache
85 """
86 gclient_root = gclient_paths.FindGclientRoot(os.getcwd())
87 if gclient_root:
88 return os.path.join(gclient_root, '.reproxy_cache',
89 hashlib.md5(tmp_dir.encode()).hexdigest())
90 return os.path.join(tmp_dir, 'cache')
91
92
Junji Watanabe607284d2023-04-20 03:14:52 +000093def set_reproxy_path_flags(out_dir, make_dirs=True):
94 """Helper to setup the logs and cache directories for reclient.
95
96 Creates the following directory structure if make_dirs is true:
Ben Segall7a0fe8b2023-04-24 20:36:55 +000097 If in a gclient checkout
98 out_dir/
99 .reproxy_tmp/
100 logs/
101 <gclient_root>
102 .reproxy_cache/
103 md5(out_dir/.reproxy_tmp)/
104
105 If not in a gclient checkout
Junji Watanabe607284d2023-04-20 03:14:52 +0000106 out_dir/
107 .reproxy_tmp/
108 logs/
109 cache/
110
111 The following env vars are set if not already set:
112 RBE_output_dir=out_dir/.reproxy_tmp/logs
113 RBE_proxy_log_dir=out_dir/.reproxy_tmp/logs
114 RBE_log_dir=out_dir/.reproxy_tmp/logs
115 RBE_cache_dir=out_dir/.reproxy_tmp/cache
116 *Nix Only:
117 RBE_server_address=unix://out_dir/.reproxy_tmp/reproxy.sock
118 Windows Only:
119 RBE_server_address=pipe://md5(out_dir/.reproxy_tmp)/reproxy.pipe
120 """
121 tmp_dir = os.path.abspath(os.path.join(out_dir, '.reproxy_tmp'))
122 log_dir = os.path.join(tmp_dir, 'logs')
Ben Segall7a0fe8b2023-04-24 20:36:55 +0000123 cache_dir = find_cache_dir(tmp_dir)
Junji Watanabe607284d2023-04-20 03:14:52 +0000124 if make_dirs:
125 os.makedirs(tmp_dir, exist_ok=True)
126 os.makedirs(log_dir, exist_ok=True)
127 os.makedirs(cache_dir, exist_ok=True)
128 os.environ.setdefault("RBE_output_dir", log_dir)
129 os.environ.setdefault("RBE_proxy_log_dir", log_dir)
130 os.environ.setdefault("RBE_log_dir", log_dir)
131 os.environ.setdefault("RBE_cache_dir", cache_dir)
132 if sys.platform.startswith('win'):
133 pipe_dir = hashlib.md5(tmp_dir.encode()).hexdigest()
134 os.environ.setdefault("RBE_server_address",
135 "pipe://%s/reproxy.pipe" % pipe_dir)
136 else:
Takuto Ikutabf67b232023-05-26 03:06:40 +0000137 # unix domain socket has path length limit, so use fixed size path here.
138 # ref: https://www.man7.org/linux/man-pages/man7/unix.7.html
139 os.environ.setdefault(
Takuto Ikutab665de62023-05-26 06:24:45 +0000140 "RBE_server_address", "unix:///tmp/reproxy_%s.sock" %
Takuto Ikutabf67b232023-05-26 03:06:40 +0000141 hashlib.sha256(tmp_dir.encode()).hexdigest())
Junji Watanabe607284d2023-04-20 03:14:52 +0000142
143
144@contextlib.contextmanager
145def build_context(argv):
146 # If use_remoteexec is set, but the reclient binaries or configs don't
147 # exist, display an error message and stop. Otherwise, the build will
148 # attempt to run with rewrapper wrapping actions, but will fail with
149 # possible non-obvious problems.
150 reclient_bin_dir = find_reclient_bin_dir()
151 reclient_cfg = find_reclient_cfg()
152 if reclient_bin_dir is None or reclient_cfg is None:
153 print(("Build is configured to use reclient but necessary binaries "
154 "or config files can't be found. Developer builds with "
155 "reclient are not yet supported. Try regenerating your "
156 "build with use_goma in place of use_remoteexec for now."),
157 file=sys.stderr)
158 yield 1
159 return
160 try:
161 set_reproxy_path_flags(find_ninja_out_dir(argv))
162 except OSError:
163 print("Error creating reproxy_tmp in output dir", file=sys.stderr)
164 yield 1
165 return
166 reproxy_ret_code = start_reproxy(reclient_cfg, reclient_bin_dir)
167 if reproxy_ret_code != 0:
168 yield reproxy_ret_code
169 return
170 try:
171 yield
172 finally:
173 print("Shutting down reproxy...", file=sys.stderr)
174 stop_reproxy(reclient_cfg, reclient_bin_dir)