diff --git a/integrationtests/Paket.IntegrationTests/ResolverSkipsConflictsFastSpecs.fs b/integrationtests/Paket.IntegrationTests/ResolverSkipsConflictsFastSpecs.fs index 67a1ac6983..3d25f5943f 100644 --- a/integrationtests/Paket.IntegrationTests/ResolverSkipsConflictsFastSpecs.fs +++ b/integrationtests/Paket.IntegrationTests/ResolverSkipsConflictsFastSpecs.fs @@ -26,6 +26,16 @@ let ``#1166 Should resolve Nancy without timeout``() = lockFile.Groups.[Constants.MainDependencyGroup].Resolution.[PackageName "Nancy"].Version |> shouldBeGreaterThan (SemVer.Parse "1.1") +[] +let ``#2289 Paket 4.x install command takes hours to complete``() = + let lockFile = install "i002289-resolve-nunit-timeout" + let nunitVersion = + lockFile.Groups.[Constants.MainDependencyGroup].Resolution.[PackageName "NUnit"].Version + nunitVersion + |> shouldBeGreaterThan (SemVer.Parse "2.0") + nunitVersion + |> shouldBeSmallerThan (SemVer.Parse "3.0") + [] [] let ``#1174 Should find Ninject error``() = diff --git a/integrationtests/scenarios/i002289-resolve-nunit-timeout/before/paket.dependencies b/integrationtests/scenarios/i002289-resolve-nunit-timeout/before/paket.dependencies new file mode 100644 index 0000000000..8a25de7d36 --- /dev/null +++ b/integrationtests/scenarios/i002289-resolve-nunit-timeout/before/paket.dependencies @@ -0,0 +1,52 @@ +redirects: on +framework: net46 + +source https://api.nuget.org/v3/index.json + +nuget Akka.Persistence.SqlServer +nuget SQLProvider +nuget Microsoft.FSharpLu.Json +nuget Microsoft.Net.Http +nuget Microsoft.AspNet.WebApi.Client +nuget FSharp.Core redirects: force +nuget FSharp.Configuration +nuget FSharp.Formatting +nuget FSharp.Data +nuget FSharp.Data.TypeProviders +nuget Newtonsoft.Json +nuget FAKE +nuget SourceLink.Fake +nuget Akka +nuget Akka.FSharp +nuget Akka.Persistence +nuget Akka.Persistence.FSharp +nuget Akka.Persistence.Sql.Common +nuget Akka.Persistence.Query +nuget Akka.Persistence.Query.Sql +nuget Akka.Logger.Serilog +nuget Akka.Monitoring +nuget Akka.Monitoring.StatsD +nuget Akka.TestKit.NUnit +nuget Akka.TestKit.Xunit2 +nuget Akka.Serialization.Wire +nuget FsPickler +nuget Serilog +nuget Serilog.Sinks.ElasticSearch +nuget Nest ~> 5 +nuget RabbitMQ.Client +nuget SSH.NET +nuget Topshelf +nuget Topshelf.Serilog +nuget NUnit ~> 2 +nuget NUnit.Runners +nuget NUnitTestAdapter +nuget TickSpec.NUnit content: none +nuget FsCheck +nuget FsCheck.Nunit +nuget System.Collections.Immutable +nuget Suave +nuget OctopusTools +nuget Suave.Swagger +nuget Quartz +nuget Akka.Quartz.Actor +nuget Oracle.ManagedDataAccess \ No newline at end of file diff --git a/src/Paket.Core/Common/Utils.fs b/src/Paket.Core/Common/Utils.fs index 4e34b49039..f671e524ef 100644 --- a/src/Paket.Core/Common/Utils.fs +++ b/src/Paket.Core/Common/Utils.fs @@ -999,3 +999,14 @@ module Seq = ) |> fun (xs,ys) -> List.rev xs :> seq<_>, List.rev ys :> seq<_> +[] +module List = + // Try to find an element in a list. + // If found, return the element and the list WITHOUT the element. + // If not found, return None and the whole original list. + let tryExtractOne fn values = + match List.tryFindIndex fn values with + | Some i -> + let v = values.[i] + Some v, (values.[0 .. i - 1 ] @ values.[i + 1 .. ]) + | None -> None, values diff --git a/src/Paket.Core/Dependencies/PackageResolver.fs b/src/Paket.Core/Dependencies/PackageResolver.fs index 53541d65bb..898e630d33 100644 --- a/src/Paket.Core/Dependencies/PackageResolver.fs +++ b/src/Paket.Core/Dependencies/PackageResolver.fs @@ -272,7 +272,7 @@ let calcOpenRequirements (exploredPackage:ResolvedPackage,globalFrameworkRestric Settings = { dependency.Settings with FrameworkRestrictions = newRestrictions } }) |> Set.filter (fun d -> resolverStep.ClosedRequirements - |> Seq.exists (fun x -> + |> Set.exists (fun x -> x.Name = d.Name && x.Settings.FrameworkRestrictions = d.Settings.FrameworkRestrictions && (x = d || @@ -281,7 +281,7 @@ let calcOpenRequirements (exploredPackage:ResolvedPackage,globalFrameworkRestric |> not) |> Set.filter (fun d -> resolverStep.OpenRequirements - |> Seq.exists (fun x -> x.Name = d.Name && (x = d || x.VersionRequirement.Range.IsGlobalOverride) && x.Settings.FrameworkRestrictions = d.Settings.FrameworkRestrictions) + |> Set.exists (fun x -> x.Name = d.Name && (x = d || x.VersionRequirement.Range.IsGlobalOverride) && x.Settings.FrameworkRestrictions = d.Settings.FrameworkRestrictions) |> not) |> Set.union rest @@ -390,7 +390,7 @@ let private explorePackageConfig getPackageDetailsF (pkgConfig:PackageConfig) = type StackPack = { ExploredPackages : Dictionary - KnownConflicts : HashSet * ((SemVerInfo * PackageSource list) list * bool) option> + KnownConflicts : HashSet * ((SemVerInfo * PackageSource list) list * bool) option> ConflictHistory : Dictionary } @@ -494,23 +494,26 @@ let private getCompatibleVersions compatibleVersions, false, tryRelaxed -let private getConflicts (currentStep:ResolverStep) (currentRequirement:PackageRequirement) (knownConflicts:HashSet<_>) = +let private getConflicts (currentStep:ResolverStep) (currentRequirement:PackageRequirement) (knownConflicts:HashSet * ((SemVerInfo * PackageSource list) list * bool) option>) = + let allRequirements = - currentStep.OpenRequirements - |> Set.filter (fun r -> r.Graph |> List.contains currentRequirement |> not) - |> Set.union currentStep.ClosedRequirements + Set.toSeq currentStep.OpenRequirements + |> Seq.filter (fun r -> r.Graph |> List.contains currentRequirement |> not) + |> Seq.append currentStep.ClosedRequirements + |> HashSet knownConflicts |> Seq.map (fun (conflicts,selectedVersion) -> match selectedVersion with - | None when Set.isSubset conflicts allRequirements -> conflicts + | None when conflicts.IsSubsetOf allRequirements -> conflicts | Some(selectedVersion,_) -> let n = (Seq.head conflicts).Name match currentStep.FilteredVersions |> Map.tryFind n with - | Some(v,_) when v = selectedVersion && Set.isSubset conflicts allRequirements -> conflicts - | _ -> Set.empty - | _ -> Set.empty) - |> Set.unionMany + | Some(v,_) when v = selectedVersion && conflicts.IsSubsetOf allRequirements -> conflicts + | _ -> HashSet() + | _ -> HashSet()) + |> Seq.collect id + |> HashSet let private getCurrentRequirement packageFilter (openRequirements:Set) (conflictHistory:Dictionary<_,_>) = @@ -575,7 +578,7 @@ let private boostConflicts match conflicts with | c::_ -> let selectedVersion = Map.tryFind c.Name filteredVersions - let key = conflicts |> Set.ofList,selectedVersion + let key = conflicts |> HashSet,selectedVersion stackpack.KnownConflicts.Add key |> ignore let reportThatResolverIsTakingLongerThanExpected = @@ -597,25 +600,24 @@ let private boostConflicts [] -type private StepFlags (ready:bool,useUnlisted:bool,hasUnlisted:bool,forceBreak:bool,firstTrial:bool,unlistedSearch:bool) = - member __.Ready = ready - member __.UseUnlisted = useUnlisted - member __.HasUnlisted = hasUnlisted - member __.ForceBreak = forceBreak - member __.FirstTrial = firstTrial - member __.UnlistedSearch = unlistedSearch - member private self.Display - with get () = - sprintf - "[< FLAGS >]\n\ - | Ready - %b\n\ - | UseUnlisted - %b\n\ - | HasUnlisted - %b\n\ - | ForceBreak - %b\n\ - | FirstTrial - %b\n\ - | UnlistedSearch - %b\n" - ready useUnlisted hasUnlisted forceBreak firstTrial unlistedSearch - override self.ToString() = self.Display +type private StepFlags = { + Ready : bool + UseUnlisted : bool + HasUnlisted : bool + ForceBreak : bool + FirstTrial : bool + UnlistedSearch : bool +} with + override self.ToString () = + sprintf + "[< FLAGS >]\n\ + | Ready - %b\n\ + | UseUnlisted - %b\n\ + | HasUnlisted - %b\n\ + | ForceBreak - %b\n\ + | FirstTrial - %b\n\ + | UnlistedSearch - %b\n" + self.Ready self.UseUnlisted self.HasUnlisted self.ForceBreak self.FirstTrial self.UnlistedSearch type private Stage = | Step of currentConflict : (ConflictState * ResolverStep * PackageRequirement) * priorConflictSteps : (ConflictState * ResolverStep * PackageRequirement * seq * StepFlags) list @@ -649,14 +651,33 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate let rec step (stage:Stage) (stackpack:StackPack) compatibleVersions (flags:StepFlags) = - let inline fuseConflicts currentConflict priorConflictSteps = - match currentConflict, priorConflictSteps with - | currentConflict, (lastConflict,lastStep,lastRequirement,lastCompatibleVersions,lastFlags)::priorConflictSteps -> + let inline fuseConflicts currentConflict priorConflictSteps conflicts = + let findMatchingStep priorConflictSteps = + let currentNames = + conflicts + |> Seq.collect (fun c -> + let graphNameList = + c.Graph |> List.map (fun (pr:PackageRequirement) -> pr.Name) + c.Name :: graphNameList) + |> Seq.toArray + priorConflictSteps + |> List.tryExtractOne (fun (_,_,lastRequirement:PackageRequirement,_,_) -> + currentNames |> Array.contains lastRequirement.Name) + + match findMatchingStep priorConflictSteps with + | None, [] -> currentConflict + | (Some head), priorConflictSteps -> + let (lastConflict, lastStep, lastRequirement, lastCompatibleVersions, lastFlags) = head let continueConflict = { currentConflict with VersionsToExplore = lastConflict.VersionsToExplore } - step (Inner((continueConflict,lastStep,lastRequirement),priorConflictSteps)) stackpack lastCompatibleVersions lastFlags - | currentConflict, [] -> currentConflict - + step (Inner((continueConflict,lastStep,lastRequirement), priorConflictSteps)) stackpack lastCompatibleVersions lastFlags + // could not find a specific package - go back one step + | None, head :: priorConflictSteps -> + let (lastConflict, lastStep, lastRequirement, lastCompatibleVersions, lastFlags) = head + let continueConflict = + { currentConflict with VersionsToExplore = lastConflict.VersionsToExplore } + step (Inner((continueConflict,lastStep,lastRequirement), priorConflictSteps)) stackpack lastCompatibleVersions lastFlags + match stage with | Step((currentConflict,currentStep,_currentRequirement), priorConflictSteps) -> if Set.isEmpty currentStep.OpenRequirements then @@ -678,10 +699,8 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate && not (conflicts |> Set.exists (fun r -> r = lastRequirement || r.Graph |> List.contains lastRequirement)) -> - let flags = - StepFlags(flags.Ready,flags.UseUnlisted,flags.HasUnlisted,true,flags.FirstTrial,flags.UnlistedSearch) - step (Inner((continueConflict,lastStep,lastRequirement),priorConflictSteps)) stackpack lastCompatibleVersions lastFlags + step (Inner((continueConflict,lastStep,lastRequirement),priorConflictSteps)) stackpack lastCompatibleVersions { flags with ForceBreak = true } | _ -> step (Inner((continueConflict,lastStep,lastRequirement),priorConflictSteps)) stackpack lastCompatibleVersions lastFlags @@ -703,15 +722,15 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate let currentConflict = let getVersionsF = getVersionsF currentRequirement.Sources ResolverStrategy.Max groupName - if Set.isEmpty conflicts then + if Seq.isEmpty conflicts then { currentConflict with - Status = Resolution.Conflict(currentStep,Set.empty,currentRequirement,getVersionsF)} + Status = Resolution.Conflict (currentStep,Set.empty,currentRequirement,getVersionsF)} else { currentConflict with - Status = Resolution.Conflict(currentStep,conflicts,Seq.head conflicts,getVersionsF)} + Status = Resolution.Conflict (currentStep,set conflicts,Seq.head conflicts,getVersionsF)} - if not (Set.isEmpty conflicts) then - fuseConflicts currentConflict priorConflictSteps + if not (Seq.isEmpty conflicts) then + fuseConflicts currentConflict priorConflictSteps conflicts else let compatibleVersions,globalOverride,tryRelaxed = getCompatibleVersions currentStep groupName currentRequirement getVersionsF @@ -721,7 +740,7 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate let currentConflict = { currentConflict with - Conflicts = conflicts + Conflicts = set conflicts TryRelaxed = tryRelaxed GlobalOverride = globalOverride } @@ -730,21 +749,23 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate boostConflicts currentStep.FilteredVersions currentRequirement stackpack currentConflict else currentConflict, stackpack - let flags = - StepFlags - ( ready = false - , useUnlisted = false - , hasUnlisted = false - , forceBreak = flags.ForceBreak - , firstTrial = flags.FirstTrial - , unlistedSearch = false - ) + let flags = { + flags with + Ready = false + UseUnlisted = false + HasUnlisted = false + UnlistedSearch = false + } step (Outer ((conflictState,currentStep,currentRequirement),priorConflictSteps)) stackpack compatibleVersions flags | Outer ((currentConflict,currentStep,currentRequirement), priorConflictSteps) -> - if flags.Ready then - fuseConflicts currentConflict priorConflictSteps + if flags.Ready then + fuseConflicts currentConflict priorConflictSteps (HashSet [ currentRequirement ]) else - let flags = StepFlags(flags.Ready,flags.UseUnlisted,flags.HasUnlisted,false,true,flags.UnlistedSearch) + let flags = { + flags with + ForceBreak = false + FirstTrial = true + } let currentConflict = { currentConflict with VersionsToExplore = compatibleVersions } step (Inner ((currentConflict,currentStep,currentRequirement), priorConflictSteps)) stackpack compatibleVersions flags @@ -758,14 +779,21 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate then // if it's been determined that an unlisted package must be used, ready must be set to false verbosefn "\nSearching for compatible unlisted package\n" - StepFlags(false,true,flags.HasUnlisted,flags.ForceBreak,flags.FirstTrial,true) + { flags with + Ready = false + UseUnlisted = true + UnlistedSearch = true + } else - StepFlags(true,flags.UseUnlisted,flags.HasUnlisted,flags.ForceBreak,flags.FirstTrial,false) + { flags with + Ready = true + UnlistedSearch = true + } step (Outer((currentConflict,currentStep,currentRequirement), priorConflictSteps)) stackpack compatibleVersions flags else - let flags = StepFlags(flags.Ready,flags.UseUnlisted,flags.HasUnlisted,flags.ForceBreak,false,flags.UnlistedSearch) + let flags = { flags with FirstTrial = false } let (version,sources) & versionToExplore = Seq.head currentConflict.VersionsToExplore let currentConflict = @@ -787,8 +815,7 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate | stackpack, Some(alreadyExplored,exploredPackage) -> let hasUnlisted = exploredPackage.Unlisted || flags.HasUnlisted - let flags = - StepFlags(flags.Ready,flags.UseUnlisted,hasUnlisted,flags.ForceBreak,flags.FirstTrial,flags.UnlistedSearch) + let flags = { flags with HasUnlisted = hasUnlisted } if exploredPackage.Unlisted && not flags.UseUnlisted then if not alreadyExplored then @@ -835,19 +862,18 @@ let Resolve (getVersionsF, getPackageDetailsF, groupName:GroupName, globalStrate let stackpack = { ExploredPackages = Dictionary() - KnownConflicts = (HashSet() : HashSet * ((SemVerInfo * PackageSource list) list * bool) option>) + KnownConflicts = (HashSet() : HashSet * ((SemVerInfo * PackageSource list) list * bool) option>) ConflictHistory = (Dictionary() : Dictionary) } - let flags = - StepFlags - ( ready = false - , useUnlisted = false - , hasUnlisted = false - , forceBreak = false - , firstTrial = true - , unlistedSearch = false - ) + let flags = { + Ready = false + UseUnlisted = false + HasUnlisted = false + ForceBreak = false + FirstTrial = true + UnlistedSearch = false + } match step (Step((currentConflict,startingStep,currentRequirement),[])) stackpack Seq.empty flags with | { Status = Resolution.Conflict _ } as conflict -> diff --git a/src/Paket.Core/Versioning/PlatformMatching.fs b/src/Paket.Core/Versioning/PlatformMatching.fs index 38711e6dcf..bcad68c3f2 100644 --- a/src/Paket.Core/Versioning/PlatformMatching.fs +++ b/src/Paket.Core/Versioning/PlatformMatching.fs @@ -1,6 +1,7 @@ module Paket.PlatformMatching open System +open ProviderImplementation.AssemblyReader.Utils.SHA1 [] let MaxPenalty = 1000000 @@ -27,7 +28,7 @@ let tryGetProfile platforms = |> List.filter (fun p -> knownInPortable |> Seq.exists ((=) p)) |> List.sort - KnownTargetProfiles.AllPortableProfiles |> Seq.tryFind (snd >> List.sort >> (=) filtered) + KnownTargetProfiles.AllPortableProfiles |> Seq.tryFind (snd >> (=) filtered) |> Option.map PortableProfile let getPlatformPenalty = @@ -77,36 +78,43 @@ let comparePaths (p1 : PathPenalty) (p2 : PathPenalty) = // prefer full framework over portable if platformCount1 = 1 && platformCount2 > 1 then -1 - else if platformCount1 > 1 && platformCount2 = 1 then + elif platformCount1 > 1 && platformCount2 = 1 then 1 // prefer lower version penalty - else if snd p1 < snd p2 then + elif snd p1 < snd p2 then -1 - else if snd p1 > snd p2 then + elif snd p1 > snd p2 then 1 // prefer portable platform whith less platforms - else if platformCount1 < platformCount2 then + elif platformCount1 < platformCount2 then -1 - else if platformCount1 > platformCount2 then + elif platformCount1 > platformCount2 then 1 else 0 + +let collectPlatforms = + let rec loop (acc:FrameworkIdentifier list) (framework:FrameworkIdentifier) (profls:TargetProfile list) = + match profls with + | [] -> acc + | (SinglePlatform f)::tl -> + if f.SupportedPlatforms |> List.exists ((=) framework) + then loop (f::acc) framework tl + else loop acc framework tl + | _::tl -> loop acc framework tl + memoize (fun (framework,profls) -> loop ([]:FrameworkIdentifier list) framework profls) + let platformsSupport = let rec platformsSupport platform platforms = if List.isEmpty platforms then MaxPenalty elif platforms |> List.exists ((=) platform) then 1 else - platforms - |> List.collect (fun (p : FrameworkIdentifier) -> - KnownTargetProfiles.AllProfiles - |> List.choose (function - | SinglePlatform f -> Some f - | _ -> None) - |> List.filter (fun f -> f.SupportedPlatforms |> List.exists ((=) p))) - |> platformsSupport platform - |> (+) 1 - + platforms |> Array.ofList + |> Array.Parallel.map (fun (p : FrameworkIdentifier) -> + collectPlatforms (p,KnownTargetProfiles.AllProfiles) + ) |> List.concat + |> platformsSupport platform |> (+) 1 memoize (fun (platform,platforms) -> platformsSupport platform platforms) diff --git a/src/Paket.Core/Versioning/Requirements.fs b/src/Paket.Core/Versioning/Requirements.fs index 58da37a456..213324d45f 100644 --- a/src/Paket.Core/Versioning/Requirements.fs +++ b/src/Paket.Core/Versioning/Requirements.fs @@ -791,25 +791,30 @@ type PackageRequirement = member this.Depth = this.Graph.Length static member Compare(x,y,startWithPackage:PackageFilter option,boostX,boostY) = - if x = y then 0 else - seq { - yield compare - (not x.VersionRequirement.Range.IsGlobalOverride,x.Depth) - (not y.VersionRequirement.Range.IsGlobalOverride,y.Depth) - yield match startWithPackage with + if obj.ReferenceEquals(x, y) then 0 else + let c = compare + (not x.VersionRequirement.Range.IsGlobalOverride,x.Depth) + (not y.VersionRequirement.Range.IsGlobalOverride,y.Depth) + if c <> 0 then c else + let c = match startWithPackage with | Some filter when filter.Match x.Name -> -1 | Some filter when filter.Match y.Name -> 1 | _ -> 0 - yield -compare x.ResolverStrategyForDirectDependencies y.ResolverStrategyForDirectDependencies - yield -compare x.ResolverStrategyForTransitives y.ResolverStrategyForTransitives - yield compare boostX boostY - yield -compare x.VersionRequirement y.VersionRequirement - yield compare x.Settings.FrameworkRestrictions y.Settings.FrameworkRestrictions - yield compare x.Parent y.Parent - yield compare x.Name y.Name - } - |> Seq.tryFind (fun x -> x <> 0) - |> Option.fold (fun _ x -> x) 0 + if c <> 0 then c else + let c = -compare x.ResolverStrategyForDirectDependencies y.ResolverStrategyForDirectDependencies + if c <> 0 then c else + let c = -compare x.ResolverStrategyForTransitives y.ResolverStrategyForTransitives + if c <> 0 then c else + let c = compare boostX boostY + if c <> 0 then c else + let c = -compare x.VersionRequirement y.VersionRequirement + if c <> 0 then c else + let c = compare x.Settings.FrameworkRestrictions y.Settings.FrameworkRestrictions + if c <> 0 then c else + let c = compare x.Parent y.Parent + if c <> 0 then c else + let c = compare x.Name y.Name + if c <> 0 then c else 0 interface System.IComparable with member this.CompareTo that = diff --git a/src/Paket/Paket.fsproj b/src/Paket/Paket.fsproj index bac8c0fa39..e17bcb73cb 100644 --- a/src/Paket/Paket.fsproj +++ b/src/Paket/Paket.fsproj @@ -27,15 +27,6 @@ 3 - Project - paket.exe - Project - install - D:\temp\coreclrtest - install - C:\dev\src\Paket\integrationtests\scenarios\loading-scripts\dependencies-file-flag\temp - install - C:\PROJ\Paket\integrationtests\scenarios\i001145-excludes\temp true @@ -45,27 +36,6 @@ 3 - D:\code\Paket\integrationtests\scenarios\i001663-google-apis\temp - update - update --hard - install - D:\code\tempp - update --keep-major - D:\code\Paket\integrationtests\scenarios\i001701-keep-major\temp - update - D:\code\PaketKopie - update -f - D:\code\Paket\integrationtests\scenarios\i001117-aws\temp - update - D:\temp\repo3 - install -v - D:\temp\IconPacksTestApp\src - update - D:\temp\unlisted - install - D:\temp\PaketWithProblem1 - install - D:\temp\paketRestoreBug 14.0