sdk
sdk copied to clipboard
[net7] Creating a cross-targeted `net7.0-ios` + `net6.0-ios` packages forces consumers to build with net7 workloads
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
- Install .NET 6.0.40x with ios workload
- Install .NET 7 RC2 with ios workload
- Create an empty library
- 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
From @rolfbjarne on Mon, 31 Oct 2022 14:21:24 GMT
@jonathanpeppers is this something you've seen for Android too?
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?
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?
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-iosbinaries referenceMicrosoft.iOS15.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?
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.
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.
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.
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.
@steveisok should we move this to SDK or keep it here?
this isn't anything set by the runtime workloads
this isn't anything set by the runtime workloads
Correct, I think this needs to move to the sdk
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
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.
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>
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
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.4target 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.
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 Any updates for this?
@rolfbjarne Any updates for this?
No updates yet, and it will likely take a little while.
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

From Xamarin.Forms:

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?
If I were to use
net7.0-iosin library project now (today) is it going take 16.2 instead of 16.0?
Yes.
Does
net7.0-iosshould 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 @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 this is the correct issue to follow
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?
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.
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.
Current plan: https://github.com/xamarin/xamarin-macios/pull/18523
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.
Thanks a lot for the update!