roslyn icon indicating copy to clipboard operation
roslyn copied to clipboard

Cannot use CSharpScript API with PublishSingleSingle option

Open CyberSinh opened this issue 4 years ago • 13 comments
trafficstars

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.

CyberSinh avatar Jan 23 '21 00:01 CyberSinh

cc @tmat

jinujoseph avatar Jan 29 '21 00:01 jinujoseph

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}");
    }
}

andersstorhaug avatar Mar 18 '21 22:03 andersstorhaug

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

BlackOfWorld avatar Jun 16 '21 16:06 BlackOfWorld

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

BlackOfWorld avatar Jun 20 '21 12:06 BlackOfWorld

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

andersstorhaug avatar Jun 22 '21 17:06 andersstorhaug

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

BlackOfWorld avatar Jun 22 '21 19:06 BlackOfWorld

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.

andersstorhaug avatar Jun 24 '21 18:06 andersstorhaug

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<T>() in Single-File Publish mode.

Squirrelies avatar Nov 11 '21 17:11 Squirrelies

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

1694439208 avatar Oct 13 '22 07:10 1694439208

Same issue, still happens.

Vic40k avatar Jan 06 '23 17:01 Vic40k

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.

oleg-shilo avatar Oct 01 '23 23:10 oleg-shilo

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

JakeSays avatar May 11 '24 21:05 JakeSays

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

Can you share

DotNetNext avatar Jun 01 '24 11:06 DotNetNext