1// Copyright 2018 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import 'dart:async'; 6 7import '../base/context.dart'; 8import '../base/file_system.dart'; 9import '../base/io.dart'; 10import '../base/platform.dart'; 11import '../base/process_manager.dart'; 12import '../cache.dart'; 13import '../convert.dart'; 14import '../globals.dart'; 15 16import 'fuchsia_dev_finder.dart'; 17import 'fuchsia_kernel_compiler.dart'; 18import 'fuchsia_pm.dart'; 19 20/// The [FuchsiaSdk] instance. 21FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>(); 22 23/// The [FuchsiaArtifacts] instance. 24FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>(); 25 26/// The Fuchsia SDK shell commands. 27/// 28/// This workflow assumes development within the fuchsia source tree, 29/// including a working fx command-line tool in the user's PATH. 30class FuchsiaSdk { 31 /// Interface to the 'pm' tool. 32 FuchsiaPM get fuchsiaPM => _fuchsiaPM ??= FuchsiaPM(); 33 FuchsiaPM _fuchsiaPM; 34 35 /// Interface to the 'dev_finder' tool. 36 FuchsiaDevFinder _fuchsiaDevFinder; 37 FuchsiaDevFinder get fuchsiaDevFinder => 38 _fuchsiaDevFinder ??= FuchsiaDevFinder(); 39 40 /// Interface to the 'kernel_compiler' tool. 41 FuchsiaKernelCompiler _fuchsiaKernelCompiler; 42 FuchsiaKernelCompiler get fuchsiaKernelCompiler => 43 _fuchsiaKernelCompiler ??= FuchsiaKernelCompiler(); 44 45 /// Example output: 46 /// $ dev_finder list -full 47 /// > 192.168.42.56 paper-pulp-bush-angel 48 Future<String> listDevices() async { 49 if (fuchsiaArtifacts.devFinder == null || 50 !fuchsiaArtifacts.devFinder.existsSync()) { 51 return null; 52 } 53 final List<String> devices = await fuchsiaDevFinder.list(); 54 if (devices == null) { 55 return null; 56 } 57 return devices.isNotEmpty ? devices[0] : null; 58 } 59 60 /// Returns the fuchsia system logs for an attached device. 61 Stream<String> syslogs(String id) { 62 Process process; 63 try { 64 final StreamController<String> controller = 65 StreamController<String>(onCancel: () { 66 process.kill(); 67 }); 68 if (fuchsiaArtifacts.sshConfig == null || 69 !fuchsiaArtifacts.sshConfig.existsSync()) { 70 printError('Cannot read device logs: No ssh config.'); 71 printError('Have you set FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR?'); 72 return null; 73 } 74 const String remoteCommand = 'log_listener --clock Local'; 75 final List<String> cmd = <String>[ 76 'ssh', 77 '-F', 78 fuchsiaArtifacts.sshConfig.absolute.path, 79 id, 80 remoteCommand 81 ]; 82 processManager.start(cmd).then((Process newProcess) { 83 if (controller.isClosed) { 84 return; 85 } 86 process = newProcess; 87 process.exitCode.whenComplete(controller.close); 88 controller.addStream(process.stdout 89 .transform(utf8.decoder) 90 .transform(const LineSplitter())); 91 }); 92 return controller.stream; 93 } catch (exception) { 94 printTrace('$exception'); 95 } 96 return const Stream<String>.empty(); 97 } 98} 99 100/// Fuchsia-specific artifacts used to interact with a device. 101class FuchsiaArtifacts { 102 /// Creates a new [FuchsiaArtifacts]. 103 FuchsiaArtifacts({ 104 this.sshConfig, 105 this.devFinder, 106 this.platformKernelDill, 107 this.flutterPatchedSdk, 108 this.kernelCompiler, 109 this.pm, 110 }); 111 112 /// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK. 113 /// 114 /// Finds tools under bin/cache/artifacts/fuchsia/tools. 115 /// Queries environment variables (first FUCHSIA_BUILD_DIR, then 116 /// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to 117 /// a device. 118 factory FuchsiaArtifacts.find() { 119 if (!platform.isLinux && !platform.isMacOS) { 120 // Don't try to find the artifacts on platforms that are not supported. 121 return FuchsiaArtifacts(); 122 } 123 final String fuchsia = Cache.instance.getArtifactDirectory('fuchsia').path; 124 final String tools = fs.path.join(fuchsia, 'tools'); 125 final String dartPrebuilts = fs.path.join(tools, 'dart_prebuilts'); 126 127 final File devFinder = fs.file(fs.path.join(tools, 'dev_finder')); 128 final File platformDill = fs.file(fs.path.join( 129 dartPrebuilts, 'flutter_runner', 'platform_strong.dill')); 130 final File patchedSdk = fs.file(fs.path.join( 131 dartPrebuilts, 'flutter_runner')); 132 final File kernelCompiler = fs.file(fs.path.join( 133 dartPrebuilts, 'kernel_compiler.snapshot')); 134 final File pm = fs.file(fs.path.join(tools, 'pm')); 135 136 // If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir 137 // relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it. 138 // TODO(zra): Consider passing the ssh config path in with a flag. 139 File sshConfig; 140 if (platform.environment.containsKey(_kFuchsiaBuildDir)) { 141 sshConfig = fs.file(fs.path.join( 142 platform.environment[_kFuchsiaBuildDir], 'ssh-keys', 'ssh_config')); 143 } else if (platform.environment.containsKey(_kFuchsiaSshConfig)) { 144 sshConfig = fs.file(platform.environment[_kFuchsiaSshConfig]); 145 } 146 return FuchsiaArtifacts( 147 sshConfig: sshConfig, 148 devFinder: devFinder.existsSync() ? devFinder : null, 149 platformKernelDill: platformDill.existsSync() ? platformDill : null, 150 flutterPatchedSdk: patchedSdk.existsSync() ? patchedSdk : null, 151 kernelCompiler: kernelCompiler.existsSync() ? kernelCompiler : null, 152 pm: pm.existsSync() ? pm : null, 153 ); 154 } 155 156 static const String _kFuchsiaSshConfig = 'FUCHSIA_SSH_CONFIG'; 157 static const String _kFuchsiaBuildDir = 'FUCHSIA_BUILD_DIR'; 158 159 /// The location of the SSH configuration file used to interact with a 160 /// Fuchsia device. 161 final File sshConfig; 162 163 /// The location of the dev finder tool used to locate connected 164 /// Fuchsia devices. 165 final File devFinder; 166 167 /// The location of the Fuchsia-specific platform dill. 168 final File platformKernelDill; 169 170 /// The directory containing [platformKernelDill]. 171 final File flutterPatchedSdk; 172 173 /// The snapshot of the Fuchsia kernel compiler. 174 final File kernelCompiler; 175 176 /// The pm tool. 177 final File pm; 178} 179