• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 'package:args/args.dart';
8
9import 'package:flutter_tools/runner.dart' as runner;
10import 'package:flutter_tools/src/artifacts.dart';
11import 'package:flutter_tools/src/base/common.dart';
12import 'package:flutter_tools/src/base/context.dart';
13import 'package:flutter_tools/src/base/file_system.dart';
14import 'package:flutter_tools/src/cache.dart';
15import 'package:flutter_tools/src/commands/attach.dart';
16import 'package:flutter_tools/src/commands/doctor.dart';
17import 'package:flutter_tools/src/device.dart';
18import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart';
19import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
20import 'package:flutter_tools/src/project.dart';
21import 'package:flutter_tools/src/runner/flutter_command.dart';
22
23final ArgParser parser = ArgParser()
24  ..addOption('build-dir', help: 'The fuchsia build directory')
25  ..addOption('dart-sdk', help: 'The prebuilt dart SDK')
26  ..addOption('target', help: 'The GN target to attach to')
27  ..addOption('entrypoint', defaultsTo: 'main.dart', help: 'The filename of the main method. Defaults to main.dart')
28  ..addOption('device', help: 'The device id to attach to')
29  ..addOption('dev-finder', help: 'The location of the dev_finder binary')
30  ..addFlag('verbose', negatable: true);
31
32// Track the original working directory so that the tool can find the
33// flutter repo in third_party.
34String originalWorkingDirectory;
35
36Future<void> main(List<String> args) async {
37  final ArgResults argResults = parser.parse(args);
38  final bool verbose = argResults['verbose'];
39  final String target = argResults['target'];
40  final List<String> targetParts = _extractPathAndName(target);
41  final String path = targetParts[0];
42  final String name = targetParts[1];
43  final File dartSdk = fs.file(argResults['dart-sdk']);
44  final String buildDirectory = argResults['build-dir'];
45  final File frontendServer = fs.file('$buildDirectory/host_x64/gen/third_party/flutter/frontend_server/frontend_server_tool.snapshot');
46  final File sshConfig = fs.file('$buildDirectory/ssh-keys/ssh_config');
47  final File devFinder = fs.file(argResults['dev-finder']);
48  final File platformKernelDill = fs.file('$buildDirectory/flutter_runner_patched_sdk/platform_strong.dill');
49  final File flutterPatchedSdk = fs.file('$buildDirectory/flutter_runner_patched_sdk');
50  final String packages = '$buildDirectory/dartlang/gen/$path/${name}_dart_library.packages';
51  final String outputDill = '$buildDirectory/${name}_tmp.dill';
52
53  // TODO(jonahwilliams): running from fuchsia root hangs hot reload for some reason.
54  // switch to the project root directory and run from there.
55  originalWorkingDirectory = fs.currentDirectory.path;
56  fs.currentDirectory = path;
57
58  if (!devFinder.existsSync()) {
59    print('Error: dev_finder not found at ${devFinder.path}.');
60    return 1;
61  }
62  if (!frontendServer.existsSync()) {
63    print(
64      'Error: frontend_server not found at ${frontendServer.path}. This '
65      'Usually means you ran fx set without specifying '
66      '--args=flutter_profile=true.'
67    );
68    return 1;
69  }
70
71  // Check for a package with a lib directory.
72  final String entrypoint = argResults['entrypoint'];
73  String targetFile = 'lib/$entrypoint';
74  if (!fs.file(targetFile).existsSync()) {
75    // Otherwise assume the package is flat.
76    targetFile = entrypoint;
77  }
78  final String deviceName = argResults['device'];
79  final List<String> command = <String>[
80    'attach',
81    '--module',
82    name,
83    '--isolate-filter',
84    name,
85    '--target',
86    targetFile,
87    '--target-model',
88    'flutter', // TODO(jonahwilliams): change to flutter_runner when dart SDK rolls
89    '--output-dill',
90    outputDill,
91    '--packages',
92    packages,
93    if (deviceName != null && deviceName.isNotEmpty) ...<String>['-d', deviceName],
94    if (verbose) '--verbose',
95  ];
96  Cache.disableLocking(); // ignore: invalid_use_of_visible_for_testing_member
97  await runner.run(
98    command,
99    <FlutterCommand>[
100      _FuchsiaAttachCommand(),
101      _FuchsiaDoctorCommand(), // If attach fails the tool will attempt to run doctor.
102    ],
103    verbose: verbose,
104    muteCommandLogging: false,
105    verboseHelp: false,
106    overrides: <Type, Generator>{
107      DeviceManager: () => _FuchsiaDeviceManager(),
108      FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig, devFinder: devFinder),
109      Artifacts: () => OverrideArtifacts(
110        parent: CachedArtifacts(),
111        frontendServer: frontendServer,
112        engineDartBinary: dartSdk,
113        platformKernelDill: platformKernelDill,
114        flutterPatchedSdk: flutterPatchedSdk,
115      ),
116    },
117  );
118}
119
120// An implementation of [DeviceManager] that only supports fuchsia devices.
121class _FuchsiaDeviceManager extends DeviceManager {
122  @override
123  List<DeviceDiscovery> get deviceDiscoverers => List<DeviceDiscovery>.unmodifiable(<DeviceDiscovery>[
124    FuchsiaDevices(),
125  ]);
126
127  @override
128  bool isDeviceSupportedForProject(Device device, FlutterProject flutterProject) {
129    return true;
130  }
131}
132
133List<String> _extractPathAndName(String gnTarget) {
134  // Separate strings like //path/to/target:app into [path/to/target, app]
135  final int lastColon = gnTarget.lastIndexOf(':');
136  if (lastColon < 0) {
137    throwToolExit('invalid path: $gnTarget');
138  }
139  final String name = gnTarget.substring(lastColon + 1);
140  // Skip '//' and chop off after :
141  if ((gnTarget.length < 3) || (gnTarget[0] != '/') || (gnTarget[1] != '/')) {
142    throwToolExit('invalid path: $gnTarget');
143  }
144  final String path = gnTarget.substring(2, lastColon);
145  return <String>[path, name];
146}
147
148class _FuchsiaDoctorCommand extends DoctorCommand {
149  @override
150  Future<FlutterCommandResult> runCommand() async {
151    Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter';
152    return super.runCommand();
153  }
154}
155
156class _FuchsiaAttachCommand extends AttachCommand {
157  @override
158  Future<FlutterCommandResult> runCommand() async {
159    Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter';
160    return super.runCommand();
161  }
162}
163