LoginSignup
40

XCFrameworkに対応する際のポイント集

Last updated at Posted at 2020-03-09

これはなに?

2019年WWDCで発表された XCFramework に対応する際のポイント集です。

プロジェクト作成

【Tips】 新規プロジェクト作成から始める場合はEmptyを選択する

Emptyを選択すると、余計なビルド設定がない状態でプロジェクト作成が出来るので可読性が良くなります。

① File > New > Project... > Cross-platform > Other > Empty と選び、新規プロジェクトを作成する。

スクリーンショット 2020-02-25 19.59.41.png

②ターゲットが空のプロジェクトが作成されるので、サポートしたいプラットフォームの数だけターゲットを追加する。
File > New > Target... > [プラットフォーム] > Frameworks

スクリーンショット 2020-02-25 20.08.08.png

iOS macOS tvOS watchOS を追加した場合:
スクリーンショット 2020-02-25 20.12.36.png

③コマンドでプロジェクトの構成を確認するには

$ xcodebuild -list -json
{
  "project" : {
    "configurations" : [
      "Debug",
      "Release"
    ],
    "name" : "Sample",
    "schemes" : [
      "iOS",
      "macOS",
      "tvOS",
      "watchOS"
    ],
    "targets" : [
      "iOS",
      "macOS",
      "tvOS",
      "watchOS"
    ]
  }
}

ビルド設定

【必須】 Deployment Target を設定する

①フレームワークがサポートしたいOSバージョンの範囲の内、下限バージョンを指定します。

スクリーンショット 2020-02-25 20.48.09.png

【オプション】 プロセッサアーキテクチャを指定する

※この章の内容は必要に応じて設定してください。
※Xcode 12以降のバージョンで新規プロジェクトを作成した場合は、comment-1 も参照してください。

フレームワークがサポートするプロセッサアーキテクチャを初期値から変更したい場合は、ARCHSVALID_ARCHSにパラメータを指定します。
その2つの初期値はBase SDKDeployment Targetの値に合わせて変動する(i)ので、Deployment Targetを正しく設定してから行ってください。

i) xcodebuildがプラットフォームに合わせて、最低限必要なアーキテクチャを設定してくれる。

スクリーンショット 2020-02-25 20.48.08.png

①コマンドで現在の値を確認するには

$ xcodebuild -showBuildSettings -project Sample.xcodeproj -scheme iOS archive | grep -e ARCHS
    ARCHS = arm64
    ARCHS_STANDARD = arm64
    ARCHS_STANDARD_32_64_BIT = armv7 arm64
    ARCHS_STANDARD_32_BIT = armv7
    ARCHS_STANDARD_64_BIT = arm64
    ARCHS_STANDARD_INCLUDING_64_BIT = arm64
    ARCHS_UNIVERSAL_IPHONE_OS = armv7 arm64
    VALID_ARCHS = arm64 arm64e armv7 armv7s

②サポートしたいアーキテクチャを、ARCHSに指定します。(ii)
VALID_ARCHSにはARCHSで指定したパラメータを含めるように指定します。

ii) ARCHSにarm64eを含める対応をするには

活用例:

# IPHONEOS_DEPLOYMENT_TARGET=8.0 に設定した場合のビルド設定を出力する
# ARCHS = armv7 arm64 になっている
$ xcodebuild -showBuildSettings -project Sample.xcodeproj -scheme iOS archive 'IPHONEOS_DEPLOYMENT_TARGET=8.0' | grep -e ARCHS
    ARCHS = armv7 arm64
    ARCHS_STANDARD = armv7 arm64
    ARCHS_STANDARD_32_64_BIT = armv7 arm64
    ARCHS_STANDARD_32_BIT = armv7
    ARCHS_STANDARD_64_BIT = arm64
    ARCHS_STANDARD_INCLUDING_64_BIT = armv7 arm64
    ARCHS_UNIVERSAL_IPHONE_OS = armv7 arm64
    VALID_ARCHS = arm64 arm64e armv7 armv7s
# ARCHS=arm64 armv7 armv7s を指定する
$ xcodebuild -showBuildSettings -project Sample.xcodeproj -scheme iOS archive 'IPHONEOS_DEPLOYMENT_TARGET=8.0' 'ARCHS=arm64 armv7 armv7s' | grep -e ARCHS
    ARCHS = arm64 armv7 armv7s    # Build settings from command line: の出力
    ARCHS = arm64 armv7 armv7s    # Build settings for action archive and target iOS: の出力
    ARCHS_STANDARD = armv7 arm64
    ARCHS_STANDARD_32_64_BIT = armv7 arm64
    ARCHS_STANDARD_32_BIT = armv7
    ARCHS_STANDARD_64_BIT = arm64
    ARCHS_STANDARD_INCLUDING_64_BIT = armv7 arm64
    ARCHS_UNIVERSAL_IPHONE_OS = armv7 arm64
    VALID_ARCHS = arm64 arm64e armv7 armv7s

