Refactor Gradle plugin (#34353)

This commit is contained in:
Emmanuel Garcia 2019-06-13 16:05:28 -07:00 committed by GitHub
parent 361e8c75d7
commit 1c6cda9a0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -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,50 +162,7 @@ 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')) {
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)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
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
} 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}")
}
}
// Add x86/x86_64 native library. Debug mode only, for now.
// 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
@ -286,13 +174,62 @@ class FlutterPlugin implements Plugin<Project> {
into 'lib/x86_64'
}
}
// 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)
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')
}
Path baseEnginePath = Paths.get(engineOut.absolutePath)
flutterJar = baseEnginePath.resolve("flutter.jar").toFile()
if (!flutterJar.isFile()) {
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.
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.
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}")
}
}
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]
})
}
project.android.buildTypes.whenObjectAdded {
def buildMode = buildModeFor(it)
addApiDependencies(project, it, project.files {
baseJar[buildMode]
})
}
}
File pluginsFile = new File(project.projectDir.parentFile.parentFile, '.flutter-plugins')
@ -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")