Skip to content

Commit c666329

Browse files
KevinRansombaronfel
authored andcommitted
Ref assemblies for non sdk apps (#7725)
* Ref assemblies for non sdk apps * Use NumberStyles.AllowDecimalPoint with Parse * Correctly this time * Improved Assembly search
1 parent cf9ec3e commit c666329

File tree

2 files changed

+67
-101
lines changed

2 files changed

+67
-101
lines changed

src/fsharp/CompileOps.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2784,7 +2784,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) =
27842784
if Directory.Exists runtimeRootWPF then
27852785
yield runtimeRootWPF // PresentationCore.dll is in C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
27862786

2787-
match getFrameworkRefsPackDirectory with
2787+
match frameworkRefsPackDirectory with
27882788
| Some path when Directory.Exists(path) ->
27892789
yield path
27902790
| _ -> ()

src/fsharp/DotNetFrameworkDependencies.fs

Lines changed: 66 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ module internal FSharp.Compiler.DotNetFrameworkDependencies
77
open System
88
open System.Collections.Generic
99
open System.Diagnostics
10+
open System.Globalization
1011
open System.IO
1112
open System.Reflection
1213

1314
type private TypeInThisAssembly = class end
1415

1516
let getFSharpCoreLibraryName = "FSharp.Core"
1617
let getFsiLibraryName = "FSharp.Compiler.Interactive.Settings"
17-
let frameworkDir = Path.GetDirectoryName(typeof<Object>.Assembly.Location)
18+
let implementationAssemblyDir = Path.GetDirectoryName(typeof<obj>.Assembly.Location)
1819
let getDefaultFSharpCoreReference = typeof<Microsoft.FSharp.Core.Unit>.Assembly.Location
1920
let getFSharpCompilerLocation = Path.GetDirectoryName(typeof<TypeInThisAssembly>.Assembly.Location)
2021

@@ -34,120 +35,85 @@ module internal FSharp.Compiler.DotNetFrameworkDependencies
3435
None
3536
with _ -> None
3637

37-
// Compare nuget version strings
38+
39+
// Algorithm:
40+
// use implementation location of obj type, on shared frameworks it will always be in:
3841
//
39-
// Format:
40-
// =======
41-
// $(Major).$(Minor).$(Build) [-SomeSuffix]
42-
// Major, Minor, Build collates normally
43-
// Strings without -SomeSuffix collate higher than SomeSuffix,
44-
// SomeSuffix collates using normal alphanumeric rules
42+
// dotnet\shared\Microsoft.NETCore.App\sdk-version\System.Private.CoreLib.dll
4543
//
46-
let deconstructVersion (version:string) =
47-
let version, suffix =
48-
let pos = version.IndexOf("-")
49-
if pos >= 0 then
50-
version.Substring(0, pos), version.Substring(pos + 1)
51-
else version, ""
52-
53-
let elements = version.Split('.')
54-
if elements.Length < 3 then
55-
struct (0, 0, 0, suffix)
56-
else
57-
struct (Int32.Parse(elements.[0]), Int32.Parse(elements.[1]), Int32.Parse(elements.[2]), suffix)
58-
59-
let versionCompare c1 c2 =
60-
if c1 = c2 then 0
61-
else
62-
try
63-
let struct (major1, minor1, build1, suffix1 ) = deconstructVersion c1
64-
let struct (major2, minor2, build2, suffix2 ) = deconstructVersion c2
65-
let v = major1 - major2
66-
if v <> 0 then v
67-
else
68-
let v = minor1 - minor2
69-
if v <> 0 then v
70-
else
71-
let v = build1 - build2
72-
if v <> 0 then v
73-
else
74-
match String.IsNullOrEmpty(suffix1), String.IsNullOrEmpty(suffix2) with
75-
| true, true -> 0
76-
| true, false -> 1
77-
| false, true -> -1
78-
| false, false -> String.Compare(suffix1, suffix2, StringComparison.InvariantCultureIgnoreCase)
79-
with _ -> 0
80-
81-
let executionTfm =
82-
let file =
83-
try
84-
let depsJsonPath = Path.ChangeExtension(Assembly.GetEntryAssembly().Location, "deps.json")
85-
if File.Exists(depsJsonPath) then
86-
File.ReadAllText(depsJsonPath)
87-
else
88-
""
89-
with _ -> ""
90-
91-
let tfmPrefix=".NETCoreApp,Version=v"
92-
let pattern = "\"name\": \"" + tfmPrefix
93-
let startPos =
94-
let startPos = file.IndexOf(pattern, StringComparison.OrdinalIgnoreCase)
95-
if startPos >= 0 then startPos + (pattern.Length) else startPos
96-
97-
let length =
98-
if startPos >= 0 then
99-
let ep = file.IndexOf("\"", startPos)
100-
if ep >= 0 then ep - startPos else ep
101-
else -1
102-
match startPos, length with
103-
| -1, _
104-
| _, -1 -> None
105-
| pos, length -> Some ("netcoreapp" + file.Substring(pos, length))
106-
107-
108-
let getFrameworkRefsPackDirectoryPath =
109-
match executionTfm with
110-
| Some _ ->
111-
let appRefDir = Path.Combine(getFSharpCompilerLocation, "../../../packs/Microsoft.NETCore.App.Ref")
112-
if Directory.Exists(appRefDir) then
113-
Some appRefDir
44+
// if that changes we will need to find another way to do this. Hopefully the sdk will eventually provide an API
45+
// use the well know location for obj to traverse the file system towards the
46+
//
47+
// packs\Microsoft.NETCore.App.Ref\sdk-version\netcoreappn.n
48+
// we will rely on the sdk-version match on the two paths to ensure that we get the product that ships with the
49+
// version of the runtime we are executing on
50+
// Use the reference assemblies for the highest netcoreapp tfm that we find in that location.
51+
let version, frameworkRefsPackDirectoryRoot =
52+
try
53+
let version = DirectoryInfo(implementationAssemblyDir).Name
54+
let microsoftNETCoreAppRef = Path.Combine(implementationAssemblyDir, "../../../packs/Microsoft.NETCore.App.Ref")
55+
if Directory.Exists(microsoftNETCoreAppRef) then
56+
Some version, Some microsoftNETCoreAppRef
11457
else
115-
None
116-
| _ -> None
58+
Some version, None
59+
with | _ -> None, None
11760

