@@ -39,8 +39,9 @@ type Arguments =
3939 interface IArgParserTemplate with
4040 member s.Usage =
4141 match s with
42- | Project _ -> " Path to your .fsproj file."
43- | Analyzers_ Path _ -> " Path to a folder where your analyzers are located."
42+ | Project _ -> " List of paths to your .fsproj file."
43+ | Analyzers_ Path _ ->
44+ " List of path to a folder where your analyzers are located. This will search recursively."
4445 | Property _ -> " A key=value pair of an MSBuild property."
4546 | Configuration _ -> " The configuration to use, e.g. Debug or Release."
4647 | Runtime _ -> " The runtime identifier (RID)."
@@ -118,28 +119,39 @@ let rec mkKn (ty: Type) =
118119
119120let mutable logger : ILogger = Abstractions.NullLogger.Instance
120121
121- let loadProject toolsPath properties projPath =
122+ let loadProjects toolsPath properties ( projPaths : string list ) =
122123 async {
124+ let projPaths =
125+ projPaths
126+ |> List.map ( fun proj -> Path.Combine( Environment.CurrentDirectory, proj) |> Path.GetFullPath)
127+
128+ for proj in projPaths do
129+ logger.LogInformation( " Loading project {0}" , proj)
130+
123131 let loader = WorkspaceLoader.Create( toolsPath, properties)
124- let parsed = loader.LoadProjects [ projPath ] |> Seq.toList
132+ let projectOptions = loader.LoadProjects projPaths
125133
126- if parsed.IsEmpty then
127- logger.LogError( " Failed to load project '{0}'" , projPath)
128- exit 1
134+ let failedLoads =
135+ projPaths
136+ |> Seq.filter ( fun path -> not ( projectOptions |> Seq.exists ( fun p -> p.ProjectFileName = path)))
137+ |> Seq.toList
129138
130- let fcsPo = FCS.mapToFSharpProjectOptions parsed.Head parsed
139+ if Seq.length failedLoads > 0 then
140+ logger.LogError( " Failed to load project '{0}'" , failedLoads)
141+ exit 1
131142
132- return fcsPo
143+ return FCS.mapManyOptions projectOptions |> Seq.toList
133144 }
134145
135- let runProjectAux
146+ let runProject
136147 ( client : Client < CliAnalyzerAttribute , CliContext >)
137148 ( fsharpOptions : FSharpProjectOptions )
138149 ( excludeIncludeFiles : Choice < Glob list , Glob list >)
139150 ( mappings : SeverityMappings )
140151 : Async < Result < AnalyzerMessage list , AnalysisFailure > list >
141152 =
142153 async {
154+ logger.LogInformation( " Checking project {0}" , fsharpOptions.ProjectFileName)
143155 let! checkProjectResults = fcs.ParseAndCheckProject( fsharpOptions)
144156
145157 let! messagesPerAnalyzer =
@@ -160,21 +172,23 @@ let runProjectAux
160172 | None -> false
161173 )
162174 |> Array.map ( fun fileName ->
163- let fileContent = File.ReadAllText fileName
164- let sourceText = SourceText.ofString fileContent
175+ async {
176+ let! fileContent = File.ReadAllTextAsync fileName |> Async.AwaitTask
177+ let sourceText = SourceText.ofString fileContent
178+ logger.LogDebug( " Checking file {0}" , fileName)
179+
180+ // Since we did ParseAndCheckProject, we can be sure that the file is in the project.
181+ // See https://fsharp.github.io/fsharp-compiler-docs/fcs/project.html for more information.
182+ let! parseAndCheckResults = fcs.GetBackgroundCheckResultsForFileInProject( fileName, fsharpOptions)
183+
184+ let ctx =
185+ Utils.createContext checkProjectResults fileName sourceText parseAndCheckResults
186+
187+ logger.LogInformation( " Running analyzers for {0}" , ctx.FileName)
188+ let! results = client.RunAnalyzers ctx
189+ return Ok results
190+ }
165191
166- Utils.typeCheckFile fcs logger fsharpOptions fileName ( Utils.SourceOfSource.SourceText sourceText)
167- |> Result.map ( Utils.createContext checkProjectResults fileName sourceText)
168- )
169- |> Array.map ( fun ctx ->
170- match ctx with
171- | Error e -> async.Return( Error e)
172- | Ok ctx ->
173- async {
174- logger.LogInformation( " Running analyzers for {0}" , ctx.FileName)
175- let! results = client.RunAnalyzers ctx
176- return Ok results
177- }
178192 )
179193 |> Async.Parallel
180194
@@ -188,20 +202,6 @@ let runProjectAux
188202 |> Seq.toList
189203 }
190204
191- let runProject
192- ( client : Client < CliAnalyzerAttribute , CliContext >)
193- toolsPath
194- properties
195- proj
196- ( excludeIncludeFiles : Choice < Glob list , Glob list >)
197- ( mappings : SeverityMappings )
198- =
199- async {
200- let path = Path.Combine( Environment.CurrentDirectory, proj) |> Path.GetFullPath
201- let! option = loadProject toolsPath properties path
202- return ! runProjectAux client option excludeIncludeFiles mappings
203- }
204-
205205let fsharpFiles = set [| " .fs" ; " .fsi" ; " .fsx" |]
206206
207207let isFSharpFile ( file : string ) =
@@ -249,7 +249,7 @@ let runFscArgs
249249 Stamp = None
250250 }
251251
252- runProjectAux client projectOptions excludeIncludeFiles mappings
252+ runProject client projectOptions excludeIncludeFiles mappings
253253
254254let printMessages ( msgs : AnalyzerMessage list ) =
255255
@@ -482,7 +482,11 @@ let main argv =
482482 use factory =
483483 LoggerFactory.Create( fun builder ->
484484 builder
485- .AddCustomFormatter( fun options -> options.UseAnalyzersMsgStyle <- false )
485+ .AddCustomFormatter( fun options ->
486+ options.UseAnalyzersMsgStyle <- false
487+ options.TimestampFormat <- " [HH:mm:ss.fff]"
488+ options.UseUtcTimestamp <- true
489+ )
486490 .SetMinimumLevel( logLevel)
487491 |> ignore
488492 )
@@ -610,7 +614,6 @@ let main argv =
610614 match projOpts, fscArgs with
611615 | [], None ->
612616 logger.LogError( " No project given. Use `--project PATH_TO_FSPROJ`." )
613-
614617 None
615618 | _ :: _, Some _ ->
616619 logger.LogError( " `--project` and `--fsc-args` cannot be combined." )
@@ -625,11 +628,16 @@ let main argv =
625628 logger.LogError( " Invalid `--project` argument. File does not exist: '{projPath}'" , projPath)
626629 exit 1
627630
628- projects
629- |> List.map ( fun projPath ->
630- runProject client toolsPath properties projPath exclInclFiles severityMapping
631- )
632- |> Async.Sequential
631+ async {
632+ let! loadedProjects = loadProjects toolsPath properties projects
633+
634+ return !
635+ loadedProjects
636+ |> List.map ( fun ( projPath : FSharpProjectOptions ) ->
637+ runProject client projPath exclInclFiles severityMapping
638+ )
639+ |> Async.Parallel
640+ }
633641 |> Async.RunSynchronously
634642 |> List.concat
635643 |> Some
0 commit comments