blob: ca2de5e73485285fa1ef90f740d52e9c33cfda08 [file] [log] [blame]
// 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 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 ddcSnapshot => _snapshotPathFor(
'dartdevc.dart.snapshot',
);
String get ddcAotSnapshot => _runFromBuildRoot
? _snapshotPathFor(
'dartdevc_aot_product.dart.snapshot',
)
: _snapshotPathFor(
'dartdevc_aot.dart.snapshot',
);
String get dart2jsSnapshot => _snapshotPathFor(
'dart2js.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 ddsSnapshot => _snapshotPathFor(
'dds.dart.snapshot',
);
String get ddsAotSnapshot => _snapshotPathFor(
'dds_aot.dart.snapshot',
);
String get frontendServerSnapshot => _snapshotPathFor(
'frontend_server.dart.snapshot',
);
String get frontendServerAotSnapshot => _runFromBuildRoot
? _snapshotPathFor(
'frontend_server_aot_product.dart.snapshot',
)
: _snapshotPathFor(
'frontend_server_aot.dart.snapshot',
);
String get dtdSnapshot => _snapshotPathFor(
'dart_tooling_daemon.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.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);
}
}