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

Need to rethink GetRunInformation as a Target #98

Closed
eerhardt opened this issue Aug 26, 2016 · 20 comments
Closed

Need to rethink GetRunInformation as a Target #98

eerhardt opened this issue Aug 26, 2016 · 20 comments

Comments

@eerhardt
Copy link
Member

Today GetRunInformation is a Target that returns the information necessary to "run" the project/app. This is intended to be a common place for VS, the CLI, and any other development environment to obtain this information, without having to code the logic in each development environment.

However, in order to get this information, that requires invoking the target (basically executing a 'design-time build').

We should reconsider this approach. A few options that have been raised:

  1. Use static properties in the .csproj
  2. The "Build" target could generate a "run.rsp" file in the obj directory that contains this information. VS and the CLI could read this file.
    • One minor drawback of this approach is that development environments still need to obtain information from the .csproj - specifically where the obj directory is. Which means there are two places that need to be interpreted, instead of just one.

Another issue that needs to be resolved is "which dotnet.exe host should be used when invoking .NET Core apps?"

One option here is the “host development environment” pass in what it thinks the path to the host should be. So the CLI can pass the path to the “dotnet.exe” that was invoked for “dotnet run”. And VS can pass in a specific host path, if it wants. If no host path is specified, then we read it from the $(PATH) environment variable. We could/should also have properties that the developer can set in their .csproj to use a specific host as well.

/cc @BillHiebert @piotrpMSFT @davkean @srivatsn

@eerhardt
Copy link
Member Author

Another piece of information to add here is the debugger type: desktop or Core.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 6, 2016

We've decided to go with a "runInformation.json" file that gets generated during the "Build" of a project. dotnet run, VS, and any other IDE can read this file to get all the information to know how to invoke the project.

Here is my "design" for this file:

{
  "command": "C:\\Program Files\\dotnet\\dotnet.exe",
  "arguments": "exec F:\\source\\MyApp\\bin\\Debug\\netcoreapp1.0\\MyApp.dll",
  "workingDirectory": " F:\\source\\MyApp\\bin\\Debug\\netcoreapp1.0",
  "debugEngines": "{2E36F1D4-B23C-435D-AB41-18E608940038}",
  "environmentVariables": {
    "Name1": "Value1",
    "Name2": "Value2"
  }
}

There will be a new MSBuild property that will hold the path to this file named $(RunInformationFilePath). Note that this property will depend on $(IntermediateOutputPath), so $(Configuration) and $(TargetFramework) will need to be set (or defaulted) when trying to evaluate the $(RunInformationFilePath) property.

Questions

  1. Is "debugEngines" a "VS-only" concept? How do other IDEs (like VS Code) interact with this? Do they need a mapping from VS's debug engines to their own?
  2. We still don't have a good way to resolve the .NET Core host (dotnet.exe) inside of MSBuild. My current thinking is that the CLI and VS can set an MSBuild property $(DotNetCoreHostPath) if they want a specific path used. Users can override this property, if they know that they want a different/specific host. If no one sets the property, the file will just contain "dotnet.exe" (or "dotnet" on non-Windows), which will resolve the host from the current PATH.
  3. Do people think the environment variables section is necessary? My thought here is that you can set this info in VS, and when you F5 in VS you expect to get these variables set. I would also expect to get these variables set when I dotnet run the same project.

@davkean
Copy link
Member

davkean commented Sep 6, 2016

Why was a external file decided? I'll prefer MSBuild properties representing this information.

@davkean
Copy link
Member

davkean commented Sep 6, 2016

@BillHiebert Thoughts on this? How does this fit in with the unification with VS code/Xamarin?

@eerhardt
Copy link
Member Author

eerhardt commented Sep 6, 2016

The thinking behind an external file was that it had the following advantages:

  1. it can be accessed outside of msbuild
  2. if we generate it during "build", we have more info available to us than what is statically available. For example, if we wanted to solve question AssemblyInfo: Determine which properties will be generated and their msbuild mappings #2 above by writing a "ResolveNetCoreHost" MSBuild Task, we could.
  3. It also gives other project types a lot of flexibility on when/how to generate it.
    • Examples of other project types: F#, Mono, UWP, etc. Just because C# .NET Core apps can define the information statically, doesn't mean every project type will be able to in the future.
    • The thinking here is that this $(RuntimeInformationFilePath) property and JSON structure will be the contract for any project who wants to be dotnet run-able.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 6, 2016

