Skip to content

Commit

Permalink
Warn when gradle builds fail because of AndroidX (flutter#27566)
Browse files Browse the repository at this point in the history
Try to detect Gradle error messages that hint at AndroidX problems, and
warn in the logs about the potential problem and point to documentation
on how to fix the issue.

Unfortunately the Gradle errors based on this root issue are varied and
project dependent. It's probably better to still leave the message
intact in case the problem is unrelated.

Also filters out the plugin warning message pending in
flutter/plugins#1138. It's still valuable to add that for people on
previous versions of Flutter, but this link should override that message
for anyone on an up to date version of Flutter.

flutter#27106
  • Loading branch information
Michael Klimushyn authored and kangwang1988 committed Feb 12, 2019
1 parent 4588c17 commit f939076
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 3 deletions.
48 changes: 46 additions & 2 deletions packages/flutter_tools/lib/src/android/gradle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,30 @@ enum FlutterPluginVersion {
// Investigation documented in #13975 suggests the filter should be a subset
// of the impact of -q, but users insist they see the error message sometimes
// anyway. If we can prove it really is impossible, delete the filter.
// This technically matches everything *except* the NDK message, since it's
// passed to a function that filters out all lines that don't match a filter.
final RegExp ndkMessageFilter = RegExp(r'^(?!NDK is missing a ".*" directory'
r'|If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning'
r'|If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to .*)');

// This regex is intentionally broad. AndroidX errors can manifest in multiple
// different ways and each one depends on the specific code config and
// filesystem paths of the project. Throwing the broadest net possible here to
// catch all known and likely cases.
//
// Example stack traces:
//
// https://github.com/flutter/flutter/issues/27226 "AAPT: error: resource android:attr/fontVariationSettings not found."
// https://github.com/flutter/flutter/issues/27106 "Android resource linking failed|Daemon: AAPT2|error: failed linking references"
// https://github.com/flutter/flutter/issues/27493 "error: cannot find symbol import androidx.annotation.NonNull;"
// https://github.com/flutter/flutter/issues/23995 "error: package android.support.annotation does not exist import android.support.annotation.NonNull;"
final RegExp androidXFailureRegex = RegExp(r'(AAPT|androidx|android\.support)');

final RegExp androidXPluginWarningRegex = RegExp(r'\*{57}'
r"|WARNING: This version of (\w+) will break your Android build if it or its dependencies aren't compatible with AndroidX."
r'|See https://goo.gl/CP92wY for more information on the problem and how to fix it.'
r'|This warning prints for all Android build failures. The real root cause of the error may be unrelated.');

FlutterPluginVersion getFlutterPluginVersion(AndroidProject project) {
final File plugin = project.hostAppGradleRoot.childFile(
fs.path.join('buildSrc', 'src', 'main', 'groovy', 'FlutterPlugin.groovy'));
Expand Down Expand Up @@ -405,17 +425,41 @@ Future<void> _buildGradleProjectV2(
command.add('-Ptarget-platform=${getNameForTargetPlatform(buildInfo.targetPlatform)}');

command.add(assembleTask);
bool potentialAndroidXFailure = false;
final int exitCode = await runCommandAndStreamOutput(
command,
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: _gradleEnv,
filter: logger.isVerbose ? null : ndkMessageFilter,
// TODO(mklim): if AndroidX warnings are no longer required, this
// mapFunction and all its associated variabled can be replaced with just
// `filter: ndkMessagefilter`.
mapFunction: (String line) {
final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line);
if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) {
potentialAndroidXFailure = true;
}
// Always print the full line in verbose mode.
if (logger.isVerbose) {
return line;
} else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) {
return null;
}

return line;
}
);
status.stop();

if (exitCode != 0)
if (exitCode != 0) {
if (potentialAndroidXFailure) {
printError('*******************************************************************************************');
printError('The Gradle failure may have been because of AndroidX incompatibilities in this Flutter app.');
printError('See https://goo.gl/CP92wY for more information on the problem and how to fix it.');
printError('*******************************************************************************************');
}
throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode);
}

if(!isBuildingBundle) {
final File apkFile = _findApkFile(project, buildInfo);
Expand Down
6 changes: 6 additions & 0 deletions packages/flutter_tools/lib/src/base/process.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ Future<Process> runCommand(List<String> cmd, {

/// This runs the command and streams stdout/stderr from the child process to
/// this process' stdout/stderr. Completes with the process's exit code.
///
/// If [filter] is null, no lines are removed.
///
/// If [filter] is non-null, all lines that do not match it are removed. If
/// [mapFunction] is present, all lines that match [filter] are also forwarded
/// to [mapFunction] for further processing.
Future<int> runCommandAndStreamOutput(List<String> cmd, {
String workingDirectory,
bool allowReentrantFlutter = false,
Expand Down
45 changes: 44 additions & 1 deletion packages/flutter_tools/test/android/gradle_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,50 @@ void main() {
expect(shouldBeToolExit, isToolExit);
});

test('regexp should only match lines without the error message', () {
test('androidXFailureRegex should match lines with likely AndroidX errors', () {
final List<String> nonMatchingLines = <String>[
':app:preBuild UP-TO-DATE',
'BUILD SUCCESSFUL in 0s',
'',
];
final List<String> matchingLines = <String>[
'AAPT: error: resource android:attr/fontVariationSettings not found.',
'AAPT: error: resource android:attr/ttcIndex not found.',
'error: package android.support.annotation does not exist',
'import android.support.annotation.NonNull;',
'import androidx.annotation.NonNull;',
'Daemon: AAPT2 aapt2-3.2.1-4818971-linux Daemon #0',
];
for (String m in nonMatchingLines) {
expect(androidXFailureRegex.hasMatch(m), isFalse);
}
for (String m in matchingLines) {
expect(androidXFailureRegex.hasMatch(m), isTrue);
}
});

test('androidXPluginWarningRegex should match lines with the AndroidX plugin warnings', () {
final List<String> nonMatchingLines = <String>[
':app:preBuild UP-TO-DATE',
'BUILD SUCCESSFUL in 0s',
'Generic plugin AndroidX text',
'',
];
final List<String> matchingLines = <String>[
'*********************************************************************************************************************************',
"WARNING: This version of image_picker will break your Android build if it or its dependencies aren't compatible with AndroidX.",
'See https://goo.gl/CP92wY for more information on the problem and how to fix it.',
'This warning prints for all Android build failures. The real root cause of the error may be unrelated.',
];
for (String m in nonMatchingLines) {
expect(androidXPluginWarningRegex.hasMatch(m), isFalse);
}
for (String m in matchingLines) {
expect(androidXPluginWarningRegex.hasMatch(m), isTrue);
}
});

test('ndkMessageFilter should only match lines without the error message', () {
final List<String> nonMatchingLines = <String>[
'NDK is missing a "platforms" directory.',
'If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to /usr/local/company/home/username/Android/Sdk/ndk-bundle.',
Expand Down

0 comments on commit f939076

Please sign in to comment.