|  | 
|  | 1 | +module internal FSharp.Compiler.ReuseTcResults | 
|  | 2 | + | 
|  | 3 | +open System.Collections.Generic | 
|  | 4 | +open System.IO | 
|  | 5 | + | 
|  | 6 | +open FSharp.Compiler.CompilerConfig | 
|  | 7 | +open FSharp.Compiler.Diagnostics | 
|  | 8 | +open FSharp.Compiler.GraphChecking | 
|  | 9 | +open FSharp.Compiler.IO | 
|  | 10 | +open FSharp.Compiler.Syntax | 
|  | 11 | +open FSharp.Compiler.Syntax.PrettyNaming | 
|  | 12 | + | 
|  | 13 | +type TcData = | 
|  | 14 | +    { | 
|  | 15 | +        CmdLine: string array | 
|  | 16 | +        Graph: string array | 
|  | 17 | +        References: string array | 
|  | 18 | +    } | 
|  | 19 | + | 
|  | 20 | +[<Sealed>] | 
|  | 21 | +type CachingDriver(tcConfig: TcConfig) = | 
|  | 22 | + | 
|  | 23 | +    let outputDir = tcConfig.outputDir |> Option.defaultValue "" | 
|  | 24 | +    let tcDataFilePath = Path.Combine(outputDir, FSharpTcDataResourceName) | 
|  | 25 | + | 
|  | 26 | +    [<Literal>] | 
|  | 27 | +    let CmdLineHeader = "CMDLINE" | 
|  | 28 | + | 
|  | 29 | +    [<Literal>] | 
|  | 30 | +    let GraphHeader = "GRAPH" | 
|  | 31 | + | 
|  | 32 | +    [<Literal>] | 
|  | 33 | +    let ReferencesHeader = "REFERENCES" | 
|  | 34 | + | 
|  | 35 | +    let writeThisTcData (tcData: TcData) = | 
|  | 36 | +        use tcDataFile = FileSystem.OpenFileForWriteShim tcDataFilePath | 
|  | 37 | + | 
|  | 38 | +        let lines = ResizeArray<string>() | 
|  | 39 | +        lines.Add $"BEGIN {CmdLineHeader}" | 
|  | 40 | +        lines.AddRange tcData.CmdLine | 
|  | 41 | +        lines.Add $"BEGIN {GraphHeader}" | 
|  | 42 | +        lines.AddRange tcData.Graph | 
|  | 43 | +        lines.Add $"BEGIN {ReferencesHeader}" | 
|  | 44 | +        lines.AddRange tcData.References | 
|  | 45 | + | 
|  | 46 | +        tcDataFile.WriteAllLines lines | 
|  | 47 | + | 
|  | 48 | +    let readPrevTcData () = | 
|  | 49 | +        if FileSystem.FileExistsShim tcDataFilePath then | 
|  | 50 | +            use tcDataFile = FileSystem.OpenFileForReadShim tcDataFilePath | 
|  | 51 | + | 
|  | 52 | +            let cmdLine = ResizeArray<string>() | 
|  | 53 | +            let graph = ResizeArray<string>() | 
|  | 54 | +            let refs = ResizeArray<string>() | 
|  | 55 | + | 
|  | 56 | +            let mutable currentHeader = "" | 
|  | 57 | + | 
|  | 58 | +            tcDataFile.ReadLines() | 
|  | 59 | +            |> Seq.iter (fun line -> | 
|  | 60 | +                match line with | 
|  | 61 | +                | line when line.StartsWith "BEGIN" -> currentHeader <- line.Split ' ' |> Array.last | 
|  | 62 | +                | line -> | 
|  | 63 | +                    match currentHeader with | 
|  | 64 | +                    | CmdLineHeader -> cmdLine.Add line | 
|  | 65 | +                    | GraphHeader -> graph.Add line | 
|  | 66 | +                    | ReferencesHeader -> refs.Add line | 
|  | 67 | +                    | _ -> invalidOp "broken tc cache") | 
|  | 68 | + | 
|  | 69 | +            Some | 
|  | 70 | +                { | 
|  | 71 | +                    CmdLine = cmdLine.ToArray() | 
|  | 72 | +                    Graph = graph.ToArray() | 
|  | 73 | +                    References = refs.ToArray() | 
|  | 74 | +                } | 
|  | 75 | + | 
|  | 76 | +        else | 
|  | 77 | +            None | 
|  | 78 | + | 
|  | 79 | +    let formatAssemblyReference (r: AssemblyReference) = | 
|  | 80 | +        let fileName = r.Text | 
|  | 81 | +        let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName | 
|  | 82 | +        sprintf "%s,%i" fileName lastWriteTime.Ticks | 
|  | 83 | + | 
|  | 84 | +    let getThisCompilationCmdLine args = args | 
|  | 85 | + | 
|  | 86 | +    // maybe split into two things? | 
|  | 87 | +    let getThisCompilationGraph inputs = | 
|  | 88 | +        let sourceFiles = | 
|  | 89 | +            inputs | 
|  | 90 | +            |> Seq.toArray | 
|  | 91 | +            |> Array.mapi (fun idx (input: ParsedInput) -> | 
|  | 92 | +                { | 
|  | 93 | +                    Idx = idx | 
|  | 94 | +                    FileName = input.FileName | 
|  | 95 | +                    ParsedInput = input | 
|  | 96 | +                }) | 
|  | 97 | + | 
|  | 98 | +        let filePairs = FilePairMap sourceFiles | 
|  | 99 | +        let graph, _ = DependencyResolution.mkGraph filePairs sourceFiles | 
|  | 100 | + | 
|  | 101 | +        let list = List<string>() | 
|  | 102 | + | 
|  | 103 | +        for KeyValue(idx, _) in graph do | 
|  | 104 | +            let fileName = sourceFiles[idx].FileName | 
|  | 105 | +            let lastWriteTime = FileSystem.GetLastWriteTimeShim fileName | 
|  | 106 | +            list.Add(sprintf "%i,%s,%i" idx fileName lastWriteTime.Ticks) | 
|  | 107 | + | 
|  | 108 | +        for KeyValue(idx, deps) in graph do | 
|  | 109 | +            for depIdx in deps do | 
|  | 110 | +                list.Add $"%i{idx} --> %i{depIdx}" | 
|  | 111 | + | 
|  | 112 | +        list.ToArray() | 
|  | 113 | + | 
|  | 114 | +    let getThisCompilationReferences = Seq.map formatAssemblyReference >> Seq.toArray | 
|  | 115 | + | 
|  | 116 | +    member _.TryReuseTcResults inputs = | 
|  | 117 | +        let prevTcDataOpt = readPrevTcData () | 
|  | 118 | + | 
|  | 119 | +        let thisTcData = | 
|  | 120 | +            { | 
|  | 121 | +                CmdLine = getThisCompilationCmdLine tcConfig.cmdLineArgs | 
|  | 122 | +                Graph = getThisCompilationGraph inputs | 
|  | 123 | +                References = getThisCompilationReferences tcConfig.referencedDLLs | 
|  | 124 | +            } | 
|  | 125 | + | 
|  | 126 | +        match prevTcDataOpt with | 
|  | 127 | +        | Some prevTcData -> | 
|  | 128 | +            use _ = Activity.start Activity.Events.reuseTcResultsCachePresent [] | 
|  | 129 | + | 
|  | 130 | +            if prevTcData = thisTcData then | 
|  | 131 | +                use _ = Activity.start Activity.Events.reuseTcResultsCacheHit [] | 
|  | 132 | + | 
|  | 133 | +                () // do nothing, yet | 
|  | 134 | +            else | 
|  | 135 | +                use _ = Activity.start Activity.Events.reuseTcResultsCacheMissed [] | 
|  | 136 | + | 
|  | 137 | +                writeThisTcData thisTcData | 
|  | 138 | + | 
|  | 139 | +        | None -> | 
|  | 140 | +            use _ = Activity.start Activity.Events.reuseTcResultsCacheAbsent [] | 
|  | 141 | + | 
|  | 142 | +            writeThisTcData thisTcData | 
0 commit comments