Skip to content

Commit

Permalink
default resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
dsyme committed Oct 13, 2016
1 parent 9e4727a commit 0bf1ac4
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 66 deletions.
202 changes: 150 additions & 52 deletions src/fsharp/ReferenceResolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,13 @@ type Resolver =
logerror:(string->string->unit)
-> ResolvedFile[]

let ScriptingNaiveResolver =
let SimulatedMSBuildResolver =
{ new Resolver with
member __.HighestInstalledNetFrameworkVersion() = "v4.5"
member __.DotNetFrameworkReferenceAssembliesRootDirectory =
#if RESHAPED_MSBUILD
""
#else
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then
let PF =
match Environment.GetEnvironmentVariable("ProgramFiles(x86)") with
Expand All @@ -77,10 +80,12 @@ let ScriptingNaiveResolver =
PF + @"\Reference Assemblies\Microsoft\Framework\.NETFramework"
else
""
#endif

member __.Resolve(resolutionEnvironment, references, targetFrameworkVersion, targetFrameworkDirectories, targetProcessorArchitecture,
outputDirectory, fsharpCoreDir, explicitIncludeDirs, implicitIncludeDir, logMessage, logWarning, logError) =

#if !RESHAPED_MSBUILD
let registrySearchPaths() =
[ let registryKey = @"Software\Microsoft\.NetFramework";
use key = Registry.LocalMachine.OpenSubKey(registryKey)
Expand All @@ -107,79 +112,126 @@ let ScriptingNaiveResolver =
match subSubSubKey.GetValue(null) with
| :? string as s -> yield s
| _ -> () ]
#endif


let results = ResizeArray()
let searchPaths =
[ yield! targetFrameworkDirectories
yield! explicitIncludeDirs
yield fsharpCoreDir
yield implicitIncludeDir
#if !RESHAPED_MSBUILD
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then
yield! registrySearchPaths() ]
yield! registrySearchPaths()
#endif
]

[| for (baggage,r) in references do
for (r, baggage) in references do
//printfn "resolving %s" r
let mutable found = false
let success path =
if not found then
//printfn "resolved %s --> %s" r path
found <- true
[ { itemSpec = path; prepareToolTip = snd; baggage=baggage } ]
else []
if Path.IsPathRooted(r) then
if FileSystem.SafeExists(r) then
yield! success r
else
let isFileName =
r.EndsWith("dll",StringComparison.InvariantCultureIgnoreCase) ||
r.EndsWith("exe",StringComparison.InvariantCultureIgnoreCase)

let qual = if isFileName then r else try AssemblyName(r).Name + ".dll" with _ -> r + ".dll"

for searchPath in searchPaths do
if not found then
let trialPath = Path.Combine(searchPath,qual)
if FileSystem.SafeExists(trialPath) then
yield! success trialPath
if not found then
let ass = try Some (System.Reflection.Assembly.ReflectionOnlyLoad(r)) with _ -> None
match ass with
| None -> ()
| Some ass -> yield! success ass.Location |] }
results.Add { itemSpec = path; prepareToolTip = snd; baggage=baggage }

#if INTERACTIVE
ScriptingNaiveResolver.DotNetFrameworkReferenceAssembliesRootDirectory
ScriptingNaiveResolver.HighestInstalledNetFrameworkVersion()
try
if not found && Path.IsPathRooted(r) then
if FileSystem.SafeExists(r) then
success r
with e -> logWarning "SR001" (e.ToString())

let fscoreDir =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
let PF =
match Environment.GetEnvironmentVariable("ProgramFiles(x86)") with
| null -> Environment.GetEnvironmentVariable("ProgramFiles") // if PFx86 is null, then we are 32-bit and just get PF
| s -> s
PF + @"\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0"
else
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
#if !RESHAPED_MSBUILD
// For this one we need to get the version search exactly right, without doing a load
try
if not found && r.StartsWith("FSharp.Core, Version=") && Environment.OSVersion.Platform = PlatformID.Win32NT then
let n = AssemblyName(r)
let fscoreDir0 =
let PF =
match Environment.GetEnvironmentVariable("ProgramFiles(x86)") with
| null -> Environment.GetEnvironmentVariable("ProgramFiles")
| s -> s
PF + @"\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\" + n.Version.ToString()
let trialPath = Path.Combine(fscoreDir0,n.Name + ".dll")
if FileSystem.SafeExists(trialPath) then
success trialPath
with e -> logWarning "SR001" (e.ToString())
#endif

