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> {
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) {
dependsOn dependenciesTask
flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable
buildMode variant.name
......@@ -222,7 +235,7 @@ class FlutterExtension {
String target
}
class FlutterTask extends DefaultTask {
abstract class BaseFlutterTask extends DefaultTask {
File flutterRoot
File flutterExecutable
String buildMode
......@@ -232,11 +245,80 @@ class FlutterTask extends DefaultTask {
String targetPath
@Optional @InputFile
File kernelFile
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
File intermediateDir
File getOutputDirectory() {
return intermediateDir
}
CopySpec getAssets() {
return project.copySpec {
......@@ -266,12 +348,7 @@ class FlutterTask extends DefaultTask {
@InputFiles
FileCollection getSourceFiles() {
File dependenciesFile
if (buildMode != 'debug') {
dependenciesFile = project.file("${intermediateDir}/snapshot.d")
} else {
dependenciesFile = project.file("${intermediateDir}/snapshot_blob.bin.d")
}
File dependenciesFile = getDependenciesFile()
FileCollection sources = readDependencies(dependenciesFile)
if (sources != null) {
// We have a dependencies file. Add a dependency on gen_snapshot as well, since the
......@@ -293,49 +370,6 @@ class FlutterTask extends DefaultTask {
@TaskAction
void build() {
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"
}
buildFlx()
}
}
......@@ -78,7 +78,7 @@ String getGradleAppOutDirV2() {
// Note: this call takes about a second to complete.
String _calculateGradleAppOutDirV2() {
final String gradle = ensureGradle();
ensureLocalProperties();
updateLocalProperties();
try {
final String properties = runCheckedSync(
<String>[gradle, 'app:properties'],
......@@ -141,28 +141,42 @@ String ensureGradle() {
return gradle;
}
/// Create android/local.properties if needed.
File ensureLocalProperties() {
final File localProperties = fs.file('android/local.properties');
if (!localProperties.existsSync()) {
localProperties.writeAsStringSync(
'sdk.dir=${escapePath(androidSdk.directory)}\n'
'flutter.sdk=${escapePath(Cache.flutterRoot)}\n'
);
/// Create android/local.properties if needed, and update Flutter settings.
void updateLocalProperties({String projectPath, String buildMode}) {
final File localProperties = (projectPath == null)
? fs.file(fs.path.join('android', 'local.properties'))
: fs.file(fs.path.join(projectPath, 'android', 'local.properties'));
bool changed = false;
SettingsFile settings;
if (localProperties.existsSync()) {
settings = new SettingsFile.parseFromFile(localProperties);
} else {
settings = new SettingsFile();
settings.values['sdk.dir'] = escapePath(androidSdk.directory);
changed = true;
}
return localProperties;
final String escapedRoot = escapePath(Cache.flutterRoot);
if (changed || settings.values['flutter.sdk'] != escapedRoot) {
settings.values['flutter.sdk'] = escapedRoot;
changed = true;
}
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 {
final File localProperties = ensureLocalProperties();
// Update the local.properties file with the build mode.
// FlutterPlugin v1 reads local.properties to determine build mode. Plugin v2
// 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.
final String buildModeName = getModeName(buildMode);
final SettingsFile settings = new SettingsFile.parseFromFile(localProperties);
settings.values['flutter.buildMode'] = buildModeName;
settings.writeContents(localProperties);
updateLocalProperties(buildMode: buildModeName);
injectPlugins();
......
......@@ -151,6 +151,8 @@ class ItemListNotifier<T> {
}
class SettingsFile {
SettingsFile();
SettingsFile.parse(String contents) {
for (String line in contents.split('\n')) {
line = line.trim();
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import '../android/android.dart' as android;
import '../android/android_sdk.dart' as android_sdk;
import '../android/gradle.dart' as gradle;
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/utils.dart';
......@@ -121,6 +122,9 @@ class CreateCommand extends FlutterCommand {
if (argResults['pub'])
await pubGet(directory: dirPath);
if (android_sdk.androidSdk != null)
gradle.updateLocalProperties(projectPath: dirPath);
appPath = fs.path.join(dirPath, 'example');
final String androidPluginIdentifier = templateContext['androidIdentifier'];
final String exampleProjectName = projectName + '_example';
......@@ -153,6 +157,9 @@ class CreateCommand extends FlutterCommand {
injectPlugins(directory: appPath);
}
if (android_sdk.androidSdk != null)
gradle.updateLocalProperties(projectPath: appPath);
printStatus('');
// 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