Architectures (ARCHS)

製品が構築されるアーキテクチャのリスト。 これは通常、プラットフォームによって提供される事前定義されたビルド設定に設定されます。 複数のアーキテクチャが指定されている場合、ユニバーサルバイナリが生成されます。

Valid Architectures (VALID_ARCHS)
ターゲットを実際に構築するアーキテクチャのスペースで区切られたリスト。 ターゲットごとに、これはアーキテクチャ(ARCHS)で指定されたリストと横断し、結果のセットが構築されます。 これにより、個々のターゲットが特定のアーキテクチャのビルドをオプトアウトできます。 結果のアーキテクチャのセットが空の場合、実行可能ファイルは生成されません。

Base SDK (SDKROOT)

ビルド中に使用されているベースSDKの名前またはパス。 製品は、指定されたSDK内にあるヘッダーとライブラリに対して構築されます。 このパスはすべての検索パスの前に追加され、環境を介してコンパイラーとリンカーに渡されます。 追加のSDKは、追加のSDK(ADDITIONAL_SDKS)設定で指定できます。

原文: Build settings reference - Xcode Help

デバイスのプロセッサアーキテクチャを調べるには:

Troubleshooting:

【必須】 Mach-O Type を設定する

①動的なXCFrameworkを生成したい場合は、Mach-ODynamic Libraryを指定します。
静的なXCFrameworkを生成したい場合は、Mach-OStatic Libraryを指定します。

スクリーンショット 2023-05-30 20.51.03.png

【必須】 ビットコードに対応する

ビットコードを有効にするために、この3つのビルド設定を指定します。

ENABLE_BITCODE=YES
BITCODE_GENERATION_MODE=bitcode
OTHER_CFLAGS=-fembed-bitcode

Enable Bitcode (ENABLE_BITCODE)

この設定を有効にすると、ターゲットまたはプロジェクトは、それをサポートするプラットフォームおよびアーキテクチャのコンパイル中にビットコードを生成する必要があります。 アーカイブビルドの場合、App Storeに送信するために、リンクされたバイナリでビットコードが生成されます。 他のビルドの場合、コンパイラーとリンカーは、コードがビットコード生成の要件を満たしているかどうかを確認しますが、実際のビットコードは生成しません。

BITCODE_GENERATION_MODE

Xcodeの隠しコマンドなのでXcode Helpには記載がないが、clangのヘルプには記述が少しあります。

$ clang --help | grep bitcode
  -fembed-bitcode-marker  Embed placeholder LLVM IR data as a marker
  -fembed-bitcode=<option>
                          Embed LLVM bitcode (option: off, all, bitcode, marker)
  -fembed-bitcode         Embed LLVM IR bitcode as data

Other C Flags (OTHER_CFLAGS)

CおよびObjective-Cファイルのコンパイラに渡す追加フラグのスペース区切りリスト。 スペースまたは特殊文字(スペースを含む可能性のあるパス名など)を含む引数は、必ずバックスラッシュでエスケープしてください。 Xcodeが特定のCまたはObjective-CコンパイラフラグのUIをまだ提供していない場合は、この設定を使用します。

【必須】 XCFrameworkに対応する

① XCFramework形式(.xcframework)で配布するために、この2つのビルド設定を指定します。

BUILD_LIBRARY_FOR_DISTRIBUTION=YES
SKIP_INSTALL=NO

Build Libraries for Distribution (BUILD_LIBRARY_FOR_DISTRIBUTION)

ライブラリが配布用にビルドされていることを確認します。 Swiftの場合、これによりライブラリの進化とモジュールインターフェイスファイルの生成がサポートされます。

Skip Install (SKIP_INSTALL)
有効にした場合、展開場所がアクティブであってもビルドされた製品をインストールしません。

destination はこの様に指定します。