let resolve s =
ScriptingNaiveResolver.Resolve(ResolutionEnvironment.CompileTimeLike,[| for a in s -> ("", a) |],"v4.5.1", [ScriptingNaiveResolver.DotNetFrameworkReferenceAssembliesRootDirectory + @"\v4.5.1" ],"", "", fscoreDir,[],__SOURCE_DIRECTORY__,ignore, (fun _ _ -> ()), (fun _ _-> ()))
let isFileName =
r.EndsWith("dll",StringComparison.OrdinalIgnoreCase) ||
r.EndsWith("exe",StringComparison.OrdinalIgnoreCase)

resolve ["System"; "mscorlib"; "mscorlib.dll"; "FSharp.Core"; "FSharp.Core.dll"; "Microsoft.SqlServer.Dmf.dll"; "Microsoft.SqlServer.Dmf" ]
let qual = if isFileName then r else try AssemblyName(r).Name + ".dll" with _ -> r + ".dll"

resolve [ "FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" ]
for searchPath in searchPaths do
try
if not found then
let trialPath = Path.Combine(searchPath,qual)
if FileSystem.SafeExists(trialPath) then
success trialPath
with e -> logWarning "SR001" (e.ToString())

#if !RESHAPED_MSBUILD
try
// Seach the GAC on Windows
if not found && not isFileName && Environment.OSVersion.Platform = PlatformID.Win32NT then
let n = AssemblyName(r)
let netfx = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
let gac = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(netfx.TrimEnd('\\'))),"assembly")
match n.Version, n.GetPublicKeyToken() with
| null, _ | _,null ->
let options =
[ for gacdir in Directory.EnumerateDirectories(gac) do
let assdir = Path.Combine(gacdir,n.Name)
if Directory.Exists(assdir) then
for tdir in Directory.EnumerateDirectories(assdir) do
let trialPath = Path.Combine(tdir,qual)
if FileSystem.SafeExists(trialPath) then
yield trialPath ]
//printfn "sorting GAC paths: %A" options
options
|> List.sort // puts latest version last
|> List.tryLast
|> function None -> () | Some p -> success p

| v,tok ->
for gacdir in Directory.EnumerateDirectories(gac) do
//printfn "searching GAC directory: %s" gacdir
let assdir = Path.Combine(gacdir,n.Name)
if Directory.Exists(assdir) then
//printfn "searching GAC directory: %s" assdir

let tokText = String.concat "" [| for b in tok -> sprintf "%02x" b |]
let verdir = Path.Combine(assdir,"v4.0_"+v.ToString()+"__"+tokText)
//printfn "searching GAC directory: %s" verdir

if Directory.Exists(verdir) then
let trialPath = Path.Combine(verdir,qual)
//printfn "searching GAC: %s" trialPath
if FileSystem.SafeExists(trialPath) then
success trialPath
with e -> logWarning "SR001" (e.ToString())
#endif

results.ToArray() }

let GetDefaultResolver(msbuildEnabled: bool, msbuildVersion: string option) =
//let msbuildEnabled = msbuildEnabled && false
#if RESHAPED_MSBUILD
ignore msbuildVersion
ignore msbuildEnabled
#else
let msbuildEnabled = msbuildEnabled && false
let msbuildVersion = defaultArg msbuildVersion "12"
let tryMSBuild v =
if msbuildEnabled then
// Detect if MSBuild v12 is on the machine, if so use the resolver from there
let mb = try System.Reflection.Assembly.Load(sprintf "Microsoft.Build.Framework, Version=%s.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" v) |> Option.ofObj with _ -> None
let ass = mb |> Option.bind (fun _ -> try System.Reflection.Assembly.Load(sprintf "FSharp.Compiler.Service.MSBuild.v%s" v) |> Option.ofObj with _ -> None)
// Detect if MSBuild is on the machine, if so use the resolver from there
let mb = try Assembly.Load(sprintf "Microsoft.Build.Framework, Version=%s.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" v) |> Option.ofObj with _ -> None
let ass = mb |> Option.bind (fun _ -> try Assembly.Load(sprintf "FSharp.Compiler.Service.MSBuild.v%s" v) |> Option.ofObj with _ -> None)
let ty = ass |> Option.bind (fun ass -> ass.GetType("Microsoft.FSharp.Compiler.MSBuildReferenceResolver") |> Option.ofObj)
let obj = ty |> Option.bind (fun ty -> ty.InvokeMember("get_Resolver",System.Reflection.BindingFlags.Static ||| System.Reflection.BindingFlags.Public ||| System.Reflection.BindingFlags.InvokeMethod ||| System.Reflection.BindingFlags.NonPublic, null, null, [| |]) |> Option.ofObj)
let obj = ty |> Option.bind (fun ty -> ty.InvokeMember("get_Resolver",BindingFlags.Static ||| BindingFlags.Public ||| BindingFlags.InvokeMethod ||| BindingFlags.NonPublic, null, null, [| |]) |> Option.ofObj)
let resolver = obj |> Option.bind (fun obj -> match obj with :? Resolver as r -> Some r | _ -> None)
resolver
else None
match tryMSBuild (defaultArg msbuildVersion "12") with
match (if msbuildEnabled then tryMSBuild msbuildVersion else None) with
| Some r -> r
| None ->
//match tryMSBuild "15" with
Expand All @@ -188,7 +240,53 @@ let GetDefaultResolver(msbuildEnabled: bool, msbuildVersion: string option) =
//match tryMSBuild "14" with
//| Some r -> r
//| None ->
match tryMSBuild "12" with
match (if msbuildEnabled && msbuildVersion <> "12" then tryMSBuild "12" else None) with
| Some r -> r
| None ->
ScriptingNaiveResolver
#endif
SimulatedMSBuildResolver


