PageRenderTime 68ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/FSharpRefactor/Microsoft.FSharpCompiler.Sources/FSharpCompiler3/ReferenceResolution.fs

#
F# | 366 lines | 294 code | 41 blank | 31 comment | 41 complexity | aa948ea24986357384718f9b45e00f21 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0
  1. namespace Viz
  2. /// This type exists to have a concrete 'Target' type for a DebuggerVisualizerAttribute.
  3. /// Ideally it would be out in its own assembly, but then the compiler would need to take a dependency on that assembly, so instead we
  4. /// pragmatically just shove this into the compiler assembly itself.
  5. type internal Visualizable(o:obj) =
  6. member this.Data = o
  7. /// assuming this assembly is already in the debuggee process, then Viz.Visualiable.Make(foo) in the Watch window will make a visualizer for foo
  8. static member Make(o:obj) = new Visualizable(o)
  9. namespace Microsoft.FSharp.Compiler
  10. module internal MSBuildResolver =
  11. open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library
  12. exception ResolutionFailure
  13. type ResolvedFrom =
  14. | AssemblyFolders
  15. | AssemblyFoldersEx
  16. | TargetFrameworkDirectory
  17. | RawFileName
  18. | GlobalAssemblyCache
  19. | Path of string
  20. | Unknown
  21. type ResolutionEnvironment = CompileTimeLike | RuntimeLike | DesigntimeLike
  22. #if SILVERLIGHT
  23. #else
  24. open System
  25. open Microsoft.Build.Tasks
  26. open Microsoft.Build.Utilities
  27. open Microsoft.Build.Framework
  28. open Microsoft.Build.BuildEngine
  29. open System.IO
  30. type ResolvedFile = {
  31. itemSpec:string
  32. resolvedFrom:ResolvedFrom
  33. fusionName:string
  34. version:string
  35. redist:string
  36. baggage:string
  37. }
  38. with override this.ToString() = sprintf "ResolvedFile(%s)" this.itemSpec
  39. type ResolutionResults = {
  40. resolvedFiles:ResolvedFile array
  41. referenceDependencyPaths:string array
  42. relatedPaths:string array
  43. referenceSatellitePaths:string array
  44. referenceScatterPaths:string array
  45. referenceCopyLocalPaths:string array
  46. suggestedBindingRedirects:string array
  47. }
  48. let DotNetFrameworkReferenceAssembliesRootDirectory =
  49. // Note that ProgramFilesX86 is correct for both x86 and x64 architectures (the reference assemblies are always in the 32-bit location, which is PF(x86) on an x64 machine)
  50. let PF =
  51. //System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) // This API is not available to bootstrap compiler
  52. match System.Environment.GetEnvironmentVariable("ProgramFiles(x86)") with
  53. | null -> System.Environment.GetEnvironmentVariable("ProgramFiles") // if PFx86 is null, then we are 32-bit and just get PF
  54. | s -> s
  55. PF + @"\Reference Assemblies\Microsoft\Framework\.NETFramework"
  56. let ReplaceFrameworkVariables(dirs) =
  57. let windowsFramework = System.Environment.GetEnvironmentVariable("windir")+ @"\Microsoft.NET\Framework"
  58. let referenceAssemblies = DotNetFrameworkReferenceAssembliesRootDirectory
  59. dirs|>List.map(fun (d:string)->d.Replace("{WindowsFramework}",windowsFramework).Replace("{ReferenceAssemblies}",referenceAssemblies))
  60. /// Derive the target framework directories.
  61. let DeriveTargetFrameworkDirectories
  62. (targetFrameworkVersion:string, // e.g. v2.0, v3.0, v3.5, v4.0 etc
  63. excludeNonExecutableAssemblies:bool, // True when the assembly must be executable and not just a stub meta assembly.
  64. logmessage:string->unit) =
  65. let targetFrameworkVersion =
  66. if not(targetFrameworkVersion.StartsWith("v",StringComparison.Ordinal)) then "v"^targetFrameworkVersion
  67. else targetFrameworkVersion
  68. let FrameworkStartsWith(short) =
  69. targetFrameworkVersion.StartsWith(short,StringComparison.Ordinal)
  70. let result =
  71. if FrameworkStartsWith("v1.0") then ReplaceFrameworkVariables([@"{WindowsFramework}\v1.0.3705"])
  72. else if FrameworkStartsWith("v1.1") then ReplaceFrameworkVariables([@"{WindowsFramework}\v1.1.4322"])
  73. else if FrameworkStartsWith("v2.0") then ReplaceFrameworkVariables([@"{WindowsFramework}\v2.0.50727"])
  74. else if FrameworkStartsWith("v3.0") then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"])
  75. else if FrameworkStartsWith("v3.5") then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v3.5"; @"{WindowsFramework}\v3.5"; @"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"])
  76. else if FrameworkStartsWith("v4.0") then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v4.0"]) // starting with .Net 4.0, the runtime dirs (WindowsFramework) are never used by MSBuild RAR
  77. else if FrameworkStartsWith("v4.5") then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v4.5"])
  78. else (ignore(excludeNonExecutableAssemblies); [])
  79. let result = result |> Array.ofList
  80. logmessage (sprintf "Derived target framework directories for version %s are: %s" targetFrameworkVersion (String.Join(",", result)))
  81. result
  82. /// Decode the ResolvedFrom code from MSBuild.
  83. let DecodeResolvedFrom(resolvedFrom:string) : ResolvedFrom =
  84. let Same a b =
  85. String.CompareOrdinal(a,b) = 0
  86. match resolvedFrom with
  87. | r when Same "{RawFileName}" r -> RawFileName
  88. | r when Same "{GAC}" r -> GlobalAssemblyCache
  89. | r when Same "{TargetFrameworkDirectory}" r -> TargetFrameworkDirectory
  90. | r when Same "{AssemblyFolders}" r -> AssemblyFolders
  91. | r when r.Length >= 10 && Same "{Registry:" (r.Substring(0,10)) -> AssemblyFoldersEx
  92. | r -> ResolvedFrom.Path r
  93. type ErrorWarningCallbackSig = ((*code:*)string->(*message*)string->unit)
  94. type Foregrounded =
  95. | ForegroundedMessage of string
  96. | ForegroundedError of string * string
  97. | ForegroundedWarning of string * string
  98. let ResolveCore(
  99. resolutionEnvironment: ResolutionEnvironment,
  100. references:(string*(*baggage*)string)[],
  101. targetFrameworkVersion:string,
  102. targetFrameworkDirectories:string list,
  103. targetProcessorArchitecture:string,
  104. outputDirectory:string,
  105. fsharpCoreExplicitDirOrFSharpBinariesDir:string,
  106. explicitIncludeDirs:string list,
  107. implicitIncludeDir:string,
  108. frameworkRegistryBase:string,
  109. assemblyFoldersSuffix:string,
  110. assemblyFoldersConditions:string,
  111. allowRawFileName:bool,
  112. logmessage:string->unit,
  113. logwarning:ErrorWarningCallbackSig,
  114. logerror:ErrorWarningCallbackSig ) =
  115. // Message Foregrounding:
  116. // In version 4.0 MSBuild began calling log methods on a background (non-UI) thread. If there is an exception thrown from
  117. // logmessage, logwarning or logerror then it would kill the process.
  118. // The fix is to catch these exceptions and log the rest of the messages to a list to output at the end.
  119. // It looks simpler to always just accumulate the messages during resolution and show them all at the end, but then
  120. // we couldn't see the messages as resolution progresses.
  121. let foregrounded = ref []
  122. let backgroundException : exn option ref = ref None
  123. let logmessage message =
  124. match !backgroundException with
  125. | Some _ -> foregrounded := ForegroundedMessage(message) :: !foregrounded
  126. | None ->
  127. try
  128. logmessage message
  129. with e ->
  130. backgroundException := Some(e)
  131. foregrounded := ForegroundedMessage(message) :: !foregrounded
  132. let logwarning code message =
  133. match !backgroundException with
  134. | Some _ -> foregrounded := ForegroundedWarning(code,message) :: !foregrounded
  135. | None ->
  136. try
  137. logwarning code message
  138. with e ->
  139. backgroundException := Some(e)
  140. foregrounded := ForegroundedWarning(code,message) :: !foregrounded
  141. let logerror code message =
  142. match !backgroundException with
  143. | Some _ -> foregrounded := ForegroundedError(code,message) :: !foregrounded
  144. | None ->
  145. try
  146. logerror code message
  147. with e ->
  148. backgroundException := Some(e)
  149. foregrounded := ForegroundedError(code,message) :: !foregrounded
  150. let engine = { new IBuildEngine with
  151. member be.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs) = true
  152. member be.LogCustomEvent(e) = logmessage e.Message
  153. member be.LogErrorEvent(e) = logerror e.Code e.Message
  154. member be.LogMessageEvent(e) = logmessage e.Message
  155. member be.LogWarningEvent(e) = logwarning e.Code e.Message
  156. member be.ColumnNumberOfTaskNode with get() = 1
  157. member be.LineNumberOfTaskNode with get() = 1
  158. member be.ContinueOnError with get() = true
  159. member be.ProjectFileOfTaskNode with get() = "" }
  160. let rar = new ResolveAssemblyReference()
  161. rar.BuildEngine <- engine
  162. // Derive target framework directory if none was supplied.
  163. let excludeNonExecutableAssemblies = (resolutionEnvironment = RuntimeLike)
  164. let targetFrameworkDirectories =
  165. if targetFrameworkDirectories=[] then DeriveTargetFrameworkDirectories(targetFrameworkVersion,excludeNonExecutableAssemblies,logmessage)
  166. else targetFrameworkDirectories |> Array.ofList
  167. // Filter for null and zero length, and escape backslashes so legitimate path characters aren't mistaken for
  168. // escape characters (E.g., ".\r.dll")
  169. let explicitIncludeDirs = explicitIncludeDirs |> List.filter(fun eid->not(String.IsNullOrEmpty(eid)))
  170. let references = references |> Array.filter(fun (path,_)->not(String.IsNullOrEmpty(path))) // |> Array.map (fun (path,baggage) -> (path.Replace("\\","\\\\"),baggage))
  171. rar.TargetFrameworkDirectories <- targetFrameworkDirectories
  172. rar.FindRelatedFiles <- false
  173. rar.FindDependencies <- false
  174. rar.FindSatellites <- false
  175. rar.FindSerializationAssemblies <- false
  176. #if BUILDING_WITH_LKG
  177. ignore targetProcessorArchitecture
  178. #else
  179. #if MONO
  180. #else
  181. rar.TargetedRuntimeVersion <- typeof<obj>.Assembly.ImageRuntimeVersion
  182. rar.CopyLocalDependenciesWhenParentReferenceInGac <- true
  183. #endif
  184. #endif
  185. rar.TargetProcessorArchitecture <- targetProcessorArchitecture
  186. rar.Assemblies <- [|for (referenceName,baggage) in references ->
  187. let item = new Microsoft.Build.Utilities.TaskItem(referenceName)
  188. item.SetMetadata("Baggage", baggage)
  189. item:>ITaskItem|]
  190. let rawFileNamePath = if allowRawFileName then ["{RawFileName}"] else []
  191. let searchPaths =
  192. match resolutionEnvironment with
  193. | DesigntimeLike
  194. | RuntimeLike ->
  195. logmessage("Using scripting resolution precedence.")
  196. // These are search paths for runtime-like or scripting resolution. GAC searching is present.
  197. rawFileNamePath @ // Quick-resolve straight to filename first
  198. explicitIncludeDirs @ // From -I, #I
  199. [implicitIncludeDir] @ // Usually the project directory
  200. [fsharpCoreExplicitDirOrFSharpBinariesDir] @ // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe
  201. ["{TargetFrameworkDirectory}"] @
  202. [sprintf "{Registry:%s,%s,%s%s}" frameworkRegistryBase targetFrameworkVersion assemblyFoldersSuffix assemblyFoldersConditions] @
  203. ["{AssemblyFolders}"] @
  204. ["{GAC}"]
  205. | CompileTimeLike ->
  206. logmessage("Using compilation resolution precedence.")
  207. // These are search paths for compile-like resolution. GAC searching is not present.
  208. ["{TargetFrameworkDirectory}"] @
  209. rawFileNamePath @ // Quick-resolve straight to filename first
  210. explicitIncludeDirs @ // From -I, #I
  211. [implicitIncludeDir] @ // Usually the project directory
  212. [fsharpCoreExplicitDirOrFSharpBinariesDir] @ // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe
  213. [sprintf "{Registry:%s,%s,%s%s}" frameworkRegistryBase targetFrameworkVersion assemblyFoldersSuffix assemblyFoldersConditions] @ // Like {Registry:Software\Microsoft\.NETFramework,v2.0,AssemblyFoldersEx}
  214. ["{AssemblyFolders}"] @
  215. [outputDirectory] @
  216. ["{GAC}"]
  217. rar.SearchPaths <- searchPaths |> Array.ofList
  218. rar.AllowedAssemblyExtensions <- [| ".dll" ; ".exe" |]
  219. let succeeded = rar.Execute()
  220. // Unroll any foregrounded messages
  221. match !backgroundException with
  222. | Some(backGroundException) ->
  223. logwarning "" "Saw error on logger thread during resolution."
  224. logwarning "" (sprintf "%A" backGroundException)
  225. logwarning "" "Showing messages seen after exception."
  226. !foregrounded
  227. |> List.iter(fun message->
  228. match message with
  229. | ForegroundedMessage(message) -> logmessage message
  230. | ForegroundedWarning(code,message) -> logwarning code message
  231. | ForegroundedError(code,message) -> logerror code message )
  232. | None -> ()
  233. if not succeeded then
  234. raise ResolutionFailure
  235. {
  236. resolvedFiles = [| for p in rar.ResolvedFiles -> {itemSpec = p.ItemSpec;
  237. resolvedFrom = DecodeResolvedFrom(p.GetMetadata("ResolvedFrom"));
  238. fusionName = p.GetMetadata("FusionName");
  239. version = p.GetMetadata("Version");
  240. redist = p.GetMetadata("Redist");
  241. baggage = p.GetMetadata("Baggage") } |]
  242. referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |]
  243. relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |]
  244. referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |]
  245. referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |]
  246. referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |]
  247. suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |]
  248. }
  249. let Resolve(
  250. resolutionEnvironment: ResolutionEnvironment,
  251. references:(string*(*baggage*)string)[],
  252. targetFrameworkVersion:string,
  253. targetFrameworkDirectories:string list,
  254. targetProcessorArchitecture:string,
  255. outputDirectory:string,
  256. fsharpCoreExplicitDirOrFSharpBinariesDir:string,
  257. explicitIncludeDirs:string list,
  258. implicitIncludeDir:string,
  259. frameworkRegistryBase:string,
  260. assemblyFoldersSuffix:string,
  261. assemblyFoldersConditions:string,
  262. logmessage:string->unit,
  263. logwarning:ErrorWarningCallbackSig,
  264. logerror:ErrorWarningCallbackSig ) =
  265. // The {RawFileName} target is 'dangerous', in the sense that is uses Directory.GetCurrentDirectory() to resolve unrooted file paths.
  266. // It is unreliable to use this mutable global state inside Visual Studio. As a result, we partition all references into a "rooted" set
  267. // (which contains e.g. C:\MyDir\MyAssem.dll) and "unrooted" (everything else). We only allow "rooted" to use {RawFileName}. Note that
  268. // unrooted may still find 'local' assemblies by virtue of the fact that "implicitIncludeDir" is one of the places searched during
  269. // assembly resolution.
  270. let references = references |> Array.map (fun ((file,baggage) as data) ->
  271. // However, MSBuild will not resolve 'relative' paths, even when e.g. implicitIncludeDir is part of the search. As a result,
  272. // if we have an unrooted path+filename, we'll assume this is relative to the project directory and root it.
  273. if FileSystem.IsPathRootedShim(file) then
  274. data // fine, e.g. "C:\Dir\foo.dll"
  275. elif not(file.Contains("\\") || file.Contains("/")) then
  276. data // fine, e.g. "System.Transactions.dll"
  277. else
  278. // we have a 'relative path', e.g. "bin/Debug/foo.exe" or "..\Yadda\bar.dll"
  279. // turn it into an absolute path based at implicitIncludeDir
  280. (System.IO.Path.Combine(implicitIncludeDir, file), baggage)
  281. )
  282. let rooted, unrooted = references |> Array.partition (fun (file,_baggage) -> FileSystem.IsPathRootedShim(file))
  283. let CallResolveCore(references, allowRawFileName) =
  284. if Array.isEmpty references then
  285. {
  286. resolvedFiles = [| |]
  287. referenceDependencyPaths = [| |]
  288. relatedPaths = [| |]
  289. referenceSatellitePaths = [| |]
  290. referenceScatterPaths = [| |]
  291. referenceCopyLocalPaths = [| |]
  292. suggestedBindingRedirects = [| |]
  293. }
  294. else
  295. // all the params are the same...
  296. ResolveCore(
  297. resolutionEnvironment,
  298. references, // ... except this
  299. targetFrameworkVersion,
  300. targetFrameworkDirectories,
  301. targetProcessorArchitecture,
  302. outputDirectory,
  303. fsharpCoreExplicitDirOrFSharpBinariesDir,
  304. explicitIncludeDirs,
  305. implicitIncludeDir,
  306. frameworkRegistryBase,
  307. assemblyFoldersSuffix,
  308. assemblyFoldersConditions,
  309. allowRawFileName, // ... and this
  310. logmessage,
  311. logwarning,
  312. logerror)
  313. let rootedResults = CallResolveCore(rooted, true)
  314. let unrootedResults = CallResolveCore(unrooted, false)
  315. // now unify the two sets of results
  316. {
  317. resolvedFiles = Array.concat [| rootedResults.resolvedFiles; unrootedResults.resolvedFiles |]
  318. referenceDependencyPaths = set rootedResults.referenceDependencyPaths |> Set.union (set unrootedResults.referenceDependencyPaths) |> Set.toArray
  319. relatedPaths = set rootedResults.relatedPaths |> Set.union (set unrootedResults.relatedPaths) |> Set.toArray
  320. referenceSatellitePaths = set rootedResults.referenceSatellitePaths |> Set.union (set unrootedResults.referenceSatellitePaths) |> Set.toArray
  321. referenceScatterPaths = set rootedResults.referenceScatterPaths |> Set.union (set unrootedResults.referenceScatterPaths) |> Set.toArray
  322. referenceCopyLocalPaths = set rootedResults.referenceCopyLocalPaths |> Set.union (set unrootedResults.referenceCopyLocalPaths) |> Set.toArray
  323. suggestedBindingRedirects = set rootedResults.suggestedBindingRedirects |> Set.union (set unrootedResults.suggestedBindingRedirects) |> Set.toArray
  324. }
  325. #endif