Unverified Commit e422d5c7 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] reland: --no-android-gradle-daemon in devicelab (#68491)

Remove devicelab specific code for shutting down gradle daemon, add --android-gradle-daemon option to build/run/drive`. Avoids need for un-tested devicelab specific handler. There are also some feature requests for this, so 2 birds one stone.

Example:

flutter build apk --no-android-gradle-daemon will pass --no-daemon on to gradle
parent 8df0c5e1
...@@ -21,6 +21,7 @@ void main() { ...@@ -21,6 +21,7 @@ void main() {
await inDirectory(complexLayoutPath, () async { await inDirectory(complexLayoutPath, () async {
await flutter('drive', options: <String>[ await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v', '-v',
'--profile', '--profile',
'--trace-startup', // Enables "endless" timeline event buffering. '--trace-startup', // Enables "endless" timeline event buffering.
......
...@@ -6,7 +6,6 @@ import 'dart:async'; ...@@ -6,7 +6,6 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:vm_service_client/vm_service_client.dart'; import 'package:vm_service_client/vm_service_client.dart';
import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/framework/utils.dart';
...@@ -88,7 +87,6 @@ Future<TaskResult> runTask( ...@@ -88,7 +87,6 @@ Future<TaskResult> runTask(
} finally { } finally {
if (!runnerFinished) if (!runnerFinished)
runner.kill(ProcessSignal.sigkill); runner.kill(ProcessSignal.sigkill);
await cleanupSystem();
await stdoutSub.cancel(); await stdoutSub.cancel();
await stderrSub.cancel(); await stderrSub.cancel();
} }
...@@ -124,42 +122,3 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async { ...@@ -124,42 +122,3 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async {
} }
} }
} }
Future<void> cleanupSystem() async {
if (deviceOperatingSystem == null || deviceOperatingSystem == DeviceOperatingSystem.android) {
print('\n\nCleaning up system after task...');
final String javaHome = await findJavaHome();
if (javaHome != null) {
// To shut gradle down, we have to call "gradlew --stop".
// To call gradlew, we need to have a gradle-wrapper.properties file along
// with a shell script, a .jar file, etc. We get these from various places
// as you see in the code below, and we save them all into a temporary dir
// which we can then delete after.
// All the steps below are somewhat tolerant of errors, because it doesn't
// really matter if this succeeds every time or not.
print('\nTelling Gradle to shut down (JAVA_HOME=$javaHome)');
final String gradlewBinaryName = Platform.isWindows ? 'gradlew.bat' : 'gradlew';
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_devicelab_shutdown_gradle.');
recursiveCopy(Directory(path.join(flutterDirectory.path, 'bin', 'cache', 'artifacts', 'gradle_wrapper')), tempDir);
copy(File(path.join(path.join(flutterDirectory.path, 'packages', 'flutter_tools'), 'templates', 'app', 'android.tmpl', 'gradle', 'wrapper', 'gradle-wrapper.properties')), Directory(path.join(tempDir.path, 'gradle', 'wrapper')));
if (!Platform.isWindows) {
await exec(
'chmod',
<String>['a+x', path.join(tempDir.path, gradlewBinaryName)],
canFail: true,
);
}
await exec(
path.join(tempDir.path, gradlewBinaryName),
<String>['--stop'],
environment: <String, String>{ 'JAVA_HOME': javaHome},
workingDirectory: tempDir.path,
canFail: true,
);
rmTree(tempDir);
print('\n');
} else {
print('Could not determine JAVA_HOME; not shutting down Gradle.');
}
}
}
...@@ -17,6 +17,7 @@ Future<TaskResult> runDartDefinesTask() async { ...@@ -17,6 +17,7 @@ Future<TaskResult> runDartDefinesTask() async {
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[ await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'--verbose', '--verbose',
'-d', '-d',
deviceId, deviceId,
......
...@@ -71,6 +71,7 @@ class GalleryTransitionTest { ...@@ -71,6 +71,7 @@ class GalleryTransitionTest {
'build', 'build',
options: <String>[ options: <String>[
'apk', 'apk',
'--no-android-gradle-daemon',
'--profile', '--profile',
'-t', '-t',
'test_driver/$testFile.dart', 'test_driver/$testFile.dart',
......
...@@ -26,7 +26,7 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env ...@@ -26,7 +26,7 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env
final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json')); final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json'));
rm(benchmarkFile); rm(benchmarkFile);
final List<String> options = <String>[ final List<String> options = <String>[
'--hot', '-d', deviceIdOverride, '--benchmark', '--verbose', '--resident', '--hot', '-d', deviceIdOverride, '--benchmark', '--verbose', '--resident', '--no-android-gradle-daemon',
]; ];
int hotReloadCount = 0; int hotReloadCount = 0;
Map<String, dynamic> twoReloadsData; Map<String, dynamic> twoReloadsData;
......
...@@ -130,6 +130,7 @@ class DriverTest { ...@@ -130,6 +130,7 @@ class DriverTest {
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
final List<String> options = <String>[ final List<String> options = <String>[
'--no-android-gradle-daemon',
'-v', '-v',
'-t', '-t',
testTarget, testTarget,
......
...@@ -347,6 +347,7 @@ TaskFunction createsScrollSmoothnessPerfTest() { ...@@ -347,6 +347,7 @@ TaskFunction createsScrollSmoothnessPerfTest() {
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[ await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v', '-v',
'--verbose-system-logs', '--verbose-system-logs',
'--profile', '--profile',
...@@ -395,6 +396,7 @@ TaskFunction createFramePolicyIntegrationTest() { ...@@ -395,6 +396,7 @@ TaskFunction createFramePolicyIntegrationTest() {
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[ await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v', '-v',
'--verbose-system-logs', '--verbose-system-logs',
'--profile', '--profile',
...@@ -459,6 +461,7 @@ class StartupTest { ...@@ -459,6 +461,7 @@ class StartupTest {
final List<Map<String, dynamic>> results = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
for (int i = 0; i < iterations; ++i) { for (int i = 0; i < iterations; ++i) {
await flutter('run', options: <String>[ await flutter('run', options: <String>[
'--no-android-gradle-daemon',
'--verbose', '--verbose',
'--profile', '--profile',
'--trace-startup', '--trace-startup',
...@@ -575,6 +578,7 @@ class PerfTest { ...@@ -575,6 +578,7 @@ class PerfTest {
await flutter('packages', options: <String>['get']); await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[ await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v', '-v',
'--verbose-system-logs', '--verbose-system-logs',
'--profile', '--profile',
......
...@@ -43,6 +43,7 @@ class TrackWidgetCreationEnabledTask { ...@@ -43,6 +43,7 @@ class TrackWidgetCreationEnabledTask {
final Process runProcess = await startProcess( final Process runProcess = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
flutterCommandArgs('run', <String>[ flutterCommandArgs('run', <String>[
'--no-android-gradle-daemon',
...?additionalArgs, ...?additionalArgs,
'--vmservice-out-file=info', '--vmservice-out-file=info',
'--track-widget-creation', '--track-widget-creation',
...@@ -78,6 +79,7 @@ class TrackWidgetCreationEnabledTask { ...@@ -78,6 +79,7 @@ class TrackWidgetCreationEnabledTask {
final Process runProcess = await startProcess( final Process runProcess = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
flutterCommandArgs('run', <String>[ flutterCommandArgs('run', <String>[
'--no-android-gradle-daemon',
...?additionalArgs, ...?additionalArgs,
'--vmservice-out-file=info', '--vmservice-out-file=info',
'--no-track-widget-creation', '--no-track-widget-creation',
......
...@@ -279,6 +279,9 @@ Future<void> buildGradleApp({ ...@@ -279,6 +279,9 @@ Future<void> buildGradleApp({
} else { } else {
command.add('-q'); command.add('-q');
} }
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (globals.artifacts is LocalEngineArtifacts) { if (globals.artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts; final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts;
final Directory localEngineRepo = _getLocalEngineRepo( final Directory localEngineRepo = _getLocalEngineRepo(
...@@ -592,6 +595,9 @@ Future<void> buildGradleAar({ ...@@ -592,6 +595,9 @@ Future<void> buildGradleAar({
} else { } else {
command.add('-q'); command.add('-q');
} }
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (target != null && target.isNotEmpty) { if (target != null && target.isNotEmpty) {
command.add('-Ptarget=$target'); command.add('-Ptarget=$target');
......
...@@ -34,6 +34,7 @@ class BuildInfo { ...@@ -34,6 +34,7 @@ class BuildInfo {
this.packagesPath = '.packages', this.packagesPath = '.packages',
this.nullSafetyMode = NullSafetyMode.autodetect, this.nullSafetyMode = NullSafetyMode.autodetect,
this.codeSizeDirectory, this.codeSizeDirectory,
this.androidGradleDaemon = true,
}); });
final BuildMode mode; final BuildMode mode;
...@@ -118,6 +119,20 @@ class BuildInfo { ...@@ -118,6 +119,20 @@ class BuildInfo {
/// will be written for code size profiling. /// will be written for code size profiling.
final String codeSizeDirectory; final String codeSizeDirectory;
/// Whether to enable the Gradle daemon when performing an Android build.
///
/// Starting the daemon is the default behavior of the gradle wrapper script created
/// in a Flutter project. Setting this value to false will cause the tool to pass
/// `--no-daemon` to the gradle wrapper script, preventing it from spawning a daemon
/// process.
///
/// For one-off builds or CI systems, preventing the daemon from spawning will
/// reduce system resource usage, at the cost of any subsequent builds starting
/// up slightly slower.
///
/// The Gradle daemon may also be disabled in the Android application's properties file.
final bool androidGradleDaemon;
static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false); static const BuildInfo debug = BuildInfo(BuildMode.debug, null, treeShakeIcons: false);
static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault); static const BuildInfo profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault); static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
......
...@@ -43,6 +43,7 @@ class BuildAarCommand extends BuildSubCommand { ...@@ -43,6 +43,7 @@ class BuildAarCommand extends BuildSubCommand {
usesTrackWidgetCreation(verboseHelp: false); usesTrackWidgetCreation(verboseHelp: false);
addNullSafetyModeOptions(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser argParser
..addMultiOption( ..addMultiOption(
'target-platform', 'target-platform',
......
...@@ -33,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -33,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand {
addBuildPerformanceFile(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp);
usesAnalyzeSizeFlag(); usesAnalyzeSizeFlag();
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser argParser
..addFlag('split-per-abi', ..addFlag('split-per-abi',
negatable: false, negatable: false,
......
...@@ -33,6 +33,7 @@ class BuildAppBundleCommand extends BuildSubCommand { ...@@ -33,6 +33,7 @@ class BuildAppBundleCommand extends BuildSubCommand {
addNullSafetyModeOptions(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp); addEnableExperimentation(hide: !verboseHelp);
usesAnalyzeSizeFlag(); usesAnalyzeSizeFlag();
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser.addMultiOption('target-platform', argParser.addMultiOption('target-platform',
splitCommas: true, splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'], defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'],
......
...@@ -88,6 +88,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment ...@@ -88,6 +88,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDeviceUserOption(); usesDeviceUserOption();
usesDeviceTimeoutOption(); usesDeviceTimeoutOption();
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp);
} }
bool get traceStartup => boolArg('trace-startup'); bool get traceStartup => boolArg('trace-startup');
......
...@@ -114,6 +114,7 @@ class FlutterOptions { ...@@ -114,6 +114,7 @@ class FlutterOptions {
static const String kDeviceTimeout = 'device-timeout'; static const String kDeviceTimeout = 'device-timeout';
static const String kAnalyzeSize = 'analyze-size'; static const String kAnalyzeSize = 'analyze-size';
static const String kNullAssertions = 'null-assertions'; static const String kNullAssertions = 'null-assertions';
static const String kAndroidGradleDaemon = 'android-gradle-daemon';
} }
abstract class FlutterCommand extends Command<void> { abstract class FlutterCommand extends Command<void> {
...@@ -611,6 +612,18 @@ abstract class FlutterCommand extends Command<void> { ...@@ -611,6 +612,18 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void addAndroidSpecificBuildOptions({ bool hide = false }) {
argParser.addFlag(
FlutterOptions.kAndroidGradleDaemon,
help: 'Whether to enable the Gradle daemon when performing an Android build. '
'Starting the daemon is the default behavior of the gradle wrapper script created '
' in a Flutter project. Setting this flag to false corresponds to passing '
"'--no-daemon' to the gradle wrapper script. This flag will cause the daemon "
'process to terminate after the build is completed',
defaultsTo: true,
);
}
/// Adds build options common to all of the desktop build commands. /// Adds build options common to all of the desktop build commands.
void addCommonDesktopBuildOptions({ bool verboseHelp = false }) { void addCommonDesktopBuildOptions({ bool verboseHelp = false }) {
addBuildModeFlags(verboseHelp: verboseHelp); addBuildModeFlags(verboseHelp: verboseHelp);
...@@ -764,6 +777,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -764,6 +777,9 @@ abstract class FlutterCommand extends Command<void> {
? stringArg(FlutterOptions.kSplitDebugInfoOption) ? stringArg(FlutterOptions.kSplitDebugInfoOption)
: null; : null;
final bool androidGradleDaemon = !argParser.options.containsKey(FlutterOptions.kAndroidGradleDaemon)
|| boolArg(FlutterOptions.kAndroidGradleDaemon);
if (dartObfuscation && (splitDebugInfoPath == null || splitDebugInfoPath.isEmpty)) { if (dartObfuscation && (splitDebugInfoPath == null || splitDebugInfoPath.isEmpty)) {
throwToolExit( throwToolExit(
'"--${FlutterOptions.kDartObfuscationOption}" can only be used in ' '"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
...@@ -827,6 +843,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -827,6 +843,7 @@ abstract class FlutterCommand extends Command<void> {
packagesPath: globalResults['packages'] as String ?? '.packages', packagesPath: globalResults['packages'] as String ?? '.packages',
nullSafetyMode: nullSafetyMode, nullSafetyMode: nullSafetyMode,
codeSizeDirectory: codeSizeDirectory, codeSizeDirectory: codeSizeDirectory,
androidGradleDaemon: androidGradleDaemon,
); );
} }
......
...@@ -1328,7 +1328,7 @@ plugin1=${plugin1.path} ...@@ -1328,7 +1328,7 @@ plugin1=${plugin1.path}
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('logs success event after a sucessful retry', () async { testUsingContext('logs success event after a successful retry', () async {
int testFnCalled = 0; int testFnCalled = 0;
when(mockProcessManager.start(any, when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
...@@ -1415,7 +1415,7 @@ plugin1=${plugin1.path} ...@@ -1415,7 +1415,7 @@ plugin1=${plugin1.path}
Usage: () => mockUsage, Usage: () => mockUsage,
}); });
testUsingContext('performs code size analyis and sends analytics', () async { testUsingContext('performs code size analysis and sends analytics', () async {
when(mockProcessManager.start(any, when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'))) environment: anyNamed('environment')))
...@@ -2059,6 +2059,59 @@ plugin1=${plugin1.path} ...@@ -2059,6 +2059,59 @@ plugin1=${plugin1.path}
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('honors --no-android-gradle-daemon setting', () async {
(globals.processManager as FakeProcessManager).addCommand(
const FakeCommand(command: <String>[
'/android/gradlew',
'-q',
'--no-daemon',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Ptrack-widget-creation=false',
'assembleRelease'
],
));
fileSystem.file('android/gradlew').createSync(recursive: true);
fileSystem.directory('android')
.childFile('gradle.properties')
.createSync(recursive: true);
fileSystem.file('android/build.gradle')
.createSync(recursive: true);
fileSystem.directory('android')
.childDirectory('app')
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
await expectLater(() async {
await buildGradleApp(
project: FlutterProject.current(),
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
null,
treeShakeIcons: false,
androidGradleDaemon: false,
),
),
target: 'lib/main.dart',
isBuildingBundle: false,
localGradleErrors: const <GradleHandledError>[],
);
}, throwsToolExit());
}, overrides: <Type, Generator>{
AndroidSdk: () => mockAndroidSdk,
AndroidStudio: () => mockAndroidStudio,
Artifacts: () => Artifacts.test(),
Cache: () => cache,
Platform: () => android,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[]),
});
testUsingContext('build aar uses selected local engine,the engine abi is arm', () async { testUsingContext('build aar uses selected local engine,the engine abi is arm', () async {
when(mockArtifacts.getArtifactPath(Artifact.flutterFramework, when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine'); platform: TargetPlatform.android_arm, mode: anyNamed('mode'))).thenReturn('engine');
......
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