diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index a0a34ca0..aa272694 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"paket": {
- "version": "7.2.1",
+ "version": "8.0.3",
"commands": [
"paket"
]
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 203e35cd..26a4a164 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,5 +8,12 @@
"test/examples",
"packages"
],
- "editor.formatOnSave": true
-}
+ "editor.formatOnSave": true,
+ "cSpell.words": [
+ "binlog",
+ "inheritdoc",
+ "tfms",
+ "vswhere",
+ "xbuild"
+ ]
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 353c63dd..6039f59f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.63.0] - 2024-02-06
+
+### Changed
+
+* [Add support for reference assemblies to project cracking and FCS ProjectOptions mapping](https://github.com/ionide/proj-info/pull/200)
+
## [0.62.0] - 2023-08-21
### Changed
diff --git a/src/Ionide.ProjInfo.FCS/Library.fs b/src/Ionide.ProjInfo.FCS/Library.fs
index b3446f2f..bd2f06b3 100644
--- a/src/Ionide.ProjInfo.FCS/Library.fs
+++ b/src/Ionide.ProjInfo.FCS/Library.fs
@@ -49,14 +49,14 @@ module FCS =
| Some p ->
(p.ProjectFileName.EndsWith(".csproj")
|| p.ProjectFileName.EndsWith(".vbproj"))
- && File.Exists p.TargetPath
+ && File.Exists p.ResolvedTargetPath
| None -> false
if p.ProjectFileName.EndsWith ".fsproj" then
knownProject
- |> Option.map (fun p ->
+ |> Option.map (fun (p: ProjectOptions) ->
let theseOptions = makeFSharpProjectReference p
- FSharpReferencedProject.FSharpReference(p.TargetPath, theseOptions)
+ FSharpReferencedProject.FSharpReference(p.ResolvedTargetPath, theseOptions)
)
elif isDotnetProject knownProject then
knownProject
diff --git a/src/Ionide.ProjInfo/Library.fs b/src/Ionide.ProjInfo/Library.fs
index ad4769b7..96806644 100644
--- a/src/Ionide.ProjInfo/Library.fs
+++ b/src/Ionide.ProjInfo/Library.fs
@@ -145,7 +145,7 @@ module LegacyFrameworkDiscovery =
|> Some
else
// taken from https://github.com/microsoft/vswhere
- // vswhere.exe is guranteed to be at the following location. refer to https://github.com/Microsoft/vswhere/issues/162
+ // vswhere.exe is guaranteed to be at the following location. refer to https://github.com/Microsoft/vswhere/issues/162
let vsWhereDir =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Visual Studio", "Installer")
|> DirectoryInfo
@@ -404,9 +404,9 @@ module ProjectLoader =
)
if String.IsNullOrWhiteSpace tfm then
- let tfms = pi.GetPropertyValue "TargetFrameworks"
+ let targetFrameworks = pi.GetPropertyValue "TargetFrameworks"
- match tfms with
+ match targetFrameworks with
| null -> None
| tfms ->
match tfms.Split(';') with
@@ -558,7 +558,7 @@ module ProjectLoader =
|> Seq.filter (fun p -> p.ItemType = "CscCommandLineArgs")
|> Seq.map (fun p -> p.EvaluatedInclude)
- let getP2Prefs (LoadedProject project) =
+ let getP2PRefs (LoadedProject project) =
project.Items
|> Seq.filter (fun p -> p.ItemType = "_MSBuildProjectReferenceExistent")
|> Seq.map (fun p ->
@@ -755,7 +755,7 @@ module ProjectLoader =
path
)
- let project = {
+ let project: ProjectOptions = {
ProjectId = Some path
ProjectFileName = path
TargetFramework = sdkInfo.TargetFramework
@@ -766,9 +766,11 @@ module ProjectLoader =
LoadTime = DateTime.Now
TargetPath =
props
- |> Seq.tryFind (fun n -> n.Name = "TargetPath")
- |> Option.map (fun n -> n.Value)
+ |> Seq.tryPick (fun n -> if n.Name = "TargetPath" then Some n.Value else None)
|> Option.defaultValue ""
+ TargetRefPath =
+ props
+ |> Seq.tryPick (fun n -> if n.Name = "TargetRefPath" then Some n.Value else None)
ProjectOutputType = outputType
ProjectSdkInfo = sdkInfo
Items = compileItems
@@ -804,13 +806,14 @@ module ProjectLoader =
"BaseIntermediateOutputPath"
"IntermediateOutputPath"
"TargetPath"
+ "TargetRefPath"
"IsCrossTargetingBuild"
"TargetFrameworks"
]
- let p2pRefs = getP2Prefs project
+ let p2pRefs = getP2PRefs project
- let comandlineArgs =
+ let commandLineArgs =
if path.EndsWith ".fsproj" then
getFscArgs project
else
@@ -826,7 +829,7 @@ module ProjectLoader =
Result.Error "not restored"
else
- let proj = mapToProject path comandlineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps
+ let proj = mapToProject path commandLineArgs p2pRefs compileItems nuGetRefs sdkInfo props customProps
Result.Ok proj
@@ -918,7 +921,7 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri
let globalProperties = ProjectLoader.getGlobalProps projectPath tfm globalProperties
ProjectInstance(projectPath, globalProperties, toolsVersion = null, projectCollection = projectCollection)
- let projectGraphProjs (paths: string seq) =
+ let projectGraphProjects (paths: string seq) =
handleProjectGraphFailures
<| fun () ->
@@ -1044,28 +1047,28 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri
then
handleError msbuildMessage result.Exception
else
- let buildProjs =
+ let builtProjects =
result.ResultsByNode.Keys
|> Seq.collect (fun (pgn: ProjectGraphNode) -> seq { yield pgn.ProjectInstance })
- |> Seq.toList
+ |> Seq.toArray
- let projectsBuilt = Seq.length buildProjs
+ let projectsBuiltCount = builtProjects.Length
match result.OverallResult with
| BuildResultCode.Success ->
logger.info (
- Log.setMessageI $"Overall Build: {result.OverallResult:overallCode}, projects built {projectsBuilt:count}"
+ Log.setMessageI $"Overall Build: {result.OverallResult:overallCode}, projects built {projectsBuiltCount:count}"
>> Log.addExn result.Exception
)
| BuildResultCode.Failure
| _ ->
logger.error (
- Log.setMessageI $"Overall Build: {result.OverallResult:overallCode}, projects built {projectsBuilt:count} : {msbuildMessage:msbuildMessage} "
+ Log.setMessageI $"Overall Build: {result.OverallResult:overallCode}, projects built {projectsBuiltCount:count} : {msbuildMessage:msbuildMessage} "
>> Log.addExn result.Exception
)
let projects =
- buildProjs
+ builtProjects
|> Seq.map (fun p -> p.FullPath, ProjectLoader.getLoadedProjectInfo p.FullPath customProperties (ProjectLoader.LoadedProject p))
|> Seq.choose (fun (projectPath, projectOptionResult) ->
@@ -1111,7 +1114,7 @@ type WorkspaceLoaderViaProjectGraph private (toolsPath, ?globalProperties: (stri
interface IWorkspaceLoader with
override this.LoadProjects(projects: string list, customProperties, binaryLogs) =
- projectGraphProjs projects
+ projectGraphProjects projects
|> Option.map (fun pg -> loadProjects (pg, customProperties, binaryLogs))
|> Option.defaultValue Seq.empty
@@ -1273,8 +1276,8 @@ type WorkspaceLoader private (toolsPath: ToolsPath, ?globalProperties: (string *
member this.LoadSln(sln, customProperties: string list, binaryLogs) =
match InspectSln.tryParseSln sln with
| Ok(_, slnData) ->
- let projs = InspectSln.loadingBuildOrder slnData
- this.LoadProjects(projs, customProperties, binaryLogs)
+ let solutionProjects = InspectSln.loadingBuildOrder slnData
+ this.LoadProjects(solutionProjects, customProperties, binaryLogs)
| Error d -> failwithf "Cannot load the sln: %A" d
member this.LoadSln(sln, customProperties) =
@@ -1340,20 +1343,20 @@ module ProjectViewer =
|> (fun path -> path.EndsWith(assemblyAttributesName))
| None -> false
- //the generated assemblyinfo.fs are not shown as sources
- let isGeneratedAssemblyinfo (name: string) =
+ //The generated AssemblyInfo.fs are not shown as sources
+ let isGeneratedAssemblyInfo (name: string) =
//TODO check is in `obj` dir for the tfm
//TODO better, get the name from fsproj
name.EndsWith($"{projName}.AssemblyInfo.{sourceFilesExtension}")
let includeSourceFile (name: string) =
not (isAssemblyAttributes name)
- && not (isGeneratedAssemblyinfo name)
+ && not (isGeneratedAssemblyInfo name)
sources
|> List.choose (
function
- | ProjectItem.Compile(name, fullpath) -> Some(name, fullpath)
+ | ProjectItem.Compile(name, fullPath) -> Some(name, fullPath)
)
|> List.filter (fun (_, p) -> includeSourceFile p)
@@ -1363,5 +1366,5 @@ module ProjectViewer =
|> Path.GetFileNameWithoutExtension
Items =
compileFiles
- |> List.map (fun (name, fullpath) -> ProjectViewerItem.Compile(fullpath, { ProjectViewerItemConfig.Link = name }))
+ |> List.map (fun (name, fullPath) -> ProjectViewerItem.Compile(fullPath, { ProjectViewerItemConfig.Link = name }))
}
diff --git a/src/Ionide.ProjInfo/Types.fs b/src/Ionide.ProjInfo/Types.fs
index 53d61074..ec1af260 100644
--- a/src/Ionide.ProjInfo/Types.fs
+++ b/src/Ionide.ProjInfo/Types.fs
@@ -59,13 +59,21 @@ module Types =
ReferencedProjects: ProjectReference list
PackageReferences: PackageReference list
LoadTime: DateTime
+ /// The path to the primary executable or loadable output of this project
TargetPath: string
+ /// If present, this project produced a reference assembly and this should be used as primary reference for downstream proejcts
+ TargetRefPath: string option
ProjectOutputType: ProjectOutputType
ProjectSdkInfo: ProjectSdkInfo
Items: ProjectItem list
Properties: Property list
CustomProperties: Property list
- }
+ } with
+ /// ResolvedTargetPath is the path to the primary reference assembly for this project.
+ /// For projects that produce ReferenceAssemblies, this is the path to the reference assembly.
+ /// For other projects, this is the same as TargetPath.
+ member x.ResolvedTargetPath =
+ defaultArg x.TargetRefPath x.TargetPath
type CompileItem = {
Name: string
diff --git a/test/Ionide.ProjInfo.Tests/TestAssets.fs b/test/Ionide.ProjInfo.Tests/TestAssets.fs
index df33e182..c80e531e 100644
--- a/test/Ionide.ProjInfo.Tests/TestAssets.fs
+++ b/test/Ionide.ProjInfo.Tests/TestAssets.fs
@@ -280,3 +280,27 @@ let ``sample9 NetSdk library`` = {
TargetFrameworks = Map.ofList [ "netstandard2.0", sourceFiles [ "Library.fs" ] ]
ProjectReferences = []
}
+
+/// dotnet sdk library with ProduceReferenceAssembly=true
+let ``NetSDK library with ProduceReferenceAssembly`` = {
+ ProjDir = "sample-netsdk-prodref"
+ AssemblyName = "l1"
+ ProjectFile =
+ "l1"
+ / "l1.fsproj"
+ TargetFrameworks = Map.ofList [ "netstandard2.0", sourceFiles [ "Library.fs" ] ]
+ ProjectReferences = []
+}
+
+
+let ``NetSDK library referencing ProduceReferenceAssembly library`` = {
+ ProjDir = "sample-netsdk-prodref"
+ AssemblyName = "l2"
+ ProjectFile =
+ "l2"
+ / "l2.fsproj"
+ TargetFrameworks = Map.ofList [ "netstandard2.0", sourceFiles [ "Library.fs" ] ]
+ ProjectReferences = [
+ ``NetSDK library with ProduceReferenceAssembly``
+ ]
+}
diff --git a/test/Ionide.ProjInfo.Tests/Tests.fs b/test/Ionide.ProjInfo.Tests/Tests.fs
index 593397a4..0a1f1237 100644
--- a/test/Ionide.ProjInfo.Tests/Tests.fs
+++ b/test/Ionide.ProjInfo.Tests/Tests.fs
@@ -30,6 +30,20 @@ let ExamplesDir =
/ "test"
/ "examples"
+let pathForTestAssets (test: TestAssetProjInfo) =
+ ExamplesDir
+ / test.ProjDir
+
+let pathForProject (test: TestAssetProjInfo) =
+ pathForTestAssets test
+ / test.ProjectFile
+
+let implAssemblyForProject (test: TestAssetProjInfo) =
+ $"{test.AssemblyName}.dll"
+
+let refAssemblyForProject (test: TestAssetProjInfo) =
+ Path.Combine("ref", implAssemblyForProject test)
+
let TestRunDir =
RepoDir
/ "test"
@@ -1482,6 +1496,7 @@ let testFCSmapManyProjCheckCaching =
PackageReferences = []
LoadTime = DateTime.MinValue
TargetPath = "TP"
+ TargetRefPath = Some "TRP"
ProjectOutputType = ProjectOutputType.Library
ProjectSdkInfo = sdkInfo
Items = []
@@ -2118,6 +2133,41 @@ let csharpLibTest toolsPath (workspaceFactory: ToolsPath -> IWorkspaceLoader) =
| _ -> failwith "Should have found a C# reference"
)
+let referenceAssemblySupportTest toolsPath prefix (workspaceFactory: ToolsPath -> IWorkspaceLoader) =
+ testCase
+ |> withLog
+ $"{prefix} can reference projects that support reference assemblies"
+ (fun logger fs ->
+ let parentProj: TestAssetProjInfo = ``NetSDK library with ProduceReferenceAssembly``
+ let childProj = ``NetSDK library referencing ProduceReferenceAssembly library``
+
+ let projPath = pathForProject childProj
+
+ // need to build the projects first so that there's something to latch on to
+ dotnet fs [
+ "build"
+ projPath
+ ]
+ |> checkExitCodeZero
+
+ let loader = workspaceFactory toolsPath
+
+ let parsed =
+ loader.LoadProjects [ projPath ]
+ |> Seq.toList
+
+ Expect.hasLength parsed 2 "Should have loaded the F# lib and the referenced F# lib"
+ let fsharpProject = parsed |> Seq.find (fun p -> Path.GetFileName(p.ProjectFileName) = Path.GetFileName(childProj.ProjectFile))
+ let mapped = FCS.mapToFSharpProjectOptions fsharpProject parsed
+ let referencedProjects = mapped.ReferencedProjects
+ Expect.hasLength referencedProjects 1 "Should have a reference to the F# ProjectReference lib"
+
+ match referencedProjects[0] with
+ | FSharpReferencedProject.FSharpReference(targetPath, _) ->
+ Expect.stringContains targetPath (refAssemblyForProject parentProj) "Should have found the ref assembly for the F# lib"
+ | _ -> failwith "Should have found a F# reference"
+ )
+
let testProjectLoadBadData =
testCase
|> withLog
@@ -2246,4 +2296,8 @@ let tests toolsPath =
testProjectLoadBadData
expensiveTests toolsPath WorkspaceLoader.Create
csharpLibTest toolsPath WorkspaceLoader.Create
+
+ referenceAssemblySupportTest toolsPath (nameof(WorkspaceLoader)) WorkspaceLoader.Create
+ referenceAssemblySupportTest toolsPath (nameof(WorkspaceLoaderViaProjectGraph)) WorkspaceLoaderViaProjectGraph.Create
+
]
diff --git a/test/examples/sample-netsdk-prodref/README.md b/test/examples/sample-netsdk-prodref/README.md
new file mode 100644
index 00000000..d84e3731
--- /dev/null
+++ b/test/examples/sample-netsdk-prodref/README.md
@@ -0,0 +1 @@
+a library (l1) with ProduceReferenceAssembly set to true, and another library (l2) that references l1
diff --git a/test/examples/sample-netsdk-prodref/l1/Library.fs b/test/examples/sample-netsdk-prodref/l1/Library.fs
new file mode 100644
index 00000000..7f8edfa6
--- /dev/null
+++ b/test/examples/sample-netsdk-prodref/l1/Library.fs
@@ -0,0 +1,5 @@
+namespace n1
+
+module Say =
+ let hello name =
+ printfn "Hello %s" name
diff --git a/test/examples/sample-netsdk-prodref/l1/l1.fsproj b/test/examples/sample-netsdk-prodref/l1/l1.fsproj
new file mode 100644
index 00000000..cfb01100
--- /dev/null
+++ b/test/examples/sample-netsdk-prodref/l1/l1.fsproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
diff --git a/test/examples/sample-netsdk-prodref/l2/Library.fs b/test/examples/sample-netsdk-prodref/l2/Library.fs
new file mode 100644
index 00000000..7f8edfa6
--- /dev/null
+++ b/test/examples/sample-netsdk-prodref/l2/Library.fs
@@ -0,0 +1,5 @@
+namespace n1
+
+module Say =
+ let hello name =
+ printfn "Hello %s" name
diff --git a/test/examples/sample-netsdk-prodref/l2/l2.fsproj b/test/examples/sample-netsdk-prodref/l2/l2.fsproj
new file mode 100644
index 00000000..b96931a5
--- /dev/null
+++ b/test/examples/sample-netsdk-prodref/l2/l2.fsproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+