From 569abf6687886fd5c30c3120cd55e6482d93d8c8 Mon Sep 17 00:00:00 2001
From: Steffen Forkmann <steffen.forkmann@msu-solutions.de>
Date: Sat, 19 Mar 2016 11:43:20 +0100
Subject: [PATCH] Simplify mixed setup with native - fixes #1523

---
 RELEASE_NOTES.md                              | 34 ++--------
 .../Paket.IntegrationTests/InstallSpecs.fs    |  8 ++-
 .../TestPaket/TestPaket.vcxprojtemplate       | 12 +++-
 .../TestPaket/TestPaket.vcxprojtemplate       | 12 +++-
 .../TestPaketDotNet.csprojtemplate            |  2 +-
 src/Paket.Core/FrameworkHandling.fs           | 24 ++++---
 src/Paket.Core/PlatformMatching.fs            | 66 ++++++++++---------
 src/Paket.Core/Utils.fs                       | 14 +++-
 8 files changed, 97 insertions(+), 75 deletions(-)

diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 1a3efd2ba3..65a3e9548c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,37 +1,17 @@
-#### 2.52.16 - 17.03.2016
+#### 2.53.0 - 19.03.2016
+* Allow to restore recursively from remote dependencies file - https://github.com/fsprojects/Paket/issues/1507
+* BUGFIX: Fix mixed mode solutions with Native - https://github.com/fsprojects/Paket/issues/1523
 * BUGFIX: Do not generate useless true conditions for Native - https://github.com/fsprojects/Paket/issues/1523
-
-#### 2.52.15 - 17.03.2016
-* USABILITY: Automatically retry with force flag if we can't get package details for a given version - https://github.com/fsprojects/Paket/issues/1526
-
-#### 2.52.14 - 17.03.2016
-* BUGFIX: Restore resolver performance - https://github.com/fsprojects/Paket/issues/1524
-
-#### 2.52.10 - 17.03.2016
-* BUGFIX: Native settings are filered correctly - https://github.com/fsprojects/Paket/issues/1523
-
-#### 2.52.9 - 16.03.2016
-* USABILITY: Improved error message when paket.dependencies can't be found - https://github.com/fsprojects/Paket/pull/1519
+* BUGFIX: Native settings are filtered correctly - https://github.com/fsprojects/Paket/issues/1523
 * BUGFIX: Force resolver to look into deeper levels - https://github.com/fsprojects/Paket/issues/1520
-
-#### 2.52.8 - 15.03.2016
+* BUGFIX: Emit net40-full moniker instead of net-40
+* USABILITY: Improved error message when paket.dependencies can't be found - https://github.com/fsprojects/Paket/pull/1519
+* USABILITY: Automatically retry with force flag if we can't get package details for a given version - https://github.com/fsprojects/Paket/issues/1526
 * USABILITY: Better error message when paket.lock an paket.dependencies are out of sync.
-
-#### 2.52.7 - 14.03.2016
-* USABILITY: Emit net40-full moniker
-
-#### 2.52.6 - 14.03.2016
 * USABILITY: Content:once doesn't add paket flags to the csproj file in order to make Orleans tools happy - https://github.com/fsprojects/Paket/issues/1513
-
-#### 2.52.4 - 14.03.2016
 * USABILITY: Be more robust in paket.references files - https://github.com/fsprojects/Paket/issues/1514
-
-#### 2.52.3 - 12.03.2016
 * USABILITY: Improved stability in lock acquiring process - https://github.com/fsprojects/Paket/issues/858
 
-#### 2.52.1 - 10.03.2016
-* Allow to restore recursively from remote dependencies file - https://github.com/fsprojects/Paket/issues/1507
-
 #### 2.52.0 - 10.03.2016
 * Allow to restore dll from remote dependencies file - https://github.com/fsprojects/Paket/issues/1507
 * Prevent paket holding locks on assemblies during binding redirects - https://github.com/fsprojects/Paket/pull/1492
diff --git a/integrationtests/Paket.IntegrationTests/InstallSpecs.fs b/integrationtests/Paket.IntegrationTests/InstallSpecs.fs
index 24c46daf09..694cbabb81 100644
--- a/integrationtests/Paket.IntegrationTests/InstallSpecs.fs
+++ b/integrationtests/Paket.IntegrationTests/InstallSpecs.fs
@@ -265,16 +265,20 @@ let ``#1523 should install native in mixed setting``() =
     let s1 = File.ReadAllText oldFile |> normalizeLineEndings
     let s2 = File.ReadAllText newFile |> normalizeLineEndings
     s2 |> shouldEqual s1
-    
+
 [<Test>]
