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

System.IO.FileNotFoundException when using IKVMC #65

Closed
vokounjan opened this issue May 30, 2022 · 27 comments
Closed

System.IO.FileNotFoundException when using IKVMC #65

vokounjan opened this issue May 30, 2022 · 27 comments
Labels
bug Something isn't working

Comments

@vokounjan
Copy link

vokounjan commented May 30, 2022

Hello,

first of all - thank you for your effort, I've been following your work and I really appreciate it. I really wish I could understand everything you've done in last few weeks and help you at least a little bit.🙂

But to the problem - I've been trying to use the new release but unfortunately with no success. I'm using Windows 10 so I downloaded the IKVM-8.2.0-prerelease.392-tools-netcoreapp3.1-win7-x86.zip artifact and tried to recompile my .jar with the new IKVMC but this error popped up:

PS C:\Users\vokounjan\source\repos\Company.Project\jar\MyLibrary> C:\Users\vokounjan\source\repos\Company.Project\bin\IKVM\ikvmc.exe -target:library -out:C:\Users\vokounjan\source\repos\Company.Project\bin\MyLibrary\MyLibrary.dll -version:1.2.3 *.jar
IKVM.NET Compiler (8.2.0-prerelease.392)
Copyright © 2022 Jeroen Frijters, Windward Studios, Jerome Haltom, Shad Storhaug


*** INTERNAL COMPILER ERROR ***

PLEASE FILE A BUG REPORT FOR IKVM.NET WHEN YOU SEE THIS MESSAGE

ikvmc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58
C:\Users\vokounjan\source\repos\Company.Project\bin\IKVM\
3.1.25 64-bit

System.IO.FileNotFoundException: Reference assemblies directory: C:\Users\vokounjan\source\repos\packs\bin.Ref\3.1.0\ref\netcoreapp3.1
   at IKVM.Reflection.Universe.BuildRefDirFrom(String runtimeDir) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 180
   at IKVM.Reflection.Universe.get_ReferenceAssembliesDirectory() in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 156
   at IKVM.Reflection.Universe.DefaultResolver(String refname, Boolean throwOnError) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 842
   at IKVM.Reflection.Universe.Load(String refname, Module requestingModule, Boolean throwOnError) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 799
   at IKVM.Internal.AssemblyResolver.Init(Universe universe, Boolean nostdlib, IList1 references, IList1 userLibPaths) in D:\a\ikvm\ikvm\ikvmc\IKVM\Internal\AssemblyResolver.cs:line 93
   at ikvmc.IkvmcCompiler.Compile(String[] args) in D:\a\ikvm\ikvm\ikvmc\IkvmcCompiler.cs:line 178
   at ikvmc.IkvmcCompiler.Main(String[] args) in D:\a\ikvm\ikvm\ikvmc\IkvmcCompiler.cs:line 112

Maybe I'm just missing something or doing something stupidly wrong. If that's the case could you please tell me what?🙂

Thanks

@vokounjan vokounjan changed the title System.IO.FileNotFoundException wbe System.IO.FileNotFoundException when using IKVMC May 30, 2022
@AliveDevil
Copy link
Collaborator

Not the only one here.
Doesn't work on a separate drive as well:

IKVM.NET Compiler (8.2.0-prerelease.392)
Copyright ¸ 2022 Jeroen Frijters, Windward Studios, Jerome Haltom, Shad Storhaug


*** INTERNAL COMPILER ERROR ***

PLEASE FILE A BUG REPORT FOR IKVM.NET WHEN YOU SEE THIS MESSAGE

ikvmc, Version=8.2.0.0, Culture=neutral, PublicKeyToken=13235d27fcbfff58
V:\Apps\IKVM\bin\
3.1.25 64-bit

