public Task <IDotNetCompilation> GetFunctionCompilationAsync(FunctionMetadata functionMetadata)
        {
            // First use the C# compiler to resolve references, to get consistency with the C# Azure Functions programming model
            // Add the #r statements from the .fsx file to the resolver source
            string scriptSource          = GetFunctionSource(functionMetadata);
            var    resolverSourceBuilder = new StringBuilder();

            using (StringReader sr = new StringReader(scriptSource))
            {
                string line;

                while ((line = sr.ReadLine()) != null)
                {
                    if (_hashRRegex.IsMatch(line))
                    {
                        resolverSourceBuilder.AppendLine(line);
                    }
                }
            }

            resolverSourceBuilder.AppendLine("using System;");
            var resolverSource = resolverSourceBuilder.ToString();

            Script <object> script = CodeAnalysis.CSharp.Scripting.CSharpScript.Create(resolverSource, options: _metadataResolver.CreateScriptOptions(), assemblyLoader: AssemblyLoader.Value);

            var compiler = new SimpleSourceCodeServices(msbuildEnabled: FSharpOption <bool> .Some(false));

            FSharpErrorInfo[] errors        = null;
            byte[]            assemblyBytes = null;
            byte[]            pdbBytes      = null;

            string scriptPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            Directory.CreateDirectory(scriptPath);

            string scriptFilePath = Path.Combine(scriptPath, Path.GetFileName(functionMetadata.ScriptFile));

            var assemblyName     = Utility.GetAssemblyNameFromMetadata(functionMetadata, Guid.NewGuid().ToString());
            var assemblyFileName = Path.Combine(scriptPath, assemblyName + ".dll");
            var pdbName          = Path.ChangeExtension(assemblyFileName, PlatformHelper.IsWindows ? "pdb" : "dll.mdb");

            try
            {
                var scriptFileBuilder = new StringBuilder();

                // Write an adjusted version of the script file, prefixing some 'open' declarations
                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
                scriptFileBuilder.AppendLine(scriptSource);

                File.WriteAllText(scriptFilePath, scriptFileBuilder.ToString());

                var otherFlags = new List <string>();

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

                var references = script.GetCompilation().References
                                 .Where(m => !(m is UnresolvedMetadataReference))
                                 .Select(m => "-r:" + m.Display)
                                 .Distinct(new FileNameEqualityComparer());

                // Add the references as reported by the metadata resolver.
                otherFlags.AddRange(references);

                if (_optimizationLevel == OptimizationLevel.Debug)
                {
                    otherFlags.Add("--optimize-");
                    otherFlags.Add("--debug+");
                    otherFlags.Add("--tailcalls-");
                }

                // TODO: FACAVAL verify if this still applies to core
                if (!PlatformHelper.IsWindows)
                {
                    var monoDir    = Path.GetDirectoryName(typeof(string).Assembly.Location);
                    var facadesDir = Path.Combine(monoDir, "Facades");
                    otherFlags.Add("--lib:" + facadesDir);
                }

                // If we have a private assembly folder, make sure the compiler uses it to resolve dependencies
                string privateAssembliesFolder = Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName);
                if (Directory.Exists(privateAssembliesFolder))
                {
                    otherFlags.Add("--lib:" + Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName));
                }

                otherFlags.Add("--out:" + assemblyFileName);

                // Get the #load closure
                FSharpChecker checker = FSharpChecker.Create(null, null, null, msbuildEnabled: FSharpOption <bool> .Some(false));
                var           loadFileOptionsAsync = checker.GetProjectOptionsFromScript(
                    filename: functionMetadata.ScriptFile,
                    source: scriptSource,
                    loadedTimeStamp: null,
                    otherFlags: null,
                    useFsiAuxLib: null,
                    assumeDotNetFramework: null,
                    extraProjectInfo: null);

                var loadFileOptions = FSharp.Control.FSharpAsync.RunSynchronously(loadFileOptionsAsync, null, null);
                foreach (var loadedFileName in loadFileOptions.Item1.ProjectFileNames)
                {
                    if (Path.GetFileName(loadedFileName) != Path.GetFileName(functionMetadata.ScriptFile))
                    {
                        otherFlags.Add(loadedFileName);
                    }
                }

                // Add the (adjusted) script file itself
                otherFlags.Add(scriptFilePath);

                // Compile the script to a static assembly
                var result = compiler.Compile(otherFlags.ToArray());
                errors = result.Item1;
                var code = result.Item2;

                if (code == 0)
                {
                    assemblyBytes = File.ReadAllBytes(assemblyFileName);
                    pdbBytes      = null;
                    if (File.Exists(pdbName))
                    {
                        pdbBytes = File.ReadAllBytes(pdbName);
                    }
                }
                else
                {
                    string message = $"F# compilation failed with arguments: {string.Join(" ", otherFlags)}";
                    _logger.LogDebug(message);
                }
            }
            finally
            {
                DeleteDirectoryAsync(scriptPath, recursive: true)
                .ContinueWith(
                    t => t.Exception.Handle(e =>
                {
                    string message = $"Unable to delete F# compilation file: {e.ToString()}";
                    _logger.LogWarning(message);
                    return(true);
                }), TaskContinuationOptions.OnlyOnFaulted);
            }

            return(Task.FromResult <IDotNetCompilation>(new FSharpCompilation(errors, assemblyBytes, pdbBytes)));
        }
