Unverified Commit 1d2eaaf2 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Ensure that flutter assets are copied in the AAR (#42306)

parent a3c750a2
...@@ -956,6 +956,7 @@ Future<void> _androidGradleTests(String subShard) async { ...@@ -956,6 +956,7 @@ Future<void> _androidGradleTests(String subShard) async {
await _runDevicelabTest('gradle_plugin_bundle_test', env: env); await _runDevicelabTest('gradle_plugin_bundle_test', env: env);
await _runDevicelabTest('module_test', env: env); await _runDevicelabTest('module_test', env: env);
await _runDevicelabTest('module_host_with_custom_build_test', env: env); await _runDevicelabTest('module_host_with_custom_build_test', env: env);
await _runDevicelabTest('build_aar_module_test', env: env);
} }
} }
......
...@@ -80,7 +80,7 @@ Future<void> main() async { ...@@ -80,7 +80,7 @@ Future<void> main() async {
'flutter_release-1.0.aar', 'flutter_release-1.0.aar',
)); ));
checkFileExists(path.join( final String releasePom = path.join(
repoPath, repoPath,
'io', 'io',
'flutter', 'flutter',
...@@ -89,78 +89,40 @@ Future<void> main() async { ...@@ -89,78 +89,40 @@ Future<void> main() async {
'flutter_release', 'flutter_release',
'1.0', '1.0',
'flutter_release-1.0.pom', 'flutter_release-1.0.pom',
)); );
checkFileExists(path.join( checkFileExists(releasePom);
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_release',
'1.0',
'device_info_release-1.0.aar',
));
checkFileExists(path.join( section('Check AOT blobs in release POM');
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_release',
'1.0',
'device_info_release-1.0.pom',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_release',
'1.0',
'package_info_release-1.0.aar',
));
checkFileExists(path.join( checkFileContains(<String>[
repoPath, 'flutter_embedding_release',
'io', 'armeabi_v7a_release',
'flutter', 'arm64_v8a_release',
'plugins', ], releasePom);
'packageinfo',
'package_info_release',
'1.0',
'package_info_release-1.0.pom',
));
section('Check assets in release AAR'); section('Check assets in release AAR');
final Iterable<String> releaseAar = await getFilesInAar(path.join( checkItContains<String>(
repoPath, <String>[
'io', ...flutterAssets,
'flutter', // AOT snapshots
'devicelab', 'jni/arm64-v8a/libapp.so',
'hello', 'jni/armeabi-v7a/libapp.so',
'flutter_release', ],
'1.0', await getFilesInAar(
'flutter_release-1.0.aar', path.join(
)); repoPath,
'io',
checkItContains<String>(<String>[ 'flutter',
'assets/flutter_assets/FontManifest.json', 'devicelab',
'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf', 'hello',
], releaseAar); 'flutter_release',
'1.0',
section('Check AOT blobs in release AAR'); 'flutter_release-1.0.aar',
)
checkItContains<String>(<String>[ )
'jni/arm64-v8a/libapp.so', );
'jni/arm64-v8a/libflutter.so',
'jni/armeabi-v7a/libapp.so',
'jni/armeabi-v7a/libflutter.so',
], releaseAar);
section('Build debug AAR'); section('Build debug AAR');
...@@ -182,7 +144,7 @@ Future<void> main() async { ...@@ -182,7 +144,7 @@ Future<void> main() async {
'flutter_debug-1.0.aar', 'flutter_debug-1.0.aar',
)); ));
checkFileExists(path.join( final String debugPom = path.join(
repoPath, repoPath,
'io', 'io',
'flutter', 'flutter',
...@@ -191,52 +153,19 @@ Future<void> main() async { ...@@ -191,52 +153,19 @@ Future<void> main() async {
'flutter_debug', 'flutter_debug',
'1.0', '1.0',
'flutter_debug-1.0.pom', 'flutter_debug-1.0.pom',
)); );
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_debug',
'1.0',
'device_info_debug-1.0.aar',
));
checkFileExists(path.join(
repoPath,
'io',
'flutter',
'plugins',
'deviceinfo',
'device_info_debug',
'1.0',
'device_info_debug-1.0.pom',
));
checkFileExists(path.join( checkFileExists(debugPom);
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_debug',
'1.0',
'package_info_debug-1.0.aar',
));
checkFileExists(path.join( section('Check AOT blobs in debug POM');
repoPath,
'io',
'flutter',
'plugins',
'packageinfo',
'package_info_debug',
'1.0',
'package_info_debug-1.0.pom',
));
checkFileContains(<String>[
'flutter_embedding_debug',
'x86_debug',
'x86_64_debug',
'armeabi_v7a_debug',
'arm64_v8a_debug',
], debugPom);
section('Check assets in debug AAR'); section('Check assets in debug AAR');
...@@ -252,23 +181,13 @@ Future<void> main() async { ...@@ -252,23 +181,13 @@ Future<void> main() async {
)); ));
checkItContains<String>(<String>[ checkItContains<String>(<String>[
'assets/flutter_assets/FontManifest.json', ...flutterAssets,
'assets/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf',
// JIT snapshots. // JIT snapshots.
'assets/flutter_assets/isolate_snapshot_data', 'assets/flutter_assets/isolate_snapshot_data',
'assets/flutter_assets/kernel_blob.bin', 'assets/flutter_assets/kernel_blob.bin',
'assets/flutter_assets/vm_snapshot_data', 'assets/flutter_assets/vm_snapshot_data',
], debugAar); ], debugAar);
section('Check AOT blobs in debug AAR');
checkItContains<String>(<String>[
'jni/arm64-v8a/libflutter.so',
'jni/armeabi-v7a/libflutter.so',
'jni/x86/libflutter.so',
'jni/x86_64/libflutter.so',
], debugAar);
return TaskResult.success(null); return TaskResult.success(null);
} on TaskResult catch (taskResult) { } on TaskResult catch (taskResult) {
return taskResult; return taskResult;
......
...@@ -84,6 +84,20 @@ void checkItDoesNotContain<T>(Iterable<T> values, Iterable<T> collection) { ...@@ -84,6 +84,20 @@ void checkItDoesNotContain<T>(Iterable<T> values, Iterable<T> collection) {
} }
} }
/// Checks that [str] contains the specified [Pattern]s, otherwise throws
/// a [TaskResult].
void checkFileContains(List<Pattern> patterns, String filePath) {
final String fileContent = File(filePath).readAsStringSync();
for (Pattern pattern in patterns) {
if (!fileContent.contains(pattern)) {
throw TaskResult.failure(
'Expected to find `$pattern` in `$filePath` '
'instead it found:\n$fileContent'
);
}
}
}
TaskResult failure(String message, ProcessResult result) { TaskResult failure(String message, ProcessResult result) {
print('Unexpected process result:'); print('Unexpected process result:');
print('Exit code: ${result.exitCode}'); print('Exit code: ${result.exitCode}');
......
...@@ -14,7 +14,7 @@ import org.gradle.api.artifacts.maven.MavenDeployer ...@@ -14,7 +14,7 @@ import org.gradle.api.artifacts.maven.MavenDeployer
import org.gradle.api.plugins.MavenPlugin import org.gradle.api.plugins.MavenPlugin
import org.gradle.api.tasks.Upload import org.gradle.api.tasks.Upload
void configureProject(Project project, File outputDir) { void configureProject(Project project, String outputDir) {
if (!project.hasProperty("android")) { if (!project.hasProperty("android")) {
throw new GradleException("Android property not found.") throw new GradleException("Android property not found.")
} }
...@@ -85,55 +85,64 @@ void addAarTask(Project project, variant) { ...@@ -85,55 +85,64 @@ void addAarTask(Project project, variant) {
if (!project.gradle.startParameter.taskNames.contains(taskName)) { if (!project.gradle.startParameter.taskNames.contains(taskName)) {
return return
} }
// NOTE(blasten): `android.defaultPublishConfig` must equal the variant name to build.
// Where variant name is `<product-flavor><Build-Type>`. However, it's too late to configure
// `defaultPublishConfig` at this point. Therefore, the code below ensures that the
// default build config uses the artifacts produced for the specific build variant.
Task bundle = project.tasks.findByName("bundle${variantName}Aar") // gradle:3.2.0
if (bundle == null) {
bundle = project.tasks.findByName("bundle${variantName}") // gradle:3.1.0
}
if (bundle == null) {
throw new GradleException("Can't generate AAR for variant ${variantName}.");
}
project.uploadArchives.repositories.mavenDeployer { project.uploadArchives.repositories.mavenDeployer {
pom { pom {
artifactId = "${project.name}_${variant.name.toLowerCase()}" artifactId = "${project.name}_${variant.name.toLowerCase()}"
} }
} }
// Clear the current archives since the current one is assigned based on overrideDefaultPublishConfig(project, variant)
// `android.defaultPublishConfig` which defaults to `release`.
project.configurations["archives"].artifacts.clear()
// Add the artifact that will be published.
project.artifacts.add("archives", bundle)
// Generate the Maven artifacts. // Generate the Maven artifacts.
finalizedBy "uploadArchives" finalizedBy "uploadArchives"
} }
} }
// This method mimics the logic in AGP when `android.defaultPublishConfig` is set in `build.gradle`:
// https://android.googlesource.com/platform/tools/base/+/studio-master-dev/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/variant/VariantHelper.java
//
// Unfortunately, `android.defaultPublishConfig` cannot be overriden at this point since
// AGP already run this code.
void overrideDefaultPublishConfig(Project project, variant) {
String variantName = variant.name.capitalize()
Task bundle = project.tasks.findByName("bundle${variantName}Aar") // gradle:3.2.0
if (bundle == null) {
bundle = project.tasks.findByName("bundle${variantName}") // gradle:3.1.0
}
if (bundle == null) {
throw new GradleException("Can't generate AAR for variant ${variantName}.");
}
// Clear the current archive artifacts since the artifacts are based on `android.defaultPublishConfig`.
project.configurations["archives"].artifacts.clear()
// Add the artifact that will be published.
project.artifacts.add("archives", bundle)
def scopeMappings = project.uploadArchives.repositories.mavenDeployer.pom.scopeMappings
// Clear the scope mappings added by AGP since they are based on the current `android.defaultPublishConfig`.
scopeMappings.mappings.clear()
// Add the new mappings.
for (Configuration configuration : flattenConfiguration(variant.runtimeConfiguration)) {
scopeMappings.addMapping(/* priority = */ 300, configuration, "compile")
}
}
Set<Configuration> flattenConfiguration(Configuration configuration) {
Set<Configuration> configs = [configuration]
for (Configuration extend : configuration.extendsFrom) {
configs.addAll(flattenConfiguration(extend))
}
return configs
}
projectsEvaluated { projectsEvaluated {
assert rootProject.hasProperty("is-plugin")
if (rootProject.property("is-plugin").toBoolean()) { if (rootProject.property("is-plugin").toBoolean()) {
if (rootProject.hasProperty("output-dir")) { assert rootProject.hasProperty("output-dir")
rootProject.buildDir = rootProject.property("output-dir")
} else {
rootProject.buildDir = "../build";
}
// In plugin projects, the Android library is the root project. // In plugin projects, the Android library is the root project.
configureProject(rootProject, rootProject.buildDir) configureProject(rootProject, rootProject.property("output-dir"))
return return
} }
// In module projects, the Android library project is the `:flutter` subproject. // In module projects, the Android library project is the `:flutter` subproject.
Project androidLibrarySubproject = rootProject.subprojects.find { it.name == "flutter" } Project androidLibraryProject = rootProject.subprojects.find { it.name == "flutter" }
// In module projects, the `buildDir` is defined in the `:app` subproject. assert androidLibraryProject != null
Project appSubproject = rootProject.subprojects.find { it.name == "app" } assert androidLibraryProject.hasProperty("output-dir")
configureProject(androidLibraryProject, androidLibraryProject.property("output-dir"))
assert appSubproject != null
assert androidLibrarySubproject != null
if (appSubproject.hasProperty("output-dir")) {
appSubproject.buildDir = appSubproject.property("output-dir")
} else {
appSubproject.buildDir = "../build/host"
}
configureProject(androidLibrarySubproject, appSubproject.buildDir)
} }
...@@ -220,6 +220,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -220,6 +220,7 @@ class FlutterPlugin implements Plugin<Project> {
} }
} }
String flutterBuildMode = buildModeFor(buildType) String flutterBuildMode = buildModeFor(buildType)
println "flutter dependency"
// Add the embedding dependency. // Add the embedding dependency.
addApiDependencies(project, buildType.name, addApiDependencies(project, buildType.name,
"io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion") "io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion")
...@@ -351,7 +352,7 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -351,7 +352,7 @@ class FlutterPlugin implements Plugin<Project> {
"io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion", "io.flutter:flutter_embedding_$flutterBuildMode:1.0.0-$engineVersion",
{ {
// Include the embedding transitive dependencies since plugins may depend on them. // Include the embedding transitive dependencies since plugins may depend on them.
transitive = true transitive = false
} }
) )
} }
...@@ -619,12 +620,17 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -619,12 +620,17 @@ class FlutterPlugin implements Plugin<Project> {
addApiDependencies(project, variant.name, project.files { addApiDependencies(project, variant.name, project.files {
packFlutterAppAotTask packFlutterAppAotTask
}) })
// We build an AAR when this property is defined.
boolean isBuildingAar = project.hasProperty('is-plugin')
// In add to app scenarios, a Gradle project contains a `:flutter` and `:app` project.
// We know that `:flutter` is used as a subproject when these tasks exists and we aren't building an AAR.
Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets") Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets")
Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets") Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets")
// In add to app scenarios, :flutter is a subproject of another Android app. boolean isUsedAsSubproject = packageAssets && cleanPackageAssets && !isBuildingAar
// We know that :flutter is used as a subproject when these tasks exist. Task copyFlutterAssetsTask = project.tasks.create(
boolean isUsedAsSubproject = packageAssets && cleanPackageAssets name: "copyFlutterAssets${variant.name.capitalize()}",
Task copyFlutterAssetsTask = project.tasks.create(name: "copyFlutterAssets${variant.name.capitalize()}", type: Copy) { type: Copy,
) {
dependsOn compileTasks dependsOn compileTasks
compileTasks.each { flutterTask -> compileTasks.each { flutterTask ->
// Add flutter_assets. // Add flutter_assets.
...@@ -645,9 +651,10 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -645,9 +651,10 @@ class FlutterPlugin implements Plugin<Project> {
into mergeAssets.outputDir into mergeAssets.outputDir
} }
if (!isUsedAsSubproject) { if (!isUsedAsSubproject) {
def mergeResources = variant.hasProperty("mergeResourcesProvider") ? def variantOutput = variant.outputs.first()
variant.mergeResourcesProvider.get() : variant.mergeResources def processResources = variantOutput.hasProperty("processResourcesProvider") ?
mergeResources.dependsOn(copyFlutterAssetsTask) variantOutput.processResourcesProvider.get() : variantOutput.processResources
processResources.dependsOn(copyFlutterAssetsTask)
return return
} }
// Flutter module included as a subproject in add to app. // Flutter module included as a subproject in add to app.
......
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