From 488799ce53f4fd47bf29306f552f552577b9b522 Mon Sep 17 00:00:00 2001
From: Steffen Forkmann <steffen.forkmann@msu-solutions.de>
Date: Thu, 30 Oct 2014 17:08:19 +0100
Subject: [PATCH] Detect target framework restrictions - references #307

---
 src/Paket.Core/FrameworkHandling.fs            |  9 ++++++---
 src/Paket.Core/LockFile.fs                     |  2 +-
 src/Paket.Core/Nuget.fs                        | 11 ++++-------
 src/Paket.Core/Nuspec.fs                       | 12 +++++++++---
 src/Paket.Core/PackageResolver.fs              |  4 ++--
 tests/Paket.Tests/Lockfile/ParserSpecs.fs      |  6 +++---
 tests/Paket.Tests/NuGetOData/ODataSpecs.fs     | 18 +++++++++---------
 tests/Paket.Tests/Nuspec/NuspecSpecs.fs        | 11 ++++++++++-
 .../Simplifier/BasicScenarioSpecs.fs           |  2 +-
 tests/Paket.Tests/TestHelpers.fs               |  2 +-
 10 files changed, 46 insertions(+), 31 deletions(-)

diff --git a/src/Paket.Core/FrameworkHandling.fs b/src/Paket.Core/FrameworkHandling.fs
index 75d889ecc6..93b9d35706 100644
--- a/src/Paket.Core/FrameworkHandling.fs
+++ b/src/Paket.Core/FrameworkHandling.fs
@@ -72,6 +72,9 @@ type FrameworkIdentifier =
          "Windows", "win"
          "WindowsPhoneApp", "wpa"
          
+         "1.0", "10" 
+         "1.1", "11" 
+         "2.0", "20" 
          "3.5", "35" 
          "4.0", "40" 
          "4.5", "45" 
@@ -84,9 +87,9 @@ type FrameworkIdentifier =
 
         match path with
         | "net" -> Some(DotNetFramework(FrameworkVersion.V2)) // not sure here
-        | "1.0" -> Some(DotNetFramework(FrameworkVersion.V1))
-        | "1.1" -> Some(DotNetFramework(FrameworkVersion.V1_1))
-        | "2.0" -> Some(DotNetFramework(FrameworkVersion.V2))
+        | "10" -> Some(DotNetFramework(FrameworkVersion.V1))
+        | "11" -> Some(DotNetFramework(FrameworkVersion.V1_1))
+        | "20" -> Some(DotNetFramework(FrameworkVersion.V2))
         | "net11" -> Some(DotNetFramework(FrameworkVersion.V1_1))
         | "net20" -> Some(DotNetFramework(FrameworkVersion.V2))
         | "net20-full" -> Some(DotNetFramework(FrameworkVersion.V2))
diff --git a/src/Paket.Core/LockFile.fs b/src/Paket.Core/LockFile.fs
index 34f0d1edd7..3dcf9559d5 100644
--- a/src/Paket.Core/LockFile.fs
+++ b/src/Paket.Core/LockFile.fs
@@ -115,7 +115,7 @@ module LockFileParser =
                     | currentPackage :: otherPackages -> 
                         { state with
                                 Packages = { currentPackage with
-                                                Dependencies = Set.add (name, VersionRequirement.AllReleases, []) currentPackage.Dependencies
+                                                Dependencies = Set.add (name, VersionRequirement.AllReleases, None) currentPackage.Dependencies
                                             } :: otherPackages }                    
                     | [] -> failwith "cannot set a dependency - no package has been specified."
                 else
diff --git a/src/Paket.Core/Nuget.fs b/src/Paket.Core/Nuget.fs
index 3ca50decc3..e516398eb1 100644
--- a/src/Paket.Core/Nuget.fs
+++ b/src/Paket.Core/Nuget.fs
@@ -33,7 +33,7 @@ let private loadNuGetOData raw =
     doc,manager
 
 type NugetPackageCache =
