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

Refactor Gradle plugin (#34353)

parent 361e8c75
......@@ -80,82 +80,13 @@ class FlutterPlugin implements Plugin<Project> {
// to match.
static final String FLUTTER_BUILD_PREFIX = "flutterBuild"
private Path baseEnginePath
private Map baseJar = [:]
private File flutterRoot
private File flutterExecutable
private String localEngine
private String localEngineSrcPath
private Properties localProperties
private File flutterJar
private File debugFlutterJar
private File profileFlutterJar
private File releaseFlutterJar
private File dynamicProfileFlutterJar
private File dynamicReleaseFlutterJar
private Properties readPropertiesIfExist(File propertiesFile) {
Properties result = new Properties()
if (propertiesFile.exists()) {
propertiesFile.withReader('UTF-8') { reader -> result.load(reader) }
}
return result
}
private List<String> getTargetPlatforms(Project project) {
if (!project.hasProperty('target-platform')) {
return DEFAULT_PLATFORMS
}
return project.property('target-platform').split(',').collect {
if (!PLATFORM_ARCH_MAP[it]) {
throw new GradleException("Invalid platform: $it.")
}
return it
}
}
private Boolean getBuildShareLibrary(Project project) {
if (project.hasProperty('build-shared-library')) {
return project.property('build-shared-library').toBoolean()
}
return false;
}
private Boolean splitPerAbi(Project project) {
if (project.hasProperty('split-per-abi')) {
return project.property('split-per-abi').toBoolean()
}
return false;
}
private String resolveProperty(Project project, String name, String defaultValue) {
if (localProperties == null) {
localProperties = readPropertiesIfExist(new File(project.projectDir.parentFile, "local.properties"))
}
String result
if (project.hasProperty(name)) {
result = project.property(name)
}
if (result == null) {
result = localProperties.getProperty(name)
}
if (result == null) {
result = defaultValue
}
return result
}
/**
* Returns the platform that is used to extract the `libflutter.so` and the .class files.
*
* Note: This is only needed to add the .class files.
* Unfortunately, the engine artifacts include the .class and libflutter.so files.
*/
private String getBasePlatform(Project project) {
if (PLATFORM_ARM64 in getTargetPlatforms(project)) {
return PLATFORM_ARM64;
}
return PLATFORM_ARM32;
}
@Override
void apply(Project project) {
......@@ -165,7 +96,7 @@ class FlutterPlugin implements Plugin<Project> {
// By default, assembling APKs generates fat APKs if multiple platforms are passed.
// Configuring split per ABI allows to generate separate APKs for each abi.
// This is a noop when building a bundle.
if (this.splitPerAbi(project)) {
if (splitPerAbi(project)) {
project.android {
splits {
abi {
......@@ -179,7 +110,7 @@ class FlutterPlugin implements Plugin<Project> {
}
}
}
this.getTargetPlatforms(project).each { targetArch ->
getTargetPlatforms(project).each { targetArch ->
String abiValue = PLATFORM_ARCH_MAP[targetArch]
project.android {
packagingOptions {
......@@ -187,7 +118,7 @@ class FlutterPlugin implements Plugin<Project> {
// Prevent the ELF library from getting corrupted.
doNotStrip "*/${abiValue}/libapp.so"
}
if (this.splitPerAbi(project)) {
if (splitPerAbi(project)) {
splits {
abi {
include abiValue
......@@ -231,69 +162,75 @@ class FlutterPlugin implements Plugin<Project> {
String flutterExecutableName = Os.isFamily(Os.FAMILY_WINDOWS) ? "flutter.bat" : "flutter"
flutterExecutable = Paths.get(flutterRoot.absolutePath, "bin", flutterExecutableName).toFile();
if (project.hasProperty('localEngineOut')) {
// x86/x86_64 native library used for debugging only, for now.
File flutterX86Jar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/flutter-x86.jar")
Task debugX86JarTask = project.tasks.create("${FLUTTER_BUILD_PREFIX}X86Jar", Jar) {
destinationDir flutterX86Jar.parentFile
archiveName flutterX86Jar.name
from("${flutterRoot}/bin/cache/artifacts/engine/android-x86/libflutter.so") {
into 'lib/x86'
}
from("${flutterRoot}/bin/cache/artifacts/engine/android-x64/libflutter.so") {
into 'lib/x86_64'
}
}
if (useLocalEngine(project)) {
String engineOutPath = project.property('localEngineOut')
File engineOut = project.file(engineOutPath)
if (!engineOut.isDirectory()) {
throw new GradleException('localEngineOut must point to a local engine build')
}
baseEnginePath = Paths.get(engineOut.absolutePath)
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
throw new GradleException('Local engine jar not found: ' + flutterJar)
throw new GradleException("Local engine jar not found: $flutterJar")
}
localEngine = engineOut.name
localEngineSrcPath = engineOut.parentFile.parent
// The local engine is built for one of the build type.
// However, we use the same engine for each of the build types.
debugFlutterJar = flutterJar
profileFlutterJar = flutterJar
releaseFlutterJar = flutterJar
dynamicProfileFlutterJar = flutterJar
dynamicReleaseFlutterJar = flutterJar
project.android.buildTypes.each {
addApiDependencies(project, it, project.files {
[debugX86JarTask, flutterJar]
})
}
} else {
String basePlatformArch = getBasePlatform(project)
// This is meant to include the compiled classes only, however it will include `libflutter.so` as well.
baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
debugFlutterJar = baseEnginePath.resolve("${basePlatformArch}").resolve("flutter.jar").toFile()
profileFlutterJar = baseEnginePath.resolve("${basePlatformArch}-profile").resolve("flutter.jar").toFile()
releaseFlutterJar = baseEnginePath.resolve("${basePlatformArch}-release").resolve("flutter.jar").toFile()
dynamicProfileFlutterJar = baseEnginePath.resolve("${basePlatformArch}-dynamic-profile").resolve("flutter.jar").toFile()
dynamicReleaseFlutterJar = baseEnginePath.resolve("${basePlatformArch}-dynamic-release").resolve("flutter.jar").toFile()
}
if (!debugFlutterJar.isFile()) {
project.exec {
executable flutterExecutable.absolutePath
args "--suppress-analytics"
args "precache"
}
if (!debugFlutterJar.isFile()) {
throw new GradleException("Unable to find flutter.jar in SDK: ${debugFlutterJar}")
Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
File debugJar = baseEnginePath.resolve("${basePlatformArch}").resolve("flutter.jar").toFile()
baseJar["debug"] = [debugX86JarTask, debugJar]
if (!debugJar.isFile()) {
project.exec {
executable flutterExecutable.absolutePath
args "--suppress-analytics"
args "precache"
}
if (!debugJar.isFile()) {
throw new GradleException("Unable to find flutter.jar in SDK: ${debugJar}")
}
}
}
// Add x86/x86_64 native library. Debug mode only, for now.
File flutterX86Jar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/flutter-x86.jar")
Task debugX86JarTask = project.tasks.create("${FLUTTER_BUILD_PREFIX}X86Jar", Jar) {
destinationDir flutterX86Jar.parentFile
archiveName flutterX86Jar.name
from("${flutterRoot}/bin/cache/artifacts/engine/android-x86/libflutter.so") {
into 'lib/x86'
baseJar["profile"] = baseEnginePath.resolve("${basePlatformArch}-profile").resolve("flutter.jar").toFile()
baseJar["release"] = baseEnginePath.resolve("${basePlatformArch}-release").resolve("flutter.jar").toFile()
baseJar["dynamicProfile"] = baseEnginePath.resolve("${basePlatformArch}-dynamic-profile").resolve("flutter.jar").toFile()
baseJar["dynamicRelease"] = baseEnginePath.resolve("${basePlatformArch}-dynamic-release").resolve("flutter.jar").toFile()
// Add flutter.jar dependencies to all <buildType>Api configurations, including custom ones
// added after applying the Flutter plugin.
project.android.buildTypes.each {
def buildMode = buildModeFor(it)
addApiDependencies(project, it, project.files {
baseJar[buildMode]
})
}
from("${flutterRoot}/bin/cache/artifacts/engine/android-x64/libflutter.so") {
into 'lib/x86_64'
project.android.buildTypes.whenObjectAdded {
def buildMode = buildModeFor(it)
addApiDependencies(project, it, project.files {
baseJar[buildMode]
})
}
}
// Add flutter.jar dependencies to all <buildType>Api configurations, including custom ones
// added after applying the Flutter plugin.
project.android.buildTypes.each {
addFlutterJarApiDependency(project, it, debugX86JarTask)
}
project.android.buildTypes.whenObjectAdded {
addFlutterJarApiDependency(project, it, debugX86JarTask)
}
File pluginsFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins')
Properties plugins = readPropertiesIfExist(pluginsFile)
......@@ -322,6 +259,75 @@ class FlutterPlugin implements Plugin<Project> {
}
}
private String resolveProperty(Project project, String name, String defaultValue) {
if (localProperties == null) {
localProperties = readPropertiesIfExist(new File(project.projectDir.parentFile, "local.properties"))
}
String result
if (project.hasProperty(name)) {
result = project.property(name)
}
if (result == null) {
result = localProperties.getProperty(name)
}
if (result == null) {
result = defaultValue
}
return result
}
private static Properties readPropertiesIfExist(File propertiesFile) {
Properties result = new Properties()
if (propertiesFile.exists()) {
propertiesFile.withReader('UTF-8') { reader -> result.load(reader) }
}
return result
}
private static List<String> getTargetPlatforms(Project project) {
if (!project.hasProperty('target-platform')) {
return DEFAULT_PLATFORMS
}
return project.property('target-platform').split(',').collect {
if (!PLATFORM_ARCH_MAP[it]) {
throw new GradleException("Invalid platform: $it.")
}
return it
}
}
private static Boolean getBuildShareLibrary(Project project) {
if (project.hasProperty('build-shared-library')) {
return project.property('build-shared-library').toBoolean()
}
return false;
}
private static Boolean splitPerAbi(Project project) {
if (project.hasProperty('split-per-abi')) {
return project.property('split-per-abi').toBoolean()
}
return false;
}
private static Boolean useLocalEngine(Project project) {
return project.hasProperty('localEngineOut')
}
/**
* Returns the platform that is used to extract the `libflutter.so` and the .class files.
*
* Note: This is only needed to add the .class files.
* Unfortunately, the engine artifacts include the .class and libflutter.so files.
*/
private static String getBasePlatform(Project project) {
if (PLATFORM_ARM64 in getTargetPlatforms(project)) {
return PLATFORM_ARM64;
}
return PLATFORM_ARM32;
}
// TODO(blasten): Clean this up.
private void addFlutterJarCompileOnlyDependency(Project project) {
if (project.state.failure) {
return
......@@ -334,54 +340,33 @@ class FlutterPlugin implements Plugin<Project> {
provided project.files(flutterJar)
}
} else {
assert baseJar["debug"].last().isFile()
assert baseJar["profile"].isFile()
assert baseJar["release"].isFile()
if (project.getConfigurations().findByName("debugCompileOnly")) {
debugCompileOnly project.files(debugFlutterJar)
profileCompileOnly project.files(profileFlutterJar)
releaseCompileOnly project.files(releaseFlutterJar)
debugCompileOnly project.files(baseJar["debug"])
profileCompileOnly project.files(baseJar["profile"])
releaseCompileOnly project.files(baseJar["release"])
} else {
debugProvided project.files(debugFlutterJar)
profileProvided project.files(profileFlutterJar)
releaseProvided project.files(releaseFlutterJar)
debugProvided project.files(baseJar["debug"])
profileProvided project.files(baseJar["profile"])
releaseProvided project.files(baseJar["release"])
}
}
}
}
/**
* Adds suitable flutter.jar api dependencies to the specified buildType.
*
* Note: The BuildType DSL type is not public, and is therefore omitted from the signature.
*/
private void addFlutterJarApiDependency(Project project, buildType, Task debugX86JarTask) {
private static void addApiDependencies(Project project, buildType, FileCollection files) {
project.dependencies {
String configuration;
// `compile` dependencies are now `api` dependencies.
if (project.getConfigurations().findByName("api")) {
configuration = buildType.name + "Api";
} else {
configuration = buildType.name + "Compile";
}
add(configuration, project.files {
String buildMode = buildModeFor(buildType)
switch (buildMode) {
case "debug":
[debugX86JarTask, debugFlutterJar]
break
case "profile":
profileFlutterJar
break
case "dynamicProfile":
dynamicProfileFlutterJar
break
case "dynamicRelease":
dynamicReleaseFlutterJar
break
case "release":
releaseFlutterJar
break
default:
throw new GradleException("Invalid build mode: ${buildMode}")
}
})
add(configuration, files)
}
}
......@@ -475,9 +460,9 @@ class FlutterPlugin implements Plugin<Project> {
extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options')
}
def targetPlatforms = this.getTargetPlatforms(project)
def targetPlatforms = getTargetPlatforms(project)
def addFlutterDeps = { variant ->
if (this.splitPerAbi(project)) {
if (splitPerAbi(project)) {
variant.outputs.each { output ->
// Assigns the new version code to versionCodeOverride, which changes the version code
// for only the output APK, not for the variant itself. Skipping this step simply
......@@ -539,13 +524,14 @@ class FlutterPlugin implements Plugin<Project> {
// This check prevents including `libflutter.so` twice, since it's included in the base platform jar.
// Unfortunately, the `pickFirst` setting in `packagingOptions` does not work when the project `:flutter`
// is included as an implementation dependency, which causes duplicated `libflutter.so`.
if (getBasePlatform(project) != targetArch) {
def engineArtifactSubdir = getEngineArtifactDirName(variant.buildType, targetArch);
// Include `libflutter.so`.
// TODO(blasten): The libs should be outside `flutter.jar` when the artifacts are downloaded.
from(project.zipTree("${flutterRoot}/bin/cache/artifacts/engine/${engineArtifactSubdir}/flutter.jar")) {
include 'lib/**'
}
if (getBasePlatform(project) == targetArch) {
return
}
def engineArtifactSubdir = getEngineArtifactDirName(variant.buildType, targetArch);
// Include `libflutter.so`.
// TODO(blasten): The libs should be outside `flutter.jar` when the artifacts are downloaded.
from(project.zipTree("${flutterRoot}/bin/cache/artifacts/engine/${engineArtifactSubdir}/flutter.jar")) {
include 'lib/**'
}
}
dependsOn flutterTasks
......@@ -560,17 +546,10 @@ class FlutterPlugin implements Plugin<Project> {
}
}
// Include the snapshots and libflutter.so in `lib/`.
project.dependencies {
String configuration;
if (project.getConfigurations().findByName("api")) {
configuration = buildType.name + "Api";
} else {
configuration = buildType.name + "Compile";
}
add(configuration, project.files {
packFlutterSnapshotsAndLibsTask
})
}
addApiDependencies(project, buildType, project.files {
packFlutterSnapshotsAndLibsTask
})
// We know that the flutter app is a subproject in another Android app when these tasks exist.
Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets")
Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets")
......
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