fsprojects / Paket

A dependency manager for .NET with support for NuGet packages and Git repositories.
https://fsprojects.github.io/Paket/
MIT License
1.99k stars 520 forks source link

Paket doesn't correctly handle Nougat compilation (v7/7.1) target for Xamarin.Android when installing Xamarin.Forms #2809

Closed tastyeggs closed 6 years ago

tastyeggs commented 6 years ago

Description

When installing a Xamarin.Forms package into a Xamarin.Android application, targeting Nougat (v7.1), Paket creates references to .netstandard1.0 libraries, instead of the monoandroid libraries.

When I drop compile version (TargetFrameworkVersion) to 6.0, it links correctly against the MonoAndroid10 libraries

Repro steps

  1. Create a new Xamarin.Android project
  2. Install Xamarin.Forms (latest 2.4.0.282)
  3. Set Compile using Android version (TargetFramework) to 7.0 or 7.1 (Nougat)
  4. Builds will fail, since the Xamarin Android platform libraries are not linked, instead .netstandard1.0 libraries are linked

It seems there is an explicit condition set for Android7/7.1 for .netstandard1.0. This does not work for Xamarin.Forms.

ZIP file with solution attached.

Expected behavior

The referenced dlls must be from lib\MonoAndroid10 directory.

Actual behavior

The referenced dlls come from lib\netstandard1.0 directory.

Known workarounds

Drop compilation target framework to 6.0, or use Nuget.

test.zip

forki commented 6 years ago

In the attached zip you didn't set your TargetFrameworkIdentifier to MonoAndroid

tastyeggs commented 6 years ago

Does this need to be set? I've never had to do that.

Also why would it then work for Android 6 and below?

forki commented 6 years ago

Yes I think it always need to be set - otherwise the when condition is not fulfilled

tastyeggs commented 6 years ago

After some digging, TargetFrameworkIdentifier is set to MonoAndroid in Xamarin.Android.CSharp.targets, that's included by default in all Xamarin.Android projects:

https://github.com/xamarin/xamarin-android/blob/master/src/Xamarin.Android.Build.Tasks/Xamarin.Android.CSharp.targets

Note that we're emitting two different When conditions for Android, so this seems to be intentional:

This includes the dlls from lib\MonoAndroid10 directory:

<When Condition="$(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v2.2' Or $(TargetFrameworkVersion) == 'v2.3' Or $(TargetFrameworkVersion) == 'v4.0.3' Or $(TargetFrameworkVersion) == 'v4.1' Or $(TargetFrameworkVersion) == 'v4.2' Or $(TargetFrameworkVersion) == 'v4.3' Or $(TargetFrameworkVersion) == 'v4.4' Or $(TargetFrameworkVersion) == 'v5.0' Or $(TargetFrameworkVersion) == 'v5.1' Or $(TargetFrameworkVersion) == 'v6.0')">

and then below that, this condition includes dlls from the netstandard10 directory:

<When Condition="($(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3' Or $(TargetFrameworkVersion) == 'v4.6' Or $(TargetFrameworkVersion) == 'v4.6.1' Or $(TargetFrameworkVersion) == 'v4.6.2' Or $(TargetFrameworkVersion) == 'v4.6.3' Or $(TargetFrameworkVersion) == 'v4.7' Or $(TargetFrameworkVersion) == 'v5.0')) Or ($(TargetFrameworkIdentifier) == '.NETStandard' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v1.2' Or $(TargetFrameworkVersion) == 'v1.3' Or $(TargetFrameworkVersion) == 'v1.4' Or $(TargetFrameworkVersion) == 'v1.5' Or $(TargetFrameworkVersion) == 'v1.6' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkIdentifier) == '.NETCoreApp' And ($(TargetFrameworkVersion) == 'v1.0' Or $(TargetFrameworkVersion) == 'v1.1' Or $(TargetFrameworkVersion) == 'v2.0')) Or ($(TargetFrameworkIdentifier) == 'MonoAndroid' And ($(TargetFrameworkVersion) == 'v7.0' Or $(TargetFrameworkVersion) == 'v7.1' Or $(TargetFrameworkVersion) == 'v8.0')) Or ($(TargetFrameworkIdentifier) == 'MonoTouch') Or ($(TargetFrameworkIdentifier) == 'Xamarin.tvOS') Or ($(TargetFrameworkIdentifier) == 'Xamarin.watchOS')">

Note that we're specifically pointing MonoAndroid v7.0/v7.1/v8.0 to these directories. My guess is something to do with some recent .NET Standard change.

I'll dig through the code when I get a chance.

tastyeggs commented 6 years ago

This likely happens because of this:

In FrameworkHandling.fs:

