Unverified Commit 16d408a7 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Reland #40810: Re-enable AAR plugins when an AndroidX failure occurred (#41160)

parent 39eecd52
...@@ -1022,6 +1022,7 @@ Future<void> _androidGradleTests(String subShard) async { ...@@ -1022,6 +1022,7 @@ Future<void> _androidGradleTests(String subShard) async {
await _runDevicelabTest('gradle_plugin_fat_apk_test', env: env); await _runDevicelabTest('gradle_plugin_fat_apk_test', env: env);
await _runDevicelabTest('gradle_r8_test', env: env); await _runDevicelabTest('gradle_r8_test', env: env);
await _runDevicelabTest('gradle_non_android_plugin_test', env: env); await _runDevicelabTest('gradle_non_android_plugin_test', env: env);
await _runDevicelabTest('gradle_jetifier_test', env: env);
} }
if (subShard == 'gradle2') { if (subShard == 'gradle2') {
await _runDevicelabTest('gradle_plugin_bundle_test', env: env); await _runDevicelabTest('gradle_plugin_bundle_test', env: env);
......
...@@ -64,6 +64,7 @@ Future<void> main() async { ...@@ -64,6 +64,7 @@ Future<void> main() async {
options: <String>[ options: <String>[
'apk', 'apk',
'--target-platform', 'android-arm', '--target-platform', 'android-arm',
'--no-shrink',
'--verbose', '--verbose',
], ],
); );
...@@ -100,7 +101,9 @@ Future<void> main() async { ...@@ -100,7 +101,9 @@ Future<void> main() async {
options: <String>[ options: <String>[
'apk', 'apk',
'--target-platform', 'android-arm', '--target-platform', 'android-arm',
'--debug', '--verbose', '--debug',
'--no-shrink',
'--verbose',
], ],
); );
}); });
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// destination of the local repository. // destination of the local repository.
// The local repository will contain the AAR and POM files. // The local repository will contain the AAR and POM files.
import java.nio.file.Paths
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.maven.MavenDeployer import org.gradle.api.artifacts.maven.MavenDeployer
...@@ -39,16 +40,32 @@ void configureProject(Project project, File outputDir) { ...@@ -39,16 +40,32 @@ void configureProject(Project project, File outputDir) {
} }
} }
} }
// Check if the project uses the Flutter plugin (defined in flutter.gradle). if (!project.property("is-plugin").toBoolean()) {
Boolean usesFlutterPlugin = project.plugins.find { it.class.name == "FlutterPlugin" } != null return
if (!usesFlutterPlugin) { }
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 { project.dependencies {
// Some plugins don't include `annotations` and they don't set // Add the embedding dependency.
// `android.useAndroidX=true` in `gradle.properties`. compileOnly ("io.flutter:flutter_embedding_release:1.0.0-$engineVersion") {
compileOnly "androidx.annotation:annotation:+" // We only need to expose io.flutter.plugin.*
compileOnly "com.android.support:support-annotations:+" // No need for the embedding transitive dependencies.
// The Flutter plugin already adds `flutter.jar`. transitive = false
compileOnly project.files("${getFlutterRoot(project)}/bin/cache/artifacts/engine/android-arm-release/flutter.jar")
} }
} }
} }
......
...@@ -8,6 +8,8 @@ import com.android.builder.model.AndroidProject ...@@ -8,6 +8,8 @@ import com.android.builder.model.AndroidProject
import com.android.build.OutputFile import com.android.build.OutputFile
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths 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.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.GradleException import org.gradle.api.GradleException
...@@ -256,29 +258,65 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -256,29 +258,65 @@ class FlutterPlugin implements Plugin<Project> {
*/ */
private void configurePlugins() { private void configurePlugins() {
if (!buildPluginAsAar()) { if (!buildPluginAsAar()) {
getPluginList().each this.&configurePlugin getPluginList().each this.&configurePluginProject
return return
} }
addPluginTasks() if (useLocalEngine()) {
List<String> tasksToExecute = project.gradle.startParameter.taskNames throw new GradleException("Local engine isn't supported when building the plugins as AAR")
Set buildTypes = getBuildTypesForTasks(tasksToExecute) }
if (tasksToExecute.contains("clean")) { List<Project> projects = [project]
// Because the plugins are built during configuration, the task "clean" // Module projects set the `hostProjects` extra property in `include_flutter.groovy`.
// cannot run in conjunction with an assembly task. // This is required to set the local repository in each host app project.
if (!buildTypes.empty) { if (project.ext.has("hostProjects")) {
throw new GradleException("Can't run the clean task along with other assemble tasks") projects.addAll(project.ext.get("hostProjects"))
}
// Configure the repository for the plugins.
projects.each { hostProject ->
hostProject.repositories {
maven {
url "${getPluginBuildDir()}/outputs/repo"
}
} }
} }
// Build plugins when a task "assembly*" will be called later. getPluginList().each { pluginName, pluginPath ->
if (!buildTypes.empty) { configurePluginAar(pluginName, pluginPath, project)
// Build the plugin during configuration.
// This is required when Jetifier is enabled, otherwise the implementation dependency
// cannot be added.
buildAarPlugins(buildTypes)
} }
} }
private void configurePlugin(String name, String _) { 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 _) {
Project pluginProject = project.rootProject.findProject(":$name") Project pluginProject = project.rootProject.findProject(":$name")
if (pluginProject == null) { if (pluginProject == null) {
project.logger.error("Plugin project :$name not found. Please update settings.gradle.") project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
...@@ -343,93 +381,6 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -343,93 +381,6 @@ class FlutterPlugin implements Plugin<Project> {
return androidPlugins 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) { private static String toCammelCase(List<String> parts) {
if (parts.empty) { if (parts.empty) {
return "" return ""
...@@ -927,56 +878,3 @@ class FlutterTask extends BaseFlutterTask { ...@@ -927,56 +878,3 @@ class FlutterTask extends BaseFlutterTask {
buildBundle() 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,9 +36,6 @@ class FeatureFlags { ...@@ -36,9 +36,6 @@ class FeatureFlags {
/// Whether flutter desktop for Windows is enabled. /// Whether flutter desktop for Windows is enabled.
bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature); 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. // Calculate whether a particular feature is enabled for the current channel.
static bool _isEnabled(Feature feature) { static bool _isEnabled(Feature feature) {
final String currentChannel = FlutterVersion.instance.channel; final String currentChannel = FlutterVersion.instance.channel;
......
...@@ -125,6 +125,7 @@ class BuildEvent extends UsageEvent { ...@@ -125,6 +125,7 @@ class BuildEvent extends UsageEvent {
BuildEvent(String parameter, { BuildEvent(String parameter, {
this.command, this.command,
this.settings, this.settings,
this.eventError,
}) : super( }) : super(
'build' + 'build' +
(FlutterCommand.current == null ? '' : '-${FlutterCommand.current.name}'), (FlutterCommand.current == null ? '' : '-${FlutterCommand.current.name}'),
...@@ -132,6 +133,7 @@ class BuildEvent extends UsageEvent { ...@@ -132,6 +133,7 @@ class BuildEvent extends UsageEvent {
final String command; final String command;
final String settings; final String settings;
final String eventError;
@override @override
void send() { void send() {
...@@ -140,6 +142,8 @@ class BuildEvent extends UsageEvent { ...@@ -140,6 +142,8 @@ class BuildEvent extends UsageEvent {
CustomDimensions.buildEventCommand: command, CustomDimensions.buildEventCommand: command,
if (settings != null) if (settings != null)
CustomDimensions.buildEventSettings: settings, CustomDimensions.buildEventSettings: settings,
if (eventError != null)
CustomDimensions.buildEventError: eventError,
}); });
flutterUsage.sendEvent(category, parameter, parameters: parameters); flutterUsage.sendEvent(category, parameter, parameters: parameters);
} }
......
...@@ -53,6 +53,7 @@ enum CustomDimensions { ...@@ -53,6 +53,7 @@ enum CustomDimensions {
commandBuildApkSplitPerAbi, // cd40 commandBuildApkSplitPerAbi, // cd40
commandBuildAppBundleTargetPlatform, // cd41 commandBuildAppBundleTargetPlatform, // cd41
commandBuildAppBundleBuildMode, // cd42 commandBuildAppBundleBuildMode, // cd42
buildEventError, // cd43
} }
String cdKey(CustomDimensions cd) => 'cd${cd.index + 1}'; String cdKey(CustomDimensions cd) => 'cd${cd.index + 1}';
......
...@@ -25,7 +25,6 @@ import 'package:process/process.dart'; ...@@ -25,7 +25,6 @@ import 'package:process/process.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart';
import '../../src/pubspec_schema.dart'; import '../../src/pubspec_schema.dart';
void main() { void main() {
...@@ -1150,6 +1149,153 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; ...@@ -1150,6 +1149,153 @@ 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', () { group('gradle build', () {
MockAndroidSdk mockAndroidSdk; MockAndroidSdk mockAndroidSdk;
MockAndroidStudio mockAndroidStudio; MockAndroidStudio mockAndroidStudio;
...@@ -1230,11 +1376,13 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; ...@@ -1230,11 +1376,13 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
fs.currentDirectory = 'path/to/project'; fs.currentDirectory = 'path/to/project';
// Let any process start. Assert after. // Let any process start. Assert after.
when(mockProcessManager.start( when(mockProcessManager.run(
any, any,
environment: anyNamed('environment'), environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory')) workingDirectory: anyNamed('workingDirectory'))
).thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess())); ).thenAnswer(
(_) async => ProcessResult(1, 0, '', ''),
);
fs.directory('build/outputs/repo').createSync(recursive: true); fs.directory('build/outputs/repo').createSync(recursive: true);
await buildGradleAar( await buildGradleAar(
...@@ -1244,11 +1392,11 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; ...@@ -1244,11 +1392,11 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
target: '', target: '',
); );
final List<String> actualGradlewCall = verify(mockProcessManager.start( final List<String> actualGradlewCall = verify(mockProcessManager.run(
captureAny, captureAny,
environment: anyNamed('environment'), environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory')), workingDirectory: anyNamed('workingDirectory')),
).captured.single; ).captured.last;
expect(actualGradlewCall, contains('/path/to/project/.android/gradlew')); expect(actualGradlewCall, contains('/path/to/project/.android/gradlew'));
expect(actualGradlewCall, contains('-PlocalEngineOut=out/android_arm')); expect(actualGradlewCall, contains('-PlocalEngineOut=out/android_arm'));
...@@ -1279,6 +1427,14 @@ Platform fakePlatform(String name) { ...@@ -1279,6 +1427,14 @@ Platform fakePlatform(String name) {
return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = 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 MockAndroidStudio extends Mock implements AndroidStudio {}
class MockDirectory extends Mock implements Directory {} class MockDirectory extends Mock implements Directory {}
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
......
...@@ -432,21 +432,6 @@ void main() { ...@@ -432,21 +432,6 @@ void main() {
expect(featureFlags.isWindowsEnabled, false); 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,7 +693,6 @@ class TestFeatureFlags implements FeatureFlags { ...@@ -693,7 +693,6 @@ class TestFeatureFlags implements FeatureFlags {
this.isMacOSEnabled = false, this.isMacOSEnabled = false,
this.isWebEnabled = false, this.isWebEnabled = false,
this.isWindowsEnabled = false, this.isWindowsEnabled = false,
this.isPluginAsAarEnabled = false,
}); });
@override @override
...@@ -707,7 +706,4 @@ class TestFeatureFlags implements FeatureFlags { ...@@ -707,7 +706,4 @@ class TestFeatureFlags implements FeatureFlags {
@override @override
final bool isWindowsEnabled; 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