mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
841e1cf0af
Co-authored-by: Miguel Lemos <miguelslemos@gmail.com>
814 lines
32 KiB
Groovy
814 lines
32 KiB
Groovy
import java.nio.file.Path
|
|
import java.nio.file.Paths
|
|
|
|
import com.android.builder.model.AndroidProject
|
|
import org.apache.tools.ant.taskdefs.condition.Os
|
|
import org.gradle.api.DefaultTask
|
|
import org.gradle.api.GradleException
|
|
import org.gradle.api.Project
|
|
import org.gradle.api.Plugin
|
|
import org.gradle.api.Task
|
|
import org.gradle.api.file.CopySpec
|
|
import org.gradle.api.file.FileCollection
|
|
import org.gradle.api.tasks.Copy
|
|
import org.gradle.api.tasks.InputFiles
|
|
import org.gradle.api.tasks.OutputDirectory
|
|
import org.gradle.api.tasks.TaskAction
|
|
import org.gradle.api.tasks.bundling.Jar
|
|
|
|
buildscript {
|
|
repositories {
|
|
google()
|
|
jcenter()
|
|
}
|
|
dependencies {
|
|
classpath 'com.android.tools.build:gradle:3.2.1'
|
|
}
|
|
}
|
|
|
|
android {
|
|
compileOptions {
|
|
sourceCompatibility 1.8
|
|
targetCompatibility 1.8
|
|
}
|
|
}
|
|
|
|
apply plugin: FlutterPlugin
|
|
|
|
class FlutterPlugin implements Plugin<Project> {
|
|
private Path baseEnginePath
|
|
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
|
|
|
|
// The name prefix for flutter builds. This is used to identify gradle tasks
|
|
// where we expect the flutter tool to provide any error output, and skip the
|
|
// standard Gradle error output in the FlutterEventLogger. If you change this,
|
|
// be sure to change any instances of this string in symbols in the code below
|
|
// to match.
|
|
static final String flutterBuildPrefix = "flutterBuild"
|
|
|
|
// The platforms (or CPU architectures) for which native code is generated.
|
|
static final Map allTargetPlatforms = [
|
|
'android-arm': 'armeabi-v7a',
|
|
'android-arm64': 'arm64-v8a',
|
|
'android-x64': 'x86_64',
|
|
'android-x86': 'x86',
|
|
]
|
|
|
|
// Supports ARM 32 and 64 bits.
|
|
// When splits are enabled, multiple APKs are generated per each CPU architecture,
|
|
// which helps decrease the size of each APK.
|
|
static final Set allArmPlatforms = [
|
|
'android-arm',
|
|
'android-arm64'
|
|
]
|
|
|
|
private Properties readPropertiesIfExist(File propertiesFile) {
|
|
Properties result = new Properties()
|
|
if (propertiesFile.exists()) {
|
|
propertiesFile.withReader('UTF-8') { reader -> result.load(reader) }
|
|
}
|
|
return result
|
|
}
|
|
|
|
private String getTargetPlatform(Project project) {
|
|
if (project.hasProperty('target-platform')) {
|
|
return project.property('target-platform')
|
|
}
|
|
return 'android-arm-all';
|
|
}
|
|
|
|
private Boolean getBuildShareLibrary(Project project) {
|
|
if (project.hasProperty('build-shared-library')) {
|
|
return project.property('build-shared-library').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
|
|
}
|
|
|
|
@Override
|
|
void apply(Project project) {
|
|
project.extensions.create("flutter", FlutterExtension)
|
|
project.afterEvaluate this.&addFlutterTask
|
|
|
|
allTargetPlatforms.each { currentTargetPlatformValue, abi ->
|
|
project.android {
|
|
packagingOptions {
|
|
pickFirst "lib/${abi}/libflutter.so"
|
|
|
|
// Disable warning by *-android-strip: File format not recognized
|
|
doNotStrip "*/${abi}/lib_vm_snapshot_data.so"
|
|
doNotStrip "*/${abi}/lib_vm_snapshot_instr.so"
|
|
doNotStrip "*/${abi}/lib_isolate_snapshot_data.so"
|
|
doNotStrip "*/${abi}/lib_isolate_snapshot_instr.so"
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add custom build types
|
|
project.android.buildTypes {
|
|
profile {
|
|
initWith debug
|
|
if (it.hasProperty('matchingFallbacks')) {
|
|
matchingFallbacks = ['debug', 'release']
|
|
}
|
|
}
|
|
dynamicProfile {
|
|
initWith debug
|
|
if (it.hasProperty('matchingFallbacks')) {
|
|
matchingFallbacks = ['debug', 'release']
|
|
}
|
|
}
|
|
dynamicRelease {
|
|
initWith debug
|
|
if (it.hasProperty('matchingFallbacks')) {
|
|
matchingFallbacks = ['debug', 'release']
|
|
}
|
|
}
|
|
}
|
|
|
|
String flutterRootPath = resolveProperty(project, "flutter.sdk", System.env.FLUTTER_ROOT)
|
|
if (flutterRootPath == null) {
|
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file or with a FLUTTER_ROOT environment variable.")
|
|
}
|
|
flutterRoot = project.file(flutterRootPath)
|
|
if (!flutterRoot.isDirectory()) {
|
|
throw new GradleException("flutter.sdk must point to the Flutter SDK directory")
|
|
}
|
|
|
|
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('File 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 targetPlatform = getTargetPlatform(project)
|
|
String targetArch = targetPlatform == 'android-arm64' ? 'arm64' : 'arm'
|
|
baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")
|
|
debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile()
|
|
profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile()
|
|
releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile()
|
|
dynamicProfileFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-profile").resolve("flutter.jar").toFile()
|
|
dynamicReleaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-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.
|
|
File flutterX86Jar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/flutter-x86.jar")
|
|
Task debugX86JarTask = project.tasks.create("${flutterBuildPrefix}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'
|
|
}
|
|
}
|
|
// 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)
|
|
|
|
plugins.each { name, _ ->
|
|
def pluginProject = project.rootProject.findProject(":$name")
|
|
if (pluginProject != null) {
|
|
project.dependencies {
|
|
if (project.getConfigurations().findByName("implementation")) {
|
|
implementation pluginProject
|
|
} else {
|
|
compile pluginProject
|
|
}
|
|
}
|
|
pluginProject.afterEvaluate {
|
|
pluginProject.android.buildTypes {
|
|
profile {
|
|
initWith debug
|
|
}
|
|
}
|
|
}
|
|
pluginProject.afterEvaluate this.&addFlutterJarCompileOnlyDependency
|
|
} else {
|
|
project.logger.error("Plugin project :$name not found. Please update settings.gradle.")
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addFlutterJarCompileOnlyDependency(Project project) {
|
|
if (project.state.failure) {
|
|
return
|
|
}
|
|
project.dependencies {
|
|
if (flutterJar != null) {
|
|
if (project.getConfigurations().findByName("compileOnly")) {
|
|
compileOnly project.files(flutterJar)
|
|
} else {
|
|
provided project.files(flutterJar)
|
|
}
|
|
} else {
|
|
if (project.getConfigurations().findByName("debugCompileOnly")) {
|
|
debugCompileOnly project.files(debugFlutterJar)
|
|
profileCompileOnly project.files(profileFlutterJar)
|
|
releaseCompileOnly project.files(releaseFlutterJar)
|
|
} else {
|
|
debugProvided project.files(debugFlutterJar)
|
|
profileProvided project.files(profileFlutterJar)
|
|
releaseProvided project.files(releaseFlutterJar)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 {
|
|
String configuration;
|
|
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}")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a Flutter build mode suitable for the specified Android buildType.
|
|
*
|
|
* Note: The BuildType DSL type is not public, and is therefore omitted from the signature.
|
|
*
|
|
* @return "debug", "profile", "dynamicProfile", "dynamicRelease", or "release" (fall-back).
|
|
*/
|
|
private static String buildModeFor(buildType) {
|
|
if (buildType.name == "profile") {
|
|
return "profile"
|
|
} else if (buildType.name == "dynamicProfile") {
|
|
return "dynamicProfile"
|
|
} else if (buildType.name == "dynamicRelease") {
|
|
return "dynamicRelease"
|
|
} else if (buildType.debuggable) {
|
|
return "debug"
|
|
}
|
|
return "release"
|
|
}
|
|
|
|
private void addFlutterTask(Project project) {
|
|
if (project.state.failure) {
|
|
return
|
|
}
|
|
if (project.flutter.source == null) {
|
|
throw new GradleException("Must provide Flutter source directory")
|
|
}
|
|
|
|
String target = project.flutter.target
|
|
if (target == null) {
|
|
target = 'lib/main.dart'
|
|
}
|
|
if (project.hasProperty('target')) {
|
|
target = project.property('target')
|
|
}
|
|
|
|
Boolean verboseValue = null
|
|
if (project.hasProperty('verbose')) {
|
|
verboseValue = project.property('verbose').toBoolean()
|
|
}
|
|
String[] fileSystemRootsValue = null
|
|
if (project.hasProperty('filesystem-roots')) {
|
|
fileSystemRootsValue = project.property('filesystem-roots').split('\\|')
|
|
}
|
|
String fileSystemSchemeValue = null
|
|
if (project.hasProperty('filesystem-scheme')) {
|
|
fileSystemSchemeValue = project.property('filesystem-scheme')
|
|
}
|
|
Boolean trackWidgetCreationValue = false
|
|
if (project.hasProperty('track-widget-creation')) {
|
|
trackWidgetCreationValue = project.property('track-widget-creation').toBoolean()
|
|
}
|
|
String compilationTraceFilePathValue = null
|
|
if (project.hasProperty('compilation-trace-file')) {
|
|
compilationTraceFilePathValue = project.property('compilation-trace-file')
|
|
}
|
|
Boolean createPatchValue = false
|
|
if (project.hasProperty('patch')) {
|
|
createPatchValue = project.property('patch').toBoolean()
|
|
}
|
|
Integer buildNumberValue = null
|
|
if (project.hasProperty('build-number')) {
|
|
buildNumberValue = project.property('build-number').toInteger()
|
|
}
|
|
String baselineDirValue = null
|
|
if (project.hasProperty('baseline-dir')) {
|
|
baselineDirValue = project.property('baseline-dir')
|
|
}
|
|
String extraFrontEndOptionsValue = null
|
|
if (project.hasProperty('extra-front-end-options')) {
|
|
extraFrontEndOptionsValue = project.property('extra-front-end-options')
|
|
}
|
|
String extraGenSnapshotOptionsValue = null
|
|
if (project.hasProperty('extra-gen-snapshot-options')) {
|
|
extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options')
|
|
}
|
|
|
|
Boolean buildSharedLibraryValue = this.getBuildShareLibrary(project)
|
|
String targetPlatformValue = this.getTargetPlatform(project)
|
|
|
|
def addFlutterDeps = { variant ->
|
|
String flutterBuildMode = buildModeFor(variant.buildType)
|
|
|
|
if (flutterBuildMode == 'debug' && project.tasks.findByName('${flutterBuildPrefix}X86Jar')) {
|
|
Task task = project.tasks.findByName("compile${variant.name.capitalize()}JavaWithJavac")
|
|
if (task) {
|
|
task.dependsOn project.flutterBuildX86Jar
|
|
}
|
|
task = project.tasks.findByName("compile${variant.name.capitalize()}Kotlin")
|
|
if (task) {
|
|
task.dependsOn project.flutterBuildX86Jar
|
|
}
|
|
}
|
|
|
|
def flutterTasks = []
|
|
def targetPlatforms = []
|
|
|
|
if (targetPlatformValue == 'android-arm-all') {
|
|
if (flutterBuildMode == 'release') {
|
|
targetPlatforms.addAll(allArmPlatforms)
|
|
} else {
|
|
targetPlatforms.add('android-arm')
|
|
}
|
|
} else {
|
|
targetPlatforms.add(targetPlatformValue)
|
|
}
|
|
|
|
targetPlatforms.each { currentTargetPlatformValue ->
|
|
String abiValue = allTargetPlatforms[currentTargetPlatformValue]
|
|
FlutterTask compileTask = project.tasks.create(name: "compile${flutterBuildPrefix}${variant.name.capitalize()}${currentTargetPlatformValue}",
|
|
type: FlutterTask) {
|
|
flutterRoot this.flutterRoot
|
|
flutterExecutable this.flutterExecutable
|
|
buildMode flutterBuildMode
|
|
localEngine this.localEngine
|
|
localEngineSrcPath this.localEngineSrcPath
|
|
abi abiValue
|
|
targetPath target
|
|
verbose verboseValue
|
|
fileSystemRoots fileSystemRootsValue
|
|
fileSystemScheme fileSystemSchemeValue
|
|
trackWidgetCreation trackWidgetCreationValue
|
|
compilationTraceFilePath compilationTraceFilePathValue
|
|
createPatch createPatchValue
|
|
buildNumber buildNumberValue
|
|
baselineDir baselineDirValue
|
|
buildSharedLibrary buildSharedLibraryValue
|
|
targetPlatform currentTargetPlatformValue
|
|
sourceDir project.file(project.flutter.source)
|
|
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}/${currentTargetPlatformValue}")
|
|
extraFrontEndOptions extraFrontEndOptionsValue
|
|
extraGenSnapshotOptions extraGenSnapshotOptionsValue
|
|
}
|
|
flutterTasks.add(compileTask)
|
|
}
|
|
|
|
def libJar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/libs.jar")
|
|
Task packFlutterSnapshotsAndLibsTask = project.tasks.create(name: "packFlutterSnapshotsAndLibs${flutterBuildPrefix}${variant.name.capitalize()}", type: Jar) {
|
|
destinationDir libJar.parentFile
|
|
archiveName libJar.name
|
|
targetPlatforms.each { targetPlatform ->
|
|
// Include `libflutter.so` for each abi.
|
|
// TODO(blasten): The libs should be outside `flutter.jar` when the artifacts are downloaded.
|
|
from(project.zipTree("${flutterRoot}/bin/cache/artifacts/engine/${targetPlatform}-release/flutter.jar")) {
|
|
include 'lib/**'
|
|
}
|
|
}
|
|
|
|
dependsOn flutterTasks
|
|
// Add the snapshots and rename them as `lib/{abi}/*.so`.
|
|
flutterTasks.each { flutterTask ->
|
|
from(flutterTask.intermediateDir) {
|
|
include 'vm_snapshot_data'
|
|
include 'vm_snapshot_instr'
|
|
include 'isolate_snapshot_data'
|
|
include 'isolate_snapshot_instr'
|
|
rename { String filename ->
|
|
return "lib/${flutterTask.abi}/lib_${filename}.so"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Include the snapshots and libflutter.so in `lib/`.
|
|
if (flutterBuildMode == 'release' && targetPlatformValue == 'android-arm-all') {
|
|
project.dependencies {
|
|
String configuration;
|
|
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.
|
|
Task packageAssets = project.tasks.findByPath(":flutter:package${variant.name.capitalize()}Assets")
|
|
Task cleanPackageAssets = project.tasks.findByPath(":flutter:cleanPackage${variant.name.capitalize()}Assets")
|
|
Task copyFlutterAssetsTask = project.tasks.create(name: "copyFlutterAssets${variant.name.capitalize()}", type: Copy) {
|
|
dependsOn flutterTasks
|
|
if (packageAssets && cleanPackageAssets) {
|
|
dependsOn packageAssets
|
|
dependsOn cleanPackageAssets
|
|
into packageAssets.outputDir
|
|
} else {
|
|
dependsOn variant.mergeAssets
|
|
dependsOn "clean${variant.mergeAssets.name.capitalize()}"
|
|
variant.mergeAssets.mustRunAfter("clean${variant.mergeAssets.name.capitalize()}")
|
|
into variant.mergeAssets.outputDir
|
|
}
|
|
flutterTasks.each { flutterTask ->
|
|
with flutterTask.assets
|
|
// Include the snapshots in the assets directory.
|
|
if (flutterBuildMode != 'release' || targetPlatformValue != 'android-arm-all') {
|
|
with flutterTask.snapshots
|
|
}
|
|
}
|
|
}
|
|
if (packageAssets) {
|
|
String mainModuleName = "app"
|
|
try {
|
|
String tmpModuleName = project.rootProject.ext.mainModuleName
|
|
if (tmpModuleName != null && !tmpModuleName.empty) {
|
|
mainModuleName = tmpModuleName
|
|
}
|
|
} catch (Exception e) {
|
|
}
|
|
// Only include configurations that exist in parent project.
|
|
Task mergeAssets = project.tasks.findByPath(":${mainModuleName}:merge${variant.name.capitalize()}Assets")
|
|
if (mergeAssets) {
|
|
mergeAssets.dependsOn(copyFlutterAssetsTask)
|
|
}
|
|
} else {
|
|
def processResources = variant.outputs.first().processResources
|
|
processResources.dependsOn(copyFlutterAssetsTask)
|
|
}
|
|
}
|
|
|
|
if (project.android.hasProperty("applicationVariants")) {
|
|
project.android.applicationVariants.all addFlutterDeps
|
|
} else {
|
|
project.android.libraryVariants.all addFlutterDeps
|
|
}
|
|
}
|
|
}
|
|
|
|
class FlutterExtension {
|
|
String source
|
|
String target
|
|
}
|
|
|
|
abstract class BaseFlutterTask extends DefaultTask {
|
|
File flutterRoot
|
|
File flutterExecutable
|
|
String buildMode
|
|
String localEngine
|
|
String localEngineSrcPath
|
|
@Input
|
|
String targetPath
|
|
@Optional @Input
|
|
Boolean verbose
|
|
@Optional @Input
|
|
String[] fileSystemRoots
|
|
@Optional @Input
|
|
String fileSystemScheme
|
|
@Input
|
|
Boolean trackWidgetCreation
|
|
@Optional @Input
|
|
String compilationTraceFilePath
|
|
@Optional @Input
|
|
Boolean createPatch
|
|
@Optional @Input
|
|
Integer buildNumber
|
|
@Optional @Input
|
|
String baselineDir
|
|
@Optional @Input
|
|
Boolean buildSharedLibrary
|
|
@Optional @Input
|
|
String targetPlatform
|
|
@Input
|
|
String abi
|
|
File sourceDir
|
|
File intermediateDir
|
|
@Optional @Input
|
|
String extraFrontEndOptions
|
|
@Optional @Input
|
|
String extraGenSnapshotOptions
|
|
|
|
@OutputFiles
|
|
FileCollection getDependenciesFiles() {
|
|
FileCollection depfiles = project.files()
|
|
|
|
// Include the kernel compiler depfile, since kernel compile is the
|
|
// first stage of AOT build in this mode, and it includes all the Dart
|
|
// sources.
|
|
depfiles += project.files("${intermediateDir}/kernel_compile.d")
|
|
|
|
// Include Core JIT kernel compiler depfile, since kernel compile is
|
|
// the first stage of JIT builds in this mode, and it includes all the
|
|
// Dart sources.
|
|
depfiles += project.files("${intermediateDir}/snapshot_blob.bin.d")
|
|
return depfiles
|
|
}
|
|
|
|
void buildBundle() {
|
|
if (!sourceDir.isDirectory()) {
|
|
throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
|
|
}
|
|
|
|
intermediateDir.mkdirs()
|
|
|
|
if (buildMode == "profile" || buildMode == "release") {
|
|
project.exec {
|
|
executable flutterExecutable.absolutePath
|
|
workingDir sourceDir
|
|
if (localEngine != null) {
|
|
args "--local-engine", localEngine
|
|
args "--local-engine-src-path", localEngineSrcPath
|
|
}
|
|
args "build", "aot"
|
|
args "--suppress-analytics"
|
|
args "--quiet"
|
|
args "--target", targetPath
|
|
args "--output-dir", "${intermediateDir}"
|
|
if (trackWidgetCreation) {
|
|
args "--track-widget-creation"
|
|
}
|
|
if (extraFrontEndOptions != null) {
|
|
args "--extra-front-end-options", "${extraFrontEndOptions}"
|
|
}
|
|
if (extraGenSnapshotOptions != null) {
|
|
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
|
|
}
|
|
if (buildSharedLibrary) {
|
|
args "--build-shared-library"
|
|
}
|
|
if (targetPlatform == null) {
|
|
args "--target-platform", "android-arm"
|
|
} else {
|
|
args "--target-platform", "${targetPlatform}"
|
|
}
|
|
args "--${buildMode}"
|
|
}
|
|
}
|
|
|
|
project.exec {
|
|
executable flutterExecutable.absolutePath
|
|
workingDir sourceDir
|
|
if (localEngine != null) {
|
|
args "--local-engine", localEngine
|
|
args "--local-engine-src-path", localEngineSrcPath
|
|
}
|
|
args "build", "bundle"
|
|
args "--suppress-analytics"
|
|
args "--target", targetPath
|
|
if (verbose) {
|
|
args "--verbose"
|
|
}
|
|
if (fileSystemRoots != null) {
|
|
for (root in fileSystemRoots) {
|
|
args "--filesystem-root", root
|
|
}
|
|
}
|
|
if (fileSystemScheme != null) {
|
|
args "--filesystem-scheme", fileSystemScheme
|
|
}
|
|
if (trackWidgetCreation) {
|
|
args "--track-widget-creation"
|
|
}
|
|
if (compilationTraceFilePath != null) {
|
|
args "--compilation-trace-file", compilationTraceFilePath
|
|
}
|
|
if (createPatch) {
|
|
args "--patch"
|
|
args "--build-number", project.android.defaultConfig.versionCode
|
|
if (buildNumber != null) {
|
|
assert buildNumber == project.android.defaultConfig.versionCode
|
|
}
|
|
}
|
|
if (baselineDir != null) {
|
|
args "--baseline-dir", baselineDir
|
|
}
|
|
if (extraFrontEndOptions != null) {
|
|
args "--extra-front-end-options", "${extraFrontEndOptions}"
|
|
}
|
|
if (extraGenSnapshotOptions != null) {
|
|
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
|
|
}
|
|
if (targetPlatform != null) {
|
|
args "--target-platform", "${targetPlatform}"
|
|
}
|
|
if (buildMode == "release" || buildMode == "profile") {
|
|
args "--precompiled"
|
|
} else {
|
|
args "--depfile", "${intermediateDir}/snapshot_blob.bin.d"
|
|
}
|
|
args "--asset-dir", "${intermediateDir}/flutter_assets"
|
|
if (buildMode == "debug") {
|
|
args "--debug"
|
|
}
|
|
if (buildMode == "profile" || buildMode == "dynamicProfile") {
|
|
args "--profile"
|
|
}
|
|
if (buildMode == "release" || buildMode == "dynamicRelease") {
|
|
args "--release"
|
|
}
|
|
if (buildMode == "dynamicProfile" || buildMode == "dynamicRelease") {
|
|
args "--dynamic"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class FlutterTask extends BaseFlutterTask {
|
|
@OutputDirectory
|
|
File getOutputDirectory() {
|
|
return intermediateDir
|
|
}
|
|
|
|
CopySpec getAssets() {
|
|
return project.copySpec {
|
|
from "${intermediateDir}"
|
|
include "flutter_assets/**" // the working dir and its files
|
|
}
|
|
}
|
|
|
|
CopySpec getSnapshots() {
|
|
return project.copySpec {
|
|
from "${intermediateDir}"
|
|
|
|
if (buildMode == 'release' || buildMode == 'profile') {
|
|
if (buildSharedLibrary) {
|
|
include "app.so"
|
|
} else {
|
|
include "vm_snapshot_data"
|
|
include "vm_snapshot_instr"
|
|
include "isolate_snapshot_data"
|
|
include "isolate_snapshot_instr"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FileCollection readDependencies(File dependenciesFile) {
|
|
if (dependenciesFile.exists()) {
|
|
try {
|
|
// Dependencies file has Makefile syntax:
|
|
// <target> <files>: <source> <files> <separated> <by> <non-escaped space>
|
|
String depText = dependenciesFile.text
|
|
// So we split list of files by non-escaped(by backslash) space,
|
|
def matcher = depText.split(': ')[1] =~ /(\\ |[^\s])+/
|
|
// then we replace all escaped spaces with regular spaces
|
|
def depList = matcher.collect{it[0].replaceAll("\\\\ ", " ")}
|
|
return project.files(depList)
|
|
} catch (Exception e) {
|
|
logger.error("Error reading dependency file ${dependenciesFile}: ${e}")
|
|
}
|
|
}
|
|
return project.files()
|
|
}
|
|
|
|
@InputFiles
|
|
FileCollection getSourceFiles() {
|
|
FileCollection sources = project.files()
|
|
for (File depfile in getDependenciesFiles()) {
|
|
sources += readDependencies(depfile)
|
|
}
|
|
if (!sources.isEmpty()) {
|
|
// We have a dependencies file. Add a dependency on gen_snapshot as well, since the
|
|
// snapshots have to be rebuilt if it changes.
|
|
sources += readDependencies(project.file("${intermediateDir}/gen_snapshot.d"))
|
|
sources += readDependencies(project.file("${intermediateDir}/frontend_server.d"))
|
|
if (localEngineSrcPath != null) {
|
|
sources += project.files("$localEngineSrcPath/$localEngine")
|
|
}
|
|
// Finally, add a dependency on pubspec.yaml as well.
|
|
return sources + project.files('pubspec.yaml')
|
|
}
|
|
// No dependencies file (or problems parsing it). Fall back to source files.
|
|
return project.fileTree(
|
|
dir: sourceDir,
|
|
exclude: ['android', 'ios'],
|
|
include: ['**/*.dart', 'pubspec.yaml']
|
|
)
|
|
}
|
|
|
|
@TaskAction
|
|
void build() {
|
|
buildBundle()
|
|
}
|
|
}
|
|
|
|
gradle.useLogger(new FlutterEventLogger())
|
|
|
|
class FlutterEventLogger extends BuildAdapter implements TaskExecutionListener {
|
|
String mostRecentTask = ""
|
|
|
|
void beforeExecute(Task task) {
|
|
mostRecentTask = task.name
|
|
}
|
|
|
|
void afterExecute(Task task, TaskState state) {}
|
|
|
|
void buildFinished(BuildResult result) {
|
|
if (result.failure != null) {
|
|
if (!(result.failure instanceof GradleException) || !mostRecentTask.startsWith(FlutterPlugin.flutterBuildPrefix)) {
|
|
result.rethrowFailure()
|
|
}
|
|
}
|
|
}
|
|
}
|