예제 #2
0
        public ICompilation GetFunctionCompilation(FunctionMetadata functionMetadata)
        {
            // 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(msbuildEnabled: FSharpOption <bool> .Some(false));

            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 (_optimizationLevel == OptimizationLevel.Debug)
                {
                    otherFlags.Add("--optimize-");
                    otherFlags.Add("--debug+");
                    otherFlags.Add("--tailcalls-");
                }

                // If we have a private assembly folder, make sure the compiler uses it to resolve dependencies
                string privateAssembliesFolder = Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName);
                if (Directory.Exists(privateAssembliesFolder))
                {
                    otherFlags.Add("--lib:" + Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName));
                }

                // 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
                FSharpChecker checker = FSharpChecker.Create(null, null, null, msbuildEnabled: FSharpOption <bool> .Some(false));
                var           loadFileOptionsAsync = checker.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));
        }
예제 #3
0
        static void Main(string[] args)
        {
            FSharpChecker fSharpChecker = FSharpChecker.Create(null, null, null, null, null, null, null, null);

            var fullPath   = Path.GetFullPath("..\\..\\..\\..\\..\\boolMutateTestApp\\boolMutateTestApp\\Program.fs");
            var sourceCode = File.ReadAllText(fullPath);

            Tuple <FSharpProjectOptions, FSharpList <FSharpErrorInfo> > fsharpoptions = FSharpAsync.RunSynchronously(fSharpChecker.GetProjectOptionsFromScript(fullPath, SourceText.ofString(sourceCode), null, null, null, null, null, null, null, null, null), null, null);
            FSharpParseFileResults result = FSharpAsync.RunSynchronously(fSharpChecker.ParseFile(fullPath, SourceText.ofString(sourceCode), fSharpChecker.GetParsingOptionsFromProjectOptions(fsharpoptions.Item1).Item1, null), null, null);
            var syntaxTree = result.ParseTree;

            if (syntaxTree.Value.IsImplFile)
            {
                var test = ((ImplFile)syntaxTree.Value).Item;
                VisitModulesAndNamespaces(test.modules);
            }
            else
            {
                Console.WriteLine("F# Interface file (*.fsi) not supported.");
            }
            //fSharpChecker.CompileToDynamicAssembly()
            //    FSharpAsync<Tuple<FSharpErrorInfo[], int>> Compile(FSharpList<SyntaxTree.ParsedInput> ast, string assemblyName, string outFile, FSharpList<string> dependencies, [OptionalArgument] FSharpOption<string> pdbFile, [OptionalArgument] FSharpOption<bool> executable, [OptionalArgument] FSharpOption<bool> noframework, [OptionalArgument] FSharpOption<string> userOpName);
        }
