Closed natemcmaster closed 6 years ago
I'll need to know more about the layout of these project tools. Who is the best person to give me a primer in order to come up with a fix?
cc @eerhardt
I'll ping you offline.
I see two options:
The latter is how standalone applications would work. What's creating this $(NuGetPackages)/.tools
layout, and why doesn't it match what would happen with a published application?
What's creating this $(NuGetPackages)/.tools layout, and why doesn't it match what would happen with a published application?
NuGet creates the .tools folder during restore. CLI reads the csproj/.tools folder to launch the tool. AFAIK there aren't any plans to change this. cref https://github.com/NuGet/Home/issues/3462. cc @emgarten @eerhardt
Regardless of how a CLI tools finds or distributes MSBuild, we need some sort of API so that BuildEnvironmentHelper.Initialize knows where to find MSBuild.exe.
@piotrpMSFT - do you have any opinions on option 1 above?
@jeffkl - we've discussed before enabling MSBuild to "run out of a NuGet cache", in order for every tool to not need its own private copy of MSBuild. Has any progress been made in this area?
@eerhardt The blockers for making MSBuild run out of the NuGet cache remain: either smarter loading behavior (load our assemblies out of a folder whose layout we totally control) or a full-featured "find files from the nuget cache" feature baked into the framework. Neither seems forthcoming.
There are possible changes coming soon to NuGet/CLI which may solve this issue independently of changes to MSBuild.
cc @yishaigalatzer @rrelyea - the discussion we had about location of a CLI tool's .deps.json file.
In our meeting this morning, we discussed this and the answer was:
The CLI will set an environment variable when invoking a tool that will point to the location of it's MSBuild install. The CLI Tool can read this environment variable to find out the path to MSBuild's installation.
🔔 Any update on this? Running into the same issue.
In preview3, the SDK will set the env variable MSBUILD_EXE_PATH to the location of MSBuild.dll.
FWIW, in most cases, CLI tools are better off invoking "dotnet-msbuild" with a custom target instead of invoking MSBuild APIs directly.
Got it to work, thanks to @rainersigwald :)
In powershell -
Here C:\Program Files\dotnet\sdk\1.0.0-preview4-004079 is the install path for dotnet sdk on my machine
@mishra14 this is very fragile. The code makes assumptions on where CLI installs MSBuild.dll which are quite certain to be broken in upcoming releases. As @natemcmaster says, the CLI already has an environment variable which it passes to processes it creates to identify the msbuild exe path. Even this should only be used in critical scenarios where invoking dotnet msbuild
simply won't work.
I know the self-created environment variable you suggest is straightforward and works at the moment, but do expect it to fail from release to release.
@piotrpMSFT : @rainersigwald mentioned that the workaround $ENV:MSBUILDEXTENSIONSPATH="C:\Program Files\dotnet\sdk\1.0.0-preview4-004079"
is needed due to a bug in current msbuild release but should be fixed in the next release (rc2?).
$ENV:MSBUILD_EXE_PATH="C:\Program Files\dotnet\sdk\1.0.0-preview4-004079\MSBuild.dll"
on the otherhand should be set by dotnet sdk. But did not on my machine. Maybe, I am missing something?
It's a subtlety. Dotnet SDK does not set any persistent environment variables. It only passes some environment variables to processes that it itself creates. Tools that need access to this value from the CLI need to be invoked by the CLI, giving the product the opportunity to redefine its own internal layout/implementation without breaking existing extensions :)
@piotrpMSFT It's worse than that. When you're developing the tool, you can't rely on the CLI setting that variable, because it's not set in dotnet run
invocations, just tool invocations. Once the tool is packaged, referred to as a tool, and run through the CLI, it should get the CLI's preferred MSBUILD_EXE_PATH
. But is there an easy way to edit-compile-debug in that environment?
(the need for MSBUILDEXTENSIONSPATH
is fixed by #1336)
@mishra14 using dotnet run
should work if your app is a netcoreapp1.0
. Are you getting error when running your app via dotnet run
or when you run it as a tool?
Are you getting the error that MSBuild cannot find itself?
System.InvalidOperationException: Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio.
Or are you getting a different error?
The dotnet run
scenario should work because we look in the AppContext.BaseDirectory
and should be able to locate assembly dependencies via NuGet. However, as a tool, the AppContext.BaseDirectory
is where the tool marker was written to and is not correct. But I believe the dotnet CLI will set MSBUILD_EXE_PATH
when the tool is run which means it should work too. I need more info on what error you're getting...
@jeffkl : I have dotnet core sdk preview4 installed.
PS C:\Users\anmishr\Documents\visual studio 2017\Projects\ConsoleApp1\ConsoleApp1> dotnet --info
.NET Command Line Tools (1.0.0-preview4-004079)
Product Information:
Version: 1.0.0-preview4-004079
Commit SHA-1 hash: 43dfa6b8ba
Runtime Environment:
OS Name: Windows
OS Version: 10.0.14393
OS Platform: Windows
RID: win10-x64
Are you getting the error that MSBuild cannot find itself? Yes
@livarcocc can comment on where we expose the env var. If adding it to run
prevents folks from assuming things about the CLI install layout, maybe we need to do that...
@mishra14 are you getting the error when you do dotnet run
? Can you check to see if files like MSBuild.dll, Microsoft.Common.targets are in your output folder.
We only expose it in the ProjectToolsCommandResolver and ProjectDependenciesCommandResolver right now. I think exposing it during run may make sense for people developing tools.
@jeffkl :
are you getting the error when you do dotnet run?: Yes on dotnet run and also from within dev15.
Can you check to see if files like MSBuild.dll, Microsoft.Common.targets are in your output folder: No. I thinks that why I needed to set the env vars. So that dotnet could pick up the correcxt place where msbuild exists.
@mishra14 What packages are you referencing? The Microsoft.Build.Runtime
package should place stuff in your output folder so that dotnet run
works. If those files aren't there, this might be a bug in NuGet.
@jeffkl : I do have runtime.
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Runtime">
<Version>15.1.0-preview-000370-00</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@emgarten is this a bug in NuGet? The project.assets.json has the correct stuff:
"Microsoft.Build.Runtime/15.1.0-preview-000370-00": {
"type": "package",
"dependencies": {
"Microsoft.Build": "[15.1.0-preview-000370-00]",
"Microsoft.Build.Framework": "[15.1.0-preview-000370-00]",
"Microsoft.Build.Tasks.Core": "[15.1.0-preview-000370-00]",
"Microsoft.Build.Utilities.Core": "[15.1.0-preview-000370-00]"
},
"contentFiles": {
"contentFiles/any/netcoreapp1.0/15.0/Microsoft.Common.props": {
"buildAction": "None",
"codeLanguage": "any",
"copyToOutput": true,
"outputPath": "15.0/Microsoft.Common.props"
},
"contentFiles/any/netcoreapp1.0/MSBuild.dll": {
"buildAction": "None",
"codeLanguage": "any",
"copyToOutput": true,
"outputPath": "MSBuild.dll"
},
"contentFiles/any/netcoreapp1.0/MSBuild.runtimeconfig.json": {
"buildAction": "None",
"codeLanguage": "any",
"copyToOutput": true,
"outputPath": "MSBuild.runtimeconfig.json"
},
"contentFiles/any/netcoreapp1.0/Microsoft.CSharp.CrossTargeting.targets": {
"buildAction": "None",
"codeLanguage": "any",
"copyToOutput": true,
"outputPath": "Microsoft.CSharp.CrossTargeting.targets"
},
// (etc)
}
},
But the files are not in the output folder after doing dotnet build
. Or is this logic part of the SDK?
C:\Users\jeffkl\Downloads\msbuildruntimerepro>dotnet new
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
+ <PackageReference Include="Microsoft.Build.Runtime">
+ <Version>15.1.0-preview-000370-00</Version>
+ </PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
C:\Users\jeffkl\Downloads\msbuildruntimerepro>dotnet restore
Restoring packages for C:\Users\jeffkl\Downloads\msbuildruntimerepro\msbuildruntimerepro.csproj...
Writing lock file to disk. Path: C:\Users\jeffkl\Downloads\msbuildruntimerepro\obj\project.assets.json
Generating MSBuild file C:\Users\jeffkl\Downloads\msbuildruntimerepro\obj\msbuildruntimerepro.csproj.nuget.g.targets.
Generating MSBuild file C:\Users\jeffkl\Downloads\msbuildruntimerepro\obj\msbuildruntimerepro.csproj.nuget.g.props.
Restore completed in 1124.0981ms for C:\Users\jeffkl\Downloads\msbuildruntimerepro\msbuildruntimerepro.csproj.
NuGet Config files used:
C:\Users\jeffkl\AppData\Roaming\NuGet\NuGet.Config
Feeds used:
https://api.nuget.org/v3/index.json
C:\Users\jeffkl\Downloads\msbuildruntimerepro>dotnet build /v:m
Microsoft (R) Build Engine version 15.1.0.0
Copyright (C) Microsoft Corporation. All rights reserved.
msbuildruntimerepro -> C:\Users\jeffkl\Downloads\msbuildruntimerepro\bin\Debug\netcoreapp1.0\msbuildruntimerepro.dll
C:\Users\jeffkl\Downloads\msbuildruntimerepro>tree /f bin\Debug\netcoreapp1.0
Folder PATH listing
Volume serial number is 4E82-251B
C:\USERS\JEFFKL\DOWNLOADS\MSBUILDRUNTIMEREPRO\BIN\DEBUG\NETCOREAPP1.0
msbuildruntimerepro.deps.json
msbuildruntimerepro.dll
msbuildruntimerepro.pdb
msbuildruntimerepro.runtimeconfig.dev.json
msbuildruntimerepro.runtimeconfig.json
No subfolders exist
Okay this is currently blocked by https://github.com/NuGet/Home/issues/3683 because our contentFiles are not being copied to the output directory. The workaround is to set MSBUILD_EXE_PATH
for now...
@jeffkl What's the current state of this for a tool? My understanding is that it should work fine since dotnet
will set MSBUILD_EXE_PATH
to its MSBuild root and allow a project to have standard dependencies on our DLLs and evaluate a project using the CLI's toolset--right? I think @natemcmaster thinks otherwise . . .
I haven't tested lately. We decided that ASP.NET Core CLI tools should not use MSBuild APIs for project evaluation. AFAIK the only tool using MSBuild object model programmatically is dotnet-prop
(https://github.com/simonech/dotnet-prop).
Instead, ASP.NET Core CLI tools accomplish indirect project evaluation by abusing the imports from MSBuildProjectExtensionsPath
. This allows us to inject targets into a project. See https://github.com/aspnet/DotNetTools/pull/206 for a brief description of how dotnet-watch implements that.
I'm not sure, I should probably set up a repro so I can test solutions.
We didn't really want people taking a dependency on MSBUILD_EXE_PATH
but it should fix the problem for now. Longer term we talked about NuGet being able to resolve assets other than reference assemblies. Another option is to put MSBuild.exe next to Microsoft.Build.dll in the appropriate package. I just need to take the time to set this up so I can test it all...
Ping on a really old thread. At this point, all of the tools I have created workaround this limitation by invoking a new MSBuild process instead of calling on MSBuild API directly. It's a less-than-ideal programming experience though.
I recently saw this: https://github.com/daveaglick/Buildalyzer. Is this kind of API something MSBuild would every provide as a 1st class thing? If not, I suugest we just close this as "wontfix" and invite tool authors to use something like https://github.com/daveaglick/Buildalyzer if they want to use MSBuild in-proc.
Using MSBuild API for in-proj project evaluation in a dotnet-CLI project tool throws this excpetion:
IIUC the problem is that BuildEnvironmentHelperSingleton looks in
AppContext.BaseDirectory
(and a few other locations) for MSBuild.exe. When dotnet-CLI invokes a project tool, the AppContext.BaseDirectory will be$(NuGetPackages)/.tools/$(ToolName)/$(ToolVersion)/netcoreapp1.0/
. The only content NuGet/CLI will put into this directory is the *.deps.json file and project.lock.json file for the tool.Using Microsoft.Build.Runtime 15.1.262-preview5.
cc @piotrpMSFT