Commit 6b54137a authored by Jakob Andersen's avatar Jakob Andersen Committed by GitHub

Improve Android builds. (#9801)

Eagerly generate local.properties, and always update the flutter.sdk
setting in it, in case FLUTTER_ROOT has changed.

Fixes #8365.
Fixes #9716 - at least the specific issue reported. My Android Studio
still complains about Gradle versions - it ships with v3.2, but requires
v3.3...

Add a 'generate dependencies' task to the Gradle build, which checks if
the snapshot dependencies file exists, and runs an extra build before
the actual FlutterTask if it doesn't. This makes the first build slower,
but sub-sequent builds (without source changes) much faster.

Fixes #9717.
parent 4bbf158b
...@@ -194,7 +194,20 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -194,7 +194,20 @@ class FlutterPlugin implements Plugin<Project> {
throw new GradleException("Build variant must be one of \"debug\", \"profile\", or \"release\" but was \"${variant.name}\"") throw new GradleException("Build variant must be one of \"debug\", \"profile\", or \"release\" but was \"${variant.name}\"")
} }
GenerateDependencies dependenciesTask = project.tasks.create("flutterDependencies${variant.name.capitalize()}", GenerateDependencies) {
flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable
buildMode variant.name
localEngine this.localEngine
localEngineSrcPath this.localEngineSrcPath
targetPath target
kernelFile kernel
sourceDir project.file(project.flutter.source)
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
}
FlutterTask flutterTask = project.tasks.create("flutterBuild${variant.name.capitalize()}", FlutterTask) { FlutterTask flutterTask = project.tasks.create("flutterBuild${variant.name.capitalize()}", FlutterTask) {
dependsOn dependenciesTask
flutterRoot this.flutterRoot flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable flutterExecutable this.flutterExecutable
buildMode variant.name buildMode variant.name
...@@ -222,7 +235,7 @@ class FlutterExtension { ...@@ -222,7 +235,7 @@ class FlutterExtension {
String target String target
} }
class FlutterTask extends DefaultTask { abstract class BaseFlutterTask extends DefaultTask {
File flutterRoot File flutterRoot
File flutterExecutable File flutterExecutable
String buildMode String buildMode
...@@ -232,11 +245,80 @@ class FlutterTask extends DefaultTask { ...@@ -232,11 +245,80 @@ class FlutterTask extends DefaultTask {
String targetPath String targetPath
@Optional @InputFile @Optional @InputFile
File kernelFile File kernelFile
File sourceDir File sourceDir
File intermediateDir
@OutputFile
File getDependenciesFile() {
if (buildMode != 'debug') {
return project.file("${intermediateDir}/snapshot.d")
}
return project.file("${intermediateDir}/snapshot_blob.bin.d")
}
void buildFlx() {
if (!sourceDir.isDirectory()) {
throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
}
intermediateDir.mkdirs()
if (buildMode != "debug") {
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "aot"
args "--quiet"
args "--target", targetPath
args "--target-platform", "android-arm"
args "--output-dir", "${intermediateDir}"
args "--${buildMode}"
}
}
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "flx"
args "--target", targetPath
if (kernelFile != null) {
args "--kernel", kernelFile.absolutePath
}
args "--output-file", "${intermediateDir}/app.flx"
if (buildMode != "debug") {
args "--precompiled"
} else {
args "--snapshot", "${intermediateDir}/snapshot_blob.bin"
args "--depfile", "${intermediateDir}/snapshot_blob.bin.d"
}
args "--working-dir", "${intermediateDir}/flx"
}
}
}
class GenerateDependencies extends BaseFlutterTask {
@TaskAction
void build() {
File dependenciesFile = getDependenciesFile();
if (!dependenciesFile.exists()) {
buildFlx()
}
}
}
class FlutterTask extends BaseFlutterTask {
@OutputDirectory @OutputDirectory
File intermediateDir File getOutputDirectory() {
return intermediateDir
}
CopySpec getAssets() { CopySpec getAssets() {
return project.copySpec { return project.copySpec {
...@@ -266,12 +348,7 @@ class FlutterTask extends DefaultTask { ...@@ -266,12 +348,7 @@ class FlutterTask extends DefaultTask {
@InputFiles @InputFiles
FileCollection getSourceFiles() { FileCollection getSourceFiles() {
File dependenciesFile File dependenciesFile = getDependenciesFile()
if (buildMode != 'debug') {
dependenciesFile = project.file("${intermediateDir}/snapshot.d")
} else {
dependenciesFile = project.file("${intermediateDir}/snapshot_blob.bin.d")
}
FileCollection sources = readDependencies(dependenciesFile) FileCollection sources = readDependencies(dependenciesFile)
if (sources != null) { if (sources != null) {
// We have a dependencies file. Add a dependency on gen_snapshot as well, since the // We have a dependencies file. Add a dependency on gen_snapshot as well, since the
...@@ -293,49 +370,6 @@ class FlutterTask extends DefaultTask { ...@@ -293,49 +370,6 @@ class FlutterTask extends DefaultTask {
@TaskAction @TaskAction
void build() { void build() {
if (!sourceDir.isDirectory()) { buildFlx()
throw new GradleException("Invalid Flutter source directory: ${sourceDir}")
}
intermediateDir.mkdirs()
if (buildMode != "debug") {
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "aot"
args "--quiet"
args "--target", targetPath
args "--target-platform", "android-arm"
args "--output-dir", "${intermediateDir}"
args "--${buildMode}"
}
}
project.exec {
executable flutterExecutable.absolutePath
workingDir sourceDir
if (localEngine != null) {
args "--local-engine", localEngine
args "--local-engine-src-path", localEngineSrcPath
}
args "build", "flx"
args "--target", targetPath
if (kernelFile != null) {
args "--kernel", kernelFile.absolutePath
}
args "--output-file", "${intermediateDir}/app.flx"
if (buildMode != "debug") {
args "--precompiled"
} else {
args "--snapshot", "${intermediateDir}/snapshot_blob.bin"
args "--depfile", "${intermediateDir}/snapshot_blob.bin.d"
}
args "--working-dir", "${intermediateDir}/flx"
}
} }
} }
...@@ -78,7 +78,7 @@ String getGradleAppOutDirV2() { ...@@ -78,7 +78,7 @@ String getGradleAppOutDirV2() {
// Note: this call takes about a second to complete. // Note: this call takes about a second to complete.
String _calculateGradleAppOutDirV2() { String _calculateGradleAppOutDirV2() {
final String gradle = ensureGradle(); final String gradle = ensureGradle();
ensureLocalProperties(); updateLocalProperties();
try { try {
final String properties = runCheckedSync( final String properties = runCheckedSync(
<String>[gradle, 'app:properties'], <String>[gradle, 'app:properties'],
...@@ -141,28 +141,42 @@ String ensureGradle() { ...@@ -141,28 +141,42 @@ String ensureGradle() {
return gradle; return gradle;
} }
/// Create android/local.properties if needed. /// Create android/local.properties if needed, and update Flutter settings.
File ensureLocalProperties() { void updateLocalProperties({String projectPath, String buildMode}) {
final File localProperties = fs.file('android/local.properties'); final File localProperties = (projectPath == null)
if (!localProperties.existsSync()) { ? fs.file(fs.path.join('android', 'local.properties'))
localProperties.writeAsStringSync( : fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
'sdk.dir=${escapePath(androidSdk.directory)}\n' bool changed = false;
'flutter.sdk=${escapePath(Cache.flutterRoot)}\n'
); SettingsFile settings;
if (localProperties.existsSync()) {
settings = new SettingsFile.parseFromFile(localProperties);
} else {
settings = new SettingsFile();
settings.values['sdk.dir'] = escapePath(androidSdk.directory);
changed = true;
}
final String escapedRoot = escapePath(Cache.flutterRoot);
if (changed || settings.values['flutter.sdk'] != escapedRoot) {
settings.values['flutter.sdk'] = escapedRoot;
changed = true;
} }
return localProperties; if (buildMode != null && settings.values['flutter.buildMode'] != buildMode) {
settings.values['flutter.buildMode'] = buildMode;
changed = true;
}
if (changed)
settings.writeContents(localProperties);
} }
Future<Null> buildGradleProject(BuildMode buildMode, String target, String kernelPath) async { Future<Null> buildGradleProject(BuildMode buildMode, String target, String kernelPath) async {
final File localProperties = ensureLocalProperties();
// Update the local.properties file with the build mode. // Update the local.properties file with the build mode.
// FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2 // FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
// uses the standard Android way to determine what to build, but we still // uses the standard Android way to determine what to build, but we still
// update local.properties, in case we want to use it in the future. // update local.properties, in case we want to use it in the future.
final String buildModeName = getModeName(buildMode); final String buildModeName = getModeName(buildMode);
final SettingsFile settings = new SettingsFile.parseFromFile(localProperties); updateLocalProperties(buildMode: buildModeName);
settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties);
injectPlugins(); injectPlugins();
......
...@@ -151,6 +151,8 @@ class ItemListNotifier<T> { ...@@ -151,6 +151,8 @@ class ItemListNotifier<T> {
} }
class SettingsFile { class SettingsFile {
SettingsFile();
SettingsFile.parse(String contents) { SettingsFile.parse(String contents) {
for (String line in contents.split('\n')) { for (String line in contents.split('\n')) {
line = line.trim(); line = line.trim();
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import '../android/android.dart' as android; import '../android/android.dart' as android;
import '../android/android_sdk.dart' as android_sdk; import '../android/android_sdk.dart' as android_sdk;
import '../android/gradle.dart' as gradle;
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/utils.dart'; import '../base/utils.dart';
...@@ -121,6 +122,9 @@ class CreateCommand extends FlutterCommand { ...@@ -121,6 +122,9 @@ class CreateCommand extends FlutterCommand {
if (argResults['pub']) if (argResults['pub'])
await pubGet(directory: dirPath); await pubGet(directory: dirPath);
if (android_sdk.androidSdk != null)
gradle.updateLocalProperties(projectPath: dirPath);
appPath = fs.path.join(dirPath, 'example'); appPath = fs.path.join(dirPath, 'example');
final String androidPluginIdentifier = templateContext['androidIdentifier']; final String androidPluginIdentifier = templateContext['androidIdentifier'];
final String exampleProjectName = projectName + '_example'; final String exampleProjectName = projectName + '_example';
...@@ -153,6 +157,9 @@ class CreateCommand extends FlutterCommand { ...@@ -153,6 +157,9 @@ class CreateCommand extends FlutterCommand {
injectPlugins(directory: appPath); injectPlugins(directory: appPath);
} }
if (android_sdk.androidSdk != null)
gradle.updateLocalProperties(projectPath: appPath);
printStatus(''); printStatus('');
// Run doctor; tell the user the next steps. // Run doctor; tell the user the next steps.
......
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