@@ -39,8 +39,9 @@ type Arguments =
39
39
interface IArgParserTemplate with
40
40
member s.Usage =
41
41
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."
44
45
| Property _ -> " A key=value pair of an MSBuild property."
45
46
| Configuration _ -> " The configuration to use, e.g. Debug or Release."
46
47
| Runtime _ -> " The runtime identifier (RID)."
@@ -118,28 +119,39 @@ let rec mkKn (ty: Type) =
118
119
119
120
let mutable logger : ILogger = Abstractions.NullLogger.Instance
120
121
121
- let loadProject toolsPath properties projPath =
122
+ let loadProjects toolsPath properties ( projPaths : string list ) =
122
123
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
+
123
131
let loader = WorkspaceLoader.Create( toolsPath, properties)
124
- let parsed = loader.LoadProjects [ projPath ] |> Seq.toList
132
+ let projectOptions = loader.LoadProjects projPaths
125
133
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
129
138
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
131
142
132
- return fcsPo
143
+ return FCS.mapManyOptions projectOptions |> Seq.toList
133
144
}
134
145
135
- let runProjectAux
146
+ let runProject
136
147
( client : Client < CliAnalyzerAttribute , CliContext >)
137
148
( fsharpOptions : FSharpProjectOptions )
138
149
( excludeIncludeFiles : Choice < Glob list , Glob list >)
139
150
( mappings : SeverityMappings )
140
151
: Async < Result < AnalyzerMessage list , AnalysisFailure > list >
141
152
=
142
153
async {
154
+ logger.LogInformation( " Checking project {0}" , fsharpOptions.ProjectFileName)
143
155
let! checkProjectResults = fcs.ParseAndCheckProject( fsharpOptions)
144
156
145
157
let! messagesPerAnalyzer =
@@ -160,21 +172,23 @@ let runProjectAux
160
172
| None -> false
161
173
)
162
174
|> 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
+ }
165
191
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
- }
178
192
)
179
193
|> Async.Parallel
180
194
@@ -188,20 +202,6 @@ let runProjectAux
188
202
|> Seq.toList
189
203
}
190
204
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
-
205
205
let fsharpFiles = set [| " .fs" ; " .fsi" ; " .fsx" |]
206
206
207
207
let isFSharpFile ( file : string ) =
@@ -249,7 +249,7 @@ let runFscArgs
249
249
Stamp = None
250
250
}
251
251
252
- runProjectAux client projectOptions excludeIncludeFiles mappings
252
+ runProject client projectOptions excludeIncludeFiles mappings
253
253
254
254
let printMessages ( msgs : AnalyzerMessage list ) =
255
255
@@ -482,7 +482,11 @@ let main argv =
482
482
use factory =
483
483
LoggerFactory.Create( fun builder ->
484
484
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
+ )
486
490
.SetMinimumLevel( logLevel)
487
491
|> ignore
488
492
)
@@ -610,7 +614,6 @@ let main argv =
610
614
match projOpts, fscArgs with
611
615
| [], None ->
612
616
logger.LogError( " No project given. Use `--project PATH_TO_FSPROJ`." )
613
-
614
617
None
615
618
| _ :: _, Some _ ->
616
619
logger.LogError( " `--project` and `--fsc-args` cannot be combined." )
@@ -625,11 +628,16 @@ let main argv =
625
628
logger.LogError( " Invalid `--project` argument. File does not exist: '{projPath}'" , projPath)
626
629
exit 1
627
630
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
+ }
633
641
|> Async.RunSynchronously
634
642
|> List.concat
635
643
|> Some
0 commit comments