jaredpar / basic-reference-assemblies

Produce NuPkg files that have .NET Reference assemblies as resources
MIT License
100 stars 15 forks source link

Copied code from readme giving error #22

Closed BrentCarter closed 2 years ago

BrentCarter commented 2 years ago

Hello. Thanks for this - it looks very promising and I hoped it would save me after hours of failure today... but when I copy the code from your readme verbatim (except for adding a System.IO using statement and changing the drive from p:\ to C:), I get this:

Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

Any ideas what could be causing this? Am I doing something wrong or is there a bug/omission in the library? I'm on Windows 10. I created a standard .net 5.0 console app, added using statements, and pasted your code in the Main method.

BrentCarter commented 2 years ago

And just to be clear, I get the error after running the program successfully creates the output exe and I try to run that exe from the command line.

jaredpar commented 2 years ago

And just to be clear, I get the error after running the program successfully creates the output exe and I try to run that exe from the command line.

How are you attempting to run that from the command line? One thing to make sure of is that the dotnet you're using to run the application is at least net5.0 (based on the version in the error message)

fuzl-llc commented 2 years ago

Thanks for the response. Here are my steps:

  1. I have the "C:\temp" folder open in Windows Explorer
  2. I run the code from your readme page
  3. I see the helloworld.exe file show up in the temp folder
  4. I Alt+D to seelct the Address bar
  5. I type "cmd" and hit enter to open a command prompt in that folder
  6. I type "h" and then tab and enter and the program runs

I get the error above in the command window. I also tried just double clicking the .exe file and get the same thing. Here's what I see when I run "dotnet --info" from the command line:

.NET SDK (reflecting any global.json): Version: 5.0.402 Commit: e9d3381880

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

Host (useful for support): Version: 5.0.11 Commit: f431858f8b

