diff --git a/src/Compiler/DependencyManager/DependencyProvider.fs b/src/Compiler/DependencyManager/DependencyProvider.fs index b6babf407f7..257d2869937 100644 --- a/src/Compiler/DependencyManager/DependencyProvider.fs +++ b/src/Compiler/DependencyManager/DependencyProvider.fs @@ -26,6 +26,8 @@ module ReflectionHelper = let resolveDependenciesMethodName = "ResolveDependencies" + let clearResultsCacheMethodName = "ClearResultsCache" + let namePropertyName = "Name" let keyPropertyName = "Key" @@ -127,7 +129,7 @@ type IDependencyManagerProvider = abstract Name: string abstract Key: string abstract HelpMessages: string[] - + abstract ClearResultsCache: unit -> unit abstract ResolveDependencies: scriptDir: string * mainScriptName: string * @@ -139,7 +141,7 @@ type IDependencyManagerProvider = timeout: int -> IResolveDependenciesResult -type ReflectionDependencyManagerProvider + type ReflectionDependencyManagerProvider ( theType: Type, nameProperty: PropertyInfo, @@ -149,10 +151,17 @@ type ReflectionDependencyManagerProvider resolveDepsEx: MethodInfo option, resolveDepsExWithTimeout: MethodInfo option, resolveDepsExWithScriptInfoAndTimeout: MethodInfo option, - outputDir: string option - ) = + clearResultCache: MethodInfo option, + outputDir: string option, + useResultsCache: bool + ) = + + let instance = + if not(isNull (theType.GetConstructor([|typeof; typeof|]))) then + Activator.CreateInstance(theType, [| outputDir :> obj; useResultsCache :> obj |]) + else + Activator.CreateInstance(theType, [| outputDir :> obj |]) - let instance = Activator.CreateInstance(theType, [| outputDir :> obj |]) let nameProperty = nameProperty.GetValue >> string let keyProperty = keyProperty.GetValue >> string @@ -163,7 +172,7 @@ type ReflectionDependencyManagerProvider | Some helpMessagesProperty -> helpMessagesProperty.GetValue >> toStringArray | None -> fun _ -> [||] - static member InstanceMaker(theType: Type, outputDir: string option) = + static member InstanceMaker(theType: Type, outputDir: string option, useResultsCache: bool) = match getAttributeNamed theType dependencyManagerAttributeName, getInstanceProperty theType namePropertyName, getInstanceProperty theType keyPropertyName, @@ -172,7 +181,6 @@ type ReflectionDependencyManagerProvider | None, _, _, _ | _, None, _, _ | _, _, None, _ -> None - | Some _, Some nameProperty, Some keyProperty, None -> let resolveMethod = getInstanceMethod @@ -223,6 +231,11 @@ type ReflectionDependencyManagerProvider |] resolveDependenciesMethodName + let clearResultsCacheMethod = + getInstanceMethod + theType [||] + clearResultsCacheMethodName + Some(fun () -> ReflectionDependencyManagerProvider( theType, @@ -233,7 +246,9 @@ type ReflectionDependencyManagerProvider resolveMethodEx, resolveMethodExWithTimeout, resolveDepsExWithScriptInfoAndTimeout, - outputDir + clearResultsCacheMethod, + outputDir, + useResultsCache ) :> IDependencyManagerProvider) @@ -287,6 +302,11 @@ type ReflectionDependencyManagerProvider |] resolveDependenciesMethodName + let clearResultsCacheMethod = + getInstanceMethod + theType [||] + clearResultsCacheMethodName + Some(fun () -> ReflectionDependencyManagerProvider( theType, @@ -297,7 +317,9 @@ type ReflectionDependencyManagerProvider resolveMethodEx, resolveMethodExWithTimeout, resolveDepsExWithScriptInfoAndTimeout, - outputDir + clearResultsCacheMethod, + outputDir, + useResultsCache ) :> IDependencyManagerProvider) @@ -377,6 +399,13 @@ type ReflectionDependencyManagerProvider /// Key of dependency Manager: used for #r "key: ... " E.g nuget member _.Key = instance |> keyProperty + /// Clear the dependency manager caches + member _.ClearResultsCache () = + match clearResultCache with + | Some clearResultsCache -> + clearResultsCache.Invoke(instance, [||]) |> ignore + | None -> () + /// Key of dependency Manager: used for #help member _.HelpMessages = instance |> helpMessagesProperty @@ -454,7 +483,7 @@ type ReflectionDependencyManagerProvider /// Provides DependencyManagement functions. /// Class is IDisposable -type DependencyProvider internal (assemblyProbingPaths: AssemblyResolutionProbe option, nativeProbingRoots: NativeResolutionProbe option) = +type DependencyProvider internal (assemblyProbingPaths: AssemblyResolutionProbe option, nativeProbingRoots: NativeResolutionProbe option, useResultsCache: bool) = // Note: creating a NativeDllResolveHandler currently installs process-wide handlers let dllResolveHandler = new NativeDllResolveHandler(nativeProbingRoots) @@ -508,7 +537,7 @@ type DependencyProvider internal (assemblyProbingPaths: AssemblyResolutionProbe let loadedProviders = enumerateDependencyManagerAssemblies compilerTools reportError |> Seq.collect (fun a -> a.GetTypes()) - |> Seq.choose (fun t -> ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir)) + |> Seq.choose (fun t -> ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir, useResultsCache)) |> Seq.map (fun maker -> maker ()) defaultProviders @@ -523,11 +552,18 @@ type DependencyProvider internal (assemblyProbingPaths: AssemblyResolutionProbe ConcurrentDictionary<_, Result>(HashIdentity.Structural) new(assemblyProbingPaths: AssemblyResolutionProbe, nativeProbingRoots: NativeResolutionProbe) = - new DependencyProvider(Some assemblyProbingPaths, Some nativeProbingRoots) + new DependencyProvider(Some assemblyProbingPaths, Some nativeProbingRoots, true) + + new(assemblyProbingPaths: AssemblyResolutionProbe, nativeProbingRoots: NativeResolutionProbe, useResultsCache) = + new DependencyProvider(Some assemblyProbingPaths, Some nativeProbingRoots, useResultsCache) - new(nativeProbingRoots: NativeResolutionProbe) = new DependencyProvider(None, Some nativeProbingRoots) + new(nativeProbingRoots: NativeResolutionProbe, useResultsCache) = + new DependencyProvider(None, Some nativeProbingRoots, useResultsCache) - new() = new DependencyProvider(None, None) + new(nativeProbingRoots: NativeResolutionProbe) = + new DependencyProvider(None, Some nativeProbingRoots, true) + + new() = new DependencyProvider(None, None, true) /// Returns a formatted help messages for registered dependencymanagers for the host to present member _.GetRegisteredDependencyManagerHelpText(compilerTools, outputDir, errorReport) = @@ -540,6 +576,14 @@ type DependencyProvider internal (assemblyProbingPaths: AssemblyResolutionProbe yield! dm.HelpMessages |] + /// Clear the DependencyManager results caches + member _.ClearResultsCache(compilerTools, outputDir, errorReport) = + let managers = + RegisteredDependencyManagers compilerTools (Option.ofString outputDir) errorReport + + for kvp in managers do + kvp.Value.ClearResultsCache() + /// Returns a formatted error message for the host to present member _.CreatePackageManagerUnknownError ( diff --git a/src/Compiler/DependencyManager/DependencyProvider.fsi b/src/Compiler/DependencyManager/DependencyProvider.fsi index 5e021dc5bed..a4c76e67b92 100644 --- a/src/Compiler/DependencyManager/DependencyProvider.fsi +++ b/src/Compiler/DependencyManager/DependencyProvider.fsi @@ -3,6 +3,7 @@ // Helper members to integrate DependencyManagers into F# codebase namespace FSharp.Compiler.DependencyManager +open System open System.Runtime.InteropServices open Internal.Utilities.Library @@ -53,6 +54,9 @@ type IDependencyManagerProvider = /// The help messages for this dependency manager inster abstract HelpMessages: string[] + /// Clear the results cache + abstract ClearResultsCache: unit -> unit + /// Resolve the dependencies, for the given set of arguments, go find the .dll references, scripts and additional include values. abstract ResolveDependencies: scriptDir: string * @@ -80,7 +84,7 @@ type ResolvingErrorReport = delegate of ErrorReportType * int * string -> unit /// provided each time the TryFindDependencyManagerByKey and TryFindDependencyManagerInPath are /// executed, which are assumed to be invariant over the lifetime of the DependencyProvider. type DependencyProvider = - interface System.IDisposable + interface IDisposable /// Construct a new DependencyProvider with no dynamic load handlers (only for compilation/analysis) new: unit -> DependencyProvider @@ -88,12 +92,25 @@ type DependencyProvider = /// Construct a new DependencyProvider with only native resolution new: nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + /// Construct a new DependencyProvider with only native resolution and specify caching + new: nativeProbingRoots: NativeResolutionProbe * useResultsCache: bool -> DependencyProvider + /// Construct a new DependencyProvider with managed and native resolution new: assemblyProbingPaths: AssemblyResolutionProbe * nativeProbingRoots: NativeResolutionProbe -> DependencyProvider + /// Construct a new DependencyProvider with managed and native resolution and specify caching + new: + assemblyProbingPaths: AssemblyResolutionProbe * + nativeProbingRoots: NativeResolutionProbe * + useResultsCache: bool -> + DependencyProvider + /// Returns a formatted help messages for registered dependencymanagers for the host to present member GetRegisteredDependencyManagerHelpText: string seq * string * ResolvingErrorReport -> string[] + /// Clear the DependencyManager results caches + member ClearResultsCache: string seq * string * ResolvingErrorReport -> unit + /// Returns a formatted error message for the host to present member CreatePackageManagerUnknownError: string seq * string * string * ResolvingErrorReport -> int * string diff --git a/src/Compiler/Driver/CompilerConfig.fs b/src/Compiler/Driver/CompilerConfig.fs index 8eb2189fb68..b1bca1b6092 100644 --- a/src/Compiler/Driver/CompilerConfig.fs +++ b/src/Compiler/Driver/CompilerConfig.fs @@ -417,6 +417,7 @@ type TcConfigBuilder = mutable subsystemVersion: int * int mutable useHighEntropyVA: bool mutable inputCodePage: int option + mutable clearResultsCache: bool mutable embedResources: string list mutable diagnosticsOptions: FSharpDiagnosticOptions mutable mlCompatibility: bool @@ -631,6 +632,7 @@ type TcConfigBuilder = diagnosticsOptions = FSharpDiagnosticOptions.Default embedResources = [] inputCodePage = None + clearResultsCache = false subsystemVersion = 4, 0 // per spec for 357994 useHighEntropyVA = false mlCompatibility = false @@ -1217,6 +1219,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member _.subsystemVersion = data.subsystemVersion member _.useHighEntropyVA = data.useHighEntropyVA member _.inputCodePage = data.inputCodePage + member _.clearResultsCache = data.clearResultsCache member _.embedResources = data.embedResources member _.diagnosticsOptions = data.diagnosticsOptions member _.mlCompatibility = data.mlCompatibility diff --git a/src/Compiler/Driver/CompilerConfig.fsi b/src/Compiler/Driver/CompilerConfig.fsi index 202aa59b44e..a11f1c6c77d 100644 --- a/src/Compiler/Driver/CompilerConfig.fsi +++ b/src/Compiler/Driver/CompilerConfig.fsi @@ -255,6 +255,8 @@ type TcConfigBuilder = mutable inputCodePage: int option + mutable clearResultsCache: bool + mutable embedResources: string list mutable diagnosticsOptions: FSharpDiagnosticOptions @@ -566,6 +568,8 @@ type TcConfig = member inputCodePage: int option + member clearResultsCache: bool + member embedResources: string list member diagnosticsOptions: FSharpDiagnosticOptions diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index b46754e88be..80536ec1fb0 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -1240,7 +1240,17 @@ let noFrameworkFlag isFsc tcConfigB = ) let advancedFlagsFsi tcConfigB = - advancedFlagsBoth tcConfigB @ [ noFrameworkFlag false tcConfigB ] + advancedFlagsBoth tcConfigB + @ [ + CompilerOption( + "clearResultsCache", + tagNone, + OptionUnit(fun () -> tcConfigB.clearResultsCache <- true), + None, + Some(FSComp.SR.optsClearResultsCache ()) + ) + noFrameworkFlag false tcConfigB + ] let advancedFlagsFsc tcConfigB = advancedFlagsBoth tcConfigB diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 513117632cc..3cad8590d7f 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -891,6 +891,7 @@ optsHelp,"Display this usage message (Short form: -?)" optsVersion,"Display compiler version banner and exit" optsResponseFile,"Read response file for more options" optsCodepage,"Specify the codepage used to read source files" +optsClearResultsCache,"Clear the package manager results cache" optsUtf8output,"Output messages in UTF-8 encoding" optsFullpaths,"Output messages with fully qualified paths" optsLib,"Specify a directory for the include path which is used to resolve source files and assemblies (Short form: -I)" diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 4fe586a84b8..60409697087 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -989,6 +989,8 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, let dependencyProvider = new DependencyProvider(NativeResolutionProbe(tcConfigB.GetNativeProbingRoots)) do + if tcConfigB.clearResultsCache then + dependencyProvider.ClearResultsCache(tcConfigB.compilerToolPaths, getOutputDir tcConfigB, reportError rangeCmdArgs) if tcConfigB.utf8output then let prev = Console.OutputEncoding Console.OutputEncoding <- Encoding.UTF8 diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index d0a995aa3b0..95f1966a336 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -432,6 +432,11 @@ Vytiskněte odvozená rozhraní všech kompilovaných souborů do přidružených souborů podpisu. + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 57d1e5450ea..e164dba0f05 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -432,6 +432,11 @@ Drucken der abgeleiteten Schnittstellen aller Dateien an zugehörige Signaturdateien + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 6a59b872c1b..ea1ecde3db6 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -432,6 +432,11 @@ Imprimir las interfaces deducidas de todos los archivos de compilación en los archivos de signatura asociados + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 7b64cc5b780..648ef9169da 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -432,6 +432,11 @@ Imprimer les interfaces inférées de tous les fichiers de compilation sur les fichiers de signature associés + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 8a36ec3b730..1a76a536e19 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -432,6 +432,11 @@ Stampare le interfacce derivate di tutti i file di compilazione nei file di firma associati + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 2509bc7585a..6da3c202ac1 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -432,6 +432,11 @@ すべてのコンパイル ファイルの推定されたインターフェイスを関連する署名ファイルに印刷します + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 8f209c3ada1..72daec99994 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -432,6 +432,11 @@ 모든 컴파일 파일의 유추된 인터페이스를 관련 서명 파일로 인쇄합니다. + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9539f9af34f..386aacfbc99 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -432,6 +432,11 @@ Drukowanie wywnioskowanych interfejsów wszystkich plików kompilacji do skojarzonych plików sygnatur + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 29bcce01250..3b581e58c9e 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -432,6 +432,11 @@ Imprimir as interfaces inferidas de todos os arquivos de compilação para os arquivos de assinatura associados + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 1fb3a354f86..3288b85bc5f 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -432,6 +432,11 @@ Печать определяемых интерфейсов всех файлов компиляции в связанные файлы подписей + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index d36bd224b9d..cc11f361ba5 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -432,6 +432,11 @@ Tüm derleme dosyalarının çıkarsanan arabirimlerini ilişkili imza dosyalarına yazdır + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index a1b2b225415..13a2e6d7300 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -432,6 +432,11 @@ 将所有编译文件的推断接口打印到关联的签名文件 + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index b8854a10e66..314dff132d8 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -432,6 +432,11 @@ 將所有編譯檔案的推斷介面列印至相關聯的簽章檔案 + + Clear the package manager results cache + Clear the package manager results cache + + Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. Invalid use of emitting a reference assembly, do not use '--staticlink', or '--refonly' and '--refout' together. diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs index 24ad0c02f38..311888a6a65 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.ProjectFile.fs @@ -13,103 +13,12 @@ type PackageReference = Script: string } -// Resolved assembly information -type internal Resolution = - { - NugetPackageId: string - NugetPackageVersion: string - PackageRoot: string - FullPath: string - AssetType: string - IsNotImplementationReference: string - InitializeSourcePath: string - NativePath: string - } - module internal ProjectFile = let fsxExt = ".fsx" let csxExt = ".csx" - let findLoadsFromResolutions (resolutions: Resolution[]) = - resolutions - |> Array.filter (fun r -> - not ( - String.IsNullOrEmpty(r.NugetPackageId) - || String.IsNullOrEmpty(r.InitializeSourcePath) - ) - && File.Exists(r.InitializeSourcePath)) - |> Array.map (fun r -> r.InitializeSourcePath) - |> Array.distinct - - let findReferencesFromResolutions (resolutions: Resolution array) = - - let equals (s1: string) (s2: string) = - String.Compare(s1, s2, StringComparison.InvariantCultureIgnoreCase) = 0 - - resolutions - |> Array.filter (fun r -> - not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.FullPath)) - && not (equals r.IsNotImplementationReference "true") - && File.Exists(r.FullPath) - && equals r.AssetType "runtime") - |> Array.map (fun r -> r.FullPath) - |> Array.distinct - - let findIncludesFromResolutions (resolutions: Resolution[]) = - let managedRoots = - resolutions - |> Array.filter (fun r -> - not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.PackageRoot)) - && Directory.Exists(r.PackageRoot)) - |> Array.map (fun r -> r.PackageRoot) - - let nativeRoots = - resolutions - |> Array.filter (fun r -> not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.NativePath))) - |> Array.map (fun r -> - if Directory.Exists(r.NativePath) then - Some r.NativePath - elif File.Exists(r.NativePath) then - Some(Path.GetDirectoryName(r.NativePath).Replace('\\', '/')) - else - None) - |> Array.filter (fun r -> r.IsSome) - |> Array.map (fun r -> r.Value) - - Array.concat [| managedRoots; nativeRoots |] |> Array.distinct - - let getResolutionsFromFile resolutionsFile = - - let lines = - try - File - .ReadAllText(resolutionsFile) - .Split([| '\r'; '\n' |], StringSplitOptions.None) - |> Array.filter (fun line -> not (String.IsNullOrEmpty(line))) - with _ -> - [||] - - [| - for line in lines do - let fields = line.Split(',') - - if fields.Length < 8 then - raise (InvalidOperationException(sprintf "Internal error - Invalid resolutions file format '%s'" line)) - else - { - NugetPackageId = fields[0] - NugetPackageVersion = fields[1] - PackageRoot = fields[2] - FullPath = fields[3] - AssetType = fields[4] - IsNotImplementationReference = fields[5] - InitializeSourcePath = fields[6] - NativePath = fields[7] - } - |] - let makeScriptFromReferences (references: string seq) poundRprefix = let expandReferences = references diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs index faa0c126948..e6868860a30 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs @@ -5,6 +5,7 @@ open System open System.Diagnostics open System.IO open System.Reflection +open System.Security.Cryptography open FSDependencyManager open Internal.Utilities.FSharpEnvironment @@ -12,6 +13,19 @@ open Internal.Utilities.FSharpEnvironment type DependencyManagerAttribute() = inherit Attribute() +// Resolved assembly information +type Resolution = + { + NugetPackageId: string + NugetPackageVersion: string + PackageRoot: string + FullPath: string + AssetType: string + IsNotImplementationReference: string + InitializeSourcePath: string + NativePath: string + } + /// The result of building the package resolution files. type PackageBuildResolutionResult = { @@ -20,10 +34,93 @@ type PackageBuildResolutionResult = stdOut: string array stdErr: string array resolutionsFile: string option + resolutions: Resolution[] + references: string list + loads: string list + includes: string list } module internal Utilities = + let verifyFilesExist files = + files |> List.tryFind (fun f -> not (File.Exists(f))) |> Option.isNone + + let findLoadsFromResolutions (resolutions: Resolution[]) = + resolutions + |> Array.filter (fun r -> + not ( + String.IsNullOrEmpty(r.NugetPackageId) + || String.IsNullOrEmpty(r.InitializeSourcePath) + ) + && File.Exists(r.InitializeSourcePath)) + |> Array.map (fun r -> r.InitializeSourcePath) + |> Array.distinct + + let findReferencesFromResolutions (resolutions: Resolution array) = + let equals (s1: string) (s2: string) = + String.Compare(s1, s2, StringComparison.InvariantCultureIgnoreCase) = 0 + + resolutions + |> Array.filter (fun r -> + not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.FullPath)) + && not (equals r.IsNotImplementationReference "true") + && File.Exists(r.FullPath) + && equals r.AssetType "runtime") + |> Array.map (fun r -> r.FullPath) + |> Array.distinct + + let findIncludesFromResolutions (resolutions: Resolution[]) = + let managedRoots = + resolutions + |> Array.filter (fun r -> + not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.PackageRoot)) + && Directory.Exists(r.PackageRoot)) + |> Array.map (fun r -> r.PackageRoot) + + let nativeRoots = + resolutions + |> Array.filter (fun r -> not (String.IsNullOrEmpty(r.NugetPackageId) || String.IsNullOrEmpty(r.NativePath))) + |> Array.map (fun r -> + if Directory.Exists(r.NativePath) then + Some r.NativePath + elif File.Exists(r.NativePath) then + Some(Path.GetDirectoryName(r.NativePath).Replace('\\', '/')) + else + None) + |> Array.filter (fun r -> r.IsSome) + |> Array.map (fun r -> r.Value) + + Array.concat [| managedRoots; nativeRoots |] |> Array.distinct + + let getResolutionsFromFile resolutionsFile = + let lines = + try + File + .ReadAllText(resolutionsFile) + .Split([| '\r'; '\n' |], StringSplitOptions.None) + |> Array.filter (fun line -> not (String.IsNullOrEmpty(line))) + with _ -> + [||] + + [| + for line in lines do + let fields = line.Split(',') + + if fields.Length < 8 then + raise (InvalidOperationException(sprintf "Internal error - Invalid resolutions file format '%s'" line)) + else + { + NugetPackageId = fields[0] + NugetPackageVersion = fields[1] + PackageRoot = fields[2] + FullPath = fields[3] + AssetType = fields[4] + IsNotImplementationReference = fields[5] + InitializeSourcePath = fields[6] + NativePath = fields[7] + } + |] + /// Return a string array delimited by commas /// Note that a quoted string is not going to be mangled into pieces. let trimChars = [| ' '; '\t'; '\''; '\"' |] @@ -160,11 +257,15 @@ module internal Utilities = let outputFile = projectPath + ".resolvedReferences.paths" - let resolutionsFile = + let resolutionsFile, resolutions, references, loads, includes = if success && File.Exists(outputFile) then - Some outputFile + let resolutions = getResolutionsFromFile outputFile + let references = (findReferencesFromResolutions resolutions) |> Array.toList + let loads = (findLoadsFromResolutions resolutions) |> Array.toList + let includes = (findIncludesFromResolutions resolutions) |> Array.toList + (Some outputFile), resolutions, references, loads, includes else - None + None, [||], List.empty, List.empty, List.empty { success = success @@ -172,6 +273,10 @@ module internal Utilities = stdOut = stdOut stdErr = stdErr resolutionsFile = resolutionsFile + resolutions = resolutions + references = references + loads = loads + includes = includes } let generateSourcesFromNugetConfigs scriptDirectory workingDir timeout = @@ -206,3 +311,5 @@ module internal Utilities = if pos >= 0 then yield ("i", source.Substring(pos).Trim()) } + + let computeSha256HashOfBytes (bytes: byte[]) : byte[] = SHA256.Create().ComputeHash(bytes) diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs index e5f8cd95704..294cf2df55d 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fs @@ -6,6 +6,7 @@ open System open System.Collections.Concurrent open System.Diagnostics open System.IO +open System.Text open FSharp.DependencyManager.Nuget open FSharp.DependencyManager.Nuget.Utilities open FSharp.DependencyManager.Nuget.ProjectFile @@ -197,6 +198,57 @@ module FSharpDependencyManager = |> List.distinct |> (fun l -> l, binLogPath, timeout) + let computeHashForResolutionInputs + ( + scriptExt: string, + directiveLines: (string * string) seq, + targetFrameworkMoniker: string, + runtimeIdentifier: string + ) : string option = + + let packageReferences, _, _ = + directiveLines |> List.ofSeq |> parsePackageDirective scriptExt + + let referencesHaveWildCardVersion = + // Verify to see if the developer specified a wildcard version. If they did then caching is not possible + let hasWildCardVersion p = + // Todo: named record please + let { + Include = package + Version = ver + RestoreSources = _ + Script = _ + } = + p + + not (String.IsNullOrWhiteSpace(package)) + && (not (String.IsNullOrWhiteSpace(ver)) && ver.Contains("*")) + + packageReferences |> List.tryFind hasWildCardVersion |> Option.isSome + + if referencesHaveWildCardVersion then + // We have wildcard references so no caching can apply + None + else + let packageReferenceText = + packageReferences + |> List.map formatPackageReference + |> Seq.concat + |> Seq.distinct + |> Seq.toArray + |> Seq.sort + |> Seq.fold (fun acc s -> acc + s) "" + + let value = + $"""Tfm={targetFrameworkMoniker}:Rid={runtimeIdentifier}:PackageReferences={packageReferenceText}:Ext={match scriptExt with + | ".csx" -> csxExt + | _ -> fsxExt}""" + + Some( + computeSha256HashOfBytes (Encoding.Unicode.GetBytes(value)) + |> Array.fold (fun acc byte -> acc + $"%02x{byte}") "" + ) + /// The results of ResolveDependencies type ResolveDependenciesResult ( @@ -237,45 +289,48 @@ type ResolveDependenciesResult member _.Roots = roots [] -type FSharpDependencyManager(outputDirectory: string option) = +type FSharpDependencyManager(outputDirectory: string option, useResultsCache: bool) = let key = "nuget" let name = "MsBuild Nuget DependencyManager" let generatedScripts = ConcurrentDictionary() - let workingDirectory = + let projectDirectory, cacheDirectory = + let createDirectory directory = + lazy + try + if not (Directory.Exists(directory)) then + Directory.CreateDirectory(directory) |> ignore + + directory + with _ -> + directory + // Calculate the working directory for dependency management // if a path wasn't supplied to the dependency manager then use the temporary directory as the root // if a path was supplied if it was rooted then use the rooted path as the root // if the path wasn't supplied or not rooted use the temp directory as the root. - let directory = - let path = - Path.Combine(Process.GetCurrentProcess().Id.ToString() + "--" + Guid.NewGuid().ToString()) + let specialDir = + Path.Combine(Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), ".packagemanagement", "nuget") - match outputDirectory with - | None -> Path.Combine(Path.GetTempPath(), path) - | Some v -> - if Path.IsPathRooted(v) then - Path.Combine(v, path) - else - Path.Combine(Path.GetTempPath(), path) + let path = + Path.Combine(Process.GetCurrentProcess().Id.ToString() + "--" + Guid.NewGuid().ToString()) - lazy - try - if not (Directory.Exists(directory)) then - Directory.CreateDirectory(directory) |> ignore + let root = + match outputDirectory with + | Some v when Path.IsPathRooted(v) -> v + | Some v -> Path.Combine(specialDir, v) + | _ -> specialDir - directory - with _ -> - directory + createDirectory (Path.Combine(root, "Projects", path)), createDirectory (Path.Combine(root, "Cache")) let deleteScripts () = try #if !DEBUG - if workingDirectory.IsValueCreated then - if Directory.Exists(workingDirectory.Value) then - Directory.Delete(workingDirectory.Value, true) + if projectDirectory.IsValueCreated then + if Directory.Exists(projectDirectory.Value) then + Directory.Delete(projectDirectory.Value, true) #else () #endif @@ -315,7 +370,7 @@ type FSharpDependencyManager(outputDirectory: string option) = let packageReferenceText = String.Join(Environment.NewLine, packageReferenceLines) - let projectPath = Path.Combine(workingDirectory.Value, "Project.fsproj") + let projectPath = Path.Combine(projectDirectory.Value, "Project.fsproj") let generateAndBuildProjectArtifacts = let writeFile path body = @@ -339,8 +394,43 @@ type FSharpDependencyManager(outputDirectory: string option) = generateAndBuildProjectArtifacts + let tryGetResultsForResolutionHash hash (projectDirectory: Lazy) : PackageBuildResolutionResult option = + match hash with + | Some hash when useResultsCache = true -> + let resolutionsFile = + Path.Combine(cacheDirectory.Value, (hash + ".resolvedReferences.paths")) + + if File.Exists(resolutionsFile) then + let resolutions, references, loads, includes = + let resolutions = getResolutionsFromFile resolutionsFile + let references = (findReferencesFromResolutions resolutions) |> Array.toList + let loads = (findLoadsFromResolutions resolutions) |> Array.toList + let includes = (findIncludesFromResolutions resolutions) |> Array.toList + resolutions, references, loads, includes + + if verifyFilesExist (references) then + Some + { + success = true + projectPath = Path.Combine(projectDirectory.Value, "Project.fsproj") + stdOut = [||] + stdErr = [||] + resolutionsFile = Some resolutionsFile + resolutions = resolutions + references = references + loads = loads + includes = includes + } + else + None + else + None + | _ -> None + do AppDomain.CurrentDomain.ProcessExit |> Event.add (fun _ -> deleteScripts ()) + new(outputDirectory: string option) = FSharpDependencyManager(outputDirectory, true) + member _.Name = name member _.Key = key @@ -357,6 +447,10 @@ type FSharpDependencyManager(outputDirectory: string option) = (SR.highestVersion ()) |] + member _.ClearResultsCache() = + Directory.Delete(cacheDirectory.Value, true) + Directory.CreateDirectory(cacheDirectory.Value) |> ignore + member _.ResolveDependencies ( scriptDirectory: string, @@ -376,34 +470,49 @@ type FSharpDependencyManager(outputDirectory: string option) = let generateAndBuildProjectArtifacts = let configIncludes = - generateSourcesFromNugetConfigs scriptDirectory workingDirectory.Value timeout + generateSourcesFromNugetConfigs scriptDirectory projectDirectory.Value timeout let directiveLines = Seq.append packageManagerTextLines configIncludes - let resolutionResult = - prepareDependencyResolutionFiles (scriptExt, directiveLines, targetFrameworkMoniker, runtimeIdentifier, timeout) + let resolutionHash = + FSharpDependencyManager.computeHashForResolutionInputs ( + scriptExt, + directiveLines, + targetFrameworkMoniker, + runtimeIdentifier + ) + + let fromCache, resolutionResult = + match tryGetResultsForResolutionHash resolutionHash projectDirectory with + | Some resolutionResult -> true, resolutionResult + | None -> + false, prepareDependencyResolutionFiles (scriptExt, directiveLines, targetFrameworkMoniker, runtimeIdentifier, timeout) match resolutionResult.resolutionsFile with | Some file -> - let resolutions = getResolutionsFromFile file - let references = (findReferencesFromResolutions resolutions) |> Array.toSeq + let generatedScriptPath = + match resolutionHash with + | Some hash -> Path.Combine(cacheDirectory.Value, hash) + scriptExt + | None -> resolutionResult.projectPath + scriptExt + + // We have succeeded to gather information -- generate script and copy the results to the cache + if not (fromCache) then + let generatedScriptBody = + makeScriptFromReferences resolutionResult.references poundRprefix - let scripts = - let generatedScriptPath = resolutionResult.projectPath + scriptExt - let generatedScriptBody = makeScriptFromReferences references poundRprefix emitFile generatedScriptPath generatedScriptBody - let loads = (findLoadsFromResolutions resolutions) |> Array.toList - List.concat [ [ generatedScriptPath ]; loads ] |> List.toSeq - let includes = (findIncludesFromResolutions resolutions) |> Array.toSeq + match resolutionHash with + | Some hash -> File.Copy(file, Path.Combine(cacheDirectory.Value, hash + ".resolvedReferences.paths"), true) + | None -> () ResolveDependenciesResult( resolutionResult.success, resolutionResult.stdOut, resolutionResult.stdErr, - references, - scripts, - includes + resolutionResult.references, + Seq.concat [ [ generatedScriptPath ]; resolutionResult.loads ], + resolutionResult.includes ) | None -> diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi index 5344b5fe6aa..56d037274af 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.fsi @@ -45,6 +45,7 @@ type ResolveDependenciesResult = [] type FSharpDependencyManager = + new: outputDirectory: string option * useResultsCache: bool -> FSharpDependencyManager new: outputDirectory: string option -> FSharpDependencyManager member Name: string @@ -53,6 +54,8 @@ type FSharpDependencyManager = member HelpMessages: string[] + member ClearResultsCache: unit -> unit + member ResolveDependencies: scriptDirectory: string * scriptName: string * diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs index cf4c136d61e..2d54bbbea11 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/DependencyManagerInteractiveTests.fs @@ -71,7 +71,7 @@ type DependencyManagerInteractiveTests() = let nativeProbingRoots () = Seq.empty - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -100,7 +100,7 @@ type DependencyManagerInteractiveTests() = let nativeProbingRoots () = Seq.empty - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -123,7 +123,7 @@ type DependencyManagerInteractiveTests() = let nativeProbingRoots () = Seq.empty - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -154,7 +154,7 @@ type DependencyManagerInteractiveTests() = let assemblyProbingPaths () = Seq.empty let nativeProbingRoots () = Seq.empty - use dp1 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp1 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -182,7 +182,7 @@ type DependencyManagerInteractiveTests() = Assert.Equal(1, result2.Roots |> Seq.length) Assert.True((result2.Roots |> Seq.head).EndsWith("/fsharp.data/3.3.3/")) - use dp2 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp2 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm2 = dp2.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then @@ -209,7 +209,7 @@ type DependencyManagerInteractiveTests() = let nativeProbingRoots () = Seq.empty - use dp1 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp1 = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -285,7 +285,7 @@ TorchSharp.Tensor.LongTensor.From([| 0L .. 100L |]).Device // Restore packages, Get Reference dll paths and package roots let result = - use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") dp.Resolve(idm, ".fsx", packagemanagerlines, reportError, "net6.0") @@ -350,7 +350,7 @@ printfn ""%A"" result let scriptText = code.Replace("$(REFERENCES)", referenceText) // Use the dependency manager to resolve assemblies and native paths - use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots), false) use script = new FSharpScript() let opt = script.Eval(scriptText) |> getValue @@ -381,7 +381,7 @@ printfn ""%A"" result // Restore packages, Get Reference dll paths and package roots let result = - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") dp.Resolve(idm, ".fsx", packagemanagerlines, reportError, "net6.0") @@ -462,7 +462,7 @@ printfn ""%A"" result // Restore packages, Get Reference dll paths and package roots let result = - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") dp.Resolve(idm, ".fsx", packagemanagerlines, reportError, "net6.0") @@ -519,7 +519,7 @@ x |> Seq.iter(fun r -> // Restore packages, Get Reference dll paths and package roots let result = - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") dp.Resolve(idm, ".fsx", packagemanagerlines, reportError, "net6.0") @@ -545,7 +545,7 @@ x |> Seq.iter(fun r -> // Restore packages, Get Reference dll paths and package roots let result = - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") dp.Resolve(idm, ".csx", packagemanagerlines, reportError, "net6.0") @@ -569,7 +569,7 @@ x |> Seq.iter(fun r -> // Set up native resolver to resolve dll's do - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) // Invoking a non-existent dll via pinvoke cause a probe. which should invoke the call back try Native.NoneSuch() |> ignore with _ -> () @@ -613,7 +613,7 @@ x |> Seq.iter(fun r -> // Set up native resolver to resolve dll's do - use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(AssemblyResolutionProbe(assemblyProbingPaths), NativeResolutionProbe(nativeProbingRoots), false) // Invoking a non-existent dll via pinvoke cause a probe. which should invoke the call back try Native.NoneSuch() |> ignore with _ -> () @@ -678,7 +678,7 @@ x |> Seq.iter(fun r -> let mutable finalPath:string = null do initialPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH")) - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") let mutable currentPath:string = null if RuntimeInformation.IsOSPlatform(OSPlatform.Windows) then @@ -695,7 +695,7 @@ x |> Seq.iter(fun r -> do initialPath <- appendSemiColon (Environment.GetEnvironmentVariable("PATH")) let mutable currentPath:string = null - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") let result = dp.Resolve(idm, ".fsx", [|"r", "Microsoft.Data.Sqlite,3.1.7"|], reportError, "net6.0") Assert.Equal(true, result.Success) @@ -810,7 +810,7 @@ x |> Seq.iter(fun r -> let mutable foundCorrectError = false let mutable foundWrongError = false - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -833,7 +833,7 @@ x |> Seq.iter(fun r -> let mutable foundCorrectError = false let mutable foundWrongError = false - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -856,7 +856,7 @@ x |> Seq.iter(fun r -> let mutable foundCorrectError = false let mutable foundWrongError = false - use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots)) + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), false) let reportError = let report errorType code message = match errorType with @@ -873,4 +873,38 @@ x |> Seq.iter(fun r -> Assert.Equal(foundWrongError, false) () + + [] + member _.``Verify that clear cache doesn't fail and clears the cache``() = + let nativeProbingRoots () = Seq.empty + let mutable foundCorrectError = false + let mutable foundWrongError = false + + use dp = new DependencyProvider(NativeResolutionProbe(nativeProbingRoots), true) + let reportError = + let report errorType code message = + match errorType with + | ErrorReportType.Error -> + if code = 3217 then foundCorrectError <- true + else foundWrongError <- true + | ErrorReportType.Warning -> printfn "PackageManagementWarning %d : %s" code message + ResolvingErrorReport (report) + + let idm = dp.TryFindDependencyManagerByKey(Seq.empty, "", reportError, "nuget") + + // Resolve and cache the results won't time out + let result = dp.Resolve(idm, ".fsx", [|"r", "FSharp.Data,3.3.3"; "r", "timeout=10000"|], reportError, "net6.0", null, "", "", "", -1) // Wait forever + + // Clear the results + foundCorrectError <- false + foundWrongError <- false + + // Now clear the cache --- this will ensure that resolving produces a timeout error. If we read from the cache the test will fail + dp.ClearResultsCache(Seq.empty, "", reportError) + + let result = dp.Resolve(idm, ".fsx", [|"r", "FSharp.Data,3.3.3"; "r", "timeout=0"|], reportError, "net6.0", null, "", "", "", -1) // Wait forever + Assert.Equal(false, result.Success) + Assert.Equal(foundCorrectError, true) + Assert.Equal(foundWrongError, false) + () diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected index 6b3cb7e4f8a..7f2f7e46280 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.CompilerService.SurfaceArea.netstandard.expected @@ -2216,7 +2216,10 @@ FSharp.Compiler.DependencyManager.DependencyProvider: System.Tuple`2[System.Int3 FSharp.Compiler.DependencyManager.DependencyProvider: System.Tuple`2[System.String,FSharp.Compiler.DependencyManager.IDependencyManagerProvider] TryFindDependencyManagerInPath(System.Collections.Generic.IEnumerable`1[System.String], System.String, FSharp.Compiler.DependencyManager.ResolvingErrorReport, System.String) FSharp.Compiler.DependencyManager.DependencyProvider: Void .ctor() FSharp.Compiler.DependencyManager.DependencyProvider: Void .ctor(FSharp.Compiler.DependencyManager.AssemblyResolutionProbe, FSharp.Compiler.DependencyManager.NativeResolutionProbe) +FSharp.Compiler.DependencyManager.DependencyProvider: Void .ctor(FSharp.Compiler.DependencyManager.AssemblyResolutionProbe, FSharp.Compiler.DependencyManager.NativeResolutionProbe, Boolean) FSharp.Compiler.DependencyManager.DependencyProvider: Void .ctor(FSharp.Compiler.DependencyManager.NativeResolutionProbe) +FSharp.Compiler.DependencyManager.DependencyProvider: Void .ctor(FSharp.Compiler.DependencyManager.NativeResolutionProbe, Boolean) +FSharp.Compiler.DependencyManager.DependencyProvider: Void ClearResultsCache(System.Collections.Generic.IEnumerable`1[System.String], System.String, FSharp.Compiler.DependencyManager.ResolvingErrorReport) FSharp.Compiler.DependencyManager.ErrorReportType FSharp.Compiler.DependencyManager.ErrorReportType+Tags: Int32 Error FSharp.Compiler.DependencyManager.ErrorReportType+Tags: Int32 Warning @@ -2248,6 +2251,7 @@ FSharp.Compiler.DependencyManager.IDependencyManagerProvider: System.String get_ FSharp.Compiler.DependencyManager.IDependencyManagerProvider: System.String get_Name() FSharp.Compiler.DependencyManager.IDependencyManagerProvider: System.String[] HelpMessages FSharp.Compiler.DependencyManager.IDependencyManagerProvider: System.String[] get_HelpMessages() +FSharp.Compiler.DependencyManager.IDependencyManagerProvider: Void ClearResultsCache() FSharp.Compiler.DependencyManager.IResolveDependenciesResult FSharp.Compiler.DependencyManager.IResolveDependenciesResult: Boolean Success FSharp.Compiler.DependencyManager.IResolveDependenciesResult: Boolean get_Success() diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl index 845f9443070..d7ab69ca858 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl @@ -90,6 +90,8 @@ Usage: fsharpi [script.fsx []] this assembly. Valid values are mscorlib, netcore or netstandard. Default - mscorlib +--clearResultsCache Clear the package manager results + cache --noframework Do not reference the default CLI assemblies by default --exec Exit fsi after loading the files or diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl index f0af4efa4b2..a7f1930e318 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl @@ -90,6 +90,8 @@ Usage: fsiAnyCpu [script.fsx []] this assembly. Valid values are mscorlib, netcore or netstandard. Default - mscorlib +--clearResultsCache Clear the package manager results + cache --noframework Do not reference the default CLI assemblies by default --exec Exit fsi after loading the files or diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl index 34c66f728ed..65a00be5c48 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl @@ -92,6 +92,8 @@ Usage: fsiAnyCpu [script.fsx []] this assembly. Valid values are mscorlib, netcore or netstandard. Default - mscorlib +--clearResultsCache Clear the package manager results + cache --noframework Do not reference the default CLI assemblies by default --exec Exit fsi after loading the files or