Skip to content

Commit adef05b

Browse files
committed
Refactor error handling for project restoration and update related types and tests
1 parent b5d8719 commit adef05b

File tree

6 files changed

+60
-31
lines changed

6 files changed

+60
-31
lines changed

src/Ionide.ProjInfo/Library.fs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ open Patterns
1717
type ProjectNotRestoredError<'e, 'buildResult> =
1818
static abstract NotRestored: 'buildResult -> 'e
1919

20-
type ProjectNotRestoredDU<'BuildResult> =
21-
| BuildErr of 'BuildResult
20+
type ParseError<'BuildResult> =
21+
| NotRestored of 'BuildResult
2222

23-
interface ProjectNotRestoredError<ProjectNotRestoredDU<'BuildResult>, 'BuildResult> with
24-
static member NotRestored(result) = BuildErr result
23+
interface ProjectNotRestoredError<ParseError<'BuildResult>, 'BuildResult> with
24+
static member NotRestored(result) = NotRestored result
2525

2626

2727
/// functions for .net sdk probing
@@ -966,6 +966,7 @@ module ProjectLoader =
966966
(analyzers: Analyzer list)
967967
(allProps: Map<string, Set<string>>)
968968
(allItems: Map<string, Set<string * Map<string, string>>>)
969+
(imports: string list)
969970
=
970971
let projDir = Path.GetDirectoryName path
971972

@@ -1041,6 +1042,7 @@ module ProjectLoader =
10411042
Analyzers = analyzers
10421043
AllProperties = allProps
10431044
AllItems = allItems
1045+
Imports = imports
10441046
}
10451047

10461048

@@ -1052,7 +1054,6 @@ module ProjectLoader =
10521054
| TraversalProjectInfo of ProjectReference list
10531055
| OtherProjectInfo of ProjectInstance
10541056

1055-
10561057
let getLoadedProjectInfo<'e when ProjectNotRestoredError<'e, LoadedProject>> (path: string) customProperties project =
10571058

10581059
match project with
@@ -1166,12 +1167,16 @@ module ProjectLoader =
11661167
)
11671168
|> Seq.toList
11681169

1170+
let imports =
1171+
p.ImportPaths
1172+
|> Seq.toList
1173+
11691174

