Unverified Commit 4a9e5bc1 authored by Michael Klimushyn's avatar Michael Klimushyn Committed by GitHub

Warn when gradle builds fail because of AndroidX (#27566)

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.

#27106
parent cd9a0037
......@@ -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'));
......@@ -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);
......
......@@ -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,
......
......@@ -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.',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment