@@ -9,6 +9,7 @@ open System
99open System.IO
1010open System.Text
1111open System.Threading
12+ open System.Reflection .Emit
1213open System.Runtime
1314open System.Collections .Generic
1415
@@ -23,6 +24,7 @@ open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
2324open Microsoft.FSharp .Compiler .AccessibilityLogic
2425open Microsoft.FSharp .Compiler .Ast
2526open Microsoft.FSharp .Compiler .CompileOps
27+ open Microsoft.FSharp .Compiler .Driver
2628open Microsoft.FSharp .Compiler .ErrorLogger
2729open Microsoft.FSharp .Compiler .Lib
2830open Microsoft.FSharp .Compiler .MSBuildResolver
@@ -2123,6 +2125,115 @@ module Helpers =
21232125 let AreSubsumable3 (( fileName1 : string , _ , o1 : FSharpProjectOptions ),( fileName2 : string , _ , o2 : FSharpProjectOptions )) =
21242126 ( fileName1 = fileName2)
21252127 && FSharpProjectOptions.AreSubsumable( o1, o2)
2128+
2129+ module CompileHelpers =
2130+ let mkCompilationErorHandlers () =
2131+ let errors = ResizeArray<_>()
2132+
2133+ let errorSink warn exn =
2134+ let mainError , relatedErrors = SplitRelatedErrors exn
2135+ let oneError trim e = errors.Add( ErrorInfo.CreateFromException ( e, warn, trim, Range.range0))
2136+ oneError false mainError
2137+ List.iter ( oneError true ) relatedErrors
2138+
2139+ let errorLogger =
2140+ { new ErrorLogger( " CompileAPI" ) with
2141+ member x.WarnSinkImpl ( exn ) = errorSink true exn
2142+ member x.ErrorSinkImpl ( exn ) = errorSink false exn
2143+ member x.ErrorCount = errors |> Seq.filter ( fun e -> e.Severity = FSharpErrorSeverity.Error) |> Seq.length }
2144+
2145+ let loggerProvider =
2146+ { new ErrorLoggerProvider() with
2147+ member x.CreateErrorLoggerThatQuitsAfterMaxErrors ( _tcConfigBuilder , _exiter ) = errorLogger }
2148+ errors, errorLogger, loggerProvider
2149+
2150+ let tryCompile errorLogger f =
2151+ use unwindParsePhase = PushThreadBuildPhaseUntilUnwind ( BuildPhase.Parse)
2152+ use unwindEL_2 = PushErrorLoggerPhaseUntilUnwind ( fun _ -> errorLogger)
2153+ let exiter = { new Exiter with member x.Exit n = raise StopProcessing }
2154+ try
2155+ f exiter
2156+ 0
2157+ with e ->
2158+ stopProcessingRecovery e Range.range0
2159+ 1
2160+
2161+ /// Compile using the given flags. Source files names are resolved via the FileSystem API. The output file must be given by a -o flag.
2162+ let compileFromArgs ( argv : string [], tcImportsCapture , dynamicAssemblyCreator ) =
2163+
2164+ let errors , errorLogger , loggerProvider = mkCompilationErorHandlers()
2165+ let result =
2166+ tryCompile errorLogger ( fun exiter ->
2167+ mainCompile ( argv, (* bannerAlreadyPrinted*) true , (* openBinariesInMemory*) true , exiter, loggerProvider, tcImportsCapture, dynamicAssemblyCreator) )
2168+
2169+ errors.ToArray(), result
2170+
2171+ let compileFromAsts ( asts , assemblyName , outFile , dependencies , noframework , pdbFile , executable , tcImportsCapture , dynamicAssemblyCreator ) =
2172+
2173+ let errors , errorLogger , loggerProvider = mkCompilationErorHandlers()
2174+
2175+ let executable = defaultArg executable true
2176+ let target = if executable then CompilerTarget.ConsoleExe else CompilerTarget.Dll
2177+
2178+ let result =
2179+ tryCompile errorLogger ( fun exiter ->
2180+ compileOfAst ( (* openBinariesInMemory=*) true , assemblyName, target, outFile, pdbFile, dependencies, noframework, exiter, loggerProvider, asts, tcImportsCapture, dynamicAssemblyCreator))
2181+
2182+ errors.ToArray(), result
2183+
2184+ let dynamicAssemblyCreator ( debugInfo : bool , tcImportsRef : TcImports option ref , execute : _ option , assemblyBuilderRef : _ option ref ) ( _tcConfig , ilGlobals , _errorLogger , outfile , _pdbfile , ilxMainModule , _signingInfo ) =
2185+
2186+ // Create an assembly builder
2187+ let assemblyName = System.Reflection.AssemblyName( System.IO.Path.GetFileNameWithoutExtension outfile)
2188+ let flags = System.Reflection.Emit.AssemblyBuilderAccess.Run
2189+ #if FX_ NO_ APP_ DOMAINS
2190+ let assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly( assemblyName, flags)
2191+ let moduleBuilder = assemblyBuilder.DefineDynamicModule( " IncrementalModule" )
2192+ #else
2193+ let assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, flags)
2194+ let moduleBuilder = assemblyBuilder.DefineDynamicModule( " IncrementalModule" , debugInfo)
2195+ #endif
2196+ // Omit resources in dynamic assemblies, because the module builder is constructed without a filename the module
2197+ // is tagged as transient and as such DefineManifestResource will throw an invalid operation if resources are present.
2198+ //
2199+ // Also, the dynamic assembly creator can't currently handle types called "<Module>" from statically linked assemblies.
2200+ let ilxMainModule =
2201+ { ilxMainModule with
2202+ TypeDefs = ilxMainModule.TypeDefs.AsList |> List.filter ( fun td -> not ( isTypeNameForGlobalFunctions td.Name)) |> mkILTypeDefs
2203+ Resources= mkILResources [] }
2204+
2205+ // The function used to resolve typees while emitting the code
2206+ let assemblyResolver s =
2207+ match tcImportsRef.Value.Value.TryFindExistingFullyQualifiedPathFromAssemblyRef s with
2208+ | Some res -> Some ( Choice1Of2 res)
2209+ | None -> None
2210+
2211+ // Emit the code
2212+ let _emEnv , execs = ILRuntimeWriter.emitModuleFragment( ilGlobals, ILRuntimeWriter.emEnv0, assemblyBuilder, moduleBuilder, ilxMainModule, debugInfo, assemblyResolver)
2213+
2214+ // Execute the top-level initialization, if requested
2215+ if execute.IsSome then
2216+ for exec in execs do
2217+ match exec() with
2218+ | None -> ()
2219+ | Some exn -> raise exn
2220+
2221+ // Register the reflected definitions for the dynamically generated assembly
2222+ for resource in ilxMainModule.Resources.AsList do
2223+ if IsReflectedDefinitionsResource resource then
2224+ Quotations.Expr.RegisterReflectedDefinitions( assemblyBuilder, moduleBuilder.Name, resource.Bytes)
2225+
2226+ // Save the result
2227+ assemblyBuilderRef := Some assemblyBuilder
2228+
2229+ let setOutputStreams execute =
2230+ // Set the output streams, if requested
2231+ match execute with
2232+ | Some ( writer, error) ->
2233+ System.Console.SetOut writer
2234+ System.Console.SetError error
2235+ | None -> ()
2236+
21262237
21272238// There is only one instance of this type, held in FSharpChecker
21282239type BackgroundCompiler ( projectCacheSize , keepAssemblyContents , keepAllBackgroundResolutions ) as self =
@@ -2697,6 +2808,66 @@ type FSharpChecker(projectCacheSize, keepAssemblyContents, keepAllBackgroundReso
26972808
26982809 member ic.TryGetRecentTypeCheckResultsForFile ( filename , options , ? source ) = ic.TryGetRecentCheckResultsForFile( filename, options,? source= source)
26992810
2811+ member ic.Compile ( argv : string []) =
2812+ CompileHelpers.compileFromArgs ( argv, None, None)
2813+
2814+ member ic.Compile ( ast : ParsedInput list , assemblyName : string , outFile : string , dependencies : string list , ? pdbFile : string , ? executable : bool , ? noframework : bool ) =
2815+ let noframework = defaultArg noframework false
2816+ CompileHelpers.compileFromAsts ( ast, assemblyName, outFile, dependencies, noframework, pdbFile, executable, None, None)
2817+
2818+ member ic.CompileToDynamicAssembly ( otherFlags : string [], execute : ( TextWriter * TextWriter ) option ) =
2819+ CompileHelpers.setOutputStreams execute
2820+
2821+ // References used to capture the results of compilation
2822+ let tcImportsRef = ref ( None: TcImports option)
2823+ let assemblyBuilderRef = ref None
2824+ let tcImportsCapture = Some ( fun tcImports -> tcImportsRef := Some tcImports)
2825+
2826+ // Function to generate and store the results of compilation
2827+ let debugInfo = otherFlags |> Array.exists ( fun arg -> arg = " -g" || arg = " --debug:+" || arg = " /debug:+" )
2828+ let dynamicAssemblyCreator = Some ( CompileHelpers.dynamicAssemblyCreator ( debugInfo, tcImportsRef, execute, assemblyBuilderRef))
2829+
2830+ // Perform the compilation, given the above capturing function.
2831+ let errorsAndWarnings , result = CompileHelpers.compileFromArgs ( otherFlags, tcImportsCapture, dynamicAssemblyCreator)
2832+
2833+ // Retrieve and return the results
2834+ let assemblyOpt =
2835+ match assemblyBuilderRef.Value with
2836+ | None -> None
2837+ | Some a -> Some ( a :> System.Reflection.Assembly)
2838+
2839+ errorsAndWarnings, result, assemblyOpt
2840+
2841+ member ic.CompileToDynamicAssembly ( asts : ParsedInput list , assemblyName : string , dependencies : string list , execute : ( TextWriter * TextWriter ) option , ? debug : bool , ? noframework : bool ) =
2842+ CompileHelpers.setOutputStreams execute
2843+
2844+ // References used to capture the results of compilation
2845+ let tcImportsRef = ref ( None: TcImports option)
2846+ let assemblyBuilderRef = ref None
2847+ let tcImportsCapture = Some ( fun tcImports -> tcImportsRef := Some tcImports)
2848+
2849+ let debugInfo = defaultArg debug false
2850+ let noframework = defaultArg noframework false
2851+ let location = Path.Combine( Path.GetTempPath(), " test" + string( hash assemblyName))
2852+ try Directory.CreateDirectory( location) |> ignore with _ -> ()
2853+
2854+ let outFile = Path.Combine( location, assemblyName + " .dll" )
2855+
2856+ // Function to generate and store the results of compilation
2857+ let dynamicAssemblyCreator = Some ( CompileHelpers.dynamicAssemblyCreator ( debugInfo, tcImportsRef, execute, assemblyBuilderRef))
2858+
2859+ // Perform the compilation, given the above capturing function.
2860+ let errorsAndWarnings , result =
2861+ CompileHelpers.compileFromAsts ( asts, assemblyName, outFile, dependencies, noframework, None, Some execute.IsSome, tcImportsCapture, dynamicAssemblyCreator)
2862+
2863+ // Retrieve and return the results
2864+ let assemblyOpt =
2865+ match assemblyBuilderRef.Value with
2866+ | None -> None
2867+ | Some a -> Some ( a :> System.Reflection.Assembly)
2868+
2869+ errorsAndWarnings, result, assemblyOpt
2870+
27002871 /// This function is called when the entire environment is known to have changed for reasons not encoded in the ProjectOptions of any project/compilation.
27012872 /// For example, the type provider approvals file may have changed.
27022873 member ic.InvalidateAll () =
0 commit comments