11861
let isInReferenceAssemblyPackDirectory filename =
119-
match getFrameworkRefsPackDirectoryPath with
120-
| Some appRefDir ->
62+
match frameworkRefsPackDirectoryRoot with
63+
| Some root ->
12164
let path = Path.GetDirectoryName(filename)
122-
path.StartsWith(appRefDir, StringComparison.OrdinalIgnoreCase)
65+
path.StartsWith(root, StringComparison.OrdinalIgnoreCase)
12366
| _ -> false
12467

125-
let getFrameworkRefsPackDirectory =
126-
match executionTfm, getFrameworkRefsPackDirectoryPath with
127-
| Some tfm, Some appRefDir ->
68+
let frameworkRefsPackDirectory =
69+
let tfmPrefix = "netcoreapp"
70+
let tfmCompare c1 c2 =
71+
let deconstructTfmApp (netcoreApp: DirectoryInfo) =
72+
let name = netcoreApp.Name
73+
try
74+
if name.StartsWith(tfmPrefix, StringComparison.InvariantCultureIgnoreCase) then
75+
Some (Double.Parse(name.Substring(tfmPrefix.Length), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture))
76+
else
77+
None
78+
with _ -> None
79+
80+
if c1 = c2 then 0
81+
else
82+
match (deconstructTfmApp c1), (deconstructTfmApp c2) with
83+
| Some c1, Some c2 -> int(c1 - c2)
84+
| None, Some _ -> -1
85+
| Some _, None -> 1
86+
| _ -> 0
87+
88+
match version, frameworkRefsPackDirectoryRoot with
89+
| Some version, Some root ->
12890
try
129-
let refDirs = Directory.GetDirectories(appRefDir)
130-
let versionPath = refDirs |> Array.sortWith (versionCompare) |> Array.last
131-
Some(Path.Combine(versionPath, "ref", tfm))
91+
let ref = Path.Combine(root, version, "ref")
92+
let highestTfm = DirectoryInfo(ref).GetDirectories()
93+
|> Array.sortWith tfmCompare
94+
|> Array.tryLast
95+
96+
match highestTfm with
97+
| Some tfm -> Some (Path.Combine(ref, tfm.Name))
98+
| None -> None
13299
with | _ -> None
133100
| _ -> None
134101

135-
136-
137102
let getDependenciesOf assemblyReferences =
138103
let assemblies = new Dictionary<string, string>()
139104

140105
// Identify path to a dll in the framework directory from a simple name
141106
let frameworkPathFromSimpleName simpleName =
142-
let pathDll = Path.Combine(frameworkDir, simpleName + ".dll")
143-
if not (File.Exists(pathDll)) then
144-
let pathExe = Path.Combine(frameworkDir, simpleName + ".exe")
145-
if not (File.Exists(pathExe)) then
146-
pathDll
147-
else
148-
pathExe
149-
else
150-
pathDll
107+
let root = Path.Combine(implementationAssemblyDir, simpleName)
108+
let pathOpt =
109+
[| ""; ".dll"; ".exe" |]
110+
|> Seq.tryPick(fun ext ->
111+
let path = root + ext
112+
if File.Exists(path) then Some path
113+
else None)
114+
match pathOpt with
115+
| Some path -> path
116+
| None -> root
151117

152118
// Collect all assembly dependencies into assemblies dictionary
153119
let rec traverseDependencies reference =
@@ -228,14 +194,14 @@ module internal FSharp.Compiler.DotNetFrameworkDependencies
228194
let getImplementationReferences () =
229195
// Coreclr supports netstandard assemblies only for now
230196
(getDependenciesOf [
231-
yield Path.Combine(frameworkDir, "netstandard.dll")
197+
yield Path.Combine(implementationAssemblyDir, "netstandard.dll")
232198
yield getDefaultFSharpCoreReference
233199
if useFsiAuxLib then yield getFsiLibraryName
234200
]).Values |> Seq.toList
235201

236202
if useSdkRefs then
237203
// Go fetch references
238-
match getFrameworkRefsPackDirectory with
204+
match frameworkRefsPackDirectory with
239205
| Some path ->
240206
try [ yield! Directory.GetFiles(path, "*.dll")
241207
yield getDefaultFSharpCoreReference

0 commit comments

Comments
 (0)