This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
A minimal engine_tools_lib
to use for local-repo Dart tooling
#45154
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
2aa40e0
Write a minimal engine_tools_lib to use for local-repo Dart tooling.
matanlurey 67b18ed
Whitespace.
matanlurey 80e9fe3
More whitespace.
matanlurey 928475b
Add to run_tests.py.
matanlurey 7507281
Reformat.
matanlurey 11f47e9
Reformat.
matanlurey ac80328
Reformat.
matanlurey 1660375
Add missing quotes.
matanlurey 4c5d551
Fix pub get.
matanlurey df5f58d
Merge branch 'main' into engine-tools-lib
matanlurey e0078f9
Move engine_tools_lib, fix a flaky test.
matanlurey eb5a7e5
Rename engine_tools_lib to engine_repo_tools.
matanlurey 90ccb77
Finish renaming.
matanlurey 3e20c04
All tests passing, Zach's suggestion.
matanlurey c83a724
Formatting.
matanlurey 4faa9d0
Merge remote-tracking branch 'upstream/main' into engine-tools-lib
matanlurey 7fe2165
Merge remote-tracking branch 'upstream/main' into engine-tools-lib
matanlurey c130851
Add a note.
matanlurey 99d44c8
Whitespace.
matanlurey fd69560
Merge branch 'main' into engine-tools-lib
matanlurey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# engine_repo_tools | ||
|
||
This is a repo-internal library for `flutter/engine`, that contains shared code | ||
for writing tools that operate on the engine repository. For example, finding | ||
the latest compiled engine artifacts in the `out/` directory: | ||
|
||
```dart | ||
import 'package:engine_repo_tools/engine_repo_tools.dart'; | ||
|
||
void main() { | ||
final engine = Engine.findWithin(); | ||
final latest = engine.latestOutput(); | ||
if (latest != null) { | ||
print('Latest compile_commands.json: ${latest.compileCommandsJson?.path}'); | ||
} | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
// 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. | ||
|
||
/// A minimal library for discovering and probing a local engine repository. | ||
/// | ||
/// This library is intended to be used by tools that need to interact with a | ||
/// local engine repository, such as `clang_tidy` or `githooks`. For example, | ||
/// finding the `compile_commands.json` file for the most recently built output: | ||
/// | ||
/// ```dart | ||
/// final Engine engine = Engine.findWithin(); | ||
/// final Output? output = engine.latestOutput(); | ||
/// if (output == null) { | ||
/// print('No output targets found.'); | ||
/// } else { | ||
/// final io.File? compileCommandsJson = output.compileCommandsJson; | ||
/// if (compileCommandsJson == null) { | ||
/// print('No compile_commands.json file found.'); | ||
/// } else { | ||
/// print('Found compile_commands.json file at ${compileCommandsJson.path}'); | ||
/// } | ||
/// } | ||
/// ``` | ||
library; | ||
|
||
import 'dart:io' as io; | ||
|
||
import 'package:path/path.dart' as p; | ||
|
||
/// Represents the `$ENGINE` directory (i.e. a checked-out Flutter engine). | ||
/// | ||
/// If you have a path to the `$ENGINE/src` directory, use [Engine.fromSrcPath]. | ||
/// | ||
/// If you have a path to a directory within the `$ENGINE/src` directory, or | ||
/// want to use the current working directory, use [Engine.findWithin]. | ||
final class Engine { | ||
/// Creates an [Engine] from a path such as `/Users/.../flutter/engine/src`. | ||
/// | ||
/// ```dart | ||
/// final Engine engine = Engine.findWithin('/Users/.../engine/src'); | ||
/// print(engine.srcDir.path); // /Users/.../engine/src | ||
/// ``` | ||
/// | ||
/// Throws a [InvalidEngineException] if the path is not a valid engine root. | ||
factory Engine.fromSrcPath(String srcPath) { | ||
// If the path does not end in `/src`, fail. | ||
if (p.basename(srcPath) != 'src') { | ||
throw InvalidEngineException.doesNotEndWithSrc(srcPath); | ||
} | ||
|
||
// If the directory does not exist, or is not a directory, fail. | ||
final io.Directory srcDir = io.Directory(srcPath); | ||
if (!srcDir.existsSync()) { | ||
throw InvalidEngineException.notADirectory(srcPath); | ||
} | ||
|
||
// Check for the existence of a `flutter` directory within `src`. | ||
final io.Directory flutterDir = io.Directory(p.join(srcPath, 'flutter')); | ||
if (!flutterDir.existsSync()) { | ||
throw InvalidEngineException.missingFlutterDirectory(srcPath); | ||
} | ||
|
||
// We do **NOT** check for the existence of a `out` directory within `src`, | ||
// it's not required to exist (i.e. a new checkout of the engine), and we | ||
// don't want to fail if it doesn't exist. | ||
final io.Directory outDir = io.Directory(p.join(srcPath, 'out')); | ||
|
||
return Engine._(srcDir, flutterDir, outDir); | ||
} | ||
|
||
/// Creates an [Engine] by looking for a `src/` directory in the given path. | ||
/// | ||
/// ```dart | ||
/// // Use the current working directory. | ||
/// final Engine engine = Engine.findWithin(); | ||
/// print(engine.srcDir.path); // /Users/.../engine/src | ||
/// | ||
/// // Use a specific directory. | ||
/// final Engine engine = Engine.findWithin('/Users/.../engine/src/foo/bar/baz'); | ||
/// print(engine.srcDir.path); // /Users/.../engine/src | ||
/// ``` | ||
/// | ||
/// If a path is not provided, the current working directory is used. | ||
/// | ||
/// Throws a [StateError] if the path is not within a valid engine. | ||
factory Engine.findWithin([String? path]) { | ||
path ??= p.current; | ||
|
||
// Search parent directories for a `src` directory. | ||
io.Directory maybeSrcDir = io.Directory(path); | ||
|
||
if (!maybeSrcDir.existsSync()) { | ||
throw StateError( | ||
'The path "$path" does not exist or is not a directory.' | ||
); | ||
} | ||
|
||
do { | ||
try { | ||
return Engine.fromSrcPath(maybeSrcDir.path); | ||
} on InvalidEngineException { | ||
// Ignore, we'll keep searching. | ||
} | ||
maybeSrcDir = maybeSrcDir.parent; | ||
} while (maybeSrcDir.parent.path != maybeSrcDir.path /* at root */); | ||
|
||
throw StateError( | ||
'The path "$path" is not within a Flutter engine source directory.' | ||
); | ||
} | ||
|
||
const Engine._( | ||
this.srcDir, | ||
this.flutterDir, | ||
this.outDir, | ||
); | ||
|
||
/// The path to the `$ENGINE/src` directory. | ||
final io.Directory srcDir; | ||
|
||
/// The path to the `$ENGINE/src/flutter` directory. | ||
final io.Directory flutterDir; | ||
|
||
/// The path to the `$ENGINE/src/out` directory. | ||
/// | ||
/// **NOTE**: This directory may not exist. | ||
final io.Directory outDir; | ||
|
||
/// Returns a list of all output targets in [outDir]. | ||
List<Output> outputs() { | ||
return outDir | ||
.listSync() | ||
.whereType<io.Directory>() | ||
.map<Output>(Output._) | ||
.toList(); | ||
} | ||
|
||
/// Returns the most recently modified output target in [outDir]. | ||
/// | ||
/// If there are no output targets, returns `null`. | ||
Output? latestOutput() { | ||
final List<Output> outputs = this.outputs(); | ||
if (outputs.isEmpty) { | ||
return null; | ||
} | ||
outputs.sort((Output a, Output b) { | ||
return b.dir.statSync().modified.compareTo(a.dir.statSync().modified); | ||
}); | ||
return outputs.first; | ||
} | ||
} | ||
|
||
/// Thrown when an [Engine] could not be created from a path. | ||
sealed class InvalidEngineException implements Exception { | ||
/// Thrown when an [Engine] was created from a path not ending in `src`. | ||
factory InvalidEngineException.doesNotEndWithSrc(String path) { | ||
return InvalidEngineSrcPathException._(path); | ||
} | ||
|
||
/// Thrown when an [Engine] was created from a directory that does not exist. | ||
factory InvalidEngineException.notADirectory(String path) { | ||
return InvalidEngineNotADirectoryException._(path); | ||
} | ||
|
||
/// Thrown when an [Engine] was created from a path not containing `flutter/`. | ||
factory InvalidEngineException.missingFlutterDirectory(String path) { | ||
return InvalidEngineMissingFlutterDirectoryException._(path); | ||
} | ||
} | ||
|
||
/// Thrown when an [Engine] was created from a path not ending in `src`. | ||
final class InvalidEngineSrcPathException implements InvalidEngineException { | ||
InvalidEngineSrcPathException._(this.path); | ||
|
||
/// The path that was used to create the [Engine]. | ||
final String path; | ||
|
||
@override | ||
String toString() { | ||
return 'The path $path does not end in `${p.separator}src`.'; | ||
} | ||
} | ||
|
||
/// Thrown when an [Engine] was created from a path that is not a directory. | ||
final class InvalidEngineNotADirectoryException implements InvalidEngineException { | ||
InvalidEngineNotADirectoryException._(this.path); | ||
|
||
/// The path that was used to create the [Engine]. | ||
final String path; | ||
|
||
@override | ||
String toString() { | ||
return 'The path "$path" does not exist or is not a directory.'; | ||
} | ||
} | ||
|
||
/// Thrown when an [Engine] was created from a path not containing `flutter/`. | ||
final class InvalidEngineMissingFlutterDirectoryException implements InvalidEngineException { | ||
InvalidEngineMissingFlutterDirectoryException._(this.path); | ||
|
||
/// The path that was used to create the [Engine]. | ||
final String path; | ||
|
||
@override | ||
String toString() { | ||
return 'The path "$path" does not contain a "flutter" directory.'; | ||
} | ||
} | ||
|
||
/// Represents a single output target in the `$ENGINE/src/out` directory. | ||
final class Output { | ||
const Output._(this.dir); | ||
|
||
/// The directory containing the output target. | ||
final io.Directory dir; | ||
|
||
/// The `compile_commands.json` file for this output target. | ||
/// | ||
/// Returns `null` if the file does not exist. | ||
io.File? get compileCommandsJson { | ||
final io.File file = io.File(p.join(dir.path, 'compile_commands.json')); | ||
if (!file.existsSync()) { | ||
return null; | ||
} | ||
return file; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# 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. | ||
|
||
name: engine_repo_tools | ||
publish_to: none | ||
environment: | ||
sdk: ^3.0.0 | ||
|
||
# Do not add any dependencies that require more than what is provided in | ||
# //third_party/pkg, //third_party/dart/pkg, or | ||
# //third_party/dart/third_party/pkg. In particular, package:test is not usable | ||
# here. | ||
|
||
# If you do add packages here, make sure you can run `pub get --offline`, and | ||
# check the .packages and .package_config to make sure all the paths are | ||
# relative to this directory into //third_party/dart | ||
|
||
dependencies: | ||
meta: any | ||
path: any | ||
|
||
dev_dependencies: | ||
async_helper: any | ||
expect: any | ||
litetest: any | ||
smith: any | ||
|
||
dependency_overrides: | ||
async_helper: | ||
path: ../../../../third_party/dart/pkg/async_helper | ||
expect: | ||
path: ../../../../third_party/dart/pkg/expect | ||
litetest: | ||
path: ../../../testing/litetest | ||
meta: | ||
path: ../../../../third_party/dart/pkg/meta | ||
path: | ||
path: ../../../../third_party/dart/third_party/pkg/path | ||
smith: | ||
path: ../../../../third_party/dart/pkg/smith |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.