11701175
if not sdkInfo.RestoreSuccess then
11711176
Error('e.NotRestored project)
11721177
else
11731178
let proj =
1174-
mapToProject path commandLineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps analyzers allProperties allItems
1179+
mapToProject path commandLineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps analyzers allProperties allItems imports
11751180

11761181
Ok(LoadedProjectInfo.StandardProjectInfo proj)
11771182
| LoadedProject.Other p -> Ok(LoadedProjectInfo.OtherProjectInfo p)
@@ -1432,7 +1437,7 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri
14321437
| Ok projectOptions ->
14331438

14341439
Some projectOptions
1435-
| Error(ProjectNotRestoredDU.BuildErr e) ->
1440+
| Error(ParseError.NotRestored e) ->
14361441
logger.error (
14371442
Log.setMessage "Failed loading projects {error}"
14381443
>> Log.addContextDestructured "error" e
@@ -1608,7 +1613,7 @@ type WorkspaceLoader private (toolsPath: ToolsPath, ?globalProperties: (string *
16081613
| ProjectLoader.LoadedProjectInfo.OtherProjectInfo p -> None
16091614

16101615
lst, info
1611-
| Error(ProjectNotRestoredDU.BuildErr e) ->
1616+
| Error(ParseError.NotRestored e) ->
16121617
loadingNotification.Trigger(WorkspaceProjectState.Failed(p, GenericError(p, "Not restored")))
16131618
[], None
16141619

src/Ionide.ProjInfo/ProjectLoader2.fs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ module SemaphoreSlimExtensions =
4545
)
4646

4747

48-
module Map =
48+
module internal Map =
4949

50-
let mapAddSome key value map =
50+
let inline mapAddSome key value map =
5151
match value with
5252
| Some v -> Map.add key v map
5353
| None -> map
5454

55-
let union loses wins =
55+
let inline union loses wins =
5656
Map.fold (fun acc key value -> Map.add key value acc) loses wins
5757

5858
let inline ofDict dictionary =
@@ -320,7 +320,7 @@ module TargetFrameworks =
320320
// type ProjectProjectMap = ProjectMap<Project>
321321
// type ProjectGraphMap = ProjectMap<ProjectGraphNode>
322322

323-
module ProjectLoading =
323+
module internal ProjectLoading =
324324

325325
// let getAllTfms (projectPath: ProjectPath) pc props =
326326
// let p = findOrCreateMatchingProject projectPath pc props
@@ -339,7 +339,7 @@ module ProjectLoading =
339339
// getAllTfms projectPath
340340
// |> Option.bind Array.tryHead
341341

342-
let defaultProjectInstanceFactory (projectPath: string) (xml: Dictionary<string, string>) (collection: ProjectCollection) =
342+
let inline defaultProjectInstanceFactory (projectPath: string) (xml: Dictionary<string, string>) (collection: ProjectCollection) =
343343
let props = Map.union (Map.ofDict xml) (Map.ofDict collection.GlobalProperties)
344344
ProjectInstance(projectPath, props, toolsVersion = null, projectCollection = collection)
345345

@@ -500,13 +500,13 @@ type ProjectLoader2 =
500500
/// or TargetFrameworks defined in the project files.</returns>
501501
/// <remarks>
502502
/// This method evaluates each project file and checks for the presence of a "TargetFramework"
503-
/// property. If it exists, the project is returned as is. If it does not
504-
/// exist, it checks for the "TargetFrameworks"
503+
/// property. If it exists, the project is returned as is. If it does not exist, it checks for the "TargetFrameworks"
505504
/// property and splits it into individual TargetFrameworks. For each TargetFramework, it creates
506-
/// a new project
507-
/// with the "TargetFramework" global property set to that TargetFramework.
505+
/// a new project with the "TargetFramework" global property set to that TargetFramework.
508506
/// </remarks>
509507
static member EvaluateAsGraphAllTfms(entryProjectFile: ProjectGraphEntryPoint seq, ?projectCollection: ProjectCollection, ?projectInstanceFactory) =
508+
// For some reason, the graph evaluation doesn't handle multiple TFMs well
509+
// So first we evaluate the graph to find all projects
510510
let graph =
511511
ProjectLoader2.EvaluateAsGraph(entryProjectFile, ?projectCollection = projectCollection, ?projectInstanceFactory = projectInstanceFactory)
512512

@@ -528,7 +528,7 @@ type ProjectLoader2 =
528528
|> Option.map (fun _ -> ProjectGraphEntryPoint(node.ProjectInstance.FullPath, globalProperties = node.ProjectInstance.GlobalProperties))
529529
)
530530

531-
531+
// Then, re-evaluate the graph with those projects
532532
ProjectLoader2.EvaluateAsGraph(projects, ?projectCollection = projectCollection, ?projectInstanceFactory = projectInstanceFactory)
533533

534534
/// <summary>
@@ -585,8 +585,7 @@ type ProjectLoader2 =
585585
/// <param name="buildParameters">Function to get build parameters for each project.</param
586586
/// ><param name="targetsToBuild">Optional targets to build. Defaults to design-time build targets.</param>
587587
/// <param name="flags">Optional flags for the build request. Defaults to ProjectLoader2.DefaultFlags.</param>
588-
/// <param name="ct">Optional cancellation token to cancel the
589-
/// build.</param>
588+
/// <param name="ct">Optional cancellation token to cancel the build.</param>
590589
/// <returns>A task that returns an array of BuildResult or an error containing the failed build and message.</returns>
591590
/// <remarks>
592591
/// This method will visit each project, build it, and then recursively visit its references.

src/Ionide.ProjInfo/Types.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ module Types =
8484
/// Will have Key Value pairs like "PkgIonide_Analyzers" -> "C:\Users\username\.nuget\packages\ionide.analyzers\0.14.7"
8585
/// The "analyzers/dotnet/fs" subfolder is not included here, just the package root
8686
Analyzers: Analyzer list
87+
/// The full file paths of all the files that during evaluation contributed to this project instance.
88+
/// This does not include projects that were never imported because a condition on an Import element was false.
89+
/// The outer ProjectRootElement that maps to this project instance itself is not included.
90+
Imports: string list
8791
} with
8892

8993
/// ResolvedTargetPath is the path to the primary reference assembly for this project.

test/Ionide.ProjInfo.Tests/ProjectLoader2Tests.fs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ module ProjectLoader2Tests =
9696
|> Seq.choose (fun x ->
9797
match x with
9898
| Ok(LoadedProjectInfo.StandardProjectInfo x) -> Some x
99-
| Result.Error(ProjectNotRestoredDU.BuildErr _) -> None
99+
| Result.Error(ParseError.NotRestored _) -> None
100100
| _ -> None
101101
)
102102
| Result.Error(BuildErrors.BuildErr(result, errorLogs)) ->
@@ -150,8 +150,11 @@ module ProjectLoader2Tests =
150150
function
151151
| Ok result ->
152152
match ProjectLoader2.Parse result with
153-
| Ok(LoadedProjectInfo.StandardProjectInfo x) -> Some x
154-
| Result.Error(ProjectNotRestoredDU.BuildErr _) -> None
153+
| Ok(LoadedProjectInfo.StandardProjectInfo x) ->
154+
let validProjectRestore = Newtonsoft.Json.JsonConvert.SerializeObject x
155+
File.WriteAllText(Path.Combine(env.TestDir.FullName, "restored-project.json"), validProjectRestore)
156+
Some x
157+
| Result.Error(ParseError.NotRestored _) -> None
155158
| _ -> None
156159
| _ -> None
157160
)
@@ -310,10 +313,12 @@ module ProjectLoader2Tests =
310313
| Ok result ->
311314
ProjectLoader2.Parse result
312315
|> Seq.choose (
313-
314316
function
315-
| Ok(LoadedProjectInfo.StandardProjectInfo x) -> Some x
316-
| Result.Error(ProjectNotRestoredDU.BuildErr _) -> None
317+
| Ok(LoadedProjectInfo.StandardProjectInfo x) ->
318+
let validProjectRestore = Newtonsoft.Json.JsonConvert.SerializeObject x
319+
File.WriteAllText(Path.Combine(env.TestDir.FullName, "restored-project.json"), validProjectRestore)
320+
Some x
321+
| Result.Error(ParseError.NotRestored _) -> None
317322
| _ -> None
318323
)
319324
|> Seq.iter (fun x -> Expect.equal x.SourceFiles expectedSources "")
@@ -323,6 +328,17 @@ module ProjectLoader2Tests =
323328
}
324329
)
325330