-destination 'generic/platform=iOS'
-destination 'generic/platform=iOS Simulator'
-destination 'generic/platform=macOS,variant=Mac Catalyst'
-destination 'generic/platform=iPadOS'
-destination 'generic/platform=iPadOS Simulator'
-destination 'generic/platform=macOS'
-destination 'generic/platform=tvOS'
-destination 'generic/platform=tvOS Simulator'
-destination 'generic/platform=watchOS'
-destination 'generic/platform=watchOS Simulator'
-destination 'generic/platform=carPlayOS'
-destination 'generic/platform=carPlayOS Simulator'

【ビルド設定のまとめ】

①まずxcodebuild archiveコマンドをサポートしたいフォーマット(-destination)の回数分実行します。そうすることで複数個の*.xcarchiveファイルが生成されます。
②その後xcodebuild -create-xcframeworkで、全ての*.xcarchiveファイルを1つのパッケージにまとめます(*.xcframework にします)。

STEP 1/2: xcodebuild archive

プロセッサアーキテクチャを指定しない場合

$ xcodebuild \
'ENABLE_BITCODE=YES' \
'BITCODE_GENERATION_MODE=bitcode' \
'OTHER_CFLAGS=-fembed-bitcode' \
'BUILD_LIBRARY_FOR_DISTRIBUTION=YES' \
'SKIP_INSTALL=NO' \
archive \
-project 'Sample.xcodeproj' \
-scheme 'iOS' \
-destination 'generic/platform=iOS' \
-configuration 'Release' \
-archivePath 'build/Sample-iOS.xcarchive'

プロセッサアーキテクチャを指定する場合

$ xcodebuild \
'ARCHS=arm64 armv7 armv7s' \
'VALID_ARCHS = arm64 arm64e armv7 armv7s' \
'ENABLE_BITCODE=YES' \
'BITCODE_GENERATION_MODE=bitcode' \
'OTHER_CFLAGS=-fembed-bitcode' \
'BUILD_LIBRARY_FOR_DISTRIBUTION=YES' \
'SKIP_INSTALL=NO' \
archive \
-project 'Sample.xcodeproj' \
-scheme 'iOS' \
-destination 'generic/platform=iOS' \
-configuration 'Release' \
-archivePath 'build/Sample-iOS.xcarchive'

STEP 2/2: xcodebuild -create-xcframework

-frameworkにはxcodebuild archiveで生成した全ての*.frameworkのパスを記述します。

$ xcodebuild \
-create-xcframework \
-framework 'build/Sample-iOS.xcarchive/Products/Library/Frameworks/iOS.framework' \
-framework 'build/Sample-iOS-Simulator.xcarchive/Products/Library/Frameworks/iOS.framework' \
-framework 'build/Sample-macOS.xcarchive/Products/Library/Frameworks/macOS.framework' \
...
...
-output 'build/Sample.xcframework'

成果物の確認方法

.xcframeworkファイルが、ビルド設定で指定した通りに生成出来たかの確認方法

cat コマンドでプロパティリストを出力するか、file または lipo -info を用いてバイナリ情報を出力出来ます。

cat の場合:

$ cat ./build/Sample.xcframework/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>AvailableLibraries</key>
	<array>
		<dict>
			<key>LibraryIdentifier</key>
			<string>watchos-armv7k_arm64_32</string>
			<key>LibraryPath</key>
			<string>watchOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>armv7k</string>
				<string>arm64_32</string>
			</array>
			<key>SupportedPlatform</key>
			<string>watchos</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>ios-arm64</string>
			<key>LibraryPath</key>
			<string>iOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>arm64</string>
			</array>
			<key>SupportedPlatform</key>
			<string>ios</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>ios-x86_64-simulator</string>
			<key>LibraryPath</key>
			<string>iOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>x86_64</string>
			</array>
			<key>SupportedPlatform</key>
			<string>ios</string>
			<key>SupportedPlatformVariant</key>
			<string>simulator</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>macos-x86_64</string>
			<key>LibraryPath</key>
			<string>macOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>x86_64</string>
			</array>
			<key>SupportedPlatform</key>
			<string>macos</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>watchos-i386-simulator</string>
			<key>LibraryPath</key>
			<string>watchOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>i386</string>
			</array>
			<key>SupportedPlatform</key>
			<string>watchos</string>
			<key>SupportedPlatformVariant</key>
			<string>simulator</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>tvos-x86_64-simulator</string>
			<key>LibraryPath</key>
			<string>tvOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>x86_64</string>
			</array>
			<key>SupportedPlatform</key>
			<string>tvos</string>
			<key>SupportedPlatformVariant</key>
			<string>simulator</string>
		</dict>
		<dict>
			<key>LibraryIdentifier</key>
			<string>tvos-arm64</string>
			<key>LibraryPath</key>
			<string>tvOS.framework</string>
			<key>SupportedArchitectures</key>
			<array>
				<string>arm64</string>
			</array>
			<key>SupportedPlatform</key>
			<string>tvos</string>
		</dict>
	</array>
	<key>CFBundlePackageType</key>
	<string>XFWK</string>
	<key>XCFrameworkFormatVersion</key>
	<string>1.0</string>