System.IO.FileNotFoundException: Reference assemblies directory: V:\packs\IKVM.Ref\3.1.0\ref\netcoreapp3.1
   at IKVM.Reflection.Universe.BuildRefDirFrom(String runtimeDir) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 180
   at IKVM.Reflection.Universe.get_ReferenceAssembliesDirectory() in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 156
   at IKVM.Reflection.Universe.DefaultResolver(String refname, Boolean throwOnError) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 842
   at IKVM.Reflection.Universe.Load(String refname, Module requestingModule, Boolean throwOnError) in D:\a\ikvm\ikvm\IKVM.Reflection\Universe.cs:line 799
   at IKVM.Internal.AssemblyResolver.Init(Universe universe, Boolean nostdlib, IList`1 references, IList`1 userLibPaths) in D:\a\ikvm\ikvm\ikvmc\IKVM\Internal\AssemblyResolver.cs:line 93
   at ikvmc.IkvmcCompiler.Compile(String[] args) in D:\a\ikvm\ikvm\ikvmc\IkvmcCompiler.cs:line 178
   at ikvmc.IkvmcCompiler.Main(String[] args) in D:\a\ikvm\ikvm\ikvmc\IkvmcCompiler.cs:line 112

Possibly: The FrameworkDir is always $(ThisFileDirectory) due to it being bundled with ikvmc.1

Footnotes

  1. https://github.com/ikvm-revived/ikvm/blob/main/reflect/Universe.cs#L170

@NightOwl888
Copy link
Contributor

@vokounjan

Thanks for the report.

A few things stand out as potential issues, although it appears the primary problem is that the ikvmc tool depends on a specific version of the .NET SDK to be installed so the reference assemblies exist (see below).

  1. You linked to the win7-x86 version of ikvmc tools, but the error message indicates 64 bit.
  2. The command is being passed *.jars and I am not sure whether wildcards/globs are supported this way (although that does sound like a good feature request if they are not). It looks like there is a -recurse: option that can be used in a similar way, though.
  3. A full path is being passed in without being escaped with double quotes, for example
    -out:"C:\Users\vokounjan\source\repos\Company.Project\bin\MyLibrary\MyLibrary.dll"

@AliveDevil

Thanks for pointing to the source. Looks like it is hard-coded to use the "3.1.0" directory instead of choosing the highest version that is installed. But then, the approach we use to resolve the directory just seems wrong.

The quick workaround is to install a version of .NET Core SDK that installs the C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1 folder and its contents, presumably SDK 3.1.100 will do it.

On my machine, the folder was created on 2019-12-09, but I currently only have the following .NET Core 3.x SDKs installed:

  • Microsoft .NET Core SDK 3.1.119 (x64) (installed on 2021-09-15)
  • Microsoft .NET Core SDK 3.1.416 (x64) (installed on 2022-03-08)

For a longer term solution, IMO it doesn't make a lot of sense to depend on a specific version of an SDK. There are at least a couple of options:

  1. Scan the "ref" directory for the highest 3.1.x version. This would require at least one .NET Core 3.1 SDK to be installed for the ikvmc tool to function.
  2. Include the reference assemblies with the ikvm tool distribution. According to the docs, reference assemblies for .NET Core are available in the Microsoft.NETCore.App.Ref NuGet package.

Since the latter option only increases the distribution size by 3.71 MB, IMO it would be better to deploy the reference assemblies with the ikvm tools for netcoreapp3.1.

On the .NET Framework side, we can also deploy reference assemblies using Microsoft.NETFramework.ReferenceAssemblies.net461, but I am not sure if we gain anything there because the package is useful in cases where the .NET Framework Developer Packs are not installed, such as when building on Linux/macOS. I suppose building on Mono is for .NET Framework is still supported, but that seems like an edge case we don't need to cover.

@AliveDevil
Copy link
Collaborator

AliveDevil commented May 30, 2022

The quick workaround is to install a version of .NET Core SDK that installs the C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1 folder and its contents, presumably SDK 3.1.100 will do it.

Already did that, but that doesn't work, as I suspect that RuntimeEnvironment.GetRuntimeDirectory returns ikvmc.exe\..\.
That way it tries to resolve to ..\packs\(ikvmc.exe-Directory Name).Ref\, which doesn't exist.

Relying on Microsoft.NETCore.App.Ref is probably a good idea, though the ikvmc binary is distributed with a ref directory for netcore3.1-target, which contains, at first glance, all reference assemblies from the building SDK.

//More Context for GetRuntimeDirectory:
The Example1 mentions this comment:

Show the path where the CLR was loaded from.

Whereas the returns description is

contains the path to the directory where the common language runtime is installed

With dotnet publish it looks like a framework independent executable is created, thus corefx.dll etc. are included within the application directory, which means that GetRuntimeDirectory always returns the application directory, not the installed .NET Core 3 runtime directory.

Footnotes

  1. https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeenvironment.getruntimedirectory?view=netcore-3.1#examples

@vokounjan
Copy link
Author

@NightOwl888

Thank you for your response.

  1. Mea culpa, I originally tried the 64bit version but then I gave a chance to 32bit and forgot to paste the 64bit log but the result was the same for both versions.
  2. Previous IKVMC versions (both original Jeroen's and net_core_compat binaries) supported the wildcard notation so I believed that the new release would too. Switching to the real filename doesn't solve my problem neither (I've tried).
  3. Same as 2. - previous versions supported this scenario and escaping doesn't make any difference. It's probably a good thing to do though.

I'll try to play with the .NET Core 3.1 versions now but I strongly support the idea of including ref assemblies (at least until the dotnet tool becomes a thing).

Thanks again

@NightOwl888
Copy link
Contributor

Alright. Since @wasabii and I both have it functioning, there appears to be another dependency somewhere that isn't being accounted for.

Going through the GitHub Actions config may yield some clues, but at first glance I don't see any tests that confirm functionality of ikvmc. Testing is a rather new addition to IKVM and there are very few cases covered at this point (contributions of test cases welcome - note we are working on the develop branch).

I can confirm that I have .NET Core 3.1, .NET 5, and .NET 6 SDKs installed locally:

.NET SDK (reflecting any global.json):
 Version:   6.0.300
 Commit:    8473146e7d

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19042
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\6.0.300\

Host (useful for support):
  Version: 6.0.5
  Commit:  70ae3df4a6

.NET SDKs installed:
  2.0.3 [C:\Program Files\dotnet\sdk]
  3.1.100 [C:\Program Files\dotnet\sdk]
  3.1.119 [C:\Program Files\dotnet\sdk]
  3.1.416 [C:\Program Files\dotnet\sdk]
  5.0.104 [C:\Program Files\dotnet\sdk]
  5.0.405 [C:\Program Files\dotnet\sdk]
  5.0.408 [C:\Program Files\dotnet\sdk]
  6.0.100-rc.2.21505.57 [C:\Program Files\dotnet\sdk]
  6.0.300 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.0-rc.2.21480.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.18 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.23 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.0-rc.2.21480.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.25 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.0-rc.2.21501.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

@AliveDevil
Copy link
Collaborator

Builds are working as they are invoked using dotnet exec ikvmc.dll1, not using ikvmc.exe for NET Core, thus the runtime directory is set to the correct path in dotnet/packs.

Footnotes

  1. https://github.com/ikvm-revived/ikvm/blob/develop/IKVM.Java/IKVM.Java.props#L63

@vokounjan
Copy link
Author

I've installed your recommended version (3.1.100 SDK). It didn't fix my problem automatically but after I manually copied files from C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref to the folder where IKVMC expects them (in my case C:\Users\vokounjan\source\repos\packs\bin.Ref\3.1.0\ref) a different error message popped up.

My dotnet --info currently is:

.NET SDK (reflecting any global.json):
 Version:   6.0.300
 Commit:    8473146e7d

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19042
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\6.0.300\

Host (useful for support):
  Version: 6.0.5
  Commit:  70ae3df4a6

.NET SDKs installed:
  3.0.103 [C:\Program Files\dotnet\sdk]
  3.1.100 [C:\Program Files\dotnet\sdk]
  3.1.414 [C:\Program Files\dotnet\sdk]
  5.0.100-rc.2.20479.15 [C:\Program Files\dotnet\sdk]
  5.0.208 [C:\Program Files\dotnet\sdk]
  5.0.400-preview.21277.10 [C:\Program Files\dotnet\sdk]
  5.0.401 [C:\Program Files\dotnet\sdk]
  5.0.402 [C:\Program Files\dotnet\sdk]
  6.0.102 [C:\Program Files\dotnet\sdk]
  6.0.201 [C:\Program Files\dotnet\sdk]
  6.0.300 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.0-rc.2.20475.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0-rc.2.20475.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.19 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.0-rc.2.20475.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET runtimes or SDKs:
  https://aka.ms/dotnet-download

@vokounjan
Copy link
Author

Builds are working as they are invoked using dotnet exec ikvmc.dll1, not using ikvmc.exe for NET Core, thus the runtime directory is set to the correct path in dotnet/packs.

Nice, that's a pretty clean solution which should work with my build pipeline. Thank you.

@AliveDevil
Copy link
Collaborator

Nice, that's a pretty clean solution which should work with my build pipeline. Thank you.

Beware! That doesn't work with the distributed binaries zip file, as those include the runtime already, thus have the same issue as running ikvmc.exe.

@vokounjan
Copy link
Author

vokounjan commented May 30, 2022

Beware! That doesn't work with the distributed binaries zip file, as those include the runtime already, thus have the same issue as running ikvmc.exe.

I see it now. I thought that executing the library directly via dotnet would reference the missing libraries magically. Nevermind. 🙂

@AliveDevil
Copy link
Collaborator

Well … there we go1:
dotnet publish creates a framework dependent assembly.
dotnet publish -r <RID> --self-contained false creates a framework dependent assembly
dotnet publish -r <RID> creates a framework independent assembly/"self-contained"

Looking at IKVM.artifacts.msbuildproj2 reveals that the publish specifies a runtime identifier, thus the dotnet tooling creates a framework independent package.
Two solutions then here:

  • Provide framework dependent binaries
  • Provide self contained binaries, and include Microsoft.NetCore.App.Ref or check whether the contained ref/ directory is sufficient.

Footnotes

  1. https://docs.microsoft.com/en-us/dotnet/core/deploying/

  2. https://github.com/ikvm-revived/ikvm/blob/develop/IKVM.artifacts.msbuildproj#L29

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

Eh, probably should have spent more time fixing this when I came across it, but it was a week ago and skipped my mind since.

Run it with -nostdlib, and point it to the reference assemblies you WANT to point it to.

That will cause it to not even bother coming up with that reference assembly path, and it'll link only with the assemblies you specify as references. This makes more sense to me, as there's no requirement that you be linking to .NET Core 3 for your assembly... even if you're running the .NET Core 3 version of ikvmc.

This is how the build of IKVM.Java works. It actually piggy backs on MS Build's resolution of assemblies, and passes those to ikvmc, ignoring whatever environment ikvmc itself might have been distributed with. It's dependencies are not necessarily your dependencies.

@AliveDevil
Copy link
Collaborator

So it is completely up to ikvmc users to specify which reference framework should be used?
That is ikvmc (net461) could be used to create net6.0 projections?
Or is the core-build used to create netcoreapp3.1 up to net6.0, and net461 is only there for .NET Framework builds?

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

It's half and half at this point. The GOAL is for any version of ikvmc to be able to create any version of assembly referencing any framework. That is, you should be able to run the ikvmc .NET 3.1 version, on Linux, to generate a Framework 4.0 assembly. Because that's somewhat of a required capability for cross-targeting.

That said, there are some places in the code where I know it makes assumptions based on whether it's running on Core or Framework, that prevent this, currently.

The default case (not specifying nostdlib) should work. But I know that how it goes about locating the required reference assemblies is a bit wonky. Needs to be fixed.

But, as a work around, you should be able to just turn that off and specify exactly what you need in your build process. You should be able to point to your own reference assemblies.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

Another thing.... I think this is just going to naturally be more work on Core, forever. It was easy on Framework. You just specify -r:System.Runtime, and go with it. Types never moved, they were always there.

However on Core they've been moving things around constantly. Everything isn't piled into mscorlib or System or System.Runtime anymore. It's been split into 30+ packages. And then combined back! And then moved around! And it keeps changing. They do provide type forwards, from old assembly names to new ones. But that only helps for runtime binding. When compiling code (like MS Build does, ultimately invoking csc.exe/Roslyn), you actually have to trace down the correct reference assemblies for the correct things.

And to top it off you can't just look at some well-known path to find em all. They aren't hanging out in C:\Windows\Microsoft.NET\Framework, a path you can find in the registry. They differ not only by runtime, but by where you install the runtime. Or whether you even have a single installed runtime as in the case of runtime-independent apps.

I'm going to fix up ikvmc to properly find it's own /ref directory, in the simple case. So the Core 3.1 build will probably find the Core 3.1 reference assemblies. But this just makes the "I want to convert an assembly from the command line to .NET Core 3.1 one off" use case easier. I think any more complicated use (building for later runtimes, incorporating .NET code), is going to require something more complicated to wrap ikvmc, determine what the actual target and build environment looks like, and pass it the necessary options.

@AliveDevil
Copy link
Collaborator

One option could be creating a build for each target runtime. So one build for net4x, netcoreapp3.1, net6.0 (and only support intermediate runtimes as long as the next LTS comes around).

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

Yeah, but the cross-targeting use case isn't fixed by that, and I think that's very important. You have to be able to run dotnet build on Linux, and expect to properly output Framework assemblies. CI/CD pipelines demand it now.

I'm using csc.exe as a model here. Roslyn is written in C#, and built against whatever version of the runtime it is built against at the time. But it's more than capable of compiling earlier versions of C#, or linking them against assemblies from Framework, or Core, or different versions of Core. If you look at the command line arguments ultimately passed to Roslyn, it's like 50+ items that are just the paths to the Reference assemblies of your target at the time. The build environment for Roslyn doesn't bleed into the execution environment. But, the users' build environment does then have to do a lot of heavy lifting.

Another consideration is MSBuild. We are going to eventually have MSBuild tasks for the functionality of ikvmc. We're going to need/want it. Well, people are going to be able to expect to open that in Visual Studio and hit Build. That's going to run the ikvmc code inside Framework, but be producing .NET Core assemblies. And likewise, the same Task loaded into 'dotnet build' should be able to produce Framework assemblies even though that's a Core environment.

Another example: You are using the .NET 6 SDK to build .NET Core 3.1 projects. This works just fine right now. All the MSBuild code runs in .NET 6, but is perfectly capable of building .NET Core output.

Basically, the version of ikvmc you are running is dictated by where you are, and the version of the references and assemblies your outputting is dictated by what you're trying to make.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

To the OP: So the best way to integrate this into a build right now, if you are using MSBuild, is to construct the ikvmc command line from the data available to MSBuild. Because it already figures out the right dependencies based on the TFM and puts them into an ItemGroup.

Here's an snippet from IKVM.Java.targets:

          <ItemGroup>
            <_IkvmcArgs Include="-nostdlib" />
            <_IkvmcArgs Include="@(ReferencePathWithRefAssemblies->Distinct()->'-reference:%(FullPath)')" />
            .....
          </ItemGroup>
          <WriteLinesToFile File="$(_BuildJavaAssemblyCoreIkvmcArgsFilePath)" Lines="@(_IkvmcArgs)" Overwrite="true" />
          <Exec Command="$(_IkvmToolExecPrefix)$(Ikvmc) -out:$(_BuildJavaAssemblyCoreTempPath)$(JavaAssemblyName) @$(_BuildJavaAssemblyCoreIkvmcArgsFilePath)" />

@(ReferencePathWithRefAssemblies) is resolved by MSBuild to be the combination of the reference assemblies associated with the TFM, any references resolved with the <Reference> element, and the proper TFM-based fold of all of the <ProjectReference> and <PackageReference> items.

@NightOwl888
Copy link
Contributor

NightOwl888 commented May 30, 2022

@wasabii - Thanks for the clarification. So, for the lower level ikvmc I agree it probably doesn't make sense to put any advanced reference assembly resolution here and let the end user do it for now. For the long term, we would ideally have a higher-level tool that accepts a target framework can piggy-back off of the .NET SDK reference assembly resolution similarly to what dotnet build does.


For the short term, it seems we will need better documentation to walk through the process of using ikvmc on .NET Core a bit more.

Two possible options for documentation are

  1. Use the GitHub wiki here in Markdown (which can be source controlled on its own branch).
  2. Add Markdown files to the repo in a top level docs directory and later plan on using a tool like DocFx to convert them into a static HTML doc website.

Both of them require the docs in Markdown, so we could easily start out with option 2 and then switch to option 1. I think option 2 is simpler to get started because it requires no extra setup to get source control for docs.


Another possible way to go for a shorter-term/mid-term fix would be to provide an IKVM.Build.targets file (or similar name) with the IKVM distribution for end users to run that does something like what our build does to resolve the assemblies AND pass them to ikvmc. Users could then make use of the targets file by importing it into a purpose-built .csproj that has their target framework(s) configured and running dotnet build -t:IKVMC or something along those lines. It is still a band aid but it is a step closer to creating a ikvm build command and we could later eliminate this step and morph the IKVM.Build.targets logic into a better tool after it has been battle tested. Or maybe we could take the extra step write a wrapper tool that generates a .csproj and executes dotnet build in the background that takes a similar approach to the above manual steps.

I may be way off in suggesting this, but I am curious what synergy @wasabii can add to this idea or if he will just flat out reject it because he has a long-term idea that can be implemented in about the same timeframe this would take.


At the end of the day though, having a hard-coded SDK version at this low level is a bug. I am just not sure what to suggest to fix the code, or if this is something all users must work around until we go through the next iteration of build toolchain improvements. Any thoughts?

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

@NightOwl888 I agree with option 2.

Another possible way to go for a shorter-term/mid-term fix would be to provide an IKVM.Build.targets file (or similar name) with the IKVM distribution for end users to run that does something like what our build does to resolve the assemblies AND pass them to ikvmc. Users

#54 Is what you're talking about.

I am working on fixing ikvmc for the simple case. It should be, unless you specify something else, searching for reference assemblies in it's own '/ref' directory. And Core has support for navigating this structure from .deps.json, instead of trying to build some crazy path by replacing strings.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

So, did some research.

The early arguments of ikvmc, and the way it did things, was modeled after csc.exe of the time. Not surprising. This is still mostly applicable when viewing it from a .NET 4 perspective. csc.exe still exists on .NET 4. And works about the same. For instance, when you compile a simple test.cs file using csc.exe on .NET 4, it works.

However, looking at Core, the picture is quite different. There is no longer a csc.exe. Roslyn, in dotnet\sdk\v\Roslyn\bincore, has a csc.dll, which models the old csc.exe utility. But it doesn't actually work. In fact it works quite like I've been saying ikvmc should work.

c:\dev\foo>dotnet exec "C:\Program Files\dotnet\sdk\6.0.203\Roslyn\bincore\csc.dll" test.cs -out:test.dll
Microsoft (R) Visual C# Compiler version 4.1.0-5.22128.4 (5d10d428)
Copyright (C) Microsoft Corporation. All rights reserved.

test.cs(3,21): error CS0518: Predefined type 'System.Object' is not defined or imported
test.cs(5,25): error CS0518: Predefined type 'System.String' is not defined or imported
test.cs(5,15): error CS0518: Predefined type 'System.Void' is not defined or imported

It can't even compile a simple single file on it's own. It's not even capable of locating it's own standard lib (netstandard) without the user specifying the path to it. You have to give it everything. It doesn't even try.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

Microsoft (R) Visual C# Compiler version 4.2.0-4.22252.24 (47cdc16a)
Copyright (C) Microsoft Corporation. All rights reserved.

test.cs(3,21): error CS0518: Predefined type 'System.Object' is not defined or imported
test.cs(5,25): error CS0518: Predefined type 'System.String' is not defined or imported
test.cs(5,15): error CS0518: Predefined type 'System.Void' is not defined or imported

The .NET Framework 4 version works the same when -nostdlib is specified.

Basically, the Core version has -nostdlib enabled always. No way to disable it either.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

I should note it is the Roslyn version of csc.

So a couple options actually.

a) Include the compilation context in the ikvmc tool output, and consider it the standard library and library search path. But only do this in the case -nostdlib is not specified. We'd bundle the entire .NET Core 3 reference set in the tool ZIP, inside /ref (we're doing this now, it just can't find it).
b) Don't include the compilation context for Core. Consider Core to have -nostdlib enabled all the time. Make the user specify the path to the reference assemblies, based on his build tool or by hand or however he chooses.
c) Have the .NET Framework version of the ikvmc tool implicitly assume it's stdlib. Have the Core version not.
d) Have neither of them do so.