-    { Dependencies : (string * VersionRequirement * (FrameworkIdentifier list)) list
+    { Dependencies : (string * VersionRequirement * (FrameworkIdentifier option)) list
       Name : string
       SourceUrl: string
       DownloadUrl : string}
@@ -135,12 +135,9 @@ let getODataDetails nugetURL raw =
         getAttribute "Dependencies"
         |> fun s -> s.Split([| '|' |], System.StringSplitOptions.RemoveEmptyEntries)
         |> Array.map (fun d -> d.Split ':')
-        |> Array.filter (fun d -> Array.isEmpty d
-                                    |> not && d.[0] <> "")
-        |> Array.map (fun a -> 
-                a.[0], 
-                if a.Length > 1 then a.[1] else "0")
-        |> Array.map (fun (name, version) -> name, NugetVersionRangeParser.parse version ,[])
+        |> Array.filter (fun d -> Array.isEmpty d |> not && d.[0] <> "")
+        |> Array.map (fun a -> a.[0],(if a.Length > 1 then a.[1] else "0"),(if a.Length > 2 && a.[2] <> "" then FrameworkIdentifier.Extract a.[2] else None))
+        |> Array.map (fun (name, version, restricted) -> name, NugetVersionRangeParser.parse version, restricted)
         |> Array.toList
 
     { Name = officialName; DownloadUrl = downloadLink; Dependencies = packages; SourceUrl = nugetURL }
diff --git a/src/Paket.Core/Nuspec.fs b/src/Paket.Core/Nuspec.fs
index e029f7130d..0bfc493a20 100644
--- a/src/Paket.Core/Nuspec.fs
+++ b/src/Paket.Core/Nuspec.fs
@@ -81,7 +81,7 @@ module NugetVersionRangeParser =
 
 type Nuspec = 
     { References : NuspecReferences 
-      Dependencies : (string * VersionRequirement * (FrameworkIdentifier list)) list
+      Dependencies : (string * VersionRequirement * (FrameworkIdentifier option)) list
       OfficialName : string
       FrameworkAssemblyReferences : FrameworkAssemblyReference list }
 
@@ -95,7 +95,7 @@ type Nuspec =
             doc.Load fi.FullName
 
             let dependencies = 
-                doc.SelectNodes "//*[local-name() = 'metadata']/*[local-name() = 'dependencies']/*[local-name() = 'dependency']"
+                doc.SelectNodes "//*[local-name() = 'dependency']"
                 |> Seq.cast<XmlNode>
                 |> Seq.map (fun node -> 
                                 let name = node.Attributes.["id"].Value                            
@@ -104,7 +104,13 @@ type Nuspec =
                                         NugetVersionRangeParser.parse node.Attributes.["version"].Value 
                                     else 
                                         NugetVersionRangeParser.parse "0"
-                                name,version,[]) 
+                                let restriction = 
+                                    if node.ParentNode.Name.ToLower() = "group" && node.ParentNode.Attributes.["targetFramework"] <> null then
+                                        let restriction = node.ParentNode.Attributes.["targetFramework"].InnerText
+                                        FrameworkIdentifier.Extract restriction
+                                    else
+                                        None
+                                name,version,restriction) 
                 |> Seq.toList
 
             let officialName = 
diff --git a/src/Paket.Core/PackageResolver.fs b/src/Paket.Core/PackageResolver.fs
index 1c92661a10..ab5bd90be6 100644
--- a/src/Paket.Core/PackageResolver.fs
+++ b/src/Paket.Core/PackageResolver.fs
@@ -13,13 +13,13 @@ type PackageDetails =
     { Name : string
       Source : PackageSource
       DownloadLink : string
-      DirectDependencies :  (string * VersionRequirement * (FrameworkIdentifier list)) Set }
+      DirectDependencies :  (string * VersionRequirement * (FrameworkIdentifier option)) Set }
 
 /// Represents data about resolved packages
 type ResolvedPackage =
     { Name : string
       Version : SemVerInfo
-      Dependencies : (string * VersionRequirement * (FrameworkIdentifier list)) Set
+      Dependencies : (string * VersionRequirement * (FrameworkIdentifier option)) Set
       Source : PackageSource }
 
     override this.ToString() = sprintf "%s %s" this.Name (this.Version.ToString())