BTW - @davkean, if you have better advantages to using all MSBuild properties to represent this information, I can easily be swayed. I just couldn't think of any myself.

@BillHiebert
Copy link
Contributor

VS has support for a launchSettings.json file which contains similar information: like environment variables and working directory. I guess they would replace the ones specified n the generated file.
I am not sure how VS Code does build & run, but I having a separate file is probably easier as they don't need to host msbuild to get to this information.
XS has a completely different implementation. They are introducing "RuntimeConfigurations" which work similar to Config\Platfrom in that a set of properties are conditioned on this value.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 6, 2016

/cc @DustinCampbell - to help with the questions on VS Code.

@blackdwarf
Copy link

blackdwarf commented Sep 6, 2016

@eerhardt where would this external file live? obj? What would be the user semantics of this file? I assume it is an implementation detail that the user doesn't have to worry about and can ignore and shouldn't touch?

@eerhardt
Copy link
Member Author

eerhardt commented Sep 6, 2016

@eerhardt where would this external file live? obj?

It defaults to be $(IntermediateOutputPath)\runInformation.json. And $(IntermediateOutputPath) defaults to obj\$(Configuration)\$(TFM). But someone can move the file by overriding the $(RunInformationFilePath), if they really need/want to (this is not common).

What would be the user semantics of this file? I assume it is an implementation detail that the user doesn't have to worry about and can ignore and shouldn't touch?

Like everything else in the obj folder, the user shouldn't need to worry about it at all.

@DustinCampbell
Copy link
Member

Actually, VS Code already has a separate file. The user can set up tasks.json and launch.json files in a .vscode folder to handle build and debug configuration. The C# extension for VS Code does a lot of work to generate these for the user. These files look very similar to the design above.

So, users will need to keep this file in sync with VS Code's assets. In the future, I'm hoping VS Code can allow us to dynamically add launch configurations. That way, we can just read configuration from wherever it is (project file, external file, whatever) and just feed it into VS Code. #10861 is tracking that.

FWIW, I'm concerned about this additional file from a concept count. We're trying to reduce concepts, not add more. Why are we inventing new things?

Note: @BillHiebert, VS Code doesn't host MSBuild -- OmniSharp does. That's already taken care of.

@blackdwarf
Copy link

blackdwarf commented Sep 7, 2016

@DustinCampbell if I'm reading what you wrote correctly, that would mean for VSCode users the runInformation.json file is not "just" an implementation detail, correct? I'm referring to this line in your comment:

So, users will need to keep this file in sync with VS Code's assets.

@DustinCampbell
Copy link
Member

Correct. Essentially, we'd have to have this file to keep other IDEs happy and VS Code's tasks.json and launch.json to keep VS Code happy.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 7, 2016

So, users will need to keep this file in sync with VS Code's assets.

Users will never read or modify the file that is being proposed - it is generated by the "Build" MSBuild Target, and it is generated into the "obj$(Configuration)$(TFM)" folder.

FWIW, I'm concerned about this additional file from a concept count.

