diff --git a/VisualFSharp.sln b/VisualFSharp.sln index dc754bb2de..0a2d8788d4 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -134,6 +134,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{D086C8C6 EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "LanguageServiceProfiling", "vsintegration\Utils\LanguageServiceProfiling\LanguageServiceProfiling.fsproj", "{E7FA3A71-51AF-4FCA-9C2F-7C853E515903}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.DependencyManager.Paket", "src\fsharp\FSharp.DependencyManager.Paket\FSharp.DependencyManager.Paket.fsproj", "{25555554-522D-4CF7-97E4-BA940F0B18F3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.UIResources", "vsintegration\src\FSharp.UIResources\FSharp.UIResources.csproj", "{C4586A06-1402-48BC-8E35-A1B8642F895B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp_Analysis", "tests\service\data\CSharp_Analysis\CSharp_Analysis.csproj", "{887630A3-4B1D-40EA-B8B3-2D842E9C40DB}" @@ -550,6 +551,18 @@ Global {E7FA3A71-51AF-4FCA-9C2F-7C853E515903}.Release|Any CPU.Build.0 = Release|Any CPU {E7FA3A71-51AF-4FCA-9C2F-7C853E515903}.Release|x86.ActiveCfg = Release|Any CPU {E7FA3A71-51AF-4FCA-9C2F-7C853E515903}.Release|x86.Build.0 = Release|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Debug|x86.ActiveCfg = Debug|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Debug|x86.Build.0 = Debug|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Proto|Any CPU.ActiveCfg = Proto|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Proto|Any CPU.Build.0 = Proto|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Proto|x86.ActiveCfg = Proto|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Proto|x86.Build.0 = Proto|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Release|Any CPU.Build.0 = Release|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Release|x86.ActiveCfg = Release|Any CPU + {25555554-522D-4CF7-97E4-BA940F0B18F3}.Release|x86.Build.0 = Release|Any CPU {C4586A06-1402-48BC-8E35-A1B8642F895B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4586A06-1402-48BC-8E35-A1B8642F895B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C4586A06-1402-48BC-8E35-A1B8642F895B}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -635,6 +648,7 @@ Global {2E60864A-E3FF-4BCC-810F-DC7C34E6B236} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449} {D086C8C6-D00D-4C3B-9AB2-A4286C9F5922} = {4C7B48D7-19AF-4AE7-9D1D-3BB289D5480D} {E7FA3A71-51AF-4FCA-9C2F-7C853E515903} = {D086C8C6-D00D-4C3B-9AB2-A4286C9F5922} + {25555554-522D-4CF7-97E4-BA940F0B18F3} = {B8DDA694-7939-42E3-95E5-265C2217C142} {C4586A06-1402-48BC-8E35-A1B8642F895B} = {4C7B48D7-19AF-4AE7-9D1D-3BB289D5480D} {887630A3-4B1D-40EA-B8B3-2D842E9C40DB} = {35636A82-401A-4C3A-B2AB-EB7DC5E9C268} {35636A82-401A-4C3A-B2AB-EB7DC5E9C268} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} diff --git a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index 6faf15a659..694e553302 100644 --- a/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj +++ b/fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj @@ -1,4 +1,4 @@ - + @@ -457,6 +457,12 @@ CodeGen/IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + Driver/CompileOps.fsi diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index e878cdffa0..439cd98b22 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -75,7 +75,6 @@ let FSharpScriptFileSuffixes = [".fsscript";".fsx"] let doNotRequireNamespaceOrModuleSuffixes = [".mli";".ml"] @ FSharpScriptFileSuffixes let FSharpLightSyntaxFileSuffixes : string list = [ ".fs";".fsscript";".fsx";".fsi" ] - //---------------------------------------------------------------------------- // ERROR REPORTING //-------------------------------------------------------------------------- @@ -2044,8 +2043,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 @@ -2213,6 +2213,7 @@ type TcConfigBuilder = framework=true implicitlyResolveAssemblies=true referencedDLLs = [] + packageManagerLines = Map.empty projectReferences = [] knownUnresolvedReferences = [] loadedSources = [] @@ -2403,18 +2404,18 @@ 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) + 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 @@ -2429,6 +2430,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) @@ -2734,6 +2742,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 @@ -2855,13 +2864,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 + 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 @@ -4671,11 +4690,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, + packageRequireF: 'state -> DependencyManagerIntegration.IDependencyManagerProvider * range * string -> 'state, loadSourceF: 'state -> range * string -> unit) (tcConfig:TcConfigBuilder, inp, pathOfMetaCommandSource, state0) = @@ -4703,13 +4721,20 @@ let ProcessMetaCommandsFromInput state | 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 @@ -4788,8 +4813,9 @@ let ApplyNoWarnsToTcConfig (tcConfig:TcConfig, inp:ParsedInput, pathOfMetaComman let tcConfigB = tcConfig.CloneOfOriginalBuilder 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, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + ProcessMetaCommandsFromInput (addNoWarn, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) let ApplyMetaCommandsFromInputToTcConfig (tcConfig:TcConfig, inp:ParsedInput, pathOfMetaCommandSource) = @@ -4797,8 +4823,9 @@ let ApplyMetaCommandsFromInputToTcConfig (tcConfig:TcConfig, inp:ParsedInput, pa let tcConfigB = tcConfig.CloneOfOriginalBuilder let getWarningNumber = fun () _ -> () 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, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) + ProcessMetaCommandsFromInput (getWarningNumber, addReferencedAssemblyByPath, addDependencyManagerText, addLoadedSource) (tcConfigB, inp, pathOfMetaCommandSource, ()) TcConfig.Create(tcConfigB, validate=false) //---------------------------------------------------------------------------- @@ -4847,8 +4874,7 @@ type CodeContext = | 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 @@ -4930,9 +4956,10 @@ module private ScriptPreprocessClosure = let nowarns = ref [] 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 () @@ -4944,12 +4971,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 @@ -4964,17 +5029,21 @@ 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 [] + yield! resolveDependencyManagerSources filename + //for (_,subFile) in sources do // printfn "visiting %s - has subsource of %s " filename subFile - for (m,subFile) in sources do if IsScript(subFile) then for subSource in ClosureSourceOfFilename(subFile,m,tcConfigResult.inputCodePage,false) do @@ -4993,7 +5062,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) = @@ -5048,7 +5119,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 } @@ -5070,15 +5141,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 mainFile, mainFileRange = 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 closureFiles,tcConfig = FindClosureFiles(mainFile, mainFileRange, closureSources, tcConfig, codeContext, lexResourceManager) GetLoadClosure(ctok, mainFile, closureFiles, tcConfig, codeContext) type LoadClosure with diff --git a/src/fsharp/CompileOps.fsi b/src/fsharp/CompileOps.fsi index 9b8634b66d..5518a33ed1 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 @@ -668,7 +670,7 @@ val RequireDLL : CompilationThreadToken * TcImports * TcEnv * thisAssemblyName: /// Processing # commands val ProcessMetaCommandsFromInput : - (('T -> range * string -> 'T) * ('T -> range * string -> 'T) * ('T -> range * string -> unit)) + (('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 0000000000..dfaab7d560 --- /dev/null +++ b/src/fsharp/DependencyManager.Integration.fs @@ -0,0 +1,253 @@ +// 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 + +#if FX_RESHAPED_REFLECTION + open PrimReflectionAdapters + open ReflectionAdapters +#endif + +// 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.tryFind (fun a -> a.GetType().Name = attributeName) + |> function | Some _ -> true | _ -> false + with | _ -> false + + let getAttributeNamed (theType: Type) attributeName = + try +#if FX_RESHAPED_REFLECTION + theType.GetTypeInfo().GetCustomAttributes false +#else + theType.GetCustomAttributes false +#endif + |> Seq.tryFind (fun a -> a.GetType().Name = attributeName) + with | _ -> None + + let getInstanceProperty<'treturn> (theType: Type) indexParameterTypes propertyName = + try + let property = theType.GetProperty(propertyName, typeof<'treturn>) + if isNull property then + None + elif not (property.GetGetMethod().IsStatic) + && property.GetIndexParameters() = indexParameterTypes + 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 Array.empty "Name" with + | None -> None + | Some nameProperty -> + match ReflectionHelper.getInstanceProperty theType Array.empty "ToolName" with + | None -> None + | Some toolNameProperty -> + match ReflectionHelper.getInstanceProperty theType Array.empty "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 = +#if FX_RESHAPED_REFLECTION + typeof.GetTypeInfo().Assembly.Location +#else + typeof.Assembly.Location +#endif + yield Path.GetDirectoryName assemblyLocation + let executingAssembly = +#if FX_RESHAPED_REFLECTION + typeof.GetTypeInfo().Assembly +#else + Assembly.GetExecutingAssembly() +#endif + yield Path.GetDirectoryName(executingAssembly.Location) +#if FX_NO_APP_DOMAINS +#else + let baseDir = AppDomain.CurrentDomain.BaseDirectory + yield baseDir +#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() = () + +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 0000000000..bde6e9c705 --- /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 4892447a51..cc3dae243a 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1339,6 +1339,8 @@ tcGlobalsSystemTypeNotFound,"The system type '%s' was required but no referenced 3213,typrelMemberHasMultiplePossibleDispatchSlots,"The member '%s' matches multiple overloads of the same method.\nPlease restrict it to one of the following:%s." 3214,methodIsNotStatic,"Method or object constructor '%s' is not static" 3215,parsUnexpectedSymbolEqualsInsteadOfIn,"Unexpected symbol '=' in expression. Did you intend to use 'for x in y .. z do' instead?" +3216,packageManagerUnknown,"Package manager key '%s' was not registered in %s. Currently registered: %s" +3217,packageManagerError,"%s" keywordDescriptionAbstract,"Indicates a method that either has no implementation in the type in which it is declared or that is virtual and has a default implementation." keyworkDescriptionAnd,"Used in mutually recursive bindings, in property declarations, and with multiple constraints on generic parameters." keywordDescriptionAs,"Used to give the current class object an object name. Also used to give a name to a whole pattern within a pattern match." @@ -1420,3 +1422,4 @@ notAFunctionButMaybeIndexerWithName,"This value is not a function and cannot be notAFunctionButMaybeIndexer,"This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?" 3217,notAFunctionButMaybeIndexerErrorCode,"" notAFunctionButMaybeDeclaration,"This value is not a function and cannot be applied. Did you forget to terminate a declaration?" +3218,couldNotLoadDependencyManagerExtenstion,"The dependency manager extension %s could not be loaded. Message: %s" \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.BuildFromSource.fsproj b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.BuildFromSource.fsproj index eba37e1261..d58cdd045a 100644 --- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.BuildFromSource.fsproj +++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.BuildFromSource.fsproj @@ -8,7 +8,7 @@ FSharp.Compiler.Private $(NoWarn);45;55;62;75;1204 true - $(DefineConstants);EXTENSIONTYPING;COMPILER;BUILD_FROM_SOURCE + $(DefineConstants);EXTENSIONTYPING;COMPILER;BUILD_FROM_SOURCE;FX_RESHAPED_REFLECTION $(OtherFlags) --warnon:1182 --maxerrors:20 --extraoptimizationloops:1 @@ -450,6 +450,12 @@ CodeGen\IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + @@ -609,6 +615,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", @@ -631,6 +638,7 @@ + diff --git a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj index d1480a6b64..ddaf7a6d88 100644 --- a/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj +++ b/src/fsharp/FSharp.Compiler.Private/FSharp.Compiler.Private.fsproj @@ -487,6 +487,12 @@ CodeGen\IlxGen.fs + + Driver\DependencyManager.Integration.fsi + + + Driver\DependencyManager.Integration.fs + @@ -642,6 +648,13 @@ + + + FSharp.DependencyManager.Paket + {25555554-522d-4cf7-97e4-ba940f0b18f3} + True + + @@ -708,4 +721,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 8dfb2b6c25..f0c250998d 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 c50e8438f9..d9581a1044 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/FSharp.DependencyManager.Paket/FSharp.DependencyManager.Paket.fsproj b/src/fsharp/FSharp.DependencyManager.Paket/FSharp.DependencyManager.Paket.fsproj new file mode 100644 index 0000000000..fca194dbc7 --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/FSharp.DependencyManager.Paket.fsproj @@ -0,0 +1,68 @@ + + + + + $(MSBuildProjectDirectory)\..\.. + FSharp.DependencyManager.Paket + + + + Debug + AnyCPU + Library + FSharp.DependencyManager.Paket + EXTENSIONTYPING;$(DefineConstants) + COMPILER;$(DefineConstants) + $(NoWarn);62;9 + {25555554-522D-4CF7-97E4-BA940F0B18F3} + true + 0x06800000 + $(OtherFlags) /warnon:1182 + + + + + + + + + + + + + + + Microsoft + StrongName + + + + + + $(FSharpSourcesRoot)\..\loc\lcl\{Lang}\$(AssemblyName).dll.lcl + $(FSharpSourcesRoot)\..\loc\lci\$(AssemblyName).dll.lci + false + false + + + + + + + + + + + + + {DED3BBD7-53F4-428A-8C9F-27968E768605} + FSharp.Core + + + + + + + + + \ No newline at end of file diff --git a/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fs b/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fs new file mode 100644 index 0000000000..20226891fd --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fs @@ -0,0 +1,20 @@ +namespace Microsoft.FSharp.DependencyManager.Paket + +/// Used as a marker that compiler scans for, although there is no hard dependency, filtered by name. +type FSharpDependencyManagerAttribute() = + inherit System.Attribute() + +type [] PaketDependencyManager() = + member __.Name = "Paket" + member __.ToolName = "paket.exe" + member __.Key = "paket" + member __.ResolveDependencies(targetFramework:string, scriptDir: string, mainScriptName: string, scriptName: string, packageManagerTextLines: string seq) = + ReferenceLoading.PaketHandler.ResolveDependencies( + targetFramework, + scriptDir, + mainScriptName, + scriptName, + packageManagerTextLines) + + interface System.IDisposable with + member __.Dispose() = () diff --git a/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fsi b/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fsi new file mode 100644 index 0000000000..625a66147d --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/PaketDependencyManager.fsi @@ -0,0 +1,12 @@ +namespace Microsoft.FSharp.DependencyManager.Paket +type [] FSharpDependencyManagerAttribute = + inherit System.Attribute + new : unit -> FSharpDependencyManagerAttribute + +type [] PaketDependencyManager = + interface System.IDisposable + new : unit -> PaketDependencyManager + member Name : string + member ToolName: string + member Key: string + member ResolveDependencies : targetFramework: string * scriptDir: string * mainScriptName: string * scriptName: string * dependencyManagerTextLines: string seq -> string option * string list diff --git a/src/fsharp/FSharp.DependencyManager.Paket/ReferenceLoading.PaketHandler.fs b/src/fsharp/FSharp.DependencyManager.Paket/ReferenceLoading.PaketHandler.fs new file mode 100644 index 0000000000..b920ae69b6 --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/ReferenceLoading.PaketHandler.fs @@ -0,0 +1,232 @@ +// 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. +// NOTE: this file is used by other parties integrating paket reference loading in scripting environments. +// Do not add any reference to F# codebase other than FSharp.Core. + +// This file should eventually end up in paket repository instead of F#. + +/// Paket invokation for In-Script reference loading +module internal ReferenceLoading.PaketHandler + +open System +open System.IO +let PM_EXE = "paket.exe" +let PM_DIR = ".paket" + +let userProfile = + let res = Environment.GetEnvironmentVariable("USERPROFILE") + if System.String.IsNullOrEmpty res then + Environment.GetEnvironmentVariable("HOME") + else res + +let MakeDependencyManagerCommand scriptType packageManagerTargetFramework projectRootDirArgument = + sprintf "install --generate-load-scripts load-script-type %s load-script-framework %s project-root \"%s\"" + scriptType packageManagerTargetFramework (System.IO.Path.GetFullPath projectRootDirArgument) + +let getDirectoryAndAllParentDirectories (directory: DirectoryInfo) = + let rec allParents (directory: DirectoryInfo) = + seq { + match directory.Parent with + | null -> () + | parent -> + yield parent + yield! allParents parent + } + + seq { + yield directory + yield! allParents directory + } + +let runningOnMono = +#if ENABLE_MONO_SUPPORT +// Officially supported way to detect if we are running on Mono. +// See http://www.mono-project.com/FAQ:_Technical +// "How can I detect if am running in Mono?" section + try + System.Type.GetType("Mono.Runtime") <> null + with e-> + // Must be robust in the case that someone else has installed a handler into System.AppDomain.OnTypeResolveEvent + // that is not reliable. + // This is related to bug 5506--the issue is actually a bug in VSTypeResolutionService.EnsurePopulated which is + // called by OnTypeResolveEvent. The function throws a NullReferenceException. I'm working with that team to get + // their issue fixed but we need to be robust here anyway. + false +#else + false +#endif + +/// Walks up directory structure and tries to find paket.exe +let findPaketExe (prioritizedSearchPaths: string seq) (baseDir: DirectoryInfo) = + let prioritizedSearchPaths = prioritizedSearchPaths |> Seq.map (fun d -> DirectoryInfo d) + + // for each given directory, we look for paket.exe and .paket/paket.exe + let getPaketAndExe (directory: DirectoryInfo) = + match directory.GetFiles(PM_EXE) with + | [| exe |] -> Some exe.FullName + | _ -> + match directory.GetDirectories(PM_DIR) with + | [| dir |] -> + match dir.GetFiles(PM_EXE) with + | [| exe |] -> Some exe.FullName + | _ -> None + | _ -> None + + let allDirs = + Seq.concat [prioritizedSearchPaths ; getDirectoryAndAllParentDirectories baseDir] + + allDirs + |> Seq.choose getPaketAndExe + |> Seq.tryHead + +/// Resolves absolute load script location: something like +/// baseDir/.paket/load/scriptName +/// or +/// baseDir/.paket/load/frameworkDir/scriptName +let GetPaketLoadScriptLocation baseDir optionalFrameworkDir scriptName = + let paketLoadFolder = System.IO.Path.Combine(PM_DIR,"load") + let frameworkDir = + match optionalFrameworkDir with + | None -> paketLoadFolder + | Some frameworkDir -> System.IO.Path.Combine(paketLoadFolder, frameworkDir) + + System.IO.Path.Combine(baseDir, frameworkDir, scriptName) + + +/// Resolve packages loaded into scripts using `paket:` in `#r` directives such as `#r @"paket: nuget AmazingNugetPackage"`. +/// This function will throw if the resolution is not successful or the tool wasn't found +/// A string given to paket command to select the output language. Can be `fsx` or `csx` +/// A string given to paket command to fix the framework. +/// List of directories which are checked first to resolve `paket.exe`. +/// +/// Filename for the main script (not necessarily existing if interactive evaluation) +/// Filename for the script (not necessarily existing if interactive evaluation) +/// Package manager text lines from script, those are meant to be just the inner part, without `#r "paket:` prefix +let ResolveDependenciesForLanguage(fileType,targetFramework:string,prioritizedSearchPaths: string seq, scriptDir: string, _mainScriptName: string, scriptName: string, packageManagerTextLinesFromScript: string seq) = + let scriptHash = string(abs(hash (scriptDir,scriptName))) + let workingDir = Path.Combine(Path.GetTempPath(), "script-packages", scriptHash) + let depsFileName = "paket.dependencies" + let workingDirSpecFile = FileInfo(Path.Combine(workingDir,depsFileName)) + if not (Directory.Exists workingDir) then + Directory.CreateDirectory workingDir |> ignore + + let packageManagerTextLinesFromScript = + packageManagerTextLinesFromScript + |> Seq.toList + |> List.filter (not << String.IsNullOrWhiteSpace) + + let rootDir,packageManagerTextLines = + let rec findSpecFile dir = + let fi = FileInfo(Path.Combine(dir,depsFileName)) + if fi.Exists then + let lockfileName = "paket.lock" + let lockFile = FileInfo(Path.Combine(fi.Directory.FullName,lockfileName)) + let depsFileLines = File.ReadAllLines fi.FullName + if lockFile.Exists then + let originalDepsFile = FileInfo(workingDirSpecFile.FullName + ".original") + if not originalDepsFile.Exists || + File.ReadAllLines originalDepsFile.FullName <> depsFileLines + then + File.Copy(fi.FullName,originalDepsFile.FullName,true) + let targetLockFile = FileInfo(Path.Combine(workingDir,lockfileName)) + File.Copy(lockFile.FullName,targetLockFile.FullName,true) + + let lines = + if List.isEmpty packageManagerTextLinesFromScript then + Array.toList depsFileLines + else + (Array.toList depsFileLines) @ ("group Main" :: packageManagerTextLinesFromScript) + + fi.Directory.FullName, lines + elif not (isNull fi.Directory.Parent) then + findSpecFile fi.Directory.Parent.FullName + else + let withImplicitSource = + match packageManagerTextLinesFromScript with + | line::_ when line.StartsWith "source" -> packageManagerTextLinesFromScript + | _ -> "source https://nuget.org/api/v2" :: packageManagerTextLinesFromScript + workingDir, ("framework: " + targetFramework) :: withImplicitSource + + findSpecFile scriptDir + + /// hardcoded to load the "Main" group (implicit in paket) + let loadScriptFileName = GetPaketLoadScriptLocation workingDir (Some targetFramework) ("main.group." + fileType) + let loadScriptFileInfo = FileInfo loadScriptFileName + let copiedScriptFileInfo = FileInfo(Path.Combine(loadScriptFileInfo.Directory.FullName,"main.group." + scriptHash + "." + fileType)) + + let additionalIncludeFolders() = + [Path.Combine(workingDir,"paket-files")] + |> List.filter Directory.Exists + + if workingDirSpecFile.Exists && + (File.ReadAllLines(workingDirSpecFile.FullName) |> Array.toList) = packageManagerTextLines && + loadScriptFileInfo.Exists && + copiedScriptFileInfo.Exists + then + (Some copiedScriptFileInfo.FullName,additionalIncludeFolders()) + else + let toolPathOpt = + // we try to resolve .paket/paket.exe any place up in the folder structure from current script + match findPaketExe prioritizedSearchPaths (DirectoryInfo scriptDir) with + | Some paketExe -> Some paketExe + | None -> + let profileExe = Path.Combine (userProfile, PM_DIR, PM_EXE) + if File.Exists profileExe then Some profileExe + else None + + match toolPathOpt with + | None -> + failwithf "Paket was not found in '%s' or a parent directory, or '%s'. Please download the tool and place it in one of the locations." + scriptDir userProfile + + | Some toolPath -> + try loadScriptFileInfo.Delete() with _ -> () + let toolPath = if runningOnMono then "mono " + toolPath else toolPath + File.WriteAllLines(workingDirSpecFile.FullName, packageManagerTextLines) + let startInfo = + System.Diagnostics.ProcessStartInfo( + FileName = toolPath, + WorkingDirectory = workingDir, + RedirectStandardOutput = true, + RedirectStandardError = true, + Arguments = MakeDependencyManagerCommand fileType targetFramework rootDir, + CreateNoWindow = true, + UseShellExecute = false) + + use p = new System.Diagnostics.Process() + let errors = ResizeArray<_>() + let log = ResizeArray<_>() + p.StartInfo <- startInfo + p.ErrorDataReceived.Add(fun d -> if not (isNull d.Data) then errors.Add d.Data) + p.OutputDataReceived.Add(fun d -> + if not (isNull d.Data) then + try + Console.ForegroundColor <- ConsoleColor.Green + Console.Write ":paket> " + Console.ResetColor() + Console.WriteLine d.Data + log.Add d.Data + finally + Console.ResetColor() + ) + p.Start() |> ignore + p.BeginErrorReadLine() + p.BeginOutputReadLine() + p.WaitForExit() + + if p.ExitCode <> 0 then + let msg = String.Join(Environment.NewLine, errors) + failwithf "Package resolution using '%s' failed, see directory '%s'.%s%s" + toolPath workingDir Environment.NewLine msg + else + File.Copy(loadScriptFileInfo.FullName,copiedScriptFileInfo.FullName,true) + (Some copiedScriptFileInfo.FullName,additionalIncludeFolders()) + +/// Resolve packages loaded into scripts using `paket:` in `#r` directives such as `#r @"paket: nuget AmazingNugetPackage"`. +/// This function will throw if the resolution is not successful or the tool wasn't found +/// A string given to paket command to fix the framework. +/// +/// Filename for the main script (not necessarily existing if interactive evaluation) +/// Filename for the script (not necessarily existing if interactive evaluation) +/// Package manager text lines from script, those are meant to be just the inner part, without `#r "paket:` prefix +let ResolveDependencies(targetFramework:string, scriptDir: string, mainScriptName: string, scriptName: string, packageManagerTextLinesFromScript: string seq) = + ResolveDependenciesForLanguage("fsx",targetFramework,Seq.empty, scriptDir, mainScriptName, scriptName,packageManagerTextLinesFromScript) diff --git a/src/fsharp/FSharp.DependencyManager.Paket/assemblyattribute.fs b/src/fsharp/FSharp.DependencyManager.Paket/assemblyattribute.fs new file mode 100644 index 0000000000..2a599ff0e8 --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/assemblyattribute.fs @@ -0,0 +1,5 @@ +namespace Microsoft.FSharp.DependencyManager.Paket + +[] +do() + diff --git a/src/fsharp/FSharp.DependencyManager.Paket/project.json b/src/fsharp/FSharp.DependencyManager.Paket/project.json new file mode 100644 index 0000000000..d9581a1044 --- /dev/null +++ b/src/fsharp/FSharp.DependencyManager.Paket/project.json @@ -0,0 +1,37 @@ +{ + "dependencies": { + "NETStandard.Library": "1.6.1", + "System.Collections.Immutable":"1.2.0", + "System.Diagnostics.Process": "4.3.0", + "System.Diagnostics.TraceSource": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "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", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Loader": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Threading.Tasks.Parallel": "4.3.0", + "System.Threading.Thread": "4.3.0", + "System.Threading.ThreadPool": "4.3.0", + "Microsoft.DiaSymReader.PortablePdb": "1.1.0", + "Microsoft.DiaSymReader": "1.1.0", + "System.ValueTuple": "4.3.1" + }, + "runtimes": { + "win7-x86": { }, + "win7-x64": { }, + "osx.10.11-x64": { }, + "ubuntu.14.04-x64": { } + }, + "frameworks": { + "netstandard1.6": { + "imports": "portable-net45+win8" + } + } +} + diff --git a/src/fsharp/Fsc-proto/Fsc-proto.fsproj b/src/fsharp/Fsc-proto/Fsc-proto.fsproj index a800602869..881d525e18 100644 --- a/src/fsharp/Fsc-proto/Fsc-proto.fsproj +++ b/src/fsharp/Fsc-proto/Fsc-proto.fsproj @@ -419,6 +419,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 c69038c4ae..0854f76eda 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -990,6 +990,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) @@ -1282,6 +1284,43 @@ 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 + + let istate = ref istate + for kv in tcConfigB.packageManagerLines do + let packageManagerKey,packageManagerLines = kv.Key,kv.Value + match packageManagerLines with + | [] -> () + | (_,m)::_ -> + let packageManagerTextLines = packageManagerLines |> List.map fst + match DependencyManagerIntegration.tryFindDependencyManagerByKey m packageManagerKey with + | None -> + errorR(DependencyManagerIntegration.createPackageManagerUnknownError packageManagerKey m) + | Some packageManager -> + match DependencyManagerIntegration.resolve packageManager tcConfigB.implicitIncludeDir "stdin.fsx" "stdin.fsx" m packageManagerTextLines with + | None -> () // error already reported + | Some (loadScript,additionalIncludeFolders) -> + for folder in additionalIncludeFolders do + tcConfigB.AddIncludePath(m,folder,"") + + match loadScript with + | Some loadScript -> istate := fsiDynamicCompiler.EvalSourceFiles (ctok, !istate, m, [loadScript], lexResourceManager, errorLogger) + | None -> () + + !istate + member fsiDynamicCompiler.ProcessMetaCommandsFromInputAsInteractiveCommands(ctok, istate, sourceFile, inp) = WithImplicitHome (tcConfigB, directoryName sourceFile) @@ -1289,6 +1328,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)) @@ -1905,34 +1945,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) 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 0000000000..38c8173ec8 --- /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 1380330716..f18e59e67d 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/tests/fsharpqa/Source/InteractiveSession/Paket/.paket/paket.exe b/tests/fsharpqa/Source/InteractiveSession/Paket/.paket/paket.exe new file mode 100644 index 0000000000..716fe25495 Binary files /dev/null and b/tests/fsharpqa/Source/InteractiveSession/Paket/.paket/paket.exe differ diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/paket.dependencies b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/paket.dependencies new file mode 100644 index 0000000000..2c6d492407 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/paket.dependencies @@ -0,0 +1,3 @@ +framework: net461 +source https://nuget.org/api/v2 +nuget SomethingThatShouldReallyNotExistAtAll \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/plot.fsx new file mode 100644 index 0000000000..ef9314286b --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithBrokenDepsFile/plot.fsx @@ -0,0 +1,7 @@ +//Package resolution + +#r "paket: " + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.dependencies b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.dependencies new file mode 100644 index 0000000000..e86188b05d --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.dependencies @@ -0,0 +1,7 @@ +framework: net461 +source https://nuget.org/api/v2 + +group Test + framework: net461 + source https://nuget.org/api/v2 + nuget NUnit diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.lock b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.lock new file mode 100644 index 0000000000..3a629dc061 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/paket.lock @@ -0,0 +1,7 @@ +FRAMEWORK: NET461 + +GROUP Test +FRAMEWORK: NET461 +NUGET + remote: https://www.nuget.org/api/v2 + NUnit (3.6.1) diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/plot.fsx new file mode 100644 index 0000000000..d7b6631287 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsCanHaveGroups/plot.fsx @@ -0,0 +1,5 @@ +#r "paket: nuget XPlot.Plotly" + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.dependencies b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.dependencies new file mode 100644 index 0000000000..9cdb7e7a90 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.dependencies @@ -0,0 +1,4 @@ +framework: net461 +source https://nuget.org/api/v2 +nuget FSharp.Data +nuget XPlot.Plotly \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.lock b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.lock new file mode 100644 index 0000000000..d34f29fc82 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/paket.lock @@ -0,0 +1,7 @@ +FRAMEWORK: NET461 +NUGET + remote: https://www.nuget.org/api/v2 + FSharp.Data (2.3.2) + Newtonsoft.Json (9.0.1) + XPlot.Plotly (1.4.2) + Newtonsoft.Json (9.0.1) diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/plot.fsx new file mode 100644 index 0000000000..bf3945e185 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithDepsFile/plot.fsx @@ -0,0 +1,5 @@ +#r "paket: " + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/package-feed/nunit.2.6.4.nupkg b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/package-feed/nunit.2.6.4.nupkg new file mode 100644 index 0000000000..379b15bf5c Binary files /dev/null and b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/package-feed/nunit.2.6.4.nupkg differ diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/paket.dependencies b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/paket.dependencies new file mode 100644 index 0000000000..7ff2ca4a3c --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/paket.dependencies @@ -0,0 +1,3 @@ +framework: net461 +source ./package-feed +nuget NUnit \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/script1.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/script1.fsx new file mode 100644 index 0000000000..14514c13f0 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithLocalSources/script1.fsx @@ -0,0 +1,6 @@ +#r "paket: " + +open NUnit.Framework + +[] +let f = () \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/.paket/paket.exe.config b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/.paket/paket.exe.config new file mode 100644 index 0000000000..2fc733f95b --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/.paket/paket.exe.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/UseGlobbing.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/UseGlobbing.fsx new file mode 100644 index 0000000000..a4a73c8a67 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/UseGlobbing.fsx @@ -0,0 +1,4 @@ +#r "paket: " +#load @"fsharp\FAKE\src\app\FakeLib\Globbing\Globbing.fs" + +let f = Fake.Globbing.search \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/paket.dependencies b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/paket.dependencies new file mode 100644 index 0000000000..46698a173d --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/PaketWithRemoteFile/paket.dependencies @@ -0,0 +1,3 @@ +source https://nuget.org/api/v2 +nuget NUnit +github fsharp/FAKE src/app/FakeLib/Globbing/Globbing.fs \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketFailTest/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketFailTest/plot.fsx new file mode 100644 index 0000000000..5170eef2e5 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketFailTest/plot.fsx @@ -0,0 +1,7 @@ +//Package resolution + +#r "paket: nuget SomeInvalidNugetPackage" + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTest/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTest/plot.fsx new file mode 100644 index 0000000000..d7b6631287 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTest/plot.fsx @@ -0,0 +1,5 @@ +#r "paket: nuget XPlot.Plotly" + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/.paket/paket.exe.config b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/.paket/paket.exe.config new file mode 100644 index 0000000000..2fc733f95b --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/.paket/paket.exe.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/plot.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/plot.fsx new file mode 100644 index 0000000000..eba5057696 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/SimplePaketTestTrim/plot.fsx @@ -0,0 +1,8 @@ +#r "paket:nuget XPlot.Plotly" + +// note we don't have space at paket:nuget +// this should work + +open XPlot.Plotly + +Chart.Line [ 1 .. 10 ] \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/env.lst b/tests/fsharpqa/Source/InteractiveSession/Paket/env.lst new file mode 100644 index 0000000000..129dc8afe7 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/env.lst @@ -0,0 +1,9 @@ + SOURCE="SimplePaketTest\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # simple direct deps + SOURCE="SimplePaketTestTrim\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # simple direct deps, with missing space in directive + SOURCE="SimplePaketFailTest\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # non-existent package + SOURCE="PaketWithDepsFile\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # with deps file + SOURCE="PaketWithDepsCanHaveGroups\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # with deps file with groups (appending to groups may kill the source) + SOURCE="PaketWithBrokenDepsFile\\plot.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # with broken deps file + SOURCE="PaketWithRemoteFile\\UseGlobbing.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # use paket remote file + SOURCE="PaketWithLocalSources\\script1.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # with local nupgk + SOURCE="transitiveloadscripts\\a.fsx" COMPILE_ONLY=1 FSIMODE=FEED SCFLAGS="--nologo" # transitive load script woes \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/a.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/a.fsx new file mode 100644 index 0000000000..0cd7912ba7 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/a.fsx @@ -0,0 +1,4 @@ +#load "b.fsx" +#load "c.fsx" + +open FSharp.Data \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/b.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/b.fsx new file mode 100644 index 0000000000..5517e10e27 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/b.fsx @@ -0,0 +1,6 @@ +#load "c.fsx" +#r "paket: nuget FSharp.Data" +printfn "---->b.fsx" +open FSharp.Data +open NUnit +printfn "---->b.fsx///" \ No newline at end of file diff --git a/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/c.fsx b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/c.fsx new file mode 100644 index 0000000000..6b6afab3b4 --- /dev/null +++ b/tests/fsharpqa/Source/InteractiveSession/Paket/transitiveloadscripts/c.fsx @@ -0,0 +1,2 @@ +#r "paket: nuget NUnit" +printfn "---->c.fsx" \ No newline at end of file diff --git a/tests/fsharpqa/Source/test.lst b/tests/fsharpqa/Source/test.lst index d4301fedb9..4f8ee97445 100644 --- a/tests/fsharpqa/Source/test.lst +++ b/tests/fsharpqa/Source/test.lst @@ -251,6 +251,7 @@ Misc01 EntryPoint Misc01 Globalization Misc01,NoMT Import Misc01,NoMT ..\..\..\testsprivate\fsharpqa\Source\InteractiveSession\AssemblyLoading +Misc01,NoMT InteractiveSession\Paket Misc01,NoMT InteractiveSession\Misc Misc01,NoMT InteractiveSession\Misc\GenericConstraintWoes\issue2411 Misc01 Libraries\Control diff --git a/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest b/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest index a1ec86e0b5..a9598f7fe1 100644 --- a/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest +++ b/vsintegration/Vsix/VisualFSharpFull/Source.extension.vsixmanifest @@ -17,6 +17,7 @@ + @@ -40,6 +41,7 @@ + diff --git a/vsintegration/Vsix/VisualFSharpFull/VisualFSharpFull.csproj b/vsintegration/Vsix/VisualFSharpFull/VisualFSharpFull.csproj index a5e8df4400..b2979bb921 100644 --- a/vsintegration/Vsix/VisualFSharpFull/VisualFSharpFull.csproj +++ b/vsintegration/Vsix/VisualFSharpFull/VisualFSharpFull.csproj @@ -311,6 +311,10 @@ false True + + {25555554-522d-4cf7-97e4-ba940f0b18f3} + FSharp.DependencyManager.Paket + diff --git a/vsintegration/Vsix/VisualFSharpOpenSource/Source.extension.vsixmanifest b/vsintegration/Vsix/VisualFSharpOpenSource/Source.extension.vsixmanifest index 4b992a555a..e88f434cc9 100644 --- a/vsintegration/Vsix/VisualFSharpOpenSource/Source.extension.vsixmanifest +++ b/vsintegration/Vsix/VisualFSharpOpenSource/Source.extension.vsixmanifest @@ -17,6 +17,7 @@ + @@ -40,6 +41,7 @@ + diff --git a/vsintegration/Vsix/VisualFSharpOpenSource/VisualFSharpOpenSource.csproj b/vsintegration/Vsix/VisualFSharpOpenSource/VisualFSharpOpenSource.csproj index 9a948205a9..9d823ed074 100644 --- a/vsintegration/Vsix/VisualFSharpOpenSource/VisualFSharpOpenSource.csproj +++ b/vsintegration/Vsix/VisualFSharpOpenSource/VisualFSharpOpenSource.csproj @@ -310,6 +310,10 @@ false True + + {25555554-522d-4cf7-97e4-ba940f0b18f3} + FSharp.DependencyManager.Paket + diff --git a/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs b/vsintegration/tests/unittests/Tests.LanguageService.ErrorList.fs index 82a1e3a40a..39e452e21b 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) =