Part of me is favoring (d). It's the easiest on us. We actually only have to go remove some code. It produces the cleanest tool, and doesn't allow a user to accidently include a platform reference he doesn't actually intend to use his assembly with. Basically the tool (every version) would act like csc.exe does on Core.

I think I favor this over having the tool behave differently depending on whether you're running the Framework or Core version. First, we would want to push everybody to the Core way of doing things. It's just the way it is now. Get over it? We don't really want to make it easier to accidently pick Framework. And people can still use the Framework tool: they just have to specify the Framework references (mscorlib) by hand, same way MS Build does.

It means the Framework tool and the Core tool would operate the same. The point of choosing one over the other wouldn't be whether you want to produce Framework or Core assemblies, but whether you were executing it on a machine that forced you to pick one version over the other.

Worth noting, on Framework MS Build, when not using the code server, and using the new SDK, csc.exe is invoked with the -nostdlib by default. Even when targeting Framework. Since MS Build knows the references anyways.

Basically the only people not using -nostdlib are people who drop to the command line and type 'csc.exe' out of Framework 4 without knowing any better.

We'd also reduce the size of the tools drop by removing the refs directory.

This would be a breaking change of user expectations with the Framework version of the tool. But now's the time for those things. And it's not like MS didn't make this same change in their Core versions.