#if INTERACTIVE
// Some manual testing
SimulatedMSBuildResolver.DotNetFrameworkReferenceAssembliesRootDirectory
SimulatedMSBuildResolver.HighestInstalledNetFrameworkVersion()

let fscoreDir =
if System.Environment.OSVersion.Platform = System.PlatformID.Win32NT then // file references only valid on Windows
let PF =
match Environment.GetEnvironmentVariable("ProgramFiles(x86)") with
| null -> Environment.GetEnvironmentVariable("ProgramFiles") // if PFx86 is null, then we are 32-bit and just get PF
| s -> s
PF + @"\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0"
else
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()

let resolve s =
SimulatedMSBuildResolver.Resolve(ResolutionEnvironment.CompileTimeLike,[| for a in s -> (a, "") |],"v4.5.1", [SimulatedMSBuildResolver.DotNetFrameworkReferenceAssembliesRootDirectory + @"\v4.5.1" ],"", "", fscoreDir,[],__SOURCE_DIRECTORY__,ignore, (fun _ _ -> ()), (fun _ _-> ()))

// Resolve partial name to something on search path
resolve ["FSharp.Core" ]

// Resolve DLL name to something on search path
resolve ["FSharp.Core.dll" ]

// Resolve from reference assemblies
resolve ["System"; "mscorlib"; "mscorlib.dll" ]

// Resolve from Registry AssemblyFolders
resolve ["Microsoft.SqlServer.Dmf.dll"; "Microsoft.SqlServer.Dmf" ]

// Resolve exact version of FSharp.Core
resolve [ "FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" ]

// Resolve from GAC:
resolve [ "EventViewer, Version=6.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ]

// Resolve from GAC:
resolve [ "EventViewer" ]

resolve [ "Microsoft.SharePoint.Client, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" ]
resolve [ "Microsoft.SharePoint.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" ]
#endif

5 changes: 0 additions & 5 deletions tests/service/FSharp.Compiler.Service.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@
<Project>{887630a3-4b1d-40ea-b8b3-2d842e9c40db}</Project>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="data\TestTP\TestTP.fsproj">
<Name>TestTP</Name>
<Project>{ff76bd3c-5e0a-4752-b6c3-044f6e15719b}</Project>
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<ItemGroup>
Expand Down
9 changes: 0 additions & 9 deletions tests/service/ProjectAnalysisTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4510,15 +4510,6 @@ module Project35b =
let options = checker.GetProjectOptionsFromScript(fileName1, fileSource1) |> Async.RunSynchronously
#endif

[<Test>]
let ``Test project35b Dependency files for ParseFileInProject`` () =
// This is testing legacy functionality
let parseFileResults = checker.ParseFileInProject(Project35b.fileName1, Project35b.fileSource1, Project35b.options) |> Async.RunSynchronously
for d in parseFileResults.DependencyFiles do
printfn "ParseFileInProject dependency: %s" d
parseFileResults.DependencyFiles |> List.exists (fun s -> s.Contains "notexist.dll") |> shouldEqual true
parseFileResults.DependencyFiles |> List.exists (fun s -> s.Contains Project35b.fileName1) |> shouldEqual true

[<Test>]
let ``Test project35b Dependency files for ParseAndCheckFileInProject`` () =
let checkFileResults =
Expand Down

0 comments on commit 0bf1ac4

Please sign in to comment.