[CFE] Add a few helper utils

These ease my life slightly.
Examples:

Compile to executable to get faster runs:
```
out/ReleaseX64/dart-sdk/bin/dart compile exe pkg/front_end/tool/gitformat_cl.dart -o ~/bin/format_cl
out/ReleaseX64/dart-sdk/bin/dart compile exe pkg/front_end/tool/git_branch_helper.dart -o ~/bin/gitbranch
```

Now I can format my entire CL with
```
$ format_cl
Using file:///path/to/dart-sdk/sdk/out/ReleaseX64/dart
stdout> Formatted 3 files (0 changed) in 0.35 seconds.
```

and get info about my branching with
```
$ gitbranch
├── origin/main
│   ├── add_a_few_cfe_util_tools [ahead 2]
```

Change-Id: I2e89196ab702660a1486894d22678e3cd0c07994
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352641
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
This commit is contained in:
Jens Johansen 2024-02-14 13:56:18 +00:00 committed by Commit Queue
parent af1b5b8044
commit 29782f8750
4 changed files with 255 additions and 2 deletions

View file

@ -15,7 +15,7 @@ Future<void> main(List<String> args) async {
// Expect something like /full/path/to/sdk/pkg/some_dir/whatever/else
if (args.length != 1) throw "Need exactly one argument.";
final List<String> changedFiles = _getChangedFiles();
final List<String> changedFiles = getChangedFiles();
String callerPath = args[0].replaceAll("\\", "/");
if (!_shouldRun(changedFiles, callerPath)) {
return;
@ -290,7 +290,7 @@ Future<void> _executePendingWorkItems(List<Work> workItems) async {
/// Queries git about changes against upstream, or origin/main if no upstream is
/// set. This is similar (but different), I believe, to what
/// `git cl presubmit` does.
List<String> _getChangedFiles() {
List<String> getChangedFiles() {
ProcessResult result = Process.runSync(
"git",
[

View file

@ -414,6 +414,7 @@ inconsistencies
increasing
incrementally
increments
indention
indents
ing
inhibit
@ -516,6 +517,7 @@ mxn
mysdk
naively
naturally
needle
negatable
newworld
ninja
@ -634,6 +636,7 @@ rediscover
reducer
reenable
referential
refname
refusing
regards
regenerate

View file

@ -0,0 +1,106 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. 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:convert' show LineSplitter, utf8;
import 'dart:developer' as dev show NativeRuntime;
import 'dart:io';
import '../presubmit_helper.dart' show getChangedFiles;
import '../test/utils/io_utils.dart';
Future<void> main() async {
Uri executable = getDartExecutable();
final List<String> allChangedFiles = getChangedFiles();
if (allChangedFiles.isEmpty) {
print("No changes in CL.");
return;
}
final List<String> changedDartFiles = [];
for (String changedFile in allChangedFiles) {
if (changedFile.toLowerCase().endsWith(".dart")) {
changedDartFiles.add(changedFile);
}
}
if (changedDartFiles.isEmpty) {
print("No changed dart files in CL.");
return;
}
Process p = await Process.start(
executable.toFilePath(), ["format", ...changedDartFiles]);
p.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String line) {
stderr.writeln("stderr> $line");
});
p.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String line) {
stdout.writeln("stdout> $line");
});
exitCode = await p.exitCode;
}
Uri getDartExecutable() {
Uri executable = Uri.base.resolve(Platform.resolvedExecutable);
if (executable == Platform.script || dev.NativeRuntime.buildId != null) {
// Probably aot compiled. We need to find "dart" another way.
bool found = false;
try {
final Uri repoDir = computeRepoDirUri();
for (String candidatePath in [
"out/ReleaseX64/dart",
"out/ReleaseX64/dart-sdk/bin/dart",
"out/ReleaseX64/dart.exe",
"out/ReleaseX64/dart-sdk/bin/dart.exe",
"xcodebuild/ReleaseX64/dart",
"xcodebuild/ReleaseX64/dart-sdk/bin/dart",
]) {
Uri candidate = repoDir.resolve(candidatePath);
if (File.fromUri(candidate).existsSync()) {
executable = candidate;
found = true;
break;
}
}
} catch (e) {
print("Warning: $e");
}
if (!found) {
Uri? candidate = where("dart");
if (candidate != null) {
executable = candidate;
} else {
throw "Couldn't find a dart executable to use.";
}
}
print("Using $executable");
}
return executable;
}
Uri? where(String needle) {
String pathEnvironment = Platform.environment["PATH"] ?? '';
List<String> paths;
if (Platform.isWindows) {
paths = pathEnvironment.split(";");
} else {
paths = pathEnvironment.split(":");
}
// This isn't great but will probably work for our purposes.
List<String> extensions = ["", ".exe", ".bat", ".com"];
for (String path in paths) {
for (String extension in extensions) {
File f = new File("$path${Platform.pathSeparator}$needle$extension");
if (f.existsSync()) {
return f.uri;
}
}
}
return null;
}

View file

@ -0,0 +1,144 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. 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:async';
import 'dart:io';
void main(List<String> args) {
// Use `runZonedGuarded` instead of try/catch, and do it here before anything
// else has been printed --- both because of
// https://github.com/dart-lang/sdk/issues/54911.
runZonedGuarded(() {
mainImpl(args);
}, (e, _) {
stderr.writeln("Error: $e");
});
}
const String CSI = "\x1b[";
Map<String, GitBranch> branches = {};
GitBranch getBranch(String name) {
GitBranch? result = branches[name];
if (result == null) {
result = branches[name] = new GitBranch(name);
}
return result;
}
void mainImpl(List<String> args) {
ProcessResult result = Process.runSync("git",
["branch", "--list", "--format=%(refname:short)%09%(upstream:short)"],
runInShell: true);
result.stdout.split("\n").forEach(processGitBranchLine);
result =
Process.runSync("git", ["branch", "--show-current"], runInShell: true);
String currentBranchName = result.stdout;
currentBranchName = currentBranchName.trim();
GitBranch currentBranch = branches[currentBranchName]!;
Set<String> involvedBranchNames = {};
currentBranch.collectSelfAndParentNames(involvedBranchNames);
currentBranch.collectSelfAndChildrenNames(involvedBranchNames);
result = Process.runSync(
"git",
[
"branch",
"--list",
"--format=%(refname:short)%09%(upstream:track)",
...involvedBranchNames
],
runInShell: true);
result.stdout.split("\n").forEach(processGitBranchTrackLine);
int indentation = currentBranch.parent?.printSelfAndParentChain() ?? 0;
currentBranch.printSelfAndChildren(indentation, color: true);
}
void processGitBranchLine(String gitLine) {
if (gitLine.isEmpty) return;
int pos = gitLine.indexOf("\t");
String thisName = gitLine.substring(0, pos);
String parentName = gitLine.substring(pos + 1).trim();
GitBranch thisBranch = getBranch(thisName);
GitBranch parentBranch = getBranch(parentName);
parentBranch.registerChild(thisBranch);
}
void processGitBranchTrackLine(String gitLine) {
if (gitLine.isEmpty) return;
int pos = gitLine.indexOf("\t");
String thisName = gitLine.substring(0, pos);
String tracking = gitLine.substring(pos + 1).trim();
GitBranch thisBranch = getBranch(thisName);
thisBranch.tracking = tracking;
}
class GitBranch {
final String name;
GitBranch? parent;
final List<GitBranch> children = [];
String? tracking;
GitBranch(this.name);
void collectSelfAndChildrenNames(Set<String> names) {
names.add(name);
for (GitBranch child in children) {
child.collectSelfAndChildrenNames(names);
}
}
void collectSelfAndParentNames(Set<String> names) {
parent?.collectSelfAndParentNames(names);
names.add(name);
}
void printSelfAndChildren(int indention, {bool color = false}) {
_printLineWithIndention(indention, color: color);
for (GitBranch child in children) {
child.printSelfAndChildren(indention + 1);
}
}
int printSelfAndParentChain() {
int indention = 0;
GitBranch? parent = this.parent;
if (parent != null) {
indention = parent.printSelfAndParentChain();
}
_printLineWithIndention(indention);
return indention + 1;
}
void registerChild(GitBranch child) {
children.add(child);
child.parent = this;
}
@override
String toString() {
return "GitBranch[$name, children = $children]";
}
void _printLineWithIndention(int indention, {bool color = false}) {
stdout.write("" * (indention));
stdout.write("├── ");
if (color) {
stdout.write("${CSI}31m");
}
stdout.write("$name");
if (color) {
stdout.write("${CSI}0m");
}
if (tracking != null) {
stdout.write(" $tracking");
}
stdout.write("\n");
}
}