Anybody have any opinions?

@NightOwl888
Copy link
Contributor

Being that the path from .NET Framework build tools to .NET Core was a long and winding road with many bumps along the way, I am in favor of trying to avoid as many of those bumps as possible.

IMO, the best way to do that is to make a contract first with a standard (but somewhat limited) set of options that cover most use cases. This is sort of why I think the ikvm build tool comes first even though things like #54 may actually sit at a lower level. With the contract out of the way, the users can enjoy updates with no breaking changes to their build, but new features added little by little as they are completed.

So, with that in mind, I agree with you that we should keep the ikvmc tool in line with what .NET Core is doing even if it is more work to deal with manually than it was previously and it may result in breaking changes from prior versions of IKVM. Either of these 2 scenarios make sense to me:

  1. Start with only ikvmc, and add more docs and a tutorial so users have access to an example or set of examples that work for their platform.
  2. Start with an ikvm build dotnet tool and a subset of options on the command line. For reference assemblies, we could provide a way through a config file or environment variable that will eventually be tossed out, but we stick with the same command line contract throughout. Or perhaps it could make some attempt to automatically resolve and use an environment variable as a fallback for users who cannot get it working until the dependency resolution is solid. Of course, we also need adequate docs so this tool can be utilized. It is new, but it should somewhat resemble the dotnet build command that people are familiar with.

