|
| 1 | +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | +// BSD-style license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +import 'dart:io'; |
| 6 | + |
| 7 | +import 'package:path/path.dart' as path; |
| 8 | + |
| 9 | +final Sdk sdk = Sdk._instance; |
| 10 | + |
| 11 | +/// A utility class for finding and referencing paths within the Dart SDK. |
| 12 | +class Sdk { |
| 13 | + static final Sdk _instance = _createSingleton(); |
| 14 | + |
| 15 | + /// Path to SDK directory. |
| 16 | + final String sdkPath; |
| 17 | + |
| 18 | + /// The SDK's semantic versioning version (x.y.z-a.b.channel). |
| 19 | + final String version; |
| 20 | + |
| 21 | + /// The SDK's git revision, if known. |
| 22 | + final String? revision; |
| 23 | + |
| 24 | + final bool runFromBuildRoot; |
| 25 | + |
| 26 | + factory Sdk() => _instance; |
| 27 | + |
| 28 | + Sdk._(this.sdkPath, this.version, this.revision, this.runFromBuildRoot); |
| 29 | + |
| 30 | + // Assume that we want to use the same Dart executable that we used to spawn |
| 31 | + // DartDev. We should be able to run programs with out/ReleaseX64/dart even |
| 32 | + // if the SDK isn't completely built. |
| 33 | + String get dart { |
| 34 | + var basename = path.basename(Platform.executable); |
| 35 | + // It's possible that Platform.executable won't include the .exe extension |
| 36 | + // on Windows (e.g., launching `dart` from cmd.exe where `dart` is on the |
| 37 | + // PATH). Append .exe in this case so the `checkArtifactExists` check won't |
| 38 | + // fail. |
| 39 | + if (Platform.isWindows && !basename.endsWith('.exe')) { |
| 40 | + basename += '.exe'; |
| 41 | + } |
| 42 | + return path.absolute( |
| 43 | + runFromBuildRoot |
| 44 | + ? sdkPath |
| 45 | + : path.absolute( |
| 46 | + sdkPath, |
| 47 | + 'bin', |
| 48 | + ), |
| 49 | + basename, |
| 50 | + ); |
| 51 | + } |
| 52 | + |
| 53 | + String get dartvm { |
| 54 | + final basename = Platform.isWindows ? 'dartvm.exe' : 'dartvm'; |
| 55 | + return path.absolute( |
| 56 | + runFromBuildRoot |
| 57 | + ? sdkPath |
| 58 | + : path.absolute( |
| 59 | + sdkPath, |
| 60 | + 'bin', |
| 61 | + ), |
| 62 | + basename, |
| 63 | + ); |
| 64 | + } |
| 65 | + |
| 66 | + String get dartAotRuntime => runFromBuildRoot |
| 67 | + ? path.absolute( |
| 68 | + sdkPath, |
| 69 | + Platform.isWindows |
| 70 | + ? 'dartaotruntime_product.exe' |
| 71 | + : 'dartaotruntime_product', |
| 72 | + ) |
| 73 | + : path.absolute( |
| 74 | + sdkPath, |
| 75 | + 'bin', |
| 76 | + Platform.isWindows ? 'dartaotruntime.exe' : 'dartaotruntime', |
| 77 | + ); |
| 78 | + |
| 79 | + String get analysisServerAotSnapshot => _snapshotPathFor( |
| 80 | + 'analysis_server_aot.dart.snapshot', |
| 81 | + ); |
| 82 | + |
| 83 | + String get analysisServerSnapshot => _snapshotPathFor( |
| 84 | + 'analysis_server.dart.snapshot', |
| 85 | + ); |
| 86 | + |
| 87 | + String get ddcAotSnapshot => runFromBuildRoot |
| 88 | + ? _snapshotPathFor( |
| 89 | + 'dartdevc_aot_product.dart.snapshot', |
| 90 | + ) |
| 91 | + : _snapshotPathFor( |
| 92 | + 'dartdevc_aot.dart.snapshot', |
| 93 | + ); |
| 94 | + |
| 95 | + String get dart2jsAotSnapshot => runFromBuildRoot |
| 96 | + ? _snapshotPathFor( |
| 97 | + 'dart2js_aot_product.dart.snapshot', |
| 98 | + ) |
| 99 | + : _snapshotPathFor( |
| 100 | + 'dart2js_aot.dart.snapshot', |
| 101 | + ); |
| 102 | + |
| 103 | + String get dart2wasmSnapshot => _snapshotPathFor( |
| 104 | + 'dart2wasm_product.snapshot', |
| 105 | + ); |
| 106 | + |
| 107 | + String get dartMCPServerAotSnapshot => _snapshotPathFor( |
| 108 | + 'dart_mcp_server_aot.dart.snapshot', |
| 109 | + ); |
| 110 | + |
| 111 | + String get ddsAotSnapshot => _snapshotPathFor( |
| 112 | + 'dds_aot.dart.snapshot', |
| 113 | + ); |
| 114 | + |
| 115 | + String get frontendServerAotSnapshot => runFromBuildRoot |
| 116 | + ? _snapshotPathFor( |
| 117 | + 'frontend_server_aot_product.dart.snapshot', |
| 118 | + ) |
| 119 | + : _snapshotPathFor( |
| 120 | + 'frontend_server_aot.dart.snapshot', |
| 121 | + ); |
| 122 | + |
| 123 | + String get dtdAotSnapshot => _snapshotPathFor( |
| 124 | + 'dart_tooling_daemon_aot.dart.snapshot', |
| 125 | + ); |
| 126 | + |
| 127 | + String get devToolsBinaries => path.absolute( |
| 128 | + runFromBuildRoot |
| 129 | + ? sdkPath |
| 130 | + : path.absolute( |
| 131 | + sdkPath, |
| 132 | + 'bin', |
| 133 | + 'resources', |
| 134 | + ), |
| 135 | + 'devtools', |
| 136 | + ); |
| 137 | + |
| 138 | + String get wasmOpt => path.absolute( |
| 139 | + runFromBuildRoot |
| 140 | + ? sdkPath |
| 141 | + : path.absolute( |
| 142 | + sdkPath, |
| 143 | + 'bin', |
| 144 | + 'utils', |
| 145 | + ), |
| 146 | + Platform.isWindows ? 'wasm-opt.exe' : 'wasm-opt', |
| 147 | + ); |
| 148 | + |
| 149 | + // This file is only generated when building the SDK and isn't generated for |
| 150 | + // non-SDK build targets. |
| 151 | + String get librariesJson => path.absolute(sdkPath, 'lib', 'libraries.json'); |
| 152 | + |
| 153 | + // This file is only generated when building the SDK and isn't generated for |
| 154 | + // non-SDK build targets. |
| 155 | + String get wasmPlatformDill => |
| 156 | + path.absolute(sdkPath, 'lib', '_internal', 'dart2wasm_platform.dill'); |
| 157 | + |
| 158 | + String _snapshotPathFor(String snapshotName) => path.absolute( |
| 159 | + runFromBuildRoot |
| 160 | + ? sdkPath |
| 161 | + : path.absolute( |
| 162 | + sdkPath, |
| 163 | + 'bin', |
| 164 | + 'snapshots', |
| 165 | + ), |
| 166 | + snapshotName, |
| 167 | + ); |
| 168 | + |
| 169 | + static Sdk _createSingleton() { |
| 170 | + // Find SDK path. |
| 171 | + (String, bool)? trySDKPath(String executablePath) { |
| 172 | + // The common case, and how cli_util.dart computes the Dart SDK directory, |
| 173 | + // [path.dirname] called twice on Platform.executable. We confirm by |
| 174 | + // asserting that the directory `./bin/snapshots/` exists in this directory: |
| 175 | + var sdkPath = path.absolute(path.dirname(path.dirname(executablePath))); |
| 176 | + var snapshotsDir = path.join(sdkPath, 'bin', 'snapshots'); |
| 177 | + var runFromBuildRoot = false; |
| 178 | + final type = FileSystemEntity.typeSync(snapshotsDir); |
| 179 | + if (type != FileSystemEntityType.directory && |
| 180 | + type != FileSystemEntityType.link) { |
| 181 | + // This is the less common case where the user is in |
| 182 | + // the checked out Dart SDK, and is executing `dart` via: |
| 183 | + // ./out/ReleaseX64/dart ... or in google3. |
| 184 | + sdkPath = path.absolute(path.dirname(executablePath)); |
| 185 | + snapshotsDir = sdkPath; |
| 186 | + runFromBuildRoot = true; |
| 187 | + } |
| 188 | + |
| 189 | + // Try to locate the DartDev snapshot to determine if we're able to find |
| 190 | + // the SDK snapshots with this SDK path. This is meant to handle |
| 191 | + // non-standard SDK layouts that can involve symlinks (e.g., Brew |
| 192 | + // installations, google3 tests, etc). |
| 193 | + final snapshot = path.join(snapshotsDir, 'dartdev_aot.dart.snapshot'); |
| 194 | + if (FileSystemEntity.typeSync(snapshot) == |
| 195 | + FileSystemEntityType.notFound) { |
| 196 | + return null; |
| 197 | + } |
| 198 | + return (sdkPath, runFromBuildRoot); |
| 199 | + } |
| 200 | + |
| 201 | + final (sdkPath, runFromBuildRoot) = |
| 202 | + trySDKPath(Platform.resolvedExecutable) ?? |
| 203 | + trySDKPath(Platform.executable)!; |
| 204 | + |
| 205 | + // Defer to [Runtime] for the version. |
| 206 | + final version = Runtime.runtime.version; |
| 207 | + |
| 208 | + return Sdk._(sdkPath, version, getRevision(sdkPath), runFromBuildRoot); |
| 209 | + } |
| 210 | + |
| 211 | + /// Reads the contents of `revision` file at SDK root. |
| 212 | + /// |
| 213 | + /// Returns `null` if the file does not exist. |
| 214 | + static String? getRevision(String sdkPath) { |
| 215 | + String? revision; |
| 216 | + final revisionFile = File(path.join(sdkPath, 'revision')); |
| 217 | + if (revisionFile.existsSync()) { |
| 218 | + revision = revisionFile.readAsStringSync().trim(); |
| 219 | + } |
| 220 | + return revision; |
| 221 | + } |
| 222 | +} |
| 223 | + |
| 224 | +/// Information about the current runtime. |
| 225 | +class Runtime { |
| 226 | + static Runtime runtime = _createSingleton(); |
| 227 | + |
| 228 | + /// The SDK's semantic versioning version (x.y.z-a.b.channel). |
| 229 | + final String version; |
| 230 | + |
| 231 | + /// The SDK's release channel (`main`, `dev`, `beta`, `stable`). |
| 232 | + /// |
| 233 | + /// May be null if [Platform.version] does not have the expected format. |
| 234 | + final String? channel; |
| 235 | + |
| 236 | + Runtime._(this.version, this.channel); |
| 237 | + |
| 238 | + static Runtime _createSingleton() { |
| 239 | + final versionString = Platform.version; |
| 240 | + // Expected format: "version (channel) ..." |
| 241 | + var version = versionString; |
| 242 | + String? channel; |
| 243 | + final versionEnd = versionString.indexOf(' '); |
| 244 | + if (versionEnd > 0) { |
| 245 | + version = versionString.substring(0, versionEnd); |
| 246 | + var channelEnd = versionString.indexOf(' ', versionEnd + 1); |
| 247 | + if (channelEnd < 0) channelEnd = versionString.length; |
| 248 | + if (versionString.startsWith('(', versionEnd + 1) && |
| 249 | + versionString.startsWith(')', channelEnd - 1)) { |
| 250 | + channel = versionString.substring(versionEnd + 2, channelEnd - 1); |
| 251 | + } |
| 252 | + } |
| 253 | + return Runtime._(version, channel); |
| 254 | + } |
| 255 | +} |
| 256 | + |
| 257 | +const useAotSnapshotFlag = 'use-aot-snapshot'; |
0 commit comments