dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.08k stars 4.04k forks source link

Cannot use CSharpScript API with PublishSingleSingle option #50719

Open CyberSinh opened 3 years ago

CyberSinh commented 3 years ago

Hi,

I use the following code to run C# scripts in my software. Everything is fine under .NET 5.0 when I publish the software without PublishSingleSingle option.

ScriptOptions options = ScriptOptions.Default
   .WithMetadataResolver(ScriptMetadataResolver.Default.WithSearchPaths(new[] { RuntimeEnvironment.GetRuntimeDirectory(), AppInfo.StartupFolder }))
   .WithSourceResolver(new SourceFileResolver(ImmutableArray<string>.Empty, AppInfo.ScriptsFolder))
   .AddReferences(Assembly.GetExecutingAssembly()); // Assembly.Location returns "" with PublishSingleFile option, but using Environment.GetCommandLineArgs()[0] instead Assembly.GetExecutingAssembly() doesn't work too

Script script = CSharpScript.Create(code, options, typeof(GlobalVariables));
//Script script = CSharpScript.Create(code, null, null); // using CSharpScript with default parameter doesn't work too
script.Compile();

But when I publish my software with the PublishSingleFile option, the code doesn't work anymore and I get the error: "can't create a metadata reference to an assembly without location".

Is there is a way to reference my main assembly and/or .NET 5.0 system assemblies embedded in the main executable? Is there any code sample to use the CSharpScript API when PublishSingleFile is enabled?

Thanks for your help.

jinujoseph commented 3 years ago

cc @tmat

andersstorhaug commented 3 years ago

Simple reproduction, with .NET 5 and PublishSingleFile set to true.

await CSharpScript.EvaluateAsync(@"""Hello World!""");

The issue seems to be from Script.GetReferencesForCompilation:

var corLib = MetadataReference.CreateFromAssemblyInternal(typeof(object).GetTypeInfo().Assembly);

Maybe this workaround could be helpful for single-file publish?

public static MetadataReference GetRawMetadataReference(this Type type)
{
    unsafe
    {
        return type.Assembly.TryGetRawMetadata(out var blob, out var length)
            ? AssemblyMetadata
                .Create(ModuleMetadata.CreateFromMetadata((IntPtr) blob, length))
                .GetReference()
            : throw new InvalidOperationException($"Could not get raw metadata for type {type}");
    }
}
BlackOfWorld commented 3 years ago

Same issue here, don't know how to fix it 😿

BlackOfWorld commented 3 years ago

@andersstorhaug have you found any reliable fix for this issue?

andersstorhaug commented 3 years ago

@BlackOfWorld I haven't found a workaround for this yet, though, this StackOverflow post may be of some use.

BlackOfWorld commented 3 years ago

I think it could be solved by memory patching IL code, but I find it very hacky workaround.

andersstorhaug commented 3 years ago

Here's an example that I was able to get working by effectively (and naively) reproducing the CSharpScript API with the aforementioned mscorlib MetadataReference workaround.

Squirrelies commented 3 years ago

This just bit me in the tail feather today using .NET 6.0 RTM (6.0.100). Any update on whether this will be addressed in a future release? The workaround did not work for me when adding the assemblies to ScriptOption.Default.AddReferences() and supplying that ScriptOptions instance to CSharpScript.EvaluateAsync() in Single-File Publish mode.

1694439208 commented 2 years ago

this issue still happens anyone know a fix? (only when i publish single file)

Vic40k commented 1 year ago

Same issue, still happens.

oleg-shilo commented 1 year ago

One of CS-Script users pointed to this thread.

CS-Script recently incorporated the existing workaround for this issue so Roslyn users can use this option fo single-file published applications:

var calc = CSScript.Evaluator
                   .Eval(@"using System;
                           public class Script
                           {
                               public int Sum(int a, int b)
                               {
                                   return a+b;
                               }
                           }
                           return new Script();");

int sum = calc.Sum(1, 2);
Console.WriteLine(sum);

The complete sample can be found here.

JakeSays commented 6 months ago

I have a fix for this but it requires a couple of small changes to roslyn.

DotNetNext commented 5 months ago

I have a fix for this but it requires a couple of small changes to roslyn.

Can you share