Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the dotnet run command discovery protocol to be settable by a Target instead of just Evaluation #42155

Closed
baronfel opened this issue Jul 15, 2024 · 13 comments · Fixed by #42240
Labels
Area-Run Issues relating to `dotnet run` Document for new feature
Milestone

Comments

@baronfel
Copy link
Member

baronfel commented Jul 15, 2024

Is your feature request related to a problem? Please describe.

When running a project, dotnet run has a protocol that it implements to determine how to run the project:

  1. Detect the project that should be run
  2. Determine the 'launch settings' and profile that will be applied to the project
  3. Build the project if required
  4. Determine the RunCommand/RunArguments/RunWorkingDirectory for the project to create an executable ICommand
  5. Apply 'launch settings' to the ICommand
  6. Invoke the ICommand

In a graphic this looks like:

sequenceDiagram
  participant dotnet as dotnet CLI
  participant project as myproj.proj
  participant exe as RunCommand
  dotnet->>dotnet: detect project to run
  dotnet->>dotnet: read launch settings from file and pick launch profile
  dotnet->>project: build project (if required)
  dotnet->>project: evaluate project to get run settings
  dotnet->>exe: create process to spawn from run settings
  dotnet->>exe: apply launch profile to process
  dotnet->>exe: spawn process
  dotnet->>exe: run process to completion
Loading

This process relies on MSBuild evaluation to determine the RunCommand/RunArguments/RunWorkingDirectory, which means that these values can only be set during evaluation - no code or logic can run to influence their values. This can be highly limiting and means that use cases that need to run some dynamic logic to determine a value to put in the RunWorkingDirectory, for example, cannot be safely run.

Describe the solution you'd like

We should have an opt-in protocol that would allow dotnet run to invoke a target to get the RunCommand/RunArguments/RunWorkingDirectory data. This target should be public and documented and would update step 4 above as follows:

  1. Determine the RunCommand/RunArguments/RunWorkingDirectory for the project to create an executable ICommand
    a. if the project supports a ComputeRunCommand Target, then run that target and read the RunCommand/RunArguments/RunWorkingDirectory after target execution
    b. otherwise read the same properties just after evaluation, as is done today
    c. when called, this target would be passed all of the same MSBuild properties as applied to the implicit build in step 3

In a graphic this would look like:

sequenceDiagram
  participant dotnet as dotnet CLI
  participant project as myproj.proj
  participant exe as RunCommand
  dotnet->>dotnet: detect project to run
  dotnet->>dotnet: read launch settings from file and pick launch profile
  dotnet->>project: build project (if required)
  dotnet->>project: evaluate project
  dotnet->>project: invoke target to get run settings
  project->>dotnet: return run settings
  dotnet->>exe: create process to spawn from run settings
  dotnet->>exe: apply launch profile to process
  dotnet->>exe: spawn process
  dotnet->>exe: run process to completion
Loading

Run Command Protocol

This new target would need to define its protocol

  • Name
  • Dependencies - what is a reasonable dependency chain for this Target? How do projects like MAUI change this dependency chain?
  • Outputs - should the target set properties, or should we take the opportunity to make a different representation

Opting-in

Projects would need to opt into the new behavior - the SDK currently computes the values here and so could change this for SDK-style projects as a default. Should there be a property-based opt-in, or should we just start doing this wholesale since the CLI and SDK targets change in lockstep?

Use cases like Azure Functions or MAUI may want to override the generation and so could override the target or set up a BeforeTargets, etc.

Compatibility

  • The MSBuild Run target defined in the SDK should have a dependency on the new target if defined.
  • The dotnet watch command currently uses the RunCommand/RunArguments/RunWorkingDirectory as inputs to GenerateWatchList - it will need to become aware of this target if necessary as well.
  • We should add a new ProjectCapability signalling that a Project uses this new mechanism, so that IDEs have a flag for behavior.
  • The new Target could be integrated into the Design-Time Build dependency chain.

Binlogs

We would to to ensure that if a binlog was requested that the configured binlog logger also applied to this new target, not just the implicit build + project evaluation for Run argument discovery

Additional context

@captainsafia for Aspire
@fabiocav for Functions
@jonathanpeppers for MAUI
@danroth27 for web
@tmat for watch
@tmeschter for project-system

@baronfel baronfel added the Area-Run Issues relating to `dotnet run` label Jul 15, 2024
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Request triage from a team member label Jul 15, 2024
@baronfel baronfel added needs team triage Requires a full team discussion and removed untriaged Request triage from a team member labels Jul 15, 2024
@jonathanpeppers
Copy link
Member

Is this similar to what I filed here:

@baronfel
Copy link
Member Author

This would be a step towards that, but not entirely that - making the command the SDK would invoke more pluggable without yielding the entire process-invocation pipeline to MSBuild (which might have constraints on e.g. terminal layout, etc.).

If we had this issue implemented (which we may do based on needs from Azure Functions) as a small extension of the current 'run protocol' would MAUI be able to use this? Would there be certain targets that MAUI would want to run as part of generating the 'run command' that would influence what the Target dependency chain should be?

@jonathanpeppers
Copy link
Member

Ok, slightly misread. Would it give a way to know the list of MSBuild properties passed to dotnet run? I think one of the main issues, is we lose these.

@jonathanpeppers
Copy link
Member

