| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:args/args.dart'; |
| |
| import 'package:flutter_tools/runner.dart' as runner; |
| import 'package:flutter_tools/src/artifacts.dart'; |
| import 'package:flutter_tools/src/base/common.dart'; |
| import 'package:flutter_tools/src/base/context.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/cache.dart'; |
| import 'package:flutter_tools/src/commands/attach.dart'; |
| import 'package:flutter_tools/src/commands/doctor.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/features.dart'; |
| import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart'; |
| import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart'; |
| import 'package:flutter_tools/src/fuchsia/fuchsia_workflow.dart'; |
| import 'package:flutter_tools/src/globals.dart' as globals; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:flutter_tools/src/runner/flutter_command.dart'; |
| |
| final ArgParser parser = ArgParser() |
| ..addOption('build-dir', help: 'The fuchsia build directory') |
| ..addOption('dart-sdk', help: 'The prebuilt dart SDK') |
| ..addOption('target', help: 'The GN target to attach to') |
| ..addOption('entrypoint', defaultsTo: 'main.dart', help: 'The filename of the main method. Defaults to main.dart') |
| ..addOption('device', help: 'The device id to attach to') |
| ..addOption('dev-finder', help: 'The location of the device-finder binary') |
| ..addFlag('verbose', negatable: true); |
| |
| // Track the original working directory so that the tool can find the |
| // flutter repo in third_party. |
| String originalWorkingDirectory; |
| |
| Future<void> main(List<String> args) async { |
| final ArgResults argResults = parser.parse(args); |
| final bool verbose = argResults['verbose'] as bool; |
| final String target = argResults['target'] as String; |
| final List<String> targetParts = _extractPathAndName(target); |
| final String path = targetParts[0]; |
| final String name = targetParts[1]; |
| final File dartSdk = globals.fs.file(argResults['dart-sdk']); |
| final String buildDirectory = argResults['build-dir'] as String; |
| final File frontendServer = globals.fs.file('$buildDirectory/host_x64/gen/third_party/flutter/frontend_server/frontend_server_tool.snapshot'); |
| final File sshConfig = globals.fs.file('$buildDirectory/ssh-keys/ssh_config'); |
| final File devFinder = globals.fs.file(argResults['dev-finder']); |
| final File platformKernelDill = globals.fs.file('$buildDirectory/flutter_runner_patched_sdk/platform_strong.dill'); |
| final File flutterPatchedSdk = globals.fs.file('$buildDirectory/flutter_runner_patched_sdk'); |
| final String packages = '$buildDirectory/dartlang/gen/$path/${name}_dart_library.packages'; |
| final String outputDill = '$buildDirectory/${name}_tmp.dill'; |
| |
| // TODO(jonahwilliams): running from fuchsia root hangs hot reload for some reason. |
| // switch to the project root directory and run from there. |
| originalWorkingDirectory = globals.fs.currentDirectory.path; |
| globals.fs.currentDirectory = path; |
| |
| if (!devFinder.existsSync()) { |
| print('Error: device-finder not found at ${devFinder.path}.'); |
| return 1; |
| } |
| if (!frontendServer.existsSync()) { |
| print( |
| 'Error: frontend_server not found at ${frontendServer.path}. This ' |
| 'Usually means you ran fx set without specifying ' |
| '--args=flutter_profile=true.' |
| ); |
| return 1; |
| } |
| |
| // Check for a package with a lib directory. |
| final String entrypoint = argResults['entrypoint'] as String; |
| String targetFile = 'lib/$entrypoint'; |
| if (!globals.fs.file(targetFile).existsSync()) { |
| // Otherwise assume the package is flat. |
| targetFile = entrypoint; |
| } |
| final String deviceName = argResults['device'] as String; |
| final List<String> command = <String>[ |
| 'attach', |
| '--module', |
| name, |
| '--target', |
| targetFile, |
| '--target-model', |
| 'flutter', // TODO(jonahwilliams): change to flutter_runner when dart SDK rolls |
| '--output-dill', |
| outputDill, |
| '--packages', |
| packages, |
| if (deviceName != null && deviceName.isNotEmpty) ...<String>['-d', deviceName], |
| if (verbose) '--verbose', |
| ]; |
| Cache.disableLocking(); // ignore: invalid_use_of_visible_for_testing_member |
| await runner.run( |
| command, |
| () => <FlutterCommand>[ |
| _FuchsiaAttachCommand(), |
| _FuchsiaDoctorCommand(), // If attach fails the tool will attempt to run doctor. |
| ], |
| verbose: verbose, |
| muteCommandLogging: false, |
| verboseHelp: false, |
| overrides: <Type, Generator>{ |
| FeatureFlags: () => const _FuchsiaFeatureFlags(), |
| DeviceManager: () => _FuchsiaDeviceManager(), |
| FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig, devFinder: devFinder), |
| Artifacts: () => OverrideArtifacts( |
| parent: CachedArtifacts( |
| fileSystem: globals.fs, |
| cache: globals.cache, |
| platform: globals.platform, |
| ), |
| frontendServer: frontendServer, |
| engineDartBinary: dartSdk, |
| platformKernelDill: platformKernelDill, |
| flutterPatchedSdk: flutterPatchedSdk, |
| ), |
| }, |
| ); |
| } |
| |
| // An implementation of [DeviceManager] that only supports fuchsia devices. |
| class _FuchsiaDeviceManager extends DeviceManager { |
| @override |
| List<DeviceDiscovery> get deviceDiscoverers => List<DeviceDiscovery>.unmodifiable(<DeviceDiscovery>[ |
| FuchsiaDevices( |
| logger: globals.logger, |
| platform: globals.platform, |
| fuchsiaWorkflow: fuchsiaWorkflow, |
| fuchsiaSdk: fuchsiaSdk, |
| ), |
| ]); |
| |
| @override |
| bool isDeviceSupportedForProject(Device device, FlutterProject flutterProject) { |
| return true; |
| } |
| } |
| |
| List<String> _extractPathAndName(String gnTarget) { |
| // Separate strings like //path/to/target:app into [path/to/target, app] |
| final int lastColon = gnTarget.lastIndexOf(':'); |
| if (lastColon < 0) { |
| throwToolExit('invalid path: $gnTarget'); |
| } |
| final String name = gnTarget.substring(lastColon + 1); |
| // Skip '//' and chop off after : |
| if ((gnTarget.length < 3) || (gnTarget[0] != '/') || (gnTarget[1] != '/')) { |
| throwToolExit('invalid path: $gnTarget'); |
| } |
| final String path = gnTarget.substring(2, lastColon); |
| return <String>[path, name]; |
| } |
| |
| class _FuchsiaDoctorCommand extends DoctorCommand { |
| @override |
| Future<FlutterCommandResult> runCommand() async { |
| Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter'; |
| return super.runCommand(); |
| } |
| } |
| |
| class _FuchsiaAttachCommand extends AttachCommand { |
| @override |
| Future<FlutterCommandResult> runCommand() async { |
| Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter'; |
| return super.runCommand(); |
| } |
| } |
| |
| class _FuchsiaFeatureFlags extends FeatureFlags { |
| const _FuchsiaFeatureFlags(); |
| |
| @override |
| bool get isLinuxEnabled => false; |
| |
| @override |
| bool get isMacOSEnabled => false; |
| |
| @override |
| bool get isWebEnabled => false; |
| |
| @override |
| bool get isWindowsEnabled => false; |
| |
| @override |
| bool get isAndroidEnabled => false; |
| |
| @override |
| bool get isIOSEnabled => false; |
| |
| @override |
| bool get isFuchsiaEnabled => true; |
| |
| @override |
| bool get isSingleWidgetReloadEnabled => false; |
| } |