mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Android lint to NNBD (flutter/engine#26826)
This commit is contained in:
parent
fddf12de3f
commit
01bf2ce3d7
@ -2,11 +2,227 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart=2.9
|
||||
import 'dart:io';
|
||||
|
||||
import 'sound_main.dart' as m;
|
||||
import 'package:args/args.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:process/process.dart';
|
||||
|
||||
// TODO(dnfield): Migrate the deps in pubspec.yaml to null-safety versions.
|
||||
// In particular see the ongoing work on package:args here:
|
||||
// https://github.com/flutter/flutter/issues/72542
|
||||
Future<void> main(List<String> args) => m.main(args);
|
||||
const LocalProcessManager processManager = LocalProcessManager();
|
||||
|
||||
/// Runs the Android SDK Lint tool on flutter/shell/platform/android.
|
||||
///
|
||||
/// This script scans the flutter/shell/platform/android directory for Java
|
||||
/// files to build a `project.xml` file. This file is then passed to the lint
|
||||
/// tool. If an `--html` flag is also passed in, HTML output is reqeusted in the
|
||||
/// directory for the optional `--out` parameter, which defaults to
|
||||
/// `lint_report`. Otherwise the output is printed to STDOUT.
|
||||
///
|
||||
/// The `--in` parameter may be specified to force this script to scan a
|
||||
/// specific location for the engine repository, and expects to be given the
|
||||
/// `src` directory that contains both `third_party` and `flutter`.
|
||||
///
|
||||
/// At the time of this writing, the Android Lint tool doesn't work well with
|
||||
/// Java > 1.8. This script will print a warning if you are not running
|
||||
/// Java 1.8.
|
||||
Future<void> main(List<String> args) async {
|
||||
final ArgParser argParser = setupOptions();
|
||||
await checkJava1_8();
|
||||
final int exitCode = await runLint(argParser, argParser.parse(args));
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
Future<int> runLint(ArgParser argParser, ArgResults argResults) async {
|
||||
final String inArgument = argResults['in'] as String;
|
||||
final Directory androidDir = Directory(path.join(
|
||||
inArgument,
|
||||
'flutter',
|
||||
'shell',
|
||||
'platform',
|
||||
'android',
|
||||
));
|
||||
if (!androidDir.existsSync()) {
|
||||
print('This command must be run from the engine/src directory, '
|
||||
'or be passed that directory as the --in parameter.\n');
|
||||
print(argParser.usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
final Directory androidSdkDir = Directory(
|
||||
path.join(inArgument, 'third_party', 'android_tools', 'sdk'),
|
||||
);
|
||||
|
||||
if (!androidSdkDir.existsSync()) {
|
||||
print('The Android SDK for this engine is missing from the '
|
||||
'third_party/android_tools directory. Have you run gclient sync?\n');
|
||||
print(argParser.usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
final bool rebaseline = argResults['rebaseline'] as bool;
|
||||
if (rebaseline) {
|
||||
print('Removing previous baseline.xml...');
|
||||
final File baselineXml = File(baselineXmlPath);
|
||||
if (baselineXml.existsSync()) {
|
||||
await baselineXml.delete();
|
||||
}
|
||||
}
|
||||
print('Preparing project.xml...');
|
||||
final IOSink projectXml = File(projectXmlPath).openWrite();
|
||||
projectXml.write('''
|
||||
<!-- THIS FILE IS GENERATED. PLEASE USE THE INCLUDED DART PROGRAM WHICH -->
|
||||
<!-- WILL AUTOMATICALLY FIND ALL .java FILES AND INCLUDE THEM HERE -->
|
||||
<project>
|
||||
<sdk dir="${androidSdkDir.path}" />
|
||||
<module name="FlutterEngine" android="true" library="true" compile-sdk-version="android-P">
|
||||
<manifest file="${path.join(androidDir.path, 'AndroidManifest.xml')}" />
|
||||
''');
|
||||
for (final FileSystemEntity entity in androidDir.listSync(recursive: true)) {
|
||||
if (!entity.path.endsWith('.java')) {
|
||||
continue;
|
||||
}
|
||||
projectXml.writeln(' <src file="${entity.path}" />');
|
||||
}
|
||||
|
||||
projectXml.write('''
|
||||
</module>
|
||||
</project>
|
||||
''');
|
||||
await projectXml.close();
|
||||
|
||||
print('Wrote project.xml, starting lint...');
|
||||
final List<String> lintArgs = <String>[
|
||||
path.join(androidSdkDir.path, 'tools', 'bin', 'lint'),
|
||||
'--project',
|
||||
projectXmlPath,
|
||||
'--showall',
|
||||
'--exitcode', // Set non-zero exit code on errors
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'--baseline',
|
||||
baselineXmlPath,
|
||||
];
|
||||
final bool html = argResults['html'] as bool;
|
||||
if (html) {
|
||||
lintArgs.addAll(<String>['--html', argResults['out'] as String]);
|
||||
}
|
||||
final String? javaHome = await getJavaHome();
|
||||
final Process lintProcess = await processManager.start(
|
||||
lintArgs,
|
||||
environment: javaHome != null
|
||||
? <String, String>{
|
||||
'JAVA_HOME': javaHome,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
lintProcess.stdout.pipe(stdout);
|
||||
lintProcess.stderr.pipe(stderr);
|
||||
return lintProcess.exitCode;
|
||||
}
|
||||
|
||||
/// Prepares an [ArgParser] for this script.
|
||||
ArgParser setupOptions() {
|
||||
final ArgParser argParser = ArgParser();
|
||||
argParser
|
||||
..addOption(
|
||||
'in',
|
||||
help: 'The path to `engine/src`.',
|
||||
defaultsTo: path.relative(
|
||||
path.join(
|
||||
projectDir,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
),
|
||||
),
|
||||
)
|
||||
..addFlag(
|
||||
'help',
|
||||
help: 'Print usage of the command.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'rebaseline',
|
||||
help: 'Recalculates the baseline for errors and warnings '
|
||||
'in this project.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'html',
|
||||
help: 'Creates an HTML output for this report instead of printing '
|
||||
'command line output.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addOption(
|
||||
'out',
|
||||
help: 'The path to write the generated HTML report. Ignored if '
|
||||
'--html is not also true.',
|
||||
defaultsTo: path.join(projectDir, 'lint_report'),
|
||||
);
|
||||
|
||||
return argParser;
|
||||
}
|
||||
|
||||
/// On macOS, we can try to find Java 1.8.
|
||||
///
|
||||
/// Otherwise, default to whatever JAVA_HOME is already.
|
||||
Future<String?> getJavaHome() async {
|
||||
if (Platform.isMacOS) {
|
||||
final ProcessResult result = await processManager.run(
|
||||
<String>['/usr/libexec/java_home', '-v', '1.8', '-F'],
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
return (result.stdout as String).trim();
|
||||
}
|
||||
}
|
||||
return Platform.environment['JAVA_HOME'];
|
||||
}
|
||||
|
||||
/// Checks that `java` points to Java 1.8.
|
||||
///
|
||||
/// The SDK lint tool may not work with Java > 1.8.
|
||||
Future<void> checkJava1_8() async {
|
||||
print('Checking Java version...');
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
final ProcessResult result = await processManager.run(
|
||||
<String>['/usr/libexec/java_home', '-v', '1.8', '-F'],
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
print('Java 1.8 not available - the linter may not work properly.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
final ProcessResult javaResult = await processManager.run(
|
||||
<String>['java', '-version'],
|
||||
);
|
||||
if (javaResult.exitCode != 0) {
|
||||
print('Could not run "java -version". '
|
||||
'Ensure Java is installed and available on your path.');
|
||||
print(javaResult.stderr);
|
||||
}
|
||||
// `java -version` writes to stderr.
|
||||
final String javaVersionStdout = javaResult.stderr as String;
|
||||
if (!javaVersionStdout.contains('"1.8')) {
|
||||
print('The Android SDK tools may not work properly with your Java version. '
|
||||
'If this process fails, please retry using Java 1.8.');
|
||||
}
|
||||
}
|
||||
|
||||
/// The root directory of this project.
|
||||
String get projectDir => path.dirname(
|
||||
path.dirname(
|
||||
path.fromUri(Platform.script),
|
||||
),
|
||||
);
|
||||
|
||||
/// The path to use for project.xml, which tells the linter where to find source
|
||||
/// files.
|
||||
String get projectXmlPath => path.join(projectDir, 'project.xml');
|
||||
|
||||
/// The path to use for baseline.xml, which tells the linter what errors or
|
||||
/// warnings to ignore.
|
||||
String get baselineXmlPath => path.join(projectDir, 'baseline.xml');
|
||||
|
||||
@ -1,228 +0,0 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:process/process.dart';
|
||||
|
||||
const LocalProcessManager processManager = LocalProcessManager();
|
||||
|
||||
/// Runs the Android SDK Lint tool on flutter/shell/platform/android.
|
||||
///
|
||||
/// This script scans the flutter/shell/platform/android directory for Java
|
||||
/// files to build a `project.xml` file. This file is then passed to the lint
|
||||
/// tool. If an `--html` flag is also passed in, HTML output is reqeusted in the
|
||||
/// directory for the optional `--out` parameter, which defaults to
|
||||
/// `lint_report`. Otherwise the output is printed to STDOUT.
|
||||
///
|
||||
/// The `--in` parameter may be specified to force this script to scan a
|
||||
/// specific location for the engine repository, and expects to be given the
|
||||
/// `src` directory that contains both `third_party` and `flutter`.
|
||||
///
|
||||
/// At the time of this writing, the Android Lint tool doesn't work well with
|
||||
/// Java > 1.8. This script will print a warning if you are not running
|
||||
/// Java 1.8.
|
||||
Future<void> main(List<String> args) async {
|
||||
final ArgParser argParser = setupOptions();
|
||||
await checkJava1_8();
|
||||
final int exitCode = await runLint(argParser, argParser.parse(args));
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
Future<int> runLint(ArgParser argParser, ArgResults argResults) async {
|
||||
final String inArgument = argResults['in'] as String;
|
||||
final Directory androidDir = Directory(path.join(
|
||||
inArgument,
|
||||
'flutter',
|
||||
'shell',
|
||||
'platform',
|
||||
'android',
|
||||
));
|
||||
if (!androidDir.existsSync()) {
|
||||
print('This command must be run from the engine/src directory, '
|
||||
'or be passed that directory as the --in parameter.\n');
|
||||
print(argParser.usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
final Directory androidSdkDir = Directory(
|
||||
path.join(inArgument, 'third_party', 'android_tools', 'sdk'),
|
||||
);
|
||||
|
||||
if (!androidSdkDir.existsSync()) {
|
||||
print('The Android SDK for this engine is missing from the '
|
||||
'third_party/android_tools directory. Have you run gclient sync?\n');
|
||||
print(argParser.usage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
final bool rebaseline = argResults['rebaseline'] as bool;
|
||||
if (rebaseline) {
|
||||
print('Removing previous baseline.xml...');
|
||||
final File baselineXml = File(baselineXmlPath);
|
||||
if (baselineXml.existsSync()) {
|
||||
await baselineXml.delete();
|
||||
}
|
||||
}
|
||||
print('Preparing project.xml...');
|
||||
final IOSink projectXml = File(projectXmlPath).openWrite();
|
||||
projectXml.write('''
|
||||
<!-- THIS FILE IS GENERATED. PLEASE USE THE INCLUDED DART PROGRAM WHICH -->
|
||||
<!-- WILL AUTOMATICALLY FIND ALL .java FILES AND INCLUDE THEM HERE -->
|
||||
<project>
|
||||
<sdk dir="${androidSdkDir.path}" />
|
||||
<module name="FlutterEngine" android="true" library="true" compile-sdk-version="android-P">
|
||||
<manifest file="${path.join(androidDir.path, 'AndroidManifest.xml')}" />
|
||||
''');
|
||||
for (final FileSystemEntity entity in androidDir.listSync(recursive: true)) {
|
||||
if (!entity.path.endsWith('.java')) {
|
||||
continue;
|
||||
}
|
||||
projectXml.writeln(' <src file="${entity.path}" />');
|
||||
}
|
||||
|
||||
projectXml.write('''
|
||||
</module>
|
||||
</project>
|
||||
''');
|
||||
await projectXml.close();
|
||||
|
||||
print('Wrote project.xml, starting lint...');
|
||||
final List<String> lintArgs = <String>[
|
||||
path.join(androidSdkDir.path, 'tools', 'bin', 'lint'),
|
||||
'--project',
|
||||
projectXmlPath,
|
||||
'--showall',
|
||||
'--exitcode', // Set non-zero exit code on errors
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'--baseline',
|
||||
baselineXmlPath,
|
||||
];
|
||||
final bool html = argResults['html'] as bool;
|
||||
if (html) {
|
||||
lintArgs.addAll(<String>['--html', argResults['out'] as String]);
|
||||
}
|
||||
final String? javaHome = await getJavaHome();
|
||||
final Process lintProcess = await processManager.start(
|
||||
lintArgs,
|
||||
environment: javaHome != null
|
||||
? <String, String>{
|
||||
'JAVA_HOME': javaHome,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
lintProcess.stdout.pipe(stdout);
|
||||
lintProcess.stderr.pipe(stderr);
|
||||
return lintProcess.exitCode;
|
||||
}
|
||||
|
||||
/// Prepares an [ArgParser] for this script.
|
||||
ArgParser setupOptions() {
|
||||
final ArgParser argParser = ArgParser();
|
||||
argParser
|
||||
..addOption(
|
||||
'in',
|
||||
help: 'The path to `engine/src`.',
|
||||
defaultsTo: path.relative(
|
||||
path.join(
|
||||
projectDir,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
),
|
||||
),
|
||||
)
|
||||
..addFlag(
|
||||
'help',
|
||||
help: 'Print usage of the command.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'rebaseline',
|
||||
help: 'Recalculates the baseline for errors and warnings '
|
||||
'in this project.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'html',
|
||||
help: 'Creates an HTML output for this report instead of printing '
|
||||
'command line output.',
|
||||
negatable: false,
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addOption(
|
||||
'out',
|
||||
help: 'The path to write the generated HTML report. Ignored if '
|
||||
'--html is not also true.',
|
||||
defaultsTo: path.join(projectDir, 'lint_report'),
|
||||
);
|
||||
|
||||
return argParser;
|
||||
}
|
||||
|
||||
/// On macOS, we can try to find Java 1.8.
|
||||
///
|
||||
/// Otherwise, default to whatever JAVA_HOME is already.
|
||||
Future<String?> getJavaHome() async {
|
||||
if (Platform.isMacOS) {
|
||||
final ProcessResult result = await processManager.run(
|
||||
<String>['/usr/libexec/java_home', '-v', '1.8', '-F'],
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
return (result.stdout as String).trim();
|
||||
}
|
||||
}
|
||||
return Platform.environment['JAVA_HOME'];
|
||||
}
|
||||
|
||||
/// Checks that `java` points to Java 1.8.
|
||||
///
|
||||
/// The SDK lint tool may not work with Java > 1.8.
|
||||
Future<void> checkJava1_8() async {
|
||||
print('Checking Java version...');
|
||||
|
||||
if (Platform.isMacOS) {
|
||||
final ProcessResult result = await processManager.run(
|
||||
<String>['/usr/libexec/java_home', '-v', '1.8', '-F'],
|
||||
);
|
||||
if (result.exitCode != 0) {
|
||||
print('Java 1.8 not available - the linter may not work properly.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
final ProcessResult javaResult = await processManager.run(
|
||||
<String>['java', '-version'],
|
||||
);
|
||||
if (javaResult.exitCode != 0) {
|
||||
print('Could not run "java -version". '
|
||||
'Ensure Java is installed and available on your path.');
|
||||
print(javaResult.stderr);
|
||||
}
|
||||
// `java -version` writes to stderr.
|
||||
final String javaVersionStdout = javaResult.stderr as String;
|
||||
if (!javaVersionStdout.contains('"1.8')) {
|
||||
print('The Android SDK tools may not work properly with your Java version. '
|
||||
'If this process fails, please retry using Java 1.8.');
|
||||
}
|
||||
}
|
||||
|
||||
/// The root directory of this project.
|
||||
String get projectDir => path.dirname(
|
||||
path.dirname(
|
||||
path.fromUri(Platform.script),
|
||||
),
|
||||
);
|
||||
|
||||
/// The path to use for project.xml, which tells the linter where to find source
|
||||
/// files.
|
||||
String get projectXmlPath => path.join(projectDir, 'project.xml');
|
||||
|
||||
/// The path to use for baseline.xml, which tells the linter what errors or
|
||||
/// warnings to ignore.
|
||||
String get baselineXmlPath => path.join(projectDir, 'baseline.xml');
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
name: android_lint
|
||||
environment:
|
||||
sdk: '>=2.12.0-0.0.dev <3.0.0'
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
# Do not add any dependencies that require more than what is provided in
|
||||
# //third_party.pkg, //third_party/dart/pkg, or
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user