Ok, so I do think this feature could possibly solve some problems for MAUI (mobile platforms):

  • Our new target can run and use any MSBuild properties passed in for dotnet run
  • Our target ensures the app is built and deployed
  • Android: $(RunCommand) becomes just the adb command to launch the app
  • iOS: $(RunCommand) becomes just the mlaunch command to launch the app

/cc @rolfbjarne

@baronfel
Copy link
Member Author

Ok, slightly misread. Would it give a way to know the list of MSBuild properties passed to dotnet run? I think one of the main issues, is we lose these.

For clarity - I've added to the spec above that calling this new target would be passed the same set of properties that were passed to the implicit build in step 3.

@danroth27
Copy link
Member

@joj @sayedihashimi @javiercn I believe we're also interested in this functionality for running ASP.NET Core + JSPS projects from the command line instead of using the SpaProxy stuff, correct?

@marcpopMSFT marcpopMSFT added this to the 9.0.1xx milestone Jul 16, 2024
@tmeschter
Copy link
Contributor

We'll need to keep VS and C# Dev Kit in mind as they would also need to run the target to pick up the expected properties. The most straightforward thing to do would be to roll the ComputeRunCommand target into the existing design-time build and collect the results from that; then they would already be available to the launch handler on F5/Ctrl+F5 without having to run another build.

It would help to have a ProjectCapability indicating that the ComputeRunCommand target is present and should be called. That is, if the capability isn't defined we get the properties from the evaluation model; if it is defined we run the target and get the properties from that.

@baronfel
Copy link
Member Author

Thanks @tmeschter - all of that sounds good to me. I'll update the description to include those notes.

@sayedihashimi
Copy link
Member

@joj @sayedihashimi @javiercn I believe we're also interested in this functionality for running ASP.NET Core + JSPS projects from the command line instead of using the SpaProxy stuff, correct?

Yes, we would like to add support for dotnet run with the JSPS projects.

@rolfbjarne
Copy link
Member

We'll need to keep VS and C# Dev Kit in mind as they would also need to run the target to pick up the expected properties. The most straightforward thing to do would be to roll the ComputeRunCommand target into the existing design-time build and collect the results from that; then they would already be available to the launch handler on F5/Ctrl+F5 without having to run another build.

Note that the run command might change depending on external factors (developer plugs in a new device for instance).

The ComputeRunCommand target will also need to be passed the currently selected device in the IDE (I suppose this is something the MAUI extension in VSCode needs to do).

@jonathanpeppers
Copy link
Member

Right now, the MAUI extension in VSCode just runs dotnet build -t:Run for mobile. With this change, they would be able to use dotnet run instead, passing in any MSBuild properties required. It sounds like @tmeschter is saying there is some different behavior for console apps (and maybe web apps), so would be nice if that could be unified.

@baronfel baronfel added Document for new feature and removed needs team triage Requires a full team discussion labels Aug 23, 2024
@baronfel
Copy link
Member Author

FYI @jonathanpeppers, @sayedihashimi, @danroth27 and others this just merged and will be available starting in RC2. Nightlies for that starting tonight should enable use of the feature. In the simplest form, you'd create a Target with BeforeTargets="ComputeRunArguments" and set any/all of RunCommand, RunArguments and RunWorkingDirectory properties to whatever you need, as well as coordinate the execution of any other setup logic. There's a short worked example in the PR you can take a look at.

@danroth27
Copy link
Member

Nice! @joj @sayedihashimi Can you take a look at whether this new functionality enables moving the VS SPA templates to use this functionality instead of SpaProxy?

@javiercn FYI.

jonathanpeppers added a commit to dotnet/android that referenced this issue Oct 30, 2024
…target

Context: dotnet/sdk#42155
Context: dotnet/sdk#42240
Fixes: dotnet/sdk#31253

The .NET SDK has introduced a new `ComputeRunArguments` MSBuild target
that allows you to set `$(RunCommand)` and `$(RunArguments)` in a more
dynamic way.

So, on Android:

* `ComputeRunArguments` depends on `Install`, so the app is deployed,
  the `<FastDeploy/>` MSBuild target runs, etc.

* `$(RunCommand)` is a path to `adb`

* `$(RunArguments)` is an `shell am start` command to launch the main
  activity.

The new implementation also allows us to use the `-p` parameter with
`dotnet run`, such as:

    dotnet run -bl -p:AdbTarget=-d

This will pass `-d` to `adb`, which allows you to select an attached
device if an emulator is running.

Previously, we had no way to pass `-p` arguments to `dotnet run`.
jonpryor pushed a commit to dotnet/android that referenced this issue Dec 2, 2024
Context: dotnet/sdk#31253
Context: dotnet/sdk#42155
Context: dotnet/sdk#42240

The .NET SDK has introduced a new `ComputeRunArguments` MSBuild
target that allows you to set `$(RunCommand)` and `$(RunArguments)`
in a more dynamic way.

So, on Android:

  * `ComputeRunArguments` depends on `Install`, so the app is
    deployed, the `<FastDeploy/>` MSBuild target runs, etc.

  * `$(RunCommand)` is a path to `adb`

  * `$(RunArguments)` is an `shell am start` command to launch the main
    activity.

The new implementation also allows us to use the `-p` parameter with
`dotnet run`, such as:

	dotnet run -bl -p:AdbTarget=-d

This will pass `-d` to `adb`, which allows you to select an attached
device if an emulator is running.

Previously, we had no way to pass `-p` arguments to `dotnet run`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Run Issues relating to `dotnet run` Document for new feature
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants