Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Unified Diff: pkg/fletchc/lib/src/worker/developer.dart

Issue 1659163007: Rename fletch -> dartino (Closed) Base URL: https://github.com/dartino/sdk.git@master
Patch Set: address comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/fletchc/lib/src/verbs/x_upgrade_verb.dart ('k') | pkg/fletchc/lib/src/worker/worker_main.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/fletchc/lib/src/worker/developer.dart
diff --git a/pkg/fletchc/lib/src/worker/developer.dart b/pkg/fletchc/lib/src/worker/developer.dart
deleted file mode 100644
index 98ca9081031af6419f79fe4cc96c468e44559cb2..0000000000000000000000000000000000000000
--- a/pkg/fletchc/lib/src/worker/developer.dart
+++ /dev/null
@@ -1,1569 +0,0 @@
-// Copyright (c) 2015, the Dartino 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.md file.
-
-library fletchc.worker.developer;
-
-import 'dart:async' show
- Future,
- Stream,
- StreamController,
- Timer;
-
-import 'dart:convert' show
- JSON,
- JsonEncoder,
- UTF8;
-
-import 'dart:io' show
- Directory,
- File,
- FileSystemEntity,
- InternetAddress,
- Platform,
- Process,
- Socket,
- SocketException;
-
-import 'package:sdk_library_metadata/libraries.dart' show
- Category;
-
-import 'package:sdk_services/sdk_services.dart' show
- OutputService,
- SDKServices;
-
-import 'package:fletch_agent/agent_connection.dart' show
- AgentConnection,
- AgentException,
- VmData;
-
-import 'package:fletch_agent/messages.dart' show
- AGENT_DEFAULT_PORT,
- MessageDecodeException;
-
-import 'package:mdns/mdns.dart' show
- MDnsClient,
- ResourceRecord,
- RRType;
-
-import 'package:path/path.dart' show
- join;
-
-import '../../vm_commands.dart' show
- VmCommandCode,
- ConnectionError,
- Debugging,
- HandShakeResult,
- ProcessBacktrace,
- ProcessBacktraceRequest,
- ProcessRun,
- ProcessSpawnForMain,
- SessionEnd,
- WriteSnapshotResult;
-
-import '../../program_info.dart' show
- Configuration,
- ProgramInfo,
- ProgramInfoBinary,
- ProgramInfoJson,
- buildProgramInfo;
-
-import '../hub/session_manager.dart' show
- FletchVm,
- SessionState,
- Sessions;
-
-import '../hub/client_commands.dart' show
- ClientCommandCode,
- handleSocketErrors;
-
-import '../verbs/infrastructure.dart' show
- ClientCommand,
- CommandSender,
- DiagnosticKind,
- FletchCompiler,
- FletchDelta,
- IncrementalCompiler,
- WorkerConnection,
- IsolatePool,
- Session,
- SharedTask,
- StreamIterator,
- throwFatalError;
-
-import '../../incremental/fletchc_incremental.dart' show
- IncrementalCompilationFailed,
- IncrementalMode,
- parseIncrementalMode,
- unparseIncrementalMode;
-
-export '../../incremental/fletchc_incremental.dart' show
- IncrementalMode;
-
-import '../../fletch_compiler.dart' show fletchDeviceType;
-
-import '../hub/exit_codes.dart' as exit_codes;
-
-import '../../fletch_system.dart' show
- FletchFunction,
- FletchSystem;
-
-import '../../bytecodes.dart' show
- Bytecode,
- MethodEnd;
-
-import '../diagnostic.dart' show
- throwInternalError;
-
-import '../guess_configuration.dart' show
- executable,
- fletchVersion,
- guessFletchVm;
-
-import '../device_type.dart' show
- DeviceType,
- parseDeviceType,
- unParseDeviceType;
-
-export '../device_type.dart' show
- DeviceType;
-
-import '../please_report_crash.dart' show
- pleaseReportCrash;
-
-import '../../debug_state.dart' as debug show
- RemoteObject,
- BackTrace;
-
-typedef Future<Null> ClientEventHandler(Session session);
-
-Uri configFileUri;
-
-Future<Socket> connect(
- String host,
- int port,
- DiagnosticKind kind,
- String socketDescription,
- SessionState state) async {
- // We are using .catchError rather than try/catch because we have seen
- // incorrect stack traces using the latter.
- Socket socket = await Socket.connect(host, port).catchError(
- (SocketException error) {
- String message = error.message;
- if (error.osError != null) {
- message = error.osError.message;
- }
- throwFatalError(kind, address: '$host:$port', message: message);
- }, test: (e) => e is SocketException);
- handleSocketErrors(socket, socketDescription, log: (String info) {
- state.log("Connected to TCP $socketDescription $info");
- });
- return socket;
-}
-
-Future<AgentConnection> connectToAgent(SessionState state) async {
- // TODO(wibling): need to make sure the agent is running.
- assert(state.settings.deviceAddress != null);
- String host = state.settings.deviceAddress.host;
- int agentPort = state.settings.deviceAddress.port;
- Socket socket = await connect(
- host, agentPort, DiagnosticKind.socketAgentConnectError,
- "agentSocket", state);
- return new AgentConnection(socket);
-}
-
-/// Return the result of a function in the context of an open [AgentConnection].
-///
-/// The result is a [Future] of this value.
-/// This function handles [AgentException] and [MessageDecodeException].
-Future withAgentConnection(
- SessionState state,
- Future f(AgentConnection connection)) async {
- AgentConnection connection = await connectToAgent(state);
- try {
- return await f(connection);
- } on AgentException catch (error) {
- throwFatalError(
- DiagnosticKind.socketAgentReplyError,
- address: '${connection.socket.remoteAddress.host}:'
- '${connection.socket.remotePort}',
- message: error.message);
- } on MessageDecodeException catch (error) {
- throwFatalError(
- DiagnosticKind.socketAgentReplyError,
- address: '${connection.socket.remoteAddress.host}:'
- '${connection.socket.remotePort}',
- message: error.message);
- } finally {
- disconnectFromAgent(connection);
- }
-}
-
-void disconnectFromAgent(AgentConnection connection) {
- assert(connection.socket != null);
- connection.socket.close();
-}
-
-Future<Null> checkAgentVersion(Uri base, SessionState state) async {
- String deviceFletchVersion = await withAgentConnection(state,
- (connection) => connection.fletchVersion());
- Uri packageFile = await lookForAgentPackage(base, version: fletchVersion);
- String fixit;
- if (packageFile != null) {
- fixit = "Try running\n"
- " 'fletch x-upgrade agent in session ${state.name}'.";
- } else {
- fixit = "Try downloading a matching SDK and running\n"
- " 'fletch x-upgrade agent in session ${state.name}'\n"
- "from the SDK's root directory.";
- }
-
- if (fletchVersion != deviceFletchVersion) {
- throwFatalError(DiagnosticKind.agentVersionMismatch,
- userInput: fletchVersion,
- additionalUserInput: deviceFletchVersion,
- fixit: fixit);
- }
-}
-
-Future<Null> startAndAttachViaAgent(Uri base, SessionState state) async {
- // TODO(wibling): integrate with the FletchVm class, e.g. have a
- // AgentFletchVm and LocalFletchVm that both share the same interface
- // where the former is interacting with the agent.
- await checkAgentVersion(base, state);
- VmData vmData = await withAgentConnection(state,
- (connection) => connection.startVm());
- state.fletchAgentVmId = vmData.id;
- String host = state.settings.deviceAddress.host;
- await attachToVm(host, vmData.port, state);
- await state.session.disableVMStandardOutput();
-}
-
-Future<Null> startAndAttachDirectly(SessionState state, Uri base) async {
- String fletchVmPath = state.compilerHelper.fletchVm.toFilePath();
- state.fletchVm = await FletchVm.start(fletchVmPath, workingDirectory: base);
- await attachToVm(state.fletchVm.host, state.fletchVm.port, state);
- await state.session.disableVMStandardOutput();
-}
-
-Future<Null> attachToVm(String host, int port, SessionState state) async {
- Socket socket = await connect(
- host, port, DiagnosticKind.socketVmConnectError, "vmSocket", state);
-
- Session session = new Session(socket, state.compiler, state.stdoutSink,
- state.stderrSink, null);
-
- // Perform handshake with VM which validates that VM and compiler
- // have the same versions.
- HandShakeResult handShakeResult = await session.handShake(fletchVersion);
- if (handShakeResult == null) {
- throwFatalError(DiagnosticKind.handShakeFailed, address: '$host:$port');
- }
- if (!handShakeResult.success) {
- throwFatalError(DiagnosticKind.versionMismatch,
- address: '$host:$port',
- userInput: fletchVersion,
- additionalUserInput: handShakeResult.version);
- }
-
- // Enable debugging to be able to communicate with VM when there
- // are errors.
- await session.runCommand(const Debugging());
-
- state.session = session;
-}
-
-Future<int> compile(
- Uri script,
- SessionState state,
- Uri base,
- {bool analyzeOnly: false,
- bool fatalIncrementalFailures: false}) async {
- IncrementalCompiler compiler = state.compiler;
- if (!compiler.isProductionModeEnabled) {
- state.resetCompiler();
- }
- Uri firstScript = state.script;
- List<FletchDelta> previousResults = state.compilationResults;
-
- FletchDelta newResult;
- try {
- if (analyzeOnly) {
- state.resetCompiler();
- state.log("Analyzing '$script'");
- return await compiler.analyze(script, base);
- } else if (previousResults.isEmpty) {
- state.script = script;
- await compiler.compile(script, base);
- newResult = compiler.computeInitialDelta();
- } else {
- try {
- state.log("Compiling difference from $firstScript to $script");
- newResult = await compiler.compileUpdates(
- previousResults.last.system, <Uri, Uri>{firstScript: script},
- logTime: state.log, logVerbose: state.log);
- } on IncrementalCompilationFailed catch (error) {
- state.log(error);
- state.resetCompiler();
- if (fatalIncrementalFailures) {
- print(error);
- state.log(
- "Aborting compilation due to --fatal-incremental-failures...");
- return exit_codes.INCREMENTAL_COMPILER_FAILED;
- }
- state.log("Attempting full compile...");
- state.script = script;
- await compiler.compile(script, base);
- newResult = compiler.computeInitialDelta();
- }
- }
- } catch (error, stackTrace) {
- pleaseReportCrash(error, stackTrace);
- return exit_codes.COMPILER_EXITCODE_CRASH;
- }
- if (newResult == null) {
- return exit_codes.DART_VM_EXITCODE_COMPILE_TIME_ERROR;
- }
-
- state.addCompilationResult(newResult);
-
- state.log("Compiled '$script' to ${newResult.commands.length} commands");
-
- return 0;
-}
-
-Future<Settings> readSettings(Uri uri) async {
- if (await new File.fromUri(uri).exists()) {
- String jsonLikeData = await new File.fromUri(uri).readAsString();
- return parseSettings(jsonLikeData, uri);
- } else {
- return null;
- }
-}
-
-Future<Uri> findFile(Uri cwd, String fileName) async {
- Uri uri = cwd.resolve(fileName);
- while (true) {
- if (await new File.fromUri(uri).exists()) return uri;
- if (uri.pathSegments.length <= 1) return null;
- uri = uri.resolve('../$fileName');
- }
-}
-
-Future<Settings> createSettings(
- String sessionName,
- Uri uri,
- Uri cwd,
- Uri configFileUri,
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator) async {
- bool userProvidedSettings = uri != null;
- if (!userProvidedSettings) {
- // Try to find a $sessionName.fletch-settings file starting from the current
- // working directory and walking up its parent directories.
- uri = await findFile(cwd, '$sessionName.fletch-settings');
-
- // If no $sessionName.fletch-settings file is found, try to find the
- // settings template file (in the SDK or git repo) by looking for a
- // .fletch-settings file starting from the fletch executable's directory
- // and walking up its parent directory chain.
- if (uri == null) {
- uri = await findFile(executable, '.fletch-settings');
- if (uri != null) print('Using template settings file $uri');
- }
- }
-
- Settings settings = new Settings.empty();
- if (uri != null) {
- String jsonLikeData = await new File.fromUri(uri).readAsString();
- settings = parseSettings(jsonLikeData, uri);
- }
- if (userProvidedSettings) return settings;
-
- // TODO(wibling): get rid of below special handling of the sessions 'remote'
- // and 'local' and come up with a fletch project concept that can contain
- // these settings.
- Uri packagesUri;
- Address address;
- switch (sessionName) {
- case "remote":
- uri = configFileUri.resolve("remote.fletch-settings");
- Settings remoteSettings = await readSettings(uri);
- if (remoteSettings != null) return remoteSettings;
- packagesUri = executable.resolve("fletch-sdk.packages");
- address = await readAddressFromUser(commandSender, commandIterator);
- if (address == null) {
- // Assume user aborted data entry.
- return settings;
- }
- break;
-
- case "local":
- uri = configFileUri.resolve("local.fletch-settings");
- Settings localSettings = await readSettings(uri);
- if (localSettings != null) return localSettings;
- // TODO(ahe): Use mock packages here.
- packagesUri = executable.resolve("fletch-sdk.packages");
- break;
-
- default:
- return settings;
- }
-
- if (!await new File.fromUri(packagesUri).exists()) {
- packagesUri = null;
- }
- settings = settings.copyWith(packages: packagesUri, deviceAddress: address);
- print("Created settings file '$uri'");
- await new File.fromUri(uri).writeAsString(
- "${const JsonEncoder.withIndent(' ').convert(settings)}\n");
- return settings;
-}
-
-Future<Address> readAddressFromUser(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator) async {
- String message = "Please enter IP address of remote device "
- "(press Enter to search for devices):";
- commandSender.sendStdout(message);
- // The list of devices found by running discovery.
- List<InternetAddress> devices = <InternetAddress>[];
- while (await commandIterator.moveNext()) {
- ClientCommand command = commandIterator.current;
- switch (command.code) {
- case ClientCommandCode.Stdin:
- if (command.data.length == 0) {
- // TODO(ahe): It may be safe to return null here, but we need to
- // check how this interacts with the debugger's InputHandler.
- throwInternalError("Unexpected end of input");
- }
- // TODO(ahe): This assumes that the user's input arrives as one
- // message. It is relatively safe to assume this for a normal terminal
- // session because we use canonical input processing (Unix line
- // buffering), but it doesn't work in general. So we should fix that.
- String line = UTF8.decode(command.data).trim();
- if (line.isEmpty && devices.isEmpty) {
- commandSender.sendStdout("\n");
- // [discoverDevices] will print out the list of device with their
- // IP address, hostname, and agent version.
- devices = await discoverDevices(prefixWithNumber: true);
- if (devices.isEmpty) {
- commandSender.sendStdout(
- "Couldn't find Fletch capable devices\n");
- commandSender.sendStdout(message);
- } else {
- if (devices.length == 1) {
- commandSender.sendStdout("\n");
- commandSender.sendStdout("Press Enter to use this device");
- } else {
- commandSender.sendStdout("\n");
- commandSender.sendStdout(
- "Found ${devices.length} Fletch capable devices\n");
- commandSender.sendStdout(
- "Please enter the number or the IP address of "
- "the remote device you would like to use "
- "(press Enter to use the first device): ");
- }
- }
- } else {
- bool checkedIndex = false;
- if (devices.length > 0) {
- if (line.isEmpty) {
- return new Address(devices[0].address, AGENT_DEFAULT_PORT);
- }
- try {
- checkedIndex = true;
- int index = int.parse(line);
- if (1 <= index && index <= devices.length) {
- return new Address(devices[index - 1].address,
- AGENT_DEFAULT_PORT);
- } else {
- commandSender.sendStdout("Invalid device index $line\n\n");
- commandSender.sendStdout(message);
- }
- } on FormatException {
- // Ignore FormatException and fall through to parse as IP address.
- }
- }
- if (!checkedIndex) {
- return parseAddress(line, defaultPort: AGENT_DEFAULT_PORT);
- }
- }
- break;
-
- default:
- throwInternalError("Unexpected ${command.code}");
- return null;
- }
- }
- return null;
-}
-
-SessionState createSessionState(
- String name,
- Settings settings,
- {Uri libraryRoot,
- Uri fletchVm,
- Uri nativesJson}) {
- if (settings == null) {
- settings = const Settings.empty();
- }
- List<String> compilerOptions = const bool.fromEnvironment("fletchc-verbose")
- ? <String>['--verbose'] : <String>[];
- compilerOptions.addAll(settings.options);
- Uri packageConfig = settings.packages;
- if (packageConfig == null) {
- packageConfig = executable.resolve("fletch-sdk.packages");
- }
-
- DeviceType deviceType = settings.deviceType ??
- parseDeviceType(fletchDeviceType);
-
- String platform = (deviceType == DeviceType.embedded)
- ? "fletch_embedded.platform"
- : "fletch_mobile.platform";
-
- FletchCompiler compilerHelper = new FletchCompiler(
- options: compilerOptions,
- packageConfig: packageConfig,
- environment: settings.constants,
- platform: platform,
- libraryRoot: libraryRoot,
- fletchVm: fletchVm,
- nativesJson: nativesJson);
-
- return new SessionState(
- name, compilerHelper,
- compilerHelper.newIncrementalCompiler(settings.incrementalMode),
- settings);
-}
-
-Future runWithDebugger(
- List<String> commands,
- Session session,
- SessionState state) async {
-
- // Method used to generate the debugger commands if none are specified.
- Stream<String> inputGenerator() async* {
- yield 't verbose';
- yield 'b main';
- yield 'r';
- while (!session.terminated) {
- yield 's';
- }
- }
-
- return commands.isEmpty ?
- session.debug(inputGenerator(), Uri.base, state, echo: true) :
- session.debug(
- new Stream<String>.fromIterable(commands), Uri.base, state,
- echo: true);
-}
-
-Future<int> run(
- SessionState state,
- {List<String> testDebuggerCommands,
- bool terminateDebugger: true}) async {
- List<FletchDelta> compilationResults = state.compilationResults;
- Session session = state.session;
-
- for (FletchDelta delta in compilationResults) {
- await session.applyDelta(delta);
- }
-
- if (testDebuggerCommands != null) {
- await runWithDebugger(testDebuggerCommands, session, state);
- return 0;
- }
-
- session.silent = true;
-
- await session.enableDebugger();
- await session.spawnProcess();
- var command = await session.debugRun();
-
- int exitCode = exit_codes.COMPILER_EXITCODE_CRASH;
- if (command == null) {
- await session.kill();
- await session.shutdown();
- throwInternalError("No command received from Fletch VM");
- }
-
- Future printException() async {
- if (!session.loaded) {
- print('### process not loaded, cannot print uncaught exception');
- return;
- }
- debug.RemoteObject exception = await session.uncaughtException();
- if (exception != null) {
- print(session.exceptionToString(exception));
- }
- }
-
- Future printTrace() async {
- if (!session.loaded) {
- print("### process not loaded, cannot print stacktrace and code");
- return;
- }
- debug.BackTrace stackTrace = await session.backTrace();
- if (stackTrace != null) {
- print(stackTrace.format());
- print(stackTrace.list(state));
- }
- }
-
- try {
- switch (command.code) {
- case VmCommandCode.UncaughtException:
- state.log("Uncaught error");
- exitCode = exit_codes.DART_VM_EXITCODE_UNCAUGHT_EXCEPTION;
- await printException();
- await printTrace();
- // TODO(ahe): Need to continue to unwind stack.
- break;
- case VmCommandCode.ProcessCompileTimeError:
- state.log("Compile-time error");
- exitCode = exit_codes.DART_VM_EXITCODE_COMPILE_TIME_ERROR;
- await printTrace();
- // TODO(ahe): Continue to unwind stack?
- break;
-
- case VmCommandCode.ProcessTerminated:
- exitCode = 0;
- break;
-
- case VmCommandCode.ConnectionError:
- state.log("Error on connection to Fletch VM: ${command.error}");
- exitCode = exit_codes.COMPILER_EXITCODE_CONNECTION_ERROR;
- break;
-
- default:
- throwInternalError("Unexpected result from Fletch VM: '$command'");
- break;
- }
- } finally {
- if (terminateDebugger) {
- await state.terminateSession();
- } else {
- // If the session terminated due to a ConnectionError or the program
- // finished don't reuse the state's session.
- if (session.terminated) {
- state.session = null;
- }
- session.silent = false;
- }
- };
-
- return exitCode;
-}
-
-Future<int> export(SessionState state,
- Uri snapshot,
- {bool binaryProgramInfo: false}) async {
- List<FletchDelta> compilationResults = state.compilationResults;
- Session session = state.session;
- state.session = null;
-
- for (FletchDelta delta in compilationResults) {
- await session.applyDelta(delta);
- }
-
- var result = await session.writeSnapshot(snapshot.toFilePath());
- if (result is WriteSnapshotResult) {
- WriteSnapshotResult snapshotResult = result;
-
- await session.shutdown();
-
- ProgramInfo info =
- buildProgramInfo(compilationResults.last.system, snapshotResult);
-
- File jsonFile = new File('${snapshot.toFilePath()}.info.json');
- await jsonFile.writeAsString(ProgramInfoJson.encode(info));
-
- if (binaryProgramInfo) {
- File binFile = new File('${snapshot.toFilePath()}.info.bin');
- await binFile.writeAsBytes(ProgramInfoBinary.encode(info));
- }
-
- return 0;
- } else {
- assert(result is ConnectionError);
- print("There was a connection error while writing the snapshot.");
- return exit_codes.COMPILER_EXITCODE_CONNECTION_ERROR;
- }
-}
-
-Future<int> compileAndAttachToVmThen(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator,
- SessionState state,
- Uri script,
- Uri base,
- bool waitForVmExit,
- Future<int> action(),
- {ClientEventHandler eventHandler}) async {
- bool startedVmDirectly = false;
- List<FletchDelta> compilationResults = state.compilationResults;
- if (compilationResults.isEmpty || script != null) {
- if (script == null) {
- throwFatalError(DiagnosticKind.noFileTarget);
- }
- int exitCode = await compile(script, state, base);
- if (exitCode != 0) return exitCode;
- compilationResults = state.compilationResults;
- assert(compilationResults != null);
- }
-
- Session session = state.session;
- if (session != null && session.loaded) {
- // We cannot reuse a session that has already been loaded. Loading
- // currently implies that some of the code has been run.
- if (state.explicitAttach) {
- // If the user explicitly called 'fletch attach' we cannot
- // create a new VM session since we don't know if the vm is
- // running locally or remotely and if running remotely there
- // is no guarantee there is an agent to start a new vm.
- //
- // The UserSession is invalid in its current state as the
- // vm session (aka. session in the code here) has already
- // been loaded and run some code.
- throwFatalError(DiagnosticKind.sessionInvalidState,
- sessionName: state.name);
- }
- state.log('Cannot reuse existing VM session, creating new.');
- await state.terminateSession();
- session = null;
- }
- if (session == null) {
- if (state.settings.deviceAddress != null) {
- await startAndAttachViaAgent(base, state);
- // TODO(wibling): read stdout from agent.
- } else {
- startedVmDirectly = true;
- await startAndAttachDirectly(state, base);
- state.fletchVm.stdoutLines.listen((String line) {
- commandSender.sendStdout("$line\n");
- });
- state.fletchVm.stderrLines.listen((String line) {
- commandSender.sendStderr("$line\n");
- });
- }
- session = state.session;
- assert(session != null);
- }
-
- eventHandler ??= defaultClientEventHandler(state, commandIterator);
- setupClientInOut(state, commandSender, eventHandler);
-
- int exitCode = exit_codes.COMPILER_EXITCODE_CRASH;
- try {
- exitCode = await action();
- } catch (error, trace) {
- print(error);
- if (trace != null) {
- print(trace);
- }
- } finally {
- if (waitForVmExit && startedVmDirectly) {
- exitCode = await state.fletchVm.exitCode;
- }
- state.detachCommandSender();
- }
- return exitCode;
-}
-
-void setupClientInOut(
- SessionState state,
- CommandSender commandSender,
- ClientEventHandler eventHandler) {
- // Forward output going into the state's outputSink using the passed in
- // commandSender. This typically forwards output to the hub (main isolate)
- // which forwards it on to stdout of the Fletch C++ client.
- state.attachCommandSender(commandSender);
-
- // Start event handling for input passed from the Fletch C++ client.
- eventHandler(state.session);
-
- // Let the hub (main isolate) know that event handling has been started.
- commandSender.sendEventLoopStarted();
-}
-
-/// Return a default client event handler bound to the current session's
-/// commandIterator and state.
-/// This handler only takes care of signals coming from the client.
-ClientEventHandler defaultClientEventHandler(
- SessionState state,
- StreamIterator<ClientCommand> commandIterator) {
- return (Session session) async {
- while (await commandIterator.moveNext()) {
- ClientCommand command = commandIterator.current;
- switch (command.code) {
- case ClientCommandCode.Signal:
- int signalNumber = command.data;
- handleSignal(state, signalNumber);
- break;
- default:
- state.log("Unhandled command from client: $command");
- }
- }
- };
-}
-
-void handleSignal(SessionState state, int signalNumber) {
- state.log("Received signal $signalNumber");
- if (!state.hasRemoteVm && state.fletchVm == null) {
- // This can happen if a user has attached to a vm using the "attach" verb
- // in which case we don't forward the signal to the vm.
- // TODO(wibling): Determine how to interpret the signal for the persistent
- // process.
- state.log('Signal $signalNumber ignored. VM was manually attached.');
- print('Signal $signalNumber ignored. VM was manually attached.');
- return;
- }
- if (state.hasRemoteVm) {
- signalAgentVm(state, signalNumber);
- } else {
- assert(state.fletchVm.process != null);
- int vmPid = state.fletchVm.process.pid;
- Process.runSync("kill", ["-$signalNumber", "$vmPid"]);
- }
-}
-
-Future signalAgentVm(SessionState state, int signalNumber) async {
- await withAgentConnection(state, (connection) {
- return connection.signalVm(state.fletchAgentVmId, signalNumber);
- });
-}
-
-String extractVersion(Uri uri) {
- List<String> nameParts = uri.pathSegments.last.split('_');
- if (nameParts.length != 3 || nameParts[0] != 'fletch-agent') {
- throwFatalError(DiagnosticKind.upgradeInvalidPackageName);
- }
- String version = nameParts[1];
- // create_debian_packages.py adds a '-1' after the hash in the package name.
- if (version.endsWith('-1')) {
- version = version.substring(0, version.length - 2);
- }
- return version;
-}
-
-/// Try to locate an Fletch agent package file assuming the normal SDK layout
-/// with SDK base directory [base].
-///
-/// If the parameter [version] is passed, the Uri is only returned, if
-/// the version matches.
-Future<Uri> lookForAgentPackage(Uri base, {String version}) async {
- String platform = "raspberry-pi2";
- Uri platformUri = base.resolve("platforms/$platform");
- Directory platformDir = new Directory.fromUri(platformUri);
-
- // Try to locate the agent package in the SDK for the selected platform.
- Uri sdkAgentPackage;
- if (await platformDir.exists()) {
- for (FileSystemEntity entry in platformDir.listSync()) {
- Uri uri = entry.uri;
- String name = uri.pathSegments.last;
- if (name.startsWith('fletch-agent') &&
- name.endsWith('.deb') &&
- (version == null || extractVersion(uri) == version)) {
- return uri;
- }
- }
- }
- return null;
-}
-
-Future<Uri> readPackagePathFromUser(
- Uri base,
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator) async {
- Uri sdkAgentPackage = await lookForAgentPackage(base);
- if (sdkAgentPackage != null) {
- String path = sdkAgentPackage.toFilePath();
- commandSender.sendStdout("Found SDK package: $path\n");
- commandSender.sendStdout("Press Enter to use this package to upgrade "
- "or enter the path to another package file:\n");
- } else {
- commandSender.sendStdout("Please enter the path to the package file "
- "you want to use:\n");
- }
-
- while (await commandIterator.moveNext()) {
- ClientCommand command = commandIterator.current;
- switch (command.code) {
- case ClientCommandCode.Stdin:
- if (command.data.length == 0) {
- throwInternalError("Unexpected end of input");
- }
- // TODO(karlklose): This assumes that the user's input arrives as one
- // message. It is relatively safe to assume this for a normal terminal
- // session because we use canonical input processing (Unix line
- // buffering), but it doesn't work in general. So we should fix that.
- String line = UTF8.decode(command.data).trim();
- if (line.isEmpty) {
- return sdkAgentPackage;
- } else {
- return base.resolve(line);
- }
- break;
-
- default:
- throwInternalError("Unexpected ${command.code}");
- return null;
- }
- }
- return null;
-}
-
-class Version {
- final List<int> version;
- final String label;
-
- Version(this.version, this.label) {
- if (version.length != 3) {
- throw new ArgumentError("version must have three parts");
- }
- }
-
- /// Returns `true` if this version's digits are greater in lexicographical
- /// order.
- ///
- /// We use a function instead of [operator >] because [label] is not used
- /// in the comparison, but it is used in [operator ==].
- bool isGreaterThan(Version other) {
- for (int part = 0; part < 3; ++part) {
- if (version[part] < other.version[part]) {
- return false;
- }
- if (version[part] > other.version[part]) {
- return true;
- }
- }
- return false;
- }
-
- bool operator ==(other) {
- return other is Version &&
- version[0] == other.version[0] &&
- version[1] == other.version[1] &&
- version[2] == other.version[2] &&
- label == other.label;
- }
-
- int get hashCode {
- return 3 * version[0] +
- 5 * version[1] +
- 7 * version[2] +
- 13 * label.hashCode;
- }
-
- /// Check if this version is a bleeding edge version.
- bool get isEdgeVersion => label == null ? false : label.startsWith('edge.');
-
- /// Check if this version is a dev version.
- bool get isDevVersion => label == null ? false : label.startsWith('dev.');
-
- String toString() {
- String labelPart = label == null ? '' : '-$label';
- return '${version[0]}.${version[1]}.${version[2]}$labelPart';
- }
-}
-
-Version parseVersion(String text) {
- List<String> labelParts = text.split('-');
- if (labelParts.length > 2) {
- throw new ArgumentError('Not a version: $text.');
- }
- List<String> digitParts = labelParts[0].split('.');
- if (digitParts.length != 3) {
- throw new ArgumentError('Not a version: $text.');
- }
- List<int> digits = digitParts.map(int.parse).toList();
- return new Version(digits, labelParts.length == 2 ? labelParts[1] : null);
-}
-
-Future<int> upgradeAgent(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator,
- SessionState state,
- Uri base,
- Uri packageUri) async {
- if (state.settings.deviceAddress == null) {
- throwFatalError(DiagnosticKind.noAgentFound);
- }
-
- while (packageUri == null) {
- packageUri =
- await readPackagePathFromUser(base, commandSender, commandIterator);
- }
-
- if (!await new File.fromUri(packageUri).exists()) {
- print('File not found: $packageUri');
- return 1;
- }
-
- Version version = parseVersion(extractVersion(packageUri));
-
- Version existingVersion = parseVersion(
- await withAgentConnection(state,
- (connection) => connection.fletchVersion()));
-
- if (existingVersion == version) {
- print('Target device is already at $version');
- return 0;
- }
-
- print("Attempting to upgrade device from "
- "$existingVersion to $version");
-
- if (existingVersion.isGreaterThan(version)) {
- commandSender.sendStdout("The existing version is greater than the "
- "version you want to use to upgrade.\n"
- "Please confirm this operation by typing 'yes' "
- "(press Enter to abort): ");
- Confirm: while (await commandIterator.moveNext()) {
- ClientCommand command = commandIterator.current;
- switch (command.code) {
- case ClientCommandCode.Stdin:
- if (command.data.length == 0) {
- throwInternalError("Unexpected end of input");
- }
- String line = UTF8.decode(command.data).trim();
- if (line.isEmpty) {
- commandSender.sendStdout("Upgrade aborted\n");
- return 0;
- } else if (line.trim().toLowerCase() == "yes") {
- break Confirm;
- }
- break;
-
- default:
- throwInternalError("Unexpected ${command.code}");
- return null;
- }
- }
- }
-
- List<int> data = await new File.fromUri(packageUri).readAsBytes();
- print("Sending package to fletch agent");
- await withAgentConnection(state,
- (connection) => connection.upgradeAgent(version.toString(), data));
- print("Transfer complete, waiting for the Fletch agent to restart. "
- "This can take a few seconds.");
-
- Version newVersion;
- int remainingTries = 20;
- // Wait for the agent to come back online to verify the version.
- while (--remainingTries > 0) {
- await new Future.delayed(const Duration(seconds: 1));
- try {
- // TODO(karlklose): this functionality should be shared with connect.
- Socket socket = await Socket.connect(
- state.settings.deviceAddress.host,
- state.settings.deviceAddress.port);
- handleSocketErrors(socket, "pollAgentVersion", log: (String info) {
- state.log("Connected to TCP waitForAgentUpgrade $info");
- });
- AgentConnection connection = new AgentConnection(socket);
- newVersion = parseVersion(await connection.fletchVersion());
- disconnectFromAgent(connection);
- if (newVersion != existingVersion) {
- break;
- }
- } on SocketException catch (e) {
- // Ignore this error and keep waiting.
- }
- }
-
- if (newVersion == existingVersion) {
- print("Failed to upgrade: the device is still at the old version.");
- print("Try running x-upgrade again. "
- "If the upgrade fails again, try rebooting the device.");
- return 1;
- } else if (newVersion == null) {
- print("Could not connect to Fletch agent after upgrade.");
- print("Try running 'fletch show devices' later to see if it has been"
- " restarted. If the device does not show up, try rebooting it.");
- return 1;
- } else {
- print("Upgrade successful.");
- }
-
- return 0;
-}
-
-Future<int> downloadTools(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator,
- SessionState state) async {
-
- void throwUnsupportedPlatform() {
- throwFatalError(
- DiagnosticKind.unsupportedPlatform,
- message: Platform.operatingSystem);
- }
-
- Future decompressFile(File zipFile, Directory destination) async {
- var result;
- if (Platform.isLinux) {
- result = await Process.run(
- "unzip", ["-o", zipFile.path, "-d", destination.path]);
- } else if (Platform.isMacOS) {
- result = await Process.run(
- "ditto", ["-x", "-k", zipFile.path, destination.path]);
- } else {
- throwUnsupportedPlatform();
- }
- if (result.exitCode != 0) {
- throwInternalError(
- "Failed to decompress ${zipFile.path} to ${destination.path}, "
- "error = ${result.exitCode}");
- }
- }
-
- const String gcsRoot = "https://storage.googleapis.com";
- String gcsBucket = "fletch-archive";
-
- Future downloadTool(String gcsPath, String zipFile, String toolName) async {
- Uri url = Uri.parse("$gcsRoot/$gcsBucket/$gcsPath/$zipFile");
- Directory tmpDir = Directory.systemTemp.createTempSync("fletch_download");
- File tmpZip = new File(join(tmpDir.path, zipFile));
-
- OutputService outputService =
- new OutputService(commandSender.sendStdout, state.log);
- SDKServices service = new SDKServices(outputService);
- print("Downloading: $toolName");
- state.log("Downloading $toolName from $url to $tmpZip");
- await service.downloadWithProgress(url, tmpZip);
- print(""); // service.downloadWithProgress does not write newline when done.
-
- // In the SDK, the tools directory is at the same level as the
- // internal (and bin) directory.
- Directory toolsDirectory =
- new Directory.fromUri(executable.resolve('../tools'));
- state.log("Decompressing ${tmpZip.path} to ${toolsDirectory.path}");
- await decompressFile(tmpZip, toolsDirectory);
- state.log("Deleting temporary directory ${tmpDir.path}");
- await tmpDir.delete(recursive: true);
- }
-
- String gcsPath;
-
- Version version = parseVersion(fletchVersion);
- if (version.isEdgeVersion) {
- print("WARNING: For bleeding edge a fixed image is used.");
- // For edge versions download use a well known version for now.
- var knownVersion = "0.3.0-edge.3c85dbafe006eb2ce16545aaf3df1352fa7a4500";
- gcsBucket = "fletch-temporary";
- gcsPath = "channels/be/raw/$knownVersion/sdk";
- } else if (version.isDevVersion) {
- // TODO(sgjesse): Change this to channels/dev/release at some point.
- gcsPath = "channels/dev/raw/$version/sdk";
- } else {
- print("Stable version not supported. Got version $version.");
- }
-
- String osName;
- if (Platform.isLinux) {
- osName = "linux";
- } else if (Platform.isMacOS) {
- osName = "mac";
- } else {
- throwUnsupportedPlatform();
- }
-
- String gccArmEmbedded = "gcc-arm-embedded-${osName}.zip";
- await downloadTool(gcsPath, gccArmEmbedded, "GCC ARM Embedded toolchain");
- String openocd = "openocd-${osName}.zip";
- await downloadTool(gcsPath, openocd, "Open On-Chip Debugger (OpenOCD)");
-
- print("Third party tools downloaded");
-
- return 0;
-}
-
-Future<WorkerConnection> allocateWorker(IsolatePool pool) async {
- WorkerConnection workerConnection =
- new WorkerConnection(await pool.getIsolate(exitOnError: false));
- await workerConnection.beginSession();
- return workerConnection;
-}
-
-SharedTask combineTasks(SharedTask task1, SharedTask task2) {
- if (task1 == null) return task2;
- if (task2 == null) return task1;
- return new CombinedTask(task1, task2);
-}
-
-class CombinedTask extends SharedTask {
- // Keep this class simple, see note in superclass.
-
- final SharedTask task1;
-
- final SharedTask task2;
-
- const CombinedTask(this.task1, this.task2);
-
- Future<int> call(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator) {
- return invokeCombinedTasks(commandSender, commandIterator, task1, task2);
- }
-}
-
-Future<int> invokeCombinedTasks(
- CommandSender commandSender,
- StreamIterator<ClientCommand> commandIterator,
- SharedTask task1,
- SharedTask task2) async {
- int result = await task1(commandSender, commandIterator);
- if (result != 0) return result;
- return task2(commandSender, commandIterator);
-}
-
-Future<String> getAgentVersion(InternetAddress host, int port) async {
- Socket socket;
- try {
- socket = await Socket.connect(host, port);
- handleSocketErrors(socket, "getAgentVersionSocket");
- } on SocketException catch (e) {
- return 'Error: no agent: $e';
- }
- try {
- AgentConnection connection = new AgentConnection(socket);
- return await connection.fletchVersion();
- } finally {
- socket.close();
- }
-}
-
-Future<List<InternetAddress>> discoverDevices(
- {bool prefixWithNumber: false}) async {
- const ipV4AddressLength = 'xxx.xxx.xxx.xxx'.length;
- print("Looking for Dartino capable devices (will search for 5 seconds)...");
- MDnsClient client = new MDnsClient();
- await client.start();
- List<InternetAddress> result = <InternetAddress>[];
- String name = '_dartino_agent._tcp.local';
- await for (ResourceRecord ptr in client.lookup(RRType.PTR, name)) {
- String domain = ptr.domainName;
- await for (ResourceRecord srv in client.lookup(RRType.SRV, domain)) {
- String target = srv.target;
- await for (ResourceRecord a in client.lookup(RRType.A, target)) {
- InternetAddress address = a.address;
- if (!address.isLinkLocal) {
- result.add(address);
- String version = await getAgentVersion(address, AGENT_DEFAULT_PORT);
- String prefix = prefixWithNumber ? "${result.length}: " : "";
- print("${prefix}Device at "
- "${address.address.padRight(ipV4AddressLength + 1)} "
- "$target ($version)");
- }
- }
- }
- // TODO(karlklose): Verify that we got an A/IP4 result for the PTR result.
- // If not, maybe the cache was flushed before access and we need to query
- // for the SRV or A type again.
- }
- client.stop();
- return result;
-}
-
-void showSessions() {
- Sessions.names.forEach(print);
-}
-
-Future<int> showSessionSettings() async {
- Settings settings = SessionState.current.settings;
- Uri source = settings.source;
- if (source != null) {
- // This should output `source.toFilePath()`, but we do it like this to be
- // consistent with the format of the [Settings.packages] value.
- print('Configured from $source}');
- }
- settings.toJson().forEach((String key, value) {
- print('$key: $value');
- });
- return 0;
-}
-
-Address parseAddress(String address, {int defaultPort: 0}) {
- String host;
- int port;
- List<String> parts = address.split(":");
- if (parts.length == 1) {
- host = InternetAddress.LOOPBACK_IP_V4.address;
- port = int.parse(
- parts[0],
- onError: (String source) {
- host = source;
- return defaultPort;
- });
- } else {
- host = parts[0];
- port = int.parse(
- parts[1],
- onError: (String source) {
- throwFatalError(
- DiagnosticKind.expectedAPortNumber, userInput: source);
- });
- }
- return new Address(host, port);
-}
-
-class Address {
- final String host;
- final int port;
-
- const Address(this.host, this.port);
-
- String toString() => "Address($host, $port)";
-
- String toJson() => "$host:$port";
-
- bool operator ==(other) {
- if (other is! Address) return false;
- return other.host == host && other.port == port;
- }
-
- int get hashCode => host.hashCode ^ port.hashCode;
-}
-
-/// See ../verbs/documentation.dart for a definition of this format.
-Settings parseSettings(String jsonLikeData, Uri settingsUri) {
- String json = jsonLikeData.split("\n")
- .where((String line) => !line.trim().startsWith("//")).join("\n");
- var userSettings;
- try {
- userSettings = JSON.decode(json);
- } on FormatException catch (e) {
- throwFatalError(
- DiagnosticKind.settingsNotJson, uri: settingsUri, message: e.message);
- }
- if (userSettings is! Map) {
- throwFatalError(DiagnosticKind.settingsNotAMap, uri: settingsUri);
- }
- Uri packages;
- final List<String> options = <String>[];
- final Map<String, String> constants = <String, String>{};
- Address deviceAddress;
- DeviceType deviceType;
- IncrementalMode incrementalMode = IncrementalMode.none;
- userSettings.forEach((String key, value) {
- switch (key) {
- case "packages":
- if (value != null) {
- if (value is! String) {
- throwFatalError(
- DiagnosticKind.settingsPackagesNotAString, uri: settingsUri,
- userInput: '$value');
- }
- packages = settingsUri.resolve(value);
- }
- break;
-
- case "options":
- if (value != null) {
- if (value is! List) {
- throwFatalError(
- DiagnosticKind.settingsOptionsNotAList, uri: settingsUri,
- userInput: "$value");
- }
- for (var option in value) {
- if (option is! String) {
- throwFatalError(
- DiagnosticKind.settingsOptionNotAString, uri: settingsUri,
- userInput: '$option');
- }
- if (option.startsWith("-D")) {
- throwFatalError(
- DiagnosticKind.settingsCompileTimeConstantAsOption,
- uri: settingsUri, userInput: '$option');
- }
- options.add(option);
- }
- }
- break;
-
- case "constants":
- if (value != null) {
- if (value is! Map) {
- throwFatalError(
- DiagnosticKind.settingsConstantsNotAMap, uri: settingsUri);
- }
- value.forEach((String key, value) {
- if (value == null) {
- // Ignore.
- } else if (value is bool || value is int || value is String) {
- constants[key] = '$value';
- } else {
- throwFatalError(
- DiagnosticKind.settingsUnrecognizedConstantValue,
- uri: settingsUri, userInput: key,
- additionalUserInput: '$value');
- }
- });
- }
- break;
-
- case "device_address":
- if (value != null) {
- if (value is! String) {
- throwFatalError(
- DiagnosticKind.settingsDeviceAddressNotAString,
- uri: settingsUri, userInput: '$value');
- }
- deviceAddress =
- parseAddress(value, defaultPort: AGENT_DEFAULT_PORT);
- }
- break;
-
- case "device_type":
- if (value != null) {
- if (value is! String) {
- throwFatalError(
- DiagnosticKind.settingsDeviceTypeNotAString,
- uri: settingsUri, userInput: '$value');
- }
- deviceType = parseDeviceType(value);
- if (deviceType == null) {
- throwFatalError(
- DiagnosticKind.settingsDeviceTypeUnrecognized,
- uri: settingsUri, userInput: '$value');
- }
- }
- break;
-
- case "incremental_mode":
- if (value != null) {
- if (value is! String) {
- throwFatalError(
- DiagnosticKind.settingsIncrementalModeNotAString,
- uri: settingsUri, userInput: '$value');
- }
- incrementalMode = parseIncrementalMode(value);
- if (incrementalMode == null) {
- throwFatalError(
- DiagnosticKind.settingsIncrementalModeUnrecognized,
- uri: settingsUri, userInput: '$value');
- }
- }
- break;
-
- default:
- throwFatalError(
- DiagnosticKind.settingsUnrecognizedKey, uri: settingsUri,
- userInput: key);
- break;
- }
- });
- return new Settings.fromSource(settingsUri,
- packages, options, constants, deviceAddress, deviceType, incrementalMode);
-}
-
-class Settings {
- final Uri source;
-
- final Uri packages;
-
- final List<String> options;
-
- final Map<String, String> constants;
-
- final Address deviceAddress;
-
- final DeviceType deviceType;
-
- final IncrementalMode incrementalMode;
-
- const Settings(
- this.packages,
- this.options,
- this.constants,
- this.deviceAddress,
- this.deviceType,
- this.incrementalMode) : source = null;
-
- const Settings.fromSource(
- this.source,
- this.packages,
- this.options,
- this.constants,
- this.deviceAddress,
- this.deviceType,
- this.incrementalMode);
-
- const Settings.empty()
- : this(null, const <String>[], const <String, String>{}, null, null,
- IncrementalMode.none);
-
- Settings copyWith({
- Uri packages,
- List<String> options,
- Map<String, String> constants,
- Address deviceAddress,
- DeviceType deviceType,
- IncrementalMode incrementalMode}) {
-
- if (packages == null) {
- packages = this.packages;
- }
- if (options == null) {
- options = this.options;
- }
- if (constants == null) {
- constants = this.constants;
- }
- if (deviceAddress == null) {
- deviceAddress = this.deviceAddress;
- }
- if (deviceType == null) {
- deviceType = this.deviceType;
- }
- if (incrementalMode == null) {
- incrementalMode = this.incrementalMode;
- }
- return new Settings(
- packages,
- options,
- constants,
- deviceAddress,
- deviceType,
- incrementalMode);
- }
-
- String toString() {
- return "Settings("
- "packages: $packages, "
- "options: $options, "
- "constants: $constants, "
- "device_address: $deviceAddress, "
- "device_type: $deviceType, "
- "incremental_mode: $incrementalMode)";
- }
-
- Map<String, dynamic> toJson() {
- Map<String, dynamic> result = <String, dynamic>{};
-
- void addIfNotNull(String name, value) {
- if (value != null) {
- result[name] = value;
- }
- }
-
- addIfNotNull("packages", packages == null ? null : "$packages");
- addIfNotNull("options", options);
- addIfNotNull("constants", constants);
- addIfNotNull("device_address", deviceAddress);
- addIfNotNull(
- "device_type",
- deviceType == null ? null : unParseDeviceType(deviceType));
- addIfNotNull(
- "incremental_mode",
- incrementalMode == null
- ? null : unparseIncrementalMode(incrementalMode));
-
- return result;
- }
-}
« no previous file with comments | « pkg/fletchc/lib/src/verbs/x_upgrade_verb.dart ('k') | pkg/fletchc/lib/src/worker/worker_main.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698