diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index fab3d9cbeb..aa19bc96af 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -48,17 +48,29 @@ class FormatCommand extends PackageCommand { super.platform, }) { argParser.addFlag('fail-on-change', hide: true); - argParser.addOption(_clangFormatArg, + argParser.addFlag(_dartArg, help: 'Format Dart files', defaultsTo: true); + argParser.addFlag(_clangFormatArg, + help: 'Format with "clang-format"', defaultsTo: true); + argParser.addFlag(_kotlinArg, + help: 'Format Kotlin files', defaultsTo: true); + argParser.addFlag(_javaArg, help: 'Format Java files', defaultsTo: true); + argParser.addFlag(_swiftArg, help: 'Format Swift files'); + argParser.addOption(_clangFormatPathArg, defaultsTo: 'clang-format', help: 'Path to "clang-format" executable.'); - argParser.addOption(_javaArg, + argParser.addOption(_javaPathArg, defaultsTo: 'java', help: 'Path to "java" executable.'); - argParser.addOption(_swiftFormatArg, - help: 'Path to "swift-format" executable.'); + argParser.addOption(_swiftFormatPathArg, + defaultsTo: 'swift-format', help: 'Path to "swift-format" executable.'); } + static const String _dartArg = 'dart'; static const String _clangFormatArg = 'clang-format'; + static const String _kotlinArg = 'kotlin'; static const String _javaArg = 'java'; - static const String _swiftFormatArg = 'swift-format'; + static const String _swiftArg = 'swift'; + static const String _clangFormatPathArg = 'clang-format-path'; + static const String _javaPathArg = 'java-path'; + static const String _swiftFormatPathArg = 'swift-format-path'; @override final String name = 'format'; @@ -80,13 +92,20 @@ class FormatCommand extends PackageCommand { // due to the startup overhead of the formatters. final Iterable files = await _getFilteredFilePaths(getFiles(), relativeTo: packagesDir); - await _formatDart(files); - await _formatJava(files, javaFormatterPath); - await _formatKotlin(files, kotlinFormatterPath); - await _formatCppAndObjectiveC(files); - final String? swiftFormat = getNullableStringArg(_swiftFormatArg); - if (swiftFormat != null) { - await _formatSwift(swiftFormat, files); + if (getBoolArg(_dartArg)) { + await _formatDart(files); + } + if (getBoolArg(_javaArg)) { + await _formatJava(files, javaFormatterPath); + } + if (getBoolArg(_kotlinArg)) { + await _formatKotlin(files, kotlinFormatterPath); + } + if (getBoolArg(_clangFormatArg)) { + await _formatCppAndObjectiveC(files); + } + if (getBoolArg(_swiftArg)) { + await _formatSwift(files); } if (getBoolArg('fail-on-change')) { @@ -158,7 +177,8 @@ class FormatCommand extends PackageCommand { } } - Future _formatSwift(String swiftFormat, Iterable files) async { + Future _formatSwift(Iterable files) async { + final String swiftFormat = await _findValidSwiftFormat(); final Iterable swiftFiles = _getPathsWithExtensions(files, {'.swift'}); if (swiftFiles.isNotEmpty) { @@ -173,7 +193,7 @@ class FormatCommand extends PackageCommand { } Future _findValidClangFormat() async { - final String clangFormat = getStringArg(_clangFormatArg); + final String clangFormat = getStringArg(_clangFormatPathArg); if (await _hasDependency(clangFormat)) { return clangFormat; } @@ -188,7 +208,18 @@ class FormatCommand extends PackageCommand { } } printError('Unable to run "clang-format". Make sure that it is in your ' - 'path, or provide a full path with --clang-format.'); + 'path, or provide a full path with --$_clangFormatPathArg.'); + throw ToolExit(_exitDependencyMissing); + } + + Future _findValidSwiftFormat() async { + final String swiftFormat = getStringArg(_swiftFormatPathArg); + if (await _hasDependency(swiftFormat)) { + return swiftFormat; + } + + printError('Unable to run "swift-format". Make sure that it is in your ' + 'path, or provide a full path with --$_swiftFormatPathArg.'); throw ToolExit(_exitDependencyMissing); } @@ -196,11 +227,11 @@ class FormatCommand extends PackageCommand { final Iterable javaFiles = _getPathsWithExtensions(files, {'.java'}); if (javaFiles.isNotEmpty) { - final String java = getStringArg('java'); + final String java = getStringArg(_javaPathArg); if (!await _hasDependency(java)) { printError( 'Unable to run "java". Make sure that it is in your path, or ' - 'provide a full path with --java.'); + 'provide a full path with --$_javaPathArg.'); throw ToolExit(_exitDependencyMissing); } @@ -220,11 +251,11 @@ class FormatCommand extends PackageCommand { final Iterable kotlinFiles = _getPathsWithExtensions(files, {'.kt'}); if (kotlinFiles.isNotEmpty) { - final String java = getStringArg('java'); + final String java = getStringArg(_javaPathArg); if (!await _hasDependency(java)) { printError( 'Unable to run "java". Make sure that it is in your path, or ' - 'provide a full path with --java.'); + 'provide a full path with --$_javaPathArg.'); throw ToolExit(_exitDependencyMissing); } diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 881ef29449..73d1864322 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -169,6 +169,16 @@ void main() { ])); }); + test('skips dart if --no-dart flag is provided', () async { + const List files = [ + 'lib/a.dart', + ]; + createFakePlugin('a_plugin', packagesDir, extraFiles: files); + + await runCapturingPrint(runner, ['format', '--no-dart']); + expect(processRunner.recordedCalls, orderedEquals([])); + }); + test('formats .java files', () async { const List files = [ 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', @@ -220,7 +230,7 @@ void main() { containsAllInOrder([ contains( 'Unable to run "java". Make sure that it is in your path, or ' - 'provide a full path with --java.'), + 'provide a full path with --java-path.'), ])); }); @@ -250,7 +260,7 @@ void main() { ])); }); - test('honors --java flag', () async { + test('honors --java-path flag', () async { const List files = [ 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', 'android/src/main/java/io/flutter/plugins/a_plugin/b.java', @@ -261,7 +271,8 @@ void main() { extraFiles: files, ); - await runCapturingPrint(runner, ['format', '--java=/path/to/java']); + await runCapturingPrint( + runner, ['format', '--java-path=/path/to/java']); expect( processRunner.recordedCalls, @@ -279,6 +290,16 @@ void main() { ])); }); + test('skips Java if --no-java flag is provided', () async { + const List files = [ + 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', + ]; + createFakePlugin('a_plugin', packagesDir, extraFiles: files); + + await runCapturingPrint(runner, ['format', '--no-java']); + expect(processRunner.recordedCalls, orderedEquals([])); + }); + test('formats c-ish files', () async { const List files = [ 'ios/Classes/Foo.h', @@ -332,7 +353,7 @@ void main() { output, containsAllInOrder([ contains('Unable to run "clang-format". Make sure that it is in your ' - 'path, or provide a full path with --clang-format.'), + 'path, or provide a full path with --clang-format-path.'), ])); }); @@ -376,7 +397,7 @@ void main() { ])); }); - test('honors --clang-format flag', () async { + test('honors --clang-format-path flag', () async { const List files = [ 'windows/foo_plugin.cpp', ]; @@ -386,8 +407,8 @@ void main() { extraFiles: files, ); - await runCapturingPrint( - runner, ['format', '--clang-format=/path/to/clang-format']); + await runCapturingPrint(runner, + ['format', '--clang-format-path=/path/to/clang-format']); expect( processRunner.recordedCalls, @@ -433,6 +454,16 @@ void main() { ])); }); + test('skips clang-format if --no-clang-format flag is provided', () async { + const List files = [ + 'linux/foo_plugin.cc', + ]; + createFakePlugin('a_plugin', packagesDir, extraFiles: files); + + await runCapturingPrint(runner, ['format', '--no-clang-format']); + expect(processRunner.recordedCalls, orderedEquals([])); + }); + group('kotlin-format', () { test('formats .kt files', () async { const List files = [ @@ -487,6 +518,16 @@ void main() { contains('Failed to format Kotlin files: exit code 1.'), ])); }); + + test('skips Kotlin if --no-kotlin flag is provided', () async { + const List files = [ + 'android/src/main/kotlin/io/flutter/plugins/a_plugin/a.kt', + ]; + createFakePlugin('a_plugin', packagesDir, extraFiles: files); + + await runCapturingPrint(runner, ['format', '--no-kotlin']); + expect(processRunner.recordedCalls, orderedEquals([])); + }); }); group('swift-format', () { @@ -500,12 +541,17 @@ void main() { extraFiles: files, ); - await runCapturingPrint( - runner, ['format', '--swift-format=/path/to/swift-format']); + await runCapturingPrint(runner, [ + 'format', + '--swift', + '--swift-format-path=/path/to/swift-format' + ]); expect( processRunner.recordedCalls, orderedEquals([ + const ProcessCall( + '/path/to/swift-format', ['--version'], null), ProcessCall( '/path/to/swift-format', ['-i', ...getPackagesDirRelativePaths(plugin, files)], @@ -513,7 +559,7 @@ void main() { ])); }); - test('skips Swift if --swift-format flag is not provided', () async { + test('skips Swift if --swift flag is not provided', () async { const List files = [ 'macos/foo.swift', ]; @@ -528,6 +574,33 @@ void main() { expect(processRunner.recordedCalls, orderedEquals([])); }); + test('fails with a clear message if swift-format is not in the path', + () async { + const List files = [ + 'macos/foo.swift', + ]; + createFakePlugin('a_plugin', packagesDir, extraFiles: files); + + processRunner.mockProcessesForExecutable['swift-format'] = + [ + FakeProcessInfo(MockProcess(exitCode: 1), ['--version']), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['format', '--swift'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + 'Unable to run "swift-format". Make sure that it is in your path, or ' + 'provide a full path with --swift-format-path.'), + ])); + }); + test('fails if swift-format fails', () async { const List files = [ 'macos/foo.swift', @@ -536,12 +609,16 @@ void main() { processRunner.mockProcessesForExecutable['swift-format'] = [ + FakeProcessInfo(MockProcess(), + ['--version']), // check for working swift-format FakeProcessInfo(MockProcess(exitCode: 1), ['-i']), ]; Error? commandError; - final List output = await runCapturingPrint( - runner, ['format', '--swift-format=swift-format'], - errorHandler: (Error e) { + final List output = await runCapturingPrint(runner, [ + 'format', + '--swift', + '--swift-format-path=swift-format' + ], errorHandler: (Error e) { commandError = e; });