|
| 1 | +# `dotnet run` for .NET MAUI Scenarios |
| 2 | + |
| 3 | +The current state of `dotnet run` for .NET MAUI projects is summarized |
| 4 | +by this issue from 2021: |
| 5 | + |
| 6 | +* [Figure out 'dotnet run' CLI experience for iOS and Android](https://github.com/dotnet/xamarin/issues/26) |
| 7 | + |
| 8 | +The pain points being: |
| 9 | + |
| 10 | +* iOS and Android use different properties to select a device, |
| 11 | + emulator, or simulator. It is difficult for developers to make this |
| 12 | + selection as you need to run platform-specific commands along with |
| 13 | + complex MSBuild properties. |
| 14 | + |
| 15 | +* Each platform has different behavior, regards to displaying console |
| 16 | + output, etc. |
| 17 | + |
| 18 | +* Using an MSIX with WindowsAppSDK doesn't support `dotnet run` at all, |
| 19 | + relying completely on IDEs like Visual Studio. |
| 20 | + |
| 21 | +* `dotnet run` does not have a concept of a "deploy" step. |
| 22 | + |
| 23 | +This has become more relevant in the AI era, as someone is going to |
| 24 | +expect AIs in "agent mode" to build and run their app. If the |
| 25 | +command-line options are difficult, AIs will fail just as badly as |
| 26 | +humans do today. |
| 27 | + |
| 28 | +## The `dotnet run` Pipeline |
| 29 | + |
| 30 | +These are the high-level steps during `dotnet run`, that we would like |
| 31 | +to make extensible for .NET MAUI (and future) scenarios. |
| 32 | + |
| 33 | +* `restore`: unchanged |
| 34 | + |
| 35 | +* "Pre-run evaluation" |
| 36 | + |
| 37 | + * If the project is multi-targeted, containing the |
| 38 | + `$(TargetFrameworks)` property _and_ that property has more than |
| 39 | + one item in it, and `-f` was not supplied... |
| 40 | + |
| 41 | + * Prompt the user to select from a list of the |
| 42 | + `$(TargetFrameworks)` |
| 43 | + |
| 44 | + * Non-interactive mode will give a friendly error message, |
| 45 | + suggesting to supply the `-f` property, listing available target |
| 46 | + frameworks in the project. |
| 47 | + |
| 48 | + * Once a `$(TargetFramework)` is selected, either from previous |
| 49 | + steps or `-f`... |
| 50 | + |
| 51 | + * If a `ComputeAvailableDevices` MSBuild target is available, provided by |
| 52 | + the iOS or Android workload, etc. ... |
| 53 | + |
| 54 | + * Call the MSBuild target, which returns a list of `@(Devices)` items... |
| 55 | + |
| 56 | +```xml |
| 57 | +<ItemGroup> |
| 58 | + <!-- Android examples --> |
| 59 | + <Devices Include="emulator-5554" Description="Pixel 7 - API 35" Type="Emulator" Status="Offline" /> |
| 60 | + <Devices Include="emulator-5555" Description="Pixel 7 - API 36" Type="Emulator" Status="Online" /> |
| 61 | + <Devices Include="0A041FDD400327" Description="Pixel 7 Pro" Type="Device" Status="Online" /> |
| 62 | + <!-- iOS examples --> |
| 63 | + <Devices Include="94E71AE5-8040-4DB2-8A9C-6CD24EF4E7DE" Description="iPhone 11 - iOS 18.6" Type="Simulator" Status="Shutdown" /> |
| 64 | + <Devices Include="FBF5DCE8-EE2B-4215-8118-3A2190DE1AD7" Description="iPhone 14 - iOS 26.0" Type="Simulator" Status="Booted" /> |
| 65 | + <Devices Include="23261B78-1E31-469C-A46E-1776D386EFD8" Description="My iPhone 13" Type="Device" Status="Unavailable" /> |
| 66 | + <Devices Include="AF40CC64-2CDB-5F16-9651-86BCDF380881" Description="My iPhone 15" Type="Device" Status="Paired" /> |
| 67 | +</ItemGroup> |
| 68 | +``` |
| 69 | + |
| 70 | +_NOTE: each workload can decide which metadata values for `%(Type)` |
| 71 | +and `%(Status)` are useful, filtering offline devices, etc. The output |
| 72 | +above would be analogous to running `adb devices`, `xcrun simctl list |
| 73 | +devices`, or `xcrun devicectl list devices`._ |
| 74 | + |
| 75 | +* Continuing on... |
| 76 | + |
| 77 | + * Prompt the user to select from this list of devices, emulators, |
| 78 | + or simulators. |
| 79 | + |
| 80 | + * Non-interactive mode will error, suggesting to supply the |
| 81 | + `--device` switch. Listing the options returned by the |
| 82 | + `ComputeAvailableDevices` MSBuild target. |
| 83 | + |
| 84 | +* `build`: unchanged, but is passed `-p:Device`. |
| 85 | + |
| 86 | +* `deploy` |
| 87 | + |
| 88 | + * If a `DeployToDevice` MSBuild target is available, provided by the |
| 89 | + iOS or Android workload, etc. |
| 90 | + |
| 91 | + * Call the MSBuild target, passing in the identifier for the selected |
| 92 | + `-p:Device` global MSBuild property. |
| 93 | + |
| 94 | + * This step needs to run, even with `--no-build`, as you may have |
| 95 | + selected a different device. |
| 96 | + |
| 97 | +* `ComputeRunArguments`: unchanged, but is passed `-p:Device`. |
| 98 | + |
| 99 | +* `run`: unchanged. `ComputeRunArguments` should have set a valid |
| 100 | + `$(RunCommand)` and `$(RunArguments)` using the value supplied by |
| 101 | + `-p:Device`. |
| 102 | + |
| 103 | +## New `dotnet run` Command-line Switches |
| 104 | + |
| 105 | +So far, it feels like no new subcommand is needed. In interactive |
| 106 | +mode, `dotnet run` will now prompt to select a `$(TargetFramework)` |
| 107 | +for all multi-targeted projects. Platform-specific projects like |
| 108 | +Android, iOS, etc. will prompt for device selection. |
| 109 | + |
| 110 | +`dotnet run --list-devices` will: |
| 111 | + |
| 112 | +* Prompt for `$(TargetFramework)` for multi-targeted projects just |
| 113 | + like when `--list-devices` is omitted. |
| 114 | + |
| 115 | + * If there is a single `$(TargetFramework)`, skip to the next step. |
| 116 | + |
| 117 | +* Call `ComputeAvailableDevices` if the MSBuild target exists, just |
| 118 | + like when `--list-devices` is omitted. |
| 119 | + |
| 120 | + * List the available targets by name, a unique identifier, and an |
| 121 | + optional status of the device. |
| 122 | + |
| 123 | + * Print a friendly message that says how to run `dotnet run` with |
| 124 | + the new `--device` switch. |
| 125 | + |
| 126 | + * If `ComputeAvailableDevices` does not exist in the project |
| 127 | + (workload), it can print a friendly message and exit. |
| 128 | + |
| 129 | +* `dotnet run --list-devices` will then basically exit early, never |
| 130 | + running any build, deploy, `ComputeRunArguments`, or run steps. |
| 131 | + |
| 132 | +A new `--device` switch will: |
| 133 | + |
| 134 | +* bypass the device-selection portion of the `run` workflow described above |
| 135 | + |
| 136 | +* Pass in the `-p:Device` global MSBuild property to all build, |
| 137 | + deploy, `ComputeRunArguments`, or run steps. |
| 138 | + |
| 139 | +* The iOS and Android workloads will know how to interpret `$(Device)` |
| 140 | + to select an appropriate device, emulator, or simulator. |
| 141 | + |
| 142 | +## What about Launch Profiles? |
| 143 | + |
| 144 | +The iOS and Android workloads ignore all |
| 145 | +`Properties/launchSettings.json` files and do nothing with them. |
| 146 | + |
| 147 | +WindowsAppSDK requires a launch profile to select between "packaged" |
| 148 | +MSIX and an "unpackaged" .exe, and so we currently provide this in the |
| 149 | +`dotnet new maui` project template: |
| 150 | + |
| 151 | +```json |
| 152 | +{ |
| 153 | + "profiles": { |
| 154 | + "Windows Machine": { |
| 155 | + "commandName": "Project", |
| 156 | + "nativeDebugging": false |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +Or if you want to be "packaged": |
| 163 | + |
| 164 | +```json |
| 165 | +{ |
| 166 | + "profiles": { |
| 167 | + "Windows Machine": { |
| 168 | + "commandName": "MsixPackage", |
| 169 | + "nativeDebugging": false |
| 170 | + } |
| 171 | + } |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +Launch profiles don't immediately look useful for iOS or Android, as |
| 176 | +the identifier you'd put in here would be different per developer -- |
| 177 | +you wouldn't want the value saved in source control. You could put a |
| 178 | +generic selection like device vs emulator/simulator, but that isn't |
| 179 | +addressing the problem we are interested in. Full launch profile |
| 180 | +support for .NET MAUI projects may be interesting to address in the |
| 181 | +future. |
| 182 | + |
| 183 | +## iOS and Android workload behavior |
| 184 | + |
| 185 | +We will work to align as much behavior as possible between the |
| 186 | +platforms. Most of this work will happen in the dotnet/android and |
| 187 | +dotnet/macios repo, and would be orthogonal to the changes in the .NET |
| 188 | +SDK. |
| 189 | + |
| 190 | +## macOS and MacCatalyst Behavior |
| 191 | + |
| 192 | +`dotnet run` "just works" on macOS Desktop, because `$(RunCommand)` |
| 193 | +can launch the app directly -- similar to console apps. |
| 194 | + |
| 195 | +## WindowsAppSDK Behavior |
| 196 | + |
| 197 | +Running a `dotnet new maui` project works today because of the default |
| 198 | +launch profile in "unpackaged" mode: |
| 199 | + |
| 200 | +```dotnetcli |
| 201 | +> dotnet run -f net10.0-windows10.0.19041.0 |
| 202 | +Using launch settings from D:\src\hellomaui\Properties\launchSettings.json... |
| 203 | +``` |
| 204 | + |
| 205 | +To improve the behavior for "packaged" / MSIX, we could: |
| 206 | + |
| 207 | +* Implement the `DeployToDevice` MSBuild target. |
| 208 | + |
| 209 | +* `ComputeAvailableDevices` is not needed. |
| 210 | + |
| 211 | +* Implement the `ComputeRunArguments` MSBuild target. |
| 212 | + |
| 213 | +In the future, we can either add this logic into the .NET MAUI |
| 214 | +workload or WindowsAppSDK itself. |
| 215 | + |
| 216 | +## Other frameworks: Avalonia, Uno, MonoGame, etc. |
| 217 | + |
| 218 | +When these frameworks run on iOS or Android, they are basically using |
| 219 | +the `ios` and `android` workloads _without_ .NET MAUI. Any iOS or |
| 220 | +Android-specific behavior would apply to these project types in the |
| 221 | +same way a `dotnet new android` or `dotnet new ios` project template |
| 222 | +would. |
0 commit comments