How does one get a universal macOS library from a Swift package dependency?

I'm getting an error building my Mac app for both Apple Silicon and Intel, related to a Swift package dependency.

The dependency is a pure Swift package (SwiftyXMLParser), and I'm bringing it into my app with a package dependency from the git repository URL.

My app is configured to build standard architectures to run natively on both Apple Silicon and Intel.

The build fails because it looks like the package manager only provides the active architecture, not a universal library, when the latter is configured. It happens when I set "Build Active Architecture Only" to NO (if it's set to YES it succeeds, but then the app won't run on the other architecture).

In particular, if I build on my M1 mini, the error is
Code Block
Could not find module 'SwiftyXMLParser' for target 'x86_64-apple-macos'; found: arm64, arm64-apple-macos


If I build on my x86 MBP, same failure but the error is opposite (no arm64, found: x86_64).

So clearly SPM+Xcode can provide either architecture - is it possible for it to provide a universal lib? Or do I need to download the source of the package and build it through a more manual process?

Accepted Reply

Okay, good news, after trying several approaches I finally found a very simple fix/workaround...

In the app build settings under Architecture, get rid of $(ARCHS_STANDARD) (click the minus to delete it) and add both architectures as explicit values, arm64 and x86_64. Apparently there's an issue with that default setting not really conveying that both architectures are needed when the package dependency is built, completely regardless of the "active architecture" setting.

Everything works great with this change, I was able to build and notarize the app on M1 and verify it now also runs on x86.

Replies

Also a little more info - the package in question does have its Xcode project macOS target configured to build universal architecture and, for Release builds, build-active-architecture-only is set to NO.

https://github.com/yahoojapan/SwiftyXMLParser
Okay, good news, after trying several approaches I finally found a very simple fix/workaround...

In the app build settings under Architecture, get rid of $(ARCHS_STANDARD) (click the minus to delete it) and add both architectures as explicit values, arm64 and x86_64. Apparently there's an issue with that default setting not really conveying that both architectures are needed when the package dependency is built, completely regardless of the "active architecture" setting.

Everything works great with this change, I was able to build and notarize the app on M1 and verify it now also runs on x86.

I'm getting the same exact symptoms with my app that has a dependency on https://github.com/mz2/Carpaccio -- I tried your suggested solution of setting the architectures by hand, but that didn't on its own help in my case. Also, tried clearing derived data and restarted Xcode to make sure that packages were fetched and built from a clean state.

The extra curious fact is that I can literally see both the ARM64 and Intel versions of the Swift module inside the derived data, and whereas under Carpaccio.swiftmodule I can see both ARM64 and Intel versions:

Code Block
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.o
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/arm64-apple-macos.swiftdoc
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/arm64.swiftmodule
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/arm64-apple-macos.swiftmodule
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/Project
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/Project/arm64-apple-macos.swiftsourceinfo
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/Project/arm64.swiftsourceinfo
./Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.swiftmodule/arm64.swiftdoc


... however, the Carpaccio.o that's built only has the active architecture there:

Code Block
./DerivedData/Sashimi-fpvhysoehxifhxcfgdnzcygfncpg/Build/Products/Debug/Carpaccio.o


I've systematically turned off "Build Active Architecture Only" for all my app's targets.

Something that may be explaining a difference between my and your experience is that for me this depended on library is linked into a framework target, not the "main" app target?

I also tried checking out the package in question separately, and build it with:

Code Block
xcrun swift build -c release --arch arm64 --arch x86_64


... I do then indeed see both expected platforms there:

Code Block
lipo -info .build/apple/Products/Release/Carpaccio.o
Architectures in the fat file: .build/apple/Products/Release/Carpaccio.o are: x86_64 arm64


Finally, got this working by setting the list of architectures by hand as you suggest, and then toggling "Any Mac (Apple Silicon, Intel)" instead of "My Mac" as the active scheme!
Post not yet marked as solved Up vote reply of mz2 Down vote reply of mz2
Thanks for providing us with your answer.
I am working with Xcode Version 12.3 (12C33) and overwriting standard architectures doesn't do the trick.

Any other good idea?

The proposed solution of explicitly setting x86_64 arm64 on macOS targets doesn't work for me either with Xcode 13.1. My .a library files, and frameworks all report that they are missing the arm64 architecture when trying to build for "Any Mac". This is because the scheme is "Debug" and that activates active arch only (in my case x86_64).

The debug setting of "build for active arch only" is not being overridden by the setting to build a universal app when "Any Mac" is set. Setting the "Build" target to "Release" avoids all the crazy errors when set to "Any Mac". But then there's a crazy error from my use of modules. mm_malloc.h must not have an equivalent on macOS arm64, or the module isn't built properly for the arm64 build. This was originally from sse intrinsics, so I'll look to replace that with something uninverals.

#include <mm_malloc.h> Module '_Builtin_intrinsics.intel.mm_malloc' requires feature 'x86'

I had a similar error in my project (using Xcode 13.2.1 on an M1 mini), but only in the unit test target associated with my program (a command line tool rather than an application). Oddly, the error pointed to the @testable import <program_name> line in one of my unit test source files, and only when the selected scheme was the one for the unit test target. My project does include a number of Swift packages.

Searching the Internet for the text of the error message led me here; I tried the approved answer (explicitly setting the architecture to "x86_64 arm64", and it seemed to work for me.

However, in subsequent experimentation I found that it appeared to be sufficient (in my case, anyway) to ensure that the Architecture was set to "$(ARCHS_STANDARD)" directly on the project, instead of inheriting the value from the "macOS Default".

In my case, I have added test target after the pod init, install etc.. And I saw, adding test target block in podfile solved my problem.

Example;

target 'SampleProject' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for SampleProject

  target 'SampleProjectTests' do
    inherit! :search_paths
    # Pods for testing
  end
end