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