member internal x.RawSupportedPlatforms =
        match x with
        ...
        | MonoAndroid MonoAndroidVersion.V7 -> [ MonoAndroid MonoAndroidVersion.V6; DotNetStandard DotNetStandardVersion.V1_6 ]
        | MonoAndroid MonoAndroidVersion.V7_1 -> [ MonoAndroid MonoAndroidVersion.V7 ]
        | MonoAndroid MonoAndroidVersion.V8 -> [ MonoAndroid MonoAndroidVersion.V7_1 ]
        ...

In combination with the following nuspec:

<references>
      ...
      <group targetFramework=".NETStandard1.0">
        <reference file="Xamarin.Forms.Core.dll" />
        <reference file="Xamarin.Forms.Platform.dll" />
        <reference file="Xamarin.Forms.Xaml.dll" />
      </group>
      ...
      <group targetFramework="MonoAndroid1.0">
        <reference file="Xamarin.Forms.Core.dll" />
        <reference file="Xamarin.Forms.Platform.dll" />
        <reference file="Xamarin.Forms.Xaml.dll" />
        <reference file="FormsViewGroup.dll" />
        <reference file="Xamarin.Forms.Platform.Android.dll" />
      </group>
     ...

My guess is this causes paket to write references for Xamarin.Forms package to the .NETStandard framework for MonoAndroid 7/7.1/8. For Xamarin Forms to work properly, the references need to be made to the MonoAndroid framework.

@forki is my assessment correct? Any suggestions on how I can go about fixing this?

forki commented 6 years ago

Why is setting the target framework not working for you?

tastyeggs commented 6 years ago

Do you mean setting the framework restriction in paket.references?

I've tried setting nuget Xamarin.Forms 2.5.0.91635 framework: MonoAndroid7.1 in paket.references, but that simply leads to the removal of the unused When conditions. MonoAndroid 7/7.1/8 are still grouped under the condition that adds the .netstandard references.

forki commented 6 years ago

No in very first comment I meant setting TargetFrameworkIdentifier in the csproj.

tastyeggs commented 6 years ago

Nope, that doesn't have any impact because:

  1. TargetFrameworkIdentifier is set by Xamarin.Android.CSharp.targets anyway
  2. The real issue here is MonoAndroid 7/7.1/8 are treated differently from MonoAndroid <= 6, and as a result they aren't getting the essential references required to compile the project.

See the above two When conditions in my third comment -- MonoAndroid 7/7.1/8 are set to use references from the .netstandard directory instead of the MonoAndroid10 directory. This works just fine when I set TargetFrameworkVersion to 6.0

forki commented 6 years ago

Can you please send a modified csproj with what you think paket should add? Then I can look at the diff

Am 26.11.2017 09:53 schrieb "tastyeggs" notifications@github.com:

Nope, that doesn't have any impact because:

  1. TargetFrameworkIdentifier is set by Xamarin.Android.CSharp.targets anyway
  2. The real issue here is MonoAndroid 7/7.1/8 are treated differently from MonoAndroid <= 6, and as a result they aren't getting the essential references required to compile the project.

See the above two When conditions in my third comment -- MonoAndroid 7/7.1/8 are set to use references from the .netstandard directory instead of the MonoAndroid10 directory. This works just fine when I set TargetFrameworkVersion to 6.0

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fsprojects/Paket/issues/2809#issuecomment-346993521, or mute the thread https://github.com/notifications/unsubscribe-auth/AADgNIgWqi0S86gJUPYEgEdZHqVkDs1_ks5s6ScggaJpZM4PqCnX .

matthid commented 6 years ago

TargetFrameworkIdentifier is set by Xamarin.Android.CSharp.targets anyway

That doesn't mean paket can find it...

forki commented 6 years ago

It's not important if paket finds it. But MSBuild

Am 26.11.2017 10:03 schrieb "Matthias Dittrich" notifications@github.com:

TargetFrameworkIdentifier is set by Xamarin.Android.CSharp.targets anyway

That doesn't mean paket can find it...

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/fsprojects/Paket/issues/2809#issuecomment-346994011, or mute the thread https://github.com/notifications/unsubscribe-auth/AADgNGRdKX9kdNpiobO3gO8PrP2dMrK8ks5s6SlagaJpZM4PqCnX .

tastyeggs commented 6 years ago

Sure, I've attached two csproj files in the ZIP file:

  1. Orig.csproj is what paket currently generates
  2. Modified.csproj is what it should generate for Xamarin.Forms to correctly work. Note that this affects lots of other packages as well -- as long as Xamarin.Android's Target Framework is set to >= 7.0 (Nougat/Oreo).

CSProjs.zip

tastyeggs commented 6 years ago

Apologies, closed by mistake

ylatuya commented 6 years ago