diff --git a/tests/Paket.Tests/Lockfile/ParserSpecs.fs b/tests/Paket.Tests/Lockfile/ParserSpecs.fs
index 1b244ed0d5..16b57e559c 100644
--- a/tests/Paket.Tests/Lockfile/ParserSpecs.fs
+++ b/tests/Paket.Tests/Lockfile/ParserSpecs.fs
@@ -40,12 +40,12 @@ let ``should parse lock file``() =
     packages.[1].Source |> shouldEqual PackageSources.DefaultNugetSource
     packages.[1].Name |> shouldEqual "Castle.Windsor-log4net"
     packages.[1].Version |> shouldEqual (SemVer.Parse "3.3")
-    packages.[1].Dependencies |> shouldEqual (Set.ofList ["Castle.Windsor", VersionRequirement.AllReleases, []; "log4net", VersionRequirement.AllReleases, []])
+    packages.[1].Dependencies |> shouldEqual (Set.ofList ["Castle.Windsor", VersionRequirement.AllReleases, None; "log4net", VersionRequirement.AllReleases, None])
     
     packages.[5].Source |> shouldEqual PackageSources.DefaultNugetSource
     packages.[5].Name |> shouldEqual "log4net"
     packages.[5].Version |> shouldEqual (SemVer.Parse "1.1")
-    packages.[5].Dependencies |> shouldEqual (Set.ofList ["log", VersionRequirement.AllReleases, []])
+    packages.[5].Dependencies |> shouldEqual (Set.ofList ["log", VersionRequirement.AllReleases, None])
 
     let sourceFiles = List.rev lockFile.SourceFiles
     sourceFiles|> shouldEqual
@@ -90,7 +90,7 @@ let ``should parse strict lock file``() =
     packages.[5].Source |> shouldEqual PackageSources.DefaultNugetSource
     packages.[5].Name |> shouldEqual "log4net"
     packages.[5].Version |> shouldEqual (SemVer.Parse "1.1")