331+
let contains (s: string) (o: string) = o.Contains(s)
332+
let endsWith (s: string) (o: string) = o.EndsWith(s)
333+
334+
let filesOfInterest = [
335+
endsWith "Directory.Build.props"
336+
endsWith "Directory.Build.targets"
337+
endsWith "Directory.Build.props"
338+
endsWith ".sln.targets"
339+
endsWith ".Build.rsp"
340+
]
341+
326342

327343
testCaseTask
328344
|> testWithEnv
@@ -359,6 +375,7 @@ module ProjectLoader2Tests =
359375

360376
let projs = ProjectLoader2.EvaluateAsProjectsAllTfms(entryPoints, projectCollection = pc)
361377

378+
let projs = Seq.toList projs
362379
// Execution
363380

364381
let bm = new BuildManagerSession()
@@ -383,11 +400,16 @@ module ProjectLoader2Tests =
383400
match result with
384401
| Result.Error _ -> failwith "expected success"
385402
| Ok result ->
403+
ignore result.ProjectStateAfterBuild.ImportPaths
404+
386405
match ProjectLoader2.Parse result with
387406

407+
| Ok(LoadedProjectInfo.StandardProjectInfo x) ->
408+
let validProjectRestore = Newtonsoft.Json.JsonConvert.SerializeObject x
409+
File.WriteAllText(Path.Combine(env.TestDir.FullName, "restored-project.json"), validProjectRestore)
388410

389-
| Ok(LoadedProjectInfo.StandardProjectInfo x) -> Expect.equal x.SourceFiles expectedSources ""
390-
| Result.Error(ProjectNotRestoredDU.BuildErr e) -> failwith "%A" e
411+
Expect.equal x.SourceFiles expectedSources ""
412+
| Result.Error(ParseError.NotRestored e) -> failwithf "%A" e
391413
| otherwise -> failwith $"Unexpected result {otherwise}"
392414

393415
}
@@ -565,7 +587,7 @@ module ProjectLoader2Tests =
565587

566588
// File.WriteAllText(Path.Combine(env.TestDir.FullName, "non-restored-project.json"), validProjectRestore)
567589
// Expect.isFalse x.ProjectSdkInfo.RestoreSuccess "expected restore to fail"
568-
| Result.Error(ProjectNotRestoredDU.BuildErr(StandardProject e)) -> ()
590+
| Result.Error(ParseError.NotRestored(StandardProject e)) -> ()
569591
| e -> failwithf "%A" e
570592

571593
return ()

test/Ionide.ProjInfo.Tests/TestUtils.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,3 @@ module Expect =
227227
match result with
228228
| Ok v -> v
229229
| Error _ -> failwith "unreachable"
230-

test/Ionide.ProjInfo.Tests/Tests.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1728,7 +1728,7 @@ let testLoadProject toolsPath =
17281728
match ProjectLoader.getLoadedProjectInfo projPath [] proj with
17291729
| Ok(ProjectLoader.LoadedProjectInfo.StandardProjectInfo proj) -> Expect.equal proj.ProjectFileName projPath "project file names"
17301730
| Ok(ProjectLoader.LoadedProjectInfo.TraversalProjectInfo refs) -> failwith "expected standard project, not a traversal project"
1731-
| Result.Error(ProjectNotRestoredDU.BuildErr exn) -> failwith $"Project Not Restored {exn}"
1731+
| Result.Error(ParseError.NotRestored exn) -> failwith $"Project Not Restored {exn}"
17321732
| otherwise -> failwith $"Unexpected result {otherwise}"
17331733
)
17341734

0 commit comments

Comments
 (0)