diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 1f007f087fa9d9502ea795e3885cf8808361f0e5..99ea401e774f8a16f6fe2c09abae25e6542b3672 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -2634,7 +2634,7 @@ type TcConfig private (data : TcConfigBuilder,validate:bool) = | MSBuildResolver.RuntimeLike -> [System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()] | _ -> - let frameworkRoot = MSBuildResolver.DotNetFrameworkReferenceAssembliesRootDirectory + let frameworkRoot = MSBuildResolver.DotNetFrameworkReferenceAssembliesRootDirectoryOnWindows let frameworkRootVersion = Path.Combine(frameworkRoot,tcConfig.targetFrameworkVersionMajorMinor) [frameworkRootVersion] with e -> diff --git a/src/fsharp/ReferenceResolution.fs b/src/fsharp/ReferenceResolution.fs index f0346c99d3e341a3419fab384ac138c8e5147271..e8c76b946448b6f335677a74800382e5d950c498 100644 --- a/src/fsharp/ReferenceResolution.fs +++ b/src/fsharp/ReferenceResolution.fs @@ -1,22 +1,20 @@ // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Viz - -/// This type exists to have a concrete 'Target' type for a DebuggerVisualizerAttribute. -/// 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 -/// pragmatically just shove this into the compiler assembly itself. -type internal Visualizable(o:obj) = - member this.Data = o - /// assuming this assembly is already in the debuggee process, then Viz.Visualiable.Make(foo) in the Watch window will make a visualizer for foo - static member Make(o:obj) = new Visualizable(o) - namespace Microsoft.FSharp.Compiler module internal MSBuildResolver = + open System + open System.IO + open Microsoft.Build.Tasks + open Microsoft.Build.Utilities + open Microsoft.Build.Framework + open Microsoft.Build.BuildEngine open Microsoft.FSharp.Compiler.AbstractIL.Internal.Library + exception ResolutionFailure + /// Describes the location where the reference was found. type ResolvedFrom = | AssemblyFolders | AssemblyFoldersEx @@ -26,77 +24,111 @@ module internal MSBuildResolver = | Path of string | Unknown - type ResolutionEnvironment = CompileTimeLike | RuntimeLike | DesigntimeLike + /// Indicates whether the resolve should follow compile-time rules or runtime rules. + type ResolutionEnvironment = + | CompileTimeLike + | RuntimeLike + | DesigntimeLike - open System - open Microsoft.Build.Tasks - open Microsoft.Build.Utilities - open Microsoft.Build.Framework - open Microsoft.Build.BuildEngine - open System.IO - - type ResolvedFile = { - itemSpec:string - resolvedFrom:ResolvedFrom - fusionName:string - version:string - redist:string - baggage:string + /// Information about a resolved file. + type ResolvedFile = + { /// Item specification + itemSpec:string + /// Location that the assembly was resolved from + resolvedFrom:ResolvedFrom + /// The long fusion name of the assembly + fusionName:string + /// The version of the assembly (like 4.0.0.0) + version:string + /// The name of the redist the assembly was found in + redist:string + /// Round-tripped baggage string + baggage:string } - with override this.ToString() = sprintf "ResolvedFile(%s)" this.itemSpec + + override this.ToString() = sprintf "ResolvedFile(%s)" this.itemSpec - type ResolutionResults = { - resolvedFiles:ResolvedFile array - referenceDependencyPaths:string array - relatedPaths:string array - referenceSatellitePaths:string array - referenceScatterPaths:string array - referenceCopyLocalPaths:string array - suggestedBindingRedirects:string array + /// Reference resolution results. All paths are fully qualified. + type ResolutionResults = + { /// Paths to primary references + resolvedFiles:ResolvedFile[] + /// Paths to dependencies + referenceDependencyPaths:string[] + /// Paths to related files (like .xml and .pdb) + relatedPaths:string[] + /// Paths to satellite assemblies used for localization. + referenceSatellitePaths:string[] + /// Additional files required to support multi-file assemblies. + referenceScatterPaths:string[] + /// Paths to files that reference resolution recommend be copied to the local directory + referenceCopyLocalPaths:string[] + /// Binding redirects that reference resolution recommends for the app.config file. + suggestedBindingRedirects:string[] } - let DotNetFrameworkReferenceAssembliesRootDirectory = + static member Empty = + { resolvedFiles = [| |] + referenceDependencyPaths = [| |] + relatedPaths = [| |] + referenceSatellitePaths = [| |] + referenceScatterPaths = [| |] + referenceCopyLocalPaths = [| |] + suggestedBindingRedirects = [| |] } + + + /// Get the Reference Assemblies directory for the .NET Framework on Window + let DotNetFrameworkReferenceAssembliesRootDirectoryOnWindows = // 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) let PF = - //System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) // This API is not available to bootstrap compiler - match System.Environment.GetEnvironmentVariable("ProgramFiles(x86)") with - | null -> System.Environment.GetEnvironmentVariable("ProgramFiles") // if PFx86 is null, then we are 32-bit and just get 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\Framework\.NETFramework" - let ReplaceFrameworkVariables(dirs) = - let windowsFramework = System.Environment.GetEnvironmentVariable("windir")+ @"\Microsoft.NET\Framework" - let referenceAssemblies = DotNetFrameworkReferenceAssembliesRootDirectory - dirs|>List.map(fun (d:string)->d.Replace("{WindowsFramework}",windowsFramework).Replace("{ReferenceAssemblies}",referenceAssemblies)) + /// When targeting .NET 2.0-3.5 on Windows, we expand the {WindowsFramework} and {ReferenceAssemblies} paths manually + let internal ReplaceVariablesForLegacyFxOnWindows(dirs: string list) = + let windowsFramework = Environment.GetEnvironmentVariable("windir")+ @"\Microsoft.NET\Framework" + let referenceAssemblies = DotNetFrameworkReferenceAssembliesRootDirectoryOnWindows + dirs |> List.map(fun d -> d.Replace("{WindowsFramework}",windowsFramework).Replace("{ReferenceAssemblies}",referenceAssemblies)) // ATTENTION!: the following code needs to be updated every time we are switching to the new MSBuild version because new .NET framework version was released // 1. List of frameworks // 2. DeriveTargetFrameworkDirectoriesFor45Plus // 3. HighestInstalledNetFrameworkVersionMajorMinor - // 4. GetPathToDotNetFramework + // 4. GetPathToDotNetFrameworkImlpementationAssemblies [] let private Net10 = "v1.0" + [] let private Net11 = "v1.1" + [] let private Net20 = "v2.0" + [] let private Net30 = "v3.0" + [] let private Net35 = "v3.5" + [] let private Net40 = "v4.0" + [] let private Net45 = "v4.5" + [] let private Net451 = "v4.5.1" + /// The list of supported .NET Framework version numbers, using the monikers of the Reference Assemblies folder. let SupportedNetFrameworkVersions = set [ Net20; Net30; Net35; Net40; Net45; Net451; (*SL only*) "v5.0" ] - let GetPathToDotNetFramework(v) = + /// Get the path to the .NET Framework implementation assemblies by using ToolLocationHelper.GetPathToDotNetFramework. + /// This is only used to specify the "last resort" path for assembly resolution. + let GetPathToDotNetFrameworkImlpementationAssemblies(v) = #if FX_ATLEAST_45 let v = match v with @@ -120,7 +152,7 @@ module internal MSBuildResolver = [] #endif - let DeriveTargetFrameworkDirectoriesFor40Plus(version) = + let GetPathToDotNetFrameworkReferenceAssembliesFor40Plus(version) = #if FX_ATLEAST_45 // starting with .Net 4.0, the runtime dirs (WindowsFramework) are never used by MSBuild RAR let v = @@ -141,8 +173,7 @@ module internal MSBuildResolver = [] #endif - /// Determine the default "frameworkVersion" (which is passed into MSBuild resolve). - /// This code uses MSBuild to determine version of the highest installed framework. + /// Use MSBuild to determine the version of the highest installed framework. let HighestInstalledNetFrameworkVersionMajorMinor() = #if FX_ATLEAST_45 if box (ToolLocationHelper.GetPathToDotNetFramework(TargetDotNetFrameworkVersion.Version451)) <> null then 4, Net451 @@ -154,133 +185,133 @@ module internal MSBuildResolver = #endif /// Derive the target framework directories. - let DeriveTargetFrameworkDirectories - (targetFrameworkVersion:string, // e.g. v2.0, v3.0, v3.5, v4.0 etc - logmessage:string->unit) = + let DeriveTargetFrameworkDirectories (targetFrameworkVersion:string, logMessage) = + let targetFrameworkVersion = - if not(targetFrameworkVersion.StartsWith("v",StringComparison.Ordinal)) then "v"^targetFrameworkVersion + if not(targetFrameworkVersion.StartsWith("v",StringComparison.Ordinal)) then "v"+targetFrameworkVersion else targetFrameworkVersion - let FrameworkStartsWith(short) = - targetFrameworkVersion.StartsWith(short,StringComparison.Ordinal) + let result = - if FrameworkStartsWith(Net10) then ReplaceFrameworkVariables([@"{WindowsFramework}\v1.0.3705"]) - else if FrameworkStartsWith(Net11) then ReplaceFrameworkVariables([@"{WindowsFramework}\v1.1.4322"]) - else if FrameworkStartsWith(Net20) then ReplaceFrameworkVariables([@"{WindowsFramework}\v2.0.50727"]) - else if FrameworkStartsWith(Net30) then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"]) - else if FrameworkStartsWith(Net35) then ReplaceFrameworkVariables([@"{ReferenceAssemblies}\v3.5"; @"{WindowsFramework}\v3.5"; @"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"]) - else DeriveTargetFrameworkDirectoriesFor40Plus(targetFrameworkVersion) + if targetFrameworkVersion.StartsWith(Net10, StringComparison.Ordinal) then ReplaceVariablesForLegacyFxOnWindows([@"{WindowsFramework}\v1.0.3705"]) + elif targetFrameworkVersion.StartsWith(Net11, StringComparison.Ordinal) then ReplaceVariablesForLegacyFxOnWindows([@"{WindowsFramework}\v1.1.4322"]) + elif targetFrameworkVersion.StartsWith(Net20, StringComparison.Ordinal) then ReplaceVariablesForLegacyFxOnWindows([@"{WindowsFramework}\v2.0.50727"]) + elif targetFrameworkVersion.StartsWith(Net30, StringComparison.Ordinal) then ReplaceVariablesForLegacyFxOnWindows([@"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"]) + elif targetFrameworkVersion.StartsWith(Net35, StringComparison.Ordinal) then ReplaceVariablesForLegacyFxOnWindows([@"{ReferenceAssemblies}\v3.5"; @"{WindowsFramework}\v3.5"; @"{ReferenceAssemblies}\v3.0"; @"{WindowsFramework}\v3.0"; @"{WindowsFramework}\v2.0.50727"]) + else GetPathToDotNetFrameworkReferenceAssembliesFor40Plus(targetFrameworkVersion) let result = result |> Array.ofList - logmessage (sprintf "Derived target framework directories for version %s are: %s" targetFrameworkVersion (String.Join(",", result))) + logMessage (sprintf "Derived target framework directories for version %s are: %s" targetFrameworkVersion (String.Join(",", result))) result /// Decode the ResolvedFrom code from MSBuild. let DecodeResolvedFrom(resolvedFrom:string) : ResolvedFrom = - let Same a b = - String.CompareOrdinal(a,b) = 0 match resolvedFrom with - | r when Same "{RawFileName}" r -> RawFileName - | r when Same "{GAC}" r -> GlobalAssemblyCache - | r when Same "{TargetFrameworkDirectory}" r -> TargetFrameworkDirectory - | r when Same "{AssemblyFolders}" r -> AssemblyFolders - | r when r.Length >= 10 && Same "{Registry:" (r.Substring(0,10)) -> AssemblyFoldersEx + | "{RawFileName}" -> RawFileName + | "{GAC}" -> GlobalAssemblyCache + | "{TargetFrameworkDirectory}" -> TargetFrameworkDirectory + | "{AssemblyFolders}" -> AssemblyFolders + | r when r.Length >= 10 && "{Registry:" = r.Substring(0,10) -> AssemblyFoldersEx | r -> ResolvedFrom.Path r - type ErrorWarningCallbackSig = ((*code:*)string->(*message*)string->unit) - - type Foregrounded = - | ForegroundedMessage of string - | ForegroundedError of string * string - | ForegroundedWarning of string * string - - let ResolveCore( - resolutionEnvironment: ResolutionEnvironment, + /// Perform assembly resolution by instantiating the ResolveAssemblyReference task directly from the MSBuild SDK. + let ResolveCore(resolutionEnvironment: ResolutionEnvironment, references:(string*(*baggage*)string)[], - targetFrameworkVersion:string, - targetFrameworkDirectories:string list, - targetProcessorArchitecture:string, - outputDirectory:string, - fsharpCoreExplicitDirOrFSharpBinariesDir:string, - explicitIncludeDirs:string list, - implicitIncludeDir:string, - frameworkRegistryBase:string, - assemblyFoldersSuffix:string, - assemblyFoldersConditions:string, - allowRawFileName:bool, - logmessage:string->unit, - logwarning:ErrorWarningCallbackSig, - logerror:ErrorWarningCallbackSig ) = - - // Message Foregrounding: - // In version 4.0 MSBuild began calling log methods on a background (non-UI) thread. If there is an exception thrown from - // logmessage, logwarning or logerror then it would kill the process. - // The fix is to catch these exceptions and log the rest of the messages to a list to output at the end. - // It looks simpler to always just accumulate the messages during resolution and show them all at the end, but then - // we couldn't see the messages as resolution progresses. - let foregrounded = ref [] - let backgroundException : exn option ref = ref None + targetFrameworkVersion: string, + targetFrameworkDirectories: string list, + targetProcessorArchitecture: string, + outputDirectory: string, + fsharpCoreExplicitDirOrFSharpBinariesDir: string, + explicitIncludeDirs: string list, + implicitIncludeDir: string, + frameworkRegistryBase: string, + assemblyFoldersSuffix: string, + assemblyFoldersConditions: string, + allowRawFileName: bool, + logMessage: (string -> unit), + logWarning: (string -> string -> unit), + logError: (string -> string -> unit)) = + + if Array.isEmpty references then ResolutionResults.Empty else + + let backgroundException = ref false - let logmessage message = - match !backgroundException with - | Some _ -> foregrounded := ForegroundedMessage(message) :: !foregrounded - | None -> - try - logmessage message - with e -> - backgroundException := Some(e) - foregrounded := ForegroundedMessage(message) :: !foregrounded - - let logwarning code message = - match !backgroundException with - | Some _ -> foregrounded := ForegroundedWarning(code,message) :: !foregrounded - | None -> - try - logwarning code message - with e -> - backgroundException := Some(e) - foregrounded := ForegroundedWarning(code,message) :: !foregrounded - - let logerror code message = - match !backgroundException with - | Some _ -> foregrounded := ForegroundedError(code,message) :: !foregrounded - | None -> - try - logerror code message - with e -> - backgroundException := Some(e) - foregrounded := ForegroundedError(code,message) :: !foregrounded + let protect f = + if not !backgroundException then + try f() + with _ -> backgroundException := true - - let engine = { new IBuildEngine with - member be.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs) = true - member be.LogCustomEvent(e) = logmessage e.Message - member be.LogErrorEvent(e) = logerror e.Code e.Message - member be.LogMessageEvent(e) = logmessage e.Message - member be.LogWarningEvent(e) = logwarning e.Code e.Message - member be.ColumnNumberOfTaskNode with get() = 1 - member be.LineNumberOfTaskNode with get() = 1 - member be.ContinueOnError with get() = true - member be.ProjectFileOfTaskNode with get() = "" } + let engine = + { new IBuildEngine with + member __.BuildProjectFile(projectFileName, targetNames, globalProperties, targetOutputs) = true + member __.LogCustomEvent(e) = protect (fun () -> logMessage e.Message) + member __.LogErrorEvent(e) = protect (fun () -> logError e.Code e.Message) + member __.LogMessageEvent(e) = protect (fun () -> logMessage e.Message) + member __.LogWarningEvent(e) = protect (fun () -> logWarning e.Code e.Message) + member __.ColumnNumberOfTaskNode = 1 + member __.LineNumberOfTaskNode = 1 + member __.ContinueOnError = true + member __.ProjectFileOfTaskNode = "" } - let rar = new ResolveAssemblyReference() - rar.BuildEngine <- engine - - // Derive target framework directory if none was supplied. + // Derive the target framework directory if none was supplied. let targetFrameworkDirectories = - if targetFrameworkDirectories=[] then DeriveTargetFrameworkDirectories(targetFrameworkVersion, logmessage) + if targetFrameworkDirectories=[] then DeriveTargetFrameworkDirectories(targetFrameworkVersion, logMessage) else targetFrameworkDirectories |> Array.ofList - // Filter for null and zero length, and escape backslashes so legitimate path characters aren't mistaken for - // escape characters (E.g., ".\r.dll") - let explicitIncludeDirs = explicitIncludeDirs |> List.filter(fun eid->not(String.IsNullOrEmpty(eid))) - let references = references |> Array.filter(fun (path,_)->not(String.IsNullOrEmpty(path))) // |> Array.map (fun (path,baggage) -> (path.Replace("\\","\\\\"),baggage)) - - rar.TargetFrameworkDirectories <- targetFrameworkDirectories - rar.FindRelatedFiles <- false - rar.FindDependencies <- false - rar.FindSatellites <- false - rar.FindSerializationAssemblies <- false + // Filter for null and zero length + let references = references |> Array.filter(fst >> String.IsNullOrEmpty >> not) + + // Determine the set of search paths for the resolution + let searchPaths = + + let explicitIncludeDirs = explicitIncludeDirs |> List.filter(String.IsNullOrEmpty >> not) + + let rawFileNamePath = if allowRawFileName then ["{RawFileName}"] else [] + + let registry = sprintf "{Registry:%s,%s,%s%s}" frameworkRegistryBase targetFrameworkVersion assemblyFoldersSuffix assemblyFoldersConditions + + [| match resolutionEnvironment with + | DesigntimeLike + | RuntimeLike -> + logMessage("Using scripting resolution precedence.") + // These are search paths for runtime-like or scripting resolution. GAC searching is present. + yield! rawFileNamePath // Quick-resolve straight to filename first + yield! explicitIncludeDirs // From -I, #I + yield implicitIncludeDir // Usually the project directory + yield fsharpCoreExplicitDirOrFSharpBinariesDir // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe + yield "{TargetFrameworkDirectory}" + yield registry + yield "{AssemblyFolders}" + yield "{GAC}" + + | CompileTimeLike -> + logMessage("Using compilation resolution precedence.") + // These are search paths for compile-like resolution. GAC searching is not present. + yield "{TargetFrameworkDirectory}" + yield! rawFileNamePath // Quick-resolve straight to filename first + yield! explicitIncludeDirs // From -I, #I + yield implicitIncludeDir // Usually the project directory + yield fsharpCoreExplicitDirOrFSharpBinariesDir // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe + yield registry + yield "{AssemblyFolders}" + yield outputDirectory + yield "{GAC}" + // use path to implementation assemblies as the last resort + yield! GetPathToDotNetFrameworkImlpementationAssemblies targetFrameworkVersion + |] + + let assemblies = + [| for (referenceName,baggage) in references -> + let item = new Microsoft.Build.Utilities.TaskItem(referenceName) + item.SetMetadata("Baggage", baggage) + item:>ITaskItem |] + + let rar = + ResolveAssemblyReference(BuildEngine=engine, TargetFrameworkDirectories=targetFrameworkDirectories, + FindRelatedFiles=false, FindDependencies=false, FindSatellites=false, + FindSerializationAssemblies=false, Assemblies=assemblies, + SearchPaths=searchPaths, + AllowedAssemblyExtensions= [| ".dll" ; ".exe" |]) #if BUILDING_WITH_LKG ignore targetProcessorArchitecture #else @@ -288,147 +319,60 @@ module internal MSBuildResolver = rar.TargetProcessorArchitecture <- targetProcessorArchitecture rar.CopyLocalDependenciesWhenParentReferenceInGac <- true #endif - rar.Assemblies <- [|for (referenceName,baggage) in references -> - let item = new Microsoft.Build.Utilities.TaskItem(referenceName) - item.SetMetadata("Baggage", baggage) - item:>ITaskItem|] - - let rawFileNamePath = if allowRawFileName then ["{RawFileName}"] else [] - let searchPaths = - match resolutionEnvironment with - | DesigntimeLike - | RuntimeLike -> - logmessage("Using scripting resolution precedence.") - // These are search paths for runtime-like or scripting resolution. GAC searching is present. - rawFileNamePath @ // Quick-resolve straight to filename first - explicitIncludeDirs @ // From -I, #I - [implicitIncludeDir] @ // Usually the project directory - [fsharpCoreExplicitDirOrFSharpBinariesDir] @ // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe - ["{TargetFrameworkDirectory}"] @ - [sprintf "{Registry:%s,%s,%s%s}" frameworkRegistryBase targetFrameworkVersion assemblyFoldersSuffix assemblyFoldersConditions] @ - ["{AssemblyFolders}"] @ - ["{GAC}"] - | CompileTimeLike -> - logmessage("Using compilation resolution precedence.") - // These are search paths for compile-like resolution. GAC searching is not present. - ["{TargetFrameworkDirectory}"] @ - rawFileNamePath @ // Quick-resolve straight to filename first - explicitIncludeDirs @ // From -I, #I - [implicitIncludeDir] @ // Usually the project directory - [fsharpCoreExplicitDirOrFSharpBinariesDir] @ // Location of explicit reference to FSharp.Core, otherwise location of fsc.exe - [sprintf "{Registry:%s,%s,%s%s}" frameworkRegistryBase targetFrameworkVersion assemblyFoldersSuffix assemblyFoldersConditions] @ // Like {Registry:Software\Microsoft\.NETFramework,v2.0,AssemblyFoldersEx} - ["{AssemblyFolders}"] @ - [outputDirectory] @ - ["{GAC}"] @ - GetPathToDotNetFramework targetFrameworkVersion // use path to implementation assemblies as the last resort - - rar.SearchPaths <- searchPaths |> Array.ofList - - rar.AllowedAssemblyExtensions <- [| ".dll" ; ".exe" |] let succeeded = rar.Execute() - // Unroll any foregrounded messages - match !backgroundException with - | Some(backGroundException) -> - logwarning "" "Saw error on logger thread during resolution." - logwarning "" (sprintf "%A" backGroundException) - logwarning "" "Showing messages seen after exception." - - !foregrounded - |> List.iter(fun message-> - match message with - | ForegroundedMessage(message) -> logmessage message - | ForegroundedWarning(code,message) -> logwarning code message - | ForegroundedError(code,message) -> logerror code message ) - | None -> () - if not succeeded then raise ResolutionFailure - { - resolvedFiles = [| for p in rar.ResolvedFiles -> {itemSpec = p.ItemSpec; - resolvedFrom = DecodeResolvedFrom(p.GetMetadata("ResolvedFrom")); - fusionName = p.GetMetadata("FusionName"); - version = p.GetMetadata("Version"); - redist = p.GetMetadata("Redist"); - baggage = p.GetMetadata("Baggage") } |] - referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |] - relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |] - referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |] - referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |] - referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |] - suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |] - } + let resolvedFiles = + [| for p in rar.ResolvedFiles -> + { itemSpec = p.ItemSpec + resolvedFrom = DecodeResolvedFrom(p.GetMetadata("ResolvedFrom")) + fusionName = p.GetMetadata("FusionName") + version = p.GetMetadata("Version") + redist = p.GetMetadata("Redist") + baggage = p.GetMetadata("Baggage") } |] + + { resolvedFiles = resolvedFiles + referenceDependencyPaths = [| for p in rar.ResolvedDependencyFiles -> p.ItemSpec |] + relatedPaths = [| for p in rar.RelatedFiles -> p.ItemSpec |] + referenceSatellitePaths = [| for p in rar.SatelliteFiles -> p.ItemSpec |] + referenceScatterPaths = [| for p in rar.ScatterFiles -> p.ItemSpec |] + referenceCopyLocalPaths = [| for p in rar.CopyLocalFiles -> p.ItemSpec |] + suggestedBindingRedirects = [| for p in rar.SuggestedRedirects -> p.ItemSpec |] } + + + + /// Perform the resolution on rooted and unrooted paths, and then combine the results. + let Resolve(resolutionEnvironment, references, targetFrameworkVersion, targetFrameworkDirectories, targetProcessorArchitecture, + outputDirectory, fsharpCoreExplicitDirOrFSharpBinariesDir, explicitIncludeDirs, implicitIncludeDir, frameworkRegistryBase, + assemblyFoldersSuffix, assemblyFoldersConditions, logMessage, logWarning, logError) = - let Resolve( - resolutionEnvironment: ResolutionEnvironment, - references:(string*(*baggage*)string)[], - targetFrameworkVersion:string, - targetFrameworkDirectories:string list, - targetProcessorArchitecture:string, - outputDirectory:string, - fsharpCoreExplicitDirOrFSharpBinariesDir:string, - explicitIncludeDirs:string list, - implicitIncludeDir:string, - frameworkRegistryBase:string, - assemblyFoldersSuffix:string, - assemblyFoldersConditions:string, - logmessage:string->unit, - logwarning:ErrorWarningCallbackSig, - logerror:ErrorWarningCallbackSig ) = // The {RawFileName} target is 'dangerous', in the sense that is uses Directory.GetCurrentDirectory() to resolve unrooted file paths. // It is unreliable to use this mutable global state inside Visual Studio. As a result, we partition all references into a "rooted" set // (which contains e.g. C:\MyDir\MyAssem.dll) and "unrooted" (everything else). We only allow "rooted" to use {RawFileName}. Note that // unrooted may still find 'local' assemblies by virtue of the fact that "implicitIncludeDir" is one of the places searched during // assembly resolution. - let references = references |> Array.map (fun ((file,baggage) as data) -> - // However, MSBuild will not resolve 'relative' paths, even when e.g. implicitIncludeDir is part of the search. As a result, - // if we have an unrooted path+filename, we'll assume this is relative to the project directory and root it. - if FileSystem.IsPathRootedShim(file) then - data // fine, e.g. "C:\Dir\foo.dll" - elif not(file.Contains("\\") || file.Contains("/")) then - data // fine, e.g. "System.Transactions.dll" - else - // we have a 'relative path', e.g. "bin/Debug/foo.exe" or "..\Yadda\bar.dll" - // turn it into an absolute path based at implicitIncludeDir - (System.IO.Path.Combine(implicitIncludeDir, file), baggage) - ) - let rooted, unrooted = references |> Array.partition (fun (file,_baggage) -> FileSystem.IsPathRootedShim(file)) - - let CallResolveCore(references, allowRawFileName) = - if Array.isEmpty references then - { - resolvedFiles = [| |] - referenceDependencyPaths = [| |] - relatedPaths = [| |] - referenceSatellitePaths = [| |] - referenceScatterPaths = [| |] - referenceCopyLocalPaths = [| |] - suggestedBindingRedirects = [| |] - } - else - // all the params are the same... - ResolveCore( - resolutionEnvironment, - references, // ... except this - targetFrameworkVersion, - targetFrameworkDirectories, - targetProcessorArchitecture, - outputDirectory, - fsharpCoreExplicitDirOrFSharpBinariesDir, - explicitIncludeDirs, - implicitIncludeDir, - frameworkRegistryBase, - assemblyFoldersSuffix, - assemblyFoldersConditions, - allowRawFileName, // ... and this - logmessage, - logwarning, - logerror) - - let rootedResults = CallResolveCore(rooted, true) - let unrootedResults = CallResolveCore(unrooted, false) + let references = + [| for ((file,baggage) as data) in references -> + // However, MSBuild will not resolve 'relative' paths, even when e.g. implicitIncludeDir is part of the search. As a result, + // if we have an unrooted path+filename, we'll assume this is relative to the project directory and root it. + if FileSystem.IsPathRootedShim(file) then + data // fine, e.g. "C:\Dir\foo.dll" + elif not(file.Contains("\\") || file.Contains("/")) then + data // fine, e.g. "System.Transactions.dll" + else + // we have a 'relative path', e.g. "bin/Debug/foo.exe" or "..\Yadda\bar.dll" + // turn it into an absolute path based at implicitIncludeDir + (Path.Combine(implicitIncludeDir, file), baggage) |] + + let rooted, unrooted = references |> Array.partition (fst >> FileSystem.IsPathRootedShim) + + let rootedResults = ResolveCore(resolutionEnvironment, rooted, targetFrameworkVersion, targetFrameworkDirectories, targetProcessorArchitecture, outputDirectory, fsharpCoreExplicitDirOrFSharpBinariesDir, explicitIncludeDirs, implicitIncludeDir, frameworkRegistryBase, assemblyFoldersSuffix, assemblyFoldersConditions, true, logMessage, logWarning, logError) + + let unrootedResults = ResolveCore(resolutionEnvironment, unrooted, targetFrameworkVersion, targetFrameworkDirectories, targetProcessorArchitecture, outputDirectory, fsharpCoreExplicitDirOrFSharpBinariesDir, explicitIncludeDirs, implicitIncludeDir, frameworkRegistryBase, assemblyFoldersSuffix, assemblyFoldersConditions, false, logMessage, logWarning, logError) + // now unify the two sets of results { resolvedFiles = Array.concat [| rootedResults.resolvedFiles; unrootedResults.resolvedFiles |] diff --git a/src/fsharp/ReferenceResolution.fsi b/src/fsharp/ReferenceResolution.fsi index 003cfdbe5ed870a424bb112a7cac95bc0c10b185..0f83ab88a6a3bd2eb3ff20d1b6a375e309207f43 100644 --- a/src/fsharp/ReferenceResolution.fsi +++ b/src/fsharp/ReferenceResolution.fsi @@ -1,15 +1,5 @@ // Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Viz - -/// This type exists to have a concrete 'Target' type for a DebuggerVisualizerAttribute. -/// 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 -/// pragmatically just shove this into the compiler assembly itself. -type internal Visualizable = - new : obj -> Visualizable - member Data : obj - /// assuming this assembly is already in the debuggee process, then Viz.Visualiable.Make(foo) in the Watch window will make a visualizer for foo - static member Make : obj -> Visualizable namespace Microsoft.FSharp.Compiler @@ -18,6 +8,7 @@ module internal MSBuildResolver = exception ResolutionFailure val SupportedNetFrameworkVersions : Set + val HighestInstalledNetFrameworkVersionMajorMinor : unit -> int * string /// Describes the location where the reference was found. @@ -30,7 +21,7 @@ module internal MSBuildResolver = | Path of string | Unknown - /// Whether the resolve should follow compile-time rules or runtime rules. + /// Indicates whether the resolve should follow compile-time rules or runtime rules. type ResolutionEnvironment = | CompileTimeLike | RuntimeLike // Don't allow stubbed-out reference assemblies @@ -39,48 +30,45 @@ module internal MSBuildResolver = #if SILVERLIGHT #else - val DotNetFrameworkReferenceAssembliesRootDirectory : string + /// Get the Reference Assemblies directory for the .NET Framework on Window + val DotNetFrameworkReferenceAssembliesRootDirectoryOnWindows : string /// Information about a resolved file. - type ResolvedFile = { - /// Item specification - itemSpec:string - /// Location that the assembly was resolved from - resolvedFrom:ResolvedFrom - /// The long fusion name of the assembly - fusionName:string - /// The version of the assembly (like 4.0.0.0) - version:string - /// The name of the redist the assembly was found in - redist:string - /// Round-tripped baggage string - baggage:string + type ResolvedFile = + { /// Item specification + itemSpec:string + /// Location that the assembly was resolved from + resolvedFrom:ResolvedFrom + /// The long fusion name of the assembly + fusionName:string + /// The version of the assembly (like 4.0.0.0) + version:string + /// The name of the redist the assembly was found in + redist:string + /// Round-tripped baggage string + baggage:string } /// Reference resolution results. All paths are fully qualified. - type ResolutionResults = { - /// Paths to primary references - resolvedFiles:ResolvedFile array - /// Paths to dependencies - referenceDependencyPaths:string array - /// Paths to related files (like .xml and .pdb) - relatedPaths:string array - /// Paths to satellite assemblies used for localization. - referenceSatellitePaths:string array - /// Additional files required to support multi-file assemblies. - referenceScatterPaths:string array - /// Paths to files that reference resolution recommend be copied to the local directory - referenceCopyLocalPaths:string array - /// Binding redirects that reference resolution recommends for the app.config file. - suggestedBindingRedirects:string array - } + type ResolutionResults = + { /// Paths to primary references + resolvedFiles:ResolvedFile[] + /// Paths to dependencies + referenceDependencyPaths:string[] + /// Paths to related files (like .xml and .pdb) + relatedPaths:string[] + /// Paths to satellite assemblies used for localization. + referenceSatellitePaths:string[] + /// Additional files required to support multi-file assemblies. + referenceScatterPaths:string[] + /// Paths to files that reference resolution recommend be copied to the local directory + referenceCopyLocalPaths:string[] + /// Binding redirects that reference resolution recommends for the app.config file. + suggestedBindingRedirects:string[] } - /// Callback for errors and warnings. - type ErrorWarningCallbackSig = - ((*code:*)string->(*message*)string->unit) - - val Resolve : + /// Perform assembly resolution on the given references + val Resolve: resolutionEnvironment: ResolutionEnvironment * references:(string*(*baggage*)string)[] * targetFrameworkVersion:string * @@ -94,6 +82,7 @@ module internal MSBuildResolver = assemblyFoldersSuffix:string * assemblyFoldersConditions:string * logmessage:(string->unit) * - logwarning:ErrorWarningCallbackSig * - logerror:ErrorWarningCallbackSig -> ResolutionResults + logwarning:(string->string->unit) * + logerror:(string->string->unit) + -> ResolutionResults #endif