Either way, the earlier we create a contract so that IKVM builds don't break every time there is an upgrade, the better. I favor the latter approach in this regard because it avoids the issue of users having to migrate from option 1 to option 2 later. But of course, this assumes that users will wait for the tool to be developed, which may or may not be the case.

@wasabii
Copy link
Contributor

wasabii commented May 30, 2022

I remain a bit confused about that. (1) creates a contract that doesn't break in the future.

I also prioritize shipping something vs shipping nothing. :)

@NightOwl888
Copy link
Contributor

I remain a bit confused about that. (1) creates a contract that doesn't break in the future.

Not sure whether you actually mean (a) or (1) with this comment. If you meant (1), I am fine with that but users are going to need some hand holding both to migrate from old ikvmc to new ikvmc and then to ikvm build later.

But if we are going to introduce breaking changes to ikvmc, let's do that sooner rather than later and make sure the documentation completely reflects them so users can get some mileage out of the tools without too much trouble.

But frankly, I don't have a strong opinion either way.

@wasabii
Copy link
Contributor

wasabii commented Jun 29, 2022

So I think this bug is resolved at this point. Because the auto directory detection stuff has been removed. You have to explicitly specify the paths to the references in Core. So I'm closing it out at this point.

@wasabii wasabii closed this as completed Jun 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants