[go: nahoru, domu]

blob: 6b735042e1c01818b3a898a63b469d004bcb5a9f [file] [log] [blame]
Mohannad Farrag007862f2024-01-31 11:36:001#!/usr/bin/env python3
2#
3# Copyright 2024 The Chromium Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6"""
7Contains general-purpose methods that can be used to execute shell,
8GN and Ninja commands.
9"""
10
11import subprocess
12import os
13import re
Mohannad Farrag30d2c2c2024-02-14 18:27:2814import pathlib
15import difflib
Mohannad Farrag007862f2024-01-31 11:36:0016
17REPOSITORY_ROOT = os.path.abspath(
18 os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
19
20_MB_PATH = os.path.join(REPOSITORY_ROOT, 'tools/mb/mb.py')
21_GN_PATH = os.path.join(REPOSITORY_ROOT, 'buildtools/linux64/gn')
Mohannad Farrag30d2c2c2024-02-14 18:27:2822_GN_ARG_MATCHER = re.compile("^.*=.*$")
Mohannad Farrag007862f2024-01-31 11:36:0023
24
25def run(command, **kwargs):
26 """See the official documentation for subprocess.call.
27
28 Args:
29 command (list[str]): command to be executed
30
31 Returns:
32 int: the return value of subprocess.call
33 """
34 print(command, kwargs)
35 return subprocess.call(command, **kwargs)
36
37
38def run_shell(command, extra_options=''):
39 """Runs a shell command.
40
41 Runs a shell command with no escaping. It is recommended
42 to use `run` instead.
43 """
44 command = command + ' ' + extra_options
45 print(command)
46 return os.system(command)
47
48
Mohannad Farrag30d2c2c2024-02-14 18:27:2849def gn(out_dir, gn_args, gn_extra=None, **kwargs):
Mohannad Farrag007862f2024-01-31 11:36:0050 """ Executes `gn gen`.
51
52 Runs `gn gen |out_dir| |gn_args + gn_extra|` which will generate
53 a GN configuration that lives under |out_dir|. This is done
54 locally on the same chromium checkout.
55
56 Args:
57 out_dir (str): Path to delegate to `gn gen`.
58 gn_args (str): Args as a string delimited by space.
59 gn_extra (str): extra args as a string delimited by space.
60
61 Returns:
62 Exit code of running `gn gen` command with argument provided.
63 """
64 cmd = [_GN_PATH, 'gen', out_dir, '--args=%s' % gn_args]
65 if gn_extra:
66 cmd += gn_extra
Mohannad Farrag30d2c2c2024-02-14 18:27:2867 return run(cmd, **kwargs)
68
69
70def compare_text_and_generate_diff(generated_text, golden_text,
71 golden_file_path):
72 """
73 Compares the generated text with the golden text.
74
75 returns a diff that can be applied with `patch` if exists.
76 """
77 golden_lines = [line.rstrip() for line in golden_text.splitlines()]
78 generated_lines = [line.rstrip() for line in generated_text.splitlines()]
79 if golden_lines == generated_lines:
80 return None
81
82 expected_path = os.path.relpath(golden_file_path, REPOSITORY_ROOT)
83
84 diff = difflib.unified_diff(
85 golden_lines,
86 generated_lines,
87 fromfile=os.path.join('before', expected_path),
88 tofile=os.path.join('after', expected_path),
89 n=0,
90 lineterm='',
91 )
92
93 return '\n'.join(diff)
94
95
96def read_file(path):
97 """Reads a file as a string"""
98 return pathlib.Path(path).read_text()
Mohannad Farrag007862f2024-01-31 11:36:0099
100
101def build(out_dir, build_target, extra_options=None):
102 """Runs `ninja build`.
103
104 Runs `ninja -C |out_dir| |build_target| |extra_options|` which will build
105 the target |build_target| for the GN configuration living under |out_dir|.
106 This is done locally on the same chromium checkout.
107
108 Returns:
109 Exit code of running `ninja ..` command with the argument provided.
110 """
111 cmd = ['ninja', '-C', out_dir, build_target] + _get_ninja_jobs_options()
112 if extra_options:
113 cmd += extra_options
114 return run(cmd)
115
116
117def android_gn_gen(is_release, target_cpu, out_dir):
118 """Runs `gn gen` using Cronet's android gn_args.
119
120 Creates a local GN configuration under |out_dir| with the provided argument
121 as input to `get_android_gn_args`, see the documentation of
122 `get_android_gn_args` for more information.
123 """
124 return gn(out_dir, ' '.join(get_android_gn_args(is_release, target_cpu)))
125
126
127def get_android_gn_args(is_release, target_cpu):
128 """Fetches the gn args for a specific builder.
129
130 Returns a list of gn args used by the builders whose target cpu
131 is |target_cpu| and (dev or rel) depending on is_release.
132
133 See https://ci.chromium.org/p/chromium/g/chromium.android/console for
134 a list of the builders
135
136 Example:
137
138 get_android_gn_args(true, 'x86') -> GN Args for `android-cronet-x86-rel`
139 get_android_gn_args(false, 'x86') -> GN Args for `android-cronet-x86-dev`
140 """
141 group_name = 'chromium.android'
142 builder_name = _map_config_to_android_builder(is_release, target_cpu)
143 # Ideally we would call `mb_py gen` directly, but we need to filter out the
144 # use_remoteexec arg, as that cannot be used in a local environment.
145 gn_args = subprocess.check_output(
146 ['python3', _MB_PATH, 'lookup', '-m', group_name, '-b',
147 builder_name]).decode('utf-8').strip()
Mohannad Farrag30d2c2c2024-02-14 18:27:28148 return filter_gn_args(gn_args.split("\n"), ["use_remoteexec"])
Mohannad Farrag007862f2024-01-31 11:36:00149
150
151def _use_goma():
152 goma_dir = (subprocess.check_output(['goma_ctl',
153 'goma_dir']).decode('utf-8').strip())
154 result = run(['goma_ctl', 'ensure_start'])
155 if not result:
156 return 'use_goma=true goma_dir="' + goma_dir + '" '
157 return ''
158
159
160def _get_ninja_jobs_options():
161 if _use_goma():
162 return ["-j1000"]
163 return []
164
165
Mohannad Farrag30d2c2c2024-02-14 18:27:28166def get_path_from_gn_label(gn_label: str) -> str:
167 """Returns the path part from a GN Label
168
169 GN label consist of two parts, path and target_name, this will
170 remove the target name and return the path or throw an error
171 if it can't remove the target_name or if it doesn't exist.
172 """
173 if ":" not in gn_label:
174 raise ValueError(f"Provided gn label {gn_label} is not a proper label")
175 return gn_label[:gn_label.find(":")]
176
177
Mohannad Farrag007862f2024-01-31 11:36:00178def _map_config_to_android_builder(is_release, target_cpu):
179 target_cpu_to_base_builder = {
180 'x86': 'android-cronet-x86',
181 'x64': 'android-cronet-x64',
182 'arm': 'android-cronet-arm',
183 'arm64': 'android-cronet-arm64',
184 'riscv64': 'android-cronet-riscv64',
185 }
186 if target_cpu not in target_cpu_to_base_builder:
187 raise ValueError('Unsupported target CPU')
188
189 builder_name = target_cpu_to_base_builder[target_cpu]
190 if is_release:
191 builder_name += '-rel'
192 else:
193 builder_name += '-dbg'
194 return builder_name
195
196
Mohannad Farrag30d2c2c2024-02-14 18:27:28197def _should_remove_arg(arg, keys):
198 """An arg is removed if its key appear in the list of |keys|"""
Mohannad Farrag185b76a2024-02-16 18:07:16199 return arg.split("=")[0].strip() in keys
Mohannad Farrag007862f2024-01-31 11:36:00200
Mohannad Farrag30d2c2c2024-02-14 18:27:28201
202def filter_gn_args(gn_args, keys_to_remove):
203 """Returns a list of filtered GN args.
204
205 (1) GN arg's returned must match the regex |_GN_ARG_MATCHER|.
206 (2) GN arg's key must not be in |keys_to_remove|.
207
208 Args:
209 gn_args: list of GN args.
210 keys_to_remove: List of string that will be removed from gn_args.
211 """
212 filtered_args = []
213 for arg in gn_args:
214 if _GN_ARG_MATCHER.match(arg) and not _should_remove_arg(
215 arg, keys_to_remove):
216 filtered_args.append(arg)
217 return filtered_args