-
-
Notifications
You must be signed in to change notification settings - Fork 27
Custom MSBuild
Although MvsSln provides native implementation for most of the features, however, part of the code still (we're considering future replacement, follow the news) relies on original MS implementation.
Unfortunately modern Microsoft.Build assemblies are much more closely integrated with Visual Studio and much more difficult to maintain independently (i.e. without VS/dotnet sdk dependencies).
Today you may note the following possible problems when using our Env features (flags: Env | LoadDefaultData
; or Env | LoadMinimalDefaultData
).
For example:
using(var sln = new Sln(path, SlnItems.Env | SlnItems.LoadMinimalDefaultData))
{
//...
}
It may produce the following:
Microsoft.Build.Exceptions.InvalidProjectFileException:
The imported project "<...>" was not found. Confirm that the path in the
<Import> declaration is correct, and that the file exists on disk.
Due to import sections when loading project files:
<Import Project="..." />
For something like this: Microsoft.CSharp.targets
, Microsoft.Common.props
, Microsoft.Cpp.Default.props
, Microsoft.Cpp.targets
, ...
Or it also may produce the following:
Microsoft.Build.Exceptions.InvalidProjectFileException:
The SDK 'Microsoft.NET.Sdk' specified could not be found.
Due to SDK-based project type:
<Project Sdk="Microsoft.NET.Sdk">...</Project>
This package Microsoft.Build.Locator is official solution from MS. That was based on two things:
- Spoofing assembly from installed instance of Visual Studio.
- (.NET Core) Manual msbuild path through MSBUILD_EXE_PATH environment variable by using available
dotnet
Sdk paths.
Here's implementation: https://github.com/microsoft/MSBuildLocator/blob/87bebc8b2014f0b52110cf46500d805bba3e072c/src/MSBuildLocator/MSBuildLocator.cs#L114
The first method is the most problematic due to isolation and actually spoofing the assembly.
We will never provide something like this together with MvsSln! Because it will require patching on your side anyway, like below.
Thus, just add this:
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
Before using MvsSln, for example:
// your assembly
MSBuildLocator.RegisterDefaults();
// ... before MvsSln
using(var sln = new Sln(path, SlnItems.Env | SlnItems.LoadMinimalDefaultData))
{
// your assembly will be fixed via the first or the second way in MSBuildLocator
}
Microsoft.Build.Locator above already implements this way, however, you can try to use it manually. Just add this:
System.Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", "fullpath to msbuild dll with env");
Before using MvsSln, for example:
System.Environment.SetEnvironmentVariable(
"MSBUILD_EXE_PATH",
@"C:\Program Files\dotnet\sdk\3.0.100\MSBuild.dll",
EnvironmentVariableTarget.Process
);
using(var sln = new Sln(@"D:\tmp\_Issues\MvsSln\PkgRef\ConsoleApp1\ConsoleApp1.sln", SlnItems.Env | SlnItems.LoadMinimalDefaultData)) {
//...
}
-
MSBUILD_EXE_PATH
feature is already implemented by MSBuild inside for work in other environment like dotnet sdk or Visual Studio. - dotnet SDK conatins Sdk Resolver (Microsoft.Build.NuGetSdkResolver.dll) that will be used due to:
<!-- SdkResolvers\Microsoft.Build.NuGetSdkResolver\Microsoft.Build.NuGetSdkResolver.xml -->
<SdkResolver>
<Path>..\..\Microsoft.Build.NuGetSdkResolver.dll</Path>
</SdkResolver>
This env finally helps to resolve SDK-based project type, and now you can process it:
<Project Sdk="Microsoft.NET.Sdk">...</Project>
And more because of other environment such as dotnet sdk etc.
Here's official MS way: https://github.com/microsoft/MSBuildLocator/blob/87bebc8b2014f0b52110cf46500d805bba3e072c/src/MSBuildLocator/DotNetSdkLocationHelper.cs#L61
dotnet --list-sdks
1.0.4 [C:\Program Files\dotnet\sdk]
1.1.0 [C:\Program Files\dotnet\sdk]
2.0.2 [C:\Program Files\dotnet\sdk]
2.0.3 [C:\Program Files\dotnet\sdk]
2.1.2 [C:\Program Files\dotnet\sdk]
2.1.4 [C:\Program Files\dotnet\sdk]
...
System.InvalidOperationException:
'Microsoft.Build.Locator.MSBuildLocator.RegisterInstance was called, but MSBuild assemblies were already loaded.
Firstly, ensure that RegisterDefaults is called before any MvsSln or MSBuild use:
MSBuildLocator.RegisterDefaults();
// ... vvvv before any use
new Sln(path, SlnItems.All)
new Project()
If not, try add reference to MSBuild assembly in your project (only at build time without copying to the output directory).
For PackageReference, use ExcludeAssets="runtime"
:
<PackageReference Include="MvsSln" Version="2.5.0" />
<PackageReference Include="Microsoft.Build" Version="16.3.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
For Reference, use <Private>False</Private>
:
<Reference Include="MvsSln, Version=2.5.0.0, Culture=neutral, PublicKeyToken=87f0bd8fb7f0a2c4, processorArchitecture=MSIL">
<HintPath>..\packages\MvsSln.2.5.0\lib\net472\MvsSln.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Build.16.3.0\lib\net472\Microsoft.Build.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Build.Locator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9dff12846e04bfbd, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Build.Locator.1.2.6\lib\net46\Microsoft.Build.Locator.dll</HintPath>
</Reference>
Or GAC if netfx:
<Reference Include="Microsoft.Build" />
<PackageReference Include="MvsSln" Version="2.5.0" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.Build" Version="16.3.0" ExcludeAssets="runtime" />
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults(); //must be invoked BEFORE use of MvsSln
// ...
using(var sln = new Sln(path, SlnItems.All)) { ... }
If you will see the error:
Microsoft.Build.Shared.InternalErrorException:
'MSB0001: Internal MSBuild Error: Type information for Microsoft.Build.Utilities.ToolLocationHelper
was present in the whitelist cache as Microsoft.Build.Utilities.ToolLocationHelper'
Try add also Microsoft.Build.Utilities.Core:
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.3.0" />
Please note: PackageReference
samples are fully compatible to Reference
type of items.
MvsSln also provides related virtual methods for your convenience.
As the real example, the .NET DllExport project uses this way to avoid some problems when loading unsupported project types:
That is, you can easily try to override actual loading with any custom logic as you need. For example:
+Microsoft.Build.dll // ~ it could be custom reference in your env
using net.r_eg.MvsSln;
using net.r_eg.MvsSln.Core;
...
public class MyEnv: IsolatedEnv, IEnvironment, IDisposable
{
public MyEnv(ISlnResult data)
: base(data)
{
}
protected override Microsoft.Build.Evaluation.Project Load(string path, IDictionary<string, string> properties)
{
return base.Load(path, properties); //TODO: your awesome logic
}
protected override void Dispose(bool disposing) => base.Dispose(disposing);
}
...
Then, for example:
using(var sln = new Sln(file, SlnItems.Projects
| SlnItems.SolutionConfPlatforms
| SlnItems.ProjectConfPlatforms))
{
IEnvironment env = new MyEnv(sln.Result);
env.LoadMinimalProjects();
...
sln.Result.ProjectItems.Where(p => p.path == "special\\projectfile.csproj");
...
}
Note: 2.4 introduces XProjectEnv (splitted IsolatedEnv as the base but without IDisposable) [?]
Except where otherwise noted, wiki content is licensed under a Creative Commons Attribution 4.0 International License. (CC BY 4.0).