예제 #4
0
        public ICompilation GetFunctionCompilation(FunctionMetadata functionMetadata)
        {
            // First use the C# compiler to resolve references, to get consistency with the C# Azure Functions programming model
            // Add the #r statements from the .fsx file to the resolver source
            string scriptSource          = GetFunctionSource(functionMetadata);
            var    resolverSourceBuilder = new StringBuilder();

            using (StringReader sr = new StringReader(scriptSource))
            {
                string line;

                while ((line = sr.ReadLine()) != null)
                {
                    if (_hashRRegex.IsMatch(line))
                    {
                        resolverSourceBuilder.AppendLine(line);
                    }
                }
            }

            resolverSourceBuilder.AppendLine("using System;");
            var resolverSource = resolverSourceBuilder.ToString();

            Script <object> script      = CodeAnalysis.CSharp.Scripting.CSharpScript.Create(resolverSource, options: _metadataResolver.CreateScriptOptions(), assemblyLoader: AssemblyLoader.Value);
            Compilation     compilation = script.GetCompilation();

            var compiler = new SimpleSourceCodeServices(msbuildEnabled: FSharpOption <bool> .Some(false));

            FSharpErrorInfo[]       errors         = null;
            FSharpOption <Assembly> assemblyOption = null;
            string scriptFilePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(functionMetadata.ScriptFile));

            var asmName = FunctionAssemblyLoader.GetAssemblyNameFromMetadata(functionMetadata, compilation.AssemblyName);
            var dllName = Path.GetTempPath() + asmName + ".dll";
            var pdbName = Path.ChangeExtension(dllName, "pdb");

            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
                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 (_optimizationLevel == OptimizationLevel.Debug)
                {
                    otherFlags.Add("--optimize-");
                    otherFlags.Add("--debug+");
                    otherFlags.Add("--tailcalls-");
                }

                // If we have a private assembly folder, make sure the compiler uses it to resolve dependencies
                string privateAssembliesFolder = Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName);
                if (Directory.Exists(privateAssembliesFolder))
                {
                    otherFlags.Add("--lib:" + Path.Combine(Path.GetDirectoryName(functionMetadata.ScriptFile), DotNetConstants.PrivateAssembliesFolderName));
                }

                otherFlags.Add("--out:" + dllName);

                // Get the #load closure
                FSharpChecker checker = FSharpChecker.Create(null, null, null, msbuildEnabled: FSharpOption <bool> .Some(false));
                var           loadFileOptionsAsync = checker.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);

                // Compile the script to a static assembly
                var result = compiler.Compile(otherFlags.ToArray());
                errors = result.Item1;
                var code = result.Item2;

                if (code == 0)
                {
                    var    assemblyBytes = File.ReadAllBytes(dllName);
                    byte[] pdbBytes      = null;
                    if (File.Exists(pdbName))
                    {
                        pdbBytes = File.ReadAllBytes(pdbName);
                    }
                    var assembly = Assembly.Load(assemblyBytes, pdbBytes);
                    assemblyOption = FSharpOption <Assembly> .Some(assembly);
                }
            }
            finally
            {
                Task.WhenAll(DeleteIfExistsAsync(scriptFilePath), DeleteIfExistsAsync(dllName), DeleteIfExistsAsync(pdbName))
                .ContinueWith(t => t.Exception.Handle(e =>
                {
                    // TODO: Trace
                    return(true);
                }), TaskContinuationOptions.OnlyOnFaulted);
            }

            return(new FSharpCompilation(errors, assemblyOption));
        }