From 03df0e663a20eaca2c448933341c649f5f37c2fa Mon Sep 17 00:00:00 2001 From: Jaime Wren Date: Fri, 22 Mar 2019 14:23:44 -0700 Subject: [PATCH 1/3] Some initial work for FlutterWeb apps --- src/io/flutter/run/LaunchState.java | 21 +++++- src/io/flutter/run/SdkFields.java | 9 ++- src/io/flutter/run/SdkRunConfig.java | 2 - src/io/flutter/run/daemon/FlutterApp.java | 9 +-- src/io/flutter/sdk/FlutterCommand.java | 67 ++++++++++++++++--- src/io/flutter/sdk/FlutterSdk.java | 17 +++++ .../vmService/DartVmServiceDebugProcess.java | 31 +++++---- 7 files changed, 125 insertions(+), 31 deletions(-) diff --git a/src/io/flutter/run/LaunchState.java b/src/io/flutter/run/LaunchState.java index 60c531947c..e82a8955b2 100644 --- a/src/io/flutter/run/LaunchState.java +++ b/src/io/flutter/run/LaunchState.java @@ -45,6 +45,7 @@ import io.flutter.dart.DartPlugin; import io.flutter.logging.FlutterLog; import io.flutter.logging.FlutterLogView; +import io.flutter.pub.PubRoot; import io.flutter.run.bazel.BazelRunConfig; import io.flutter.run.daemon.*; import org.jetbrains.annotations.NotNull; @@ -120,9 +121,21 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws final Project project = getEnvironment().getProject(); final FlutterDevice device = DeviceService.getInstance(project).getSelectedDevice(); + // If the device is null and the project is not a flutter web project, show a message that a device is required and return null. if (device == null) { - showNoDeviceConnectedMessage(project); - return null; + boolean isFlutterWeb = false; + final String filePath = ((SdkRunConfig)runConfig).getFields().getFilePath(); + if(filePath != null) { + final MainFile main = MainFile.verify(filePath, project).get(); + final PubRoot root = PubRoot.forDirectory(main.getAppDir()); + if (root != null) { + isFlutterWeb = FlutterUtils.declaresFlutterWeb(root.getPubspec()); + } + } + if (!isFlutterWeb) { + showNoDeviceConnectedMessage(project); + return null; + } } final FlutterApp app = myCreateAppCallback.createApp(device); @@ -147,7 +160,9 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws } } - device.bringToFront(); + if (device != null) { + device.bringToFront(); + } // Check for and display any analysis errors when we launch an app. if (env.getRunProfile() instanceof SdkRunConfig) { diff --git a/src/io/flutter/run/SdkFields.java b/src/io/flutter/run/SdkFields.java index 642768d56d..2071b1ca81 100644 --- a/src/io/flutter/run/SdkFields.java +++ b/src/io/flutter/run/SdkFields.java @@ -14,6 +14,7 @@ import com.jetbrains.lang.dart.sdk.DartConfigurable; import com.jetbrains.lang.dart.sdk.DartSdk; import io.flutter.FlutterBundle; +import io.flutter.FlutterUtils; import io.flutter.dart.DartPlugin; import io.flutter.pub.PubRoot; import io.flutter.run.daemon.FlutterDevice; @@ -128,7 +129,13 @@ public GeneralCommandLine createFlutterSdkRunCommand(@NotNull Project project, if (buildFlavor != null) { args = ArrayUtil.append(args, "--flavor=" + buildFlavor); } - final FlutterCommand command = flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args); + + final FlutterCommand command; + if(FlutterUtils.declaresFlutterWeb(root.getPubspec())) { + command = flutterSdk.flutterRunWeb(root, runMode, false); + } else { + command = flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args); + } return command.createGeneralCommandLine(project); } diff --git a/src/io/flutter/run/SdkRunConfig.java b/src/io/flutter/run/SdkRunConfig.java index 0559092841..ff6c949691 100644 --- a/src/io/flutter/run/SdkRunConfig.java +++ b/src/io/flutter/run/SdkRunConfig.java @@ -142,8 +142,6 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final RunMode mode = RunMode.fromEnv(env); final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); final LaunchState.CreateAppCallback createAppCallback = (device) -> { - if (device == null) return null; - final GeneralCommandLine command = getCommand(env, device); { // Workaround for https://github.com/flutter/flutter/issues/16766 diff --git a/src/io/flutter/run/daemon/FlutterApp.java b/src/io/flutter/run/daemon/FlutterApp.java index ebddee8186..bed11b4e39 100644 --- a/src/io/flutter/run/daemon/FlutterApp.java +++ b/src/io/flutter/run/daemon/FlutterApp.java @@ -68,7 +68,8 @@ public class FlutterApp { private final @NotNull Project myProject; private final @Nullable Module myModule; private final @NotNull RunMode myMode; - private final @NotNull FlutterDevice myDevice; + // TODO(github.com/flutter/flutter-intellij/issues/3293) myDevice is not-null for all run configurations except flutter web configurations + private final @Nullable FlutterDevice myDevice; private final @NotNull ProcessHandler myProcessHandler; private final @NotNull ExecutionEnvironment myExecutionEnvironment; private final @NotNull DaemonApi myDaemonApi; @@ -122,7 +123,7 @@ public static FlutterApp fromEnv(@NotNull ExecutionEnvironment env) { FlutterApp(@NotNull Project project, @Nullable Module module, @NotNull RunMode mode, - @NotNull FlutterDevice device, + @Nullable FlutterDevice device, @NotNull ProcessHandler processHandler, @NotNull ExecutionEnvironment executionEnvironment, @NotNull DaemonApi daemonApi, @@ -237,7 +238,7 @@ public static FlutterApp start(@NotNull ExecutionEnvironment env, @NotNull Project project, @Nullable Module module, @NotNull RunMode mode, - @NotNull FlutterDevice device, + @Nullable FlutterDevice device, @NotNull GeneralCommandLine command, @Nullable String analyticsStart, @Nullable String analyticsStop) @@ -593,7 +594,7 @@ public FlutterDevice device() { } public String deviceId() { - return myDevice.deviceId(); + return myDevice != null ? myDevice.deviceId() : ""; } public void setFlutterDebugProcess(FlutterDebugProcess flutterDebugProcess) { diff --git a/src/io/flutter/sdk/FlutterCommand.java b/src/io/flutter/sdk/FlutterCommand.java index 4953a536c4..43f48c4024 100644 --- a/src/io/flutter/sdk/FlutterCommand.java +++ b/src/io/flutter/sdk/FlutterCommand.java @@ -48,13 +48,28 @@ public class FlutterCommand { @NotNull private final List args; + // TODO(github.com/flutter/flutter-intellij/issues/3293) This is a temporary "/bin/webdev" that can be provided to test some + // webdev directly. + @Nullable + private final String localWebDevExe = "webdev"; + + private final boolean isFlutterWeb; + /** * @see FlutterSdk for methods to create specific commands. */ FlutterCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull Type type, String... args) { + this(sdk, workDir, type, false, args); + } + + /** + * @see FlutterSdk for methods to create specific commands. + */ + FlutterCommand(@NotNull FlutterSdk sdk, @Nullable VirtualFile workDir, @NotNull Type type, boolean isFlutterWeb, String... args) { this.sdk = sdk; this.workDir = workDir; this.type = type; + this.isFlutterWeb = isFlutterWeb; this.args = ImmutableList.copyOf(args); } @@ -245,23 +260,59 @@ public OSProcessHandler startProcessOrShowError(@Nullable Project project) { public GeneralCommandLine createGeneralCommandLine(@Nullable Project project) { final GeneralCommandLine line = new GeneralCommandLine(); line.setCharset(CharsetToolkit.UTF8_CHARSET); + if (isFlutterWeb) { + if (workDir != null) { + line.setWorkDirectory(workDir.getPath()); + } + if(localWebDevExe != null || !localWebDevExe.isEmpty()) { + line.setExePath(localWebDevExe); + line.addParameters("daemon"); + if(args.contains("--debug")) { + line.addParameters("--debug"); + } + if(args.contains("--hot-reload")) { + line.addParameters("--hot-reload"); + } + } else { + line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); + line.addParameters(args); + } + } + else { + line.withEnvironment(FlutterSdkUtil.FLUTTER_HOST_ENV, FlutterSdkUtil.getFlutterHostEnvValue()); - line.withEnvironment(FlutterSdkUtil.FLUTTER_HOST_ENV, FlutterSdkUtil.getFlutterHostEnvValue()); + final String androidHome = IntelliJAndroidSdk.chooseAndroidHome(project, false); + if (androidHome != null) { + line.withEnvironment("ANDROID_HOME", androidHome); + } - final String androidHome = IntelliJAndroidSdk.chooseAndroidHome(project, false); - if (androidHome != null) { - line.withEnvironment("ANDROID_HOME", androidHome); + line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); + if (workDir != null) { + line.setWorkDirectory(workDir.getPath()); + } + if (!isDoctorCommand()) { + line.addParameter("--no-color"); + } + line.addParameters(type.subCommand); + line.addParameters(args); } + return line; + } + + /** + * Creates a FlutterWeb command line to run. + */ + @NotNull + public GeneralCommandLine createFlutterWebCommandLine(@Nullable Project project) { + final GeneralCommandLine line = new GeneralCommandLine(); + line.setCharset(CharsetToolkit.UTF8_CHARSET); - line.setExePath(FileUtil.toSystemDependentName(sdk.getHomePath() + "/bin/" + FlutterSdkUtil.flutterScriptName())); if (workDir != null) { line.setWorkDirectory(workDir.getPath()); } - if (!isDoctorCommand()) { - line.addParameter("--no-color"); - } line.addParameters(type.subCommand); line.addParameters(args); + return line; } diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java index 8a4bc7893b..720c100914 100644 --- a/src/io/flutter/sdk/FlutterSdk.java +++ b/src/io/flutter/sdk/FlutterSdk.java @@ -269,6 +269,23 @@ else if (flutterLaunchMode == FlutterLaunchMode.RELEASE) { return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.ATTACH, args.toArray(new String[]{ })); } + public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode, boolean hotReload) { + // flutter packages pub global run webdev serve [--debug] [--hot-reload] + final List args = new ArrayList<>(); + args.add("global"); + args.add("run"); + args.add("webdev"); + args.add("daemon"); + if (mode == RunMode.DEBUG) { + //args.add("--debug"); + //args.add("--start-paused"); + } + if (hotReload) { + args.add("--hot-reload"); + } + return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PACKAGES_PUB, true, args.toArray(new String[]{ })); + } + public FlutterCommand flutterRunOnTester(@NotNull PubRoot root, @NotNull String mainPath) { final List args = new ArrayList<>(); args.add("--machine"); diff --git a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java index 2c25e7bdd4..00cc5bc4cc 100644 --- a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java +++ b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java @@ -41,6 +41,7 @@ import io.flutter.FlutterBundle; import io.flutter.FlutterUtils; import io.flutter.run.FlutterLaunchMode; +import io.flutter.run.daemon.FlutterApp; import io.flutter.server.vmService.frame.DartVmServiceEvaluator; import io.flutter.server.vmService.frame.DartVmServiceStackFrame; import io.flutter.server.vmService.frame.DartVmServiceSuspendContext; @@ -262,17 +263,21 @@ public void scheduleConnect() { // because "flutter run" has already connected to it. final VmService vmService; final VmOpenSourceLocationListener vmOpenSourceLocationListener; - try { - vmService = VmService.connect(url); - vmOpenSourceLocationListener = VmOpenSourceLocationListener.connect(url); - } - catch (IOException | RuntimeException e) { - onConnectFailed("Failed to connect to the VM observatory service at: " + url + "\n" - + e.toString() + "\n" + - formatStackTraces(e)); - return; + // TODO(github.com/flutter/flutter-intellij/issues/3293) The following check disables the WebSocket connection for all FlutterWeb + // run configurations. + if (myConnector instanceof FlutterApp && (((FlutterApp)myConnector).device() != null)) { + try { + vmService = VmService.connect(url); + vmOpenSourceLocationListener = VmOpenSourceLocationListener.connect(url); + } + catch (IOException | RuntimeException e) { + onConnectFailed("Failed to connect to the VM observatory service at: " + url + "\n" + + e.toString() + "\n" + + formatStackTraces(e)); + return; + } + onConnectSucceeded(vmService, vmOpenSourceLocationListener); } - onConnectSucceeded(vmService, vmOpenSourceLocationListener); }); } @@ -391,7 +396,7 @@ public void runToPosition(@NotNull XSourcePosition position, @Nullable XSuspendC public void isolateSuspended(@NotNull final IsolateRef isolateRef) { final String id = isolateRef.getId(); - assert(!mySuspendedIsolateIds.containsKey(id)); + assert (!mySuspendedIsolateIds.containsKey(id)); if (!mySuspendedIsolateIds.containsKey(id)) { mySuspendedIsolateIds.put(id, new CompletableFuture<>()); } @@ -406,10 +411,10 @@ public CompletableFuture whenIsolateResumed(String isolateId) { if (future == null) { // Isolate wasn't actually suspended. return CompletableFuture.completedFuture(null); - } else { + } + else { return future; } - } public boolean isIsolateAlive(@NotNull final String isolateId) { From fa529c354166f8e898682e4e89128fdffc1e8dfd Mon Sep 17 00:00:00 2001 From: Jaime Wren Date: Tue, 26 Mar 2019 10:28:28 -0700 Subject: [PATCH 2/3] comments from @jacob314 --- src/io/flutter/run/SdkFields.java | 13 +++++-------- src/io/flutter/run/SdkRunConfig.java | 3 +++ src/io/flutter/run/daemon/FlutterApp.java | 4 ++++ src/io/flutter/sdk/FlutterSdk.java | 11 +++-------- .../server/vmService/DartVmServiceDebugProcess.java | 7 ++++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/io/flutter/run/SdkFields.java b/src/io/flutter/run/SdkFields.java index 2071b1ca81..3c39cad0d7 100644 --- a/src/io/flutter/run/SdkFields.java +++ b/src/io/flutter/run/SdkFields.java @@ -125,17 +125,14 @@ public GeneralCommandLine createFlutterSdkRunCommand(@NotNull Project project, throw new ExecutionException("Entrypoint isn't within a Flutter pub root"); } - String[] args = additionalArgs == null ? new String[]{} : additionalArgs.split(" "); + String[] args = additionalArgs == null ? new String[]{ } : additionalArgs.split(" "); if (buildFlavor != null) { args = ArrayUtil.append(args, "--flavor=" + buildFlavor); } - final FlutterCommand command; - if(FlutterUtils.declaresFlutterWeb(root.getPubspec())) { - command = flutterSdk.flutterRunWeb(root, runMode, false); - } else { - command = flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args); - } + final FlutterCommand command = FlutterUtils.declaresFlutterWeb(root.getPubspec()) ? + flutterSdk.flutterRunWeb(root, runMode) : + flutterSdk.flutterRun(root, main.getFile(), device, runMode, flutterLaunchMode, project, args); return command.createGeneralCommandLine(project); } @@ -157,7 +154,7 @@ public GeneralCommandLine createFlutterSdkAttachCommand(@NotNull Project project throw new ExecutionException("Entrypoint isn't within a Flutter pub root"); } - String[] args = additionalArgs == null ? new String[]{} : additionalArgs.split(" "); + String[] args = additionalArgs == null ? new String[]{ } : additionalArgs.split(" "); if (buildFlavor != null) { args = ArrayUtil.append(args, "--flavor=" + buildFlavor); } diff --git a/src/io/flutter/run/SdkRunConfig.java b/src/io/flutter/run/SdkRunConfig.java index ff6c949691..c1948dfb0e 100644 --- a/src/io/flutter/run/SdkRunConfig.java +++ b/src/io/flutter/run/SdkRunConfig.java @@ -142,6 +142,9 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final RunMode mode = RunMode.fromEnv(env); final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); final LaunchState.CreateAppCallback createAppCallback = (device) -> { + // Up until the FlutterWeb support, device was checked for null and returned. The device can only be null if this is a FlutterWeb + // execution, this expecation is checked elsewhere. + final GeneralCommandLine command = getCommand(env, device); { // Workaround for https://github.com/flutter/flutter/issues/16766 diff --git a/src/io/flutter/run/daemon/FlutterApp.java b/src/io/flutter/run/daemon/FlutterApp.java index bed11b4e39..7ff3c0e748 100644 --- a/src/io/flutter/run/daemon/FlutterApp.java +++ b/src/io/flutter/run/daemon/FlutterApp.java @@ -597,6 +597,10 @@ public String deviceId() { return myDevice != null ? myDevice.deviceId() : ""; } + public boolean isWebDev() { + return myDevice == null; + } + public void setFlutterDebugProcess(FlutterDebugProcess flutterDebugProcess) { myFlutterDebugProcess = flutterDebugProcess; myFlutterLog.setFlutterApp(this); diff --git a/src/io/flutter/sdk/FlutterSdk.java b/src/io/flutter/sdk/FlutterSdk.java index 720c100914..270506af8d 100644 --- a/src/io/flutter/sdk/FlutterSdk.java +++ b/src/io/flutter/sdk/FlutterSdk.java @@ -269,20 +269,15 @@ else if (flutterLaunchMode == FlutterLaunchMode.RELEASE) { return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.ATTACH, args.toArray(new String[]{ })); } - public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode, boolean hotReload) { + public FlutterCommand flutterRunWeb(@NotNull PubRoot root, @NotNull RunMode mode) { // flutter packages pub global run webdev serve [--debug] [--hot-reload] final List args = new ArrayList<>(); args.add("global"); args.add("run"); args.add("webdev"); args.add("daemon"); - if (mode == RunMode.DEBUG) { - //args.add("--debug"); - //args.add("--start-paused"); - } - if (hotReload) { - args.add("--hot-reload"); - } + // TODO After debug is supported by webdev, this should be modified to check for debug and add any additional needed flags: + // i.e. if (mode == RunMode.DEBUG) { args.add("--debug"); } return new FlutterCommand(this, root.getRoot(), FlutterCommand.Type.PACKAGES_PUB, true, args.toArray(new String[]{ })); } diff --git a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java index 00cc5bc4cc..117cc1baaf 100644 --- a/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java +++ b/src/io/flutter/server/vmService/DartVmServiceDebugProcess.java @@ -263,9 +263,10 @@ public void scheduleConnect() { // because "flutter run" has already connected to it. final VmService vmService; final VmOpenSourceLocationListener vmOpenSourceLocationListener; - // TODO(github.com/flutter/flutter-intellij/issues/3293) The following check disables the WebSocket connection for all FlutterWeb - // run configurations. - if (myConnector instanceof FlutterApp && (((FlutterApp)myConnector).device() != null)) { + // TODO(github.com/flutter/flutter-intellij/issues/3293, github.com/dart-lang/webdev/issues/233) The following check disables the + // WebSocket connection for all FlutterWeb run configurations, for some reason the WebSocket port can't be connected to, see listed + // issue above. + if (myConnector instanceof FlutterApp && (!((FlutterApp)myConnector).isWebDev())) { try { vmService = VmService.connect(url); vmOpenSourceLocationListener = VmOpenSourceLocationListener.connect(url); From bcb344ff3781f487746b8d284de7bfb87b232ab5 Mon Sep 17 00:00:00 2001 From: Jaime Wren Date: Wed, 27 Mar 2019 12:46:08 -0700 Subject: [PATCH 3/3] 2 more nit comments from @jacob314 --- src/io/flutter/run/SdkRunConfig.java | 4 ++-- src/io/flutter/run/daemon/FlutterApp.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/io/flutter/run/SdkRunConfig.java b/src/io/flutter/run/SdkRunConfig.java index c1948dfb0e..5f4c32b4e1 100644 --- a/src/io/flutter/run/SdkRunConfig.java +++ b/src/io/flutter/run/SdkRunConfig.java @@ -142,8 +142,8 @@ public LaunchState getState(@NotNull Executor executor, @NotNull ExecutionEnviro final RunMode mode = RunMode.fromEnv(env); final Module module = ModuleUtilCore.findModuleForFile(mainFile.getFile(), env.getProject()); final LaunchState.CreateAppCallback createAppCallback = (device) -> { - // Up until the FlutterWeb support, device was checked for null and returned. The device can only be null if this is a FlutterWeb - // execution, this expecation is checked elsewhere. + // Up until the FlutterWeb support, device was checked for null and returned. The device + // can only be null if this is a FlutterWeb execution, this expecation is checked elsewhere. final GeneralCommandLine command = getCommand(env, device); { diff --git a/src/io/flutter/run/daemon/FlutterApp.java b/src/io/flutter/run/daemon/FlutterApp.java index 7ff3c0e748..e4abf22d3e 100644 --- a/src/io/flutter/run/daemon/FlutterApp.java +++ b/src/io/flutter/run/daemon/FlutterApp.java @@ -597,6 +597,7 @@ public String deviceId() { return myDevice != null ? myDevice.deviceId() : ""; } + // TODO this should be a temporary hack until there is some mocked out "browser" device public boolean isWebDev() { return myDevice == null; }