-
Notifications
You must be signed in to change notification settings - Fork 812
/
Copy pathfsc.fs
1259 lines (1049 loc) · 44.4 KB
/
fsc.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
// Driver for F# compiler.
//
// Roughly divides into:
// - Parsing
// - Flags
// - Importing IL assemblies
// - Compiling (including optimizing)
// - Linking (including ILX-IL transformation)
module internal FSharp.Compiler.Driver
open System
open System.Collections.Generic
open System.Diagnostics
open System.Globalization
open System.IO
open System.Reflection
open System.Text
open System.Threading
open Internal.Utilities
open Internal.Utilities.TypeHashing
open Internal.Utilities.Library
open Internal.Utilities.Library.Extras
open FSharp.Compiler
open FSharp.Compiler.AbstractIL
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.AbstractIL.ILBinaryReader
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.CheckDeclarations
open FSharp.Compiler.CompilerConfig
open FSharp.Compiler.CompilerDiagnostics
open FSharp.Compiler.CompilerImports
open FSharp.Compiler.CompilerOptions
open FSharp.Compiler.CreateILModule
open FSharp.Compiler.DependencyManager
open FSharp.Compiler.Diagnostics
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.Features
open FSharp.Compiler.IlxGen
open FSharp.Compiler.InfoReader
open FSharp.Compiler.IO
open FSharp.Compiler.ParseAndCheckInputs
open FSharp.Compiler.OptimizeInputs
open FSharp.Compiler.ScriptClosure
open FSharp.Compiler.StaticLinking
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Range
open FSharp.Compiler.TypedTree
open FSharp.Compiler.TypedTreeOps
open FSharp.Compiler.XmlDocFileWriter
open FSharp.Compiler.CheckExpressionsOps
//----------------------------------------------------------------------------
// Reporting - warnings, errors
//----------------------------------------------------------------------------
/// An error logger that reports errors up to some maximum, notifying the exiter when that maximum is reached
[<AbstractClass>]
type DiagnosticsLoggerUpToMaxErrors(tcConfigB: TcConfigBuilder, exiter: Exiter, nameForDebugging) =
inherit DiagnosticsLogger(nameForDebugging)
let mutable errors = 0
/// Called when an error or warning occurs
abstract HandleIssue: tcConfig: TcConfig * diagnostic: PhasedDiagnostic * severity: FSharpDiagnosticSeverity -> unit
/// Called when 'too many errors' has occurred
abstract HandleTooManyErrors: text: string -> unit
override _.ErrorCount = errors
override x.DiagnosticSink(diagnostic, severity) =
let tcConfig = TcConfig.Create(tcConfigB, validate = false)
match diagnostic.AdjustSeverity(tcConfigB.diagnosticsOptions, severity) with
| FSharpDiagnosticSeverity.Error ->
if errors >= tcConfig.maxErrors then
x.HandleTooManyErrors(FSComp.SR.fscTooManyErrors ())
exiter.Exit 1
x.HandleIssue(tcConfig, diagnostic, FSharpDiagnosticSeverity.Error)
errors <- errors + 1
match diagnostic.Exception, tcConfigB.simulateException with
| InternalError(msg, _), None
| Failure msg, None -> Debug.Assert(false, sprintf "Bug in compiler: %s\n%s" msg (diagnostic.Exception.ToString()))
| :? KeyNotFoundException, None ->
Debug.Assert(false, sprintf "Lookup exception in compiler: %s" (diagnostic.Exception.ToString()))
| _ -> ()
| FSharpDiagnosticSeverity.Hidden -> ()
| s -> x.HandleIssue(tcConfig, diagnostic, s)
/// Create an error logger that counts and prints errors
let ConsoleDiagnosticsLogger (tcConfigB: TcConfigBuilder, exiter: Exiter) =
{ new DiagnosticsLoggerUpToMaxErrors(tcConfigB, exiter, "ConsoleDiagnosticsLogger") with
member _.HandleTooManyErrors(text: string) =
DoWithDiagnosticColor FSharpDiagnosticSeverity.Warning (fun () -> Printf.eprintfn "%s" text)
member _.HandleIssue(tcConfig, diagnostic, severity) =
DoWithDiagnosticColor severity (fun () ->
writeViaBuffer stderr (fun buf -> diagnostic.Output(buf, tcConfig, severity))
stderr.WriteLine())
}
:> DiagnosticsLogger
/// DiagnosticLoggers can be sensitive to the TcConfig flags. During the checking
/// of the flags themselves we have to create temporary loggers, until the full configuration is
/// available.
type IDiagnosticsLoggerProvider =
abstract CreateLogger: tcConfigB: TcConfigBuilder * exiter: Exiter -> DiagnosticsLogger
type CapturingDiagnosticsLogger with
/// Commit the delayed diagnostics via a fresh temporary logger of the right kind.
member x.CommitDelayedDiagnostics(diagnosticsLoggerProvider: IDiagnosticsLoggerProvider, tcConfigB, exiter) =
let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter)
x.CommitDelayedDiagnostics diagnosticsLogger
/// The default DiagnosticsLogger implementation, reporting messages to the Console up to the maxerrors maximum
type ConsoleLoggerProvider() =
interface IDiagnosticsLoggerProvider with
member _.CreateLogger(tcConfigB, exiter) =
ConsoleDiagnosticsLogger(tcConfigB, exiter)
/// Notify the exiter if any error has occurred
let AbortOnError (diagnosticsLogger: DiagnosticsLogger, exiter: Exiter) =
if diagnosticsLogger.ErrorCount > 0 then
exiter.Exit 1
let TypeCheck
(ctok, tcConfig, tcImports, tcGlobals, diagnosticsLogger: DiagnosticsLogger, assemblyName, tcEnv0, openDecls0, inputs, exiter: Exiter)
=
try
if isNil inputs then
error (Error(FSComp.SR.fscNoImplementationFiles (), rangeStartup))
let ccuName = assemblyName
let tcInitialState =
GetInitialTcState(rangeStartup, ccuName, tcConfig, tcGlobals, tcImports, tcEnv0, openDecls0)
let eagerFormat (diag: PhasedDiagnostic) = diag.EagerlyFormatCore true
CheckClosedInputSet(
ctok,
(fun () -> diagnosticsLogger.CheckForRealErrorsIgnoringWarnings),
tcConfig,
tcImports,
tcGlobals,
None,
tcInitialState,
eagerFormat,
inputs
)
with exn ->
errorRecovery exn rangeStartup
exiter.Exit 1
/// Check for .fsx and, if present, compute the load closure for of #loaded files.
///
/// This is the "script compilation" feature that has always been present in the F# compiler, that allows you to compile scripts
/// and get the load closure and references from them. This applies even if the script is in a project (with 'Compile' action), for example.
///
/// Any DLL references implied by package references are also retrieved from the script.
///
/// When script compilation is invoked, the outputs are not necessarily a functioning application - the referenced DLLs are not
/// copied to the output folder, for example (except perhaps FSharp.Core.dll).
///
/// NOTE: there is similar code in IncrementalBuilder.fs and this code should really be reconciled with that
let AdjustForScriptCompile (tcConfigB: TcConfigBuilder, commandLineSourceFiles, lexResourceManager, dependencyProvider) =
let combineFilePath file =
try
if FileSystem.IsPathRootedShim file then
file
else
Path.Combine(tcConfigB.implicitIncludeDir, file)
with _ ->
error (Error(FSComp.SR.pathIsInvalid file, rangeStartup))
let commandLineSourceFiles = commandLineSourceFiles |> List.map combineFilePath
tcConfigB.embedSourceList <- tcConfigB.embedSourceList |> List.map combineFilePath
// Script compilation is active if the last item being compiled is a script and --noframework has not been specified
let mutable allSources = []
let tcConfig = TcConfig.Create(tcConfigB, validate = false)
let AddIfNotPresent (fileName: string) =
if not (allSources |> List.contains fileName) then
allSources <- fileName :: allSources
let AppendClosureInformation fileName =
if IsScript fileName then
let closure =
LoadClosure.ComputeClosureOfScriptFiles(
tcConfig,
[ fileName, rangeStartup ],
CodeContext.Compilation,
lexResourceManager,
dependencyProvider
)
// Record the new references (non-framework) references from the analysis of the script. (The full resolutions are recorded
// as the corresponding #I paths used to resolve them are local to the scripts and not added to the tcConfigB - they are
// added to localized clones of the tcConfigB).
let references =
closure.References
|> List.collect snd
|> List.filter (fun r ->
not (equals r.originalReference.Range range0)
&& not (equals r.originalReference.Range rangeStartup))
references
|> List.iter (fun r -> tcConfigB.AddReferencedAssemblyByPath(r.originalReference.Range, r.resolvedPath))
// Also record the other declarations from the script.
closure.NoWarns
|> List.collect (fun (n, ms) -> ms |> List.map (fun m -> m, n))
|> List.iter (fun (x, m) -> tcConfigB.TurnWarningOff(x, m))
closure.SourceFiles |> List.map fst |> List.iter AddIfNotPresent
closure.AllRootFileDiagnostics |> List.iter diagnosticSink
// If there is a target framework for the script then push that as a requirement into the overall compilation and add all the framework references implied
// by the script too.
let primaryAssembly =
if closure.UseDesktopFramework then
PrimaryAssembly.Mscorlib
else
PrimaryAssembly.System_Runtime
tcConfigB.SetPrimaryAssembly primaryAssembly
if tcConfigB.implicitlyReferenceDotNetAssemblies then
let references = closure.References |> List.collect snd
for reference in references do
tcConfigB.AddReferencedAssemblyByPath(reference.originalReference.Range, reference.resolvedPath)
else
AddIfNotPresent fileName
// Find closure of .fsx files.
commandLineSourceFiles |> List.iter AppendClosureInformation
List.rev allSources
let SetProcessThreadLocals tcConfigB =
match tcConfigB.preferredUiLang with
| Some s -> Thread.CurrentThread.CurrentUICulture <- CultureInfo(s)
| None -> ()
let ProcessCommandLineFlags (tcConfigB: TcConfigBuilder, lcidFromCodePage, argv) =
let mutable inputFilesRef = []
let collect name =
if List.exists (FileSystemUtils.checkSuffix name) [ ".resx" ] then
error (Error(FSComp.SR.fscResxSourceFileDeprecated name, rangeStartup))
else
inputFilesRef <- name :: inputFilesRef
let abbrevArgs = GetAbbrevFlagSet tcConfigB true
// This is where flags are interpreted by the command line fsc.exe.
ParseCompilerOptions(collect, GetCoreFscCompilerOptions tcConfigB, List.tail (PostProcessCompilerArgs abbrevArgs argv))
let inputFiles = List.rev inputFilesRef
// Check if we have a codepage from the console
match tcConfigB.lcid with
| Some _ -> ()
| None -> tcConfigB.lcid <- lcidFromCodePage
SetProcessThreadLocals tcConfigB
(* step - get dll references *)
let dllFiles, sourceFiles =
inputFiles
|> List.map FileSystemUtils.trimQuotes
|> List.partition FileSystemUtils.isDll
match dllFiles with
| [] -> ()
| h :: _ -> errorR (Error(FSComp.SR.fscReferenceOnCommandLine h, rangeStartup))
dllFiles
|> List.iter (fun f -> tcConfigB.AddReferencedAssemblyByPath(rangeStartup, f))
sourceFiles
/// Write a .fsi file for the --sig option
module InterfaceFileWriter =
let WriteInterfaceFile (tcGlobals, tcConfig: TcConfig, infoReader, declaredImpls: CheckedImplFile list) =
// there are two modes here:
// * write one unified sig file to a given path, or
// * write individual sig files to paths matching their impl files
let denv = DisplayEnv.InitialForSigFileGeneration tcGlobals
let denv =
{ denv with
shrinkOverloads = false
printVerboseSignatures = true
}
let writeToFile os (CheckedImplFile(contents = mexpr)) =
let text =
NicePrint.layoutImpliedSignatureOfModuleOrNamespace true denv infoReader AccessibleFromSomewhere range0 mexpr
|> Display.squashTo 80
|> LayoutRender.showL
Printf.fprintf os "%s\n\n" text
let writeHeader filePath os =
if
filePath <> ""
&& not (List.exists (FileSystemUtils.checkSuffix filePath) FSharpIndentationAwareSyntaxFileSuffixes)
then
fprintfn os "#light"
fprintfn os ""
let writeAllToSameFile declaredImpls =
/// Use a UTF-8 Encoding with no Byte Order Mark
let os =
if String.IsNullOrEmpty(tcConfig.printSignatureFile) then
Console.Out
else
FileSystem.OpenFileForWriteShim(tcConfig.printSignatureFile, FileMode.Create).GetWriter()
writeHeader tcConfig.printSignatureFile os
for impl in declaredImpls do
writeToFile os impl
if tcConfig.printSignatureFile <> "" then
os.Dispose()
let extensionForFile (filePath: string) =
if (List.exists (FileSystemUtils.checkSuffix filePath) FSharpMLCompatFileSuffixes) then
".mli"
else
".fsi"
let writeToSeparateFiles (declaredImpls: CheckedImplFile list) =
for CheckedImplFile(qualifiedNameOfFile = name) as impl in declaredImpls do
let fileName =
!!Path.ChangeExtension(name.Range.FileName, extensionForFile name.Range.FileName)
printfn "writing impl file to %s" fileName
use os = FileSystem.OpenFileForWriteShim(fileName, FileMode.Create).GetWriter()
writeHeader fileName os
writeToFile os impl
if tcConfig.printSignature then
writeAllToSameFile declaredImpls
else if tcConfig.printAllSignatureFiles then
writeToSeparateFiles declaredImpls
//----------------------------------------------------------------------------
// CopyFSharpCore
//----------------------------------------------------------------------------
// If the --nocopyfsharpcore switch is not specified, this will:
// 1) Look into the referenced assemblies, if FSharp.Core.dll is specified, it will copy it to output directory.
// 2) If not, but FSharp.Core.dll exists beside the compiler binaries, it will copy it to output directory.
// 3) If not, it will produce an error.
let CopyFSharpCore (outFile: string, referencedDlls: AssemblyReference list) =
let outDir = !!Path.GetDirectoryName(outFile)
let fsharpCoreAssemblyName = GetFSharpCoreLibraryName() + ".dll"
let fsharpCoreDestinationPath = Path.Combine(outDir, fsharpCoreAssemblyName)
let copyFileIfDifferent src dest =
if
not (FileSystem.FileExistsShim dest)
|| (FileSystem.GetCreationTimeShim src <> FileSystem.GetCreationTimeShim dest)
then
FileSystem.CopyShim(src, dest, true)
let fsharpCoreReferences =
referencedDlls
|> Seq.tryFind (fun dll ->
String.Equals(Path.GetFileName(dll.Text), fsharpCoreAssemblyName, StringComparison.CurrentCultureIgnoreCase))
match fsharpCoreReferences with
| Some referencedFsharpCoreDll -> copyFileIfDifferent referencedFsharpCoreDll.Text fsharpCoreDestinationPath
| None ->
let executionLocation = Assembly.GetExecutingAssembly().Location
let compilerLocation = !!Path.GetDirectoryName(executionLocation)
let compilerFsharpCoreDllPath =
Path.Combine(compilerLocation, fsharpCoreAssemblyName)
if FileSystem.FileExistsShim compilerFsharpCoreDllPath then
copyFileIfDifferent compilerFsharpCoreDllPath fsharpCoreDestinationPath
else
errorR (Error(FSComp.SR.fsharpCoreNotFoundToBeCopied (), rangeCmdArgs))
// Try to find an AssemblyVersion attribute
let TryFindVersionAttribute g attrib attribName attribs deterministic =
match AttributeHelpers.TryFindStringAttribute g attrib attribs with
| Some versionString ->
if deterministic && versionString.Contains("*") then
errorR (Error(FSComp.SR.fscAssemblyWildcardAndDeterminism (attribName, versionString), rangeStartup))
try
Some(parseILVersion versionString)
with e ->
// Warning will be reported by CheckExpressions.fs
None
| _ -> None
//----------------------------------------------------------------------------
// Main phases of compilation. These are written as separate functions with explicit argument passing
// to ensure transient objects are eligible for GC and only actual required information
// is propagated.
//-----------------------------------------------------------------------------
[<NoEquality; NoComparison>]
type Args<'T> = Args of 'T
let getParallelReferenceResolutionFromEnvironment () =
Environment.GetEnvironmentVariable("FCS_ParallelReferenceResolution")
|> Option.ofObj
|> Option.bind (fun flag ->
match bool.TryParse flag with
| true, runInParallel ->
if runInParallel then
Some ParallelReferenceResolution.On
else
Some ParallelReferenceResolution.Off
| false, _ -> None)
/// First phase of compilation.
/// - Set up console encoding and code page settings
/// - Process command line, flags and collect filenames
/// - Resolve assemblies
/// - Import assemblies
/// - Parse source files
/// - Check the inputs
let main1
(
ctok,
argv,
legacyReferenceResolver,
bannerAlreadyPrinted,
reduceMemoryUsage: ReduceMemoryFlag,
defaultCopyFSharpCore: CopyFSharpCoreFlag,
exiter: Exiter,
diagnosticsLoggerProvider: IDiagnosticsLoggerProvider,
disposables: DisposablesTracker
) =
// See Bug 735819
let lcidFromCodePage =
let thread = Thread.CurrentThread
if
(Console.OutputEncoding.CodePage <> 65001)
&& (Console.OutputEncoding.CodePage <> thread.CurrentUICulture.TextInfo.OEMCodePage)
&& (Console.OutputEncoding.CodePage <> thread.CurrentUICulture.TextInfo.ANSICodePage)
&& (CultureInfo.InvariantCulture <> thread.CurrentUICulture)
then
thread.CurrentUICulture <- CultureInfo("en-US")
Some 1033
else
None
let directoryBuildingFrom = Directory.GetCurrentDirectory()
let tryGetMetadataSnapshot = (fun _ -> None)
let defaultFSharpBinariesDir =
FSharpEnvironment.BinFolderOfDefaultFSharpCompiler(None).Value
let tcConfigB =
TcConfigBuilder.CreateNew(
legacyReferenceResolver,
defaultFSharpBinariesDir,
reduceMemoryUsage = reduceMemoryUsage,
implicitIncludeDir = directoryBuildingFrom,
isInteractive = false,
isInvalidationSupported = false,
defaultCopyFSharpCore = defaultCopyFSharpCore,
tryGetMetadataSnapshot = tryGetMetadataSnapshot,
sdkDirOverride = None,
rangeForErrors = range0,
compilationMode = CompilationMode.OneOff
)
tcConfigB.exiter <- exiter
// Preset: --optimize+ -g --tailcalls+ (see 4505)
SetOptimizeSwitch tcConfigB OptionSwitch.On
SetDebugSwitch tcConfigB None OptionSwitch.Off
SetTailcallSwitch tcConfigB OptionSwitch.On
// Now install a delayed logger to hold all errors from flags until after all flags have been parsed (for example, --vserrors)
let delayForFlagsLogger = CapturingDiagnosticsLogger("DelayFlagsLogger")
SetThreadDiagnosticsLoggerNoUnwind delayForFlagsLogger
// Share intern'd strings across all lexing/parsing
let lexResourceManager = Lexhelp.LexResourceManager()
let dependencyProvider = new DependencyProvider()
// Process command line, flags and collect filenames
let sourceFiles =
// The ParseCompilerOptions function calls imperative function to process "real" args
// Rather than start processing, just collect names, then process them.
try
let files = ProcessCommandLineFlags(tcConfigB, lcidFromCodePage, argv)
let files = CheckAndReportSourceFileDuplicates(ResizeArray.ofList files)
AdjustForScriptCompile(tcConfigB, files, lexResourceManager, dependencyProvider)
with e ->
errorRecovery e rangeStartup
delayForFlagsLogger.CommitDelayedDiagnostics(diagnosticsLoggerProvider, tcConfigB, exiter)
exiter.Exit 1
tcConfigB.conditionalDefines <- "COMPILED" :: tcConfigB.conditionalDefines
// Override ParallelReferenceResolution set on the CLI with an environment setting if present.
match getParallelReferenceResolutionFromEnvironment () with
| Some parallelReferenceResolution -> tcConfigB.parallelReferenceResolution <- parallelReferenceResolution
| None -> ()
if tcConfigB.utf8output && Console.OutputEncoding <> Encoding.UTF8 then
let previousEncoding = Console.OutputEncoding
Console.OutputEncoding <- Encoding.UTF8
disposables.Register(
{ new IDisposable with
member _.Dispose() =
Console.OutputEncoding <- previousEncoding
}
)
// Display the banner text, if necessary
if not bannerAlreadyPrinted then
Console.Write(GetBannerText tcConfigB)
// Create tcGlobals and frameworkTcImports
let outfile, pdbfile, assemblyName =
try
tcConfigB.DecideNames sourceFiles
with e ->
errorRecovery e rangeStartup
delayForFlagsLogger.CommitDelayedDiagnostics(diagnosticsLoggerProvider, tcConfigB, exiter)
exiter.Exit 1
// DecideNames may give "no inputs" error. Abort on error at this point. bug://3911
if not tcConfigB.continueAfterParseFailure && delayForFlagsLogger.ErrorCount > 0 then
delayForFlagsLogger.CommitDelayedDiagnostics(diagnosticsLoggerProvider, tcConfigB, exiter)
exiter.Exit 1
// If there's a problem building TcConfig, abort
let tcConfig =
try
TcConfig.Create(tcConfigB, validate = false)
with e ->
errorRecovery e rangeStartup
delayForFlagsLogger.CommitDelayedDiagnostics(diagnosticsLoggerProvider, tcConfigB, exiter)
exiter.Exit 1
if tcConfig.showTimes then
Activity.Profiling.addConsoleListener () |> disposables.Register
tcConfig.writeTimesToFile
|> Option.iter (fun f ->
Activity.CsvExport.addCsvFileListener f |> disposables.Register
Activity.start
"FSC compilation"
[
Activity.Tags.project, tcConfig.outputFile |> Option.defaultValue String.Empty
]
|> disposables.Register)
let diagnosticsLogger = diagnosticsLoggerProvider.CreateLogger(tcConfigB, exiter)
// Install the global error logger and never remove it. This logger does have all command-line flags considered.
SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger
// Forward all errors from flags
delayForFlagsLogger.CommitDelayedDiagnostics diagnosticsLogger
if not tcConfigB.continueAfterParseFailure then
AbortOnError(diagnosticsLogger, exiter)
// Resolve assemblies
ReportTime tcConfig "Import mscorlib+FSharp.Core"
let foundationalTcConfigP = TcConfigProvider.Constant tcConfig
let sysRes, otherRes, knownUnresolved =
TcAssemblyResolutions.SplitNonFoundationalResolutions(tcConfig)
// Import basic assemblies
let tcGlobals, frameworkTcImports =
TcImports.BuildFrameworkTcImports(foundationalTcConfigP, sysRes, otherRes)
|> Async.RunImmediate
let ilSourceDocs =
[
for sourceFile in sourceFiles -> tcGlobals.memoize_file (FileIndex.fileIndexOfFile sourceFile)
]
// Register framework tcImports to be disposed in future
disposables.Register frameworkTcImports
// Parse sourceFiles
ReportTime tcConfig "Parse inputs"
use unwindParsePhase = UseBuildPhase BuildPhase.Parse
let inputs =
ParseInputFiles(tcConfig, lexResourceManager, sourceFiles, diagnosticsLogger, false)
let inputs, _ =
(Map.empty, inputs)
||> List.mapFold (fun state (input, x) ->
let inputT, stateT = DeduplicateParsedInputModuleName state input
(inputT, x), stateT)
// Print the AST if requested
if tcConfig.printAst then
for input, _filename in inputs do
printf "AST:\n"
printfn "%+A" input
printf "\n"
if tcConfig.parseOnly then
exiter.Exit 0
if not tcConfig.continueAfterParseFailure then
AbortOnError(diagnosticsLogger, exiter)
// Apply any nowarn flags
let tcConfig =
(tcConfig, inputs)
||> List.fold (fun z (input, sourceFileDirectory) ->
ApplyMetaCommandsFromInputToTcConfig(z, input, sourceFileDirectory, dependencyProvider))
let tcConfigP = TcConfigProvider.Constant tcConfig
// Import other assemblies
ReportTime tcConfig "Import non-system references"
let tcImports =
TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider)
|> Async.RunImmediate
// register tcImports to be disposed in future
disposables.Register tcImports
if not tcConfig.continueAfterParseFailure then
AbortOnError(diagnosticsLogger, exiter)
if tcConfig.importAllReferencesOnly then
exiter.Exit 0
// Build the initial type checking environment
ReportTime tcConfig "Typecheck"
// Read the source file content for the `CallerArgumentExpression` feature
readAndStoreFileContents tcConfig sourceFiles
use unwindParsePhase = UseBuildPhase BuildPhase.TypeCheck
let tcEnv0, openDecls0 =
GetInitialTcEnv(assemblyName, rangeStartup, tcConfig, tcImports, tcGlobals)
// Type check the inputs
let inputs = inputs |> List.map fst
let tcState, topAttrs, typedAssembly, _tcEnvAtEnd =
TypeCheck(ctok, tcConfig, tcImports, tcGlobals, diagnosticsLogger, assemblyName, tcEnv0, openDecls0, inputs, exiter)
AbortOnError(diagnosticsLogger, exiter)
ReportTime tcConfig "Typechecked"
Args(
ctok,
tcGlobals,
tcImports,
frameworkTcImports,
tcState.Ccu,
typedAssembly,
topAttrs,
tcConfig,
outfile,
pdbfile,
assemblyName,
diagnosticsLogger,
exiter,
ilSourceDocs
)
/// Second phase of compilation.
/// - Write the signature file, check some attributes
let main2
(Args(ctok,
tcGlobals,
tcImports: TcImports,
frameworkTcImports,
generatedCcu: CcuThunk,
typedImplFiles,
topAttrs,
tcConfig: TcConfig,
outfile,
pdbfile,
assemblyName,
diagnosticsLogger,
exiter: Exiter,
ilSourceDocs))
=
if tcConfig.typeCheckOnly then
exiter.Exit 0
generatedCcu.Contents.SetAttribs(generatedCcu.Contents.Attribs @ topAttrs.assemblyAttrs)
use unwindPhase = UseBuildPhase BuildPhase.CodeGen
let signingInfo = ValidateKeySigningAttributes(tcConfig, tcGlobals, topAttrs)
AbortOnError(diagnosticsLogger, exiter)
// Build an updated diagnosticsLogger that filters according to the scopedPragmas. Then install
// it as the updated global error logger and never remove it
let oldLogger = diagnosticsLogger
let diagnosticsLogger =
let scopedPragmas =
[
for CheckedImplFile(pragmas = pragmas) in typedImplFiles do
yield! pragmas
]
GetDiagnosticsLoggerFilteringByScopedPragmas(true, scopedPragmas, tcConfig.diagnosticsOptions, oldLogger)
SetThreadDiagnosticsLoggerNoUnwind diagnosticsLogger
// Try to find an AssemblyVersion attribute
let assemVerFromAttrib =
match
TryFindVersionAttribute
tcGlobals
"System.Reflection.AssemblyVersionAttribute"
"AssemblyVersionAttribute"
topAttrs.assemblyAttrs
tcConfig.deterministic
with
| Some v ->
match tcConfig.version with
| VersionNone -> Some v
| _ ->
warning (Error(FSComp.SR.fscAssemblyVersionAttributeIgnored (), rangeStartup))
None
| _ ->
match tcConfig.version with
| VersionNone -> Some(ILVersionInfo(0us, 0us, 0us, 0us)) //If no attribute was specified in source then version is 0.0.0.0
| _ -> Some(tcConfig.version.GetVersionInfo tcConfig.implicitIncludeDir)
// write interface, xmldoc
ReportTime tcConfig "Write Interface File"
use _ = UseBuildPhase BuildPhase.Output
if tcConfig.printSignature || tcConfig.printAllSignatureFiles then
InterfaceFileWriter.WriteInterfaceFile(tcGlobals, tcConfig, InfoReader(tcGlobals, tcImports.GetImportMap()), typedImplFiles)
ReportTime tcConfig "Write XML doc signatures"
if tcConfig.xmlDocOutputFile.IsSome then
XmlDocWriter.ComputeXmlDocSigs(tcGlobals, generatedCcu)
ReportTime tcConfig "Write XML docs"
tcConfig.xmlDocOutputFile
|> Option.iter (fun xmlFile ->
let xmlFile = tcConfig.MakePathAbsolute xmlFile
XmlDocWriter.WriteXmlDocFile(tcGlobals, assemblyName, generatedCcu, xmlFile))
// Pass on only the minimum information required for the next phase
Args(
ctok,
tcConfig,
tcImports,
frameworkTcImports,
tcGlobals,
diagnosticsLogger,
generatedCcu,
outfile,
typedImplFiles,
topAttrs,
pdbfile,
assemblyName,
assemVerFromAttrib,
signingInfo,
exiter,
ilSourceDocs
)
/// Third phase of compilation.
/// - encode signature data
/// - optimize
/// - encode optimization data
let main3
(Args(ctok,
tcConfig,
tcImports,
frameworkTcImports: TcImports,
tcGlobals,
diagnosticsLogger: DiagnosticsLogger,
generatedCcu: CcuThunk,
outfile,
typedImplFiles,
topAttrs,
pdbfile,
assemblyName,
assemVerFromAttrib,
signingInfo,
exiter: Exiter,
ilSourceDocs))
=
// Encode the signature data
ReportTime tcConfig "Encode Interface Data"
let exportRemapping = MakeExportRemapping generatedCcu generatedCcu.Contents
let sigDataAttributes, sigDataResources =
try
EncodeSignatureData(tcConfig, tcGlobals, exportRemapping, generatedCcu, outfile, false)
with e ->
errorRecoveryNoRange e
exiter.Exit 1
let metadataVersion =
match tcConfig.metadataVersion with
| Some v -> v
| _ ->
match frameworkTcImports.DllTable.TryFind tcConfig.primaryAssembly.Name with
| Some ib -> ib.RawMetadata.TryGetILModuleDef().Value.MetadataVersion
| _ -> ""
let optimizedImpls, optDataResources =
// Perform optimization
use _ = UseBuildPhase BuildPhase.Optimize
let optEnv0 = GetInitialOptimizationEnv(tcImports, tcGlobals)
let importMap = tcImports.GetImportMap()
let optimizedImpls, optimizationData, _ =
ApplyAllOptimizations(
tcConfig,
tcGlobals,
(LightweightTcValForUsingInBuildMethodCall tcGlobals),
outfile,
importMap,
false,
optEnv0,
generatedCcu,
typedImplFiles
)
AbortOnError(diagnosticsLogger, exiter)
// Encode the optimization data
ReportTime tcConfig ("Encoding OptData")
optimizedImpls, EncodeOptimizationData(tcGlobals, tcConfig, outfile, exportRemapping, (generatedCcu, optimizationData), false)
if tcGlobals.langVersion.SupportsFeature LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage then
match optimizedImpls with
| CheckedAssemblyAfterOptimization checkedImplFileAfterOptimizations ->
ReportTime tcConfig ("TailCall Checks")
for f in checkedImplFileAfterOptimizations do
TailCallChecks.CheckImplFile(tcGlobals, tcImports.GetImportMap(), true, f.ImplFile.Contents)
let refAssemblySignatureHash =
match tcConfig.emitMetadataAssembly with
| MetadataAssemblyGeneration.None -> None
| MetadataAssemblyGeneration.ReferenceOnly
| MetadataAssemblyGeneration.ReferenceOut _ ->
let hasIvt =
TryFindFSharpStringAttribute tcGlobals tcGlobals.attrib_InternalsVisibleToAttribute topAttrs.assemblyAttrs
|> Option.isSome
let observer = if hasIvt then PublicAndInternal else PublicOnly
let optDataHash =
optDataResources
|> List.map (fun ilResource ->
use s = ilResource.GetBytes().AsStream()
let sha256 = System.Security.Cryptography.SHA256.Create()
sha256.ComputeHash s)
|> List.sumBy (hash >> int64)
|> hash
try
Fsharp.Compiler.SignatureHash.calculateSignatureHashOfFiles typedImplFiles tcGlobals observer
+ Fsharp.Compiler.SignatureHash.calculateHashOfAssemblyTopAttributes topAttrs tcConfig.platform
+ optDataHash
|> Some
with e ->
printfn "Unexpected error when hashing implied signature, will hash the all of .NET metadata instead. Error: %O " e
None
// Pass on only the minimum information required for the next phase
Args(
ctok,
tcConfig,
tcImports,
tcGlobals,
diagnosticsLogger,
generatedCcu,
outfile,
optimizedImpls,
topAttrs,
pdbfile,
assemblyName,
sigDataAttributes,
sigDataResources,
optDataResources,
assemVerFromAttrib,
signingInfo,
metadataVersion,
exiter,
ilSourceDocs,
refAssemblySignatureHash
)
/// Fourth phase of compilation.
/// - Static linking
/// - IL code generation
let main4
(tcImportsCapture, dynamicAssemblyCreator)
(Args(ctok,
tcConfig: TcConfig,
tcImports,
tcGlobals: TcGlobals,
diagnosticsLogger,
generatedCcu: CcuThunk,
outfile,
optimizedImpls,
topAttrs,
pdbfile,
assemblyName,
sigDataAttributes,
sigDataResources,
optDataResources,
assemVerFromAttrib,
signingInfo,
metadataVersion,
exiter: Exiter,
ilSourceDocs,
refAssemblySignatureHash))
=
match tcImportsCapture with
| None -> ()
| Some f -> f tcImports
if tcConfig.standalone && generatedCcu.UsesFSharp20PlusQuotations then
error (Error(FSComp.SR.fscQuotationLiteralsStaticLinking0 (), rangeStartup))
// Compute a static linker, it gets called later.
let staticLinker = StaticLink(ctok, tcConfig, tcImports, tcGlobals)
ReportTime tcConfig "TAST -> IL"
use _ = UseBuildPhase BuildPhase.IlxGen
// Create the Abstract IL generator
let ilxGenerator =
CreateIlxAssemblyGenerator(tcConfig, tcImports, tcGlobals, (LightweightTcValForUsingInBuildMethodCall tcGlobals), generatedCcu)
let codegenBackend =
(if Option.isSome dynamicAssemblyCreator then
IlReflectBackend
else
IlWriteBackend)
// Generate the Abstract IL Code
let codegenResults =
GenerateIlxCode(
codegenBackend,
Option.isSome dynamicAssemblyCreator,
tcConfig,
topAttrs,
optimizedImpls,
generatedCcu.AssemblyName,
ilxGenerator
)