I can reproduce the issue too, where for Android it's using lib/netstandard1.0 instead of lib/MonoAndroid10. My csproj is using, adding TargetFrameworkIdentifier although it's not needed since Xamarin.Android.CSharp.targets is already setting it

 <TargetFrameworkVersion>v7.0</TargetFrameworkVersion>
 <TargetFrameworkIdentifier>MonoAndroid</TargetFrameworkIdentifier>

Here is the relevant part of the csproj where for iOS it's doing it correctly and for Android it's using the same condition as for Net45

285   <Choose>
286     <When Condition="$(TargetFrameworkIdentifier) == 'Xamarin.iOS'">
287       <ItemGroup>
288         <Reference Include="Xamarin.Forms.Core">
289           <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath>
290           <Private>True</Private>
291           <Paket>True</Paket>
292         </Reference>
293         <Reference Include="Xamarin.Forms.Platform">
294           <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll</HintPath>
295           <Private>True</Private>
296           <Paket>True</Paket>
297         </Reference>
298         <Reference Include="Xamarin.Forms.Platform.iOS">
299           <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll</HintPath>
300           <Private>True</Private>
301           <Paket>True</Paket>
302         </Reference>
303         <Reference Include="Xamarin.Forms.Xaml">
304           <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll</HintPath>
305           <Private>True</Private>
306           <Paket>True</Paket>
307         </Reference>
308       </ItemGroup>
309     </When>
310     <When Condition="($(TargetFrameworkIdentifier) == 'MonoAndroid' And $(TargetFrameworkVersion) == 'v7.0') Or ($(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5')">
311       <ItemGroup>
312         <Reference Include="Xamarin.Forms.Core">
313           <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Core.dll</HintPath>
314           <Private>True</Private>
315           <Paket>True</Paket>
316         </Reference>
317         <Reference Include="Xamarin.Forms.Platform">
318           <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Platform.dll</HintPath>
319           <Private>True</Private>
320           <Paket>True</Paket>
321         </Reference>
322         <Reference Include="Xamarin.Forms.Xaml">
323           <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Xaml.dll</HintPath>
324           <Private>True</Private>
325           <Paket>True</Paket>
326         </Reference>
327       </ItemGroup>
328     </When>
329   </Choose>

And this is how it should be

  <Choose>
    <When Condition="$(TargetFrameworkIdentifier) == 'Xamarin.iOS'">
      <ItemGroup>
        <Reference Include="Xamarin.Forms.Core">
          <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Platform">
          <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Platform.iOS">
          <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Xaml">
          <HintPath>..\..\packages\Xamarin.Forms\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
      </ItemGroup>
    </When>
    <When Condition="$(TargetFrameworkIdentifier) == 'MonoAndroid'">
      <ItemGroup>
        <Reference Include="Xamarin.Forms.Core">
          <HintPath>..\..\packages\Xamarin.Forms\lib\MonoAndroid10\Xamarin.Forms.Core.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Platform">
          <HintPath>..\..\packages\Xamarin.Forms\lib\MonoAndroid10\Xamarin.Forms.Platform.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Platform.iOS">
          <HintPath>..\..\packages\Xamarin.Forms\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Xaml">
          <HintPath>..\..\packages\Xamarin.Forms\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
      </ItemGroup>
    </When>
    <When Condition="$(TargetFrameworkIdentifier) == '.NETFramework' And $(TargetFrameworkVersion) == 'v4.5'">
      <ItemGroup>
        <Reference Include="Xamarin.Forms.Core">
          <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Core.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Platform">
          <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Platform.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
        <Reference Include="Xamarin.Forms.Xaml">
          <HintPath>..\..\packages\Xamarin.Forms\lib\netstandard1.0\Xamarin.Forms.Xaml.dll</HintPath>
          <Private>True</Private>
          <Paket>True</Paket>
        </Reference>
      </ItemGroup>
    </When>
ylatuya commented 6 years ago

Here is a diff of the changes after trying to upgrade to Xamarin.Forms 2.5 that explains a bit better the impact of the issue: https://gist.github.com/ylatuya/55eb4104b436c053c59dcd9e63e28f8b

ylatuya commented 6 years ago

I have found another issue related to the same problem. Xamarin.Forms 2.5 has dependencies to Xamarin.Android.Support.foo packages that are no longer referenced in the csproj either.

ylatuya commented 6 years ago

Is there anything else we can do to help resolve this issue? I would like to help but I have zero experience in F# or any other functional language. Paket is currently unusable right now for Xamarin Forms projects and we have to manually update dependencies. Rolling back to using nuget would be a downgrade in functionalities and we'd like to avoid it at any cost :)

forki commented 6 years ago

should be fixed. I added a penalty for switiching from monoandroid to netstandard