Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default RID is always x64, even on arm64 machines #17841

Closed
mattjohnsonpint opened this issue Mar 17, 2023 · 14 comments
Closed

Default RID is always x64, even on arm64 machines #17841

mattjohnsonpint opened this issue Mar 17, 2023 · 14 comments
Labels
enhancement The issue or pull request is an enhancement
Milestone

Comments

@mattjohnsonpint
Copy link

mattjohnsonpint commented Mar 17, 2023

Steps to Reproduce

dotnet new ios -n IosApp1
cd IosApp1
dotnet build

(Or a MAUI app targeting iOS, etc.)

Expected Behavior

  • On an Intel Mac, it should build using RID iossimulator-x64
  • On an Apple Silicon Mac, it should build using RID iossimulator-arm64

Actual Behavior

It always builds using iossimulator-x64

(Note, the same is true for Mac Catalyst targets. It always builds for maccatalyst-x64.)

Workaround

Add to the csproj:

<OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</OSArchitecture>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'Arm64'">iossimulator-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'X64'">iossimulator-x64</RuntimeIdentifier>

(This shouldn't be necessary though.)

Environment

Version information
Visual Studio Enterprise 2022 for Mac
Version 17.5.2 (build 14)
Installation UUID: 3c0bb86b-9d71-42a3-8e98-a5904826ab76

Runtime
.NET 7.0.1 (64-bit)
Architecture: Arm64
Microsoft.macOS.Sdk 12.3.2372; git-rev-head:754abbf6a3563f6267e5717ae832b4ac25b1f2fb; git-branch:release/7.0.1xx-xcode13.3

Roslyn (Language Service)
4.5.0-3.23056.2+97881342e427ff5cdcba8f12b12ff8e6f3564431

NuGet
Version: 6.4.0.117

.NET SDK (Arm64)
SDK: /usr/local/share/dotnet/sdk/7.0.202/Sdks
SDK Versions:
	7.0.202
	7.0.200
	7.0.102
	6.0.407
	6.0.406
	6.0.405
MSBuild SDKs: /Applications/Visual Studio.app/Contents/MonoBundle/MSBuild/Current/bin/Sdks

.NET SDK (x64)
SDK Version: 3.1.426

.NET Runtime (Arm64)
Runtime: /usr/local/share/dotnet/dotnet
Runtime Versions:
	7.0.4
	7.0.3
	7.0.2
	6.0.15
	6.0.14
	6.0.13

.NET Runtime (x64)
Runtime: /usr/local/share/dotnet/x64/dotnet
Runtime Version: 3.1.32

Xamarin.Profiler
Version: 1.8.0.49
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

Updater
Version: 11

Apple Developer Tools
Xcode: 14.2 21534
Build: 14C18

Xamarin.Mac
Version: 9.1.0.5 Visual Studio Enterprise
Hash: 7738c90c9
Branch: xcode14.2
Build date: 2023-01-25 15:56:14-0500

Xamarin.iOS
Version: 16.2.0.5 Visual Studio Enterprise
Hash: 7738c90c9
Branch: xcode14.2
Build date: 2023-01-25 15:56:15-0500

Xamarin Designer
Version: 17.5.3.47
Hash: e8b5d371c3
Branch: remotes/origin/d17-5
Build date: 2023-03-09 19:43:36 UTC

Xamarin.Android
Version: 13.2.0.0 (Visual Studio Enterprise)
Commit: xamarin-android/d17-5/797e2e1
Android SDK: /Users/matt/Library/Android/sdk
	Supported Android versions:
		12.1 (API level 32)
		12.0 (API level 31)
		8.1  (API level 27)
		11.0 (API level 30)
		10.0 (API level 29)
		9.0  (API level 28)
		13.0 (API level 33)

SDK Command-line Tools Version: 7.0
SDK Platform Tools Version: 33.0.3
SDK Build Tools Version: 33.0.0 rc4

Build Information: 
Mono: 6dd9def
Java.Interop: xamarin/java.interop/main@149d70fe
SQLite: xamarin/sqlite/3.40.0@fdc1e34
Xamarin.Android Tools: xamarin/xamarin-android-tools/main@9f02d77

Microsoft Build of OpenJDK
Java SDK: /Library/Java/JavaVirtualMachines/microsoft-11.jdk
11.0.16.1
Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

Eclipse Temurin JDK
Java SDK: /Library/Java/JavaVirtualMachines/temurin-8.jdk
1.8.0.302
Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

Android SDK Manager
Version: 17.5.0.33
Hash: f0c0c52
Branch: remotes/origin/d17-5~2
Build date: 2023-03-09 19:43:41 UTC

Android Device Manager
Version: 0.0.0.1245
Hash: 7f8a990
Branch: 7f8a990
Build date: 2023-03-09 19:43:41 UTC

Build Information
Release ID: 1705020014
Git revision: f95e127ba5837148420d81c7c14c47ca8eade102
Build date: 2023-03-09 19:41:47+00
Build branch: release-17.5
Build lane: release-17.5

Operating System
Mac OS X 13.2.1
Darwin 22.3.0 Darwin Kernel Version 22.3.0
    Mon Jan 30 20:38:37 PST 2023
    root:xnu-8792.81.3~2/RELEASE_ARM64_T6000 arm64

Build Logs

log.binlog.zip

Example Project (If Possible)

IosApp1.zip

@mattjohnsonpint mattjohnsonpint changed the title Default simulator is always x64, even on arm64 machines Default RID is always x64, even on arm64 machines Mar 17, 2023
@mattjohnsonpint
Copy link
Author

Why this matters? Because if you are using an app or library with a binding to a native framework, and you launch your app in Visual Studio for Mac on an arm64 machine, it tries builds for x64 and then throws an exception when trying to use a component from the native framework.

For example, using Sentry.Maui will give this exception on initialization:

image

Exception details are specific to that library, but the key point is the native class hasn't been loaded.

If you manually set the RID for iossimulator-arm64 then everything works fine.

@mattjohnsonpint
Copy link
Author

mattjohnsonpint commented Mar 18, 2023

Also, I think my "workaround" is problematic because it will then build using iossimulator-arm64 even for non-simulator builds. I'm not sure the correct property to test to ensure it's a simulator build. I tried _SdkIsSimulator and _IsDotNetSimulatorBuild but neither seemed to work. It might be too early in the build process to use them directly in the csproj.

@rolfbjarne
Copy link
Member

Why this matters? Because if you are using an app or library with a binding to a native framework, and you launch your app in Visual Studio for Mac on an arm64 machine, it tries builds for x64 and then throws an exception when trying to use a component from the native framework.

This isn't entirely accurate: building for x64 should work just fine on an arm64 (if you have Rosetta installed), the app will be an x64 app, using x64 native libraries. The only problem could be if the NuGet doesn't provide an x64 version of the native library (but in that case the NuGet wouldn't work on an x64 machine).

So if a NuGet isn't working when building for x64 on an arm64 machine, something else is wrong.

In any case the plan is currently to change the default for .NET 8.

Also, I think my "workaround" is problematic because it will then build using iossimulator-arm64 even for non-simulator builds. I'm not sure the correct property to test to ensure it's a simulator build. I tried _SdkIsSimulator and _IsDotNetSimulatorBuild but neither seemed to work. It might be too early in the build process to use them directly in the csproj.

Correct, it won't work properly if you build for device in the IDE. This is because the default RuntimeIdentifier is actually overridden by the IDE when building in the IDE (to whatever device/simulator you're building for) - but you're overriding the default, and thus also overriding what the IDE does.

AFAIK there's currently no trivial way to do what you want (force a RuntimeIdentifier for simulator only), but you might be able to create a custom configuration that does it, something like this:

<PropertyGroup Condition="'$(Configuration)' == 'DebugSimulator'">
    <RuntimeIdentifier>iossimulator-arm64</RuntimeIdentifier>
</PropertyGroup>

and then selecting the DebugSimulator configuration in the IDE when you want to debug in the simulator.

@rolfbjarne rolfbjarne added the enhancement The issue or pull request is an enhancement label Mar 21, 2023
@rolfbjarne rolfbjarne added this to the .NET 8 milestone Mar 21, 2023
@rolfbjarne rolfbjarne added this to Other in .NET 8 - Themes Mar 21, 2023
@rolfbjarne
Copy link
Member

Actually, I wonder if this would work:

<OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</OSArchitecture>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'Arm64' And '$(RuntimeIdentifier)' == 'iossimulator-x64'">iossimulator-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'X64'">iossimulator-x64</RuntimeIdentifier>

which looks a bit weird, but the idea is that if the IDE has set RuntimeIdentifier=iossimulator-x64, and we're on arm64, then change RuntimeIdentifier to iossimulator-arm64

@mattjohnsonpint
Copy link
Author

I tried that - it doesn't work in the default case when the RuntimeIdentifier isn't specified.

@rolfbjarne
Copy link
Member

Slight tweak, this seems to work for me:

<OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</OSArchitecture>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'Arm64' And '$(_iOSRuntimeIdentifier)' == 'iossimulator-x64'">iossimulator-arm64</RuntimeIdentifier>
<RuntimeIdentifier Condition="'$(OSArchitecture)' == 'X64'">iossimulator-x64</RuntimeIdentifier>

@mattjohnsonpint
Copy link
Author

mattjohnsonpint commented Mar 23, 2023

Good call on _iOSRuntimeIdentifier (set by VS). But I need it to work from either VS or the command line. After a bit of testing, I ended up with the following - which seems to work well in all cases:

<PropertyGroup>
  <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)</_OSArchitecture>
  <_TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</_TargetPlatformIdentifier>
  <RuntimeIdentifier Condition="'$(_TargetPlatformIdentifier)' == 'ios' And '$(_OSArchitecture)' == 'Arm64' And ('$(_iOSRuntimeIdentifier)' == 'iossimulator-x64' Or ('$(_iOSRuntimeIdentifier)' == '' And '$(RuntimeIdentifier)' == ''))">iossimulator-arm64</RuntimeIdentifier>
</PropertyGroup>

(No need to set x64, since that would be correct already.)

Also note - This has to be in the app's csproj, after the target framework is set. Ideally it would be in Directory.Build.props, but that's too early - and Directory.Build.targets is too late - as would be any custom target.

@mattjohnsonpint
Copy link
Author

This isn't entirely accurate: building for x64 should work just fine on an arm64 (if you have Rosetta installed), the app will be an x64 app, using x64 native libraries. The only problem could be if the NuGet doesn't provide an x64 version of the native library (but in that case the NuGet wouldn't work on an x64 machine).

Hmmm.. Well, the native libraries are like this:

image

There's only two versions - either ios-arm64 used on real devices, or ios-arm64_x86_64-simulator used on simulators for either arm64 or x64. The latter being a "fat library" containing both architectures.

Is it possible that the arm64 code from the fat library was loaded, and then we get the error because we targeted iossimulator-x64? I'm not sure if the code that's throwing "the native class hasn't been loaded" is bound to the architecture of the host or of the target. I can see that when mismatched, perhaps that could be an issue?

@rolfbjarne
Copy link
Member

Hmmm.. Well, the native libraries are like this:

Yes, that looks correct :/

In any case, we'll change the default RID for .NET 8, and in the meantime I believe you have a viable workaround.

@Arslan007
Copy link

@rolfbjarne this solution doesn't work for me as I am using AppCenter and it doesnot have simulator slice in .a file. I am still stucked

@mattjohnsonpint
Copy link
Author

... we'll change the default RID for .NET 8

It would be great if this was fixed for .NET 6+, since they're still in their support lifecycle.

I believe it's in this section?

https://github.com/xamarin/xamarin-macios/blob/d6694c46e998dcd265a9f9a93319b8bf16f8ea2c/dotnet/targets/Xamarin.Shared.Sdk.props#L67C69-L74

Thanks.

@rolfbjarne
Copy link
Member

It would be great if this was fixed for .NET 6+, since they're still in their support lifecycle.

It's a rather big change, and will break some people, because there are a lot of third-party native libraries that don't ship an arm64 slice for the simulator, which is why we will only do this starting with .NET 8.

@rolfbjarne
Copy link
Member

@Arslan007

@rolfbjarne this solution doesn't work for me as I am using AppCenter and it does not have simulator slice in .a file. I am still stucked

You might be running into a different problem, so I suggest filing a new issue (to avoid making things confusing here), and we can diagnose your problem there.

rolfbjarne added a commit that referenced this issue Aug 7, 2023
* When publishing a mobile app, choose a device architecture (as opposed
  to showing an error if none was specified by the developer)
* When building for the simulator, choose the arm64 runtime identifier
  if we're running on an arm64 machine.
* When building a Debug build for desktop, choose the arm64 runtime
  identifier if we're running on an arm64 machine.

Fixes #18455.
Fixes #16152.
Fixes #17841.

Section in the release notes: https://github.com/xamarin/xamarin-macios/wiki/.NET-8-release-notes#default-runtimeidentifiers

---------

Co-authored-by: Alex Soto <alex@alexsoto.me>
@rolfbjarne
Copy link
Member

Fixed in #18495.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement The issue or pull request is an enhancement
Projects
No open projects
Development

No branches or pull requests

3 participants