</dict>
</plist>

file の場合:

# 単一のアーキテクチャに対応しているバイナリの場合の出力結果
$ file ./build/Sample.xcframework/ios-arm64/iOS.framework/iOS
./build/Sample.xcframework/ios-arm64/iOS.framework/iOS: Mach-O 64-bit dynamically linked shared library arm64

# 複数のアーキテクチャに対応しているバイナリの場合の出力結果
$ file ./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS
./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS: Mach-O universal binary with 2 architectures: [arm_v7k:Mach-O dynamically linked shared library arm_v7k] [arm64_32_v8:Mach-O dynamically linked shared library arm64_32_v8]
./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS (for architecture armv7k):	Mach-O dynamically linked shared library arm_v7k
./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS (for architecture arm64_32):	Mach-O dynamically linked shared library arm64_32_v8

lipo -info の場合:

# 単一のアーキテクチャに対応しているバイナリの場合の出力結果
$ lipo -info ./build/Sample.xcframework/ios-arm64/iOS.framework/iOS
Non-fat file: ./build/Sample.xcframework/ios-arm64/iOS.framework/iOS is architecture: arm64

# 複数のアーキテクチャに対応しているバイナリの場合の出力結果
$ lipo -info ./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS
Architectures in the fat file: ./build/Sample.xcframework/watchos-armv7k_arm64_32/watchOS.framework/watchOS are: armv7k arm64_32 

また otool コマンドを用いると、バイナリ詳細情報が出力されます。

otool -arch -l の場合:

$ otool -arch arm64 -l ./build/Sample.xcframework/ios-arm64/iOS.framework/iOS 
./build/Sample.xcframework/ios-arm64/iOS.framework/iOS:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           6    16        928 0x00100085
Load command 0
      cmd LC_SEGMENT_64
  cmdsize 232
  segname __TEXT
   vmaddr 0x0000000000000000
   vmsize 0x0000000000008000
  fileoff 0
 filesize 32768
  maxprot 0x00000005
 initprot 0x00000005
   nsects 2
    flags 0x0
Section
  sectname __text
   segname __TEXT
      addr 0x0000000000007fd0
      size 0x0000000000000000
    offset 32720
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x80000400
 reserved1 0
 reserved2 0
Section
  sectname __const
   segname __TEXT
      addr 0x0000000000007fd0
      size 0x0000000000000030
    offset 32720
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Load command 1
      cmd LC_SEGMENT_64
  cmdsize 152
  segname __LLVM
   vmaddr 0x0000000000008000
   vmsize 0x0000000000004000
  fileoff 32768
 filesize 16384
  maxprot 0x00000003
 initprot 0x00000003
   nsects 1
    flags 0x4
Section
  sectname __bundle
   segname __LLVM
      addr 0x0000000000008000
      size 0x00000000000009b3
    offset 32768
     align 2^0 (1)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
Load command 2
      cmd LC_SEGMENT_64
  cmdsize 72
  segname __LINKEDIT
   vmaddr 0x000000000000c000
   vmsize 0x00000000000000a8
  fileoff 49152
 filesize 168
  maxprot 0x00000001
 initprot 0x00000001
   nsects 0
    flags 0x0
Load command 3
          cmd LC_ID_DYLIB
      cmdsize 56
         name @rpath/iOS.framework/iOS (offset 24)
   time stamp 1 Thu Jan  1 09:00:01 1970
      current version 1.0.0
compatibility version 1.0.0
Load command 4
            cmd LC_DYLD_INFO_ONLY
        cmdsize 48
     rebase_off 0
    rebase_size 0
       bind_off 0
      bind_size 0
  weak_bind_off 0
 weak_bind_size 0
  lazy_bind_off 0
 lazy_bind_size 0
     export_off 49152
    export_size 48
Load command 5
     cmd LC_SYMTAB
 cmdsize 24
  symoff 49208
   nsyms 3
  stroff 49256
 strsize 64
Load command 6
            cmd LC_DYSYMTAB
        cmdsize 80
      ilocalsym 0
      nlocalsym 0
     iextdefsym 0
     nextdefsym 2
      iundefsym 2
      nundefsym 1
         tocoff 0
           ntoc 0
      modtaboff 0
        nmodtab 0
   extrefsymoff 0
    nextrefsyms 0
 indirectsymoff 0
  nindirectsyms 0
      extreloff 0
        nextrel 0
      locreloff 0
        nlocrel 0
Load command 7
     cmd LC_UUID
 cmdsize 24
    uuid FF4E63B0-7561-39BA-AABC-86C790D038FD
Load command 8
       cmd LC_BUILD_VERSION
   cmdsize 32
  platform 2
       sdk 13.2
     minos 13.2
    ntools 1
      tool 3
   version 520.0
Load command 9
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 0.0
Load command 10
          cmd LC_ENCRYPTION_INFO_64
      cmdsize 24
     cryptoff 16384
    cryptsize 16384
      cryptid 0
          pad 0
Load command 11
          cmd LC_LOAD_DYLIB
      cmdsize 56
         name /usr/lib/libSystem.B.dylib (offset 24)
   time stamp 2 Thu Jan  1 09:00:02 1970
      current version 1281.0.0
compatibility version 1.0.0
Load command 12
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/Frameworks (offset 12)
Load command 13
          cmd LC_RPATH
      cmdsize 40
         path @loader_path/Frameworks (offset 12)
Load command 14
      cmd LC_FUNCTION_STARTS
  cmdsize 16
  dataoff 49200
 datasize 8
Load command 15
      cmd LC_DATA_IN_CODE
  cmdsize 16
  dataoff 49208
 datasize 0

otool -arch が対応しているフラグ一覧:

any little big ppc64 x86_64 x86_64h arm64 ppc970-64 arm64_32 arm64e ppc i386 m68k hppa sparc m88k i860 veo arm ppc601 ppc603 ppc603e ppc603ev ppc604 ppc604e ppc750 ppc7400 ppc7450 ppc970 i486 i486SX pentium i586 pentpro i686 pentIIm3 pentIIm5 pentium4 m68030 m68040 hppa7100LC veo1 veo2 veo3 veo4 armv4t armv5 xscale armv6 armv6m armv7 armv7f armv7s armv7k armv7m armv7em arm64v8

Debug symbol (dSYM)

dSYMファイルとは:
アプリクラッシュ時に出力されるスタックトレースを復元する場合に必要な、シンボルファイルです。
復元される主な情報は、作成したframeworkのクラス名・メソッド名です。
必要に応じて、XCFrameworkと同様に配布するのがいいかと思います。

frameworkのdSYMファイルはどこに生成されるのか?

dSYMファイルは xcodebuild archive で生成した *.xcarchive ファイルの配下 (*.xcarchive/dSYMs/*.framework.dSYM) に保存されます。

Bitcode Symbol Maps (BCSymbolMaps)

BCSymbolMapsファイルとは:
ビットコードを有効にしたときに生成される、ビルド時の設定値が記述されたテキストファイルです。

frameworkのBCSymbolMapsファイルはどこに生成されるのか?

BCSymbolMapsファイルは xcodebuild archive で生成した *.xcarchive ファイルの配下 (*.xcarchive/BCSymbolMaps/*.bcsymbolmap) に保存されます。

XCFrameworkを配布する

CarthageやSwift PMなどで配布したい場合は、関連するソフトウェアの対応状況の各種リンクを参照してください。

CocoaPodsで配布する

XCFrameworkをCocoaPodsで配布するための手順と、追加で指定するパラメータについて。

① 生成した*.xcframeworkファイルをzipなどにアーカイブする。
② HTTP(S)で、zipファイルを配布します。
AWS S3や自社HTTPサーバー、GitHub Releasesなどでzipを配布するのが一般的だと思います。
sourcehttp を選択し、zipファイルのURLを指定する。

Using HTTP to download a compressed file of the code. It supports zip, tgz, bz2, txz and tar.
spec.source = { :http => 'http://dev.wechatapp.com/download/sdk/WeChat_SDK_iOS_en.zip'

vendored_frameworks や ios.vendored_frameworks などを指定する
platformdeployment_target を指定する
cocoapods_version>= 1.9.0 を指定する
swift_versions と swift_version を指定する

その他

参考にした資料

お手軽にXCFramework対応したい場合に良さそうなソフトウェア

関連するソフトウェアの対応状況

執筆時の環境

  • macOS: 10.14.5
  • Xcode: 11.3.1

今回作成したプロジェクトとスクリプト

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
40