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