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

[flutter_tools] allow pulling performance data from assemble (#55699)

parent 46a5c550
...@@ -666,7 +666,7 @@ class _BuildInstance { ...@@ -666,7 +666,7 @@ class _BuildInstance {
Future<bool> _invokeInternal(Node node) async { Future<bool> _invokeInternal(Node node) async {
final PoolResource resource = await resourcePool.request(); final PoolResource resource = await resourcePool.request();
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
bool passed = true; bool succeeded = true;
bool skipped = false; bool skipped = false;
// The build system produces a list of aggregate input and output // The build system produces a list of aggregate input and output
...@@ -702,7 +702,7 @@ class _BuildInstance { ...@@ -702,7 +702,7 @@ class _BuildInstance {
skipped = true; skipped = true;
logger.printTrace('Skipping target: ${node.target.name}'); logger.printTrace('Skipping target: ${node.target.name}');
updateGraph(); updateGraph();
return passed; return succeeded;
} }
logger.printTrace('${node.target.name}: Starting due to ${node.invalidatedReasons}'); logger.printTrace('${node.target.name}: Starting due to ${node.invalidatedReasons}');
await node.target.build(environment); await node.target.build(environment);
...@@ -741,7 +741,7 @@ class _BuildInstance { ...@@ -741,7 +741,7 @@ class _BuildInstance {
// TODO(jonahwilliams): throw specific exception for expected errors to mark // TODO(jonahwilliams): throw specific exception for expected errors to mark
// as non-fatal. All others should be fatal. // as non-fatal. All others should be fatal.
node.target.clearStamp(environment); node.target.clearStamp(environment);
passed = false; succeeded = false;
skipped = false; skipped = false;
exceptionMeasurements[node.target.name] = ExceptionMeasurement( exceptionMeasurements[node.target.name] = ExceptionMeasurement(
node.target.name, exception, stackTrace); node.target.name, exception, stackTrace);
...@@ -752,11 +752,11 @@ class _BuildInstance { ...@@ -752,11 +752,11 @@ class _BuildInstance {
target: node.target.name, target: node.target.name,
elapsedMilliseconds: stopwatch.elapsedMilliseconds, elapsedMilliseconds: stopwatch.elapsedMilliseconds,
skipped: skipped, skipped: skipped,
passed: passed, succeeded: succeeded,
analyicsName: node.target.analyticsName, analyicsName: node.target.analyticsName,
); );
} }
return passed; return succeeded;
} }
} }
...@@ -781,14 +781,14 @@ class PerformanceMeasurement { ...@@ -781,14 +781,14 @@ class PerformanceMeasurement {
@required this.target, @required this.target,
@required this.elapsedMilliseconds, @required this.elapsedMilliseconds,
@required this.skipped, @required this.skipped,
@required this.passed, @required this.succeeded,
@required this.analyicsName, @required this.analyicsName,
}); });
final int elapsedMilliseconds; final int elapsedMilliseconds;
final String target; final String target;
final bool skipped; final bool skipped;
final bool passed; final bool succeeded;
final String analyicsName; final String analyicsName;
} }
......
...@@ -17,6 +17,7 @@ import '../build_system/targets/macos.dart'; ...@@ -17,6 +17,7 @@ import '../build_system/targets/macos.dart';
import '../build_system/targets/web.dart'; import '../build_system/targets/web.dart';
import '../build_system/targets/windows.dart'; import '../build_system/targets/windows.dart';
import '../cache.dart'; import '../cache.dart';
import '../convert.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
...@@ -72,6 +73,10 @@ class AssembleCommand extends FlutterCommand { ...@@ -72,6 +73,10 @@ class AssembleCommand extends FlutterCommand {
abbr: 'd', abbr: 'd',
help: 'Allows passing configuration to a target with --define=target=key=value.', help: 'Allows passing configuration to a target with --define=target=key=value.',
); );
argParser.addOption(
'performance-measurement-file',
help: 'Output individual target performance to a JSON file.'
);
argParser.addMultiOption( argParser.addMultiOption(
'input', 'input',
abbr: 'i', abbr: 'i',
...@@ -231,6 +236,10 @@ class AssembleCommand extends FlutterCommand { ...@@ -231,6 +236,10 @@ class AssembleCommand extends FlutterCommand {
if (argResults.wasParsed('build-outputs')) { if (argResults.wasParsed('build-outputs')) {
writeListIfChanged(result.outputFiles, stringArg('build-outputs')); writeListIfChanged(result.outputFiles, stringArg('build-outputs'));
} }
if (argResults.wasParsed('performance-measurement-file')) {
final File outFile = globals.fs.file(argResults['performance-measurement-file']);
writePerformanceData(result.performance.values, outFile);
}
if (argResults.wasParsed('depfile')) { if (argResults.wasParsed('depfile')) {
final File depfileFile = globals.fs.file(stringArg('depfile')); final File depfileFile = globals.fs.file(stringArg('depfile'));
final Depfile depfile = Depfile(result.inputFiles, result.outputFiles); final Depfile depfile = Depfile(result.inputFiles, result.outputFiles);
...@@ -262,6 +271,26 @@ void writeListIfChanged(List<File> files, String path) { ...@@ -262,6 +271,26 @@ void writeListIfChanged(List<File> files, String path) {
} }
} }
/// Output performance measurement data in [outFile].
@visibleForTesting
void writePerformanceData(Iterable<PerformanceMeasurement> measurements, File outFile) {
final Map<String, Object> jsonData = <String, Object>{
'targets': <Object>[
for (final PerformanceMeasurement measurement in measurements)
<String, Object>{
'name': measurement.analyicsName,
'skipped': measurement.skipped,
'succeeded': measurement.succeeded,
'elapsedMilliseconds': measurement.elapsedMilliseconds,
}
]
};
if (!outFile.parent.existsSync()) {
outFile.parent.createSync(recursive: true);
}
outFile.writeAsStringSync(json.encode(jsonData));
}
class _CompositeTarget extends Target { class _CompositeTarget extends Target {
_CompositeTarget(this.dependencies); _CompositeTarget(this.dependencies);
......
...@@ -3,11 +3,14 @@ ...@@ -3,11 +3,14 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/assemble.dart'; import 'package:flutter_tools/src/commands/assemble.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
...@@ -97,6 +100,37 @@ void main() { ...@@ -97,6 +100,37 @@ void main() {
expect(testLogger.errorText, isNot(contains(testStackTrace.toString()))); expect(testLogger.errorText, isNot(contains(testStackTrace.toString())));
}); });
testbed.test('flutter assemble outputs JSON performance data to provided file', () async {
when(globals.buildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
.thenAnswer((Invocation invocation) async {
return BuildResult(success: true, performance: <String, PerformanceMeasurement>{
'hello': PerformanceMeasurement(
target: 'hello',
analyicsName: 'bar',
elapsedMilliseconds: 123,
skipped: false,
succeeded: true,
),
});
});
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>[
'assemble',
'-o Output',
'--performance-measurement-file=out.json',
'debug_macos_bundle_flutter_assets',
]);
expect(globals.fs.file('out.json'), exists);
expect(
json.decode(globals.fs.file('out.json').readAsStringSync()),
containsPair('targets', contains(
containsPair('name', 'bar'),
)),
);
});
testbed.test('flutter assemble does not inject engine revision with local-engine', () async { testbed.test('flutter assemble does not inject engine revision with local-engine', () async {
Environment environment; Environment environment;
when(globals.artifacts.isLocalEngine).thenReturn(true); when(globals.artifacts.isLocalEngine).thenReturn(true);
...@@ -170,6 +204,36 @@ void main() { ...@@ -170,6 +204,36 @@ void main() {
expect(inputs.readAsStringSync(), contains('fizz')); expect(inputs.readAsStringSync(), contains('fizz'));
expect(inputs.lastModifiedSync(), isNot(theDistantPast)); expect(inputs.lastModifiedSync(), isNot(theDistantPast));
}); });
testWithoutContext('writePerformanceData outputs performance data in JSON form', () {
final List<PerformanceMeasurement> performanceMeasurement = <PerformanceMeasurement>[
PerformanceMeasurement(
analyicsName: 'foo',
target: 'hidden',
skipped: false,
succeeded: true,
elapsedMilliseconds: 123,
)
];
final FileSystem fileSystem = MemoryFileSystem.test();
final File outFile = fileSystem.currentDirectory
.childDirectory('foo')
.childFile('out.json');
writePerformanceData(performanceMeasurement, outFile);
expect(outFile, exists);
expect(json.decode(outFile.readAsStringSync()), <String, Object>{
'targets': <Object>[
<String, Object>{
'name': 'foo',
'skipped': false,
'succeeded': true,
'elapsedMilliseconds': 123,
},
],
});
});
} }
class MockBuildSystem extends Mock implements BuildSystem {} class MockBuildSystem extends Mock implements BuildSystem {}
......
...@@ -119,14 +119,14 @@ void main() { ...@@ -119,14 +119,14 @@ void main() {
analyicsName: 'kernel_snapshot', analyicsName: 'kernel_snapshot',
target: 'kernel_snapshot', target: 'kernel_snapshot',
elapsedMilliseconds: 1000, elapsedMilliseconds: 1000,
passed: true, succeeded: true,
skipped: false, skipped: false,
), ),
'anything': PerformanceMeasurement( 'anything': PerformanceMeasurement(
analyicsName: 'android_aot', analyicsName: 'android_aot',
target: 'anything', target: 'anything',
elapsedMilliseconds: 1000, elapsedMilliseconds: 1000,
passed: true, succeeded: true,
skipped: false, skipped: false,
), ),
}); });
......
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