.NET SDKs installed: 3.1.301 [C:\Program Files\dotnet\sdk] 3.1.400 [C:\Program Files\dotnet\sdk] 5.0.101 [C:\Program Files\dotnet\sdk] 5.0.401 [C:\Program Files\dotnet\sdk] 5.0.402 [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.1.20 [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.NETCore.App 2.1.30 [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.1.20451.14 [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.WindowsDesktop.App 3.1.20 [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]

So, I have 5.0, but also a bunch of other runtimes... I assumed it would use the latest and/or the version it was built with (both of which are .NET 5). My project file has <TargetFramework>net5.0</TargetFramework> so I'm targeting 5.0. Do I somehow have to force it when I run it to use 5.0?

jaredpar commented 2 years ago

My project file has net5.0 so I'm targeting 5.0. Do I somehow have to force it when I run it to use 5.0?

The project file though isn't going to be connected to the application that you write to disk. That is just an app that is passed to dotnet exec. Hence the project file isn't going to come into play.

I assumed it would use the latest and/or the version it was built with (both of which are .NET 5).

The dotnet execution model works a bit differently than that. It doesn't really have a notion of "what it was built for", instead it just uses the .deps.json files to look for a runtime.

I re-read my README.md after looking at this bug. I need to update it a bit. The execution is more meant for in-memory execution, not on disk execution. On disk execution is a lot more complex (have to mix compilation and deployment). Gonig to clean this up.

fuzl-llc commented 2 years ago

That's great, thanks! I hope you'll be able to include info to make both in-memory and on-disk scenarios work. I'm trying to teach some of my students the very basics of how code is converted from human-readable source code to executable code. I used to just use notepad and csc.exe which made it very approachable, even for young kids. However, I want to also use the latest C# features (like top level statements) and the old csc doesn't support that... so I thought I could create a very simple console app that would compile a single source code file in a very simple way to keep my teaching approach alive.

fuzl-llc commented 2 years ago

Looks like you were updating/checking in changes right as I submitted my last comment... and you decided to just make it do in-memory only. I was able to get the on disk version to work using a helloworld.runtimeconfig.json file:

{ "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "5.0.11" } } }

But then I had to run it using "dotnet helloworld.exe" rather than just "helloworld.exe" which I what I'd like. I'll look into .deps.json as well... but seems like that may also require running with dotnet command. Any pointers in the right direction if I just want to create a "regular" app in .NET 5 using dynamic compilation that I can run like any other app from the command line, i.e. just double click "helloworld.exe" and have it run?

jaredpar commented 2 years ago

But then I had to run it using "dotnet helloworld.exe" rather than just "helloworld.exe" which I what I'd like.

In .NET Core the notion of having a direct exe is generally referred to as having an apphost file. That is a tiny bit of native code (generally a small C program) that is responsible for finding the correct dotnet runtime version and then launching a program based on that.

Generally when using an apphost file you'll see both a DLL and EXE with the same name. Example if you build a simple "hello world" C# application with an apphost you end up with: Console.exe and Console.dll. The apphost file (the exe) just looks for the right dotnet to execute the dll with.

Generating the apphost file dynamically isn't really in the scope of this project. That is another thorny problem.

fuzl-llc commented 2 years ago

Thanks for those details. While investigating this, I just noticed that apphost file concept for the first time myself - since moving to .NET core I never noticed the "ref" folder with the DLL that is the actual app that the EXE runs. So, I guess I can look into creating an apphost file or maybe a self-contained deployment but I guess that's like you said and mixing in deployment which isn't what this project is about... My only other option I can think of is to just use the old csc.exe for this little demo but it wouldn't feel very good since I guess then I'm probably relying on the old .NET Framework which may not even be on student machines in the future... a lot to think about. Anyway, thanks again.

fuzl-llc commented 2 years ago

In case anyone comes across this in the future, an acceptable workaround for my case (where we know the .NET SDK is installed) is to just simply write your own console app to create your own csc.exe and delegate to the dotnet publish command. I wanted to use all the cool dynamic compilation stuff but as you've learned above it gets complicated when you want to publish to disk. Just have your custom csc.exe program create a temporary C# project file in the same folder as the source files, and specify that you want a single file (and optionally self-contained) output exe. Then use a System.Diagnostics.Process to run "dotnet publish --runtime win-x64 --self-contained." Then copy the output exe to the folder with their source files and delete all the temporary folders/files from the publish. You now have a way to simulate the old model of just running csc.exe with the source files to compile being passed as arguments.

I realize this is somewhat ridiculous, but for my students, on day one, I REALLY wanted them to see "csc.exe MyProgram.txt -> MyProgram.exe" and that they can run MyProgram.exe from Windows Explorer or the command line just like any other Windows program, not using the dotnet command. Since we do learn this on day one, I can't get into the complexity of how .NET core is cross-platform and how we now have csc.dll and the apphost files stuff (which many seasoned developers don't even know about). All the students have just installed the .NET SDK so we know their machine can dotnet publish... so while it is a bit of a weird case, it works for me. It also means the sample program can be even simpler since top level statements and implicit usings allow for a single line of code to produce working output - no need to explain namespaces, classes, and the Main method to them just yet.

Thanks again @jaredpar for your help. If there is ever a more elegant solution to be had I'd love to be able to do everything dynamically using reference assemblies...

jaredpar commented 2 years ago

I realize this is somewhat ridiculous, but for my students, on day one, I REALLY wanted them to see "csc.exe MyProgram.txt -> MyProgram.exe" and that they can run MyProgram.exe from Windows Explorer or the command line just like any other Windows program, not using the dotnet command.

Completely understand that. I wish there was a more portable way of creating the .exe files that I could embrace here but unfortunately there is not right now.

The main purpose I wanted to solve with this was the ability to run compilations in memory easily using standard reference assemblies. That is easy in a project file but very difficult to construct at an API level. We had all manner of tricks to do it in the C# compiler test suite and I eventually factored them out to this which is pretty approachable. Really good for in memory execution which is what we mostly do. But for publish more is needed.

Thanks again @jaredpar for your help.

Glad I could help a bit here :smile: