sdk icon indicating copy to clipboard operation
sdk copied to clipboard

[net7] Creating a cross-targeted `net7.0-ios` + `net6.0-ios` packages forces consumers to build with net7 workloads

Open rolfbjarne opened this issue 2 years ago • 32 comments

From @jeromelaban on Mon, 31 Oct 2022 14:18:39 GMT

Creating a cross-targeted net7.0-ios + net6.0-ios packages forces consumers to build with net7 workloads, causing dependents on net6.0 to fail building with the following message:

CSC : error CS1705: Assembly 'Uno.UI' with identity 'Uno.UI, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null'
uses 'Microsoft.iOS, Version=16.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065' which 
has a higher version than referenced assembly 'Microsoft.iOS' with identity 'Microsoft.iOS, Version=15.4.300.0, 
Culture=neutral, PublicKeyToken=84e04ff9cfb79065'

This means that any library author currently cannot build packages for net6.0 and net7.0 without resorting to manual package authoring and multi-builds with global.json modifications.

Steps to Reproduce

  1. Install .NET 6.0.40x with ios workload
  2. Install .NET 7 RC2 with ios workload
  3. Create an empty library
  4. Pack for <TargetFrameworks>net6.0-ios;net7.0-ios</TargetFramework>

Expected Behavior

net6.0-ios binaries reference Microsoft.iOS 15.4.300.x

Actual Behavior

The net6.0-ios binaries reference Microsoft.iOS 16.0.x

Environment

.NET 7.0 RC2 with workloads (VS 17.4 Preview 5)

Build Logs

I can provide those as needed.

As needed.

Example Project (If Possible)

Copied from original issue xamarin/xamarin-macios#16524

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @rolfbjarne on Mon, 31 Oct 2022 14:21:24 GMT

@jonathanpeppers is this something you've seen for Android too?

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @jeromelaban on Mon, 31 Oct 2022 14:23:11 GMT

Android does not seem to exhibit this problem at this point, maybe the binaries did not change versions enough to cause this?

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @jonathanpeppers on Mon, 31 Oct 2022 14:25:01 GMT

I think we've always kept Mono.Android.dll at version 0.0.0.0 to avoid this...

I don't know if that is the right approach, but it's been that way for a long time, maybe since beginning of Xamarin.Android?

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

From @rolfbjarne on Mon, 07 Nov 2022 09:59:35 GMT

net6.0-ios binaries reference Microsoft.iOS 15.4.300.x

We've released iOS 16 support for net6.0-ios, so it's expected that net6.0-ios assemblies built with the latest .NET 6 workloads reference Microsoft.iOS 16.0.*.

CSC : error CS1705: Assembly 'Uno.UI' with identity 'Uno.UI, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null' uses 'Microsoft.iOS, Version=16.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065' which has a higher version than referenced assembly 'Microsoft.iOS' with identity 'Microsoft.iOS, Version=15.4.300.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065'

This error seems to indicate that the consumer isn't using the latest version of the .NET 6 workloads (with iOS 16 support).

Can you include a binlog of a build that fails like this?

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @msftbot[bot] on Mon, 07 Nov 2022 09:59:43 GMT

Hi @jeromelaban. We have added the "need-info" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @filipnavara on Mon, 07 Nov 2022 10:03:28 GMT

This error seems to indicate that the consumer isn't using the latest version of the .NET 6 workloads (with .iOS 16 support).

So, funny story... I keep trying to explain to the SDK team that I need to update workloads for different SDK versions (https://github.com/dotnet/sdk/issues/27109). This is yet another use case for that. The suggestion to use "global.json" to force specific SDK version doesn't work if you want to multi-target, and they insist that the broken --sdk-version switch is not supposed to be used for that.

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @jeromelaban on Mon, 07 Nov 2022 13:48:50 GMT

@rolfbjarne the issue here is that the final app is not supposed to be updating to the latest of .NET 6 SDK. If my library is cross-targeting for net6-ios and net7-ios, I'm assuming that it will work like net6 and net7 and that it will be compatible with net6 built apps (with any net6 SDK), and net7 built apps (with a net7 SDK).

Now I understand that there's the underlying native SDK variant, where it should be that I should build net6.0-ios15, net6.0-ios16 and net7.0-ios16, but is it possible to do with only a net7 SDK installed?

Note that here, the overall scenario is that as a nuget package author I do not want to impose an SDK update to my consumers on package upgrades, regardless of the SDK used to build that the package. I still want them to build able to build an app two years from now without updating anything.

I'll try to get a binlog.

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

From @rolfbjarne on Mon, 23 Jan 2023 22:38:58 GMT

Ok, so to recap, what's needed is a way to build with the earliest version (or a specific version) of a workload for a given .NET version (the project doesn't really have to be multi-targetting, that just makes the problem more complicated to solve), to ensure maximum compatibility with your customers.

The use-case would be to compile a library that uses the earliest managed API for the workload (to maximize compatibility with that .NET version, since consumers of that library can then use any workload version when consuming said library).

And ideally this would be specified in the csproj somehow.

It seems that specifying the OS version in the TargetFramework could be a way to express this:

<TargetFrameworks>net6.0-ios15.4;net7.0-ios16.0</TargetFrameworks>

but that doesn't work (the net6.0-ios15.4 target framework still builds with newer reference assembly from the workload for iOS 16.0, and not the older for iOS 15.4).

Moving to dotnet/runtime, since I don't think we can do this today with how workloads are designed and implemented.

rolfbjarne avatar Jan 23 '23 22:01 rolfbjarne

@steveisok should we move this to SDK or keep it here?

marek-safar avatar Jan 24 '23 08:01 marek-safar

this isn't anything set by the runtime workloads

lewing avatar Jan 24 '23 20:01 lewing

this isn't anything set by the runtime workloads

Correct, I think this needs to move to the sdk

steveisok avatar Jan 24 '23 20:01 steveisok

to me this looks like the problem is coming from the manifest

		"Microsoft.iOS.Sdk.net7": {
			"kind": "sdk",
			"version": "16.0.1478",
			"alias-to": {
				"any": "Microsoft.iOS.Sdk"
			}
		},
		"Microsoft.iOS.Sdk.net6": {
			"kind": "sdk",
			"version": "16.0.527",
			"alias-to": {
				"any": "Microsoft.iOS.Sdk"
			}
		},

that says to use 16.0.527 when targeting net6

<Project>
	<ImportGroup Condition=" '$(TargetPlatformIdentifier)' == 'iOS' ">
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Sdk.net7" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '7.0')) " />
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Sdk.net6" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) " />
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Windows.Sdk.Aliased.net7" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '7.0')) And $([MSBuild]::IsOSPlatform('windows'))" />
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Windows.Sdk.Aliased.net6" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) And $([MSBuild]::IsOSPlatform('windows'))" />
	</ImportGroup>

	<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), '6.0')) ">
		<SdkSupportedTargetPlatformIdentifier Include="ios" DisplayName="iOS" />
	</ItemGroup>

	<PropertyGroup Condition=" '$(TargetPlatformIdentifier)' == 'iOS' and $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) ">
		<AfterMicrosoftNETSdkTargets>$(AfterMicrosoftNETSdkTargets);$(_XamarinSdkRootDirectory)..\16.0.1478\targets\Xamarin.Shared.Sdk.MultiTarget.targets</AfterMicrosoftNETSdkTargets>
	</PropertyGroup>
</Project>

I think you'll need to add 15.x variant to the workload to have it work like that

lewing avatar Jan 24 '23 20:01 lewing

If I'm understanding what @rolfbjarne was saying, for iOS or any platform that plays fast and loose with compat, some/all of the older workload versions should hang around so that we can reference it.

Yeah, I agree with @lewing, the only way I know how to pull it off is to add however many 15.x variants you need.

steveisok avatar Jan 24 '23 20:01 steveisok

my understanding is if you add net6.0-ios15 packs and aliases and import those by checking TargetPlatformVersion in the targets file you will get what you want for the Micrsoft.iOS reference, if we need ios15 specific builds of runtime we'll have to add those and the version check to the runtime manifest as well (i hope we would just need to reference the last build and not continue to service those builds as well)

cc @dsplaisted

something like

                "Microsoft.iOS15.Sdk.net6": {
			"kind": "sdk",
			"version": "15.4.300",
			"alias-to": {
				"any": "Microsoft.iOS.Sdk"
			}
		},

and

	<ImportGroup Condition=" '$(TargetPlatformIdentifier)' == 'iOS' ">
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Sdk.net7" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '7.0')) " />
		<Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier).Sdk.net6" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) and $([MSBuild]::VersionEquals($(TargetPlatformVersion), '16'))" />
                <Import Project="Sdk.props" Sdk="Microsoft.$(TargetPlatformIdentifier)15.Sdk.net6" Condition=" $([MSBuild]::VersionEquals($(TargetFrameworkVersion), '6.0')) and $([MSBuild]::VersionEquals($(TargetPlatformVersion), '15'))" />
	</ImportGroup>

lewing avatar Jan 24 '23 21:01 lewing

this would potentially lead to very large workloads even if you split them along the platform version. I really feel like we keep running into places where being tied to the manifest versions and forcing everything to be bundled limits things

lewing avatar Jan 24 '23 22:01 lewing

It seems that specifying the OS version in the TargetFramework could be a way to express this:

<TargetFrameworks>net6.0-ios15.4;net7.0-ios16.0</TargetFrameworks>

but that doesn't work (the net6.0-ios15.4 target framework still builds with newer reference assembly from the workload for iOS 16.0, and not the older for iOS 15.4).

As far as I'm understanding it, this is the main problem.

You don't necessarily need to split into separate workloads. If you want, you can have a single workload that handles multiple versions, and will use the appropriate targeting and runtime packs. That's essentially what we do in the base SDK, we have a mapping from the target framework to the targeting and runtime pack versions. You don't have to include all those assets in the workload either, you can have it so if they are not installed they will be downloaded during NuGet restore.

dsplaisted avatar Jan 25 '23 19:01 dsplaisted

OK, thanks for the ideas how to implement this in the workload, I'll give it a try and see if I can make it work somehow.

rolfbjarne avatar Jan 25 '23 19:01 rolfbjarne

@rolfbjarne Any updates for this?

Youssef1313 avatar Feb 01 '23 04:02 Youssef1313

@rolfbjarne Any updates for this?

No updates yet, and it will likely take a little while.

rolfbjarne avatar Feb 01 '23 06:02 rolfbjarne

If I were to use net7.0-ios in library project now (today) is it going take 16.2 instead of 16.0? Is it the same case for dotnet android also?

Here is an example of MAUI package taken from https://dev.azure.com/xamarin/public/_build/results?buildId=78627&view=artifacts&pathAsName=false&type=publishedArtifacts Screen Shot 2023-02-08 at 7 47 38 AM

From Xamarin.Forms: Screen Shot 2023-02-08 at 7 50 37 AM

I still don't understand how this work in dotnet and xamarin. Does net7.0-ios should implicitly uses the latest version or the minimum version (like 16.2, 16.0 or just 0.0) to avoid this confusion?

yunusefendi52 avatar Feb 08 '23 00:02 yunusefendi52

If I were to use net7.0-ios in library project now (today) is it going take 16.2 instead of 16.0?

Yes.

Does net7.0-ios should implicitly uses the latest version or the minimum version (like 16.2, 16.0 or just 0.0) to avoid this confusion?

I believe there will be confusion no matter what we do, because if we don't implicitly use the latest version, people will have to opt in to any updates we ship whenever Apple releases OS updates.

rolfbjarne avatar Feb 08 '23 09:02 rolfbjarne

@rolfbjarne @lewing would you know if there's another SDK or runtime related issue that can be followed about a progress on this issue? Thanks!

jeromelaban avatar Jun 08 '23 12:06 jeromelaban

@jeromelaban this is the correct issue to follow

rolfbjarne avatar Jun 08 '23 13:06 rolfbjarne

This same thing happens when you have a net8 SDK installed and you build for net7. Is there any hacks we can do to make it work for now?

mattleibow avatar Jun 22 '23 10:06 mattleibow

This same thing happens when you have a net8 SDK installed and you build for net7. Is there any hacks we can do to make it work for now?

I don't know of any good/easy ones, there's the obvious rather cumbersome workaround of: install workload 7, build, install workload 8, build, merge the results somehow.

rolfbjarne avatar Jun 22 '23 11:06 rolfbjarne

I don't know of any good/easy ones, there's the obvious rather cumbersome workaround of: install workload 7, build, install workload 8, build, merge the results somehow.

This is close to what we're doing in Uno, which is building each target framework in its own job, then use a separate job that bundles all together using a nuspec. It's cumbersome, definitely :)

Somehow related, I wonder, was it considered to build some sorts of a lightweight version of the workloads, with reference assemblies only so that installation would go faster? This would be something to improve https://github.com/dotnet/sdk/issues/24843.

jeromelaban avatar Jun 22 '23 11:06 jeromelaban

Current plan: https://github.com/xamarin/xamarin-macios/pull/18523

rolfbjarne avatar Jun 30 '23 11:06 rolfbjarne

This has been fixed (https://github.com/xamarin/xamarin-macios/pull/18931), and should be in .NET 8 RC 2.

Contrary to other platforms, library authors will have to be explicit about the target platform version if they want the earliest:

<TargetFrameworks>net7.0-ios16.0;net7.0-ios16.4;net8.0-ios-17.0</TargetFrameworks>

This will create a library for three different iOS versions.

Also note that we won't support all released OS versions, it'll typically be the first release for a given .NET version (which was iOS 16.0 for .NET 7), and the latest (currently iOS 16.4). If Apple releases iOS 16.5, we won't support using net7.0-ios16.4 anymore when using the latest stable workload.

rolfbjarne avatar Sep 06 '23 17:09 rolfbjarne

Thanks a lot for the update!

jeromelaban avatar Sep 06 '23 17:09 jeromelaban