-let ``#1523 should not emit true in mixed setting``() = 
+let ``#1523 should emit correct native in mixed setting``() = 
     install "i001523-not-true" |> ignore
     let newFile = Path.Combine(scenarioTempPath "i001523-not-true","TestPaket","TestPaket.vcxproj")
     let oldFile = Path.Combine(originalScenarioPath "i001523-not-true","TestPaket","TestPaket.vcxprojtemplate")
     let s1 = File.ReadAllText oldFile |> normalizeLineEndings
     let s2 = File.ReadAllText newFile |> normalizeLineEndings
     s2 |> shouldEqual s1
+
     
+[<Test>]
+let ``#1523 should emit correct .NET in mixed setting``() = 
+    install "i001523-not-true" |> ignore
     let newFile = Path.Combine(scenarioTempPath "i001523-not-true","TestPaketDotNet","TestPaketDotNet.csproj")
     let oldFile = Path.Combine(originalScenarioPath "i001523-not-true","TestPaketDotNet","TestPaketDotNet.csprojtemplate")
     let s1 = File.ReadAllText oldFile |> normalizeLineEndings
diff --git a/integrationtests/scenarios/i001523-mixed-native/before/TestPaket/TestPaket.vcxprojtemplate b/integrationtests/scenarios/i001523-mixed-native/before/TestPaket/TestPaket.vcxprojtemplate
index 080afc5592..a0494c74e1 100644
--- a/integrationtests/scenarios/i001523-mixed-native/before/TestPaket/TestPaket.vcxprojtemplate
+++ b/integrationtests/scenarios/i001523-mixed-native/before/TestPaket/TestPaket.vcxprojtemplate
@@ -163,7 +163,7 @@
     </When>
   </Choose>
   <Choose>
-    <When Condition="('$(Configuration)|$(Platform)'=='Debug|Win32') Or ('$(Configuration)|$(Platform)'=='Debug|arm') Or ('$(Configuration)|$(Platform)'=='Debug|x64') Or ('$(Configuration)|$(Platform)'=='Release|Win32') Or ('$(Configuration)|$(Platform)'=='Release|x64') Or ('$(Configuration)|$(Platform)'=='Release|arm')">
+    <When Condition="'$(Platform)'=='Win32'">
       <ItemGroup>
         <Reference Include="boost_filesystem-vc140-mt-1_60">
           <HintPath>..\packages\boost_filesystem-vc140\lib\native\address-model-32\lib\boost_filesystem-vc140-mt-1_60.dll</HintPath>
@@ -175,6 +175,10 @@
           <Private>True</Private>
           <Paket>True</Paket>
         </Reference>
+      </ItemGroup>
+    </When>
+    <When Condition="'$(Platform)'=='x64'">
+      <ItemGroup>
         <Reference Include="boost_filesystem-vc140-mt-1_60">
           <HintPath>..\packages\boost_filesystem-vc140\lib\native\address-model-64\lib\boost_filesystem-vc140-mt-1_60.dll</HintPath>
           <Private>True</Private>
@@ -196,7 +200,7 @@
     </When>
   </Choose>
   <Choose>
-    <When Condition="('$(Configuration)|$(Platform)'=='Debug|Win32') Or ('$(Configuration)|$(Platform)'=='Debug|arm') Or ('$(Configuration)|$(Platform)'=='Debug|x64') Or ('$(Configuration)|$(Platform)'=='Release|Win32') Or ('$(Configuration)|$(Platform)'=='Release|x64') Or ('$(Configuration)|$(Platform)'=='Release|arm')">
+    <When Condition="'$(Platform)'=='Win32'">
       <ItemGroup>
         <Reference Include="boost_system-vc140-mt-1_60">
           <HintPath>..\packages\boost_system-vc140\lib\native\address-model-32\lib\boost_system-vc140-mt-1_60.dll</HintPath>
@@ -208,6 +212,10 @@
           <Private>True</Private>
           <Paket>True</Paket>
         </Reference>
+      </ItemGroup>
+    </When>
+    <When Condition="'$(Platform)'=='x64'">
+      <ItemGroup>
         <Reference Include="boost_system-vc140-mt-1_60">
           <HintPath>..\packages\boost_system-vc140\lib\native\address-model-64\lib\boost_system-vc140-mt-1_60.dll</HintPath>
           <Private>True</Private>
diff --git a/integrationtests/scenarios/i001523-not-true/before/TestPaket/TestPaket.vcxprojtemplate b/integrationtests/scenarios/i001523-not-true/before/TestPaket/TestPaket.vcxprojtemplate
index b4d6e9054a..ea269bec2c 100644
--- a/integrationtests/scenarios/i001523-not-true/before/TestPaket/TestPaket.vcxprojtemplate
+++ b/integrationtests/scenarios/i001523-not-true/before/TestPaket/TestPaket.vcxprojtemplate
@@ -165,7 +165,7 @@
     </When>
   </Choose>
   <Choose>
-    <When Condition="('$(Configuration)|$(Platform)'=='Debug|Win32') Or ('$(Configuration)|$(Platform)'=='Debug|arm') Or ('$(Configuration)|$(Platform)'=='Debug|x64') Or ('$(Configuration)|$(Platform)'=='Release|Win32') Or ('$(Configuration)|$(Platform)'=='Release|x64') Or ('$(Configuration)|$(Platform)'=='Release|arm')">
+    <When Condition="'$(Platform)'=='Win32'">
       <ItemGroup>
         <Reference Include="boost_filesystem-vc140-mt-1_60">
           <HintPath>..\packages\boost_filesystem-vc140\lib\native\address-model-32\lib\boost_filesystem-vc140-mt-1_60.dll</HintPath>
@@ -177,6 +177,10 @@
           <Private>True</Private>
           <Paket>True</Paket>
         </Reference>
+      </ItemGroup>
+    </When>
+    <When Condition="'$(Platform)'=='x64'">
+      <ItemGroup>
         <Reference Include="boost_filesystem-vc140-mt-1_60">
           <HintPath>..\packages\boost_filesystem-vc140\lib\native\address-model-64\lib\boost_filesystem-vc140-mt-1_60.dll</HintPath>
           <Private>True</Private>
@@ -198,7 +202,7 @@
     </When>
   </Choose>
   <Choose>
-    <When Condition="('$(Configuration)|$(Platform)'=='Debug|Win32') Or ('$(Configuration)|$(Platform)'=='Debug|arm') Or ('$(Configuration)|$(Platform)'=='Debug|x64') Or ('$(Configuration)|$(Platform)'=='Release|Win32') Or ('$(Configuration)|$(Platform)'=='Release|x64') Or ('$(Configuration)|$(Platform)'=='Release|arm')">
+    <When Condition="'$(Platform)'=='Win32'">
       <ItemGroup>
         <Reference Include="boost_system-vc140-mt-1_60">
           <HintPath>..\packages\boost_system-vc140\lib\native\address-model-32\lib\boost_system-vc140-mt-1_60.dll</HintPath>
@@ -210,6 +214,10 @@
           <Private>True</Private>
           <Paket>True</Paket>
         </Reference>
+      </ItemGroup>
+    </When>
+    <When Condition="'$(Platform)'=='x64'">
+      <ItemGroup>
         <Reference Include="boost_system-vc140-mt-1_60">
           <HintPath>..\packages\boost_system-vc140\lib\native\address-model-64\lib\boost_system-vc140-mt-1_60.dll</HintPath>
           <Private>True</Private>
diff --git a/integrationtests/scenarios/i001523-not-true/before/TestPaketDotNet/TestPaketDotNet.csprojtemplate b/integrationtests/scenarios/i001523-not-true/before/TestPaketDotNet/TestPaketDotNet.csprojtemplate
index ee74ad370a..7925aaa10b 100644
--- a/integrationtests/scenarios/i001523-not-true/before/TestPaketDotNet/TestPaketDotNet.csprojtemplate
+++ b/integrationtests/scenarios/i001523-not-true/before/TestPaketDotNet/TestPaketDotNet.csprojtemplate
@@ -69,7 +69,7 @@
     </When>
   </Choose>
   <Choose>
-    <When Condition="($(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5.2') Or ('$(Configuration)|$(Platform)'=='Debug|Win32') Or ('$(Configuration)|$(Platform)'=='Debug|arm') Or ('$(Configuration)|$(Platform)'=='Debug|x64') Or ('$(Configuration)|$(Platform)'=='Release|Win32') Or ('$(Configuration)|$(Platform)'=='Release|x64') Or ('$(Configuration)|$(Platform)'=='Release|arm')">
+    <When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5.2'">
       <ItemGroup>
         <Reference Include="nunit.framework">
           <HintPath>..\packages\NUnit\lib\nunit.framework.dll</HintPath>
diff --git a/src/Paket.Core/FrameworkHandling.fs b/src/Paket.Core/FrameworkHandling.fs
index 0adf76f09c..23a454d8a2 100644
--- a/src/Paket.Core/FrameworkHandling.fs
+++ b/src/Paket.Core/FrameworkHandling.fs
@@ -205,6 +205,8 @@ module FrameworkDetection =
                 | "native/x86/release" -> Some(Native("Release","Win32"))
                 | "native/x64/release" -> Some(Native("Release","x64"))
                 | "native/arm/release" -> Some(Native("Release","arm"))