-    packages.[5].Dependencies |> shouldEqual (Set.ofList ["log", VersionRequirement.AllReleases, []])
+    packages.[5].Dependencies |> shouldEqual (Set.ofList ["log", VersionRequirement.AllReleases, None])
 
 let dogfood = """NUGET
   remote: https://nuget.org/api/v2
diff --git a/tests/Paket.Tests/NuGetOData/ODataSpecs.fs b/tests/Paket.Tests/NuGetOData/ODataSpecs.fs
index a89437a2d1..ceedb1eeaa 100644
--- a/tests/Paket.Tests/NuGetOData/ODataSpecs.fs
+++ b/tests/Paket.Tests/NuGetOData/ODataSpecs.fs
@@ -18,7 +18,7 @@ let ``can detect explicit dependencies for Fantomas``() =
     |> shouldEqual 
         { Name = "Fantomas"
           DownloadUrl = "http://www.nuget.org/api/v2/package/Fantomas/1.6.0"
-          Dependencies = ["FSharp.Compiler.Service",DependenciesFileParser.parseVersionRequirement(">= 0.0.73"), []]
+          Dependencies = ["FSharp.Compiler.Service",DependenciesFileParser.parseVersionRequirement(">= 0.0.73"), None]
           SourceUrl = fakeUrl }
 
 
@@ -29,10 +29,10 @@ let ``can detect explicit dependencies for Fleece``() =
         { Name = "Fleece"
           DownloadUrl = "http://www.nuget.org/api/v2/package/Fleece/0.4.0"
           Dependencies = 
-            ["FSharpPlus",DependenciesFileParser.parseVersionRequirement(">= 0.0.4"), []
-             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), []
-             "ReadOnlyCollectionExtensions",DependenciesFileParser.parseVersionRequirement(">= 1.2.0"), []
-             "System.Json",DependenciesFileParser.parseVersionRequirement(">= 4.0.20126.16343"), []]
+            ["FSharpPlus",DependenciesFileParser.parseVersionRequirement(">= 0.0.4"), None
+             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), None
+             "ReadOnlyCollectionExtensions",DependenciesFileParser.parseVersionRequirement(">= 1.2.0"), None
+             "System.Json",DependenciesFileParser.parseVersionRequirement(">= 4.0.20126.16343"), None]
           SourceUrl = fakeUrl }
 
 [<Test>]
@@ -42,8 +42,8 @@ let ``can detect explicit dependencies for ReadOnlyCollectionExtensions``() =
         { Name = "ReadOnlyCollectionExtensions"
           DownloadUrl = "http://www.nuget.org/api/v2/package/ReadOnlyCollectionExtensions/1.2.0"
           Dependencies = 
-            ["LinqBridge",DependenciesFileParser.parseVersionRequirement(">= 1.3.0"), []
-             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), []
-             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), []
-             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), []]
+            ["LinqBridge",DependenciesFileParser.parseVersionRequirement(">= 1.3.0"), Some(DotNetFramework(FrameworkVersion.V2))
+             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V2))
+             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V3_5))
+             "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V4_Client))]
           SourceUrl = fakeUrl }
\ No newline at end of file
diff --git a/tests/Paket.Tests/Nuspec/NuspecSpecs.fs b/tests/Paket.Tests/Nuspec/NuspecSpecs.fs
index 8fc325b275..c32eff8500 100644
--- a/tests/Paket.Tests/Nuspec/NuspecSpecs.fs
+++ b/tests/Paket.Tests/Nuspec/NuspecSpecs.fs
@@ -89,4 +89,13 @@ let ``can detect empty dependencies for log4net``() =
 [<Test>]
 let ``can detect explicit dependencies for Fantomas``() = 
     Nuspec.Load("Nuspec/Fantomas.nuspec").Dependencies
-    |> shouldEqual ["FSharp.Compiler.Service",DependenciesFileParser.parseVersionRequirement(">= 0.0.57"), []]
\ No newline at end of file
+    |> shouldEqual ["FSharp.Compiler.Service",DependenciesFileParser.parseVersionRequirement(">= 0.0.57"), None]
+
+[<Test>]
+let ``can detect explicit dependencies for ReadOnlyCollectionExtensions``() = 
+    Nuspec.Load("Nuspec/ReadOnlyCollectionExtensions.nuspec").Dependencies
+    |> shouldEqual 
+        ["LinqBridge",DependenciesFileParser.parseVersionRequirement(">= 1.3.0"), Some(DotNetFramework(FrameworkVersion.V2))
+         "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V2))
+         "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V3_5))
+         "ReadOnlyCollectionInterfaces",DependenciesFileParser.parseVersionRequirement("1.0.0"), Some(DotNetFramework(FrameworkVersion.V4_Client))]
\ No newline at end of file
diff --git a/tests/Paket.Tests/Simplifier/BasicScenarioSpecs.fs b/tests/Paket.Tests/Simplifier/BasicScenarioSpecs.fs
index 14e7aa2988..5391462b6b 100644
--- a/tests/Paket.Tests/Simplifier/BasicScenarioSpecs.fs
+++ b/tests/Paket.Tests/Simplifier/BasicScenarioSpecs.fs
@@ -11,7 +11,7 @@ let toPackages =
         { Name = name
           Version = SemVer.Parse ver
           Source = PackageSources.DefaultNugetSource
-          Dependencies = deps |> List.map (fun (name, verRan) -> name, NugetVersionRangeParser.parse verRan,[]) |> Set.ofList } : PackageResolver.ResolvedPackage)
+          Dependencies = deps |> List.map (fun (name, verRan) -> name, NugetVersionRangeParser.parse verRan,None) |> Set.ofList } : PackageResolver.ResolvedPackage)
 
 let graph1 = 
     ["A", "3.3.0", ["B", "3.3.0"; "C", "1.0"]
diff --git a/tests/Paket.Tests/TestHelpers.fs b/tests/Paket.Tests/TestHelpers.fs
index 97e02eb2ea..bf7af7bc5e 100644
--- a/tests/Paket.Tests/TestHelpers.fs
+++ b/tests/Paket.Tests/TestHelpers.fs
@@ -12,7 +12,7 @@ let PackageDetailsFromGraph (graph : seq<string * string * (string * VersionRequ
     let name,dependencies = 
         graph
         |> Seq.filter (fun (p, v, _) -> p.ToLower() = package.ToLower() && v = version)
-        |> Seq.map (fun (n, _, d) -> n,d |> List.map (fun (x,y) -> x,y,[]))
+        |> Seq.map (fun (n, _, d) -> n,d |> List.map (fun (x,y) -> x,y,None))
         |> Seq.head
 
     { Name = name