| // 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:async'; | 
 | import 'dart:io' show File; | 
 |  | 
 | import 'package:args/args.dart'; | 
 | import 'package:dartdev/src/generate_kernel.dart'; | 
 | import 'package:frontend_server/resident_frontend_server_utils.dart' | 
 |     show ResidentCompilerInfo; | 
 |  | 
 | import '../core.dart'; | 
 | import '../resident_frontend_constants.dart'; | 
 | import '../resident_frontend_utils.dart'; | 
 |  | 
 | class CompilationServerCommand extends DartdevCommand { | 
 |   static const commandName = 'compilation-server'; | 
 |  | 
 |   static const commandDescription = 'Control resident frontend compilers.'; | 
 |  | 
 |   static const legacyResidentServerInfoFileFlag = 'resident-server-info-file'; | 
 |   static const residentCompilerInfoFileFlag = residentCompilerInfoFileOption; | 
 |   static const residentCompilerInfoFileFlagDescription = | 
 |       'The path to an info file that the Dart CLI will use to communicate with ' | 
 |       'a resident frontend compiler. Each unique info file is associated with ' | 
 |       'a unique resident frontend compiler. If this flag is ommitted, the ' | 
 |       'default info file will be used.'; | 
 |  | 
 |   static const inaccessibleDefaultResidentCompilerInfoFileMessage = | 
 |       'The default resident frontend compiler info file could not be accessed. ' | 
 |       'Please explicitly provide the path to a resident compiler info file ' | 
 |       'using the --resident-compiler-info-file option.'; | 
 |  | 
 |   CompilationServerCommand({bool verbose = false}) | 
 |       : super( | 
 |           commandName, | 
 |           commandDescription, | 
 |           false, | 
 |           hidden: !verbose, | 
 |         ) { | 
 |     addSubcommand(CompilationServerStartCommand()); | 
 |     addSubcommand(CompilationServerShutdownCommand()); | 
 |   } | 
 |  | 
 |   @override | 
 |   CommandCategory get commandCategory => CommandCategory.tools; | 
 | } | 
 |  | 
 | class CompilationServerStartCommand extends DartdevCommand { | 
 |   static const commandName = 'start'; | 
 |  | 
 |   static const commandDescription = 'Start a resident frontend compiler.'; | 
 |  | 
 |   CompilationServerStartCommand({bool verbose = false}) | 
 |       : super( | 
 |           commandName, | 
 |           commandDescription, | 
 |           false, | 
 |           hidden: !verbose, | 
 |         ) { | 
 |     argParser | 
 |       ..addOption( | 
 |         CompilationServerCommand.residentCompilerInfoFileFlag, | 
 |         help: CompilationServerCommand.residentCompilerInfoFileFlagDescription, | 
 |       ) | 
 |       ..addOption( | 
 |         CompilationServerCommand.legacyResidentServerInfoFileFlag, | 
 |         // This option is only available for backwards compatibility, and should | 
 |         // never be shown in the help message. | 
 |         hide: true, | 
 |       ); | 
 |   } | 
 |  | 
 |   @override | 
 |   FutureOr<int> run() async { | 
 |     final args = argResults!; | 
 |  | 
 |     final File? residentCompilerInfoFile = | 
 |         getResidentCompilerInfoFileConsideringArgs(args); | 
 |     if (residentCompilerInfoFile == null) { | 
 |       log.stderr( | 
 |         CompilationServerCommand | 
 |             .inaccessibleDefaultResidentCompilerInfoFileMessage, | 
 |       ); | 
 |       return DartdevCommand.errorExitCode; | 
 |     } | 
 |  | 
 |     try { | 
 |       await ensureCompilationServerIsRunning( | 
 |         residentCompilerInfoFile, | 
 |         quiet: false, | 
 |       ); | 
 |     } catch (e) { | 
 |       // We already print the error in `ensureCompilationServerIsRunning` when we | 
 |       // throw a state error. | 
 |       if (e is! StateError) { | 
 |         print(e.toString()); | 
 |       } | 
 |       return 64; | 
 |     } | 
 |     return 0; | 
 |   } | 
 | } | 
 |  | 
 | class CompilationServerShutdownCommand extends DartdevCommand { | 
 |   static const commandName = 'shutdown'; | 
 |  | 
 |   static const commandDescription = ''' | 
 | Shut down a resident frontend compiler. | 
 |  | 
 | Note that this command name and usage could change as we evolve the resident frontend compiler behavior.'''; | 
 |  | 
 |   CompilationServerShutdownCommand({bool verbose = false}) | 
 |       : super(commandName, commandDescription, false, hidden: !verbose) { | 
 |     argParser | 
 |       ..addOption( | 
 |         CompilationServerCommand.residentCompilerInfoFileFlag, | 
 |         help: CompilationServerCommand.residentCompilerInfoFileFlagDescription, | 
 |       ) | 
 |       ..addOption( | 
 |         CompilationServerCommand.legacyResidentServerInfoFileFlag, | 
 |         // This option is only available for backwards compatibility, and should | 
 |         // never be shown in the help message. | 
 |         hide: true, | 
 |       ); | 
 |   } | 
 |  | 
 |   // This argument parser is here solely to ensure that VM specific flags are | 
 |   // provided before any command and to provide a more consistent help message | 
 |   // with the rest of the tool. | 
 |   @override | 
 |   ArgParser createArgParser() { | 
 |     return ArgParser(); | 
 |   } | 
 |  | 
 |   @override | 
 |   FutureOr<int> run() async { | 
 |     final args = argResults!; | 
 |  | 
 |     final File? residentCompilerInfoFile = | 
 |         getResidentCompilerInfoFileConsideringArgs(args); | 
 |     if (residentCompilerInfoFile == null) { | 
 |       log.stderr( | 
 |         CompilationServerCommand | 
 |             .inaccessibleDefaultResidentCompilerInfoFileMessage, | 
 |       ); | 
 |       return DartdevCommand.errorExitCode; | 
 |     } | 
 |  | 
 |     if (!residentCompilerInfoFile.existsSync()) { | 
 |       log.stdout('No resident frontend compiler instance running.'); | 
 |       return 0; | 
 |     } | 
 |  | 
 |     final residentCompilerInfo = | 
 |         ResidentCompilerInfo.fromFile(residentCompilerInfoFile); | 
 |     final address = residentCompilerInfo.address; | 
 |     final port = residentCompilerInfo.port; | 
 |     // There is nothing actionable the user can do in response to an error | 
 |     // occurring when shutting down. So, we call | 
 |     // [shutDownOrForgetResidentFrontendCompiler] here to ensure that when such | 
 |     // an error occurs, we don't report it to the user and instead just forget | 
 |     // about the compiler that couldn't be shut down. | 
 |     await shutDownOrForgetResidentFrontendCompiler(residentCompilerInfoFile); | 
 |     log.stdout( | 
 |       'The Resident Frontend Compiler instance at ${address.host}:$port was ' | 
 |       'successfully shutdown.', | 
 |     ); | 
 |     return 0; | 
 |   } | 
 | } |