+                | "native/address-model-32" -> Some(Native("","Win32"))
+                | "native/address-model-64" -> Some(Native("","x64"))
                 | "native" -> Some(Native("",""))
                 | "sl"  | "sl3" | "sl30" -> Some (Silverlight "v3.0")
                 | "sl4" | "sl40" -> Some (Silverlight "v4.0")
@@ -333,7 +335,7 @@ module KnownTargetProfiles =
         SinglePlatform(WindowsPhoneSilverlight "v8.0")
         SinglePlatform(WindowsPhoneSilverlight "v8.1")]
 
-    let AllProfiles =
+    let AllDotNetProfiles =
        DotNetFrameworkProfiles @ 
        WindowsProfiles @ 
        SilverlightProfiles @
@@ -342,13 +344,6 @@ module KnownTargetProfiles =
         SinglePlatform(MonoTouch)
         SinglePlatform(XamariniOS)
         SinglePlatform(XamarinMac)
-        SinglePlatform(Native("",""))
-        SinglePlatform(Native("Debug","Win32"))
-        SinglePlatform(Native("Debug","arm"))
-        SinglePlatform(Native("Debug","x64"))
-        SinglePlatform(Native("Release","Win32"))
-        SinglePlatform(Native("Release","x64"))
-        SinglePlatform(Native("Release","arm"))
         SinglePlatform(WindowsPhoneApp "v8.1")
         PortableProfile("Profile2", [ DotNetFramework FrameworkVersion.V4; Silverlight "v4.0"; Windows "v4.5"; WindowsPhoneSilverlight "v7.0" ])
         PortableProfile("Profile3", [ DotNetFramework FrameworkVersion.V4; Silverlight "v4.0" ])
@@ -395,6 +390,19 @@ module KnownTargetProfiles =
         PortableProfile("Profile336", [ DotNetFramework FrameworkVersion.V4; Silverlight "v5.0"; Windows "v4.5"; WindowsPhoneApp "v8.1"; WindowsPhoneSilverlight "v8.0" ])
         PortableProfile("Profile344", [ DotNetFramework FrameworkVersion.V4_5; Silverlight "v5.0"; Windows "v4.5"; WindowsPhoneApp "v8.1"; WindowsPhoneSilverlight "v8.0" ])]
 
