9d2f622f38
Fixes: https://github.com/dart-lang/sdk/issues/56865 Change-Id: I1a88ba40351ed1b42bb1a800b384a00eb7548225 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/418740 Commit-Queue: Derek Xu <derekx@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
222 lines
7.3 KiB
Dart
222 lines
7.3 KiB
Dart
// Copyright (c) 2022, 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:args/args.dart';
|
|
import 'package:frontend_server/resident_frontend_server_utils.dart'
|
|
show
|
|
computeCachedDillAndCompilerOptionsPaths,
|
|
ResidentCompilerInfo,
|
|
sendAndReceiveResponse;
|
|
import 'package:kernel/binary/tag.dart' show isValidSdkHash;
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:pub/pub.dart';
|
|
|
|
import 'core.dart';
|
|
import 'resident_frontend_constants.dart';
|
|
import 'resident_frontend_utils.dart';
|
|
import 'sdk.dart';
|
|
import 'unified_analytics.dart';
|
|
|
|
typedef CompileRequestGeneratorCallback = String Function({
|
|
required String executable,
|
|
required String outputDill,
|
|
required ArgResults args,
|
|
String? packages,
|
|
});
|
|
|
|
/// Uses the resident frontend compiler to compute a kernel file for
|
|
/// [executable]. Throws a [FrontendCompilerException] if the compilation
|
|
/// fails or if the source code contains compilation errors.
|
|
///
|
|
/// [executable] is expected to contain a path to the dart source file and
|
|
/// a package_config file.
|
|
///
|
|
/// [serverInfoFile] is the location that should be checked to find an existing
|
|
/// Resident Frontend Compiler. If one does not exist, a server is created and
|
|
/// its address and port information is written to this file location.
|
|
///
|
|
/// [args] is the [ArgResults] object that is created by the DartDev commands.
|
|
/// This is where the optional path override for the serverInfoFile is passed
|
|
/// in.
|
|
///
|
|
/// [compileRequestGenerator] is applied to produce a request for the Resident
|
|
/// Frontend Server.
|
|
Future<DartExecutableWithPackageConfig> generateKernel(
|
|
DartExecutableWithPackageConfig executable,
|
|
File serverInfoFile,
|
|
ArgResults args,
|
|
CompileRequestGeneratorCallback compileRequestGenerator, {
|
|
required bool quiet,
|
|
bool aot = false,
|
|
}) async {
|
|
// Locates the package_config.json and cached kernel file, makes sure the
|
|
// resident frontend server is up and running, and computes a kernel.
|
|
await ensureCompilationServerIsRunning(
|
|
serverInfoFile,
|
|
quiet: quiet,
|
|
);
|
|
|
|
final packageRoot = _packageRootFor(executable);
|
|
final packageConfig =
|
|
packageRoot != null ? p.join(packageRoot, packageConfigName) : null;
|
|
|
|
final canonicalizedExecutablePath = p.canonicalize(executable.executable);
|
|
final cachedDillPath =
|
|
computeCachedDillAndCompilerOptionsPaths(canonicalizedExecutablePath)
|
|
.cachedDillPath;
|
|
|
|
Map<String, dynamic> result;
|
|
try {
|
|
result = await sendAndReceiveResponse(
|
|
compileRequestGenerator(
|
|
executable: canonicalizedExecutablePath,
|
|
outputDill: cachedDillPath,
|
|
packages: packageConfig,
|
|
args: args,
|
|
),
|
|
serverInfoFile,
|
|
);
|
|
} on FileSystemException catch (e) {
|
|
throw FrontendCompilerException._(e.message, CompilationIssue.serverError);
|
|
}
|
|
if (!result[responseSuccessString]) {
|
|
if (result.containsKey(responseErrorString)) {
|
|
throw FrontendCompilerException._(
|
|
result[responseErrorString],
|
|
CompilationIssue.serverError,
|
|
);
|
|
} else {
|
|
throw FrontendCompilerException._(
|
|
(result[responseOutputString] as List<dynamic>).join('\n'),
|
|
CompilationIssue.compilationError,
|
|
);
|
|
}
|
|
}
|
|
return DartExecutableWithPackageConfig(
|
|
executable: cachedDillPath,
|
|
packageConfig: packageConfig,
|
|
);
|
|
}
|
|
|
|
/// Ensures that a Resident Frontend Compiler associated with [serverInfoFile]
|
|
/// will be running at the point when the future returned by this function
|
|
/// completes. Throws a [FrontendCompilerException] if starting the server
|
|
/// fails.
|
|
Future<void> ensureCompilationServerIsRunning(
|
|
File serverInfoFile, {
|
|
required bool quiet,
|
|
}) async {
|
|
if (serverInfoFile.existsSync()) {
|
|
final residentCompilerInfo = ResidentCompilerInfo.fromFile(serverInfoFile);
|
|
if (residentCompilerInfo.sdkHash != null &&
|
|
isValidSdkHash(residentCompilerInfo.sdkHash!)) {
|
|
// There is already a Resident Frontend Compiler associated with
|
|
// [serverInfoFile] that is running and compatible with the Dart SDK that
|
|
// the user is currently using.
|
|
return;
|
|
} else {
|
|
if (!quiet) {
|
|
log.stderr(
|
|
'The Dart SDK has been upgraded or downgraded since the Resident '
|
|
'Frontend Compiler was started, so the Resident Frontend Compiler will '
|
|
'now be restarted for compatibility reasons.',
|
|
);
|
|
}
|
|
await shutDownOrForgetResidentFrontendCompiler(serverInfoFile);
|
|
}
|
|
}
|
|
try {
|
|
Directory(p.dirname(serverInfoFile.path)).createSync(recursive: true);
|
|
final Process frontendServerProcess;
|
|
if (File(sdk.frontendServerAotSnapshot).existsSync()) {
|
|
frontendServerProcess = await Process.start(
|
|
sdk.dartAotRuntime,
|
|
[
|
|
sdk.frontendServerAotSnapshot,
|
|
'--resident-info-file-name=${serverInfoFile.absolute.path}'
|
|
],
|
|
workingDirectory: homeDir?.path,
|
|
mode: ProcessStartMode.detachedWithStdio,
|
|
);
|
|
} else {
|
|
// AOT snapshots cannot be generated on IA32, so we need this fallback
|
|
// branch until support for IA32 is dropped (https://dartbug.com/49969).
|
|
frontendServerProcess = await Process.start(
|
|
sdk.dart,
|
|
[
|
|
sdk.frontendServerSnapshot,
|
|
'--resident-info-file-name=${serverInfoFile.absolute.path}'
|
|
],
|
|
workingDirectory: homeDir?.path,
|
|
mode: ProcessStartMode.detachedWithStdio,
|
|
);
|
|
}
|
|
|
|
final serverOutput =
|
|
String.fromCharCodes(await frontendServerProcess.stdout.first).trim();
|
|
if (serverOutput.startsWith('Error')) {
|
|
throw StateError(serverOutput);
|
|
}
|
|
if (!quiet) {
|
|
// Prints the server's address and port information
|
|
log.stdout(serverOutput);
|
|
log.stdout('');
|
|
log.stdout(
|
|
'Run dart compilation-server shutdown to terminate the process.',
|
|
);
|
|
}
|
|
} catch (e) {
|
|
throw FrontendCompilerException._(
|
|
e.toString(),
|
|
CompilationIssue.serverCreationError,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Returns the path to the root of the [executable]'s package, or null
|
|
/// if it is a standalone dart file.
|
|
String? _packageRootFor(DartExecutableWithPackageConfig executable) {
|
|
Directory currentDirectory =
|
|
Directory(p.dirname(p.canonicalize(executable.executable)));
|
|
|
|
while (currentDirectory.parent.path != currentDirectory.path) {
|
|
if (File(p.join(currentDirectory.path, packageConfigName)).existsSync()) {
|
|
return currentDirectory.path;
|
|
}
|
|
currentDirectory = currentDirectory.parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Indicates the type of issue encountered with the
|
|
/// Resident Frontend Compiler
|
|
enum CompilationIssue {
|
|
/// Communication with the Resident Frontend Compiler failed.
|
|
serverError,
|
|
|
|
/// The Resident Frontend Compiler failed to launch
|
|
serverCreationError,
|
|
|
|
/// There were compilation errors in the Dart source code.
|
|
compilationError,
|
|
|
|
/// Resident mode is only supported for sources within Dart packages
|
|
standaloneProgramError,
|
|
}
|
|
|
|
/// Indicates an error with the Resident Frontend Compiler.
|
|
class FrontendCompilerException implements Exception {
|
|
final String message;
|
|
final CompilationIssue issue;
|
|
|
|
FrontendCompilerException._(this.message, this.issue);
|
|
|
|
@override
|
|
String toString() {
|
|
return 'FrontendCompilerException: $message';
|
|
}
|
|
}
|