Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[et] Resolve dart_executable as an executable #52250

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ group("flutter") {
public_deps += [
"//flutter/shell/testing",
"//flutter/tools/const_finder",
"//flutter/tools/engine_tool",
"//flutter/tools/font_subset",
]
}
Expand Down
104 changes: 104 additions & 0 deletions build/dart/rules.gni
Original file line number Diff line number Diff line change
Expand Up @@ -390,3 +390,107 @@ template("application_snapshot") {
}
}
}

# Generates a Dart executable using `dart compile exe`
#
# Arguments
# main_dart (required):
# The Dart entrypoint file.
#
# package_config (required):
# The packages.json file for the app.
#
# output (optional):
# The output executable name. By default, the target name. On Windows, an
# 'exe' is automatically appended.
#
# deps (optional):
# Additional dependencies. Dependencies on the frontend server and
# Flutter's platform.dill are included by default. This rule creates and
# uses a depfile, so listing all Dart sources is not necessary.
template("dart_executable") {
assert(defined(invoker.main_dart), "Must specify 'main_dart'")

main_dart = invoker.main_dart

package_config = rebase_path(".dart_tool/package_config.json")
if (defined(invoker.package_config)) {
package_config = rebase_path(invoker.package_config)
}
name = target_name

extra_deps = []
if (defined(invoker.deps)) {
extra_deps += invoker.deps
}

extra_inputs = [ main_dart ]
if (defined(invoker.inputs)) {
extra_inputs += invoker.inputs
}
extra_inputs += [ package_config ]

ext = ""
if (is_win) {
ext = ".exe"
}
output = "$target_gen_dir/$name$ext"
if (defined(invoker.output)) {
output = invoker.output
}

exe_vm_args = [ "--deterministic" ]
if (defined(invoker.vm_args)) {
exe_vm_args += invoker.vm_args
}

abs_output = rebase_path(output)
exe_vm_args += [
"compile",
"exe",
"--packages=$package_config",
"--output=$abs_output",
]

if (flutter_prebuilt_dart_sdk) {
action(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
deps = extra_deps
script = "//build/gn_run_binary.py"
inputs = extra_inputs
outputs = [ output ]
pool = "//flutter/build/dart:dart_pool"

dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext", root_build_dir)

args = [ dart ]
args += exe_vm_args
args += [ rebase_path(main_dart) ]
metadata = {
action_type = [ "dart_executable" ]
}
}
} else {
dart_action(target_name) {
forward_variables_from(invoker,
[
"testonly",
"visibility",
])
script = main_dart
pool = "//flutter/build/dart:dart_pool"
deps = extra_deps
inputs = extra_inputs
outputs = [ output ]
vm_args = exe_vm_args
args = []
metadata = {
action_type = [ "dart_executable" ]
}
}
}
}
88 changes: 88 additions & 0 deletions tools/engine_tool/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# 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("//flutter/build/dart/rules.gni")

dart_executable("engine_tool") {
main_dart = "bin/et.dart"
output = "$root_out_dir/et"
}

group("tests") {
testonly = true
public_deps = [
":build_command_test",
":entry_point_test",
":fetch_command_test",
":format_command_test",
":gn_utils_test",
":lint_command_test",
":logger_test",
":proc_utils_test",
":query_command_test",
":run_command_test",
":test_command_test",
":worker_pool_test",
]
}

dart_executable("build_command_test") {
testonly = true
main_dart = "test/build_command_test.dart"
}

dart_executable("entry_point_test") {
testonly = true
main_dart = "test/entry_point_test.dart"
}

dart_executable("fetch_command_test") {
testonly = true
main_dart = "test/fetch_command_test.dart"
}

dart_executable("format_command_test") {
testonly = true
main_dart = "test/format_command_test.dart"
}

dart_executable("gn_utils_test") {
testonly = true
main_dart = "test/gn_utils_test.dart"
}

dart_executable("lint_command_test") {
testonly = true
main_dart = "test/lint_command_test.dart"
}

dart_executable("logger_test") {
testonly = true
main_dart = "test/logger_test.dart"
}

dart_executable("proc_utils_test") {
testonly = true
main_dart = "test/proc_utils_test.dart"
}

dart_executable("query_command_test") {
testonly = true
main_dart = "test/query_command_test.dart"
}

dart_executable("run_command_test") {
testonly = true
main_dart = "test/run_command_test.dart"
}

dart_executable("test_command_test") {
testonly = true
main_dart = "test/test_command_test.dart"
}

dart_executable("worker_pool_test") {
testonly = true
main_dart = "test/worker_pool_test.dart"
}
96 changes: 70 additions & 26 deletions tools/engine_tool/lib/src/gn_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ enum BuildTargetType {
staticLibrary,
}

BuildTargetType? _buildTargetTypeFromString(String type) {
BuildTargetType? _buildTargetTypeFromString(String type, Map<String, Object?>? metadata) {
switch (type) {
case 'action':
if (_getActionType(metadata) == 'dart_executable') {
return BuildTargetType.executable;
}
return null;
case 'executable':
return BuildTargetType.executable;
case 'shared_library':
Expand All @@ -46,6 +51,16 @@ BuildTargetType? _buildTargetTypeFromString(String type) {
}
}

String? _getActionType(Map<String, Object?>? metadata) {
if (metadata != null) {
final List<String>? action_type = getListOfString(metadata, 'action_type');
if (action_type != null && action_type.isNotEmpty) {
return action_type[0];
}
}
return null;
}

// TODO(johnmccutchan): What should we do about source_sets and other
// output-less targets? Also, what about action targets which are kind of
// "internal" build steps? For now we are ignoring them.
Expand Down Expand Up @@ -77,30 +92,9 @@ final class BuildTarget {
Future<Map<String, BuildTarget>> findTargets(
Environment environment, Directory buildDir) async {
final Map<String, BuildTarget> r = <String, BuildTarget>{};
final List<String> getBuildInfoCommandLine = <String>[
gnBinPath(environment),
'desc',
buildDir.path,
'*',
'--format=json',
];

final ProcessRunnerResult result = await environment.processRunner.runProcess(
getBuildInfoCommandLine,
workingDirectory: environment.engine.srcDir,
failOk: true);

// Handle any process failures.
fatalIfFailed(environment, getBuildInfoCommandLine, result);

late final Map<String, Object?> jsonResult;
try {
jsonResult = jsonDecode(result.stdout) as Map<String, Object?>;
} catch (e) {
environment.logger.fatal(
'gn desc output could not be parsed:\nE=$e\nIN=${result.stdout}\n');
}

final Map<String, Object?> jsonResult =
await _runGnDesc(buildDir.path, '*', environment);
for (final MapEntry<String, Object?> targetEntry in jsonResult.entries) {
final String label = targetEntry.key;
if (targetEntry.value == null) {
Expand All @@ -113,14 +107,15 @@ Future<Map<String, BuildTarget>> findTargets(
if (typeString == null) {
environment.logger.fatal('gn desc is missing target type: $properties');
}
final BuildTargetType? type = _buildTargetTypeFromString(typeString!);
final Map<String, Object?>? metadata = getMap(properties, 'metadata');
final BuildTargetType? type = _buildTargetTypeFromString(typeString!, metadata);
if (type == null) {
// Target is a type that we don't support.
continue;
}
final bool testOnly = getBool(properties, 'testonly');
final List<String> outputs =
getListOfString(properties, 'outputs') ?? <String>[];
await _runGnOutputs(buildDir.path, label, environment);
File? executable;
if (type == BuildTargetType.executable) {
if (outputs.isEmpty) {
Expand All @@ -135,6 +130,55 @@ Future<Map<String, BuildTarget>> findTargets(
return r;
}

/// Returns the JSON output of running `gn desc buildDir label`.
Future<Map<String, Object?>> _runGnDesc(
String buildDir, String label, Environment environment) async {
final List<String> commandline = <String>[
gnBinPath(environment),
'desc',
buildDir,
label,
'--format=json',
];

final ProcessRunnerResult result = await environment.processRunner.runProcess(
commandline,
workingDirectory: environment.engine.srcDir,
failOk: true);

// Handle any process failures.
fatalIfFailed(environment, commandline, result);

late final Map<String, Object?> jsonResult;
try {
jsonResult = jsonDecode(result.stdout) as Map<String, Object?>;
} catch (e) {
environment.logger.fatal(
'gn desc output could not be parsed:\nE=$e\nIN=${result.stdout}\n');
}
return jsonResult;
}

/// Returns the output paths returned by `gn outputs buildDir label`.
Future<List<String>> _runGnOutputs(
String buildDir, String label, Environment environment) async {
final List<String> commandline = <String>[
gnBinPath(environment),
'outputs',
buildDir,
label,
];
final ProcessRunnerResult result = await environment.processRunner.runProcess(
commandline,
workingDirectory: environment.engine.srcDir,
failOk: true);

// Handle any process failures.
fatalIfFailed(environment, commandline, result);

return result.stdout.split('\n');
}

/// Process selectors and filter allTargets for matches.
///
/// We support:
Expand Down
8 changes: 8 additions & 0 deletions tools/engine_tool/lib/src/json_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,11 @@ List<String>? getListOfString(Map<String, Object?> map, String field) {
}
return (map[field]! as List<Object?>).cast<String>();
}

/// Returns the value in map[field] iff it is a Map<String, Object?>. null otherwise.
Map<String, Object?>? getMap(Map<String, Object?> map, String field) {
if (map[field] case final Map<String, Object?> value) {
return value;
}
return null;
}