Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 3116c41

Browse files
authored
[et] Adds a format command (#50747)
For flutter/flutter#132807
1 parent 7002504 commit 3116c41

File tree

10 files changed

+538
-58
lines changed

10 files changed

+538
-58
lines changed

ci/bin/format.dart

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,9 @@ abstract class FormatChecker {
269269
...types,
270270
]);
271271
}
272-
return output.split('\n').where((String line) => line.isNotEmpty).toList();
272+
return output.split('\n').where(
273+
(String line) => line.isNotEmpty && !line.contains('third_party')
274+
).toList();
273275
}
274276

275277
/// Generates a reporting function to supply to ProcessRunner to use instead
@@ -285,7 +287,7 @@ abstract class FormatChecker {
285287
final String pendingStr = pending.toString().padLeft(3);
286288
final String failedStr = failed.toString().padLeft(3);
287289

288-
stderr.write('$name Jobs: $percent% done, '
290+
stdout.write('$name Jobs: $percent% done, '
289291
'$completedStr/$totalStr completed, '
290292
'$inProgressStr in progress, '
291293
'$pendingStr pending, '
@@ -296,7 +298,7 @@ abstract class FormatChecker {
296298
/// Clears the last printed report line so garbage isn't left on the terminal.
297299
@protected
298300
void reportDone() {
299-
stderr.write('\r${' ' * 100}\r');
301+
stdout.write('\r${' ' * 100}\r');
300302
}
301303
}
302304

@@ -436,7 +438,7 @@ class ClangFormatChecker extends FormatChecker {
436438
} else {
437439
error('Found ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}'
438440
' which ${plural ? 'were' : 'was'} formatted incorrectly.');
439-
stdout.writeln('To fix, run:');
441+
stdout.writeln('To fix, run `et format` or:');
440442
stdout.writeln();
441443
stdout.writeln('git apply <<DONE');
442444
for (final WorkerJob job in failed) {
@@ -594,7 +596,7 @@ class JavaFormatChecker extends FormatChecker {
594596
} else {
595597
error('Found ${failed.length} Java file${plural ? 's' : ''}'
596598
' which ${plural ? 'were' : 'was'} formatted incorrectly.');
597-
stdout.writeln('To fix, run:');
599+
stdout.writeln('To fix, run `et format` or:');
598600
stdout.writeln();
599601
stdout.writeln('git apply <<DONE');
600602
for (final WorkerJob job in failed) {
@@ -727,7 +729,7 @@ class GnFormatChecker extends FormatChecker {
727729
} else {
728730
error('Found ${failed.length} GN file${plural ? 's' : ''}'
729731
' which ${plural ? 'were' : 'was'} formatted incorrectly.');
730-
stdout.writeln('To fix, run:');
732+
stdout.writeln('To fix, run `et format` or:');
731733
stdout.writeln();
732734
stdout.writeln('git apply <<DONE');
733735
for (final WorkerJob job in failed) {
@@ -822,7 +824,12 @@ class PythonFormatChecker extends FormatChecker {
822824
} else {
823825
error('Found ${incorrect.length} python file${plural ? 's' : ''}'
824826
' which ${plural ? 'were' : 'was'} formatted incorrectly:');
825-
incorrect.forEach(stderr.writeln);
827+
stdout.writeln('To fix, run `et format` or:');
828+
stdout.writeln();
829+
stdout.writeln('git apply <<DONE');
830+
incorrect.forEach(stdout.writeln);
831+
stdout.writeln('DONE');
832+
stdout.writeln();
826833
}
827834
} else {
828835
message('All python files formatted correctly.');
@@ -1129,7 +1136,7 @@ Future<int> main(List<String> arguments) async {
11291136
message ??= '';
11301137
switch (type) {
11311138
case MessageType.message:
1132-
stderr.writeln(message);
1139+
stdout.writeln(message);
11331140
case MessageType.error:
11341141
stderr.writeln('ERROR: $message');
11351142
case MessageType.warning:
@@ -1160,11 +1167,7 @@ Future<int> main(List<String> arguments) async {
11601167
message('Unable to apply $humanCheckName format fixes.');
11611168
}
11621169
} else {
1163-
message('Performing $humanCheckName format check');
11641170
stepResult = await checker.checkFormatting();
1165-
if (!stepResult) {
1166-
message('Found $humanCheckName format problems.');
1167-
}
11681171
}
11691172
result = result && stepResult;
11701173
}

tools/engine_tool/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ before it will work.
1414
The tool has the following commands.
1515

1616
* `help` - Prints helpful information about commands and usage.
17+
* `format` - Formats files in the engine tree using various off-the-shelf
18+
formatters.
1719
* `query builds` - Lists the CI builds described under `ci/builders` that the
1820
host platform is capable of executing.
1921

tools/engine_tool/lib/src/commands/command_runner.dart

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import 'package:args/command_runner.dart';
77
import 'package:engine_build_configs/engine_build_configs.dart';
88

99
import '../environment.dart';
10+
import 'command.dart';
11+
import 'format_command.dart';
1012
import 'query_command.dart';
1113

1214
/// The root command runner.
@@ -17,10 +19,16 @@ final class ToolCommandRunner extends CommandRunner<int> {
1719
required this.environment,
1820
required this.configs,
1921
}) : super(toolName, toolDescription) {
20-
addCommand(QueryCommand(
21-
environment: environment,
22-
configs: configs,
23-
));
22+
final List<CommandBase> commands = <CommandBase>[
23+
FormatCommand(
24+
environment: environment,
25+
),
26+
QueryCommand(
27+
environment: environment,
28+
configs: configs,
29+
),
30+
];
31+
commands.forEach(addCommand);
2432
}
2533

2634
/// The name of the tool as reported in the tool's usage and help
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2013 The Flutter 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+
5+
// ignore_for_file: public_member_api_docs
6+
7+
// The purpose of this list of flags in a file separate from the command
8+
// definitions is to ensure that flags are named consistently across
9+
// subcommands. For example, unless there's a compelling reason to have both,
10+
// we'd avoid having one subcommand define an --all flag while another defines
11+
// an --everything flag.
12+
13+
// Keep this list alphabetized.
14+
const String allFlag = 'all';
15+
const String builderFlag = 'builder';
16+
const String dryRunFlag = 'dry-run';
17+
const String quietFlag = 'quiet';
18+
const String verboseFlag = 'verbose';
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright 2013 The Flutter 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+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io' as io;
8+
9+
import 'package:path/path.dart' as p;
10+
11+
import '../logger.dart';
12+
import 'command.dart';
13+
import 'flags.dart';
14+
15+
/// The 'format' command.
16+
///
17+
/// The format command implementation below works by spawning another Dart VM to
18+
/// run the program under ci/bin/format.dart.
19+
///
20+
// TODO(team-engine): Part of https://github.com/flutter/flutter/issues/132807.
21+
// Instead, format.dart should be moved under the engine_tool package and
22+
// invoked by a function call. The file ci/bin/format.dart should be split up so
23+
// that each of its `FormatCheckers` is in a separate file under src/formatters,
24+
// and they should be unit-tested.
25+
final class FormatCommand extends CommandBase {
26+
// ignore: public_member_api_docs
27+
FormatCommand({
28+
required super.environment,
29+
}) {
30+
argParser
31+
..addFlag(
32+
allFlag,
33+
abbr: 'a',
34+
help: 'By default only dirty files are checked. This flag causes all '
35+
'files to be checked. (Slow)',
36+
negatable: false,
37+
)
38+
..addFlag(
39+
dryRunFlag,
40+
abbr: 'd',
41+
help: 'Do not fix formatting in-place. Instead, print file diffs to '
42+
'the logs.',
43+
negatable: false,
44+
)
45+
..addFlag(
46+
quietFlag,
47+
abbr: 'q',
48+
help: 'Silences all log messages except for errors and warnings',
49+
negatable: false,
50+
)
51+
..addFlag(
52+
verboseFlag,
53+
abbr: 'v',
54+
help: 'Prints verbose output',
55+
negatable: false,
56+
);
57+
}
58+
59+
@override
60+
String get name => 'format';
61+
62+
@override
63+
String get description => 'Formats files using standard formatters and styles.';
64+
65+
@override
66+
Future<int> run() async {
67+
final bool all = argResults![allFlag]! as bool;
68+
final bool dryRun = argResults![dryRunFlag]! as bool;
69+
final bool quiet = argResults![quietFlag]! as bool;
70+
final bool verbose = argResults![verboseFlag]! as bool;
71+
final String formatPath = p.join(
72+
environment.engine.flutterDir.path, 'ci', 'bin', 'format.dart',
73+
);
74+
75+
final io.Process process = await environment.processRunner.processManager.start(
76+
<String>[
77+
environment.platform.resolvedExecutable,
78+
formatPath,
79+
if (all) '--all-files',
80+
if (!dryRun) '--fix',
81+
if (verbose) '--verbose',
82+
],
83+
workingDirectory: environment.engine.flutterDir.path,
84+
);
85+
final Completer<void> stdoutComplete = Completer<void>();
86+
final Completer<void> stderrComplete = Completer<void>();
87+
88+
final _FormatStreamer streamer = _FormatStreamer(
89+
environment.logger,
90+
dryRun,
91+
quiet,
92+
);
93+
process.stdout
94+
.transform<String>(const Utf8Decoder())
95+
.transform(const LineSplitter())
96+
.listen(
97+
streamer.nextStdout,
98+
onDone: () async => stdoutComplete.complete(),
99+
);
100+
process.stderr
101+
.transform<String>(const Utf8Decoder())
102+
.transform(const LineSplitter())
103+
.listen(
104+
streamer.nextStderr,
105+
onDone: () async => stderrComplete.complete(),
106+
);
107+
108+
await Future.wait<void>(<Future<void>>[
109+
stdoutComplete.future, stderrComplete.future,
110+
]);
111+
final int exitCode = await process.exitCode;
112+
113+
return exitCode;
114+
}
115+
}
116+
117+
class _FormatStreamer {
118+
_FormatStreamer(this.logger, this.dryRun, this.quiet);
119+
120+
final Logger logger;
121+
final bool dryRun;
122+
final bool quiet;
123+
124+
bool inADiff = false;
125+
bool inProgress = false;
126+
127+
void nextStdout(String line) {
128+
if (quiet) {
129+
return;
130+
}
131+
final String l = line.trim();
132+
if (l == 'To fix, run `et format` or:') {
133+
inADiff = true;
134+
}
135+
if (l.isNotEmpty && (!inADiff || dryRun)) {
136+
if (_isProgressLine(l)) {
137+
inProgress = true;
138+
logger.clearLine();
139+
logger.status('$l\r', newline: false);
140+
} else {
141+
if (inProgress) {
142+
logger.clearLine();
143+
inProgress = false;
144+
}
145+
logger.status(l);
146+
}
147+
}
148+
if (l == 'DONE') {
149+
inADiff = false;
150+
}
151+
}
152+
153+
void nextStderr(String line) {
154+
final String l = line.trim();
155+
if (l.isEmpty) {
156+
return;
157+
}
158+
logger.error(l);
159+
}
160+
161+
bool _isProgressLine(String l) {
162+
final List<String> words = l.split(',');
163+
return words.isNotEmpty && words[0].endsWith('% done');
164+
}
165+
}

tools/engine_tool/lib/src/commands/query_command.dart

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,25 @@
55
import 'package:engine_build_configs/engine_build_configs.dart';
66

77
import 'command.dart';
8+
import 'flags.dart';
89

9-
const String _allFlag = 'all';
10-
const String _builderFlag = 'builder';
11-
const String _verboseFlag = 'verbose';
12-
13-
/// The root 'query' command.
10+
// ignore: public_member_api_docs
1411
final class QueryCommand extends CommandBase {
15-
/// Constructs the 'query' command.
12+
// ignore: public_member_api_docs
1613
QueryCommand({
1714
required super.environment,
1815
required this.configs,
1916
}) {
2017
// Add options here that are common to all queries.
2118
argParser
2219
..addFlag(
23-
_allFlag,
20+
allFlag,
2421
abbr: 'a',
2522
help: 'List all results, even when not relevant on this platform',
2623
negatable: false,
2724
)
2825
..addOption(
29-
_builderFlag,
26+
builderFlag,
3027
abbr: 'b',
3128
help: 'Restrict the query to a single builder.',
3229
allowed: <String>[
@@ -42,7 +39,7 @@ final class QueryCommand extends CommandBase {
4239
},
4340
)
4441
..addFlag(
45-
_verboseFlag,
42+
verboseFlag,
4643
abbr: 'v',
4744
help: 'Respond to queries with extra information',
4845
negatable: false,
@@ -65,9 +62,9 @@ final class QueryCommand extends CommandBase {
6562
'and tests.';
6663
}
6764

68-
/// The 'query builds' command.
65+
// ignore: public_member_api_docs
6966
final class QueryBuildsCommand extends CommandBase {
70-
/// Constructs the 'query build' command.
67+
// ignore: public_member_api_docs
7168
QueryBuildsCommand({
7269
required super.environment,
7370
required this.configs,
@@ -87,9 +84,9 @@ final class QueryBuildsCommand extends CommandBase {
8784
Future<int> run() async {
8885
// Loop through all configs, and log those that are compatible with the
8986
// current platform.
90-
final bool all = parent!.argResults![_allFlag]! as bool;
91-
final String? builderName = parent!.argResults![_builderFlag] as String?;
92-
final bool verbose = parent!.argResults![_verboseFlag] as bool;
87+
final bool all = parent!.argResults![allFlag]! as bool;
88+
final String? builderName = parent!.argResults![builderFlag] as String?;
89+
final bool verbose = parent!.argResults![verboseFlag] as bool;
9390
if (!verbose) {
9491
environment.logger.status(
9592
'Add --verbose to see detailed information about each builder',

0 commit comments

Comments
 (0)