Remove AndroidX workarounds (#86911)
This commit is contained in:
parent
aa89a6e5ad
commit
e7a9adeeab
@ -1,138 +0,0 @@
|
||||
// Copyright 2014 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:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
|
||||
final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradlew';
|
||||
|
||||
/// Tests that AARs can be built on plugin projects.
|
||||
Future<void> main() async {
|
||||
await task(() async {
|
||||
|
||||
section('Find Java');
|
||||
|
||||
final String? javaHome = await findJavaHome();
|
||||
if (javaHome == null)
|
||||
return TaskResult.failure('Could not find Java');
|
||||
print('\nUsing JAVA_HOME=$javaHome');
|
||||
|
||||
section('Create plugin project');
|
||||
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
|
||||
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
|
||||
try {
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>[
|
||||
'--org', 'io.flutter.devicelab',
|
||||
'--template', 'plugin',
|
||||
'hello',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
section('Build release AAR');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['aar', '--verbose', '--release'],
|
||||
);
|
||||
});
|
||||
|
||||
final String repoPath = path.join(
|
||||
projectDir.path,
|
||||
'build',
|
||||
'outputs',
|
||||
'repo',
|
||||
);
|
||||
|
||||
final File releaseAar = File(path.join(
|
||||
repoPath,
|
||||
'io',
|
||||
'flutter',
|
||||
'devicelab',
|
||||
'hello',
|
||||
'hello_release',
|
||||
'1.0',
|
||||
'hello_release-1.0.aar',
|
||||
));
|
||||
|
||||
if (!exists(releaseAar)) {
|
||||
return TaskResult.failure('Failed to build the release AAR file.');
|
||||
}
|
||||
|
||||
final File releasePom = File(path.join(
|
||||
repoPath,
|
||||
'io',
|
||||
'flutter',
|
||||
'devicelab',
|
||||
'hello',
|
||||
'hello_release',
|
||||
'1.0',
|
||||
'hello_release-1.0.pom',
|
||||
));
|
||||
|
||||
if (!exists(releasePom)) {
|
||||
return TaskResult.failure('Failed to build the release POM file.');
|
||||
}
|
||||
|
||||
section('Build debug AAR');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'aar',
|
||||
'--verbose',
|
||||
'--debug',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
final File debugAar = File(path.join(
|
||||
repoPath,
|
||||
'io',
|
||||
'flutter',
|
||||
'devicelab',
|
||||
'hello',
|
||||
'hello_debug',
|
||||
'1.0',
|
||||
'hello_debug-1.0.aar',
|
||||
));
|
||||
|
||||
if (!exists(debugAar)) {
|
||||
return TaskResult.failure('Failed to build the debug AAR file.');
|
||||
}
|
||||
|
||||
final File debugPom = File(path.join(
|
||||
repoPath,
|
||||
'io',
|
||||
'flutter',
|
||||
'devicelab',
|
||||
'hello',
|
||||
'hello_debug',
|
||||
'1.0',
|
||||
'hello_debug-1.0.pom',
|
||||
));
|
||||
|
||||
if (!exists(debugPom)) {
|
||||
return TaskResult.failure('Failed to build the debug POM file.');
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
} catch (e) {
|
||||
return TaskResult.failure(e.toString());
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
}
|
||||
});
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
// Copyright 2014 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:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
final String gradlew = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
|
||||
final String gradlewExecutable = Platform.isWindows ? '.\\$gradlew' : './$gradlew';
|
||||
|
||||
/// Tests that [settings_aar.gradle] is created when possible.
|
||||
Future<void> main() async {
|
||||
await task(() async {
|
||||
|
||||
section('Find Java');
|
||||
|
||||
final String? javaHome = await findJavaHome();
|
||||
if (javaHome == null)
|
||||
return TaskResult.failure('Could not find Java');
|
||||
print('\nUsing JAVA_HOME=$javaHome');
|
||||
|
||||
section('Create app project');
|
||||
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
|
||||
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
|
||||
try {
|
||||
await inDirectory(tempDir, () async {
|
||||
await flutter(
|
||||
'create',
|
||||
options: <String>['hello'],
|
||||
);
|
||||
});
|
||||
|
||||
section('Override settings.gradle V1');
|
||||
|
||||
final String relativeNewSettingsGradle = path.join('android', 'settings_aar.gradle');
|
||||
|
||||
section('Build APK');
|
||||
|
||||
late String stdout;
|
||||
await inDirectory(projectDir, () async {
|
||||
stdout = await evalFlutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'--flavor', 'does-not-exist',
|
||||
],
|
||||
canFail: true, // The flavor doesn't exist.
|
||||
);
|
||||
});
|
||||
|
||||
const String newFileContent = "include ':app'";
|
||||
|
||||
final File settingsGradle = File(path.join(projectDir.path, 'android', 'settings.gradle'));
|
||||
final File newSettingsGradle = File(path.join(projectDir.path, 'android', 'settings_aar.gradle'));
|
||||
|
||||
if (!newSettingsGradle.existsSync()) {
|
||||
return TaskResult.failure('Expected file: `${newSettingsGradle.path}`.');
|
||||
}
|
||||
|
||||
if (newSettingsGradle.readAsStringSync().trim() != newFileContent) {
|
||||
return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V1.');
|
||||
}
|
||||
|
||||
if (!stdout.contains('Creating `$relativeNewSettingsGradle`') ||
|
||||
!stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
|
||||
return TaskResult.failure('Expected update message in stdout.');
|
||||
}
|
||||
|
||||
section('Override settings.gradle V2');
|
||||
|
||||
const String deprecatedFileContentV2 = r'''
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withInputStream { stream -> plugins.load(stream) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
''';
|
||||
settingsGradle.writeAsStringSync(deprecatedFileContentV2, flush: true);
|
||||
newSettingsGradle.deleteSync();
|
||||
|
||||
section('Build APK');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
stdout = await evalFlutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'--flavor', 'does-not-exist',
|
||||
],
|
||||
canFail: true, // The flavor doesn't exist.
|
||||
);
|
||||
});
|
||||
|
||||
if (newSettingsGradle.readAsStringSync().trim() != newFileContent) {
|
||||
return TaskResult.failure('Expected to create `${newSettingsGradle.path}` V2.');
|
||||
}
|
||||
|
||||
if (!stdout.contains('Creating `$relativeNewSettingsGradle`') ||
|
||||
!stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
|
||||
return TaskResult.failure('Expected update message in stdout.');
|
||||
}
|
||||
|
||||
section('Override settings.gradle with custom logic');
|
||||
|
||||
const String customDeprecatedFileContent = r'''
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withInputStream { stream -> plugins.load(stream) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
// some custom logic
|
||||
''';
|
||||
settingsGradle.writeAsStringSync(customDeprecatedFileContent, flush: true);
|
||||
newSettingsGradle.deleteSync();
|
||||
|
||||
section('Build APK');
|
||||
|
||||
final StringBuffer stderr = StringBuffer();
|
||||
await inDirectory(projectDir, () async {
|
||||
stdout = await evalFlutter(
|
||||
'build',
|
||||
options: <String>[
|
||||
'apk',
|
||||
'--flavor', 'does-not-exist',
|
||||
],
|
||||
canFail: true, // The flavor doesn't exist.
|
||||
stderr: stderr,
|
||||
);
|
||||
});
|
||||
|
||||
if (newSettingsGradle.existsSync()) {
|
||||
return TaskResult.failure('Unexpected file: `${newSettingsGradle.path}`.');
|
||||
}
|
||||
|
||||
if (!stdout.contains('Creating `$relativeNewSettingsGradle`')) {
|
||||
return TaskResult.failure('Expected update message in stdout.');
|
||||
}
|
||||
|
||||
if (stdout.contains('`$relativeNewSettingsGradle` created successfully')) {
|
||||
return TaskResult.failure('Unexpected message in stdout.');
|
||||
}
|
||||
|
||||
if (!stderr.toString().contains('Flutter tried to create the file '
|
||||
'`$relativeNewSettingsGradle`, but failed.')) {
|
||||
return TaskResult.failure('Expected failure message in stdout.');
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
} catch (e) {
|
||||
return TaskResult.failure(e.toString());
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
}
|
||||
});
|
||||
}
|
@ -289,51 +289,8 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
* Finally, the project's `settings.gradle` loads each plugin's android directory as a subproject.
|
||||
*/
|
||||
private void configurePlugins() {
|
||||
if (!buildPluginAsAar()) {
|
||||
getPluginList().each this.&configurePluginProject
|
||||
getPluginDependencies().each this.&configurePluginDependencies
|
||||
return
|
||||
}
|
||||
project.repositories {
|
||||
maven {
|
||||
url "${getPluginBuildDir()}/outputs/repo"
|
||||
}
|
||||
}
|
||||
getPluginList().each { pluginName, pluginPath ->
|
||||
configurePluginAar(pluginName, pluginPath, project)
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern GROUP_PATTERN = ~/group\s+\'(.+)\'/
|
||||
private static final Pattern PROJECT_NAME_PATTERN = ~/rootProject\.name\s+=\s+\'(.+)\'/
|
||||
|
||||
// Adds the plugin AAR dependency to the app project.
|
||||
private void configurePluginAar(String pluginName, String pluginPath, Project project) {
|
||||
// Extract the group id from the plugin's build.gradle.
|
||||
// This is `group '<group-id>'`
|
||||
File pluginBuildFile = project.file(Paths.get(pluginPath, "android", "build.gradle"));
|
||||
if (!pluginBuildFile.exists()) {
|
||||
throw new GradleException("Plugin $pluginName doesn't have the required file $pluginBuildFile.")
|
||||
}
|
||||
|
||||
Matcher groupParts = GROUP_PATTERN.matcher(pluginBuildFile.text)
|
||||
assert groupParts.count == 1
|
||||
assert groupParts.hasGroup()
|
||||
String groupId = groupParts[0][1]
|
||||
|
||||
// Extract the artifact name from the plugin's settings.gradle.
|
||||
// This is `rootProject.name = '<artifact-name>'`
|
||||
File pluginSettings = project.file(Paths.get(pluginPath, "android", "settings.gradle"));
|
||||
if (!pluginSettings.exists()) {
|
||||
throw new GradleException("Plugin $pluginName doesn't have the required file $pluginSettings.")
|
||||
}
|
||||
Matcher projectNameParts = PROJECT_NAME_PATTERN.matcher(pluginSettings.text)
|
||||
assert projectNameParts.count == 1
|
||||
assert projectNameParts.hasGroup()
|
||||
String artifactId = "${projectNameParts[0][1]}_release"
|
||||
|
||||
assert !groupId.empty
|
||||
project.dependencies.add("api", "$groupId:$artifactId:+")
|
||||
}
|
||||
|
||||
// Adds the plugin project dependency to the app project .
|
||||
@ -544,10 +501,6 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
return project.plugins.hasPlugin("com.android.application");
|
||||
}
|
||||
|
||||
private static Boolean buildPluginAsAar() {
|
||||
return System.getProperty('build-plugins-as-aars') == 'true'
|
||||
}
|
||||
|
||||
// Returns true if the build mode is supported by the current call to Gradle.
|
||||
// This only relevant when using a local engine. Because the engine
|
||||
// is built for a specific mode, the call to Gradle must match that mode.
|
||||
|
@ -105,57 +105,6 @@ Iterable<String> _apkFilesFor(AndroidBuildInfo androidBuildInfo) {
|
||||
return <String>['app$flavorString-$buildType.apk'];
|
||||
}
|
||||
|
||||
/// Tries to create `settings_aar.gradle` in an app project by removing the subprojects
|
||||
/// from the existing `settings.gradle` file. This operation will fail if the existing
|
||||
/// `settings.gradle` file has local edits.
|
||||
@visibleForTesting
|
||||
void createSettingsAarGradle(Directory androidDirectory, Logger logger) {
|
||||
final FileSystem fileSystem = androidDirectory.fileSystem;
|
||||
final File newSettingsFile = androidDirectory.childFile('settings_aar.gradle');
|
||||
if (newSettingsFile.existsSync()) {
|
||||
return;
|
||||
}
|
||||
final File currentSettingsFile = androidDirectory.childFile('settings.gradle');
|
||||
if (!currentSettingsFile.existsSync()) {
|
||||
return;
|
||||
}
|
||||
final String currentFileContent = currentSettingsFile.readAsStringSync();
|
||||
|
||||
final String newSettingsRelativeFile = fileSystem.path.relative(newSettingsFile.path);
|
||||
final Status status = logger.startProgress('✏️ Creating `$newSettingsRelativeFile`...');
|
||||
|
||||
final String flutterRoot = fileSystem.path.absolute(Cache.flutterRoot!);
|
||||
final File legacySettingsDotGradleFiles = fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools',
|
||||
'gradle', 'settings.gradle.legacy_versions'));
|
||||
assert(legacySettingsDotGradleFiles.existsSync());
|
||||
final String settingsAarContent = fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools',
|
||||
'gradle', 'settings_aar.gradle.tmpl')).readAsStringSync();
|
||||
|
||||
// Get the `settings.gradle` content variants that should be patched.
|
||||
final List<String> existingVariants = legacySettingsDotGradleFiles.readAsStringSync().split(';EOF');
|
||||
existingVariants.add(settingsAarContent);
|
||||
|
||||
bool exactMatch = false;
|
||||
for (final String fileContentVariant in existingVariants) {
|
||||
if (currentFileContent.trim() == fileContentVariant.trim()) {
|
||||
exactMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exactMatch) {
|
||||
status.cancel();
|
||||
logger.printStatus('${logger.terminal.warningMark} Flutter tried to create the file `$newSettingsRelativeFile`, but failed.');
|
||||
// Print how to manually update the file.
|
||||
logger.printStatus(fileSystem.file(fileSystem.path.join(flutterRoot, 'packages','flutter_tools',
|
||||
'gradle', 'manual_migration_settings.gradle.md')).readAsStringSync());
|
||||
throwToolExit('Please create the file and run this command again.');
|
||||
}
|
||||
// Copy the new file.
|
||||
newSettingsFile.writeAsStringSync(settingsAarContent);
|
||||
status.stop();
|
||||
logger.printStatus('${logger.terminal.successMark} `$newSettingsRelativeFile` created successfully.');
|
||||
}
|
||||
|
||||
/// An implementation of the [AndroidBuilder] that delegates to gradle.
|
||||
class AndroidGradleBuilder implements AndroidBuilder {
|
||||
AndroidGradleBuilder({
|
||||
@ -262,17 +211,13 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
/// * [target] is the target dart entry point. Typically, `lib/main.dart`.
|
||||
/// * If [isBuildingBundle] is `true`, then the output artifact is an `*.aab`,
|
||||
/// otherwise the output artifact is an `*.apk`.
|
||||
/// * The plugins are built as AARs if [shouldBuildPluginAsAar] is `true`. This isn't set by default
|
||||
/// because it makes the build slower proportional to the number of plugins.
|
||||
/// * [retries] is the max number of build retries in case one of the [GradleHandledError] handler
|
||||
/// returns [GradleBuildStatus.retry] or [GradleBuildStatus.retryWithAarPlugins].
|
||||
Future<void> buildGradleApp({
|
||||
required FlutterProject project,
|
||||
required AndroidBuildInfo androidBuildInfo,
|
||||
required String target,
|
||||
required bool isBuildingBundle,
|
||||
required List<GradleHandledError> localGradleErrors,
|
||||
bool shouldBuildPluginAsAar = false,
|
||||
bool validateDeferredComponents = true,
|
||||
bool deferredComponentsEnabled = false,
|
||||
int retries = 1,
|
||||
@ -286,7 +231,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
if (!project.android.isSupportedVersion) {
|
||||
_exitWithUnsupportedProjectMessage(_usage, _logger.terminal);
|
||||
}
|
||||
final Directory buildDirectory = project.android.buildDirectory;
|
||||
|
||||
final bool usesAndroidX = isAppUsingAndroidX(project.android.hostAppGradleRoot);
|
||||
if (usesAndroidX) {
|
||||
@ -304,16 +248,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
// from the local.properties file.
|
||||
updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo);
|
||||
|
||||
if (shouldBuildPluginAsAar) {
|
||||
// Create a settings.gradle that doesn't import the plugins as subprojects.
|
||||
createSettingsAarGradle(project.android.hostAppGradleRoot, _logger);
|
||||
await buildPluginsAsAar(
|
||||
project,
|
||||
androidBuildInfo,
|
||||
buildDirectory: buildDirectory.childDirectory('app'),
|
||||
);
|
||||
}
|
||||
|
||||
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
|
||||
final String assembleTask = isBuildingBundle
|
||||
? getBundleTaskFor(buildInfo)
|
||||
@ -392,13 +326,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
if (androidBuildInfo.splitPerAbi) {
|
||||
command.add('-Psplit-per-abi=true');
|
||||
}
|
||||
if (shouldBuildPluginAsAar) {
|
||||
// Pass a system flag instead of a project flag, so this flag can be
|
||||
// read from include_flutter.groovy.
|
||||
command.add('-Dbuild-plugins-as-aars=true');
|
||||
// Don't use settings.gradle from the current project since it includes the plugins as subprojects.
|
||||
command.add('--settings-file=settings_aar.gradle');
|
||||
}
|
||||
if (androidBuildInfo.fastStart) {
|
||||
command.add('-Pfast-start=true');
|
||||
}
|
||||
@ -407,12 +334,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
GradleHandledError? detectedGradleError;
|
||||
String? detectedGradleErrorLine;
|
||||
String? consumeLog(String line) {
|
||||
// This message was removed from first-party plugins,
|
||||
// but older plugin versions still display this message.
|
||||
if (androidXPluginWarningRegex.hasMatch(line)) {
|
||||
// Don't pipe.
|
||||
return null;
|
||||
}
|
||||
if (detectedGradleError != null) {
|
||||
// Pipe stdout/stderr from Gradle.
|
||||
return line;
|
||||
@ -468,7 +389,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
line: detectedGradleErrorLine!,
|
||||
project: project,
|
||||
usesAndroidX: usesAndroidX,
|
||||
shouldBuildPluginAsAar: shouldBuildPluginAsAar,
|
||||
);
|
||||
|
||||
if (retries >= 1) {
|
||||
@ -481,19 +401,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
target: target,
|
||||
isBuildingBundle: isBuildingBundle,
|
||||
localGradleErrors: localGradleErrors,
|
||||
shouldBuildPluginAsAar: shouldBuildPluginAsAar,
|
||||
retries: retries - 1,
|
||||
);
|
||||
BuildEvent(successEventLabel, type: 'gradle', flutterUsage: _usage).send();
|
||||
return;
|
||||
case GradleBuildStatus.retryWithAarPlugins:
|
||||
await buildGradleApp(
|
||||
project: project,
|
||||
androidBuildInfo: androidBuildInfo,
|
||||
target: target,
|
||||
isBuildingBundle: isBuildingBundle,
|
||||
localGradleErrors: localGradleErrors,
|
||||
shouldBuildPluginAsAar: true,
|
||||
retries: retries - 1,
|
||||
);
|
||||
BuildEvent(successEventLabel, type: 'gradle', flutterUsage: _usage).send();
|
||||
@ -749,57 +656,6 @@ class AndroidGradleBuilder implements AndroidBuilder {
|
||||
color: TerminalColor.green,
|
||||
);
|
||||
}
|
||||
|
||||
/// Builds the plugins as AARs.
|
||||
@visibleForTesting
|
||||
Future<void> buildPluginsAsAar(
|
||||
FlutterProject flutterProject,
|
||||
AndroidBuildInfo androidBuildInfo, {
|
||||
required Directory buildDirectory,
|
||||
}) async {
|
||||
final File flutterPluginFile = flutterProject.flutterPluginsFile;
|
||||
if (!flutterPluginFile.existsSync()) {
|
||||
return;
|
||||
}
|
||||
final List<String> plugins = flutterPluginFile.readAsStringSync().split('\n');
|
||||
for (final String plugin in plugins) {
|
||||
final List<String> pluginParts = plugin.split('=');
|
||||
if (pluginParts.length != 2) {
|
||||
continue;
|
||||
}
|
||||
final Directory pluginDirectory = _fileSystem.directory(pluginParts.last);
|
||||
assert(pluginDirectory.existsSync());
|
||||
|
||||
final String pluginName = pluginParts.first;
|
||||
final File buildGradleFile = pluginDirectory.childDirectory('android').childFile('build.gradle');
|
||||
if (!buildGradleFile.existsSync()) {
|
||||
_logger.printTrace("Skipping plugin $pluginName since it doesn't have a android/build.gradle file");
|
||||
continue;
|
||||
}
|
||||
_logger.printStatus('Building plugin $pluginName...');
|
||||
try {
|
||||
await buildGradleAar(
|
||||
project: FlutterProject.fromDirectory(pluginDirectory),
|
||||
androidBuildInfo: AndroidBuildInfo(
|
||||
BuildInfo(
|
||||
BuildMode.release, // Plugins are built as release.
|
||||
null, // Plugins don't define flavors.
|
||||
treeShakeIcons: androidBuildInfo.buildInfo.treeShakeIcons,
|
||||
packagesPath: androidBuildInfo.buildInfo.packagesPath,
|
||||
),
|
||||
),
|
||||
target: '',
|
||||
outputDirectory: buildDirectory,
|
||||
buildNumber: '1.0'
|
||||
);
|
||||
} on ToolExit {
|
||||
// Log the entire plugin entry in `.flutter-plugins` since it
|
||||
// includes the plugin name and the version.
|
||||
BuildEvent('gradle-plugin-aar-failure', type: 'gradle', eventError: plugin, flutterUsage: _usage).send();
|
||||
throwToolExit('The plugin $pluginName could not be built due to the issue above.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints how to consume the AAR from a host app.
|
||||
|
@ -31,7 +31,6 @@ class GradleHandledError {
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) handler;
|
||||
|
||||
/// The [BuildEvent] label is named gradle-[eventLabel].
|
||||
@ -46,8 +45,6 @@ enum GradleBuildStatus {
|
||||
exit,
|
||||
/// The tool can retry the exact same build.
|
||||
retry,
|
||||
/// The tool can build the plugins as AAR and retry the build.
|
||||
retryWithAarPlugins,
|
||||
}
|
||||
|
||||
/// Returns a simple test function that evaluates to `true` if at least one of
|
||||
@ -74,7 +71,6 @@ final List<GradleHandledError> gradleErrors = <GradleHandledError>[
|
||||
minSdkVersion,
|
||||
transformInputIssue,
|
||||
lockFileDepMissing,
|
||||
androidXFailureHandler, // Keep last since the pattern is broader.
|
||||
];
|
||||
|
||||
// Permission defined error message.
|
||||
@ -87,7 +83,6 @@ final GradleHandledError permissionDeniedErrorHandler = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
globals.printStatus('${globals.logger.terminal.warningMark} Gradle does not have execution permission.', emphasis: true);
|
||||
globals.printStatus(
|
||||
@ -124,7 +119,6 @@ final GradleHandledError networkErrorHandler = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
globals.printError(
|
||||
'${globals.logger.terminal.warningMark} Gradle threw an error while downloading artifacts from the network. '
|
||||
@ -154,7 +148,6 @@ final GradleHandledError r8FailureHandler = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
globals.printStatus('${globals.logger.terminal.warningMark} The shrinker may have failed to optimize the Java bytecode.', emphasis: true);
|
||||
globals.printStatus('To disable the shrinker, pass the `--no-shrink` flag to this command.', indent: 4);
|
||||
@ -164,91 +157,6 @@ final GradleHandledError r8FailureHandler = GradleHandledError(
|
||||
eventLabel: 'r8',
|
||||
);
|
||||
|
||||
// AndroidX failure.
|
||||
//
|
||||
// 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.');
|
||||
|
||||
@visibleForTesting
|
||||
final GradleHandledError androidXFailureHandler = GradleHandledError(
|
||||
test: (String line) {
|
||||
return !androidXPluginWarningRegex.hasMatch(line) &&
|
||||
_androidXFailureRegex.hasMatch(line);
|
||||
},
|
||||
handler: ({
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
final bool hasPlugins = project.flutterPluginsFile.existsSync();
|
||||
if (!hasPlugins) {
|
||||
// If the app doesn't use any plugin, then it's unclear where
|
||||
// the incompatibility is coming from.
|
||||
BuildEvent(
|
||||
'gradle-android-x-failure',
|
||||
type: 'gradle',
|
||||
eventError: 'app-not-using-plugins',
|
||||
flutterUsage: globals.flutterUsage,
|
||||
).send();
|
||||
}
|
||||
if (hasPlugins && !usesAndroidX) {
|
||||
// If the app isn't using AndroidX, then the app is likely using
|
||||
// a plugin already migrated to AndroidX.
|
||||
globals.printStatus(
|
||||
'AndroidX incompatibilities may have caused this build to fail. '
|
||||
'Please migrate your app to AndroidX. See https://goo.gl/CP92wY .'
|
||||
);
|
||||
BuildEvent(
|
||||
'gradle-android-x-failure',
|
||||
type: 'gradle',
|
||||
eventError: 'app-not-using-androidx',
|
||||
flutterUsage: globals.flutterUsage,
|
||||
).send();
|
||||
}
|
||||
if (hasPlugins && usesAndroidX && shouldBuildPluginAsAar) {
|
||||
// This is a dependency conflict instead of an AndroidX failure since
|
||||
// by this point the app is using AndroidX, the plugins are built as
|
||||
// AARs, Jetifier translated Support libraries for AndroidX equivalents.
|
||||
BuildEvent(
|
||||
'gradle-android-x-failure',
|
||||
type: 'gradle',
|
||||
eventError: 'using-jetifier',
|
||||
flutterUsage: globals.flutterUsage,
|
||||
).send();
|
||||
}
|
||||
if (hasPlugins && usesAndroidX && !shouldBuildPluginAsAar) {
|
||||
globals.printStatus(
|
||||
'The build failed likely due to AndroidX incompatibilities in a plugin. '
|
||||
'The tool is about to try using Jetifier to solve the incompatibility.'
|
||||
);
|
||||
BuildEvent(
|
||||
'gradle-android-x-failure',
|
||||
type: 'gradle',
|
||||
eventError: 'not-using-jetifier',
|
||||
flutterUsage: globals.flutterUsage,
|
||||
).send();
|
||||
return GradleBuildStatus.retryWithAarPlugins;
|
||||
}
|
||||
return GradleBuildStatus.exit;
|
||||
},
|
||||
eventLabel: 'android-x',
|
||||
);
|
||||
|
||||
/// Handle Gradle error thrown when Gradle needs to download additional
|
||||
/// Android SDK components (e.g. Platform Tools), and the license
|
||||
/// for that component has not been accepted.
|
||||
@ -261,7 +169,6 @@ final GradleHandledError licenseNotAcceptedHandler = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
const String licenseNotAcceptedMatcher =
|
||||
r'You have not accepted the license agreements of the following SDK components:\s*\[(.+)\]';
|
||||
@ -295,7 +202,6 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
final RunResult tasksRunResult = await globals.processUtils.run(
|
||||
<String>[
|
||||
@ -368,7 +274,6 @@ final GradleHandledError minSdkVersion = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
final File gradleFile = project.directory
|
||||
.childDirectory('android')
|
||||
@ -409,7 +314,6 @@ final GradleHandledError transformInputIssue = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
final File gradleFile = project.directory
|
||||
.childDirectory('android')
|
||||
@ -443,7 +347,6 @@ final GradleHandledError lockFileDepMissing = GradleHandledError(
|
||||
required String line,
|
||||
required FlutterProject project,
|
||||
required bool usesAndroidX,
|
||||
required bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
final File gradleFile = project.directory
|
||||
.childDirectory('android')
|
||||
|
@ -101,7 +101,6 @@ void main() {
|
||||
String line,
|
||||
FlutterProject project,
|
||||
bool usesAndroidX,
|
||||
bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
handlerCalled = true;
|
||||
return GradleBuildStatus.exit;
|
||||
@ -206,7 +205,6 @@ void main() {
|
||||
String line,
|
||||
FlutterProject project,
|
||||
bool usesAndroidX,
|
||||
bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
return GradleBuildStatus.retry;
|
||||
},
|
||||
@ -290,7 +288,6 @@ void main() {
|
||||
String line,
|
||||
FlutterProject project,
|
||||
bool usesAndroidX,
|
||||
bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
handlerCalled = true;
|
||||
return GradleBuildStatus.exit;
|
||||
@ -454,7 +451,6 @@ void main() {
|
||||
String line,
|
||||
FlutterProject project,
|
||||
bool usesAndroidX,
|
||||
bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
return GradleBuildStatus.retry;
|
||||
},
|
||||
@ -570,117 +566,6 @@ void main() {
|
||||
));
|
||||
});
|
||||
|
||||
testUsingContext('Can retry gradle build with plugins built as AARs', () async {
|
||||
final AndroidGradleBuilder builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
artifacts: Artifacts.test(),
|
||||
usage: testUsage,
|
||||
gradleUtils: FakeGradleUtils(),
|
||||
platform: FakePlatform(),
|
||||
);
|
||||
processManager.addCommand(const FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-q',
|
||||
'-Ptarget-platform=android-arm,android-arm64,android-x64',
|
||||
'-Ptarget=lib/main.dart',
|
||||
'-Pdart-obfuscation=false',
|
||||
'-Ptrack-widget-creation=false',
|
||||
'-Ptree-shake-icons=false',
|
||||
'assembleRelease',
|
||||
],
|
||||
exitCode: 1,
|
||||
stderr: '\nSome gradle message\n',
|
||||
));
|
||||
processManager.addCommand(const FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-q',
|
||||
'-Ptarget-platform=android-arm,android-arm64,android-x64',
|
||||
'-Ptarget=lib/main.dart',
|
||||
'-Pdart-obfuscation=false',
|
||||
'-Ptrack-widget-creation=false',
|
||||
'-Ptree-shake-icons=false',
|
||||
'-Dbuild-plugins-as-aars=true',
|
||||
'--settings-file=settings_aar.gradle',
|
||||
'assembleRelease'
|
||||
],
|
||||
exitCode: 1,
|
||||
stderr: '\nSome gradle message\n',
|
||||
));
|
||||
|
||||
fileSystem.directory('android')
|
||||
.childFile('build.gradle')
|
||||
.createSync(recursive: true);
|
||||
|
||||
fileSystem.directory('android')
|
||||
.childFile('gradle.properties')
|
||||
.createSync(recursive: true);
|
||||
|
||||
fileSystem.directory('android')
|
||||
.childDirectory('app')
|
||||
.childFile('build.gradle')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
|
||||
|
||||
int testFnCalled = 0;
|
||||
bool builtPluginAsAar = false;
|
||||
await expectLater(() async {
|
||||
await builder.buildGradleApp(
|
||||
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
|
||||
androidBuildInfo: const AndroidBuildInfo(
|
||||
BuildInfo(
|
||||
BuildMode.release,
|
||||
null,
|
||||
treeShakeIcons: false,
|
||||
),
|
||||
),
|
||||
target: 'lib/main.dart',
|
||||
isBuildingBundle: false,
|
||||
localGradleErrors: <GradleHandledError>[
|
||||
GradleHandledError(
|
||||
test: (String line) {
|
||||
if (line.contains('Some gradle message')) {
|
||||
testFnCalled++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
handler: ({
|
||||
String line,
|
||||
FlutterProject project,
|
||||
bool usesAndroidX,
|
||||
bool shouldBuildPluginAsAar,
|
||||
}) async {
|
||||
if (testFnCalled == 2) {
|
||||
builtPluginAsAar = shouldBuildPluginAsAar;
|
||||
}
|
||||
return GradleBuildStatus.retryWithAarPlugins;
|
||||
},
|
||||
eventLabel: 'random-event-label',
|
||||
),
|
||||
],
|
||||
);
|
||||
}, throwsToolExit(
|
||||
message: 'Gradle task assembleRelease failed with exit code 1'
|
||||
));
|
||||
|
||||
expect(testFnCalled, equals(2));
|
||||
expect(builtPluginAsAar, isTrue);
|
||||
|
||||
expect(testUsage.events, contains(
|
||||
const TestUsageEvent(
|
||||
'build',
|
||||
'gradle',
|
||||
label: 'gradle-random-event-label-failure',
|
||||
parameters: CustomDimensions(),
|
||||
),
|
||||
));
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
});
|
||||
|
||||
testUsingContext('indicates that an APK has been built successfully', () async {
|
||||
final AndroidGradleBuilder builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
|
@ -12,7 +12,6 @@ import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -31,10 +30,8 @@ void main() {
|
||||
minSdkVersion,
|
||||
transformInputIssue,
|
||||
lockFileDepMissing,
|
||||
androidXFailureHandler,
|
||||
])
|
||||
);
|
||||
expect(gradleErrors.last, equals(androidXFailureHandler));
|
||||
});
|
||||
});
|
||||
|
||||
@ -355,155 +352,6 @@ Command: /home/android/gradlew assembleRelease
|
||||
});
|
||||
});
|
||||
|
||||
group('AndroidX', () {
|
||||
final TestUsage testUsage = TestUsage();
|
||||
|
||||
testWithoutContext('pattern', () {
|
||||
expect(androidXFailureHandler.test(
|
||||
'AAPT: error: resource android:attr/fontVariationSettings not found.'
|
||||
), isTrue);
|
||||
|
||||
expect(androidXFailureHandler.test(
|
||||
'AAPT: error: resource android:attr/ttcIndex not found.'
|
||||
), isTrue);
|
||||
|
||||
expect(androidXFailureHandler.test(
|
||||
'error: package android.support.annotation does not exist'
|
||||
), isTrue);
|
||||
|
||||
expect(androidXFailureHandler.test(
|
||||
'import android.support.annotation.NonNull;'
|
||||
), isTrue);
|
||||
|
||||
expect(androidXFailureHandler.test(
|
||||
'import androidx.annotation.NonNull;'
|
||||
), isTrue);
|
||||
|
||||
expect(androidXFailureHandler.test(
|
||||
'Daemon: AAPT2 aapt2-3.2.1-4818971-linux Daemon #0'
|
||||
), isTrue);
|
||||
});
|
||||
|
||||
testUsingContext('handler - no plugins', () async {
|
||||
final GradleBuildStatus status = await androidXFailureHandler
|
||||
.handler(line: '', project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory));
|
||||
|
||||
expect(testUsage.events, contains(
|
||||
TestUsageEvent(
|
||||
'build',
|
||||
'gradle',
|
||||
label: 'gradle-android-x-failure',
|
||||
parameters: CustomDimensions.fromMap(<String, String>{
|
||||
'cd43': 'app-not-using-plugins',
|
||||
}),
|
||||
),
|
||||
));
|
||||
|
||||
expect(status, equals(GradleBuildStatus.exit));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Usage: () => testUsage,
|
||||
});
|
||||
|
||||
testUsingContext('handler - plugins and no AndroidX', () async {
|
||||
globals.fs.file('.flutter-plugins').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus status = await androidXFailureHandler
|
||||
.handler(
|
||||
line: '',
|
||||
project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory),
|
||||
usesAndroidX: false,
|
||||
);
|
||||
|
||||
expect(testLogger.statusText,
|
||||
contains(
|
||||
'AndroidX incompatibilities may have caused this build to fail. '
|
||||
'Please migrate your app to AndroidX. See https://goo.gl/CP92wY .'
|
||||
)
|
||||
);
|
||||
|
||||
expect(testUsage.events, contains(
|
||||
TestUsageEvent(
|
||||
'build',
|
||||
'gradle',
|
||||
label: 'gradle-android-x-failure',
|
||||
parameters: CustomDimensions.fromMap(<String, String>{
|
||||
'cd43': 'app-not-using-androidx',
|
||||
}),
|
||||
),
|
||||
));
|
||||
|
||||
expect(status, equals(GradleBuildStatus.exit));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Usage: () => testUsage,
|
||||
});
|
||||
|
||||
testUsingContext('handler - plugins, AndroidX, and AAR', () async {
|
||||
globals.fs.file('.flutter-plugins').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus status = await androidXFailureHandler.handler(
|
||||
line: '',
|
||||
project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory),
|
||||
usesAndroidX: true,
|
||||
shouldBuildPluginAsAar: true,
|
||||
);
|
||||
|
||||
expect(testUsage.events, contains(
|
||||
TestUsageEvent(
|
||||
'build',
|
||||
'gradle',
|
||||
label: 'gradle-android-x-failure',
|
||||
parameters: CustomDimensions.fromMap(<String, String>{
|
||||
'cd43': 'using-jetifier',
|
||||
}),
|
||||
),
|
||||
));
|
||||
|
||||
expect(status, equals(GradleBuildStatus.exit));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Usage: () => testUsage,
|
||||
});
|
||||
|
||||
testUsingContext('handler - plugins, AndroidX, and no AAR', () async {
|
||||
globals.fs.file('.flutter-plugins').createSync(recursive: true);
|
||||
|
||||
final GradleBuildStatus status = await androidXFailureHandler.handler(
|
||||
line: '',
|
||||
project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory),
|
||||
usesAndroidX: true,
|
||||
shouldBuildPluginAsAar: false,
|
||||
);
|
||||
|
||||
expect(testLogger.statusText,
|
||||
contains(
|
||||
'The build failed likely due to AndroidX incompatibilities in a plugin. '
|
||||
'The tool is about to try using Jetifier to solve the incompatibility.'
|
||||
)
|
||||
);
|
||||
|
||||
expect(testUsage.events, contains(
|
||||
TestUsageEvent(
|
||||
'build',
|
||||
'gradle',
|
||||
label: 'gradle-android-x-failure',
|
||||
parameters: CustomDimensions.fromMap(<String, String>{
|
||||
'cd43': 'not-using-jetifier',
|
||||
}),
|
||||
),
|
||||
));
|
||||
expect(status, equals(GradleBuildStatus.retryWithAarPlugins));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Usage: () => testUsage,
|
||||
});
|
||||
});
|
||||
|
||||
group('permission errors', () {
|
||||
testUsingContext('pattern', () async {
|
||||
const String errorMessage = '''
|
||||
|
@ -7,7 +7,6 @@
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/android/android_sdk.dart';
|
||||
import 'package:flutter_tools/src/android/gradle.dart';
|
||||
import 'package:flutter_tools/src/android/gradle_errors.dart';
|
||||
import 'package:flutter_tools/src/android/gradle_utils.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
@ -18,7 +17,6 @@ import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -194,117 +192,6 @@ void main() {
|
||||
}, overrides: <Type, Generator>{
|
||||
AndroidSdk: () => null,
|
||||
});
|
||||
|
||||
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 (final String m in nonMatchingLines) {
|
||||
expect(androidXPluginWarningRegex.hasMatch(m), isFalse);
|
||||
}
|
||||
for (final String m in matchingLines) {
|
||||
expect(androidXPluginWarningRegex.hasMatch(m), isTrue);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('Config files', () {
|
||||
Directory tempDir;
|
||||
FileSystem fileSystem;
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_settings_aar_test.');
|
||||
});
|
||||
|
||||
testUsingContext('create settings_aar.gradle when current settings.gradle loads plugins', () {
|
||||
const String currentSettingsGradle = r'''
|
||||
include ':app'
|
||||
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
if (pluginDirectory.exists()) {
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String settingsAarFile = '''
|
||||
include ':app'
|
||||
''';
|
||||
|
||||
tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle);
|
||||
|
||||
final String toolGradlePath = fileSystem.path.join(
|
||||
fileSystem.path.absolute(Cache.flutterRoot),
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'gradle');
|
||||
fileSystem.directory(toolGradlePath).createSync(recursive: true);
|
||||
fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings.gradle.legacy_versions'))
|
||||
.writeAsStringSync(currentSettingsGradle);
|
||||
|
||||
fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings_aar.gradle.tmpl'))
|
||||
.writeAsStringSync(settingsAarFile);
|
||||
|
||||
createSettingsAarGradle(tempDir, testLogger);
|
||||
|
||||
expect(testLogger.statusText, contains('created successfully'));
|
||||
expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext("create settings_aar.gradle when current settings.gradle doesn't load plugins", () {
|
||||
const String currentSettingsGradle = '''
|
||||
include ':app'
|
||||
''';
|
||||
|
||||
const String settingsAarFile = '''
|
||||
include ':app'
|
||||
''';
|
||||
|
||||
tempDir.childFile('settings.gradle').writeAsStringSync(currentSettingsGradle);
|
||||
|
||||
final String toolGradlePath = fileSystem.path.join(
|
||||
fileSystem.path.absolute(Cache.flutterRoot),
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'gradle');
|
||||
fileSystem.directory(toolGradlePath).createSync(recursive: true);
|
||||
fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings.gradle.legacy_versions'))
|
||||
.writeAsStringSync(currentSettingsGradle);
|
||||
|
||||
fileSystem.file(fileSystem.path.join(toolGradlePath, 'settings_aar.gradle.tmpl'))
|
||||
.writeAsStringSync(settingsAarFile);
|
||||
|
||||
createSettingsAarGradle(tempDir, testLogger);
|
||||
|
||||
expect(testLogger.statusText, contains('created successfully'));
|
||||
expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
});
|
||||
|
||||
group('Gradle local.properties', () {
|
||||
@ -618,197 +505,6 @@ flutter:
|
||||
});
|
||||
});
|
||||
|
||||
group('buildPluginsAsAar', () {
|
||||
FileSystem fs;
|
||||
FakeProcessManager fakeProcessManager;
|
||||
FakeAndroidSdk androidSdk;
|
||||
AndroidGradleBuilder builder;
|
||||
BufferLogger logger;
|
||||
Platform platform;
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
fs = MemoryFileSystem.test();
|
||||
fakeProcessManager = FakeProcessManager.empty();
|
||||
androidSdk = FakeAndroidSdk();
|
||||
platform = FakePlatform(operatingSystem: 'linux');
|
||||
|
||||
builder = AndroidGradleBuilder(
|
||||
logger: logger,
|
||||
processManager: fakeProcessManager,
|
||||
fileSystem: fs,
|
||||
artifacts: Artifacts.test(),
|
||||
usage: TestUsage(),
|
||||
gradleUtils: FakeGradleUtils(),
|
||||
platform: platform,
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('calls gradle', () async {
|
||||
final Directory androidDirectory = globals.fs.directory('android.');
|
||||
androidDirectory.createSync();
|
||||
androidDirectory
|
||||
.childFile('pubspec.yaml')
|
||||
.writeAsStringSync('name: irrelevant');
|
||||
|
||||
final Directory plugin1 = globals.fs.directory('plugin1.');
|
||||
plugin1
|
||||
..createSync()
|
||||
..childFile('pubspec.yaml')
|
||||
.writeAsStringSync('''
|
||||
name: irrelevant
|
||||
flutter:
|
||||
plugin:
|
||||
androidPackage: irrelevant
|
||||
''');
|
||||
|
||||
plugin1
|
||||
.childDirectory('android')
|
||||
.childFile('build.gradle')
|
||||
.createSync(recursive: true);
|
||||
|
||||
final Directory plugin2 = globals.fs.directory('plugin2.');
|
||||
plugin2
|
||||
..createSync()
|
||||
..childFile('pubspec.yaml')
|
||||
.writeAsStringSync('''
|
||||
name: irrelevant
|
||||
flutter:
|
||||
plugin:
|
||||
androidPackage: irrelevant
|
||||
''');
|
||||
|
||||
plugin2
|
||||
.childDirectory('android')
|
||||
.childFile('build.gradle')
|
||||
.createSync(recursive: true);
|
||||
|
||||
androidDirectory
|
||||
.childFile('.flutter-plugins')
|
||||
.writeAsStringSync('''
|
||||
plugin1=${plugin1.path}
|
||||
plugin2=${plugin2.path}
|
||||
''');
|
||||
final Directory buildDirectory = androidDirectory
|
||||
.childDirectory('build');
|
||||
buildDirectory
|
||||
.childDirectory('outputs')
|
||||
.childDirectory('repo')
|
||||
.createSync(recursive: true);
|
||||
|
||||
final String flutterRoot = globals.fs.path.absolute(Cache.flutterRoot);
|
||||
final String initScript = globals.fs.path.join(
|
||||
flutterRoot,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'gradle',
|
||||
'aar_init_script.gradle',
|
||||
);
|
||||
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-I=$initScript',
|
||||
'-Pflutter-root=$flutterRoot',
|
||||
'-Poutput-dir=${buildDirectory.path}',
|
||||
'-Pis-plugin=true',
|
||||
'-PbuildNumber=1.0',
|
||||
'-q',
|
||||
'-Pdart-obfuscation=false',
|
||||
'-Ptrack-widget-creation=false',
|
||||
'-Ptree-shake-icons=true',
|
||||
'-Ptarget-platform=android-arm,android-arm64,android-x64',
|
||||
'assembleAarRelease',
|
||||
],
|
||||
workingDirectory: plugin1.childDirectory('android').path,
|
||||
),
|
||||
FakeCommand(
|
||||
command: <String>[
|
||||
'gradlew',
|
||||
'-I=$initScript',
|
||||
'-Pflutter-root=$flutterRoot',
|
||||
'-Poutput-dir=${buildDirectory.path}',
|
||||
'-Pis-plugin=true',
|
||||
'-PbuildNumber=1.0',
|
||||
'-q',
|
||||
'-Pdart-obfuscation=false',
|
||||
'-Ptrack-widget-creation=false',
|
||||
'-Ptree-shake-icons=true',
|
||||
'-Ptarget-platform=android-arm,android-arm64,android-x64',
|
||||
'assembleAarRelease',
|
||||
],
|
||||
workingDirectory: plugin2.childDirectory('android').path,
|
||||
)]);
|
||||
|
||||
await builder.buildPluginsAsAar(
|
||||
FlutterProject.fromDirectoryTest(androidDirectory),
|
||||
const AndroidBuildInfo(BuildInfo(
|
||||
BuildMode.release,
|
||||
'',
|
||||
treeShakeIcons: true,
|
||||
dartObfuscation: true,
|
||||
buildNumber: '2.0'
|
||||
)),
|
||||
buildDirectory: buildDirectory,
|
||||
);
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
AndroidSdk: () => androidSdk,
|
||||
FileSystem: () => fs,
|
||||
Platform: () => platform,
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
GradleUtils: () => FakeGradleUtils(),
|
||||
});
|
||||
|
||||
testUsingContext('skips plugin without a android/build.gradle file', () async {
|
||||
final Directory androidDirectory = globals.fs.directory('android.');
|
||||
androidDirectory.createSync();
|
||||
androidDirectory
|
||||
.childFile('pubspec.yaml')
|
||||
.writeAsStringSync('name: irrelevant');
|
||||
|
||||
final Directory plugin1 = globals.fs.directory('plugin1.');
|
||||
plugin1
|
||||
..createSync()
|
||||
..childFile('pubspec.yaml')
|
||||
.writeAsStringSync('''
|
||||
name: irrelevant
|
||||
flutter:
|
||||
plugin:
|
||||
androidPackage: irrelevant
|
||||
''');
|
||||
|
||||
androidDirectory
|
||||
.childFile('.flutter-plugins')
|
||||
.writeAsStringSync('''
|
||||
plugin1=${plugin1.path}
|
||||
''');
|
||||
// Create an empty android directory.
|
||||
// https://github.com/flutter/flutter/issues/46898
|
||||
plugin1.childDirectory('android').createSync();
|
||||
|
||||
final Directory buildDirectory = androidDirectory.childDirectory('build');
|
||||
|
||||
buildDirectory
|
||||
.childDirectory('outputs')
|
||||
.childDirectory('repo')
|
||||
.createSync(recursive: true);
|
||||
|
||||
await builder.buildPluginsAsAar(
|
||||
FlutterProject.fromDirectoryTest(androidDirectory),
|
||||
const AndroidBuildInfo(BuildInfo.release),
|
||||
buildDirectory: buildDirectory,
|
||||
);
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
AndroidSdk: () => androidSdk,
|
||||
FileSystem: () => fs,
|
||||
ProcessManager: () => fakeProcessManager,
|
||||
GradleUtils: () => FakeGradleUtils(),
|
||||
});
|
||||
});
|
||||
|
||||
group('printHowToConsumeAar', () {
|
||||
BufferLogger logger;
|
||||
FileSystem fileSystem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user