blob: ace20254ebca7e7b4511468c2a874a330858d1e9 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:process_runner/process_runner.dart';
import 'environment.dart';
import 'proc_utils.dart';
/// Canonicalized build targets start with this prefix.
const String buildTargetPrefix = '//';
/// A suffix to build targets that recursively selects all child build targets.
const String _buildTargetGlobSuffix = '/...';
/// Information about a test build target.
final class TestTarget {
/// Construct a test target.
TestTarget(this.label, this.executable);
/// The build target label. `//flutter/fml:fml_unittests`
final String label;
/// The executable file produced after the build target is built.
final File executable;
@override
String toString() {
return 'target=$label executable=${executable.path}';
}
}
/// Returns test targets for a given build directory.
Future<Map<String, TestTarget>> findTestTargets(
Environment environment, Directory buildDir) async {
final Map<String, TestTarget> r = <String, TestTarget>{};
final List<String> getLabelsCommandLine = <String>[
gnBinPath(environment),
'ls',
buildDir.path,
'--type=executable',
'--testonly=true',
'--as=label',
];
final List<String> getOutputsCommandLine = <String>[
gnBinPath(environment),
'ls',
buildDir.path,
'--type=executable',
'--testonly=true',
'--as=output'
];
// Spawn the two processes concurrently.
final Future<ProcessRunnerResult> futureLabelsResult =
environment.processRunner.runProcess(getLabelsCommandLine,
workingDirectory: environment.engine.srcDir, failOk: true);
final Future<ProcessRunnerResult> futureOutputsResult =
environment.processRunner.runProcess(getOutputsCommandLine,
workingDirectory: environment.engine.srcDir, failOk: true);
// Await the futures, we need both to complete so the order doesn't matter.
final ProcessRunnerResult labelsResult = await futureLabelsResult;
final ProcessRunnerResult outputsResult = await futureOutputsResult;
// Handle any process failures.
fatalIfFailed(environment, getLabelsCommandLine, labelsResult);
fatalIfFailed(environment, getOutputsCommandLine, outputsResult);
// Extract the labels
final String rawLabels = labelsResult.stdout;
final String rawOutputs = outputsResult.stdout;
final List<String> labels = rawLabels.split('\n');
final List<String> outputs = rawOutputs.split('\n');
if (labels.length != outputs.length) {
environment.logger.fatal(
'gn ls output is inconsistent A and B should be the same length:\nA=$labels\nB=$outputs');
}
// Drop the empty line at the end of the output.
if (labels.isNotEmpty) {
if (labels.last.isNotEmpty || outputs.last.isNotEmpty) {
throw AssertionError('expected last line of output to be blank.');
}
labels.removeLast();
outputs.removeLast();
}
for (int i = 0; i < labels.length; i++) {
final String label = labels[i];
final String output = outputs[i];
if (label.isEmpty) {
throw AssertionError('expected line to not be empty.');
}
if (output.isEmpty) {
throw AssertionError('expected line to not be empty.');
}
r[label] = TestTarget(label, File(p.join(buildDir.path, output)));
}
return r;
}
/// Process selectors and filter allTargets for matches.
///
/// We support:
/// 1) Exact label matches (the '//' prefix will be stripped off).
/// 2) '/...' suffix which selects all targets that match the prefix.
Set<TestTarget> selectTargets(
List<String> selectors, Map<String, TestTarget> allTargets) {
final Set<TestTarget> selected = <TestTarget>{};
for (String selector in selectors) {
if (!selector.startsWith(buildTargetPrefix)) {
// Insert the prefix when necessary.
selector = '$buildTargetPrefix$selector';
}
final bool recursiveMatch = selector.endsWith(_buildTargetGlobSuffix);
if (recursiveMatch) {
// Remove the /... suffix.
selector = selector.substring(
0, selector.length - _buildTargetGlobSuffix.length);
// TODO(johnmccutchan): Accelerate this by using a trie.
for (final TestTarget target in allTargets.values) {
if (target.label.startsWith(selector)) {
selected.add(target);
}
}
} else {
for (final TestTarget target in allTargets.values) {
if (target.label == selector) {
selected.add(target);
}
}
}
}
return selected;
}