// Copyright (c) 2019, 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. /// This executable provides the ability to run the migration tool in process /// on a single package. It should be invoked with two command-line arguments: /// a path to a configuration file and the name of a package to migrate. /// /// The configuration file format is a JSON map, with the following keys: /// - `sdk_root`: path to the SDK source code on the user's machine (this is the /// directory that contains `pkg`, `third_party`, `tests`, etc. /// - `output_root`: if present, path to the directory on the user's machine /// where output HTML files should go. A subdirectory will be created for /// each package that is migrated. /// - `external_packages`: a map (name => path) of additional non-SDK packages /// that may need to be migrated. /// - `port`: if present, the port where a server should be spawned serving HTML /// pages. library migration_runner; import 'dart:convert'; import 'dart:io' as io; import 'package:analysis_server/protocol/protocol.dart'; import 'package:analysis_server/protocol/protocol_constants.dart'; import 'package:analysis_server/protocol/protocol_generated.dart'; import 'package:analysis_server/src/analysis_server.dart'; import 'package:analysis_server/src/server/crash_reporting_attachments.dart'; import 'package:analysis_server/src/utilities/mocks.dart'; import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:analyzer/instrumentation/instrumentation.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:cli_util/cli_util.dart'; import 'package:path/path.dart' as path; import 'package:test/test.dart'; Future main(List args) async { if (args.length != 2) { throw StateError( 'Exactly two arguments are required: the path to a JSON configuration ' 'file, and the name of the package to migrate'); } var testInfoJsonPath = args[0]; var testInfoJson = json.decode(io.File(testInfoJsonPath).readAsStringSync()); var packageName = args[1]; var testInfo = TestInfo(testInfoJson); var packageRoot = testInfo.packageRoot(packageName); var port = testInfo.port; print('Preparing to migrate'); var migrationTest = MigrationTest(); migrationTest.setUp(); print('Migrating'); await migrationTest.run(packageRoot, port); migrationTest.tearDown(); print('Done. Please point your browser to localhost:$port/\$filePath'); } class MigrationBase { ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; late MockServerChannel serverChannel; late AnalysisServer server; AnalysisServer createAnalysisServer() { // // Create server // var options = AnalysisServerOptions(); var sdkPath = getSdkPath(); return AnalysisServer( serverChannel, resourceProvider, options, DartSdkManager(sdkPath), CrashReportingAttachmentsBuilder.empty, InstrumentationService.NULL_SERVICE); } void processNotification(Notification notification) { if (notification.event == SERVER_NOTIFICATION_ERROR) { fail('${notification.toJson()}'); } } Future sendAnalysisSetAnalysisRoots(List directories) { var request = AnalysisSetAnalysisRootsParams(directories, []).toRequest('0'); return waitResponse(request); } Future sendEditDartfix(List directories, int port) { var request = EditDartfixParams(directories, includedFixes: ['non-nullable'], port: port) .toRequest('1'); return waitResponse(request); } void setUp() { serverChannel = MockServerChannel(); server = createAnalysisServer(); server.pluginManager = TestPluginManager(); // listen for notifications var notificationStream = serverChannel.notificationController.stream; notificationStream.listen((Notification notification) { processNotification(notification); }); } void tearDown() { server.done(); } /// Returns a [Future] that completes when the server's analysis is complete. Future waitForTasksFinished() { return server.onAnalysisComplete; } /// Completes with a successful [Response] for the given [request]. Future waitResponse(Request request, {bool throwOnError = true}) async { return serverChannel.sendRequest(request, throwOnError: throwOnError); } } class MigrationTest extends MigrationBase { Future run(String packageRoot, int port) async { var packageRoots = [packageRoot]; await sendAnalysisSetAnalysisRoots(packageRoots); await sendEditDartfix(packageRoots, port); } } class TestInfo { static const Set thirdPartyPackages = { 'charcode', 'collection', 'logging', 'meta', 'pedantic', 'typed_data' }; static const Set builtInPackages = {'meta', 'path'}; final Map testInfoJson; TestInfo(this.testInfoJson); Map get externalPackages => ((testInfoJson['external_packages'] ?? {}) as Map).cast(); String get outputRoot => testInfoJson['output_root'] as String; int get port => testInfoJson['port'] as int; String get sdkRoot => testInfoJson['sdk_root'] as String; String packageRoot(String packageName) { if (thirdPartyPackages.contains(packageName)) { return path.join(sdkRoot, 'third_party', 'pkg', packageName); } else if (builtInPackages.contains(packageName)) { return path.join(sdkRoot, 'pkg', packageName); } else if (externalPackages.containsKey(packageName)) { return externalPackages[packageName] as String; } else { throw StateError('Unrecognized package $packageName'); } } }