| // 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 Directory androidDir = Directory(path.join( |
| argResults['in'], |
| '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(argResults['in'], '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; |
| } |
| |
| if (argResults['rebaseline']) { |
| print('Removing previous baseline.xml...'); |
| final File baselineXml = File(baselineXmlPath); |
| if (baselineXml.existsSync()) { |
| await baselineXml.delete(); |
| } |
| } |
| print('Preparing projext.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, |
| ]; |
| if (argResults['html']) { |
| lintArgs.addAll(<String>['--html', argResults['out']]); |
| } |
| 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 await 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 the HTML report to. 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.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; |
| 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'); |