33open System
44open System.Collections .Generic
55open System.IO
6- open System.Linq
76open FSharp.Compiler .CodeAnalysis
87open FSharp.Compiler .Text
98open FSharp.Compiler .Symbols
@@ -77,15 +76,13 @@ let graphFromTypedTree
7776 ( checker : FSharpChecker )
7877 ( projectDir : string )
7978 ( projectOptions : FSharpProjectOptions )
80- : Dictionary < string , File > * IReadOnlyDictionary < File , File []> =
81- let files = Dictionary< string, File>()
82-
83- let filesWithDeps =
84- projectOptions
85- .SourceFiles
86- .AsParallel()
87- .AsOrdered()
88- .Select( fun fileName idx ->
79+ : Async < Dictionary < string , File > * IReadOnlyDictionary < File , File []>> =
80+ async {
81+ let files = Dictionary< string, File>()
82+
83+ let! filesWithDeps =
84+ projectOptions.SourceFiles
85+ |> Array.mapi ( fun idx fileName ->
8986 async {
9087 let sourceText = ( File.ReadAllText >> SourceText.ofString) fileName
9188 let! parseResult , checkResult = checker.ParseAndCheckFileInProject( fileName, 1 , sourceText, projectOptions)
@@ -111,108 +108,111 @@ let graphFromTypedTree
111108 FsiBacked = isFisBacked
112109 }
113110
114- files.Add( Path.GetFileName ( fileName), file)
111+ files.Add( Path.GetRelativePath ( projectDir , fileName), file)
115112
116113 return ( file, collector.Deps)
117- }
118- |> Async.RunSynchronously)
119- .ToArray()
114+ })
115+ |> Async.Parallel
120116
121- let graph =
122- filesWithDeps
123- |> Seq.sortBy ( fun ( file , _ ) -> file.Idx.Idx)
124- |> Seq.map ( fun ( file , deps ) ->
125- let depsAsFiles = deps |> Array.map ( fun dep -> files.[ dep])
126- file, depsAsFiles)
127- |> readOnlyDict
117+ let graph =
118+ filesWithDeps
119+ |> Seq.sortBy ( fun ( file , _ ) -> file.Idx.Idx)
120+ |> Seq.map ( fun ( file , deps ) ->
121+ let depsAsFiles = deps |> Array.map ( fun dep -> files.[ dep])
122+ file, depsAsFiles)
123+ |> readOnlyDict
128124
129- files, graph
125+ return files, graph
126+ }
130127
131128[<TestCaseSource( nameof codebases) >]
132- [<Explicit( " Really slow ! Only useful as a sanity check that the test codebase is sound." ) >]
129+ [<Explicit( " Slow ! Only useful as a sanity check that the test codebase is sound." ) >]
133130let ``Create Graph from typed tree`` ( code : Codebase ) =
134131 let previousDir = Environment.CurrentDirectory
135132
136- try
137- Environment.CurrentDirectory <- code.WorkDir
138-
139- let args = File.ReadAllLines( code.Path) |> Array.map replacePaths
140- let fileName = Path.GetFileNameWithoutExtension( args.[ 0 ]. Replace( " -o:" , " " ))
141-
142- let sourceFiles , otherOptions =
143- args
144- |> Array.partition ( fun option ->
145- not ( option.StartsWith( " -" ))
146- && ( option.EndsWith( " .fs" ) || option.EndsWith( " .fsi" )))
147-
148- let otherOptions =
149- otherOptions
150- |> Array.map ( fun otherOption ->
151- // The reference to fsharp code needs to be an absolute one
152- if otherOption.StartsWith( " -r:.." ) then
153- let absoluteBit = otherOption.Split( ':' ).[ 1 ]
154- $" -r:{Path.Combine(code.WorkDir, absoluteBit)}"
133+ async {
134+
135+ try
136+ Environment.CurrentDirectory <- code.WorkDir
137+
138+ let args = File.ReadAllLines( code.Path) |> Array.map replacePaths
139+ let fileName = Path.GetFileNameWithoutExtension( args.[ 0 ]. Replace( " -o:" , " " ))
140+
141+ let sourceFiles , otherOptions =
142+ args
143+ |> Array.partition ( fun option ->
144+ not ( option.StartsWith( " -" ))
145+ && ( option.EndsWith( " .fs" ) || option.EndsWith( " .fsi" )))
146+
147+ let otherOptions =
148+ otherOptions
149+ |> Array.map ( fun otherOption ->
150+ // The reference to fsharp code needs to be an absolute one
151+ if otherOption.StartsWith( " -r:.." ) then
152+ let absoluteBit = otherOption.Split( ': ' ).[ 1 ]
153+ $" -r:{Path.Combine(code.WorkDir, absoluteBit)}"
154+ else
155+ otherOption)
156+
157+ let proj =
158+ {
159+ ProjectFileName = fileName
160+ ProjectId = None
161+ SourceFiles = sourceFiles
162+ OtherOptions = otherOptions
163+ ReferencedProjects = [||]
164+ IsIncompleteTypeCheckEnvironment = false
165+ UseScriptResolutionRules = false
166+ LoadTime = DateTime.Now
167+ UnresolvedReferences = None
168+ OriginalLoadReferences = []
169+ Stamp = None
170+ }
171+
172+ let! files , graphFromTypedTree = graphFromTypedTree checker code.WorkDir proj
173+ let path = $" {fileName}.typed-tree.deps.json"
174+ graphFromTypedTree |> Graph.map ( fun n -> n.Name) |> Graph.serialiseToJson path
175+
176+ let sourceFiles =
177+ files.Values
178+ |> Seq.sortBy ( fun file -> file.Idx.Idx)
179+ |> Seq.map ( fun file ->
180+ let ast =
181+ match file.AST with
182+ | ASTOrFsix.AST ast -> ast
183+ | ASTOrFsix.Fsix _ -> failwith " unexpected fsix"
184+
185+ { Idx = file.Idx; AST = ast }: SourceFile)
186+ |> Seq.toArray
187+
188+ let graphFromHeuristic = DependencyResolution.detectFileDependencies sourceFiles
189+ let path = $" {fileName}.deps.json"
190+
191+ graphFromHeuristic.Graph
192+ |> Graph.map ( fun n -> n.Name)
193+ |> Graph.serialiseToJson path
194+
195+ Assert.True( graphFromTypedTree.Count = graphFromHeuristic.Graph.Count, " Both graphs should have the same amount of entries." )
196+
197+ let depNames ( files : File array ) =
198+ Array.map ( fun ( f : File ) -> Path.GetFileName( f.Name)) files
199+ |> String.concat " , "
200+
201+ for KeyValue ( file, deps) in graphFromHeuristic.Graph do
202+ let depsFromTypedTree = graphFromTypedTree.[ file]
203+
204+ if Array.isEmpty depsFromTypedTree && not ( Array.isEmpty deps) then
205+ printfn $" {file.Name} has %A {(depNames deps)} while the typed tree had none!"
155206 else
156- otherOption)
157-
158- let proj =
159- {
160- ProjectFileName = fileName
161- ProjectId = None
162- SourceFiles = sourceFiles
163- OtherOptions = otherOptions
164- ReferencedProjects = [||]
165- IsIncompleteTypeCheckEnvironment = false
166- UseScriptResolutionRules = false
167- LoadTime = DateTime.Now
168- UnresolvedReferences = None
169- OriginalLoadReferences = []
170- Stamp = None
171- }
172-
173- let files , graphFromTypedTree = graphFromTypedTree checker code.WorkDir proj
174- let path = $" {fileName}.typed-tree.deps.json"
175- graphFromTypedTree |> Graph.map ( fun n -> n.Name) |> Graph.serialiseToJson path
176-
177- let sourceFiles =
178- files.Values
179- |> Seq.sortBy ( fun file -> file.Idx.Idx)
180- |> Seq.map ( fun file ->
181- let ast =
182- match file.AST with
183- | ASTOrFsix.AST ast -> ast
184- | ASTOrFsix.Fsix _ -> failwith " unexpected fsix"
185-
186- { Idx = file.Idx; AST = ast }: SourceFile)
187- |> Seq.toArray
188-
189- let graphFromHeuristic = DependencyResolution.detectFileDependencies sourceFiles
190- let path = $" {fileName}.deps.json"
191-
192- graphFromHeuristic.Graph
193- |> Graph.map ( fun n -> n.Name)
194- |> Graph.serialiseToJson path
195-
196- Assert.True( graphFromTypedTree.Count = graphFromHeuristic.Graph.Count, " Both graphs should have the same amount of entries." )
197-
198- let depNames ( files : File array ) =
199- Array.map ( fun ( f : File ) -> Path.GetFileName( f.Name)) files
200- |> String.concat " , "
201-
202- for KeyValue ( file, deps) in graphFromHeuristic.Graph do
203- let depsFromTypedTree = graphFromTypedTree.[ file]
204-
205- if Array.isEmpty depsFromTypedTree && not ( Array.isEmpty deps) then
206- printfn $" {file.Name} has %A {(depNames deps)} while the typed tree had none!"
207- else
208- let isSuperSet =
209- depsFromTypedTree |> Seq.forall ( fun ttDep -> Seq.contains ttDep deps)
210-
211- Assert.IsTrue(
212- isSuperSet,
213- $""" {file.Name} did not contain a superset of the typed tree dependencies:
207+ let isSuperSet =
208+ depsFromTypedTree |> Seq.forall ( fun ttDep -> Seq.contains ttDep deps)
209+
210+ Assert.IsTrue(
211+ isSuperSet,
212+ $""" {file.Name} did not contain a superset of the typed tree dependencies:
214213Typed tree dependencies: %A {depNames depsFromTypedTree}.
215214Heuristic dependencies: %A {depNames deps}."""
216- )
217- finally
218- Environment.CurrentDirectory <- previousDir
215+ )
216+ finally
217+ Environment.CurrentDirectory <- previousDir
218+ }
0 commit comments