ILSupport
ILSupport copied to clipboard
Can't build template project
Steps to reproduce:
- Create C# Class Library (.NET Standard) with IL Support
- Run
dotnet build
against it
This presents the following error
Microsoft (R) Build Engine version 16.3.0+0f4c62fea for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 23.11 ms for C:\Users\p-kel\source\repos\IL Experiments\IL Experiments\IL Experiments.csproj.
C:\Users\p-kel\source\repos\IL Experiments\IL Experiments\IL Experiments.csproj(21,5): error MSB4062: The "Microsoft.Build.Tasks.GetFrameworkSdkPath" task could not be loaded from the assembly Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
Build FAILED.
C:\Users\p-kel\source\repos\IL Experiments\IL Experiments\IL Experiments.csproj(21,5): error MSB4062: The "Microsoft.Build.Tasks.GetFrameworkSdkPath" task could not be loaded from the assembly Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a. Confirm that the <UsingTask> declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.61
And the output of dotnet --info
:
.NET Core SDK (reflecting any global.json):
Version: 3.0.100
Commit: 04339c3a26
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.0.100\
Host (useful for support):
Version: 3.0.0
Commit: 7d57652f33
.NET Core SDKs installed:
3.0.100 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Considering this is a .NET Standard project I would think this should build regardless, since a non IL Support project would just fine.
Upon further testing it seems like this happens regardless of the project type.
Moreso, building projects inside Visual Studio causes Visual Studio to report the following error.
The command ""bin\ildasm.exe" /nobar /linenum /output:"C:\Users\p-kel\source\repos\IL Experiments\IL Support\obj\Debug\IL Support.il" "C:\Users\p-kel\source\repos\IL Experiments\IL Support\obj\Debug\IL Support.dll"" exited with code 9009.
My understanding is that 9009
means it was unable to find the tool, but I most definately have it installed.
From this error, it seems $(FrameworkSdkPath) is empty under .NET Core 3.0.
The location fallbacks for ILDasm are those lines in your csproj: https://github.com/ins0mniaque/ILSupport/blob/7e440fbf71930a12449560d6fcaca0e9c13ba6bb/IL%20Support.ProjectTemplates/IL%20Support.targets#L26-L43
Meanwhile, you can add another fallback pointing directly to ILDasm. The correct fix would be to find the variable that gives the correct $(FrameworkSdkPath) under .NET Core 3.0.
I'll investigate as soon as possible.
Okay thanks, this sets me on the right path, so I can look into it as well.
A small tip for debugging... You can put:
<Message Importance="High" Text="Test: $(FrameworkSdkPath)" />
inside the <Target>
tag to print out variables when building.
I found these $(Win10SDKBinPath)
$(Win10SDKVersion)
which I'm pretty sure are the correct variables. I'll play around with getting this to work tomorrow.
Apparently I misunderstood what I was looking at. Those were locally created variables, that were filled in by looking for a specific registry key (which of course doesn't exist anymore either).
I looked around a bit for a solution, and it appears it might be a bit more complicated than expected.
To have access to ILDasm with .NET Core, and to make it work cross-platform, it seems I'll have to make new .NET Core / Standard project templates that use the Microsoft.NETCore.ILDAsm NuGet package, and then somehow figure out where the binary gets installed.
On macOS, it seems to be ~/.nuget/packages/runtime.osx-x64.microsoft.netcore.ildasm/2.0.8/runtimes/osx-x64/native/ildasm. I expect it to be %UserProfile%\.nuget\packages\runtime.win-x64.microsoft.netcore.ildasm\2.0.8\runtimes\win-x64\native\ildasm.exe or something similar.
The package reference looks like this:
<PackageReference Include="Microsoft.NETCore.ILDAsm" Version="2.0.8" />
You could try adding this package to your project, and then a fallback for: %UserProfile%\.nuget\packages\runtime.win-x64.microsoft.netcore.ildasm\2.0.8\runtimes\win-x64\native\ildasm.exe or whatever is the correct path to ILDasm is.
It might be necessary to instead reference the "runtime.win-x64.Microsoft.NETCore.ILDAsm" package to have the binary installed.
This issue on the dotnet repository is tracking the problem: https://github.com/dotnet/cli/issues/6223
In that thread there was this: dotnet-ildasm which might abstract away a lot of the complexities of getting the right tool.
This might be a viable solution, and could be included in the project with:
<DotNetCliToolReference Include="dotnet-ildasm" Version="0.11.0" />
but I'm not sure where the ildasm.exe binary will end up.
However, I'd rather have a solution that does not include NuGet packages not maintained by Microsoft, for security reasons.
I completely understand that. I'll look into the nuget packages and where they dump their contents.
I randomly stumbled upon this: https://github.com/ntscosta/TaxAuditCommunity-Codes/blob/master/src/corefx-master/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj
While it uses a different SDK ("Microsoft.NET.Sdk.IL"), it seems to be able to call ildasm on .NET Core by using the $(NuGetPackageRoot) variable. So maybe
$(NuGetPackageRoot)\packages\runtime.win-x64.microsoft.netcore.ildasm\2.0.8\runtimes\win-x64\native\ildasm.exe
or
$(NuGetPackageRoot)\runtime.win-x64.microsoft.netcore.ildasm\2.0.8\runtimes\win-x64\native\ildasm.exe
would work.
And that approach should work for any platform. Perfect. I'll play around with it.
Here's where I'm at.
Microsoft.NETCore.ILDasm doesn't seem to actually contain anything remotely useful.
And in fact nothing useful is pulled down when a restore occurs
Which then requires instead to reference the specific runtime.$(platformName)-$(architecture).Microsoft.NETCore.ILDAsm package which actually does.
And also sometimes there's a $(platformVersion) after the name, but only sometimes.
This same problem exists with Microsoft.NETCore.ILAsm
So the tools are available. However...
It's not runnable in the state nuget fetches it in.
This can be dealt with however. The runtime library ildasm needs is irrelevant to the runtime library of the project, so it's a non-issue, just an inconvenience.
Right?
Well this time around it's runtime.$(platformName)$(platformVer)-$(architecture).Microsoft.NETCore.Runtime.CoreCLR
or runtime.$(platformName)-$(architecture).Microsoft.NETCore.App
Good luck figuring out which one is needed, it's remarkably inconsistent from what I can tell.
But once done, does yeild a working ildasm.
From there, I built a multitarget library for about 9 different targets, covering .NET Framework, .NET Core, and .NET Standard, of both new and old versions. All we able to be disassembled.
So it seems like this is possible, just a massive PIA.
Thank you for taking the time to figure all this out, and it got me searching in the right place.
After a while I found this: https://github.com/dotnet/coreclr/blob/master/src/.nuget/Microsoft.NET.Sdk.IL/targets/Microsoft.NET.Sdk.IL.targets
Which led me to this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<_OSPlatform Condition="$([MSBuild]::IsOSPlatform('windows'))">win</_OSPlatform>
<_OSPlatform Condition="$([MSBuild]::IsOSPlatform('linux'))">linux</_OSPlatform>
<_OSPlatform Condition="$([MSBuild]::IsOSPlatform('osx'))">osx</_OSPlatform>
<_OSPlatform Condition="$([MSBuild]::IsOSPlatform('freebsd'))">freebsd</_OSPlatform>
<_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</_OSArchitecture>
<MicrosoftNetCoreIlasmPackageRuntimeId>$(_OSPlatform)-$(_OSArchitecture.ToLower())</MicrosoftNetCoreIlasmPackageRuntimeId>
<MicrosoftNetCoreIlasmPackageName>runtime.$(MicrosoftNetCoreIlasmPackageRuntimeId).microsoft.netcore.ilasm</MicrosoftNetCoreIlasmPackageName>
<MicrosoftNetCoreIlasmPackageVersion>2.0.8</MicrosoftNetCoreIlasmPackageVersion>
<MicrosoftNetCoreIlasm>$(NuGetPackageRoot)\$(MicrosoftNetCoreIlasmPackageName)\$(MicrosoftNetCoreIlasmPackageVersion)\runtimes\$(MicrosoftNetCoreIlasmPackageRuntimeId)\native\ilasm</MicrosoftNetCoreIlasm>
<MicrosoftNetCoreIldasmPackageName>runtime.$(MicrosoftNetCoreIlasmPackageRuntimeId).microsoft.netcore.ildasm</MicrosoftNetCoreIldasmPackageName>
<MicrosoftNetCoreIldasmPackageVersion>2.0.8</MicrosoftNetCoreIldasmPackageVersion>
<MicrosoftNetCoreIldasm>$(NuGetPackageRoot)\$(MicrosoftNetCoreIldasmPackageName)\$(MicrosoftNetCoreIldasmPackageVersion)\runtimes\$(MicrosoftNetCoreIlasmPackageRuntimeId)\native\ildasm</MicrosoftNetCoreIldasm>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="$(MicrosoftNetCoreIlasmPackageName)" Version="$(MicrosoftNetCoreIlasmPackageVersion)" PrivateAssets="all" />
<PackageReference Include="$(MicrosoftNetCoreIldasmPackageName)" Version="$(MicrosoftNetCoreIldasmPackageVersion)" PrivateAssets="all" />
</ItemGroup>
<Target Name="DisplayTestMessage" BeforeTargets="BeforeBuild">
<Message Importance="High" Text="Package: $(MicrosoftNetCoreIlasmPackageName)" />
<Message Importance="High" Text="Binary: $(MicrosoftNetCoreIlasm)" />
<Message Importance="High" Text="Package: $(MicrosoftNetCoreIldasmPackageName)" />
<Message Importance="High" Text="Binary: $(MicrosoftNetCoreIldasm)" />
</Target>
</Project>
which on macOS outputs the correct path for ilasm/ildasm (and it runs):
Package: runtime.osx-x64.microsoft.netcore.ilasm
Binary: ~/.nuget/packages/runtime.osx-x64.microsoft.netcore.ilasm/2.0.8/runtimes/osx-x64/native/ilasm
Package: runtime.osx-x64.microsoft.netcore.ildasm
Binary: ~/.nuget/packages/runtime.osx-x64.microsoft.netcore.ildasm/2.0.8/runtimes/osx-x64/native/ildasm
Still not sure how to deal with CoreCLR. I don't seem to have a package for it, but ildasm ran correctly. I'll do some tests on Windows when I have time.
And I haven't found a way to not target a specific version. I tried briefly using wildcards and an ItemGroup like in Microsoft.NET.Sdk.IL.targets, but couldn't make work.
Path resolution looks okay on Windows.
I'll look into ways to deal with the library.
Thank you for taking the time to figure all this out
Hey no problem. I do FOSS development too, and know what it's like when users just go "hey this is broke, fix it". I'm not familiar with MSBuild, but I can do what I can to help out.
I can't seem to find a way around this that isn't referencing the specific library package and manually moving it over.
I didn't have time to test this, but I found out that running dotnet list package
outputs the following:
Project 'ildasm_crap' has the following package references
[netcoreapp3.0]:
Top-level Package Requested Resolved
> runtime.osx-x64.microsoft.netcore.ilasm 2.0.8 2.0.8
> runtime.osx-x64.microsoft.netcore.ildasm 2.0.8 2.0.8
It might be possible to reference the package using:
<PackageReference Include="$(MicrosoftNetCoreIldasmPackageName)" Version="*" PrivateAssets="all" />
and then extract the version number from dotnet list package
in a target that runs after Nuget Packages are restored.
Such a target would be defined like so:
<Target BeforeTargets="BeforeBuild" AfterTargets="Restore">
... somehow set MicrosoftNetCoreIldasmPackageVersion here using "dotnet list package" ...
</Target>
This is really beyond my knowledge of MSBuild and the project syntax. I don't think I'm going to be of much help at this point. I've tried a bit but I'm completely lost.
Frankly, all of this is getting hackish, and for the new .NET projects, it should probably be published as a project SDK, so the usage would simply be to change the SDK from:
<Project Sdk="Microsoft.NET.Sdk">
to <Project Sdk="ILSupport.NET.Sdk">
and targets would be automatically imported. And by the time I get to do this, maybe Microsoft will have added a better way to call ildasm.
And all this aside, a better way to use custom IL is probably to move all the IL code to an .ilproj with the Microsoft.NET.Sdk.IL project SDK as in the System.Runtime.CompilerServices.Unsafe project: https://github.com/dotnet/corefx/blob/master/src/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj
It's not published to NuGet yet, but it seems people are able to use it: https://github.com/dotnet/coreclr/issues/20816