This is not a user concept at all. This is the "hand-shake" between the MSBuild project and something that wants to "run" the project's output. As it currently sits, there is no abstract way of providing this information, and all callers need to implement the same rules:

  1. What is the "output" of the project? (it doesn't have to be bin\Debug, MSBuild projects can set it to anything they want - a different disk, it could be named artifacts, etc).
  2. Is this a Desktop .NET Framework app? If so, then directly invoke the output as an executable.
  3. Is this a .NET Core app? If so, use the dotnet.exe host to invoke the app, and pass in the output as an argument.
  4. (future) Is this a Mono app? A Xamarin app? How do we run those?

The idea here is to put all those rules into the MSBuild project, and consumers who want to run the app (like dotnet run, VS, etc), just need to read this file to understand how to invoke it regardless of the rules.

You can image that VS Code's tasks.json and launch.json could be generated from this file. As it looks today, the current generation won't work for MSBuild projects, since you are hard-coding bin\$(Configuration) which won't always be correct.

@DustinCampbell
Copy link
Member

Got it. So, MSBuild will have to be invoked to produce this file? Allow me to walk through the core VS Code scenario:

  • User types dotnet new to create a new project.
  • User types code . to open the project in VS Code.
  • The new project is opened in VS Code, which causes the C# extension in VS Code to be activated.
  • The C# extension launches OmniSharp for the opened project.
  • OmniSharp performs a DesignTimeBuild in order to gather the necessary information to populate its Roslyn workspace.

At this point, we offer to generate a tasks.json and launch.json so that users can F5 successfully. If I understand you correctly, it sounds like there's a bit of a "chicken and egg" problem because the run configuration wouldn't be available until a real Build (i.e. not a DesignTimeBuild) occurs. Am I right about that?

@eerhardt
Copy link
Member Author

eerhardt commented Sep 7, 2016

MSBuild will have to be invoked to produce this file?

MSBuild is the only place this information can be retrieved from, so yes, it must be invoked. The user's .csproj could have something like <OutputPath Condition="XXX">foo</OutputPath>. MSBuild is the only place that will be able to evaluate the Condition="XXX" to know what the actual OutputPath will be.

If I understand you correctly, it sounds like there's a bit of a "chicken and egg" problem because the run configuration wouldn't be available until a real Build (i.e. not a DesignTimeBuild) occurs. Am I right about that?

My current thinking is that the Target that produces this file is hooked into the "Build" target, so the file is always produced during the build. But there is no reason why that Target couldn't be invoked directly by a DesignTimeBuild to produce the file.

@blackdwarf
Copy link

blackdwarf commented Sep 7, 2016

From my perspective, as someone who cares deeply about our overall UX, I would say that I have no problem if we produce this file as long as we can guarantee that the user will never see it and will never have to worry about it in any way or form.

@eerhardt invoking that target manually as you suggest, however, would trigger a full build or no?

@davkean
Copy link
Member

davkean commented Sep 7, 2016

@eerhardt There's a difference to evaluation time and build time. Project systems try very hard to avoid building a project (via design-time build) to get data that can be gathered during evaluation (statically). Design-time builds are a magnitude more expensive than evaluations, and in the legacy project system, the cause of 8 of the top 10 UI delays in csproj.

I would prefer a static way of retrieving this data - if you want to produce the file based on that, I'm okay, but from project system perspective I don't want a build to retrieve data that be retrieved statically.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 7, 2016

I would prefer a static way of retrieving this data - if you want to produce the file based on that, I'm okay, but from project system perspective I don't want a build to retrieve data that be retrieved statically.

Are we guaranteed this information will always be able to be retrieved statically for all project types that we want to run? It would be very counter-productive to make this static data for C# .NET Core apps, and then the next project type we tried enabling was not able to produce this information statically. (Honestly, even for .NET Core apps, I can imagine someone needing the "ResolveNetCoreHost" Task in order to be able to figure out which "dotnet.exe" host to invoke.)

Then we would have the same situation we have today with all the consumers being forced to have rules - "If it is this type of project, look for the static information. If it is this type of project, do something else to get it".

So if we go with the "both" approach that seems it would solve both scenarios. Consumers who don't want to know about these rules (like dotnet run) can consume the runInformation.json file. Consumers who don't want to invoke builds (like VS and VS Code) can consume the static information. And those consumers have the understanding that to support other project types, they may need to do extra work in the future.

@eerhardt invoking that target, however, would trigger a full build or no?

No. The full build would depend on this target. Just invoking just this target wouldn't trigger the full build.

@eerhardt
Copy link
Member Author

eerhardt commented Sep 9, 2016

@davkean @BillHiebert - What static properties should we go with for the "run" information?

Should we go with the existing:

    <StartProgram>dotnet.exe</StartProgram>
    <StartArguments>exec $(OutDir)$(TargetName).dll</StartArguments>
    <StartWorkingDirectory>$(OutDir)</StartWorkingDirectory>

set of properties? Or should we invent a new set of properties that potentially take their value from these?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants