Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f5abfa0

Browse files
authored
Introduce a GN rule that (explicitly) generates a dart test wrapper (#55475)
Closes flutter/flutter#155769. This is a variant of the approach in #52241, based on feedback from @jakemac53 and @jonahwilliams (who originally sped up `dart test` significantly by using `frontend_server` under the scenes: dart-lang/test#1399), in short: ```gn # tools/engine_tool/BUILD.gn import("//flutter/build/dart/internal/gen_dartcli_call.gni") gen_dartcli_call("tests") { args = [ "test" ] cwd = "//flutter/tools/engine_tool" } ``` This stanza, when built (`ninja -C ../out/host_debug flutter/tools/engine_tool:tests`) generates the following file: ```sh # ../out/host_debug/gen/flutter/tools/engine_tool/tests set -e # Needed because if it is set, cd may print the path it changed to. unset CDPATH # Store the current working directory. prev_cwd=$(pwd) # Set a trap to restore the working directory. trap 'cd "$prev_cwd"' EXIT CD_PATH="/Users/matanl/Developer/engine/src/flutter/tools/engine_tool" if [ -n "$CD_PATH" ]; then cd "$CD_PATH" fi /Users/matanl/Developer/engine/src/flutter/prebuilts/macos-arm64/dart-sdk/bin/dart "test" ``` In turn, when executed (`../out/host_debug/gen/flutter/tools/engine_tool/tests`) it just runs `dart test`: ```sh flutter % ../out/host_debug/gen/flutter/tools/engine_tool/tests Building package executable... Built test:test. 00:00 +0: test/test_command_test.dart: test command executes test 00:00 +3: test/run_command_test.dart: run command invokes flutter run ... 00:00 +117: All tests passed! ``` There is no actual compilation performed by the tool (that is handled implicitly by `dart test`), and as a result neither a `depfile` is needed, nor generating a pre-compiled artifact like a snapshot or AOT elf/assembly. --- This work is incomplete, that is, we'd want to properly tag the executable so `et` can find it, and create a wrapper template (i.e. `dart_test`) that tightens things up a bit, but I wanted to show the work at this intermediate step to get feedback before moving forward. /cc @jonahwilliams, @jtmcdole as well.
1 parent a6a7b84 commit f5abfa0

File tree

6 files changed

+217
-0
lines changed

6 files changed

+217
-0
lines changed

BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@ group("flutter") {
129129

130130
if (build_engine_artifacts) {
131131
public_deps += [
132+
"//flutter/build/dart/test:gen_dartcli_call",
133+
"//flutter/build/dart/test:gen_executable_call",
132134
"//flutter/shell/testing",
133135
"//flutter/tools/const_finder",
136+
"//flutter/tools/engine_tool:tests",
134137
"//flutter/tools/font_subset",
135138
]
136139
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
import("//flutter/build/dart/internal/gen_executable_call.gni")
6+
import("//flutter/common/config.gni")
7+
8+
# Generates an executable that runs `dart` with a command and set of arguments.
9+
#
10+
# Parameters:
11+
# args (optional):
12+
# Arguments to pass to the Dart CLI.
13+
#
14+
# cwd (optional):
15+
# The working directory for the command.
16+
#
17+
# output (optional):
18+
# Overrides the full output path.
19+
# Defaults to $root_out_dir/gen/$target_path/$target_name; for example
20+
# //flutter/foo/bar emits a binary at out/{variant}/gen/flutter/foo/bar.
21+
template("gen_dartcli_call") {
22+
# Build a reference to the Dart CLI (depending on prebuilt or source).
23+
ext = ""
24+
if (is_win) {
25+
ext = ".exe"
26+
}
27+
dart = rebase_path("$host_prebuilt_dart_sdk/bin/dart$ext")
28+
29+
# Add default arguments to the Dart CLI.
30+
dart_args = []
31+
if (defined(invoker.args)) {
32+
dart_args += invoker.args
33+
}
34+
dart_args += [ "--suppress-analytics" ]
35+
36+
# Actually generate the shell script.
37+
gen_executable_call(target_name) {
38+
command = rebase_path(dart)
39+
args = dart_args
40+
forward_variables_from(invoker,
41+
[
42+
"cwd",
43+
"output",
44+
])
45+
}
46+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
# Generates an executable that runs a command and set of arguments.
6+
#
7+
# Parameters:
8+
# command (required):
9+
# The command to run, which is typically an absolute path to an executable.
10+
#
11+
# args (optional):
12+
# Arguments to pass to the command.
13+
#
14+
# cwd (optional):
15+
# The working directory for the command.
16+
#
17+
# output (optional):
18+
# Overrides the full output path.
19+
# Defaults to $root_out_dir/gen/$target_path/$target_name; for example
20+
# //flutter/foo/bar emits a binary at out/{variant}/gen/flutter/foo/bar.
21+
template("gen_executable_call") {
22+
assert(defined(invoker.command), "Must specify 'command'")
23+
24+
# The command to run.
25+
command = invoker.command
26+
27+
# The output path.
28+
output = "$root_gen_dir/$target_name"
29+
if (defined(invoker.output)) {
30+
output = invoker.output
31+
} else {
32+
# Construct the output path.
33+
output = get_label_info(target_name, "target_gen_dir")
34+
}
35+
36+
# Build the command line arguments.
37+
call_args = [
38+
"--output",
39+
rebase_path(output),
40+
"--command",
41+
command,
42+
]
43+
if (defined(invoker.cwd)) {
44+
call_args += [
45+
"--cwd",
46+
rebase_path(invoker.cwd),
47+
]
48+
}
49+
if (defined(invoker.args)) {
50+
call_args += [
51+
"--",
52+
string_join(" ", invoker.args),
53+
]
54+
}
55+
56+
# Run build_cmd.py to generate the file and make it executable.
57+
action(target_name) {
58+
script = "//flutter/build/dart/internal/gen_executable_call.py"
59+
outputs = [ output ]
60+
args = call_args
61+
}
62+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright 2013 The Flutter Authors. All rights reserved.
4+
# Use of this source code is governed by a BSD-style license that can be
5+
# found in the LICENSE file.
6+
7+
"""Generates a shell or batch script to run a command."""
8+
9+
import argparse
10+
import os
11+
import string
12+
13+
14+
def main():
15+
parser = argparse.ArgumentParser(description=__doc__)
16+
parser.add_argument('--output', required=True, help='Output file')
17+
parser.add_argument('--command', required=True, help='Command to run')
18+
parser.add_argument('--cwd', required=False, help='Working directory')
19+
parser.add_argument('rest', nargs='*', help='Arguments to pass to the command')
20+
21+
# Rest of the arguments are passed to the command.
22+
args = parser.parse_args()
23+
24+
out_path = os.path.dirname(args.output)
25+
if not os.path.exists(out_path):
26+
os.makedirs(out_path)
27+
28+
script = string.Template(
29+
'''#!/bin/sh
30+
31+
set -e
32+
33+
# Set a trap to restore the working directory.
34+
trap "popd > /dev/null" EXIT
35+
pushd "$cwd" > /dev/null
36+
37+
$command $args
38+
'''
39+
)
40+
41+
params = {
42+
'command': args.command,
43+
'args': ' '.join(args.rest),
44+
'cwd': args.cwd if args.cwd else '',
45+
}
46+
47+
with open(args.output, 'w') as f:
48+
f.write(script.substitute(params))
49+
50+
# Make the script executable.
51+
os.chmod(args.output, 0o755)
52+
53+
54+
if __name__ == '__main__':
55+
main()

build/dart/test/BUILD.gn

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
import("//flutter/build/dart/internal/gen_dartcli_call.gni")
6+
import("//flutter/build/dart/internal/gen_executable_call.gni")
7+
8+
# Manual test targets that can be used to iterate on individual rules.
9+
10+
# ninja -C ../out/host_debug flutter/build/dart/test:gen_executable_call
11+
# ../out/host_debug/gen/flutter/build/dart/test/gen_executable_call
12+
#
13+
# Expected output: "Hello, World!"
14+
gen_executable_call("gen_executable_call") {
15+
command = "echo"
16+
args = [ "Hello, World!" ]
17+
}
18+
19+
# ninja -C ../out/host_debug flutter/build/dart/test:gen_executable_call_with_cwd
20+
# ../out/host_debug/gen/flutter/build/dart/test/gen_executable_call_with_cwd
21+
#
22+
# Expected output: {varies}/flutter/build/dart/test
23+
gen_executable_call("gen_executable_call_with_cwd") {
24+
command = "echo \$PWD"
25+
cwd = "//flutter/build/dart/test"
26+
}
27+
28+
# ninja -C ../out/host_debug flutter/build/dart/test:gen_dartcli_call
29+
# ../out/host_debug/gen/flutter/build/dart/test/gen_dartcli_call
30+
#
31+
# Expected output: Dart SDK version: 3.6.0-273.0.dev (dev)
32+
#
33+
# ... or something like ^, obviously it will constantly change as the SDK rolls.
34+
gen_dartcli_call("gen_dartcli_call") {
35+
args = [ "--version" ]
36+
}

tools/engine_tool/BUILD.gn

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
import("//flutter/build/dart/internal/gen_dartcli_call.gni")
6+
7+
# TODO(matanl): WIP in https://github.com/flutter/flutter/issues/155769.
8+
#
9+
# Example usage:
10+
# ninja -C ../out/host_debug flutter/tools/engine_tool:tests
11+
# ../out/host_debug/gen/flutter/tools/engine_tool/tests
12+
gen_dartcli_call("tests") {
13+
args = [ "test" ]
14+
cwd = "//flutter/tools/engine_tool"
15+
}

0 commit comments

Comments
 (0)