diff --git a/integrationtests/Paket.IntegrationTests/PaketCoreSpecs.fs b/integrationtests/Paket.IntegrationTests/PaketCoreSpecs.fs index 3d3acfa4d0..08c5711d9f 100644 --- a/integrationtests/Paket.IntegrationTests/PaketCoreSpecs.fs +++ b/integrationtests/Paket.IntegrationTests/PaketCoreSpecs.fs @@ -32,6 +32,19 @@ let ``#1251 full installer demo``() = lockFile.Groups.[Constants.MainDependencyGroup].Resolution.[PackageName "FAKE"].Version |> shouldBeGreaterThan (SemVer.Parse "4") +[] +let ``apply framework restriction`` ([] force, [] source: string) = + use __ = prepare "i001251-installer-demo" + let deps = $"""source {source} + framework: =net8.0 + nuget Microsoft.AspNetCore.WebUtilities""" + + let dependenciesFile = DependenciesFile.FromSource(scenarioTempPath "i001251-installer-demo",deps) + let lockFile,_,_,_ = UpdateProcess.SelectiveUpdate(dependenciesFile, alternativeProjectRoot, PackageResolver.UpdateMode.Install, SemVerUpdateMode.NoRestriction, force) + + let version = lockFile.Groups.[Constants.MainDependencyGroup].Resolution.[PackageName "Microsoft.AspNetCore.WebUtilities"].Version + Assert.Less(version, SemVer.Parse "9") + [] let ``#1251 install FSharp.Collections.ParallelSeq``() = use __ = prepare "i001251-installer-demo" diff --git a/src/Paket.Core/Dependencies/NuGet.fs b/src/Paket.Core/Dependencies/NuGet.fs index d322946b27..3a222e8046 100644 --- a/src/Paket.Core/Dependencies/NuGet.fs +++ b/src/Paket.Core/Dependencies/NuGet.fs @@ -525,6 +525,7 @@ let rec private getPackageDetails alternativeProjectRoot root force (parameters: DownloadLink = encodeURL nugetObject.DownloadUrl Unlisted = nugetObject.Unlisted LicenseUrl = nugetObject.LicenseUrl + AvailableFrameworks = nugetObject.AvailableFrameworks DirectDependencies = nugetObject.GetDependencies() } } let rec GetPackageDetails alternativeProjectRoot root force (parameters:GetPackageDetailsParameters): Async = diff --git a/src/Paket.Core/Dependencies/NuGetCache.fs b/src/Paket.Core/Dependencies/NuGetCache.fs index 18567f65f6..c2ba72bb77 100644 --- a/src/Paket.Core/Dependencies/NuGetCache.fs +++ b/src/Paket.Core/Dependencies/NuGetCache.fs @@ -121,6 +121,7 @@ type NuGetPackageCache = Unlisted : bool DownloadUrl : string LicenseUrl : string + AvailableFrameworks: FrameworkIdentifier list Version: string CacheVersion: string } @@ -375,7 +376,7 @@ let getNuSpecFromNupkg (fileName:string) = use zip = new ZipArchive(zipToCreate, ZipArchiveMode.Read) let zippedNuspec = zip.Entries |> Seq.find (fun f -> f.FullName.EndsWith ".nuspec") use stream = zippedNuspec.Open() - Nuspec.Load(Path.Combine(fileName, Path.GetFileName zippedNuspec.FullName), stream) + Nuspec.Load(Path.Combine(fileName, Path.GetFileName zippedNuspec.FullName), stream, zip.Entries |> Seq.map _.FullName |> Seq.toArray) let getCacheDataFromExtractedPackage (packageName:PackageName) (version:SemVerInfo) = async { match TryGetFallbackNupkg packageName version with @@ -390,6 +391,7 @@ let getCacheDataFromExtractedPackage (packageName:PackageName) (version:SemVerIn CacheVersion = NuGetPackageCache.CurrentCacheVersion LicenseUrl = nuspec.LicenseUrl Version = version.Normalize() + AvailableFrameworks = nuspec.AvailableFramework Unlisted = false } .WithDependencies nuspec.Dependencies.Value |> Some @@ -406,6 +408,7 @@ let getCacheDataFromExtractedPackage (packageName:PackageName) (version:SemVerIn SourceUrl = targetFolder.FullName CacheVersion = NuGetPackageCache.CurrentCacheVersion LicenseUrl = nuspec.LicenseUrl + AvailableFrameworks = nuspec.AvailableFramework Version = version.Normalize() Unlisted = false } .WithDependencies nuspec.Dependencies.Value diff --git a/src/Paket.Core/Dependencies/NuGetLocal.fs b/src/Paket.Core/Dependencies/NuGetLocal.fs index 7699e1a61d..ebed278cff 100644 --- a/src/Paket.Core/Dependencies/NuGetLocal.fs +++ b/src/Paket.Core/Dependencies/NuGetLocal.fs @@ -74,6 +74,7 @@ let getDetailsFromLocalNuGetPackage isCache alternativeProjectRoot root localNuG CacheVersion = NuGetPackageCache.CurrentCacheVersion LicenseUrl = nuspec.LicenseUrl Version = version.Normalize() + AvailableFrameworks = nuspec.AvailableFramework Unlisted = isCache } .WithDependencies nuspec.Dependencies.Value |> ODataSearchResult.Match diff --git a/src/Paket.Core/Dependencies/NuGetV2.fs b/src/Paket.Core/Dependencies/NuGetV2.fs index 6f7d984549..f89382e2d4 100644 --- a/src/Paket.Core/Dependencies/NuGetV2.fs +++ b/src/Paket.Core/Dependencies/NuGetV2.fs @@ -228,6 +228,7 @@ let private handleODataEntry nugetURL packageName version entry = SourceUrl = nugetURL CacheVersion = NuGetPackageCache.CurrentCacheVersion LicenseUrl = licenseUrl + AvailableFrameworks = [] Version = (SemVer.Parse v).Normalize() Unlisted = publishDate = Constants.MagicUnlistingDate } .WithDependencies dependencies diff --git a/src/Paket.Core/Dependencies/NuGetV3.fs b/src/Paket.Core/Dependencies/NuGetV3.fs index 019481c631..39f757325f 100644 --- a/src/Paket.Core/Dependencies/NuGetV3.fs +++ b/src/Paket.Core/Dependencies/NuGetV3.fs @@ -764,6 +764,7 @@ let getPackageDetails (source:NuGetV3Source) (packageName:PackageName) (version: DownloadUrl = relevantPage.DownloadLink LicenseUrl = catalogData.LicenseUrl Version = version.Normalize() + AvailableFrameworks = [] CacheVersion = NuGetPackageCache.CurrentCacheVersion } .WithDependencies optimized |> ODataSearchResult.Match diff --git a/src/Paket.Core/Dependencies/Nuspec.fs b/src/Paket.Core/Dependencies/Nuspec.fs index bc8e0087df..0866e7f2bc 100644 --- a/src/Paket.Core/Dependencies/Nuspec.fs +++ b/src/Paket.Core/Dependencies/Nuspec.fs @@ -65,14 +65,15 @@ type Nuspec = OfficialName : string // Currently only used for testing Version : string + AvailableFramework: FrameworkIdentifier list LicenseUrl : string IsDevelopmentDependency : bool FrameworkAssemblyReferences : FrameworkAssemblyReference list } - static member All = { Version = ""; References = NuspecReferences.All; Dependencies = lazy []; FrameworkAssemblyReferences = []; OfficialName = ""; LicenseUrl = ""; IsDevelopmentDependency = false } - static member Explicit references = { Version = ""; References = NuspecReferences.Explicit references; Dependencies = lazy []; FrameworkAssemblyReferences = []; OfficialName = ""; LicenseUrl = ""; IsDevelopmentDependency = false } + static member All = { Version = ""; References = NuspecReferences.All; Dependencies = lazy []; FrameworkAssemblyReferences = []; OfficialName = ""; LicenseUrl = ""; IsDevelopmentDependency = false; AvailableFramework = [] } + static member Explicit references = { Version = ""; References = NuspecReferences.Explicit references; Dependencies = lazy []; FrameworkAssemblyReferences = []; OfficialName = ""; LicenseUrl = ""; IsDevelopmentDependency = false; AvailableFramework = [] } /// load the file from an XmlDocument. The fileName is only used for error reporting. - static member private Load(fileName:string, doc:XmlDocument) = + static member private Load(fileName:string, doc:XmlDocument, allPackageFiles: string[]) = let frameworks = doc |> getDescendants "group" @@ -126,6 +127,11 @@ type Nuspec = match doc |> getNode "package" |> optGetNode "metadata" |> optGetNode "developmentDependency" with | Some link -> String.equalsIgnoreCase link.InnerText "true" | None -> false + AvailableFramework = + allPackageFiles + |> Seq.choose FrameworkDetection.DetectFromPath + |> Seq.distinct + |> Seq.toList FrameworkAssemblyReferences = let grouped = doc @@ -142,30 +148,31 @@ type Nuspec = |> List.fold FrameworkRestriction.combineRestrictionsWithOr FrameworkRestriction.EmptySet) } ] } /// load the file from an nuspec text stream. The fileName is only used for error reporting. - static member internal Load(fileName:string, f:Stream) = + static member internal Load(fileName:string, f:Stream, allPackageFiles) = let doc = new XmlDocument() doc.Load f - Nuspec.Load (fileName, doc) + Nuspec.Load (fileName, doc, allPackageFiles) /// load the file from an xml text. The fileName is only used for error reporting. - static member internal Load(fileName:string, text:string) = + static member internal Load(fileName:string, text:string, allPackageFiles) = let doc = new XmlDocument() doc.LoadXml text - Nuspec.Load (fileName, doc) + Nuspec.Load (fileName, doc, allPackageFiles) /// load the file from a given file. - static member Load(fileName : string) = + static member Load(fileName: string) = let fi = FileInfo(fileName) if not fi.Exists then Nuspec.All else + let allPackageFiles = fi.Directory.EnumerateFiles("*", SearchOption.AllDirectories) |> Seq.map _.FullName |> Seq.toArray try use f = File.OpenRead(fi.FullName) - Nuspec.Load(fileName, f) + Nuspec.Load(fileName, f, allPackageFiles) with | exn -> try let text = File.ReadAllText(fi.FullName) // work around mono bug https://github.com/fsprojects/Paket/issues/1189 - Nuspec.Load(fileName, text) + Nuspec.Load(fileName, text, allPackageFiles) with | ex -> raise (IOException("Cannot load " + fileName + Environment.NewLine + "Message: " + ex.Message)) \ No newline at end of file diff --git a/src/Paket.Core/Dependencies/PackageResolver.fs b/src/Paket.Core/Dependencies/PackageResolver.fs index 956d038e1c..e9c8d31e3f 100644 --- a/src/Paket.Core/Dependencies/PackageResolver.fs +++ b/src/Paket.Core/Dependencies/PackageResolver.fs @@ -23,6 +23,7 @@ type PackageDetails = { DownloadLink : string LicenseUrl : string Unlisted : bool + AvailableFrameworks: FrameworkIdentifier list DirectDependencies : DependencySet } @@ -65,6 +66,7 @@ type ResolvedPackage = { Kind : ResolvedPackageKind Settings : InstallSettings Source : PackageSource + AvailableFrameworks : FrameworkIdentifier list } with override this.ToString () = sprintf "%O %O" this.Name this.Version @@ -506,6 +508,7 @@ let private explorePackageConfig (getPackageDetailsBlock:PackageDetailsSyncFunc) Kind = if Set.contains packageDetails.Name pkgConfig.CliTools then ResolvedPackageKind.DotnetCliTool else ResolvedPackageKind.Package IsRuntimeDependency = false + AvailableFrameworks = packageDetails.AvailableFrameworks } with | exn -> @@ -1351,7 +1354,18 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre conflictingWithOpen |> Seq.append conflictingWithClosed) + let conflictingFramework = + match exploredPackage.AvailableFrameworks with + | [] -> [] + | _ -> + exploredPackage.AvailableFrameworks + |> Seq.exists (fun framework -> exploredPackage.Settings.FrameworkRestrictions.IsSupersetOf(FrameworkRestriction.Exactly(framework))) + |> function + | false -> [exploredPackage.Name, VersionRequirement (VersionRange.Maximum exploredPackage.Version, PreReleaseStatus.All)] + | true -> [] + let canTakePackage = + Seq.isEmpty conflictingFramework && Seq.isEmpty conflictingResolvedPackages && Seq.isEmpty conflictingDepsRanges @@ -1381,9 +1395,13 @@ let Resolve (getVersionsRaw : PackageVersionsFunc, getPreferredVersionsRaw : Pre getVersionsBlock ResolverStrategy.Max (GetPackageVersionsParameters.ofParams currentRequirement.Sources groupName packName) currentStep let conflictingPackageName,vr = - match Seq.tryHead conflictingResolvedPackages with - | Some (conflictingPackage,(_,vr,_)) -> conflictingPackage.Name,vr - | None -> Seq.head conflictingDepsRanges + conflictingFramework + |> Seq.tryHead + |> Option.defaultWith (fun () -> + match Seq.tryHead conflictingResolvedPackages with + | Some (conflictingPackage,(_,vr,_)) -> conflictingPackage.Name,vr + | None -> Seq.head conflictingDepsRanges + ) let currentConflict = { currentConflict with diff --git a/src/Paket.Core/PaketConfigFiles/LockFile.fs b/src/Paket.Core/PaketConfigFiles/LockFile.fs index 91006224e4..651fe6d934 100644 --- a/src/Paket.Core/PaketConfigFiles/LockFile.fs +++ b/src/Paket.Core/PaketConfigFiles/LockFile.fs @@ -499,6 +499,7 @@ module LockFileParser = Settings = settings Version = SemVer.Parse version Kind = kind + AvailableFrameworks = [] // TODO: write stuff into the lockfile and read it here IsRuntimeDependency = isRuntimeDependency } :: currentGroup.Packages }::otherGroups | None -> failwith "no source has been specified." diff --git a/tests/Paket.Tests/InstallModel/Xml/RxXaml.fs b/tests/Paket.Tests/InstallModel/Xml/RxXaml.fs index 4e86107a58..f43c8a4247 100644 --- a/tests/Paket.Tests/InstallModel/Xml/RxXaml.fs +++ b/tests/Paket.Tests/InstallModel/Xml/RxXaml.fs @@ -31,6 +31,7 @@ let ``should generate Xml for Rx-XAML 2.2.4 with correct framework assembly refe Dependencies = lazy [] LicenseUrl = "" IsDevelopmentDependency = false + AvailableFramework = [] FrameworkAssemblyReferences = [{ AssemblyName = "WindowsBase"; FrameworkRestrictions = makeOrList [FrameworkRestriction.Exactly(DotNetFramework FrameworkVersion.V4_5)] } { AssemblyName = "WindowsBase"; FrameworkRestrictions = makeOrList [FrameworkRestriction.Exactly(DotNetFramework FrameworkVersion.V4)] } diff --git a/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithExistingFrameworkReferences.fs b/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithExistingFrameworkReferences.fs index f9147535a2..9f4f7dfab7 100644 --- a/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithExistingFrameworkReferences.fs +++ b/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithExistingFrameworkReferences.fs @@ -68,6 +68,7 @@ let ``should generate Xml for System.Net.Http 2.2.8``() = Dependencies = lazy [] LicenseUrl = "" IsDevelopmentDependency = false + AvailableFramework = [] FrameworkAssemblyReferences = [{ AssemblyName = "System.Net.Http"; FrameworkRestrictions = makeOrList [FrameworkRestriction.Exactly(DotNetFramework(FrameworkVersion.V4_5))] } { AssemblyName = "System.Net.Http.WebRequest"; FrameworkRestrictions = makeOrList [FrameworkRestriction.Exactly(DotNetFramework(FrameworkVersion.V4_5))] }]}) diff --git a/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithFrameworkReferences.fs b/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithFrameworkReferences.fs index c47ebd398e..b2fa018c54 100644 --- a/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithFrameworkReferences.fs +++ b/tests/Paket.Tests/InstallModel/Xml/SystemNetHttpWithFrameworkReferences.fs @@ -75,6 +75,7 @@ let ``should generate Xml for System.Net.Http 2.2.8``() = Dependencies = lazy [] LicenseUrl = "" IsDevelopmentDependency = false + AvailableFramework = [] FrameworkAssemblyReferences = [{ AssemblyName = "System.Net.Http"; FrameworkRestrictions = makeOrList [FrameworkRestriction.AtLeast(DotNetFramework(FrameworkVersion.V4_5))] } { AssemblyName = "System.Net.Http.WebRequest"; FrameworkRestrictions = makeOrList [FrameworkRestriction.Exactly(DotNetFramework(FrameworkVersion.V4_5))] }]}) diff --git a/tests/Paket.Tests/NuGetOData/ODataSpecs.fs b/tests/Paket.Tests/NuGetOData/ODataSpecs.fs index e8bfa97f5b..4d2008e3b3 100644 --- a/tests/Paket.Tests/NuGetOData/ODataSpecs.fs +++ b/tests/Paket.Tests/NuGetOData/ODataSpecs.fs @@ -37,6 +37,7 @@ let ``can detect explicit dependencies for Fantomas``() = LicenseUrl = "http://github.com/dungpa/fantomas/blob/master/LICENSE.md" CacheVersion = NuGet.NuGetPackageCache.CurrentCacheVersion Version = "1.6.0" + AvailableFrameworks = [] SourceUrl = fakeUrl } [] @@ -57,6 +58,7 @@ let ``can detect explicit dependencies for Rx-PlaformServices``() = LicenseUrl = "http://go.microsoft.com/fwlink/?LinkID=261272" Version = "2.3.0" CacheVersion = NuGet.NuGetPackageCache.CurrentCacheVersion + AvailableFrameworks = [] SourceUrl = fakeUrl } |> ODataSearchResult.Match) @@ -72,6 +74,7 @@ let ``can detect explicit dependencies for EasyNetQ``() = LicenseUrl = "https://github.com/mikehadlow/EasyNetQ/blob/master/licence.txt" CacheVersion = NuGet.NuGetPackageCache.CurrentCacheVersion Version = "0.40.3.352" + AvailableFrameworks = [] SourceUrl = fakeUrl } |> ODataSearchResult.Match) @@ -90,6 +93,7 @@ let ``can detect explicit dependencies for Fleece``() = PackageName "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"),"true" PackageName "ReadOnlyCollectionExtensions",DependenciesFileParser.parseVersionRequirement(">= 1.2.0"),"true" PackageName "System.Json",DependenciesFileParser.parseVersionRequirement(">= 4.0.20126.16343"),"true"] + AvailableFrameworks = [] SourceUrl = fakeUrl } [] @@ -106,6 +110,7 @@ let ``can detect explicit dependencies for ReadOnlyCollectionExtensions``() = SerializedDependencies = [PackageName "LinqBridge",DependenciesFileParser.parseVersionRequirement(">= 1.3.0"), "&& (>= net20) (< net35)" PackageName "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), ">= net20"] + AvailableFrameworks = [] SourceUrl = fakeUrl } [] @@ -120,6 +125,7 @@ let ``can detect explicit dependencies for Math.Numerics``() = CacheVersion = NuGet.NuGetPackageCache.CurrentCacheVersion SerializedDependencies = [PackageName "TaskParallelLibrary",DependenciesFileParser.parseVersionRequirement(">= 1.0.2856"), "&& (>= net35) (< net40)" ] + AvailableFrameworks = [] SourceUrl = fakeUrl } |> ODataSearchResult.Match) @@ -166,4 +172,5 @@ let ``can ignore unknown frameworks``() = LicenseUrl = "https://github.com/dotnet/BenchmarkDotNet/blob/master/LICENSE.md" CacheVersion = NuGet.NuGetPackageCache.CurrentCacheVersion Version = "0.10.1" + AvailableFrameworks = [] SourceUrl = fakeUrl } \ No newline at end of file diff --git a/tests/Paket.Tests/Resolver/PropertyTests.fs b/tests/Paket.Tests/Resolver/PropertyTests.fs index 0f97788f4a..4e78d11c12 100644 --- a/tests/Paket.Tests/Resolver/PropertyTests.fs +++ b/tests/Paket.Tests/Resolver/PropertyTests.fs @@ -69,6 +69,7 @@ let bruteForce ((g,deps):ResolverPuzzle) = Settings = InstallSettings.Default Source = PackageSources.DefaultNuGetSource Kind = ResolvedPackageKind.Package + AvailableFrameworks = [] IsRuntimeDependency = false } let deps' = packageDeps @ deps diff --git a/tests/Paket.Tests/TestHelpers.fs b/tests/Paket.Tests/TestHelpers.fs index 8c32ee0ebf..4f3d4192b3 100644 --- a/tests/Paket.Tests/TestHelpers.fs +++ b/tests/Paket.Tests/TestHelpers.fs @@ -37,7 +37,7 @@ let OfGraphWithRestriction (g:seq) : DependencyGraph = g |> Seq.map (fun nuspecText -> - let nspec = Nuspec.Load("in-memory", nuspecText) + let nspec = Nuspec.Load("in-memory", nuspecText, [||]) nspec.OfficialName, nspec.Version, nspec.Dependencies.Value |> List.map (fun (a,b,c) -> a.CompareString, b, c), RuntimeGraph.Empty) |> Seq.toList @@ -60,6 +60,7 @@ let PackageDetailsFromGraph (graph : DependencyGraph) (parameters:GetPackageDeta DownloadLink = "" LicenseUrl = "" Unlisted = false + AvailableFrameworks = [] DirectDependencies = Set.ofList dependencies } |> async.Return diff --git a/tests/Paket.Tests/Versioning/FrameworkRestrictionTests.fs b/tests/Paket.Tests/Versioning/FrameworkRestrictionTests.fs index 8cbe96a302..d7e2de3b48 100644 --- a/tests/Paket.Tests/Versioning/FrameworkRestrictionTests.fs +++ b/tests/Paket.Tests/Versioning/FrameworkRestrictionTests.fs @@ -140,6 +140,7 @@ let ``__unknowntfm__ should not match everything`` () = Dependencies = lazy [] LicenseUrl = "" IsDevelopmentDependency = false + AvailableFramework = [] FrameworkAssemblyReferences = []}) let target = TargetProfile.PortableProfile PortableProfileType.Profile344 let newModel = model.ApplyFrameworkRestrictions (FrameworkRestriction.ExactlyPlatform target)