Skip to content

Commit 3dfc2cb

Browse files
authored
Adds script to run malioc locally (flutter#185371)
adds script to help running malioc locally ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent 2000a5a commit 3dfc2cb

1 file changed

Lines changed: 238 additions & 0 deletions

File tree

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#!/usr/bin/env dart
2+
// Copyright 2013 The Flutter Authors. All rights reserved.
3+
// Use of this source code is governed by a BSD-style license that can be
4+
// found in the LICENSE file.
5+
6+
// This script automates the process of downloading the `malioc` (Mali Offline Compiler)
7+
// tools, running the GN and Ninja builds to generate shader performance statistics,
8+
// and diffing the results against the golden file (`malioc.json`).
9+
//
10+
// By default, it extracts the version of `malioc` specified in
11+
// `ci/builders/linux_unopt.json`, downloads it to a temporary directory, and
12+
// automatically cleans it up when finished.
13+
//
14+
// Usage:
15+
// dart flutter/impeller/tools/malioc_download_and_diff.dart [options]
16+
//
17+
// Options:
18+
// --config=<config> The build configuration (default: android_debug_unopt).
19+
// --target=<target> The Ninja target to build (default: impeller).
20+
// --malioc-path=<path> Use a local `malioc` binary instead of downloading.
21+
// --update Update the golden file with the new results.
22+
23+
// ignore_for_file: avoid_print
24+
25+
import 'dart:convert';
26+
import 'dart:io';
27+
28+
class Options {
29+
Options(this.config, this.target, this.update, this.maliocPath);
30+
final String config;
31+
final String target;
32+
final bool update;
33+
final String? maliocPath;
34+
}
35+
36+
void main(List<String> arguments) async {
37+
if (!Platform.isLinux) {
38+
print('Error: This script is only supported on Linux.');
39+
exit(1);
40+
}
41+
final Options options = parseArgs(arguments);
42+
43+
final scriptFile = File(Platform.script.toFilePath());
44+
final Directory toolsDir = scriptFile.parent;
45+
final Directory impellerDir = toolsDir.parent;
46+
final Directory flutterDir = impellerDir.parent;
47+
final Directory srcRoot = flutterDir.parent;
48+
49+
Directory? tempDir;
50+
String? maliocPath = options.maliocPath;
51+
52+
try {
53+
if (maliocPath == null) {
54+
tempDir = Directory.systemTemp.createTempSync('malioc_tools_');
55+
final String armToolsVersion = await getArmToolsVersion(flutterDir);
56+
print('Using arm_tools version: $armToolsVersion');
57+
await downloadMalioc(tempDir, armToolsVersion);
58+
maliocPath = await findMalioc(tempDir);
59+
}
60+
print('Using malioc at: $maliocPath');
61+
62+
await runGN(flutterDir, srcRoot, options.config, maliocPath);
63+
await runNinja(srcRoot, options.config, options.target);
64+
await runDiff(flutterDir, srcRoot, options.config, options.update);
65+
} finally {
66+
if (tempDir != null && tempDir.existsSync()) {
67+
print('Cleaning up temporary directory: ${tempDir.path}');
68+
await tempDir.delete(recursive: true);
69+
}
70+
}
71+
}
72+
73+
Options parseArgs(List<String> arguments) {
74+
var config = 'android_debug_unopt';
75+
var target = 'impeller';
76+
var update = false;
77+
String? maliocPath;
78+
79+
for (var i = 0; i < arguments.length; i++) {
80+
final String arg = arguments[i];
81+
if (arg == '--config' && i + 1 < arguments.length) {
82+
config = arguments[++i];
83+
} else if (arg.startsWith('--config=')) {
84+
config = arg.substring('--config='.length);
85+
} else if (arg == '--target' && i + 1 < arguments.length) {
86+
target = arguments[++i];
87+
} else if (arg.startsWith('--target=')) {
88+
target = arg.substring('--target='.length);
89+
} else if (arg == '--malioc-path' && i + 1 < arguments.length) {
90+
maliocPath = arguments[++i];
91+
} else if (arg.startsWith('--malioc-path=')) {
92+
maliocPath = arg.substring('--malioc-path='.length);
93+
} else if (arg == '--update') {
94+
update = true;
95+
} else if (arg == '--help' || arg == '-h') {
96+
print(
97+
'Usage: dart malioc_download_and_diff.dart [--config=<config>] [--target=<target>] [--malioc-path=<path>] [--update]',
98+
);
99+
exit(0);
100+
}
101+
}
102+
return Options(config, target, update, maliocPath);
103+
}
104+
105+
Future<String> getArmToolsVersion(Directory flutterDir) async {
106+
final jsonFile = File('${flutterDir.path}/ci/builders/linux_unopt.json');
107+
if (!jsonFile.existsSync()) {
108+
return 'last_updated:2023-02-03T15:32:01-0800';
109+
}
110+
try {
111+
final String content = await jsonFile.readAsString();
112+
final json = jsonDecode(content) as Map<String, dynamic>;
113+
final builds = json['builds'] as List<dynamic>;
114+
for (final build in builds) {
115+
if (build is Map<String, dynamic> && build['name'] == 'ci/android_debug_unopt') {
116+
final deps = build['dependencies'] as List<dynamic>?;
117+
if (deps != null) {
118+
for (final Object? dep in deps) {
119+
if (dep is Map<String, dynamic> && dep['dependency'] == 'arm_tools') {
120+
return dep['version'] as String;
121+
}
122+
}
123+
}
124+
}
125+
}
126+
} catch (e) {
127+
print('Error parsing ${jsonFile.path}: $e');
128+
}
129+
return 'last_updated:2023-02-03T15:32:01-0800';
130+
}
131+
132+
Future<void> downloadMalioc(Directory armToolsDir, String version) async {
133+
if (!armToolsDir.existsSync()) {
134+
await armToolsDir.create(recursive: true);
135+
}
136+
137+
print('Initializing CIPD in ${armToolsDir.path}...');
138+
final Process cipdInitProcess = await Process.start(
139+
'cipd',
140+
['init'],
141+
workingDirectory: armToolsDir.path,
142+
mode: ProcessStartMode.inheritStdio,
143+
);
144+
final int initExitCode = await cipdInitProcess.exitCode;
145+
if (initExitCode != 0) {
146+
throw Exception('Error running cipd init: $initExitCode');
147+
}
148+
149+
print('Downloading malioc tools to ${armToolsDir.path}...');
150+
final Process cipdProcess = await Process.start('cipd', [
151+
'install',
152+
'flutter_internal/tools/arm-tools',
153+
version,
154+
'-root',
155+
armToolsDir.path,
156+
], mode: ProcessStartMode.inheritStdio);
157+
final int exitCode = await cipdProcess.exitCode;
158+
if (exitCode != 0) {
159+
throw Exception('Error running cipd install: $exitCode');
160+
}
161+
}
162+
163+
Future<String> findMalioc(Directory armToolsDir) async {
164+
if (armToolsDir.existsSync()) {
165+
await for (final FileSystemEntity entity in armToolsDir.list(recursive: true)) {
166+
if (entity is File && entity.path.endsWith('/malioc')) {
167+
return entity.path;
168+
}
169+
}
170+
}
171+
throw Exception('Could not find malioc executable in downloaded tools.');
172+
}
173+
174+
Future<void> runGN(
175+
Directory flutterDir,
176+
Directory srcRoot,
177+
String config,
178+
String maliocPath,
179+
) async {
180+
print('Running GN...');
181+
final gnArgs = ['--malioc-path', maliocPath];
182+
if (config == 'android_debug_unopt') {
183+
gnArgs.addAll(['--android', '--unoptimized']);
184+
} else if (config == 'host_debug_unopt') {
185+
gnArgs.add('--unoptimized');
186+
}
187+
188+
final Process process = await Process.start(
189+
'${flutterDir.path}/tools/gn',
190+
gnArgs,
191+
workingDirectory: srcRoot.path,
192+
mode: ProcessStartMode.inheritStdio,
193+
);
194+
final int exitCode = await process.exitCode;
195+
if (exitCode != 0) {
196+
throw Exception('Error running GN: $exitCode');
197+
}
198+
}
199+
200+
Future<void> runNinja(Directory srcRoot, String config, String target) async {
201+
print("Building target '$target' in $config...");
202+
final Process process = await Process.start(
203+
'ninja',
204+
['-C', '${srcRoot.path}/out/$config', target],
205+
workingDirectory: srcRoot.path,
206+
mode: ProcessStartMode.inheritStdio,
207+
);
208+
final int exitCode = await process.exitCode;
209+
if (exitCode != 0) {
210+
throw Exception('Error running Ninja: $exitCode');
211+
}
212+
}
213+
214+
Future<void> runDiff(Directory flutterDir, Directory srcRoot, String config, bool update) async {
215+
print('Generating diff...');
216+
final diffArgs = [
217+
'${flutterDir.path}/impeller/tools/malioc_diff.py',
218+
'--before',
219+
'${flutterDir.path}/impeller/tools/malioc.json',
220+
'--after',
221+
'${srcRoot.path}/out/$config/gen/malioc',
222+
'--print-diff',
223+
];
224+
if (update) {
225+
diffArgs.add('--update');
226+
}
227+
228+
final Process process = await Process.start(
229+
'python3',
230+
diffArgs,
231+
workingDirectory: srcRoot.path,
232+
mode: ProcessStartMode.inheritStdio,
233+
);
234+
final int exitCode = await process.exitCode;
235+
if (exitCode != 0) {
236+
throw Exception('Error running diff: $exitCode');
237+
}
238+
}

0 commit comments

Comments
 (0)