oleg-shilo / cs-script.core

.NET Core port of CS-Script
MIT License
100 stars 18 forks source link

LoadFile fails when using dotnet publish -p:PublishSingleFile=true >> (Value cannot be null. Parameter ('path') #21

Open janmechtel opened 3 years ago

janmechtel commented 3 years ago

First of all thanks alot for CS-script!

At the moment, the below is more a cosmetic issue, It would be nice to have fewer files to deploy.

I'm happy to investigate, but don't worry to close this if it's weird.

The problem only occurs when i add -p:PublishSingleFile=true to donet publish

If I build and publish like this:

dotnet publish --configuration Release -r win-x64 -p:PublishSingleFile=true

During runtime, when loading the scripts with LoadFile this happens:

ERROR: System.ArgumentNullException: Value cannot be null. (Parameter 'path')
   at System.IO.Path.GetFullPath(String path)
   at CSScriptLib.EvaluatorBase`1.<>c.<GetReferencedAssemblies>b__22_1(String x)
   at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()
   at System.Linq.Set`1.UnionWith(IEnumerable`1 other)
   at System.Linq.Enumerable.DistinctIterator`1.FillSet()
   at System.Linq.Enumerable.DistinctIterator`1.ToArray()
   at CSScriptLib.EvaluatorBase`1.GetReferencedAssemblies(String code, String[] searchDirs)
   at CSScriptLib.EvaluatorBase`1.ReferenceAssembliesFromCode(String code, String[] searchDirs)
   at CSScriptLib.RoslynEvaluator.Compile(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.CompileCode(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.LoadFile(String scriptFile, Object[] args)
   at DynamicsTransformer.Program.LoadTransformations()
   at DynamicsTransformer.Program.ProcessDocuments(Boolean dryRun)
   at DynamicsTransformer.Program.Main()
sportismygame commented 3 years ago

I also experienced the same today. Seems a .NET issue, as mentioned here:

https://stackoverflow.com/questions/58428375/cannot-get-original-executable-path-for-net-core-3-0-single-file-ppublishsin

https://github.com/dotnet/runtime/issues/13051

After doing some basic tests I managed to run the script in a single file application switching from

dynamic script = CSScript.Evaluator.LoadMethod(scriptToRun);

to

dynamic script = (new CodeDomEvaluator()).LoadMethod(scriptToRun);
oleg-shilo commented 3 years ago

A few points.

sportismygame commented 3 years ago

Thank you Oleg! (and thanks for this great library) After rewriting some code today I found that property of CSScript and wanted to edit my post but you were faster!

Oleg, since you mentioned this

CodeDomEvaluator is actually superior to the RoslynEvaluator but it does require pretense of .NET SDK

Does this mean that this bundled executable won't run on a machine without the .NET SDK installed? It is required then to publish it with the .NET embedded (is one of the options of publishing, makes the executable much bigger)?

Thank you

sportismygame commented 3 years ago

Indeed I get this error when I run the application on a machine without .NET SDK installed: image

It says it is a warning, is there a way of bypassing this?

janmechtel commented 3 years ago

Thanks for figuring this out.

I've briefly tried switching to: dynamic script = CSScript.CodeDomEvaluator.LoadMethod(scriptToRun);

But have run into other more issues afterwards.

At first the path for the sub-directory from where i want to load the script file is not working anymore (with .Evaluator. it was?).

Secondly even when I copied the scripts to the location where it was trying to load from I got another error.

Since it's seems that .NET SDK is required, I wouldn't gain the benefit of having a single file. I'm fine with closing this @sportismygame

FIRST ERROR: .\Transformations\ leads to \Transformations\Transformations\ ?

ERROR: System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\XXX\bin\Release\net5.0\win-x64\publish\Transformations\Transformations\MainTransform.cs'.
   at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle)
   at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks)
   at System.IO.File.InternalReadAllText(String path, Encoding encoding)
   at System.IO.File.ReadAllText(String path)
   at CSScriptLib.CSharpParser.Construct(String script, Boolean isFile, String[] directivesToSearch, String[] probingDirs)
   at CSScriptLib.CSharpParser..ctor(String script, Boolean isFile, String[] directivesToSearch, String[] probingDirs)
   at CSScriptLib.FileParser.ProcessFile()
   at CSScriptLib.FileParser..ctor(String fileName, ParsingParams prams, Boolean process, Boolean imported, String[] searchDirs, Boolean throwOnError)
   at CSScriptLib.ScriptParser.Init(String fileName, String[] searchDirs)
   at CSScriptLib.ScriptParser..ctor(String fileName, String[] searchDirs, Boolean throwOnError)
   at CSScriptLib.ProjectBuilder.GenerateProjectFor(String script)
   at CSScriptLib.Project.GenerateProjectFor(String script)
   at CSScriptLib.CodeDomEvaluator.Compile(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.CompileCode(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.LoadFile(String scriptFile, Object[] args)
   at XXX.Program.LoadTransformations()
   at XXX.Program.ProcessDocuments(Boolean dryRun)
   at XXX.Program.Main()

SECOND ERROR:

ERROR: System.ArgumentNullException: Value cannot be null. (Parameter 'value')
   at System.String.StartsWith(String value, StringComparison comparisonType)
   at CSScriptLib.CoreExtensions.IsSharedAssembly(String path)
   at CSScriptLib.CodeDomEvaluator.<>c.<CompileAssemblyFromFileBatch_with_Csc>b__4_0(String x)
   at System.Linq.Utilities.<>c__DisplayClass1_0`1.<CombinePredicates>b__0(TSource x)
   at System.Linq.Enumerable.WhereArrayIterator`1.ToList()
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at CSScriptLib.CodeDomEvaluator.CompileAssemblyFromFileBatch_with_Csc(String[] fileNames, String[] ReferencedAssemblies, String outAssembly, Boolean IncludeDebugInformation, CompileInfo info)
   at CSScriptLib.CodeDomEvaluator.Compile(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.CompileCode(String scriptText, String scriptFile, CompileInfo info)
   at CSScriptLib.EvaluatorBase`1.LoadFile(String scriptFile, Object[] args)
   at XXX.Program.LoadTransformations()
   at XXX.Program.ProcessDocuments(Boolean dryRun)
   at XXX.Program.Main()
oleg-shilo commented 3 years ago

Sorry for the delay with the response.

It says it is a warning, is there a way of bypassing this?

Yes indeed it is a deployment constrain. You see, since ~2004 MS was always releasing .NET runtime with the compilers included. But starting from 2012 (C#5). They stopped it. And the only way to compile any syntax higher than C#5 was to distribute Roslyn compilers along with your app (~20M).

While .NET Core/5 has made it much easier to get Roslyn binaries (compilers) it still does not include them in the .NET Runtime distro but to SDK only.

Thus sadly, no there is no work around for this. Meaning that if you have the luxury of using .NET 5 SDK you can host CS-Script engine that tunnels the compilation to either

  1. Roslyn compiler (csc.exe) - CSScript.CodeDomEvaluator mode
  2. Roslyn scripting (Microsoft.CodeAnalysis.CSharp.Scripting.dll) - CSScript.RoslynEvaluator mode

But if you rely on .NET 5 Runtime then only option 2 is available. :(