Skip to content

Commit 0d0f524

Browse files
authored
use correct timestamp and file stream for cross-language references (#153)
1 parent e63ea95 commit 0d0f524

File tree

6 files changed

+97
-12
lines changed

6 files changed

+97
-12
lines changed

src/Ionide.ProjInfo.FCS/Library.fs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ open Ionide.ProjInfo.Types
66
open FSharp.Compiler.CodeAnalysis
77

88
module FCS =
9+
let private loadFromDotnetDll (p: ProjectOptions) =
10+
/// because only a successful compilation will be written to a DLL, we can rely on
11+
/// the file metadata for things like write times
12+
let projectFile = FileInfo p.TargetPath
13+
14+
let getStamp () = projectFile.LastWriteTimeUtc
15+
16+
let getStream (ctok: System.Threading.CancellationToken) =
17+
projectFile.OpenRead() :> Stream |> Some
18+
19+
FSharpReferencedProject.CreatePortableExecutable(p.TargetPath, getStamp, getStream)
20+
921
let rec mapToFSharpProjectOptions (projectOptions: ProjectOptions) (allKnownProjects: ProjectOptions seq) : FSharpProjectOptions =
1022
{ ProjectId = None
1123
ProjectFileName = projectOptions.ProjectFileName
@@ -26,8 +38,7 @@ module FCS =
2638
knownProject
2739
|> Option.map (fun p -> FSharpReferencedProject.CreateFSharp(p.TargetPath, mapToFSharpProjectOptions p allKnownProjects))
2840
elif isDotnetProject knownProject then
29-
knownProject
30-
|> Option.map (fun p -> FSharpReferencedProject.CreatePortableExecutable(p.TargetPath, (fun () -> DateTime.Now), (fun _ -> None)))
41+
knownProject |> Option.map loadFromDotnetDll
3142
else
3243
None)
3344
IsIncompleteTypeCheckEnvironment = false

test/Ionide.ProjInfo.Tests/Tests.fs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
module Tests
22

3+
open DotnetProjInfo.TestAssets
34
open Expecto
4-
open FileUtils
5-
open Medallion.Shell
6-
open System.IO
75
open Expecto.Logging
8-
open DotnetProjInfo.TestAssets
9-
open Ionide.ProjInfo
10-
open System.Collections.Generic
11-
open Ionide.ProjInfo.Types
12-
open Ionide.ProjInfo
136
open Expecto.Logging.Message
7+
open FileUtils
148
open FSharp.Compiler.CodeAnalysis
9+
open Ionide.ProjInfo
10+
open Ionide.ProjInfo
11+
open Ionide.ProjInfo.Types
12+
open Medallion.Shell
13+
open System
14+
open System.Collections.Generic
15+
open System.IO
16+
open System.Threading
1517

1618
#nowarn "25"
1719

@@ -727,10 +729,22 @@ let testProjectNotFound toolsPath workspaceLoader (workspaceFactory: ToolsPath -
727729

728730
Expect.equal (watcher.Notifications |> List.item 1) (WorkspaceProjectState.Failed(wrongPath, (GetProjectOptionsErrors.ProjectNotFound(wrongPath)))) "check error type")
729731

732+
let internalGetCSharpReferenceInfo =
733+
fun (r: FSharpReferencedProject) ->
734+
let rCase, fields =
735+
FSharp.Reflection.FSharpValue.GetUnionFields(r, typeof<FSharpReferencedProject>, System.Reflection.BindingFlags.Public ||| System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance)
736+
if rCase.Name = "PEReference" then
737+
let path: string = fields[0] :?> _
738+
let getStamp: unit -> DateTime = fields[1] :?> _
739+
let reader = fields[2]
740+
Some (path, getStamp, reader)
741+
else
742+
None
743+
730744
let internalGetProjectOptions =
731745
fun (r: FSharpReferencedProject) ->
732746
let rCase, fields =
733-
FSharp.Reflection.FSharpValue.GetUnionFields(r, typeof<FSharpReferencedProject>, System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance)
747+
FSharp.Reflection.FSharpValue.GetUnionFields(r, typeof<FSharpReferencedProject>, System.Reflection.BindingFlags.Public ||| System.Reflection.BindingFlags.NonPublic ||| System.Reflection.BindingFlags.Instance)
734748

735749
if rCase.Name = "FSharpReference" then
736750
let projOptions: FSharpProjectOptions = rCase.GetFields().[1].GetValue(box r) :?> _
@@ -826,7 +840,7 @@ let testFCSmapManyProj toolsPath workspaceLoader (workspaceFactory: ToolsPath ->
826840
copyDirFromAssets fs ``sample3 Netsdk projs``.ProjDir testDir
827841

828842
let projPath = testDir / (``sample3 Netsdk projs``.ProjectFile)
829-
843+
830844
// Build csproj, so it should be referenced as FSharpReferencedProject.PortableExecutable
831845
let csproj = testDir / ``sample3 Netsdk projs``.ProjectReferences.[0].ProjectFile
832846
dotnet fs [ "build"; csproj ] |> checkExitCodeZero
@@ -1175,6 +1189,27 @@ let expensiveTests toolsPath (workspaceFactory: ToolsPath -> IWorkspaceLoader) =
11751189
Expect.exists references (fun r -> r.Contains "packs" && r.Contains "Microsoft.Android.") "Should have found a reference to android dlls in the packs directory"
11761190
}
11771191

1192+
let csharpLibTest toolsPath (workspaceFactory: ToolsPath -> IWorkspaceLoader) =
1193+
testCase |> withLog "can load project that has a csharp project reference" (fun logger fs ->
1194+
let projPath = Path.Combine(__SOURCE_DIRECTORY__, "..", "examples", "sample-referenced-csharp-project", "fsharp-exe", "fsharp-exe.fsproj")
1195+
// need to build the projects first so that there's something to latch on to
1196+
dotnet fs ["build"; projPath] |> checkExitCodeZero
1197+
1198+
let loader = workspaceFactory toolsPath
1199+
let parsed = loader.LoadProjects [ projPath ] |> Seq.toList
1200+
Expect.hasLength parsed 2 "Should have loaded the F# exe and the C# lib"
1201+
let fsharpProject = parsed[0]
1202+
let mapped = FCS.mapToFSharpProjectOptions fsharpProject parsed
1203+
let referencedProjects = mapped.ReferencedProjects
1204+
Expect.hasLength referencedProjects 1 "Should have a reference to the C# lib"
1205+
match internalGetCSharpReferenceInfo referencedProjects[0] with
1206+
| Some (path, getStamp, reader) ->
1207+
let fileName = System.IO.Path.GetFileName path
1208+
Expect.equal fileName "csharp-lib.dll" "Should have found the C# lib"
1209+
| None ->
1210+
failwith "Should have found a C# reference"
1211+
)
1212+
11781213
let testProjectLoadBadData =
11791214
testCase |> withLog "Does not crash when loading malformed cache data" (fun logger fs ->
11801215
let testDir = inDir fs "sample_netsdk_bad_cache"
@@ -1285,4 +1320,5 @@ let tests toolsPath =
12851320
testLegacyFrameworkMultiProject toolsPath "can load legacy multi project file" false (fun (tools, props) -> WorkspaceLoader.Create(tools, globalProperties = props))
12861321
testProjectLoadBadData
12871322
expensiveTests toolsPath WorkspaceLoader.Create
1323+
csharpLibTest toolsPath WorkspaceLoader.Create
12881324
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace csharp_lib;
2+
public class Class1
3+
{
4+
public static string GetString()
5+
{
6+
return "Hello, World!";
7+
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<RootNamespace>csharp_lib</RootNamespace>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<Warn>$(Warn);7</Warn>
9+
</PropertyGroup>
10+
11+
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// For more information see https://aka.ms/fsharp-console-apps
2+
printfn $"{csharp_lib.Class1.GetString()}"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<RootNamespace>fsharp_exe</RootNamespace>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<Compile Include="Program.fs" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\csharp-lib\csharp-lib.csproj" />
15+
</ItemGroup>
16+
17+
</Project>

0 commit comments

Comments
 (0)