diff --git a/fcs/.gitignore b/fcs/.gitignore new file mode 100644 index 00000000000..09ed5be8e7e --- /dev/null +++ b/fcs/.gitignore @@ -0,0 +1 @@ +/FSharp.Compiler.Service.netstandard diff --git a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj index 0ebd29eea3d..6f54345b348 100644 --- a/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj +++ b/fcs/FSharp.Compiler.Service.netstandard/FSharp.Compiler.Service.netstandard.fsproj @@ -500,6 +500,12 @@ CodeGen/IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + Driver/CompileOps.fsi diff --git a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index eebbaa0a931..dfaf75dcc8b 100644 --- a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj +++ b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj @@ -475,6 +475,12 @@ CodeGen/IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + Driver/CompileOps.fsi diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs index abc321272d5..62168e09f02 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.fs @@ -4300,6 +4300,15 @@ type internal SR private() = /// An error occurred while reading the F# metadata of assembly '%s'. A reserved construct was utilized. You may need to upgrade your F# compiler or use an earlier version of the assembly that doesn't make use of a specific construct. /// (Originally from ..\FSComp.txt:1424) static member pickleUnexpectedNonZero(a0 : System.String) = (3219, GetStringFunc("pickleUnexpectedNonZero",",,,%s,,,") a0) + /// The dependency manager extension '%s' could not be loaded. Message: %s + /// (Originally from ..\FSComp.txt:1425) + static member couldNotLoadDependencyManagerExtenstion(a0 : System.String, a1 : System.String) = (3220, GetStringFunc("couldNotLoadDependencyManagerExtenstion",",,,%s,,,%s,,,") a0 a1) + /// Package manager key '%s' was not registered in %s. Currently registered: %s + /// (Originally from ..\FSComp.txt:1426) + static member packageManagerUnknown(a0 : System.String, a1 : System.String, a2 : System.String) = (3221, GetStringFunc("packageManagerUnknown",",,,%s,,,%s,,,%s,,,") a0 a1 a2) + /// %s + /// (Originally from ..\FSComp.txt:1427) + static member packageManagerError(a0 : System.String) = (3222, GetStringFunc("packageManagerError",",,,%s,,,") a0) /// Call this method once to validate that all known resources are valid; throws if not static member RunStartupValidation() = @@ -5698,4 +5707,7 @@ type internal SR private() = ignore(GetString("notAFunctionButMaybeDeclaration")) ignore(GetString("ArgumentsInSigAndImplMismatch")) ignore(GetString("pickleUnexpectedNonZero")) + ignore(GetString("couldNotLoadDependencyManagerExtenstion")) + ignore(GetString("packageManagerUnknown")) + ignore(GetString("packageManagerError")) () diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx index 71f965fc9fa..fe54353fcdd 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx +++ b/src/buildfromsource/FSharp.Compiler.Private/FSComp.resx @@ -4303,4 +4303,13 @@ An error occurred while reading the F# metadata of assembly '{0}'. A reserved construct was utilized. You may need to upgrade your F# compiler or use an earlier version of the assembly that doesn't make use of a specific construct. + + The dependency manager extension {0} could not be loaded. Message: {1} + + + Package manager key '{0}' was not registered in {1}. Currently registered: {2} + + + {0} + \ No newline at end of file diff --git a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj index 6e4a625372c..2c761f10279 100644 --- a/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj +++ b/src/buildfromsource/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj @@ -454,7 +454,12 @@ CodeGen\IlxGen.fs - + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + Driver\CompileOps.fsi @@ -628,7 +633,6 @@ - @@ -638,6 +642,7 @@ + diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 0cab042759a..f91942b029e 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -77,7 +77,6 @@ let FSharpScriptFileSuffixes = [".fsscript";".fsx"] let doNotRequireNamespaceOrModuleSuffixes = [".mli";".ml"] @ FSharpScriptFileSuffixes let FSharpLightSyntaxFileSuffixes : string list = [ ".fs";".fsscript";".fsx";".fsi" ] - //---------------------------------------------------------------------------- // ERROR REPORTING //-------------------------------------------------------------------------- @@ -2200,8 +2199,9 @@ type TcConfigBuilder = mutable implicitlyResolveAssemblies: bool mutable light: bool option mutable conditionalCompilationDefines: string list - mutable loadedSources: (range * string) list + mutable loadedSources: (range * string * string) list mutable referencedDLLs : AssemblyReference list + mutable packageManagerLines : Map mutable projectReferences : IProjectReference list mutable knownUnresolvedReferences : UnresolvedAssemblyReference list optimizeForMemory: bool @@ -2361,6 +2361,7 @@ type TcConfigBuilder = framework = true implicitlyResolveAssemblies = true referencedDLLs = [] + packageManagerLines = Map.empty projectReferences = [] knownUnresolvedReferences = [] loadedSources = [] @@ -2563,18 +2564,19 @@ type TcConfigBuilder = if ok && not (List.contains absolutePath tcConfigB.includes) then tcConfigB.includes <- tcConfigB.includes ++ absolutePath - member tcConfigB.AddLoadedSource(m, path, pathLoadedFrom) = - if FileSystem.IsInvalidPathShim(path) then - warning(Error(FSComp.SR.buildInvalidFilename(path), m)) + member tcConfigB.AddLoadedSource(m, originalPath, pathLoadedFrom) = + if FileSystem.IsInvalidPathShim(originalPath) then + warning(Error(FSComp.SR.buildInvalidFilename(originalPath), m)) else let path = - match TryResolveFileUsingPaths(tcConfigB.includes @ [pathLoadedFrom], m, path) with + match TryResolveFileUsingPaths(tcConfigB.includes @ [pathLoadedFrom], m, originalPath) with | Some(path) -> path | None -> - // File doesn't exist in the paths. Assume it will be in the load-ed from directory. - ComputeMakePathAbsolute pathLoadedFrom path - if not (List.contains path (List.map snd tcConfigB.loadedSources)) then - tcConfigB.loadedSources <- tcConfigB.loadedSources ++ (m, path) + // File doesn't exist in the paths. Assume it will be in the load-ed from directory. + ComputeMakePathAbsolute pathLoadedFrom originalPath + if not (List.contains path (List.map (fun (_, _, path) -> path) tcConfigB.loadedSources)) then + tcConfigB.loadedSources <- tcConfigB.loadedSources ++ (m, originalPath, path) + member tcConfigB.AddEmbeddedSourceFile (file) = tcConfigB.embedSourceList <- tcConfigB.embedSourceList ++ file @@ -2589,6 +2591,13 @@ type TcConfigBuilder = let projectReference = tcConfigB.projectReferences |> List.tryPick (fun pr -> if pr.FileName = path then Some pr else None) tcConfigB.referencedDLLs <- tcConfigB.referencedDLLs ++ AssemblyReference(m, path, projectReference) + member tcConfigB.AddDependencyManagerText (packageManager:DependencyManagerIntegration.IDependencyManagerProvider,m,path:string) = + let path = DependencyManagerIntegration.removeDependencyManagerKey packageManager.Key path + + match tcConfigB.packageManagerLines |> Map.tryFind packageManager.Key with + | Some lines -> tcConfigB.packageManagerLines <- Map.add packageManager.Key (lines ++ (path, m)) tcConfigB.packageManagerLines + | _ -> tcConfigB.packageManagerLines <- Map.add packageManager.Key [path, m] tcConfigB.packageManagerLines + member tcConfigB.RemoveReferencedAssemblyByPath (m, path) = tcConfigB.referencedDLLs <- tcConfigB.referencedDLLs |> List.filter (fun ar-> ar.Range <> m || ar.Text <> path) @@ -2889,6 +2898,7 @@ type TcConfig private (data : TcConfigBuilder, validate:bool) = member x.embedAllSource = data.embedAllSource member x.embedSourceList = data.embedSourceList member x.sourceLink = data.sourceLink + member x.packageManagerLines = data.packageManagerLines member x.ignoreSymbolStoreSequencePoints = data.ignoreSymbolStoreSequencePoints member x.internConstantStrings = data.internConstantStrings member x.extraOptimizationIterations = data.extraOptimizationIterations @@ -3022,13 +3032,23 @@ type TcConfig private (data : TcConfigBuilder, validate:bool) = member tcConfig.GetAvailableLoadedSources() = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parameter - let resolveLoadedSource (m, path) = + let resolveLoadedSource (m, originalPath, path) = try if not(FileSystem.SafeExists(path)) then - error(LoadedSourceNotFoundIgnoring(path, m)) - None - else Some(m, path) + let secondTrial = + tcConfig.includes + |> List.tryPick (fun root -> + let path = ComputeMakePathAbsolute root originalPath + if FileSystem.SafeExists(path) then Some path else None) + + match secondTrial with + | Some path -> Some(m,path) + | None -> + error(LoadedSourceNotFoundIgnoring(path,m)) + None + else Some(m,path) with e -> errorRecovery e m; None + tcConfig.loadedSources |> List.choose resolveLoadedSource |> List.distinct @@ -4844,11 +4864,10 @@ let RequireDLL (ctok, tcImports:TcImports, tcEnv, thisAssemblyName, m, file) = let tcEnv = (tcEnv, asms) ||> List.fold (fun tcEnv asm -> AddCcuToTcEnv(g, amap, m, tcEnv, thisAssemblyName, asm.FSharpViewOfMetadata, asm.AssemblyAutoOpenAttributes, asm.AssemblyInternalsVisibleToAttributes)) tcEnv, (dllinfos, asms) - - let ProcessMetaCommandsFromInput - (nowarnF: 'state -> range * string -> 'state, - dllRequireF: 'state -> range * string -> 'state, + (nowarnF: 'state -> range * string -> 'state, + dllRequireF: 'state -> range * string -> 'state, + packageRequireF: 'state -> DependencyManagerIntegration.IDependencyManagerProvider * range * string -> 'state, loadSourceF: 'state -> range * string -> unit) (tcConfig:TcConfigBuilder, inp, pathOfMetaCommandSource, state0) = @@ -4874,15 +4893,22 @@ let ProcessMetaCommandsFromInput | _ -> errorR(Error(FSComp.SR.buildInvalidHashIDirective(), m)) state - | ParsedHashDirective("nowarn", numbers, m) -> - List.fold (fun state d -> nowarnF state (m, d)) state numbers - | ParsedHashDirective(("reference" | "r"), args, m) -> + | ParsedHashDirective("nowarn",numbers,m) -> + List.fold (fun state d -> nowarnF state (m,d)) state numbers + + | ParsedHashDirective(("reference" | "r"),args,m) -> if not canHaveScriptMetaCommands then errorR(HashReferenceNotAllowedInNonScript(m)) match args with | [path] -> matchedm<-m - dllRequireF state (m, path) + match DependencyManagerIntegration.tryFindDependencyManagerInPath m (path:string) with + | DependencyManagerIntegration.ReferenceType.RegisteredDependencyManager packageManager -> + packageRequireF state (packageManager,m,path) + | DependencyManagerIntegration.ReferenceType.Library path -> + dllRequireF state (m,path) + | DependencyManagerIntegration.ReferenceType.UnknownType -> + state // error already reported | _ -> errorR(Error(FSComp.SR.buildInvalidHashrDirective(), m)) state @@ -4959,19 +4985,21 @@ let ProcessMetaCommandsFromInput let ApplyNoWarnsToTcConfig (tcConfig:TcConfig, inp:ParsedInput, pathOfMetaCommandSource) = // Clone let tcConfigB = tcConfig.CloneOfOriginalBuilder - let addNoWarn = fun () (m, s) -> tcConfigB.TurnWarningOff(m, s) - let addReferencedAssemblyByPath = fun () (_m, _s) -> () - let addLoadedSource = fun () (_m, _s) -> () - ProcessMetaCommandsFromInput (addNoWarn, addReferencedAssemblyByPath, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + let addNoWarn = fun () (m,s) -> tcConfigB.TurnWarningOff(m, s) + let addReferencedAssemblyByPath = fun () (_m,_s) -> () + let addDependencyManagerText = fun () (_prefix,_m,_s) -> () + let addLoadedSource = fun () (_m,_s) -> () + ProcessMetaCommandsFromInput (addNoWarn, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) let ApplyMetaCommandsFromInputToTcConfig (tcConfig:TcConfig, inp:ParsedInput, pathOfMetaCommandSource) = // Clone let tcConfigB = tcConfig.CloneOfOriginalBuilder let getWarningNumber = fun () _ -> () - let addReferencedAssemblyByPath = fun () (m, s) -> tcConfigB.AddReferencedAssemblyByPath(m, s) - let addLoadedSource = fun () (m, s) -> tcConfigB.AddLoadedSource(m, s, pathOfMetaCommandSource) - ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + let addReferencedAssemblyByPath = fun () (m,s) -> tcConfigB.AddReferencedAssemblyByPath(m,s) + let addDependencyManagerText = fun () (packageManager, m,s) -> tcConfigB.AddDependencyManagerText(packageManager,m,s) + let addLoadedSource = fun () (m,s) -> tcConfigB.AddLoadedSource(m,s,pathOfMetaCommandSource) + ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) //---------------------------------------------------------------------------- @@ -5019,9 +5047,9 @@ type CodeContext = | CompilationAndEvaluation // in fsi.exe | Compilation // in fsc.exe | Editing // in VS - -module private ScriptPreprocessClosure = + +module ScriptPreprocessClosure = open Internal.Utilities.Text.Lexing /// Represents an input to the closure finding process @@ -5097,11 +5125,12 @@ module private ScriptPreprocessClosure = let ApplyMetaCommandsFromInputToTcConfigAndGatherNoWarn (tcConfig:TcConfig, inp:ParsedInput, pathOfMetaCommandSource) = let tcConfigB = tcConfig.CloneOfOriginalBuilder let nowarns = ref [] - let getWarningNumber = fun () (m, s) -> nowarns := (s, m) :: !nowarns - let addReferencedAssemblyByPath = fun () (m, s) -> tcConfigB.AddReferencedAssemblyByPath(m, s) - let addLoadedSource = fun () (m, s) -> tcConfigB.AddLoadedSource(m, s, pathOfMetaCommandSource) + let getWarningNumber = fun () (m,s) -> nowarns := (s,m) :: !nowarns + let addReferencedAssemblyByPath = fun () (m,s) -> tcConfigB.AddReferencedAssemblyByPath(m,s) + let addDependencyManagerText = fun () (packageManagerPrefix,m,s) -> tcConfigB.AddDependencyManagerText(packageManagerPrefix,m,s) + let addLoadedSource = fun () (m,s) -> tcConfigB.AddLoadedSource(m,s,pathOfMetaCommandSource) try - ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) with ReportedError _ -> // Recover by using whatever did end up in the tcConfig () @@ -5113,12 +5142,50 @@ module private ScriptPreprocessClosure = let tcConfigB = tcConfig.CloneOfOriginalBuilder TcConfig.Create(tcConfigB, validate=false), nowarns - let FindClosureFiles(closureSources, tcConfig:TcConfig, codeContext, lexResourceManager:Lexhelp.LexResourceManager) = - let tcConfig = ref tcConfig + let FindClosureFiles(mainFile, _m, closureSources, origTcConfig:TcConfig, codeContext, lexResourceManager:Lexhelp.LexResourceManager) = + let tcConfig = ref origTcConfig let observedSources = Observed() - let rec loop (ClosureSource(filename, m, source, parseRequired)) = - [ if not (observedSources.HaveSeen(filename)) then + let loadScripts = HashSet<_>() + + // Resolve the packages + let rec resolveDependencyManagerSources scriptName = + if not (loadScripts.Contains scriptName) then + [ for kv in tcConfig.Value.packageManagerLines do + let packageManagerKey,packageManagerLines = kv.Key,kv.Value + match packageManagerLines with + | [] -> () + | (_,m)::_ -> + match origTcConfig.packageManagerLines |> Map.tryFind packageManagerKey with + | Some oldDependencyManagerLines when oldDependencyManagerLines = packageManagerLines -> () + | _ -> + match DependencyManagerIntegration.tryFindDependencyManagerByKey m packageManagerKey with + | None -> + errorR(DependencyManagerIntegration.createPackageManagerUnknownError packageManagerKey m) + | Some packageManager -> + let packageManagerTextLines = packageManagerLines |> List.map fst + match DependencyManagerIntegration.resolve packageManager tcConfig.Value.implicitIncludeDir mainFile scriptName m packageManagerTextLines with + | None -> () // error already reported + | Some (loadScript,additionalIncludeFolders) -> + // This may incrementally update tcConfig too with new #r references + // New package text is ignored on this second phase + + if not (isNil additionalIncludeFolders) then + let tcConfigB = tcConfig.Value.CloneOfOriginalBuilder + for folder in additionalIncludeFolders do + tcConfigB.AddIncludePath(m,folder,"") + tcConfig := TcConfig.Create(tcConfigB, validate=false) + + match loadScript with + | Some loadScript -> + let loadScriptText = File.ReadAllText loadScript + loadScripts.Add loadScript |> ignore + yield! loop (ClosureSource(loadScript,m,loadScriptText,true)) + | None -> () ] + else [] + + and loop (ClosureSource(filename, m, source, parseRequired)) = + [ if not (observedSources.HaveSeen(filename)) then observedSources.SetSeen(filename) //printfn "visiting %s" filename if IsScript(filename) || parseRequired then @@ -5133,15 +5200,20 @@ module private ScriptPreprocessClosure = let errorLogger = CapturingErrorLogger("FindClosureMetaCommands") use _unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _ -> errorLogger) let pathOfMetaCommandSource = Path.GetDirectoryName(filename) + let preSources = (!tcConfig).GetAvailableLoadedSources() let tcConfigResult, noWarns = ApplyMetaCommandsFromInputToTcConfigAndGatherNoWarn (!tcConfig, parsedScriptAst, pathOfMetaCommandSource) tcConfig := tcConfigResult // We accumulate the tcConfig in order to collect assembly references - + + yield! resolveDependencyManagerSources filename + let postSources = (!tcConfig).GetAvailableLoadedSources() let sources = if preSources.Length < postSources.Length then postSources.[preSources.Length..] else [] - //for (_, subFile) in sources do + yield! resolveDependencyManagerSources filename + + //for (_,subFile) in sources do // printfn "visiting %s - has subsource of %s " filename subFile for (m, subFile) in sources do @@ -5162,7 +5234,9 @@ module private ScriptPreprocessClosure = //printfn "yielding non-script source %s" filename yield ClosureFile(filename, m, None, [], [], []) ] - closureSources |> List.collect loop, !tcConfig + let sources = closureSources |> List.collect loop + + sources, !tcConfig /// Reduce the full directive closure into LoadClosure let GetLoadClosure(ctok, rootFilename, closureFiles, tcConfig:TcConfig, codeContext) = @@ -5217,7 +5291,7 @@ module private ScriptPreprocessClosure = UnresolvedReferences = unresolvedReferences Inputs = sourceInputs NoWarns = List.groupByFirst globalNoWarns - OriginalLoadReferences = tcConfig.loadedSources + OriginalLoadReferences = tcConfig.loadedSources |> List.map (fun (m,_,path) -> m,path) ResolutionDiagnostics = resolutionDiagnostics AllRootFileDiagnostics = allRootDiagnostics LoadClosureRootFileDiagnostics = loadClosureRootDiagnostics } @@ -5239,15 +5313,15 @@ module private ScriptPreprocessClosure = let tcConfig = CreateScriptSourceTcConfig(legacyReferenceResolver, defaultFSharpBinariesDir, filename, codeContext, useSimpleResolution, useFsiAuxLib, Some references0, applyCommmandLineArgs, assumeDotNetFramework) let closureSources = [ClosureSource(filename, range0, source, true)] - let closureFiles, tcConfig = FindClosureFiles(closureSources, tcConfig, codeContext, lexResourceManager) + let closureFiles, tcConfig = FindClosureFiles(filename, range0, closureSources, tcConfig, codeContext, lexResourceManager) GetLoadClosure(ctok, filename, closureFiles, tcConfig, codeContext) - + /// Given source filename, find the full load closure /// Used from fsi.fs and fsc.fs, for #load and command line - let GetFullClosureOfScriptFiles(ctok, tcConfig:TcConfig, files:(string*range) list, codeContext, lexResourceManager:Lexhelp.LexResourceManager) = - let mainFile = fst (List.last files) - let closureSources = files |> List.collect (fun (filename, m) -> ClosureSourceOfFilename(filename, m, tcConfig.inputCodePage, true)) - let closureFiles, tcConfig = FindClosureFiles(closureSources, tcConfig, codeContext, lexResourceManager) + let GetFullClosureOfScriptFiles(ctok, tcConfig:TcConfig,files:(string*range) list,codeContext,lexResourceManager:Lexhelp.LexResourceManager) = + let mainFile, mainFileRange = List.last files + let closureSources = files |> List.collect (fun (filename,m) -> ClosureSourceOfFilename(filename,m,tcConfig.inputCodePage,true)) + let closureFiles,tcConfig = FindClosureFiles(mainFile, mainFileRange, closureSources, tcConfig, codeContext, lexResourceManager) GetLoadClosure(ctok, mainFile, closureFiles, tcConfig, codeContext) type LoadClosure with @@ -5261,8 +5335,7 @@ type LoadClosure with static member ComputeClosureOfSourceFiles (ctok, tcConfig:TcConfig, files:(string*range) list, codeContext, lexResourceManager:Lexhelp.LexResourceManager) = use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse ScriptPreprocessClosure.GetFullClosureOfScriptFiles (ctok, tcConfig, files, codeContext, lexResourceManager) - - + //---------------------------------------------------------------------------- // Initial type checking environment diff --git a/src/fsharp/CompileOps.fsi b/src/fsharp/CompileOps.fsi index 77916b63f49..40bfe37eb97 100755 --- a/src/fsharp/CompileOps.fsi +++ b/src/fsharp/CompileOps.fsi @@ -258,9 +258,11 @@ type TcConfigBuilder = mutable light: bool option mutable conditionalCompilationDefines: string list /// Sources added into the build with #load - mutable loadedSources: (range * string) list + mutable loadedSources: (range * string * string) list mutable referencedDLLs: AssemblyReference list + mutable packageManagerLines: Map + mutable projectReferences: IProjectReference list mutable knownUnresolvedReferences: UnresolvedAssemblyReference list optimizeForMemory: bool @@ -659,8 +661,8 @@ val WriteOptimizationData: TcGlobals * filename: string * inMem: bool * CcuThunk val RequireDLL: CompilationThreadToken * TcImports * TcEnv * thisAssemblyName: string * referenceRange: range * file: string -> TcEnv * (ImportedBinary list * ImportedAssembly list) /// Processing # commands -val ProcessMetaCommandsFromInput: - (('T -> range * string -> 'T) * ('T -> range * string -> 'T) * ('T -> range * string -> unit)) +val ProcessMetaCommandsFromInput : + (('T -> range * string -> 'T) * ('T -> range * string -> 'T) * ('T -> DependencyManagerIntegration.IDependencyManagerProvider * range * string -> 'T) * ('T -> range * string -> unit)) -> TcConfigBuilder * Ast.ParsedInput * string * 'T -> 'T diff --git a/src/fsharp/DependencyManager.Integration.fs b/src/fsharp/DependencyManager.Integration.fs new file mode 100644 index 00000000000..8f3b71a3b72 --- /dev/null +++ b/src/fsharp/DependencyManager.Integration.fs @@ -0,0 +1,237 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +/// Helper members to integrate DependencyManagers into F# codebase +module internal Microsoft.FSharp.Compiler.DependencyManagerIntegration + +open System +open System.Reflection +open System.IO +open Microsoft.FSharp.Compiler.ErrorLogger + +// NOTE: this contains mostly members whose intents are : +// * to keep ReferenceLoading.PaketHandler usable outside of F# (so it can be used in scriptcs & others) +// * to minimize footprint of integration in fsi/CompileOps + +/// hardcoded to net461 as we don't have fsi on netcore +let targetFramework = "net461" + +module ReflectionHelper = + let assemblyHasAttribute (theAssembly: Assembly) attributeName = + try + CustomAttributeExtensions.GetCustomAttributes(theAssembly) + |> Seq.exists (fun a -> a.GetType().Name = attributeName) + with | _ -> false + + let getAttributeNamed (theType: Type) attributeName = + try + theType.GetTypeInfo().GetCustomAttributes false + |> Seq.tryFind (fun a -> a.GetType().Name = attributeName) + with | _ -> None + + let getInstanceProperty<'treturn> (theType: Type) propertyName = + try + let property = theType.GetProperty(propertyName, typeof<'treturn>) + if isNull property then + None + elif not (property.GetGetMethod().IsStatic) + && property.GetIndexParameters() = Array.empty + then + Some property + else + None + with | _ -> None + + let getInstanceMethod<'treturn> (theType: Type) (parameterTypes: Type array) methodName = + try + let theMethod = theType.GetMethod(methodName, parameterTypes) + if isNull theMethod then + None + else + Some theMethod + with | _ -> None + + let implements<'timplemented> (theType: Type) = + typeof<'timplemented>.IsAssignableFrom(theType) + +(* this is the loose contract for now, just to define the shape, but this is resolved through reflection *) +type internal IDependencyManagerProvider = + inherit System.IDisposable + abstract Name : string + abstract ToolName: string + abstract Key: string + abstract ResolveDependencies : targetFramework: string * scriptDir: string * mainScriptName: string * scriptName: string * packageManagerTextLines: string seq -> string option * string list + +[] +type ReferenceType = +| RegisteredDependencyManager of IDependencyManagerProvider +| Library of string +| UnknownType + +type ReflectionDependencyManagerProvider(theType: Type, nameProperty: PropertyInfo, toolNameProperty: PropertyInfo, keyProperty: PropertyInfo, resolveDeps: MethodInfo) = + let instance = Activator.CreateInstance(theType) :?> IDisposable + let nameProperty = nameProperty.GetValue >> string + let toolNameProperty = toolNameProperty.GetValue >> string + let keyProperty = keyProperty.GetValue >> string + static member InstanceMaker (theType: System.Type) = + if not (ReflectionHelper.implements theType) then None + else + match ReflectionHelper.getAttributeNamed theType "FSharpDependencyManagerAttribute" with + | None -> None + | Some _ -> + match ReflectionHelper.getInstanceProperty theType "Name" with + | None -> None + | Some nameProperty -> + match ReflectionHelper.getInstanceProperty theType "ToolName" with + | None -> None + | Some toolNameProperty -> + match ReflectionHelper.getInstanceProperty theType "Key" with + | None -> None + | Some keyProperty -> + match ReflectionHelper.getInstanceMethod theType [|typeof;typeof;typeof;typeof;typeof;|] "ResolveDependencies" with + | None -> None + | Some resolveDependenciesMethod -> + Some (fun () -> new ReflectionDependencyManagerProvider(theType, nameProperty, toolNameProperty, keyProperty, resolveDependenciesMethod) :> IDependencyManagerProvider) + + interface IDependencyManagerProvider with + member __.Name = instance |> nameProperty + member __.ToolName = instance |> toolNameProperty + member __.Key = instance |> keyProperty + member __.ResolveDependencies(targetFramework, scriptDir, mainScriptName, scriptName, packageManagerTextLines) = + let arguments = [|box targetFramework; box scriptDir; box mainScriptName; box scriptName; box packageManagerTextLines|] + resolveDeps.Invoke(instance, arguments) :?> _ + interface IDisposable with + member __.Dispose () = instance.Dispose() + + +let assemblySearchPaths = lazy ( + [ + let assemblyLocation = + typeof.GetTypeInfo().Assembly.Location + yield Path.GetDirectoryName assemblyLocation + let executingAssembly = + typeof.GetTypeInfo().Assembly + yield Path.GetDirectoryName(executingAssembly.Location) +#if !FX_NO_APP_DOMAINS + yield AppDomain.CurrentDomain.BaseDirectory +#else +#endif + ] + |> List.distinct) + +let enumerateDependencyManagerAssembliesFromCurrentAssemblyLocation m = + assemblySearchPaths.Force() + |> Seq.collect (fun path -> Directory.EnumerateFiles(path,"*DependencyManager*.dll")) + |> Seq.choose (fun path -> + try + Some(AbstractIL.Internal.Library.Shim.FileSystem.AssemblyLoadFrom path) + with + | exn -> + warning(Error(FSComp.SR.couldNotLoadDependencyManagerExtenstion(path,exn.Message),m)) + None) + |> Seq.filter (fun a -> ReflectionHelper.assemblyHasAttribute a "FSharpDependencyManagerAttribute") + +type ProjectDependencyManager() = + interface IDependencyManagerProvider with + member __.Name = "Project loader" + member __.ToolName = "" + member __.Key = "project" + member __.ResolveDependencies(_targetFramework:string, _scriptDir: string, _mainScriptName: string, _scriptName: string, _packageManagerTextLines: string seq) = + None,[] + + interface System.IDisposable with + member __.Dispose() = () + +type RefDependencyManager() = + interface IDependencyManagerProvider with + member __.Name = "Ref library loader" + member __.ToolName = "" + member __.Key = "ref" + member __.ResolveDependencies(_targetFramework:string, _scriptDir: string, _mainScriptName: string, _scriptName: string, _packageManagerTextLines: string seq) = + None,[] + + interface System.IDisposable with + member __.Dispose() = () + +type ImplDependencyManager() = + interface IDependencyManagerProvider with + member __.Name = "Impl library loader" + member __.ToolName = "" + member __.Key = "impl" + member __.ResolveDependencies(_targetFramework:string, _scriptDir: string, _mainScriptName: string, _scriptName: string, _packageManagerTextLines: string seq) = + None,[] + + interface System.IDisposable with + member __.Dispose() = () + + +// TBD: Need to do something about this +let registeredDependencyManagers = ref None + +let RegisteredDependencyManagers m = + match !registeredDependencyManagers with + | Some managers -> managers + | None -> + let defaultProviders = + [new ProjectDependencyManager() :> IDependencyManagerProvider + new RefDependencyManager() :> IDependencyManagerProvider + new ImplDependencyManager() :> IDependencyManagerProvider] + + let loadedProviders = + enumerateDependencyManagerAssembliesFromCurrentAssemblyLocation m + |> Seq.collect (fun a -> a.GetTypes()) + |> Seq.choose ReflectionDependencyManagerProvider.InstanceMaker + |> Seq.map (fun maker -> maker ()) + + defaultProviders + |> Seq.append loadedProviders + |> Seq.map (fun pm -> pm.Key, pm) + |> Map.ofSeq + +let createPackageManagerUnknownError packageManagerKey m = + let registeredKeys = String.Join(", ", RegisteredDependencyManagers m |> Seq.map (fun kv -> kv.Value.Key)) + let searchPaths = assemblySearchPaths.Force() + Error(FSComp.SR.packageManagerUnknown(packageManagerKey, String.Join(", ", searchPaths), registeredKeys),m) + +let tryFindDependencyManagerInPath m (path:string) : ReferenceType = + try + if path.Contains ":" && not (System.IO.Path.IsPathRooted path) then + let managers = RegisteredDependencyManagers m + match managers |> Seq.tryFind (fun kv -> path.StartsWith(kv.Value.Key + ":" )) with + | None -> + errorR(createPackageManagerUnknownError (path.Split(':').[0]) m) + ReferenceType.UnknownType + | Some kv -> ReferenceType.RegisteredDependencyManager kv.Value + else + ReferenceType.Library path + with + | e -> + errorR(Error(FSComp.SR.packageManagerError(e.Message),m)) + ReferenceType.UnknownType + +let removeDependencyManagerKey (packageManagerKey:string) (path:string) = path.Substring(packageManagerKey.Length + 1).Trim() + +let tryFindDependencyManagerByKey m (key:string) : IDependencyManagerProvider option = + try + RegisteredDependencyManagers m |> Map.tryFind key + with + | e -> + errorR(Error(FSComp.SR.packageManagerError(e.Message),m)) + None + +let resolve (packageManager:IDependencyManagerProvider) implicitIncludeDir mainScriptName fileName m packageManagerTextLines = + try + let loadScript,additionalIncludeFolders = + packageManager.ResolveDependencies( + targetFramework, + implicitIncludeDir, + mainScriptName, + fileName, + packageManagerTextLines) + + Some(loadScript,additionalIncludeFolders) + with e -> + if e.InnerException <> null then + errorR(Error(FSComp.SR.packageManagerError(e.InnerException.Message),m)) + else + errorR(Error(FSComp.SR.packageManagerError(e.Message),m)) + None \ No newline at end of file diff --git a/src/fsharp/DependencyManager.Integration.fsi b/src/fsharp/DependencyManager.Integration.fsi new file mode 100644 index 00000000000..bde6e9c705f --- /dev/null +++ b/src/fsharp/DependencyManager.Integration.fsi @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +/// Helper members to integrate DependencyManagers into F# codebase +module internal Microsoft.FSharp.Compiler.DependencyManagerIntegration + +open Microsoft.FSharp.Compiler.Range + +type IDependencyManagerProvider = + inherit System.IDisposable + abstract Name : string + abstract ToolName: string + abstract Key: string + abstract ResolveDependencies : string * string * string * string * string seq -> string option * string list + +[] +type ReferenceType = +| RegisteredDependencyManager of IDependencyManagerProvider +| Library of string +| UnknownType + +val tryFindDependencyManagerInPath : range -> string -> ReferenceType +val tryFindDependencyManagerByKey : range -> string -> IDependencyManagerProvider option +val removeDependencyManagerKey : string -> string -> string +val createPackageManagerUnknownError : string -> range -> exn + +val resolve : IDependencyManagerProvider -> string -> string -> string -> range -> string seq -> (string option * string list) option diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index deeb8d5daf0..e6aaddd1662 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1422,3 +1422,6 @@ notAFunctionButMaybeIndexer,"This expression is not a function and cannot be app notAFunctionButMaybeDeclaration,"This value is not a function and cannot be applied. Did you forget to terminate a declaration?" 3218,ArgumentsInSigAndImplMismatch,"The argument names in the signature '%s' and implementation '%s' do not match. The argument name from the signature file will be used. This may cause problems when debugging or profiling." 3219,pickleUnexpectedNonZero,"An error occurred while reading the F# metadata of assembly '%s'. A reserved construct was utilized. You may need to upgrade your F# compiler or use an earlier version of the assembly that doesn't make use of a specific construct." +3220,couldNotLoadDependencyManagerExtenstion,"The dependency manager extension '%s' could not be loaded. Message: %s" +3221,packageManagerUnknown,"Package manager key '%s' was not registered in %s. Currently registered: %s" +3222,packageManagerError,"%s" diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj index 214f60cdc9f..93eccd4f62c 100644 --- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj +++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj @@ -505,6 +505,12 @@ CodeGen\IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + @@ -681,6 +687,13 @@ + + + FSharp.DependencyManager.Paket + {25555554-522d-4cf7-97e4-ba940f0b18f3} + True + + @@ -722,4 +735,4 @@ FSharp.Core - \ No newline at end of file + diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.netcore.nuspec b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.netcore.nuspec index 3b663e8d3d5..fad0fec3d49 100644 --- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.netcore.nuspec +++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.netcore.nuspec @@ -23,6 +23,7 @@ + diff --git a/src/fsharp/FSharp.Compiler.Private/project.json b/src/fsharp/FSharp.Compiler.Private/project.json index 09cfefb19fc..d368de741a5 100644 --- a/src/fsharp/FSharp.Compiler.Private/project.json +++ b/src/fsharp/FSharp.Compiler.Private/project.json @@ -8,6 +8,7 @@ "System.Linq.Queryable": "4.3.0", "System.Net.Requests": "4.3.0", "System.Reflection.Emit": "4.3.0", + "System.Reflection.Extensions": "4.3.0", "System.Reflection.Metadata": "1.4.2", "System.Reflection.TypeExtensions": "4.3.0", "System.Runtime": "4.3.0", diff --git a/src/fsharp/Fsc-proto/Fsc-proto.fsproj b/src/fsharp/Fsc-proto/Fsc-proto.fsproj index fd3d209e512..22dda9b1bbb 100644 --- a/src/fsharp/Fsc-proto/Fsc-proto.fsproj +++ b/src/fsharp/Fsc-proto/Fsc-proto.fsproj @@ -420,6 +420,12 @@ IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + CompileOps.fsi diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index 3a178a5c185..216b282004b 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -985,6 +985,8 @@ type internal FsiDynamicCompiler let mutable fragmentId = 0 let mutable prevIt : ValRef option = None + let mutable needsPackageResolution = false + let generateDebugInfo = tcConfigB.debuginfo let valuePrinter = FsiValuePrinter(fsi, tcGlobals, generateDebugInfo, resolveAssemblyRef, outWriter) @@ -1273,6 +1275,40 @@ type internal FsiDynamicCompiler resolutions, { istate with tcState = tcState.NextStateAfterIncrementalFragment(tcEnv); optEnv = optEnv } + + member __.EvalDependencyManagerTextFragment (packageManager:DependencyManagerIntegration.IDependencyManagerProvider,m,path: string) = + let path = DependencyManagerIntegration.removeDependencyManagerKey packageManager.Key path + + match tcConfigB.packageManagerLines |> Map.tryFind packageManager.Key with + | Some lines -> tcConfigB.packageManagerLines <- Map.add packageManager.Key (lines @ [path,m]) tcConfigB.packageManagerLines + | _ -> tcConfigB.packageManagerLines <- Map.add packageManager.Key [path,m] tcConfigB.packageManagerLines + + needsPackageResolution <- true + + member fsiDynamicCompiler.CommitDependencyManagerText (ctok, istate: FsiDynamicCompilerState, lexResourceManager, errorLogger) = + if not needsPackageResolution then istate else + needsPackageResolution <- false + + tcConfigB.packageManagerLines |> Seq.fold(fun istate kv -> + let packageManagerKey,packageManagerLines = kv.Key, kv.Value + match packageManagerLines with + | [] -> istate + | (_, m)::_ -> + let packageManagerTextLines = packageManagerLines |> List.map fst + match DependencyManagerIntegration.tryFindDependencyManagerByKey m packageManagerKey with + | None -> + errorR(DependencyManagerIntegration.createPackageManagerUnknownError packageManagerKey m) + istate + | Some packageManager -> + match DependencyManagerIntegration.resolve packageManager tcConfigB.implicitIncludeDir "stdin.fsx" "stdin.fsx" m packageManagerTextLines with + | None -> istate // error already reported + | Some (loadScript, additionalIncludeFolders) -> + for folder in additionalIncludeFolders do + tcConfigB.AddIncludePath(m, folder, "") + match loadScript with + | Some loadScript -> fsiDynamicCompiler.EvalSourceFiles(ctok, istate, m, [loadScript], lexResourceManager, errorLogger) + | None -> istate) istate + member fsiDynamicCompiler.ProcessMetaCommandsFromInputAsInteractiveCommands(ctok, istate, sourceFile, inp) = WithImplicitHome (tcConfigB, directoryName sourceFile) @@ -1280,6 +1316,7 @@ type internal FsiDynamicCompiler ProcessMetaCommandsFromInput ((fun st (m,nm) -> tcConfigB.TurnWarningOff(m,nm); st), (fun st (m,nm) -> snd (fsiDynamicCompiler.EvalRequireReference (ctok, st, m, nm))), + (fun st (packageManagerPrefix,m,nm) -> fsiDynamicCompiler.EvalDependencyManagerTextFragment (packageManagerPrefix,m,nm); st), (fun _ _ -> ())) (tcConfigB, inp, Path.GetDirectoryName sourceFile, istate)) @@ -1896,34 +1933,48 @@ type internal FsiInteractionProcessor istate |> InteractiveCatch errorLogger (fun istate -> match action with | IDefns ([ ],_) -> + let istate = fsiDynamicCompiler.CommitDependencyManagerText(ctok, istate, lexResourceManager, errorLogger) istate,Completed None + | IDefns ([ SynModuleDecl.DoExpr(_,expr,_)],_) -> + let istate = fsiDynamicCompiler.CommitDependencyManagerText(ctok, istate, lexResourceManager, errorLogger) fsiDynamicCompiler.EvalParsedExpression(ctok, errorLogger, istate, expr) + | IDefns (defs,_) -> + let istate = fsiDynamicCompiler.CommitDependencyManagerText(ctok, istate, lexResourceManager, errorLogger) fsiDynamicCompiler.EvalParsedDefinitions (ctok, errorLogger, istate, true, false, defs),Completed None | IHash (ParsedHashDirective("load",sourceFiles,m),_) -> + let istate = fsiDynamicCompiler.CommitDependencyManagerText(ctok, istate, lexResourceManager, errorLogger) fsiDynamicCompiler.EvalSourceFiles (ctok, istate, m, sourceFiles, lexResourceManager, errorLogger),Completed None | IHash (ParsedHashDirective(("reference" | "r"),[path],m),_) -> - let resolutions,istate = fsiDynamicCompiler.EvalRequireReference(ctok, istate, m, path) - resolutions |> List.iter (fun ar -> - let format = - if tcConfig.shadowCopyReferences then - let resolvedPath = ar.resolvedPath.ToUpperInvariant() - let fileTime = File.GetLastWriteTimeUtc(resolvedPath) - match referencedAssemblies.TryGetValue(resolvedPath) with - | false, _ -> - referencedAssemblies.Add(resolvedPath, fileTime) - FSIstrings.SR.fsiDidAHashr(ar.resolvedPath) - | true, time when time <> fileTime -> - FSIstrings.SR.fsiDidAHashrWithStaleWarning(ar.resolvedPath) - | _ -> - FSIstrings.SR.fsiDidAHashr(ar.resolvedPath) - else - FSIstrings.SR.fsiDidAHashrWithLockWarning(ar.resolvedPath) - fsiConsoleOutput.uprintnfnn "%s" format) - istate,Completed None + match DependencyManagerIntegration.tryFindDependencyManagerInPath m (path:string) with + | DependencyManagerIntegration.ReferenceType.RegisteredDependencyManager packageManager -> + fsiDynamicCompiler.EvalDependencyManagerTextFragment(packageManager,m,path) + istate,Completed None + | DependencyManagerIntegration.ReferenceType.UnknownType -> + // error already reported + istate,Completed None + | DependencyManagerIntegration.ReferenceType.Library path -> + let resolutions,istate = fsiDynamicCompiler.EvalRequireReference(ctok, istate, m, path) + resolutions |> List.iter (fun ar -> + let format = + if tcConfig.shadowCopyReferences then + let resolvedPath = ar.resolvedPath.ToUpperInvariant() + let fileTime = File.GetLastWriteTimeUtc(resolvedPath) + match referencedAssemblies.TryGetValue(resolvedPath) with + | false, _ -> + referencedAssemblies.Add(resolvedPath, fileTime) + FSIstrings.SR.fsiDidAHashr(ar.resolvedPath) + | true, time when time <> fileTime -> + FSIstrings.SR.fsiDidAHashrWithStaleWarning(ar.resolvedPath) + | _ -> + FSIstrings.SR.fsiDidAHashr(ar.resolvedPath) + else + FSIstrings.SR.fsiDidAHashrWithLockWarning(ar.resolvedPath) + fsiConsoleOutput.uprintnfnn "%s" format) + istate,Completed None | IHash (ParsedHashDirective("I",[path],m),_) -> tcConfigB.AddIncludePath (m,path, tcConfig.implicitIncludeDir) @@ -2079,10 +2130,6 @@ type internal FsiInteractionProcessor reusingLexbufForParsing tokenizer.LexBuffer (fun () -> Parser.typedSeqExprEOF tokenizer.Lexer tokenizer.LexBuffer) -// let parseType (tokenizer:LexFilter.LexFilter) = -// reusingLexbufForParsing tokenizer.LexBuffer (fun () -> -// Parser.typEOF tokenizer.Lexer tokenizer.LexBuffer) - let mainThreadProcessParsedExpression ctok errorLogger (expr, istate) = istate |> InteractiveCatch errorLogger (fun istate -> istate |> mainThreadProcessAction ctok (fun ctok _tcConfig istate -> diff --git a/tests/fsharpqa/Source/InteractiveSession/Misc/UnknownDependencyManager/script1.fsx b/tests/fsharpqa/Source/InteractiveSession/Misc/UnknownDependencyManager/script1.fsx new file mode 100644 index 00000000000..38c8173ec88 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Misc/UnknownDependencyManager/script1.fsx @@ -0,0 +1,5 @@ +//Package manager key 'unk' was not registered. + +#r "unk: blubblub" + +let x = 1 \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Misc/env.lst b/tests/fsharpqa/Source/InteractiveSession/Misc/env.lst index 13803307162..f18e59e67d6 100644 --- a/tests/fsharpqa/Source/InteractiveSession/Misc/env.lst +++ b/tests/fsharpqa/Source/InteractiveSession/Misc/env.lst @@ -165,3 +165,6 @@ NOMONO SOURCE=Regressions01.fs COMPILE_ONLY=1 FSIMODE=PIPE SCFLAGS="--nologo" SOURCE=..\\Misc\\ccc\\RelativeHashRResolution03_1.fsx COMPILE_ONLY=1 SCFLAGS="--nologo --simpleresolution --noframework -r:\"%FSCOREDLLPATH%\"" # RelativeHashRResolution03_fscrelativesimple SOURCE=..\\Misc\\aaa\\bbb\\RelativeHashRResolution04_1.fsx COMPILE_ONLY=1 SCFLAGS="--nologo --simpleresolution --noframework -r:\"%FSCOREDLLPATH%\"" # RelativeHashRResolution04_fscrelativesimple SOURCE=..\\Misc\\aaa\\bbb\\RelativeHashRResolution05_1.fsx COMPILE_ONLY=1 SCFLAGS="--nologo --simpleresolution --noframework -r:\"%FSCOREDLLPATH%\"" # RelativeHashRResolution05_fscrelativesimple + +# dependency managers +SOURCE="UnknownDependencyManager\\script1.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # with unknown manager \ No newline at end of file diff --git a/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs b/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs index d72c2c4032f..f2efba1c5d4 100644 --- a/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs +++ b/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs @@ -55,13 +55,19 @@ type UsingMSBuild() as this = member private this.VerifyErrorListCountAtOpenProject(fileContents : string, num : int) = let (solution, project, file) = this.CreateSingleFileProject(fileContents) let errorList = GetErrors(project) + let errorTexts = new System.Text.StringBuilder() for error in errorList do printfn "%A" error.Severity - printf "%s\n" (error.ToString()) - if (num = errorList.Length) then - () - else - failwithf "The error list number is not the expected %d" num + let s = error.ToString() + errorTexts.AppendLine s |> ignore + printf "%s\n" s + + if num <> errorList.Length then + failwithf "The error list number is not the expected %d but %d%s%s" + num + errorList.Length + System.Environment.NewLine + (errorTexts.ToString()) //Verify the warning list Count member private this.VerifyWarningListCountAtOpenProject(fileContents : string, expectedNum : int, ?addtlRefAssy : list) =