Skip to content

Commit f939076

Browse files
Michael Klimushynkangwang1988
authored andcommitted
Warn when gradle builds fail because of AndroidX (flutter#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. flutter#27106
1 parent 4588c17 commit f939076

3 files changed

Lines changed: 96 additions & 3 deletions

File tree

packages/flutter_tools/lib/src/android/gradle.dart

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,30 @@ enum FlutterPluginVersion {
4242
// Investigation documented in #13975 suggests the filter should be a subset
4343
// of the impact of -q, but users insist they see the error message sometimes
4444
// anyway. If we can prove it really is impossible, delete the filter.
45+
// This technically matches everything *except* the NDK message, since it's
46+
// passed to a function that filters out all lines that don't match a filter.
4547
final RegExp ndkMessageFilter = RegExp(r'^(?!NDK is missing a ".*" directory'
4648
r'|If you are not using NDK, unset the NDK variable from ANDROID_NDK_HOME or local.properties to remove this warning'
4749
r'|If you are using NDK, verify the ndk.dir is set to a valid NDK directory. It is currently set to .*)');
4850

51+
// This regex is intentionally broad. AndroidX errors can manifest in multiple
52+
// different ways and each one depends on the specific code config and
53+
// filesystem paths of the project. Throwing the broadest net possible here to
54+
// catch all known and likely cases.
55+
//
56+
// Example stack traces:
57+
//
58+
// https://github.com/flutter/flutter/issues/27226 "AAPT: error: resource android:attr/fontVariationSettings not found."
59+
// https://github.com/flutter/flutter/issues/27106 "Android resource linking failed|Daemon: AAPT2|error: failed linking references"
60+
// https://github.com/flutter/flutter/issues/27493 "error: cannot find symbol import androidx.annotation.NonNull;"
61+
// https://github.com/flutter/flutter/issues/23995 "error: package android.support.annotation does not exist import android.support.annotation.NonNull;"
62+
final RegExp androidXFailureRegex = RegExp(r'(AAPT|androidx|android\.support)');
63+
64+
final RegExp androidXPluginWarningRegex = RegExp(r'\*{57}'
65+
r"|WARNING: This version of (\w+) will break your Android build if it or its dependencies aren't compatible with AndroidX."
66+
r'|See https://goo.gl/CP92wY for more information on the problem and how to fix it.'
67+
r'|This warning prints for all Android build failures. The real root cause of the error may be unrelated.');
68+
4969
FlutterPluginVersion getFlutterPluginVersion(AndroidProject project) {
5070
final File plugin = project.hostAppGradleRoot.childFile(
5171
fs.path.join('buildSrc', 'src', 'main', 'groovy', 'FlutterPlugin.groovy'));
@@ -405,17 +425,41 @@ Future<void> _buildGradleProjectV2(
405425
command.add('-Ptarget-platform=${getNameForTargetPlatform(buildInfo.targetPlatform)}');
406426

407427
command.add(assembleTask);
428+
bool potentialAndroidXFailure = false;
408429
final int exitCode = await runCommandAndStreamOutput(
409430
command,
410431
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
411432
allowReentrantFlutter: true,
412433
environment: _gradleEnv,
413-
filter: logger.isVerbose ? null : ndkMessageFilter,
434+
// TODO(mklim): if AndroidX warnings are no longer required, this
435+
// mapFunction and all its associated variabled can be replaced with just
436+
// `filter: ndkMessagefilter`.
437+
mapFunction: (String line) {
438+
final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line);
439+
if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) {
440+
potentialAndroidXFailure = true;
441+
}
442+
// Always print the full line in verbose mode.
443+
if (logger.isVerbose) {
444+
return line;
445+
} else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) {
446+
return null;
447+
}
448+
449+
return line;
450+
}
414451
);
415452
status.stop();
416453

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

420464
if(!isBuildingBundle) {
421465
final File apkFile = _findApkFile(project, buildInfo);

packages/flutter_tools/lib/src/base/process.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ Future<Process> runCommand(List<String> cmd, {
122122

123123
/// This runs the command and streams stdout/stderr from the child process to
124124
/// this process' stdout/stderr. Completes with the process's exit code.
125+
///
126+
/// If [filter] is null, no lines are removed.
127+
///
128+
/// If [filter] is non-null, all lines that do not match it are removed. If
129+
/// [mapFunction] is present, all lines that match [filter] are also forwarded
130+
/// to [mapFunction] for further processing.
125131
Future<int> runCommandAndStreamOutput(List<String> cmd, {
126132
String workingDirectory,
127133
bool allowReentrantFlutter = false,

packages/flutter_tools/test/android/gradle_test.dart

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,50 @@ void main() {
4141
expect(shouldBeToolExit, isToolExit);
4242
});
4343

44-
test('regexp should only match lines without the error message', () {
44+
test('androidXFailureRegex should match lines with likely AndroidX errors', () {
45+
final List<String> nonMatchingLines = <String>[
46+
':app:preBuild UP-TO-DATE',
47+
'BUILD SUCCESSFUL in 0s',
48+
'',
49+
];
50+
final List<String> matchingLines = <String>[
51+
'AAPT: error: resource android:attr/fontVariationSettings not found.',
52+
'AAPT: error: resource android:attr/ttcIndex not found.',
53+
'error: package android.support.annotation does not exist',
54+
'import android.support.annotation.NonNull;',
55+
'import androidx.annotation.NonNull;',
56+
'Daemon: AAPT2 aapt2-3.2.1-4818971-linux Daemon #0',
57+
];
58+
for (String m in nonMatchingLines) {
59+
expect(androidXFailureRegex.hasMatch(m), isFalse);
60+
}
61+
for (String m in matchingLines) {
62+
expect(androidXFailureRegex.hasMatch(m), isTrue);
63+
}
64+
});
65+
66+
test('androidXPluginWarningRegex should match lines with the AndroidX plugin warnings', () {
67+
final List<String> nonMatchingLines = <String>[
68+
':app:preBuild UP-TO-DATE',
69+
'BUILD SUCCESSFUL in 0s',
70+
'Generic plugin AndroidX text',
71+
'',
72+
];
73+
final List<String> matchingLines = <String>[
74+
'*********************************************************************************************************************************',
75+
"WARNING: This version of image_picker will break your Android build if it or its dependencies aren't compatible with AndroidX.",
76+
'See https://goo.gl/CP92wY for more information on the problem and how to fix it.',
77+
'This warning prints for all Android build failures. The real root cause of the error may be unrelated.',
78+
];
79+
for (String m in nonMatchingLines) {
80+
expect(androidXPluginWarningRegex.hasMatch(m), isFalse);
81+
}
82+
for (String m in matchingLines) {
83+
expect(androidXPluginWarningRegex.hasMatch(m), isTrue);
84+
}
85+
});
86+
87+
test('ndkMessageFilter should only match lines without the error message', () {
4588
final List<String> nonMatchingLines = <String>[
4689
'NDK is missing a "platforms" directory.',
4790
'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.',

0 commit comments

Comments
 (0)