Unverified Commit 2c8813c8 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Revert "Re-enable AAR plugins when an AndroidX failure occurred (#40810)" (#41042)

This reverts commit 96482eeb.
parent 919cc97f
......@@ -1022,7 +1022,6 @@ Future<void> _androidGradleTests(String subShard) async {
await _runDevicelabTest('gradle_plugin_fat_apk_test', env: env);
await _runDevicelabTest('gradle_r8_test', env: env);
await _runDevicelabTest('gradle_non_android_plugin_test', env: env);
await _runDevicelabTest('gradle_jetifier_test', env: env);
}
if (subShard == 'gradle2') {
await _runDevicelabTest('gradle_plugin_bundle_test', env: env);
......
......@@ -64,7 +64,6 @@ Future<void> main() async {
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--no-shrink',
'--verbose',
],
);
......@@ -101,9 +100,7 @@ Future<void> main() async {
options: <String>[
'apk',
'--target-platform', 'android-arm',
'--debug',
'--no-shrink',
'--verbose',
'--debug', '--verbose',
],
);
});
......
......@@ -7,7 +7,6 @@
// destination of the local repository.
// The local repository will contain the AAR and POM files.
import java.nio.file.Paths
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.maven.MavenDeployer
......@@ -40,32 +39,16 @@ void configureProject(Project project, File outputDir) {
}
}
}
if (!project.property("is-plugin").toBoolean()) {
return
}
if (project.hasProperty('localEngineOut')) {
// TODO(egarciad): Support local engine.
// This most likely requires refactoring `flutter.gradle`, so the logic can be reused.
throw new GradleException(
"Local engine isn't supported when building the plugins as AAR. " +
"See: https://github.com/flutter/flutter/issues/40866")
}
// This is a Flutter plugin project. Plugin projects don't apply the Flutter Gradle plugin,
// as a result, add the dependency on the embedding.
project.repositories {
maven {
url "http://download.flutter.io"
}
}
String engineVersion = Paths.get(getFlutterRoot(project), "bin", "internal", "engine.version")
.toFile().text.trim()
project.dependencies {
// Add the embedding dependency.
compileOnly ("io.flutter:flutter_embedding_release:1.0.0-$engineVersion") {
// We only need to expose io.flutter.plugin.*
// No need for the embedding transitive dependencies.
transitive = false
// Check if the project uses the Flutter plugin (defined in flutter.gradle).
Boolean usesFlutterPlugin = project.plugins.find { it.class.name == "FlutterPlugin" } != null
if (!usesFlutterPlugin) {
project.dependencies {
// Some plugins don't include `annotations` and they don't set
// `android.useAndroidX=true` in `gradle.properties`.
compileOnly "androidx.annotation:annotation:+"
compileOnly "com.android.support:support-annotations:+"
// The Flutter plugin already adds `flutter.jar`.
compileOnly project.files("${getFlutterRoot(project)}/bin/cache/artifacts/engine/android-arm-release/flutter.jar")
}
}
}
......
......@@ -8,8 +8,6 @@ import com.android.builder.model.AndroidProject
import com.android.build.OutputFile
import java.nio.file.Path
import java.nio.file.Paths
import java.util.regex.Matcher
import java.util.regex.Pattern
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
......@@ -258,65 +256,29 @@ class FlutterPlugin implements Plugin<Project> {
*/
private void configurePlugins() {
if (!buildPluginAsAar()) {
getPluginList().each this.&configurePluginProject
getPluginList().each this.&configurePlugin
return
}
if (useLocalEngine()) {
throw new GradleException("Local engine isn't supported when building the plugins as AAR")
}
List<Project> projects = [project]
// Module projects set the `hostProjects` extra property in `include_flutter.groovy`.
// This is required to set the local repository in each host app project.
if (project.ext.has("hostProjects")) {
projects.addAll(project.ext.get("hostProjects"))
}
// Configure the repository for the plugins.
projects.each { hostProject ->
hostProject.repositories {
maven {
url "${getPluginBuildDir()}/outputs/repo"
}
addPluginTasks()
List<String> tasksToExecute = project.gradle.startParameter.taskNames
Set buildTypes = getBuildTypesForTasks(tasksToExecute)
if (tasksToExecute.contains("clean")) {
// Because the plugins are built during configuration, the task "clean"
// cannot run in conjunction with an assembly task.
if (!buildTypes.empty) {
throw new GradleException("Can't run the clean task along with other assemble tasks")
}
}
getPluginList().each { pluginName, pluginPath ->
configurePluginAar(pluginName, pluginPath, project)
// Build plugins when a task "assembly*" will be called later.
if (!buildTypes.empty) {
// Build the plugin during configuration.
// This is required when Jetifier is enabled, otherwise the implementation dependency
// cannot be added.
buildAarPlugins(buildTypes)
}
}
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 .
private void configurePluginProject(String name, String _) {
private void configurePlugin(String name, String _) {
Project pluginProject = project.rootProject.findProject(":$name")
if (pluginProject == null) {
project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
......@@ -381,6 +343,93 @@ class FlutterPlugin implements Plugin<Project> {
return androidPlugins
}
private void addPluginTasks() {
Properties plugins = getPluginList()
project.android.buildTypes.each { buildType ->
plugins.each { name, path ->
String buildModeValue = buildType.debuggable ? "debug" : "release"
List<String> taskNameParts = ["build", "plugin", buildModeValue]
taskNameParts.addAll(name.split("_"))
String taskName = toCammelCase(taskNameParts)
// Build types can be extended. For example, a build type can extend the `debug` mode.
// In such cases, prevent creating the same task.
if (project.tasks.findByName(taskName) == null) {
project.tasks.create(name: taskName, type: FlutterPluginTask) {
flutterExecutable this.flutterExecutable
buildMode buildModeValue
verbose isVerbose()
pluginDir project.file(path)
sourceDir project.file(project.flutter.source)
intermediateDir getPluginBuildDir()
}
}
}
}
}
private void buildAarPlugins(Set buildTypes) {
List<Project> projects = [project]
// Module projects set the `hostProjects` extra property in `include_flutter.groovy`.
// This is required to set the local repository in each host app project.
if (project.ext.has("hostProjects")) {
projects.addAll(project.ext.get("hostProjects"))
}
projects.each { hostProject ->
hostProject.repositories {
maven {
url "${getPluginBuildDir()}/outputs/repo"
}
}
}
buildTypes.each { buildType ->
project.tasks.withType(FlutterPluginTask).all { pluginTask ->
String buildMode = buildType.debuggable ? "debug" : "release"
if (pluginTask.buildMode != buildMode) {
return
}
pluginTask.execute()
pluginTask.intermediateDir.eachFileRecurse(FILES) { file ->
if (file.name != "maven-metadata.xml") {
return
}
def mavenMetadata = new XmlParser().parse(file)
String groupId = mavenMetadata.groupId.text()
String artifactId = mavenMetadata.artifactId.text()
if (!artifactId.endsWith(buildMode)) {
return
}
// Add the plugin dependency based on the Maven metadata.
addApiDependencies(project, buildType.name, "$groupId:$artifactId:+@aar", {
transitive = true
})
}
}
}
}
/**
* Returns a set with the build type names that apply to the given list of tasks
* required to configure the plugin dependencies.
*/
private Set getBuildTypesForTasks(List<String> tasksToExecute) {
Set buildTypes = []
tasksToExecute.each { task ->
project.android.buildTypes.each { buildType ->
if (task == "androidDependencies" || task.endsWith("dependencies")) {
// The tasks to query the dependencies includes all the build types.
buildTypes.add(buildType)
} else if (task.endsWith("assemble")) {
// The `assemble` task includes all the build types.
buildTypes.add(buildType)
} else if (task.endsWith(buildType.name.capitalize())) {
buildTypes.add(buildType)
}
}
}
return buildTypes
}
private static String toCammelCase(List<String> parts) {
if (parts.empty) {
return ""
......@@ -878,3 +927,56 @@ class FlutterTask extends BaseFlutterTask {
buildBundle()
}
}
class FlutterPluginTask extends DefaultTask {
File flutterExecutable
@Optional @Input
Boolean verbose
@Input
String buildMode
@Input
File pluginDir
@Input
File intermediateDir
File sourceDir
@InputFiles
FileCollection getSourceFiles() {
return project.fileTree(
dir: sourceDir,
exclude: ["android", "ios"],
include: ["pubspec.yaml"]
)
}
@OutputDirectory
File getOutputDirectory() {
return intermediateDir
}
@TaskAction
void build() {
intermediateDir.mkdirs()
project.exec {
executable flutterExecutable.absolutePath
workingDir pluginDir
args "build", "aar"
args "--quiet"
args "--suppress-analytics"
args "--output-dir", "${intermediateDir}"
switch (buildMode) {
case 'release':
args "--release"
break
case 'debug':
args "--debug"
break
default:
assert false
}
if (verbose) {
args "--verbose"
}
}
}
}
......@@ -36,6 +36,9 @@ class FeatureFlags {
/// Whether flutter desktop for Windows is enabled.
bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature);
/// Whether plugins are built as AARs in app projects.
bool get isPluginAsAarEnabled => _isEnabled(flutterBuildPluginAsAarFeature);
// Calculate whether a particular feature is enabled for the current channel.
static bool _isEnabled(Feature feature) {
final String currentChannel = FlutterVersion.instance.channel;
......
......@@ -125,7 +125,6 @@ class BuildEvent extends UsageEvent {
BuildEvent(String parameter, {
this.command,
this.settings,
this.eventError,
}) : super(
'build' +
(FlutterCommand.current == null ? '' : '-${FlutterCommand.current.name}'),
......@@ -133,7 +132,6 @@ class BuildEvent extends UsageEvent {
final String command;
final String settings;
final String eventError;
@override
void send() {
......@@ -142,8 +140,6 @@ class BuildEvent extends UsageEvent {
CustomDimensions.buildEventCommand: command,
if (settings != null)
CustomDimensions.buildEventSettings: settings,
if (eventError != null)
CustomDimensions.buildEventError: eventError,
});
flutterUsage.sendEvent(category, parameter, parameters: parameters);
}
......
......@@ -53,7 +53,6 @@ enum CustomDimensions {
commandBuildApkSplitPerAbi, // cd40
commandBuildAppBundleTargetPlatform, // cd41
commandBuildAppBundleBuildMode, // cd42
buildEventError, // cd43
}
String cdKey(CustomDimensions cd) => 'cd${cd.index + 1}';
......
......@@ -25,6 +25,7 @@ import 'package:process/process.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart';
import '../../src/pubspec_schema.dart';
void main() {
......@@ -1154,153 +1155,6 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
});
});
group('isAppUsingAndroidX', () {
FileSystem fs;
setUp(() {
fs = MemoryFileSystem();
});
testUsingContext('returns true when the project is using AndroidX', () async {
final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
androidDirectory
.childFile('gradle.properties')
.writeAsStringSync('android.useAndroidX=true');
expect(isAppUsingAndroidX(androidDirectory), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
testUsingContext('returns false when the project is not using AndroidX', () async {
final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
androidDirectory
.childFile('gradle.properties')
.writeAsStringSync('android.useAndroidX=false');
expect(isAppUsingAndroidX(androidDirectory), isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
testUsingContext('returns false when gradle.properties does not exist', () async {
final Directory androidDirectory = fs.systemTempDirectory.createTempSync('android.');
expect(isAppUsingAndroidX(androidDirectory), isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
});
group('buildPluginsAsAar', () {
FileSystem fs;
MockProcessManager mockProcessManager;
MockAndroidSdk mockAndroidSdk;
setUp(() {
fs = MemoryFileSystem();
mockProcessManager = MockProcessManager();
when(mockProcessManager.run(
any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(1, 0, '', ''));
mockAndroidSdk = MockAndroidSdk();
when(mockAndroidSdk.directory).thenReturn('irrelevant');
});
testUsingContext('calls gradle', () async {
final Directory androidDirectory = fs.directory('android.');
androidDirectory.createSync();
androidDirectory
.childFile('pubspec.yaml')
.writeAsStringSync('name: irrelevant');
final Directory plugin1 = fs.directory('plugin1.');
plugin1
..createSync()
..childFile('pubspec.yaml')
.writeAsStringSync('''
name: irrelevant
flutter:
plugin:
androidPackage: irrelevant
''');
final Directory plugin2 = fs.directory('plugin2.');
plugin2
..createSync()
..childFile('pubspec.yaml')
.writeAsStringSync('''
name: irrelevant
flutter:
plugin:
androidPackage: irrelevant
''');
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);
await buildPluginsAsAar(
FlutterProject.fromPath(androidDirectory.path),
const AndroidBuildInfo(BuildInfo.release),
buildDirectory: buildDirectory.path,
);
final String flutterRoot = fs.path.absolute(Cache.flutterRoot);
final String initScript = fs.path.join(flutterRoot, 'packages',
'flutter_tools', 'gradle', 'aar_init_script.gradle');
verify(mockProcessManager.run(
<String>[
'gradlew',
'-I=$initScript',
'-Pflutter-root=$flutterRoot',
'-Poutput-dir=${buildDirectory.path}',
'-Pis-plugin=true',
'-Ptarget-platform=android-arm,android-arm64',
'assembleAarRelease',
],
environment: anyNamed('environment'),
workingDirectory: plugin1.childDirectory('android').path),
).called(1);
verify(mockProcessManager.run(
<String>[
'gradlew',
'-I=$initScript',
'-Pflutter-root=$flutterRoot',
'-Poutput-dir=${buildDirectory.path}',
'-Pis-plugin=true',
'-Ptarget-platform=android-arm,android-arm64',
'assembleAarRelease',
],
environment: anyNamed('environment'),
workingDirectory: plugin2.childDirectory('android').path),
).called(1);
}, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
FileSystem: () => fs,
GradleUtils: () => FakeGradleUtils(),
ProcessManager: () => mockProcessManager,
});
});
group('gradle build', () {
MockAndroidSdk mockAndroidSdk;
MockAndroidStudio mockAndroidStudio;
......@@ -1381,13 +1235,11 @@ plugin2=${plugin2.path}
fs.currentDirectory = 'path/to/project';
// Let any process start. Assert after.
when(mockProcessManager.run(
when(mockProcessManager.start(
any,
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'))
).thenAnswer(
(_) async => ProcessResult(1, 0, '', ''),
);
).thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
fs.directory('build/outputs/repo').createSync(recursive: true);
await buildGradleAar(
......@@ -1397,11 +1249,11 @@ plugin2=${plugin2.path}
target: ''
);
final List<String> actualGradlewCall = verify(mockProcessManager.run(
final List<String> actualGradlewCall = verify(mockProcessManager.start(
captureAny,
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory')),
).captured.last;
).captured.single;
expect(actualGradlewCall, contains('/path/to/project/.android/gradlew'));
expect(actualGradlewCall, contains('-PlocalEngineOut=out/android_arm'));
......@@ -1432,14 +1284,6 @@ Platform fakePlatform(String name) {
return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name;
}
class FakeGradleUtils extends GradleUtils {
@override
Future<String> getExecutable(FlutterProject project) async {
return 'gradlew';
}
}
class MockAndroidSdk extends Mock implements AndroidSdk {}
class MockAndroidStudio extends Mock implements AndroidStudio {}
class MockDirectory extends Mock implements Directory {}
class MockFile extends Mock implements File {}
......
......@@ -432,6 +432,21 @@ void main() {
expect(featureFlags.isWindowsEnabled, false);
}));
/// Plugins as AARS
test('plugins built as AARs with config on master', () => testbed.run(() {
when(mockFlutterVerion.channel).thenReturn('master');
when<bool>(mockFlutterConfig.getValue('enable-build-plugin-as-aar')).thenReturn(false);
expect(featureFlags.isPluginAsAarEnabled, false);
}));
test('plugins built as AARs with config on dev', () => testbed.run(() {
when(mockFlutterVerion.channel).thenReturn('dev');
when<bool>(mockFlutterConfig.getValue('enable-build-plugin-as-aar')).thenReturn(false);
expect(featureFlags.isPluginAsAarEnabled, false);
}));
});
}
......
......@@ -693,6 +693,7 @@ class TestFeatureFlags implements FeatureFlags {
this.isMacOSEnabled = false,
this.isWebEnabled = false,
this.isWindowsEnabled = false,
this.isPluginAsAarEnabled = false,
});
@override
......@@ -706,4 +707,7 @@ class TestFeatureFlags implements FeatureFlags {
@override
final bool isWindowsEnabled;
@override
final bool isPluginAsAarEnabled;
}
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