| // 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(); | |
| 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-T"> | |
| <manifest file="${path.join(androidDir.path, 'AndroidManifest.xml')}" /> | |
| '''); | |
| for (final FileSystemEntity entity in androidDir.listSync(recursive: true)) { | |
| if (!entity.path.endsWith('.java')) { | |
| continue; | |
| } | |
| if (entity.path.endsWith('Test.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, 'cmdline-tools', 'latest', 'bin', 'lint'), | |
| '--project', projectXmlPath, | |
| '--compile-sdk-version', '33', | |
| '--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 = getJavaHome(inArgument); | |
| print('Using JAVA_HOME=$javahome'); | |
| final Process lintProcess = await processManager.start( | |
| lintArgs, | |
| environment: <String, String>{ | |
| 'JAVA_HOME': javahome, | |
| }, | |
| ); | |
| 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, | |
| ) | |
| ..addFlag( | |
| 'rebaseline', | |
| help: 'Recalculates the baseline for errors and warnings ' | |
| 'in this project.', | |
| negatable: false, | |
| ) | |
| ..addFlag( | |
| 'html', | |
| help: 'Creates an HTML output for this report instead of printing ' | |
| 'command line output.', | |
| negatable: 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; | |
| } | |
| String getJavaHome(String src) { | |
| if (Platform.isMacOS) { | |
| return path.normalize('$src/third_party/java/openjdk/Contents/Home/'); | |
| } | |
| return path.normalize('$src/third_party/java/openjdk/'); | |
| } | |
| /// 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'); |