Unverified Commit b847ba5a authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Send build timing to analytics (#34049)

parent bd413bff
......@@ -4,6 +4,7 @@
import 'dart:async';
import 'package:crypto/crypto.dart';
import 'package:meta/meta.dart';
import '../android/android_sdk.dart';
......@@ -352,6 +353,7 @@ Future<void> _buildGradleProjectV1(FlutterProject project, String gradle) async
timeout: timeoutConfiguration.slowOperation,
multilineOutput: true,
);
final Stopwatch sw = Stopwatch()..start();
final int exitCode = await runCommandAndStreamOutput(
<String>[fs.file(gradle).absolute.path, 'build'],
workingDirectory: project.android.hostAppGradleRoot.path,
......@@ -359,6 +361,7 @@ Future<void> _buildGradleProjectV1(FlutterProject project, String gradle) async
environment: _gradleEnv,
);
status.stop();
flutterUsage.sendTiming('build', 'gradle-v1', Duration(milliseconds: sw.elapsedMilliseconds));
if (exitCode != 0)
throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode);
......@@ -366,6 +369,25 @@ Future<void> _buildGradleProjectV1(FlutterProject project, String gradle) async
printStatus('Built ${fs.path.relative(project.android.gradleAppOutV1File.path)}.');
}
String _hex(List<int> bytes) {
final StringBuffer result = StringBuffer();
for (int part in bytes)
result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
return result.toString();
}
String _calculateSha(File file) {
final Stopwatch sw = Stopwatch()..start();
final List<int> bytes = file.readAsBytesSync();
printTrace('calculateSha: reading file took ${sw.elapsedMilliseconds}us');
flutterUsage.sendTiming('build', 'apk-sha-read', Duration(milliseconds: sw.elapsedMilliseconds));
sw.reset();
final String sha = _hex(sha1.convert(bytes).bytes);
printTrace('calculateSha: computing sha took ${sw.elapsedMilliseconds}us');
flutterUsage.sendTiming('build', 'apk-sha-calc', Duration(milliseconds: sw.elapsedMilliseconds));
return sha;
}
Future<void> _buildGradleProjectV2(
FlutterProject flutterProject,
String gradle,
......@@ -439,30 +461,35 @@ Future<void> _buildGradleProjectV2(
command.add(assembleTask);
bool potentialAndroidXFailure = false;
final int exitCode = await runCommandAndStreamOutput(
command,
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: _gradleEnv,
// TODO(mklim): if AndroidX warnings are no longer required, this
// mapFunction and all its associated variabled can be replaced with just
// `filter: ndkMessagefilter`.
mapFunction: (String line) {
final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line);
if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) {
potentialAndroidXFailure = true;
}
// Always print the full line in verbose mode.
if (logger.isVerbose) {
return line;
} else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) {
return null;
}
final Stopwatch sw = Stopwatch()..start();
int exitCode = 1;
try {
exitCode = await runCommandAndStreamOutput(
command,
workingDirectory: flutterProject.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: _gradleEnv,
// TODO(mklim): if AndroidX warnings are no longer required, this
// mapFunction and all its associated variabled can be replaced with just
// `filter: ndkMessagefilter`.
mapFunction: (String line) {
final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line);
if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) {
potentialAndroidXFailure = true;
}
// Always print the full line in verbose mode.
if (logger.isVerbose) {
return line;
} else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) {
return null;
}
return line;
},
);
status.stop();
return line;
},
);
} finally {
status.stop();
}
if (exitCode != 0) {
if (potentialAndroidXFailure) {
......@@ -478,6 +505,7 @@ Future<void> _buildGradleProjectV2(
}
throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode);
}
flutterUsage.sendTiming('build', 'gradle-v2', Duration(milliseconds: sw.elapsedMilliseconds));
if (!isBuildingBundle) {
final File apkFile = _findApkFile(project, buildInfo);
......@@ -488,7 +516,7 @@ Future<void> _buildGradleProjectV2(
printTrace('calculateSha: ${project.apkDirectory}/app.apk');
final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1');
apkShaFile.writeAsStringSync(calculateSha(apkFile));
apkShaFile.writeAsStringSync(_calculateSha(apkFile));
String appSize;
if (buildInfo.mode == BuildMode.debug) {
......
......@@ -15,6 +15,7 @@ import '../dart/package_map.dart';
import '../globals.dart';
import '../macos/xcode.dart';
import '../project.dart';
import '../usage.dart';
import 'context.dart';
import 'file_system.dart';
import 'fingerprint.dart';
......@@ -194,7 +195,9 @@ class AOTSnapshotter {
// }
final SnapshotType snapshotType = SnapshotType(platform, buildMode);
final int genSnapshotExitCode = await _timedStep('snapshot(CompileTime)', () => genSnapshot.run(
final int genSnapshotExitCode =
await _timedStep('snapshot(CompileTime)', 'aot-snapshot',
() => genSnapshot.run(
snapshotType: snapshotType,
additionalArgs: genSnapshotArgs,
iosArch: iosArch,
......@@ -281,7 +284,9 @@ class AOTSnapshotter {
final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d');
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput = await _timedStep('frontend(CompileTime)', () => kernelCompiler.compile(
final CompilerOutput compilerOutput =
await _timedStep('frontend(CompileTime)', 'aot-kernel',
() => kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
mainPath: mainPath,
packagesPath: packagesPath,
......@@ -323,12 +328,13 @@ class AOTSnapshotter {
/// to find.
/// Important: external performance tracking tools expect format of this
/// output to be stable.
Future<T> _timedStep<T>(String marker, FutureOr<T> Function() action) async {
Future<T> _timedStep<T>(String marker, String analyticsVar, FutureOr<T> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
final T value = await action();
if (reportTimings) {
printStatus('$marker: ${sw.elapsedMilliseconds} ms.');
}
flutterUsage.sendTiming('build', analyticsVar, Duration(milliseconds: sw.elapsedMilliseconds));
return value;
}
}
......
......@@ -5,7 +5,6 @@
import 'dart:async';
import 'dart:math' show Random, max;
import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart';
import '../convert.dart';
......@@ -53,24 +52,6 @@ bool get isRunningOnBot {
return botDetector.isRunningOnBot;
}
String hex(List<int> bytes) {
final StringBuffer result = StringBuffer();
for (int part in bytes)
result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
return result.toString();
}
String calculateSha(File file) {
final Stopwatch stopwatch = Stopwatch()..start();
final List<int> bytes = file.readAsBytesSync();
printTrace('calculateSha: reading file took ${stopwatch.elapsedMicroseconds}us');
stopwatch.reset();
final String sha = hex(sha1.convert(bytes).bytes);
stopwatch.stop();
printTrace('calculateSha: computing sha took ${stopwatch.elapsedMicroseconds}us');
return sha;
}
/// Convert `foo_bar` to `fooBar`.
String camelCase(String str) {
int index = str.indexOf('_');
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:meta/meta.dart';
import '../asset.dart';
......@@ -12,11 +14,20 @@ import '../build_info.dart';
import '../bundle.dart';
import '../convert.dart';
import '../devfs.dart';
import '../globals.dart';
import '../project.dart';
import '../usage.dart';
import 'fuchsia_pm.dart';
import 'fuchsia_sdk.dart';
Future<void> _timedBuildStep(String name, Future<void> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
await action();
printTrace('$name: ${sw.elapsedMilliseconds} ms.');
flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds));
}
// Building a Fuchsia package has a few steps:
// 1. Do the custom kernel compile using the kernel compiler from the Fuchsia
// SDK. This produces .dilp files (among others) and a manifest file.
......@@ -32,10 +43,13 @@ Future<void> buildFuchsia(
outDir.createSync(recursive: true);
}
await fuchsiaSdk.fuchsiaKernelCompiler.build(
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo);
await _buildAssets(fuchsiaProject, target, buildInfo);
await _buildPackage(fuchsiaProject, target, buildInfo);
await _timedBuildStep('fuchsia-kernel-compile',
() => fuchsiaSdk.fuchsiaKernelCompiler.build(
fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo));
await _timedBuildStep('fuchsia-build-assets',
() => _buildAssets(fuchsiaProject, target, buildInfo));
await _timedBuildStep('fuchsia-build-package',
() => _buildPackage(fuchsiaProject, target, buildInfo));
}
Future<void> _buildAssets(
......
......@@ -24,6 +24,7 @@ import '../macos/cocoapod_utils.dart';
import '../macos/xcode.dart';
import '../project.dart';
import '../services.dart';
import '../usage.dart';
import 'code_signing.dart';
import 'xcodeproj.dart';
......@@ -374,7 +375,7 @@ Future<XcodeBuildResult> buildXcodeProject({
buildCommands.add('SCRIPT_OUTPUT_STREAM_FILE=${scriptOutputPipeFile.absolute.path}');
}
final Stopwatch buildStopwatch = Stopwatch()..start();
final Stopwatch sw = Stopwatch()..start();
initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation);
final RunResult buildResult = await runAsync(
buildCommands,
......@@ -387,11 +388,11 @@ Future<XcodeBuildResult> buildXcodeProject({
buildSubStatus = null;
initialBuildStatus?.cancel();
initialBuildStatus = null;
buildStopwatch.stop();
printStatus(
'Xcode build done.'.padRight(kDefaultStatusPadding + 1)
+ '${getElapsedAsSeconds(buildStopwatch.elapsed).padLeft(5)}',
+ '${getElapsedAsSeconds(sw.elapsed).padLeft(5)}',
);
flutterUsage.sendTiming('build', 'xcode-ios', Duration(milliseconds: sw.elapsedMilliseconds));
// Run -showBuildSettings again but with the exact same parameters as the build.
final Map<String, String> buildSettings = parseXcodeBuildSettings(runCheckedSync(
......
......@@ -13,6 +13,7 @@ import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
import '../usage.dart';
/// Builds the Linux project through the Makefile.
Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String target = 'lib/main.dart'}) async {
......@@ -37,6 +38,8 @@ export PROJECT_DIR=${linuxProject.project.directory.path}
..createSync(recursive: true)
..writeAsStringSync(buffer.toString());
// Invoke make.
final Stopwatch sw = Stopwatch()..start();
final Process process = await processManager.start(<String>[
'make',
'-C',
......@@ -63,4 +66,5 @@ export PROJECT_DIR=${linuxProject.project.directory.path}
if (result != 0) {
throwToolExit('Build process failed');
}
flutterUsage.sendTiming('build', 'make-linux', Duration(milliseconds: sw.elapsedMilliseconds));
}
......@@ -12,6 +12,7 @@ import '../convert.dart';
import '../globals.dart';
import '../ios/xcodeproj.dart';
import '../project.dart';
import '../usage.dart';
import 'cocoapod_utils.dart';
/// Builds the macOS project through xcode build.
......@@ -37,6 +38,7 @@ Future<void> buildMacOS(FlutterProject flutterProject, BuildInfo buildInfo) asyn
config = 'Release';
}
// Run build script provided by application.
final Stopwatch sw = Stopwatch()..start();
final Process process = await processManager.start(<String>[
'/usr/bin/env',
'xcrun',
......@@ -69,4 +71,5 @@ Future<void> buildMacOS(FlutterProject flutterProject, BuildInfo buildInfo) asyn
if (result != 0) {
throwToolExit('Build process failed');
}
flutterUsage.sendTiming('build', 'xcode-macos', Duration(milliseconds: sw.elapsedMilliseconds));
}
......@@ -13,12 +13,14 @@ import '../build_info.dart';
import '../bundle.dart';
import '../globals.dart';
import '../project.dart';
import '../usage.dart';
/// The [WebCompilationProxy] instance.
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo) async {
final Status status = logger.startProgress('Compiling $target for the Web...', timeout: null);
final Stopwatch sw = Stopwatch()..start();
final Directory outputDir = fs.directory(getWebBuildDirectory())
..createSync(recursive: true);
bool result;
......@@ -55,6 +57,11 @@ Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo bu
if (result == false) {
throwToolExit('Failed to compile $target for the Web.');
}
String buildName = 'ddc';
if (buildInfo.isRelease) {
buildName = 'dart2js';
}
flutterUsage.sendTiming('build', buildName, Duration(milliseconds: sw.elapsedMilliseconds));
}
/// An indirection on web compilation.
......
......@@ -13,6 +13,7 @@ import '../cache.dart';
import '../convert.dart';
import '../globals.dart';
import '../project.dart';
import '../usage.dart';
import 'msbuild_utils.dart';
import 'visual_studio.dart';
......@@ -48,6 +49,7 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {S
final String configuration = buildInfo.isDebug ? 'Debug' : 'Release';
final String solutionPath = windowsProject.solutionFile.path;
final Stopwatch sw = Stopwatch()..start();
// Run the script with a relative path to the project using the enclosing
// directory as the workingDirectory, to avoid hitting the limit on command
// lengths in batch scripts if the absolute path to the project is long.
......@@ -78,4 +80,5 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {S
if (result != 0) {
throwToolExit('Build process failed');
}
flutterUsage.sendTiming('build', 'vs_build', Duration(milliseconds: sw.elapsedMilliseconds));
}
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