oleg-shilo / cs-script

C# scripting platform
http://www.cs-script.net
MIT License
1.57k stars 234 forks source link

Add support for response file to pass compiler options #296

Open rp0815 opened 2 years ago

rp0815 commented 2 years ago

Under some circumstances the command line for running the csc.exe becomes way too long to be started with Process.Start(). OK, this limit usually is 32KiB, but having many referenced assemblies with full pathes will easily lead to those long command lines.

There is a common way to make it work nevertheless - response files. All or a part of the compiler options can be written into a response file which then is passed to csc.exe using option "@".

For version 4.4.4-pre I manipulated file Evaluator.CodeDom.cs at line 307 to write all references into a file and pass it as response file. Have a look at the code.

#if false
                    // the original command line
                    cmd = $@" {common_args.JoinBy(" ")} /out:""{assembly}"" {refs_args.JoinBy(" ")} {source_args.JoinBy(" ")} ";
#else
                    // create the command line but insert all refs into the response file
                    // but all options could go into this file, thus cmd would be "<path>\csc.exe @script.rsp" only
                    var responseFile = "script.rsp";
                    var fullResponseFile = Path.Combine(build_dir, responseFile);
                    using (var wr = new StreamWriter(fullResponseFile))
                    {
                        foreach (var r in refs_args)
                            wr.WriteLine(r);
                    }
                    Debug.WriteLine($"refs written to temp file '{fullResponseFile}'");

                    cmd = $@" {common_args.JoinBy(" ")} /out:""{assembly}"" @""{responseFile}"" {source_args.JoinBy(" ")} ";
                    Debug.WriteLine($"cmd '{cmd}'");
#endif

This can be made better: switch to activate response file support, write all options into this file.

Having this function would let us use the current cs-script instead of the old .NET framework version.

oleg-shilo commented 2 years ago

Hosted

CSScript.EvaluatorConfig.CompilerOptions = "...";

CLI https://github.com/oleg-shilo/cs-script/wiki/CS-Script---Command-Line-Interface#-cooptions

rp0815 commented 2 years ago

I tried to use EvaluatorConfig.CompilerOptions = "@responsefile.rsp" and it did not work. csc.exe could not locate the response file. I tried to get the AppDomain's assemblies and write their pathes into responsefile.rsp which is located in the same folder as the main script file. But, the response file is not copied to the .build folder, where csc.exe expects it. If I set the complete path of the response file csc.exe simply concatenates this path to the .build path and again does not find the response file. Thatswhy I manipulated Evaluator.CodeDom.cs to see if it would work in general - and it does.

oleg-shilo commented 2 years ago

Sorry, I misinterpret your issue description. So please ignore my first response.

And to make it a bit simpler can you please use "code-syntax" blocks in the descriptions so it is easier to read the code. I have fixed your first message in this thread.

Agree, that having a compiler options file is a handy option, particularly for debugging purposes. And I like your very proposed syntax.

Changing the issue to "enhancement"

oleg-shilo commented 2 years ago

Tested your scenario and it seems to work with the complete path. See the test case in the ece3704

        [Fact]
        public void use_resp_file()
        {
            var respFile = $"{nameof(use_resp_file)}.resp".GetFullPath();
            File.WriteAllText(respFile, "/r:Foo.dll");

            CSScript.EvaluatorConfig.CompilerOptions = $"\"@{respFile}\"";

            try
            {
                dynamic script = CSScript.CodeDomEvaluator
                                         .LoadMethod(@"public (int, int) func()
                                                   {
                                                       return (0,5);
                                                   }");
            }
            catch (CompilerException e)
            {
                Assert.Contains("Metadata file 'Foo.dll' could not be found", e.Message);
            }
        }