public override CompilationResult Compile(string source) { var fileName = Path.GetTempFileName(); var sourceFileName = Path.ChangeExtension(fileName, ".fs"); var assemblyFileName = Path.ChangeExtension(fileName, ".dll"); var sourceCodeServices = new SimpleSourceCodeServices(); File.WriteAllText(sourceFileName, source); return (ToResult( sourceCodeServices .CompileToDynamicAssembly( FscArguments(assemblyFileName, sourceFileName, source), FSharpOption <Tuple <TextWriter, TextWriter> > .None))); }
public ICompilation GetFunctionCompilation(FunctionMetadata functionMetadata) { // TODO: Get debug flag from context. Set to true for now. bool debug = true; // First use the C# compiler to resolve references, to get consistenct with the C# Azure Functions programming model Script <object> script = CodeAnalysis.CSharp.Scripting.CSharpScript.Create("using System;", options: _metadataResolver.CreateScriptOptions(), assemblyLoader: AssemblyLoader.Value); Compilation compilation = script.GetCompilation(); var compiler = new SimpleSourceCodeServices(); FSharpErrorInfo[] errors = null; FSharpOption <Assembly> assemblyOption = null; string scriptFilePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(functionMetadata.ScriptFile)); try { var scriptFileBuilder = new StringBuilder(); // Write an adjusted version of the script file, prefixing some 'open' decarations foreach (string import in script.Options.Imports) { scriptFileBuilder.AppendLine("open " + import); } // Suppress undesirable warnings scriptFileBuilder.AppendLine("#nowarn \"988\""); // Set the line to match the original script scriptFileBuilder.AppendLine("# 0 @\"" + functionMetadata.ScriptFile + "\""); // Add our original script string scriptSource = GetFunctionSource(functionMetadata); scriptFileBuilder.AppendLine(scriptSource); File.WriteAllText(scriptFilePath, scriptFileBuilder.ToString()); var otherFlags = new List <string>(); // For some reason CompileToDynamicAssembly wants "fsc.exe" as the first arg, it is ignored. otherFlags.Add("fsc.exe"); // The --noframework option is used because we will shortly add references to mscorlib and FSharp.Core // as dictated by the C# reference resolver, and F# doesn't like getting multiple references to those. otherFlags.Add("--noframework"); // Add the references as reported by the C# reference resolver. foreach (var mdr in compilation.References) { if (!mdr.Display.Contains("Unresolved ")) { otherFlags.Add("-r:" + mdr.Display); } } // Above we have used the C# reference resolver to get the basic set of DLL references for the compilation. // // However F# has its own view on default options. For scripts these should include the // following framework facade references. otherFlags.Add("-r:System.Linq.dll"); // System.Linq.Expressions.Expression<T> otherFlags.Add("-r:System.Reflection.dll"); // System.Reflection.ParameterInfo otherFlags.Add("-r:System.Linq.Expressions.dll"); // System.Linq.IQueryable<T> otherFlags.Add("-r:System.Threading.Tasks.dll"); // valuetype [System.Threading.Tasks]System.Threading.CancellationToken otherFlags.Add("-r:System.IO.dll"); // System.IO.TextWriter otherFlags.Add("-r:System.Net.Requests.dll"); // System.Net.WebResponse etc. otherFlags.Add("-r:System.Collections.dll"); // System.Collections.Generic.List<T> otherFlags.Add("-r:System.Runtime.Numerics.dll"); // BigInteger otherFlags.Add("-r:System.Threading.dll"); // OperationCanceledException otherFlags.Add("-r:System.Runtime.dll"); otherFlags.Add("-r:System.Numerics.dll"); if (debug) { otherFlags.Add("--optimize-"); otherFlags.Add("--debug+"); otherFlags.Add("--tailcalls-"); } // This output DLL isn't actually written by FSharp.Compiler.Service when CompileToDynamicAssembly is called otherFlags.Add("--out:" + Path.ChangeExtension(Path.GetTempFileName(), "dll")); // Get the #load closure var loadFileOptionsAsync = FSharpChecker.Create().GetProjectOptionsFromScript(functionMetadata.ScriptFile, scriptSource, null, null, null); var loadFileOptions = FSharp.Control.FSharpAsync.RunSynchronously(loadFileOptionsAsync, null, null); foreach (var loadedFileName in loadFileOptions.ProjectFileNames) { if (Path.GetFileName(loadedFileName) != Path.GetFileName(functionMetadata.ScriptFile)) { otherFlags.Add(loadedFileName); } } // Add the (adjusted) script file itself otherFlags.Add(scriptFilePath); // Make the output streams (unused) var outStreams = FSharpOption <Tuple <TextWriter, TextWriter> > .Some(new Tuple <TextWriter, TextWriter>(Console.Out, Console.Error)); // Compile the script to a dynamic assembly var result = compiler.CompileToDynamicAssembly(otherFlags: otherFlags.ToArray(), execute: outStreams); errors = result.Item1; assemblyOption = result.Item3; } finally { File.Delete(scriptFilePath); } return(new FSharpCompilation(errors, assemblyOption)); }