|  | // Copyright (c) 2014, 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'; | 
|  | import 'dart:isolate'; | 
|  | import 'dart:math'; | 
|  |  | 
|  | import 'package:analysis_server/protocol/protocol_constants.dart' | 
|  | show PROTOCOL_VERSION; | 
|  | import 'package:analysis_server/src/analysis_server.dart'; | 
|  | import 'package:analysis_server/src/lsp/lsp_socket_server.dart'; | 
|  | import 'package:analysis_server/src/server/crash_reporting.dart'; | 
|  | import 'package:analysis_server/src/server/crash_reporting_attachments.dart'; | 
|  | import 'package:analysis_server/src/server/detachable_filesystem_manager.dart'; | 
|  | import 'package:analysis_server/src/server/dev_server.dart'; | 
|  | import 'package:analysis_server/src/server/diagnostic_server.dart'; | 
|  | import 'package:analysis_server/src/server/error_notifier.dart'; | 
|  | import 'package:analysis_server/src/server/features.dart'; | 
|  | import 'package:analysis_server/src/server/http_server.dart'; | 
|  | import 'package:analysis_server/src/server/isolate_analysis_server.dart'; | 
|  | import 'package:analysis_server/src/server/lsp_stdio_server.dart'; | 
|  | import 'package:analysis_server/src/server/sdk_configuration.dart'; | 
|  | import 'package:analysis_server/src/server/stdio_server.dart'; | 
|  | import 'package:analysis_server/src/socket_server.dart'; | 
|  | import 'package:analysis_server/src/utilities/request_statistics.dart'; | 
|  | import 'package:analysis_server/starter.dart'; | 
|  | import 'package:analyzer/file_system/physical_file_system.dart'; | 
|  | import 'package:analyzer/instrumentation/file_instrumentation.dart'; | 
|  | import 'package:analyzer/instrumentation/instrumentation.dart'; | 
|  | import 'package:analyzer/src/dart/sdk/sdk.dart'; | 
|  | import 'package:analyzer/src/generated/engine.dart'; | 
|  | import 'package:analyzer/src/generated/sdk.dart'; | 
|  | import 'package:args/args.dart'; | 
|  | import 'package:cli_util/cli_util.dart'; | 
|  | import 'package:linter/src/rules.dart' as linter; | 
|  | import 'package:telemetry/crash_reporting.dart'; | 
|  | import 'package:telemetry/telemetry.dart' as telemetry; | 
|  |  | 
|  | /// The [Driver] class represents a single running instance of the analysis | 
|  | /// server application.  It is responsible for parsing command line options | 
|  | /// and starting the HTTP and/or stdio servers. | 
|  | class Driver implements ServerStarter { | 
|  | /// The name of the application that is used to start a server. | 
|  | static const BINARY_NAME = 'analysis_server'; | 
|  |  | 
|  | /// The name of the option used to set the identifier for the client. | 
|  | static const String CLIENT_ID = 'client-id'; | 
|  |  | 
|  | /// The name of the option used to set the version for the client. | 
|  | static const String CLIENT_VERSION = 'client-version'; | 
|  |  | 
|  | /// The name of the option used to disable exception handling. | 
|  | static const String DISABLE_SERVER_EXCEPTION_HANDLING = | 
|  | 'disable-server-exception-handling'; | 
|  |  | 
|  | /// The name of the option to disable the completion feature. | 
|  | static const String DISABLE_SERVER_FEATURE_COMPLETION = | 
|  | 'disable-server-feature-completion'; | 
|  |  | 
|  | /// The name of the option to disable the search feature. | 
|  | static const String DISABLE_SERVER_FEATURE_SEARCH = | 
|  | 'disable-server-feature-search'; | 
|  |  | 
|  | /// The name of the option used to print usage information. | 
|  | static const String HELP_OPTION = 'help'; | 
|  |  | 
|  | /// The name of the flag used to configure reporting analytics. | 
|  | static const String ANALYTICS_FLAG = 'analytics'; | 
|  |  | 
|  | /// Suppress analytics for this session. | 
|  | static const String SUPPRESS_ANALYTICS_FLAG = 'suppress-analytics'; | 
|  |  | 
|  | /// The name of the option used to cause instrumentation to also be written to | 
|  | /// a local file. | 
|  | static const String PROTOCOL_TRAFFIC_LOG = 'protocol-traffic-log'; | 
|  | static const String PROTOCOL_TRAFFIC_LOG_ALIAS = 'instrumentation-log-file'; | 
|  |  | 
|  | /// The name of the option used to specify if [print] should print to the | 
|  | /// console instead of being intercepted. | 
|  | static const String INTERNAL_PRINT_TO_CONSOLE = 'internal-print-to-console'; | 
|  |  | 
|  | /// The name of the option used to describe the new analysis driver logger. | 
|  | static const String ANALYSIS_DRIVER_LOG = 'analysis-driver-log'; | 
|  | static const String ANALYSIS_DRIVER_LOG_ALIAS = 'new-analysis-driver-log'; | 
|  |  | 
|  | /// The option for specifying the http diagnostic port. | 
|  | /// If specified, users can review server status and performance information | 
|  | /// by opening a web browser on http://localhost:<port> | 
|  | static const String DIAGNOSTIC_PORT = 'diagnostic-port'; | 
|  | static const String DIAGNOSTIC_PORT_ALIAS = 'port'; | 
|  |  | 
|  | /// The path to the SDK. | 
|  | static const String DART_SDK = 'dart-sdk'; | 
|  | static const String DART_SDK_ALIAS = 'sdk'; | 
|  |  | 
|  | /// The path to the data cache. | 
|  | static const String CACHE_FOLDER = 'cache'; | 
|  |  | 
|  | /// The name of the flag specifying the server protocol to use. | 
|  | static const String SERVER_PROTOCOL = 'protocol'; | 
|  | static const String PROTOCOL_ANALYZER = 'analyzer'; | 
|  | static const String PROTOCOL_LSP = 'lsp'; | 
|  |  | 
|  | /// The name of the flag to use the Language Server Protocol (LSP). | 
|  | static const String USE_LSP = 'lsp'; | 
|  |  | 
|  | /// A directory to analyze in order to train an analysis server snapshot. | 
|  | static const String TRAIN_USING = 'train-using'; | 
|  |  | 
|  | /// The builder for attachments that should be included into crash reports. | 
|  | CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder = | 
|  | CrashReportingAttachmentsBuilder.empty; | 
|  |  | 
|  | /// An optional manager to handle file systems which may not always be | 
|  | /// available. | 
|  | DetachableFileSystemManager detachableFileSystemManager; | 
|  |  | 
|  | /// The instrumentation service that is to be used by the analysis server. | 
|  | InstrumentationService instrumentationService; | 
|  |  | 
|  | HttpAnalysisServer httpServer; | 
|  |  | 
|  | Driver(); | 
|  |  | 
|  | /// Use the given command-line [arguments] to start this server. | 
|  | /// | 
|  | /// If [sendPort] is not null, assumes this is launched in an isolate and will | 
|  | /// connect to the original isolate via an [IsolateChannel]. | 
|  | @override | 
|  | void start( | 
|  | List<String> arguments, { | 
|  | SendPort sendPort, | 
|  | bool defaultToLsp = false, | 
|  | }) { | 
|  | var parser = createArgParser(defaultToLsp: defaultToLsp); | 
|  | var results = parser.parse(arguments); | 
|  |  | 
|  | var analysisServerOptions = AnalysisServerOptions(); | 
|  | analysisServerOptions.newAnalysisDriverLog = | 
|  | results[ANALYSIS_DRIVER_LOG] ?? results[ANALYSIS_DRIVER_LOG_ALIAS]; | 
|  | analysisServerOptions.clientId = results[CLIENT_ID]; | 
|  | if (results.wasParsed(USE_LSP)) { | 
|  | analysisServerOptions.useLanguageServerProtocol = results[USE_LSP]; | 
|  | } else { | 
|  | analysisServerOptions.useLanguageServerProtocol = | 
|  | results[SERVER_PROTOCOL] == PROTOCOL_LSP; | 
|  | } | 
|  | // For clients that don't supply their own identifier, use a default based | 
|  | // on whether the server will run in LSP mode or not. | 
|  | analysisServerOptions.clientId ??= | 
|  | analysisServerOptions.useLanguageServerProtocol | 
|  | ? 'unknown.client.lsp' | 
|  | : 'unknown.client.classic'; | 
|  |  | 
|  | analysisServerOptions.clientVersion = results[CLIENT_VERSION]; | 
|  | analysisServerOptions.cacheFolder = results[CACHE_FOLDER]; | 
|  |  | 
|  | // Read in any per-SDK overrides specified in <sdk>/config/settings.json. | 
|  | var sdkConfig = SdkConfiguration.readFromSdk(); | 
|  | analysisServerOptions.configurationOverrides = sdkConfig; | 
|  |  | 
|  | // Analytics | 
|  | bool disableAnalyticsForSession = results[SUPPRESS_ANALYTICS_FLAG]; | 
|  | if (results.wasParsed(TRAIN_USING)) { | 
|  | disableAnalyticsForSession = true; | 
|  | } | 
|  |  | 
|  | // Use sdkConfig to optionally override analytics settings. | 
|  | final analyticsId = sdkConfig.analyticsId ?? 'UA-26406144-29'; | 
|  | final forceAnalyticsEnabled = sdkConfig.analyticsForceEnabled == true; | 
|  | var analytics = telemetry.createAnalyticsInstance( | 
|  | analyticsId, | 
|  | 'analysis-server', | 
|  | disableForSession: disableAnalyticsForSession, | 
|  | forceEnabled: forceAnalyticsEnabled, | 
|  | ); | 
|  | analysisServerOptions.analytics = analytics; | 
|  |  | 
|  | // Record the client name as the application installer ID. | 
|  | analytics.setSessionValue('aiid', analysisServerOptions.clientId); | 
|  | if (analysisServerOptions.clientVersion != null) { | 
|  | analytics.setSessionValue('cd1', analysisServerOptions.clientVersion); | 
|  | } | 
|  |  | 
|  | var shouldSendCallback = () { | 
|  | // Check sdkConfig to optionally force reporting on. | 
|  | if (sdkConfig.crashReportingForceEnabled == true) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // TODO(devoncarew): Replace with a real enablement check. | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | // Crash reporting | 
|  |  | 
|  | // Use sdkConfig to optionally override analytics settings. | 
|  | final crashProductId = sdkConfig.crashReportingId ?? 'Dart_analysis_server'; | 
|  | final crashReportSender = | 
|  | CrashReportSender.prod(crashProductId, shouldSendCallback); | 
|  |  | 
|  | if (telemetry.SHOW_ANALYTICS_UI) { | 
|  | if (results.wasParsed(ANALYTICS_FLAG)) { | 
|  | analytics.enabled = results[ANALYTICS_FLAG]; | 
|  | print(telemetry.createAnalyticsStatusMessage(analytics.enabled)); | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | { | 
|  | bool disableCompletion = results[DISABLE_SERVER_FEATURE_COMPLETION]; | 
|  | bool disableSearch = results[DISABLE_SERVER_FEATURE_SEARCH]; | 
|  | if (disableCompletion || disableSearch) { | 
|  | analysisServerOptions.featureSet = FeatureSet( | 
|  | completion: !disableCompletion, | 
|  | search: !disableSearch, | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (results[HELP_OPTION]) { | 
|  | _printUsage(parser, analytics, fromHelp: true); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | final defaultSdkPath = _getSdkPath(results); | 
|  | final dartSdkManager = DartSdkManager(defaultSdkPath); | 
|  |  | 
|  | // TODO(brianwilkerson) It would be nice to avoid creating an SDK that | 
|  | // cannot be re-used, but the SDK is needed to create a package map provider | 
|  | // in the case where we need to run `pub` in order to get the package map. | 
|  | var defaultSdk = _createDefaultSdk(defaultSdkPath); | 
|  | // | 
|  | // Initialize the instrumentation service. | 
|  | // | 
|  | String logFilePath = | 
|  | results[PROTOCOL_TRAFFIC_LOG] ?? results[PROTOCOL_TRAFFIC_LOG_ALIAS]; | 
|  | var allInstrumentationServices = instrumentationService == null | 
|  | ? <InstrumentationService>[] | 
|  | : [instrumentationService]; | 
|  | if (logFilePath != null) { | 
|  | _rollLogFiles(logFilePath, 5); | 
|  | allInstrumentationServices.add( | 
|  | InstrumentationLogAdapter(FileInstrumentationLogger(logFilePath))); | 
|  | } | 
|  |  | 
|  | var errorNotifier = ErrorNotifier(); | 
|  | allInstrumentationServices | 
|  | .add(CrashReportingInstrumentation(crashReportSender)); | 
|  | instrumentationService = | 
|  | MulticastInstrumentationService(allInstrumentationServices); | 
|  |  | 
|  | instrumentationService.logVersion( | 
|  | results[TRAIN_USING] != null | 
|  | ? 'training-0' | 
|  | : _readUuid(instrumentationService), | 
|  | analysisServerOptions.clientId, | 
|  | analysisServerOptions.clientVersion, | 
|  | PROTOCOL_VERSION, | 
|  | defaultSdk.languageVersion.toString(), | 
|  | ); | 
|  | AnalysisEngine.instance.instrumentationService = instrumentationService; | 
|  |  | 
|  | int diagnosticServerPort; | 
|  | final String portValue = | 
|  | results[DIAGNOSTIC_PORT] ?? results[DIAGNOSTIC_PORT_ALIAS]; | 
|  | if (portValue != null) { | 
|  | try { | 
|  | diagnosticServerPort = int.parse(portValue); | 
|  | } on FormatException { | 
|  | print('Invalid port number: $portValue'); | 
|  | print(''); | 
|  | _printUsage(parser, analytics); | 
|  | exitCode = 1; | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (analysisServerOptions.useLanguageServerProtocol) { | 
|  | if (sendPort != null) { | 
|  | throw UnimplementedError( | 
|  | 'Isolate usage not implemented for LspAnalysisServer'); | 
|  | } | 
|  | startLspServer(results, analysisServerOptions, dartSdkManager, | 
|  | instrumentationService, diagnosticServerPort, errorNotifier); | 
|  | } else { | 
|  | startAnalysisServer( | 
|  | results, | 
|  | analysisServerOptions, | 
|  | parser, | 
|  | dartSdkManager, | 
|  | crashReportingAttachmentsBuilder, | 
|  | instrumentationService, | 
|  | RequestStatisticsHelper(), | 
|  | analytics, | 
|  | diagnosticServerPort, | 
|  | errorNotifier, | 
|  | sendPort); | 
|  | } | 
|  | } | 
|  |  | 
|  | void startAnalysisServer( | 
|  | ArgResults results, | 
|  | AnalysisServerOptions analysisServerOptions, | 
|  | ArgParser parser, | 
|  | DartSdkManager dartSdkManager, | 
|  | CrashReportingAttachmentsBuilder crashReportingAttachmentsBuilder, | 
|  | InstrumentationService instrumentationService, | 
|  | RequestStatisticsHelper requestStatistics, | 
|  | telemetry.Analytics analytics, | 
|  | int diagnosticServerPort, | 
|  | ErrorNotifier errorNotifier, | 
|  | SendPort sendPort, | 
|  | ) { | 
|  | var capture = results[DISABLE_SERVER_EXCEPTION_HANDLING] | 
|  | ? (_, Function f, {Function(String) print}) => f() | 
|  | : _captureExceptions; | 
|  | String trainDirectory = results[TRAIN_USING]; | 
|  | if (trainDirectory != null) { | 
|  | if (!FileSystemEntity.isDirectorySync(trainDirectory)) { | 
|  | print("Training directory '$trainDirectory' not found.\n"); | 
|  | exitCode = 1; | 
|  | return null; | 
|  | } | 
|  | } | 
|  | final serve_http = diagnosticServerPort != null; | 
|  |  | 
|  | // | 
|  | // Register lint rules. | 
|  | // | 
|  | linter.registerLintRules(); | 
|  |  | 
|  | var diagnosticServer = _DiagnosticServerImpl(); | 
|  |  | 
|  | // Ping analytics with our initial call. | 
|  | analytics.sendScreenView('home'); | 
|  |  | 
|  | // | 
|  | // Create the sockets and start listening for requests. | 
|  | // | 
|  | final socketServer = SocketServer( | 
|  | analysisServerOptions, | 
|  | dartSdkManager, | 
|  | crashReportingAttachmentsBuilder, | 
|  | instrumentationService, | 
|  | requestStatistics, | 
|  | diagnosticServer, | 
|  | detachableFileSystemManager); | 
|  | httpServer = HttpAnalysisServer(socketServer); | 
|  |  | 
|  | errorNotifier.server = socketServer.analysisServer; | 
|  |  | 
|  | diagnosticServer.httpServer = httpServer; | 
|  | if (serve_http) { | 
|  | diagnosticServer.startOnPort(diagnosticServerPort); | 
|  | } | 
|  |  | 
|  | if (trainDirectory != null) { | 
|  | if (sendPort != null) { | 
|  | throw UnimplementedError( | 
|  | 'isolate usage not supported for DevAnalysisServer'); | 
|  | } | 
|  | var tempDriverDir = | 
|  | Directory.systemTemp.createTempSync('analysis_server_'); | 
|  | analysisServerOptions.cacheFolder = tempDriverDir.path; | 
|  |  | 
|  | var devServer = DevAnalysisServer(socketServer); | 
|  | devServer.initServer(); | 
|  |  | 
|  | () async { | 
|  | // We first analyze code with an empty driver cache. | 
|  | print('Analyzing with an empty driver cache:'); | 
|  | var exitCode = await devServer.processDirectories([trainDirectory]); | 
|  | if (exitCode != 0) exit(exitCode); | 
|  |  | 
|  | print(''); | 
|  |  | 
|  | // Then again with a populated cache. | 
|  | print('Analyzing with a populated driver cache:'); | 
|  | exitCode = await devServer.processDirectories([trainDirectory]); | 
|  | if (exitCode != 0) exit(exitCode); | 
|  |  | 
|  | if (serve_http) { | 
|  | httpServer.close(); | 
|  | } | 
|  | await instrumentationService.shutdown(); | 
|  |  | 
|  | socketServer.analysisServer.shutdown(); | 
|  |  | 
|  | try { | 
|  | tempDriverDir.deleteSync(recursive: true); | 
|  | } catch (_) { | 
|  | // ignore any exception | 
|  | } | 
|  |  | 
|  | exit(exitCode); | 
|  | }(); | 
|  | } else { | 
|  | capture(instrumentationService, () { | 
|  | Future serveResult; | 
|  | if (sendPort == null) { | 
|  | var stdioServer = StdioAnalysisServer(socketServer); | 
|  | serveResult = stdioServer.serveStdio(); | 
|  | } else { | 
|  | var isolateAnalysisServer = IsolateAnalysisServer(socketServer); | 
|  | serveResult = isolateAnalysisServer.serveIsolate(sendPort); | 
|  | } | 
|  | serveResult.then((_) async { | 
|  | if (serve_http) { | 
|  | httpServer.close(); | 
|  | } | 
|  | await instrumentationService.shutdown(); | 
|  | socketServer.analysisServer.shutdown(); | 
|  | if (sendPort == null) exit(0); | 
|  | }); | 
|  | }, | 
|  | print: results[INTERNAL_PRINT_TO_CONSOLE] | 
|  | ? null | 
|  | : httpServer.recordPrint); | 
|  | } | 
|  | } | 
|  |  | 
|  | void startLspServer( | 
|  | ArgResults args, | 
|  | AnalysisServerOptions analysisServerOptions, | 
|  | DartSdkManager dartSdkManager, | 
|  | InstrumentationService instrumentationService, | 
|  | int diagnosticServerPort, | 
|  | ErrorNotifier errorNotifier, | 
|  | ) { | 
|  | var capture = args[DISABLE_SERVER_EXCEPTION_HANDLING] | 
|  | ? (_, Function f, {Function(String) print}) => f() | 
|  | : _captureExceptions; | 
|  | final serve_http = diagnosticServerPort != null; | 
|  |  | 
|  | linter.registerLintRules(); | 
|  |  | 
|  | var diagnosticServer = _DiagnosticServerImpl(); | 
|  |  | 
|  | final socketServer = LspSocketServer( | 
|  | analysisServerOptions, | 
|  | diagnosticServer, | 
|  | dartSdkManager, | 
|  | instrumentationService, | 
|  | ); | 
|  | errorNotifier.server = socketServer.analysisServer; | 
|  |  | 
|  | httpServer = HttpAnalysisServer(socketServer); | 
|  |  | 
|  | diagnosticServer.httpServer = httpServer; | 
|  | if (serve_http) { | 
|  | diagnosticServer.startOnPort(diagnosticServerPort); | 
|  | } | 
|  |  | 
|  | capture(instrumentationService, () { | 
|  | var stdioServer = LspStdioAnalysisServer(socketServer); | 
|  | stdioServer.serveStdio().then((_) async { | 
|  | // Only shutdown the server and exit if the server is not already | 
|  | // handling the shutdown. | 
|  | if (!socketServer.analysisServer.willExit) { | 
|  | socketServer.analysisServer.shutdown(); | 
|  | exit(0); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /// Execute the given [callback] within a zone that will capture any unhandled | 
|  | /// exceptions and both report them to the client and send them to the given | 
|  | /// instrumentation [service]. If a [print] function is provided, then also | 
|  | /// capture any data printed by the callback and redirect it to the function. | 
|  | void _captureExceptions( | 
|  | InstrumentationService service, void Function() callback, | 
|  | {void Function(String line) print}) { | 
|  | void errorFunction(Zone self, ZoneDelegate parent, Zone zone, | 
|  | dynamic exception, StackTrace stackTrace) { | 
|  | service.logException(exception, stackTrace); | 
|  | throw exception; | 
|  | } | 
|  |  | 
|  | var printFunction = print == null | 
|  | ? null | 
|  | : (Zone self, ZoneDelegate parent, Zone zone, String line) { | 
|  | // Note: we don't pass the line on to stdout, because that is | 
|  | // reserved for communication to the client. | 
|  | print(line); | 
|  | }; | 
|  | var zoneSpecification = ZoneSpecification( | 
|  | handleUncaughtError: errorFunction, print: printFunction); | 
|  | return runZoned(callback, zoneSpecification: zoneSpecification); | 
|  | } | 
|  |  | 
|  | DartSdk _createDefaultSdk(String defaultSdkPath) { | 
|  | var resourceProvider = PhysicalResourceProvider.INSTANCE; | 
|  | return FolderBasedDartSdk( | 
|  | resourceProvider, | 
|  | resourceProvider.getFolder(defaultSdkPath), | 
|  | ); | 
|  | } | 
|  |  | 
|  | /// Constructs a uuid combining the current date and a random integer. | 
|  | String _generateUuidString() { | 
|  | var millisecondsSinceEpoch = DateTime.now().millisecondsSinceEpoch; | 
|  | var random = Random().nextInt(0x3fffffff); | 
|  | return '$millisecondsSinceEpoch$random'; | 
|  | } | 
|  |  | 
|  | String _getSdkPath(ArgResults args) { | 
|  | if (args[DART_SDK] != null) { | 
|  | return args[DART_SDK]; | 
|  | } else if (args[DART_SDK_ALIAS] != null) { | 
|  | return args[DART_SDK_ALIAS]; | 
|  | } else { | 
|  | return getSdkPath(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Print information about how to use the server. | 
|  | void _printUsage( | 
|  | ArgParser parser, | 
|  | telemetry.Analytics analytics, { | 
|  | bool fromHelp = false, | 
|  | }) { | 
|  | print('Usage: $BINARY_NAME [flags]'); | 
|  | print(''); | 
|  | print('Supported flags are:'); | 
|  | print(parser.usage); | 
|  |  | 
|  | if (telemetry.SHOW_ANALYTICS_UI) { | 
|  | // Print analytics status and information. | 
|  | if (fromHelp) { | 
|  | print(''); | 
|  | print(telemetry.analyticsNotice); | 
|  | } | 
|  | print(''); | 
|  | print(telemetry.createAnalyticsStatusMessage(analytics.enabled, | 
|  | command: ANALYTICS_FLAG)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Read the UUID from disk, generating and storing a new one if necessary. | 
|  | String _readUuid(InstrumentationService service) { | 
|  | final instrumentationLocation = | 
|  | PhysicalResourceProvider.INSTANCE.getStateLocation('.instrumentation'); | 
|  | if (instrumentationLocation == null) { | 
|  | return _generateUuidString(); | 
|  | } | 
|  | var uuidFile = File(instrumentationLocation.getChild('uuid.txt').path); | 
|  | try { | 
|  | if (uuidFile.existsSync()) { | 
|  | var uuid = uuidFile.readAsStringSync(); | 
|  | if (uuid != null && uuid.length > 5) { | 
|  | return uuid; | 
|  | } | 
|  | } | 
|  | } catch (exception, stackTrace) { | 
|  | service.logException(exception, stackTrace); | 
|  | } | 
|  | var uuid = _generateUuidString(); | 
|  | try { | 
|  | uuidFile.parent.createSync(recursive: true); | 
|  | uuidFile.writeAsStringSync(uuid); | 
|  | } catch (exception, stackTrace) { | 
|  | service.logException(exception, stackTrace); | 
|  | // Slightly alter the uuid to indicate it was not persisted | 
|  | uuid = 'temp-$uuid'; | 
|  | } | 
|  | return uuid; | 
|  | } | 
|  |  | 
|  | /// Create and return the parser used to parse the command-line arguments. | 
|  | static ArgParser createArgParser({ | 
|  | int usageLineLength, | 
|  | bool includeHelpFlag = true, | 
|  | bool defaultToLsp = false, | 
|  | }) { | 
|  | var parser = ArgParser(usageLineLength: usageLineLength); | 
|  | if (includeHelpFlag) { | 
|  | parser.addFlag(HELP_OPTION, | 
|  | abbr: 'h', negatable: false, help: 'Print this usage information.'); | 
|  | } | 
|  | parser.addOption(CLIENT_ID, | 
|  | valueHelp: 'name', | 
|  | help: 'An identifier for the analysis server client.'); | 
|  | parser.addOption(CLIENT_VERSION, | 
|  | valueHelp: 'version', | 
|  | help: 'The version of the analysis server client.'); | 
|  | parser.addOption(DART_SDK, | 
|  | valueHelp: 'path', help: 'Override the Dart SDK to use for analysis.'); | 
|  | parser.addOption(DART_SDK_ALIAS, hide: true); | 
|  | parser.addOption(CACHE_FOLDER, | 
|  | valueHelp: 'path', | 
|  | help: 'Override the location of the analysis server\'s cache.'); | 
|  |  | 
|  | parser.addOption( | 
|  | SERVER_PROTOCOL, | 
|  | defaultsTo: defaultToLsp ? PROTOCOL_LSP : PROTOCOL_ANALYZER, | 
|  | valueHelp: 'protocol', | 
|  | allowed: [PROTOCOL_LSP, PROTOCOL_ANALYZER], | 
|  | allowedHelp: { | 
|  | PROTOCOL_LSP: 'The Language Server Protocol ' | 
|  | '(https://microsoft.github.io/language-server-protocol)', | 
|  | PROTOCOL_ANALYZER: 'Dart\'s analysis server protocol ' | 
|  | '(https://dart.dev/go/analysis-server-protocol)', | 
|  | }, | 
|  | help: | 
|  | 'Specify the protocol to use to communicate with the analysis server.', | 
|  | ); | 
|  | // This option is hidden but still accepted; it's effectively translated to | 
|  | // the 'protocol' option above. | 
|  | parser.addFlag(USE_LSP, | 
|  | defaultsTo: false, | 
|  | negatable: false, | 
|  | help: 'Whether to use the Language Server Protocol (LSP).', | 
|  | hide: true); | 
|  |  | 
|  | parser.addSeparator('Server diagnostics:'); | 
|  |  | 
|  | parser.addOption(PROTOCOL_TRAFFIC_LOG, | 
|  | valueHelp: 'file path', | 
|  | help: 'Write server protocol traffic to the given file.'); | 
|  | parser.addOption(PROTOCOL_TRAFFIC_LOG_ALIAS, hide: true); | 
|  |  | 
|  | parser.addOption(ANALYSIS_DRIVER_LOG, | 
|  | valueHelp: 'file path', | 
|  | help: 'Write analysis driver diagnostic data to the given file.'); | 
|  | parser.addOption(ANALYSIS_DRIVER_LOG_ALIAS, hide: true); | 
|  |  | 
|  | parser.addOption(DIAGNOSTIC_PORT, | 
|  | valueHelp: 'port', | 
|  | help: 'Serve a web UI for status and performance data on the given ' | 
|  | 'port.'); | 
|  | parser.addOption(DIAGNOSTIC_PORT_ALIAS, hide: true); | 
|  |  | 
|  | // | 
|  | // Hidden; these have not yet been made public. | 
|  | // | 
|  | parser.addFlag(ANALYTICS_FLAG, | 
|  | help: 'enable or disable sending analytics information to Google', | 
|  | hide: !telemetry.SHOW_ANALYTICS_UI); | 
|  | parser.addFlag(SUPPRESS_ANALYTICS_FLAG, | 
|  | negatable: false, | 
|  | help: 'suppress analytics for this session', | 
|  | hide: !telemetry.SHOW_ANALYTICS_UI); | 
|  |  | 
|  | // | 
|  | // Hidden; these are for internal development. | 
|  | // | 
|  | parser.addOption(TRAIN_USING, | 
|  | valueHelp: 'path', | 
|  | help: 'Pass in a directory to analyze for purposes of training an ' | 
|  | 'analysis server snapshot.', | 
|  | hide: true); | 
|  | parser.addFlag(DISABLE_SERVER_EXCEPTION_HANDLING, | 
|  | // TODO(jcollins-g): Pipeline option through and apply to all | 
|  | // exception-nullifying runZoned() calls. | 
|  | help: 'disable analyzer exception capture for interactive debugging ' | 
|  | 'of the server', | 
|  | defaultsTo: false, | 
|  | hide: true); | 
|  | parser.addFlag(DISABLE_SERVER_FEATURE_COMPLETION, | 
|  | help: 'disable all completion features', defaultsTo: false, hide: true); | 
|  | parser.addFlag(DISABLE_SERVER_FEATURE_SEARCH, | 
|  | help: 'disable all search features', defaultsTo: false, hide: true); | 
|  | parser.addFlag(INTERNAL_PRINT_TO_CONSOLE, | 
|  | help: 'enable sending `print` output to the console', | 
|  | defaultsTo: false, | 
|  | negatable: false, | 
|  | hide: true); | 
|  |  | 
|  | // | 
|  | // Hidden; these are deprecated and no longer read from. | 
|  | // | 
|  |  | 
|  | // Removed 11/15/2020. | 
|  | parser.addOption('completion-model', hide: true); | 
|  | // Removed 11/8/2020. | 
|  | parser.addFlag('dartpad', hide: true); | 
|  | // Removed 11/15/2020. | 
|  | parser.addFlag('enable-completion-model', hide: true); | 
|  | // Removed 10/30/2020. | 
|  | parser.addMultiOption('enable-experiment', hide: true); | 
|  | // Removed 9/23/2020. | 
|  | parser.addFlag('enable-instrumentation', hide: true); | 
|  | // Removed 11/12/2020. | 
|  | parser.addOption('file-read-mode', hide: true); | 
|  | // Removed 11/12/2020. | 
|  | parser.addFlag('ignore-unrecognized-flags', hide: true); | 
|  | // Removed 11/8/2020. | 
|  | parser.addFlag('preview-dart-2', hide: true); | 
|  | // Removed 11/12/2020. | 
|  | parser.addFlag('useAnalysisHighlight2', hide: true); | 
|  | // Removed 11/13/2020. | 
|  | parser.addFlag('use-new-relevance', hide: true); | 
|  | // Removed 9/23/2020. | 
|  | parser.addFlag('use-fasta-parser', hide: true); | 
|  |  | 
|  | return parser; | 
|  | } | 
|  |  | 
|  | /// Perform log files rolling. | 
|  | /// | 
|  | /// Rename existing files with names `[path].(x)` to `[path].(x+1)`. | 
|  | /// Keep at most [numOld] files. | 
|  | /// Rename the file with the given [path] to `[path].1`. | 
|  | static void _rollLogFiles(String path, int numOld) { | 
|  | for (var i = numOld - 1; i >= 0; i--) { | 
|  | try { | 
|  | var oldPath = i == 0 ? path : '$path.$i'; | 
|  | File(oldPath).renameSync('$path.${i + 1}'); | 
|  | } catch (e) { | 
|  | // If a file can't be renamed, then leave it and attempt to rename the | 
|  | // remaining files. | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Implements the [DiagnosticServer] class by wrapping an [HttpAnalysisServer]. | 
|  | class _DiagnosticServerImpl extends DiagnosticServer { | 
|  | HttpAnalysisServer httpServer; | 
|  |  | 
|  | _DiagnosticServerImpl(); | 
|  |  | 
|  | @override | 
|  | Future<int> getServerPort() => httpServer.serveHttp(); | 
|  |  | 
|  | Future startOnPort(int port) { | 
|  | return httpServer.serveHttp(port); | 
|  | } | 
|  | } |