diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 82be584fc4e..0074d4aa2db 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -308,6 +308,17 @@ copy_tree("copy_prebuilt_devtools") { exclude = "{}" } +# This action compiles the devtools app from sources. +action("build_devtools") { + output = "$root_out_dir/$dart_sdk_output/web/devtools_app" + outputs = [ output ] + script = "../tools/build_devtools.py" + args = [ + "--output", + rebase_path(output), + ] +} + # This loop generates rules to copy libraries to lib/ foreach(library, _full_sdk_libraries) { copy_tree("copy_${library}_library") { diff --git a/tools/build_devtools.dart b/tools/build_devtools.dart deleted file mode 100755 index 7ac30b31027..00000000000 --- a/tools/build_devtools.dart +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env dart -// Copyright (c) 2025, 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:io'; - -import 'package:path/path.dart' as path; - -// Builds the Flutter DevTools app from source. -void main() { - final flutterSdk = path.absolute('third_party', 'flutter', 'bin', 'flutter'); - if (!File(flutterSdk).existsSync()) { - stderr.writeln('Missing Flutter SDK at "$flutterSdk"; ' - 'make sure that "../.client" has a `custom_vars` section with ' - '`"build_devtools_from_sources": True,` and then run `gclient sync`'); - exitCode = 1; - return; - } - - final devtoolsDir = path.absolute('third_party', 'devtools_src'); - if (!Directory(devtoolsDir).existsSync()) { - stderr.writeln('Missing devtools dir in devtools sources "$devtoolsDir"; ' - 'make sure that "../.client" has a `custom_vars` section with ' - '`"build_devtools_from_sources": True,` and then run `gclient sync`'); - exitCode = 1; - return; - } - - final dtPath = path.absolute(devtoolsDir, 'tool', 'bin', 'dt.dart'); - final buildResult = Process.runSync( - Platform.resolvedExecutable, - [dtPath, '--flutter-sdk-path=$flutterSdk', 'build'], - workingDirectory: devtoolsDir, - ); - if (buildResult.exitCode != 0) { - stderr.writeln( - '\'${Platform.resolvedExecutable} $dtPath --flutter-sdk-path=$flutterSdk ' - 'build\' failed: exit code ${buildResult.exitCode}'); - stderr.writeln('stdout: >>>${buildResult.stdout}<<<'); - stderr.writeln('stderr: >>>${buildResult.stderr}<<<'); - exitCode = 1; - return; - } - - // One final check. - final buildDir = path.absolute('third_party', 'devtools_src', 'packages', - 'devtools_app', 'build', 'web'); - final mainJs = path.absolute(buildDir, 'main.dart.js'); - final mainWasm = path.absolute(buildDir, 'main.dart.wasm'); - if (!File(mainJs).existsSync()) { - stderr.writeln('Missing expected built JS app at "$mainJs"'); - exitCode = 1; - return; - } - if (!File(mainWasm).existsSync()) { - stderr.writeln('Missing expected built WASM app at "$mainWasm"'); - exitCode = 1; - return; - } - - // Otherwise, this script is silent. -} diff --git a/tools/build_devtools.py b/tools/build_devtools.py new file mode 100755 index 00000000000..3400f4b75a6 --- /dev/null +++ b/tools/build_devtools.py @@ -0,0 +1,108 @@ +#!/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. + +import argparse +import os +import shutil +import subprocess +import sys + +tools_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(tools_dir) +import utils + + +def main(): + parser = argparse.ArgumentParser(description='Builds the DevTools app.') + parser.add_argument('--output', help='Output directory') + args = parser.parse_args() + + if args.output: + args.output = os.path.abspath(args.output) + + # Set up paths relative to the SDK root. + sdk_root = utils.DART_DIR + devtools_src_dir = os.path.join(sdk_root, 'third_party', 'devtools_src') + devtools_app_dir = os.path.join(devtools_src_dir, 'packages', + 'devtools_app') + + if not os.path.isdir(devtools_src_dir): + print( + f'Missing devtools dir in devtools sources "{devtools_src_dir}"; ' + 'make sure that "../.client" has a `custom_vars` section with ' + '"`"build_devtools_from_sources": True,` and then run `gclient sync`' + '\n') + return 1 + + if not os.path.isdir(devtools_app_dir): + print( + f'Error: DevTools app directory not found at {devtools_app_dir}\n') + return 1 + + flutter_bin = os.path.normpath( + os.path.join(sdk_root, 'third_party', 'flutter', 'bin', 'flutter')) + if utils.GuessOS() == 'win32': + flutter_bin += '.bat' + if not os.path.isfile(flutter_bin): + print( + f'Missing Flutter SDK at "{flutter_bin}"; ' + 'make sure that "../.client" has a `custom_vars` section with ' + '"`"build_devtools_from_sources": True,` and then run `gclient sync`' + '\n') + return 1 + + try: + os.chdir(devtools_app_dir) + + print(f'Running {flutter_bin} pub get...') + subprocess.run([flutter_bin, 'pub', 'get'], check=True) + + print(f'Changing directory to {devtools_src_dir}') + os.chdir(devtools_src_dir) + + # We use the 'dart' binary found in the same directory as the 'flutter' binary. + dart_bin = os.path.join(os.path.dirname(flutter_bin), 'dart') + if utils.GuessOS() == 'win32': + dart_bin += '.exe' + dt_path = os.path.join('tool', 'bin', 'dt.dart') + + print(f'Building DevTools web app using dt tool...') + build_command = [ + dart_bin, dt_path, f'--flutter-sdk-path={flutter_bin}', 'build' + ] + subprocess.run(build_command, check=True) + + build_dir = os.path.join(devtools_app_dir, 'build', 'web') + main_js = os.path.join(build_dir, 'main.dart.js') + main_wasm = os.path.join(build_dir, 'main.dart.wasm') + + if not os.path.isfile(main_js): + print(f'Missing expected built JS app at "{main_js}"\n') + return 1 + + if not os.path.isfile(main_wasm): + print(f'Missing expected built WASM app at "{main_wasm}"\n') + return 1 + + if args.output: + print(f'Copying build results to {args.output}...') + if os.path.isdir(args.output): + shutil.rmtree(args.output) + elif os.path.exists(args.output): + os.remove(args.output) + shutil.copytree(build_dir, args.output) + + print('DevTools build successful.') + return 0 + except subprocess.CalledProcessError as e: + print(f'Error: Command failed with exit code {e.returncode}') + return e.returncode + except Exception as e: + print(f'An unexpected error occurred: {e}') + return 1 + + +if __name__ == '__main__': + sys.exit(main())