Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reference assemblies to ProjInfo #200

Merged
merged 3 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"paket": {
"version": "7.2.1",
"version": "8.0.3",
"commands": [
"paket"
]
Expand Down
11 changes: 9 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,12 @@
"test/examples",
"packages"
],
"editor.formatOnSave": true
}
"editor.formatOnSave": true,
"cSpell.words": [
"binlog",
"inheritdoc",
"tfms",
"vswhere",
"xbuild"
]
}
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/Ionide.ProjInfo.FCS/Library.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 28 additions & 25 deletions src/Ionide.ProjInfo/Library.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -755,7 +755,7 @@ module ProjectLoader =
path
)

let project = {
let project: ProjectOptions = {
ProjectId = Some path
ProjectFileName = path
TargetFramework = sdkInfo.TargetFramework
Expand All @@ -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)
Comment on lines +771 to +773
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we have the potential property, we can flow it through

ProjectOutputType = outputType
ProjectSdkInfo = sdkInfo
Items = compileItems
Expand Down Expand Up @@ -804,13 +806,14 @@ module ProjectLoader =
"BaseIntermediateOutputPath"
"IntermediateOutputPath"
"TargetPath"
"TargetRefPath"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to grab the property in the first place

"IsCrossTargetingBuild"
"TargetFrameworks"
]

let p2pRefs = getP2Prefs project
let p2pRefs = getP2PRefs project

let comandlineArgs =
let commandLineArgs =
if path.EndsWith ".fsproj" then
getFscArgs project
else
Expand All @@ -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

Expand Down Expand Up @@ -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 () ->
Expand Down Expand Up @@ -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) ->
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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) =
Expand Down Expand Up @@ -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)

Expand All @@ -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 }))
}
10 changes: 9 additions & 1 deletion src/Ionide.ProjInfo/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +72 to +76
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and we can make it easier for callers to get the correct field.


type CompileItem = {
Name: string
Expand Down
24 changes: 24 additions & 0 deletions test/Ionide.ProjInfo.Tests/TestAssets.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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``
]
}
54 changes: 54 additions & 0 deletions test/Ionide.ProjInfo.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1482,6 +1496,7 @@ let testFCSmapManyProjCheckCaching =
PackageReferences = []
LoadTime = DateTime.MinValue
TargetPath = "TP"
TargetRefPath = Some "TRP"
ProjectOutputType = ProjectOutputType.Library
ProjectSdkInfo = sdkInfo
Items = []
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

]
1 change: 1 addition & 0 deletions test/examples/sample-netsdk-prodref/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a library (l1) with ProduceReferenceAssembly set to true, and another library (l2) that references l1
5 changes: 5 additions & 0 deletions test/examples/sample-netsdk-prodref/l1/Library.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace n1

module Say =
let hello name =
printfn "Hello %s" name
12 changes: 12 additions & 0 deletions test/examples/sample-netsdk-prodref/l1/l1.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
</PropertyGroup>

<ItemGroup>
<Compile Include="Library.fs" />
</ItemGroup>

</Project>
5 changes: 5 additions & 0 deletions test/examples/sample-netsdk-prodref/l2/Library.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace n1

module Say =
let hello name =
printfn "Hello %s" name
Loading
Loading