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() {
await inDirectory(complexLayoutPath, () async {
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v',
'--profile',
'--trace-startup', // Enables "endless" timeline event buffering.
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:vm_service_client/vm_service_client.dart';
import 'package:flutter_devicelab/framework/utils.dart';
......@@ -88,7 +87,6 @@ Future<TaskResult> runTask(
} finally {
if (!runnerFinished)
runner.kill(ProcessSignal.sigkill);
await cleanupSystem();
await stdoutSub.cancel();
await stderrSub.cancel();
}
......@@ -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 {
await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'--verbose',
'-d',
deviceId,
......
......@@ -71,6 +71,7 @@ class GalleryTransitionTest {
'build',
options: <String>[
'apk',
'--no-android-gradle-daemon',
'--profile',
'-t',
'test_driver/$testFile.dart',
......
......@@ -26,7 +26,7 @@ TaskFunction createHotModeTest({String deviceIdOverride, Map<String, String> env
final File benchmarkFile = file(path.join(_editedFlutterGalleryDir.path, 'hot_benchmark.json'));
rm(benchmarkFile);
final List<String> options = <String>[
'--hot', '-d', deviceIdOverride, '--benchmark', '--verbose', '--resident',
'--hot', '-d', deviceIdOverride, '--benchmark', '--verbose', '--resident', '--no-android-gradle-daemon',
];
int hotReloadCount = 0;
Map<String, dynamic> twoReloadsData;
......
......@@ -130,6 +130,7 @@ class DriverTest {
await flutter('packages', options: <String>['get']);
final List<String> options = <String>[
'--no-android-gradle-daemon',
'-v',
'-t',
testTarget,
......
......@@ -347,6 +347,7 @@ TaskFunction createsScrollSmoothnessPerfTest() {
await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v',
'--verbose-system-logs',
'--profile',
......@@ -395,6 +396,7 @@ TaskFunction createFramePolicyIntegrationTest() {
await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v',
'--verbose-system-logs',
'--profile',
......@@ -459,6 +461,7 @@ class StartupTest {
final List<Map<String, dynamic>> results = <Map<String, dynamic>>[];
for (int i = 0; i < iterations; ++i) {
await flutter('run', options: <String>[
'--no-android-gradle-daemon',
'--verbose',
'--profile',
'--trace-startup',
......@@ -575,6 +578,7 @@ class PerfTest {
await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v',
'--verbose-system-logs',
'--profile',
......
......@@ -43,6 +43,7 @@ class TrackWidgetCreationEnabledTask {
final Process runProcess = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
flutterCommandArgs('run', <String>[
'--no-android-gradle-daemon',
...?additionalArgs,
'--vmservice-out-file=info',
'--track-widget-creation',
......@@ -78,6 +79,7 @@ class TrackWidgetCreationEnabledTask {
final Process runProcess = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
flutterCommandArgs('run', <String>[
'--no-android-gradle-daemon',
...?additionalArgs,
'--vmservice-out-file=info',
'--no-track-widget-creation',
......
......@@ -279,6 +279,9 @@ Future<void> buildGradleApp({
} else {
command.add('-q');
}
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (globals.artifacts is LocalEngineArtifacts) {
final LocalEngineArtifacts localEngineArtifacts = globals.artifacts as LocalEngineArtifacts;
final Directory localEngineRepo = _getLocalEngineRepo(
......@@ -592,6 +595,9 @@ Future<void> buildGradleAar({
} else {
command.add('-q');
}
if (!buildInfo.androidGradleDaemon) {
command.add('--no-daemon');
}
if (target != null && target.isNotEmpty) {
command.add('-Ptarget=$target');
......
......@@ -34,6 +34,7 @@ class BuildInfo {
this.packagesPath = '.packages',
this.nullSafetyMode = NullSafetyMode.autodetect,
this.codeSizeDirectory,
this.androidGradleDaemon = true,
});
final BuildMode mode;
......@@ -118,6 +119,20 @@ class BuildInfo {
/// will be written for code size profiling.
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 profile = BuildInfo(BuildMode.profile, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
static const BuildInfo jitRelease = BuildInfo(BuildMode.jitRelease, null, treeShakeIcons: kIconTreeShakerEnabledDefault);
......
......@@ -43,6 +43,7 @@ class BuildAarCommand extends BuildSubCommand {
usesTrackWidgetCreation(verboseHelp: false);
addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser
..addMultiOption(
'target-platform',
......
......@@ -33,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand {
addBuildPerformanceFile(hide: !verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp);
usesAnalyzeSizeFlag();
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser
..addFlag('split-per-abi',
negatable: false,
......
......@@ -33,6 +33,7 @@ class BuildAppBundleCommand extends BuildSubCommand {
addNullSafetyModeOptions(hide: !verboseHelp);
addEnableExperimentation(hide: !verboseHelp);
usesAnalyzeSizeFlag();
addAndroidSpecificBuildOptions(hide: !verboseHelp);
argParser.addMultiOption('target-platform',
splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64', 'android-x64'],
......
......@@ -88,6 +88,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesDeviceUserOption();
usesDeviceTimeoutOption();
addDdsOptions(verboseHelp: verboseHelp);
addAndroidSpecificBuildOptions(hide: !verboseHelp);
}
bool get traceStartup => boolArg('trace-startup');
......
......@@ -114,6 +114,7 @@ class FlutterOptions {
static const String kDeviceTimeout = 'device-timeout';
static const String kAnalyzeSize = 'analyze-size';
static const String kNullAssertions = 'null-assertions';
static const String kAndroidGradleDaemon = 'android-gradle-daemon';
}
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.
void addCommonDesktopBuildOptions({ bool verboseHelp = false }) {
addBuildModeFlags(verboseHelp: verboseHelp);
......@@ -764,6 +777,9 @@ abstract class FlutterCommand extends Command<void> {
? stringArg(FlutterOptions.kSplitDebugInfoOption)
: null;
final bool androidGradleDaemon = !argParser.options.containsKey(FlutterOptions.kAndroidGradleDaemon)
|| boolArg(FlutterOptions.kAndroidGradleDaemon);
if (dartObfuscation && (splitDebugInfoPath == null || splitDebugInfoPath.isEmpty)) {
throwToolExit(
'"--${FlutterOptions.kDartObfuscationOption}" can only be used in '
......@@ -827,6 +843,7 @@ abstract class FlutterCommand extends Command<void> {
packagesPath: globalResults['packages'] as String ?? '.packages',
nullSafetyMode: nullSafetyMode,
codeSizeDirectory: codeSizeDirectory,
androidGradleDaemon: androidGradleDaemon,
);
}
......
......@@ -1328,7 +1328,7 @@ plugin1=${plugin1.path}
ProcessManager: () => mockProcessManager,
});
testUsingContext('logs success event after a sucessful retry', () async {
testUsingContext('logs success event after a successful retry', () async {
int testFnCalled = 0;
when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'),
......@@ -1415,7 +1415,7 @@ plugin1=${plugin1.path}
Usage: () => mockUsage,
});
testUsingContext('performs code size analyis and sends analytics', () async {
testUsingContext('performs code size analysis and sends analytics', () async {
when(mockProcessManager.start(any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment')))
......@@ -2059,6 +2059,59 @@ plugin1=${plugin1.path}
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 {
when(mockArtifacts.getArtifactPath(Artifact.flutterFramework,
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