blob: 92e24398fb7f21d82688f3d1fd3ff896582687af [file] [edit]
#!/usr/bin/env python3
# Copyright (c) 2026, 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.
# build_test_fast.py
#
# A smart wrapper around tools/build.py and tools/test.py that uses a
# minimal, hardcoded mapping to determine exactly what needs to be built.
# This avoids building the entire SDK (like CI does) just to run a single test.
import sys
import os
import subprocess
import platform
def get_host_os():
if sys.platform == 'win32':
return 'win'
elif sys.platform == 'darwin':
return 'mac'
return 'linux'
def get_host_arch():
machine = platform.machine().lower()
if machine in ['arm64', 'aarch64']:
return 'arm64'
return 'x64'
def get_minimal_build_targets(compiler, test_paths):
targets = set()
# 1. Base compiler targets (Minimal!)
if compiler == 'dart2wasm':
targets.update([
'dartaotruntime', 'dart2wasm_platform.dill', 'dart2wasm',
'create_common_sdk', 'wasm-opt'
])
elif compiler == 'dart2js':
# Often dart2js_bot or dart2js_platform.dill is enough, rather than create_sdk
targets.update(
['dartaotruntime', 'dart2js_platform.dill', 'create_common_sdk'])
elif compiler == 'ddc':
targets.update(['ddc_stable_test_local', 'create_common_sdk'])
elif compiler in ['analyzer', 'dart2analyzer']:
pass # Often no build needed, or just dartanalyzer
elif compiler == 'dartkp':
targets.update(['runtime', 'runtime_precompiled'])
elif compiler in ['vm', 'dartk', 'app_jitk', 'none']:
targets.update(['runtime'])
else:
targets.update(['runtime'])
# 2. Path-specific add-ons
for path in test_paths:
if 'tests/ffi' in path:
targets.update(['ffi_test_functions', 'ffi_test_dynamic_library'])
return list(targets)
def guess_compiler_from_paths(test_paths):
# Try to guess the best compiler based on the test paths
for path in test_paths:
# Wasm tests
if 'tests/web/wasm' in path or 'pkg/dart2wasm' in path:
return 'dart2wasm'
# DDC specific tests
elif 'tests/dartdevc' in path or 'pkg/dev_compiler' in path:
return 'ddc'
# General web / dart2js tests
elif 'tests/web' in path or 'pkg/compiler' in path:
return 'dart2js'
# Analyzer / language server tests
elif 'pkg/analyzer' in path or 'pkg/analysis_server' in path:
return 'dart2analyzer'
# Default to the VM's JIT compiler
return 'dartk'
def main():
args = sys.argv[1:]
compiler = None
runtime = None
test_paths = []
# test.py defaults to release mode, while build.py defaults to debug.
# We MUST pass the mode and arch explicitly to build.py to match.
mode = 'release'
arch = get_host_arch()
system = get_host_os()
# Parse out compiler, mode, arch and test paths
skip_next = False
for i, arg in enumerate(args):
if skip_next:
skip_next = False
continue
if arg == '-c' or arg == '--compiler':
compiler = args[i + 1]
skip_next = True
elif arg.startswith('--compiler='):
compiler = arg.split('=', 1)[1]
elif arg == '-m' or arg == '--mode':
mode = args[i + 1]
skip_next = True
elif arg.startswith('--mode='):
mode = arg.split('=', 1)[1]
elif arg == '-a' or arg == '--arch':
arch = args[i + 1]
skip_next = True
elif arg.startswith('--arch='):
arch = arg.split('=', 1)[1]
elif arg == '-r' or arg == '--runtime':
runtime = args[i + 1]
skip_next = True
elif arg.startswith('--runtime='):
runtime = arg.split('=', 1)[1]
elif not arg.startswith('-'):
test_paths.append(arg)
# Infer the compiler if not explicitly provided
inferred = False
if compiler is None:
compiler = guess_compiler_from_paths(test_paths)
inferred = True
if inferred:
print(
f"🔮 Inferred compiler '\033[1m{compiler}\033[0m' from test paths.")
# Default to d8 for web tests to reduce noise, if no runtime specified
inferred_runtime = False
if runtime is None and compiler in ['dart2js', 'dart2wasm', 'ddc']:
runtime = 'd8'
inferred_runtime = True
print(
f"🔮 Inferred runtime '\033[1m{runtime}\033[0m' for web compiler to reduce noise."
)
build_args = get_minimal_build_targets(compiler, test_paths)
targets_str = ' '.join(build_args) if build_args else '(none)'
print(
f"🎯 Determined minimal build targets for compiler '\033[1m{compiler}\033[0m': \033[1m{targets_str}\033[0m"
)
stars_line = "🔹 " * 35
if build_args:
# 3. Build Dart using the minimal targets, matching test.py's mode and arch defaults
build_script = os.path.join(os.path.dirname(__file__), 'build.py')
build_cmd = [sys.executable, build_script, '-m', mode, '-a', arch
] + build_args
print(f"🚀 Building: python3 tools/build.py {' '.join(build_cmd[2:])}")
print(stars_line)
build_result = subprocess.run(build_cmd)
print(stars_line)
if build_result.returncode != 0:
print("❌ Build failed! Aborting test run.")
sys.exit(build_result.returncode)
print("⭐⭐⭐ Build succeeded! ⭐⭐⭐\n")
else:
print("❓ No build targets required!\n")
# 4. Run the tests
test_script = os.path.join(os.path.dirname(__file__), 'test.py')
if inferred:
args = ['-c', compiler] + args
if inferred_runtime:
args = ['-r', runtime] + args
test_cmd = [sys.executable, test_script] + args
print(f"🧪 Running tests: python3 tools/test.py {' '.join(args)}")
print(stars_line)
test_result = subprocess.run(test_cmd)
print(stars_line)
if test_result.returncode != 0:
print("\n" + "⚠️ " * 35)
print(" TEST FAILED!")
print(
"If this failure looks like a missing file, missing snapshot, or compilation error,"
)
print(
"it's possible that `build_test_fast.py` didn't build all the required dependencies."
)
print(
"💡 You may need to update the `get_minimal_build_targets()` mapping in this script."
)
print(
" (Check tools/bots/test_matrix.json to see what CI builds for this test suite!)"
)
print("⚠️ " * 35 + "\n")
else:
print("⭐⭐⭐ Tests succeeded! ⭐⭐⭐")
sys.exit(test_result.returncode)
if __name__ == '__main__':
main()