+    let AllNativeProfiles =
+        [ Native("","")
+          Native("","Win32")
+          Native("","x64")
+          Native("Debug","Win32")
+          Native("Debug","arm")
+          Native("Debug","x64")
+          Native("Release","Win32")
+          Native("Release","x64")
+          Native("Release","arm")]
+
+    let AllProfiles = (AllNativeProfiles |> List.map (fun p -> SinglePlatform p)) @ AllDotNetProfiles
+
     let FindPortableProfile name =
         AllProfiles
         |> List.pick (function
diff --git a/src/Paket.Core/PlatformMatching.fs b/src/Paket.Core/PlatformMatching.fs
index 4b495d58f9..ea7487c78f 100644
--- a/src/Paket.Core/PlatformMatching.fs
+++ b/src/Paket.Core/PlatformMatching.fs
@@ -34,8 +34,9 @@ let private pathPenalties = System.Collections.Concurrent.ConcurrentDictionary<_
 
 let getPathPenalty (path:string) (platform:FrameworkIdentifier) =
     if String.IsNullOrWhiteSpace path then
-        // an empty path is considered compatible with every target, but with a high penalty so explicit paths are preferred
-        10
+        match platform with
+        | Native(_) -> MaxPenalty // an empty path is inconsidered compatible with native targets            
+        | _ -> 10 // an empty path is considered compatible with every .NET target, but with a high penalty so explicit paths are preferred
     else
         let key = path,platform
         match pathPenalties.TryGetValue key with
@@ -137,13 +138,14 @@ let getTargetCondition (target:TargetProfile) =
         | XamariniOS -> "$(TargetFrameworkIdentifier) == 'Xamarin.iOS'", ""
         | XamarinMac -> "$(TargetFrameworkIdentifier) == 'Xamarin.Mac'", ""
         | Native("","") -> "true", ""
+        | Native("",bits) -> (sprintf "'$(Platform)'=='%s'" bits), ""
         | Native(profile,bits) -> (sprintf "'$(Configuration)|$(Platform)'=='%s|%s'" profile bits), ""
     | PortableProfile(name, _) -> sprintf "$(TargetFrameworkProfile) == '%O'" name,""
 
 let getCondition (referenceCondition:string option) (targets : TargetProfile list) =
     let inline CheckIfFullyInGroup typeName matchF (processed,targets) =
         let fullyContained = 
-            KnownTargetProfiles.AllProfiles 
+            KnownTargetProfiles.AllDotNetProfiles 
             |> List.filter matchF
             |> List.forall (fun p -> targets |> Seq.exists ((=) p))
 
@@ -172,31 +174,33 @@ let getCondition (referenceCondition:string option) (targets : TargetProfile lis
         |> List.append grouped
         |> List.groupBy fst
 
-    conditions
-    |> List.map (fun (group,conditions) ->
-        match List.ofSeq conditions with
-        | [ _,"" ] -> group
-        | [ _,detail ] -> sprintf "%s And %s" group detail
-        | [] -> "false"
-        | conditions ->
-            let detail =
-                conditions
-                |> List.map snd
-                |> Set.ofSeq
-                |> fun cs -> String.Join(" Or ",cs)
-            sprintf "%s And (%s)" group detail)
-    |> fun l -> 
-            match l with
-            | [] -> ""
-            | [x] -> x
-            | xs -> String.Join(" Or ", List.map (fun cs -> sprintf "(%s)" cs) xs)
-    |> fun s -> 
-        match referenceCondition with 
-        | None -> s
-        | Some condition ->
-            // msbuild triggers a warning MSB4130 when we leave out the quotes around the condition
-            // and add the condition at the end
-            if s = "$(TargetFrameworkIdentifier) == 'true'" then
-                sprintf "'$(%s)' == 'True'" condition
-            else
-                sprintf "'$(%s)' == 'True' And (%s)" condition s
\ No newline at end of file
+    let conditionString =
+        let andString = 
+            conditions
+            |> List.map (fun (group,conditions) ->
+                match List.ofSeq conditions with
+                | [ _,"" ] -> group
+                | [ _,detail ] -> sprintf "%s And %s" group detail
+                | [] -> "false"
+                | conditions ->
+                    let detail =
+                        conditions
+                        |> List.map snd
+                        |> Set.ofSeq
+                        |> fun cs -> String.Join(" Or ",cs)
+                    sprintf "%s And (%s)" group detail)
+        
+        match andString with
+        | [] -> ""
+        | [x] -> x
+        | xs -> String.Join(" Or ", List.map (fun cs -> sprintf "(%s)" cs) xs)
+    
+    match referenceCondition with 
+    | None -> conditionString
+    | Some condition ->
+        // msbuild triggers a warning MSB4130 when we leave out the quotes around the condition
+        // and add the condition at the end
+        if conditionString = "$(TargetFrameworkIdentifier) == 'true'" then
+            sprintf "'$(%s)' == 'True'" condition
+        else
+            sprintf "'$(%s)' == 'True' And (%s)" condition conditionString
\ No newline at end of file
diff --git a/src/Paket.Core/Utils.fs b/src/Paket.Core/Utils.fs
index 2eabc09cb2..62b5feedc7 100644
--- a/src/Paket.Core/Utils.fs
+++ b/src/Paket.Core/Utils.fs
@@ -127,18 +127,28 @@ let getNative (path:string) =
     if path.Contains "/arm/release" then "/arm/release" else
     if path.Contains "/x64/debug" then "/x64/debug" else
     if path.Contains "/x64/release" then "/x64/release" else
+    if path.Contains "/address-model-32" then "/address-model-32" else
+    if path.Contains "/address-model-64" then "/address-model-64" else
     ""
 
 let extractPath infix (fileName : string) : string option =
     let path = fileName.Replace("\\", "/").ToLower()
     let fi = FileInfo path
 
-    let startPos = path.LastIndexOf (sprintf "%s/" infix)
+    let packagesPos = path.LastIndexOf "packages/"
+    let startPos =
+        if packagesPos >= 0 then
+            path.IndexOf(sprintf "%s/" infix,packagesPos)
+        else
+            path.LastIndexOf(sprintf "%s/" infix)
+
     let endPos = path.IndexOf('/', startPos + infix.Length + 1)
     if startPos < 0 then None 
     elif endPos < 0 then Some("")
     else
-        Some (path.Substring(startPos + infix.Length + 1, endPos - startPos - infix.Length - 1) + getNative path)
+        let nativePart = getNative path
+        let libPart = path.Substring(startPos + infix.Length + 1, endPos - startPos - infix.Length - 1)
+        Some (libPart + nativePart)
 
 /// [omit]
 let inline normalizeXml (doc:XmlDocument) =