fuchsia_sdk.dart 5.75 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:async';

7
import '../base/context.dart';
8 9
import '../base/file_system.dart';
import '../base/io.dart';
10
import '../base/platform.dart';
11
import '../convert.dart';
12
import '../globals.dart' as globals;
13

14 15 16 17
import 'fuchsia_dev_finder.dart';
import 'fuchsia_kernel_compiler.dart';
import 'fuchsia_pm.dart';

18
/// The [FuchsiaSdk] instance.
19
FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
20

21
/// Returns [true] if the current platform supports Fuchsia targets.
22 23
bool isFuchsiaSupportedPlatform(Platform platform) {
  return platform.isLinux || platform.isMacOS;
24 25
}

26 27 28 29 30
/// The Fuchsia SDK shell commands.
///
/// This workflow assumes development within the fuchsia source tree,
/// including a working fx command-line tool in the user's PATH.
class FuchsiaSdk {
31 32 33 34
  /// Interface to the 'pm' tool.
  FuchsiaPM get fuchsiaPM => _fuchsiaPM ??= FuchsiaPM();
  FuchsiaPM _fuchsiaPM;

35
  /// Interface to the 'device-finder' tool.
36 37
  FuchsiaDevFinder _fuchsiaDevFinder;
  FuchsiaDevFinder get fuchsiaDevFinder =>
38 39 40 41 42
      _fuchsiaDevFinder ??= FuchsiaDevFinder(
        fuchsiaArtifacts: globals.fuchsiaArtifacts,
        logger: globals.logger,
        processManager: globals.processManager
      );
43 44 45 46 47 48

  /// Interface to the 'kernel_compiler' tool.
  FuchsiaKernelCompiler _fuchsiaKernelCompiler;
  FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
      _fuchsiaKernelCompiler ??= FuchsiaKernelCompiler();

49 50
  /// Returns any attached devices is a newline-denominated String.
  ///
51
  /// Example output:
52 53 54
  ///
  ///     $ device-finder list -full
  ///     > 192.168.42.56 paper-pulp-bush-angel
55
  Future<String> listDevices({ Duration timeout }) async {
56 57
    if (globals.fuchsiaArtifacts.devFinder == null ||
        !globals.fuchsiaArtifacts.devFinder.existsSync()) {
58
      return null;
59
    }
60
    final List<String> devices = await fuchsiaDevFinder.list(timeout: timeout);
61 62 63
    if (devices == null) {
      return null;
    }
64
    return devices.isNotEmpty ? devices.join('\n') : null;
65 66
  }

67 68
  /// Returns the fuchsia system logs for an attached device where
  /// [id] is the IP address of the device.
69
  Stream<String> syslogs(String id) {
70 71
    Process process;
    try {
72
      final StreamController<String> controller = StreamController<String>(onCancel: () {
73 74
        process.kill();
      });
75 76
      if (globals.fuchsiaArtifacts.sshConfig == null ||
          !globals.fuchsiaArtifacts.sshConfig.existsSync()) {
77 78
        globals.printError('Cannot read device logs: No ssh config.');
        globals.printError('Have you set FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR?');
79 80 81 82 83 84
        return null;
      }
      const String remoteCommand = 'log_listener --clock Local';
      final List<String> cmd = <String>[
        'ssh',
        '-F',
85
        globals.fuchsiaArtifacts.sshConfig.absolute.path,
86
        id, // The device's IP.
87
        remoteCommand,
88
      ];
89
      globals.processManager.start(cmd).then((Process newProcess) {
90 91 92 93
        if (controller.isClosed) {
          return;
        }
        process = newProcess;
94
        process.exitCode.whenComplete(controller.close);
95 96 97
        controller.addStream(process.stdout
            .transform(utf8.decoder)
            .transform(const LineSplitter()));
98 99
      });
      return controller.stream;
100
    } on Exception catch (exception) {
101
      globals.printTrace('$exception');
102
    }
103
    return const Stream<String>.empty();
104 105
  }
}
106 107 108 109

/// Fuchsia-specific artifacts used to interact with a device.
class FuchsiaArtifacts {
  /// Creates a new [FuchsiaArtifacts].
110 111 112
  FuchsiaArtifacts({
    this.sshConfig,
    this.devFinder,
113
    this.pm,
114
  });
115

116 117 118 119 120 121 122
  /// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK.
  ///
  /// Finds tools under bin/cache/artifacts/fuchsia/tools.
  /// Queries environment variables (first FUCHSIA_BUILD_DIR, then
  /// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to
  /// a device.
  factory FuchsiaArtifacts.find() {
123
    if (!isFuchsiaSupportedPlatform(globals.platform)) {
124 125 126
      // Don't try to find the artifacts on platforms that are not supported.
      return FuchsiaArtifacts();
    }
127 128 129 130
    // If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir
    // relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it.
    // TODO(zra): Consider passing the ssh config path in with a flag.
    File sshConfig;
131 132 133 134 135
    if (globals.platform.environment.containsKey(_kFuchsiaBuildDir)) {
      sshConfig = globals.fs.file(globals.fs.path.join(
          globals.platform.environment[_kFuchsiaBuildDir], 'ssh-keys', 'ssh_config'));
    } else if (globals.platform.environment.containsKey(_kFuchsiaSshConfig)) {
      sshConfig = globals.fs.file(globals.platform.environment[_kFuchsiaSshConfig]);
136
    }
137

138 139
    final String fuchsia = globals.cache.getArtifactDirectory('fuchsia').path;
    final String tools = globals.fs.path.join(fuchsia, 'tools');
140
    final File devFinder = globals.fs.file(globals.fs.path.join(tools, 'device-finder'));
141
    final File pm = globals.fs.file(globals.fs.path.join(tools, 'pm'));
142

143 144
    return FuchsiaArtifacts(
      sshConfig: sshConfig,
145 146
      devFinder: devFinder.existsSync() ? devFinder : null,
      pm: pm.existsSync() ? pm : null,
147 148 149 150 151 152
    );
  }

  static const String _kFuchsiaSshConfig = 'FUCHSIA_SSH_CONFIG';
  static const String _kFuchsiaBuildDir = 'FUCHSIA_BUILD_DIR';

153
  /// The location of the SSH configuration file used to interact with a
154 155 156 157 158 159
  /// Fuchsia device.
  final File sshConfig;

  /// The location of the dev finder tool used to locate connected
  /// Fuchsia devices.
  final File devFinder;
160

161 162
  /// The pm tool.
  final File pm;
163 164 165

  /// Returns true if the [sshConfig] file is not null and exists.
  bool get hasSshConfig => sshConfig != null && sshConfig.existsSync();
166
}