// Copyright (c) 2020, 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; import 'core.dart'; final Sdk sdk = Sdk._instance; /// A utility class for finding and referencing paths within the Dart SDK. class Sdk { static final Sdk _instance = _createSingleton(); /// Path to SDK directory. final String sdkPath; /// The SDK's semantic versioning version (x.y.z-a.b.channel). final String version; /// The SDK's git revision, if known. final String? revision; final bool _runFromBuildRoot; factory Sdk() => _instance; Sdk._(this.sdkPath, this.version, this.revision, bool runFromBuildRoot) : _runFromBuildRoot = runFromBuildRoot; // Assume that we want to use the same Dart executable that we used to spawn // DartDev. We should be able to run programs with out/ReleaseX64/dart even // if the SDK isn't completely built. String get dart { var basename = path.basename(Platform.executable); // It's possible that Platform.executable won't include the .exe extension // on Windows (e.g., launching `dart` from cmd.exe where `dart` is on the // PATH). Append .exe in this case so the `checkArtifactExists` check won't // fail. if (Platform.isWindows && !basename.endsWith('.exe')) { basename += '.exe'; } return path.absolute( _runFromBuildRoot ? sdkPath : path.absolute( sdkPath, 'bin', ), basename, ); } String get dartvm { final basename = Platform.isWindows? 'dartvm.exe' : 'dartvm'; return path.absolute( _runFromBuildRoot ? sdkPath : path.absolute( sdkPath, 'bin', ), basename, ); } String get dartAotRuntime => _runFromBuildRoot ? path.absolute( sdkPath, Platform.isWindows ? 'dartaotruntime_product.exe' : 'dartaotruntime_product', ) : path.absolute( sdkPath, 'bin', Platform.isWindows ? 'dartaotruntime.exe' : 'dartaotruntime', ); String get analysisServerAotSnapshot => _snapshotPathFor( 'analysis_server_aot.dart.snapshot', ); String get analysisServerSnapshot => _snapshotPathFor( 'analysis_server.dart.snapshot', ); String get ddcAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'dartdevc_aot_product.dart.snapshot', ) : _snapshotPathFor( 'dartdevc_aot.dart.snapshot', ); String get dart2jsAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'dart2js_aot_product.dart.snapshot', ) : _snapshotPathFor( 'dart2js_aot.dart.snapshot', ); String get dart2wasmSnapshot => _snapshotPathFor( 'dart2wasm_product.snapshot', ); String get dartMCPServerAotSnapshot => _snapshotPathFor( 'dart_mcp_server_aot.dart.snapshot', ); String get ddsAotSnapshot => _snapshotPathFor( 'dds_aot.dart.snapshot', ); String get frontendServerAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'frontend_server_aot_product.dart.snapshot', ) : _snapshotPathFor( 'frontend_server_aot.dart.snapshot', ); String get dtdAotSnapshot => _snapshotPathFor( 'dart_tooling_daemon_aot.dart.snapshot', ); String get devToolsBinaries => path.absolute( _runFromBuildRoot ? sdkPath : path.absolute( sdkPath, 'bin', 'resources', ), 'devtools', ); String get wasmOpt => path.absolute( _runFromBuildRoot ? sdkPath : path.absolute( sdkPath, 'bin', 'utils', ), Platform.isWindows ? 'wasm-opt.exe' : 'wasm-opt', ); // This file is only generated when building the SDK and isn't generated for // non-SDK build targets. String get librariesJson { if (_runFromBuildRoot) _emitNonSdkFileAccessWarning('libraries.json'); return path.absolute(sdkPath, 'lib', 'libraries.json'); } // This file is only generated when building the SDK and isn't generated for // non-SDK build targets. String get wasmPlatformDill { if (_runFromBuildRoot) { _emitNonSdkFileAccessWarning('dart2wasm_platform.dill'); } return path.absolute( sdkPath, 'lib', '_internal', 'dart2wasm_platform.dill'); } void _emitNonSdkFileAccessWarning(String file) { log.stderr( "WARNING: Attempting to access '$file' from a build root " 'executable. This file is only present in the context of a full Dart ' 'SDK.', ); } String _snapshotPathFor(String snapshotName) => path.absolute( _runFromBuildRoot ? sdkPath : path.absolute( sdkPath, 'bin', 'snapshots', ), snapshotName, ); static bool checkArtifactExists(String path, {bool logError = true}) { if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) { if (logError) { log.stderr( 'Could not find $path. Have you built the full Dart SDK?', ); } return false; } return true; } static Sdk _createSingleton() { // Find SDK path. (String, bool)? trySDKPath(String executablePath) { // The common case, and how cli_util.dart computes the Dart SDK directory, // [path.dirname] called twice on Platform.executable. We confirm by // asserting that the directory `./bin/snapshots/` exists in this directory: var sdkPath = path.absolute(path.dirname(path.dirname(executablePath))); var snapshotsDir = path.join(sdkPath, 'bin', 'snapshots'); var runFromBuildRoot = false; final type = FileSystemEntity.typeSync(snapshotsDir); if (type != FileSystemEntityType.directory && type != FileSystemEntityType.link) { // This is the less common case where the user is in // the checked out Dart SDK, and is executing `dart` via: // ./out/ReleaseX64/dart ... or in google3. sdkPath = path.absolute(path.dirname(executablePath)); snapshotsDir = sdkPath; runFromBuildRoot = true; } // Try to locate the DartDev snapshot to determine if we're able to find // the SDK snapshots with this SDK path. This is meant to handle // non-standard SDK layouts that can involve symlinks (e.g., Brew // installations, google3 tests, etc). if (!checkArtifactExists( path.join(snapshotsDir, 'dartdev_aot.dart.snapshot'), logError: false, )) { return null; } return (sdkPath, runFromBuildRoot); } final (sdkPath, runFromBuildRoot) = trySDKPath(Platform.resolvedExecutable) ?? trySDKPath(Platform.executable)!; // Defer to [Runtime] for the version. var version = Runtime.runtime.version; return Sdk._(sdkPath, version, getRevision(sdkPath), runFromBuildRoot); } /// Reads the contents of `revision` file at SDK root. /// /// Returns `null` if the file does not exist. static String? getRevision(String sdkPath) { String? revision; final revisionFile = File(path.join(sdkPath, 'revision')); if (revisionFile.existsSync()) { revision = revisionFile.readAsStringSync().trim(); } return revision; } } /// Information about the current runtime. class Runtime { static Runtime runtime = _createSingleton(); /// The SDK's semantic versioning version (x.y.z-a.b.channel). final String version; /// The SDK's release channel (`main`, `dev`, `beta`, `stable`). /// /// May be null if [Platform.version] does not have the expected format. final String? channel; Runtime._(this.version, this.channel); static Runtime _createSingleton() { var versionString = Platform.version; // Expected format: "version (channel) ..." var version = versionString; String? channel; var versionEnd = versionString.indexOf(' '); if (versionEnd > 0) { version = versionString.substring(0, versionEnd); var channelEnd = versionString.indexOf(' ', versionEnd + 1); if (channelEnd < 0) channelEnd = versionString.length; if (versionString.startsWith('(', versionEnd + 1) && versionString.startsWith(')', channelEnd - 1)) { channel = versionString.substring(versionEnd + 2, channelEnd - 1); } } return Runtime._(version, channel); } } const useAotSnapshotFlag = 'use-aot-snapshot';