如何修复我的构建:具有 CocoaPods 依赖项和 2 个目标(iOS 和 tvOS)的 .framework 文件
How to fix my build: .framework file with CocoaPods dependencies and 2 targets (iOS and tvOS)
我有一个需求稍微复杂的项目,所以让我们从期望的目标开始:
- 该项目是一个 SDK,用于连接我们的后端服务(不是应用程序)
- 该项目必须输出一个
.framework
可以包含在 Xamarin 应用程序中的文件(我们所有的内部应用程序都是用 Xamarin 构建的)
- 该项目必须是具有
.podspec
文件的适当 CocoaPod,以便第三方可以将 SDK 包含在他们自己的应用程序中(客户请求)
- 该项目依赖三个第三方依赖项,其中一个与其他依赖项的交付方式不同:
- CocoaLumberjack 和 JSONModel,当在
Podfile
或 .podspec
中作为依赖项列出时,将下载它们各自的项目并将它们作为子项目包含在您自己的工作区中,待构建
- GoogleInterativeMediaAds,当在
Podfile
或 .podspec
中被列为依赖项时,将下载 .framework
文件并且是完全闭源的
- 项目必须能够为 iOS 和 构建tvOS(我们有两者的应用程序和客户端)
- 请注意,虽然 JSONModel 和 CocoaLumberjack 都宣传他们有 iOS 和 tvOS 支持,但有 separate CocoaPods 用于GoogleInteractiveMediaAds(
GoogleAds-IMA-iOS-SDK ~> 3.9
和 GoogleAds-IMA-tvOS-SDK ~> 4.0
)
- 项目输出的任何
.framework
文件必须同时包含嵌入式位码 和 是胖二进制文件
- 该项目有几个静态资产(PNG 文件和 XIB 文件)需要作为
.bundle
包含在 .framework
中
现在,然后:我已经完成了所有这些工作。这花了很长时间并进行了大量试验,但我有一个满足上述所有要求的项目。我的 Podfile 看起来像这样:
播客文件
use_frameworks!
def common()
pod 'CocoaLumberjack', '~> 3.5'
pod 'JSONModel', '~> 1.8'
end
target 'MyFramework-iOS' do
platform :ios, '9.0'
common()
pod 'GoogleAds-IMA-iOS-SDK', '~> 3.9'
end
target 'MyFramework-tvOS' do
platform :tvos, '9.1'
common()
pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.0'
end
我的 .podspec
看起来像这样:
MyFramework.podspec
Pod::Spec.new do |spec|
spec.name = "MyFramework"
spec.version = "1.0.0"
spec.summary = "A framework for interfacing with our back-end"
spec.description = <<-DESC
A longer description
DESC
spec.homepage = "https://example.com/"
spec.license = "MIT"
spec.authors = {
"Steven Barnett" => "xxx@example.com"
}
spec.ios.deployment_target = "9.0"
spec.tvos.deployment_target = "9.1"
spec.source = { :git => "https://bitbucket.org/xxx/MyFramework.git", :tag => "#{spec.version}" }
spec.source_files = "Sources/Common/**/*.{h,m}", "Sources/MyFramework.h"
spec.ios.source_files = "Sources/iOS/**/*.{h,m}"
spec.tvos.source_files = "Sources/tvOS/**/*.{h,m}"
spec.ios.resource_bundles = {
"MyFramework" => ["Assets.xcassets", "Sources/iOS/**/*.{xib}"]
}
spec.tvos.resource_bundles = {
"MyFramework" => ["Assets.xcassets", "Sources/tvOS/**/*.{xib}"]
}
spec.frameworks = "Foundation", "UIKit", "AVKit"
spec.requires_arc = true
spec.dependency "CocoaLumberjack", "~> 3.5"
spec.dependency "JSONModel", "~> 1.8"
spec.ios.dependency "GoogleAds-IMA-iOS-SDK", "~> 3.9"
spec.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "~> 4.0"
end
我的源代码分为三个文件夹:
./Sources/Common
包含我们 99% 的代码,在 iOS 和 tvOS 中很常见
./Sources/iOS
包含一些仅在 iOS 上可用的功能。具体来说,iOSg运行在自定义视频播放器的布局方面更加灵活,所以我们有自定义控件
./Sources/tvOS
包含一些仅在 tvOS 上可用的功能。具体来说,XIB 和 .m
播放器控件文件的配对集合
在我项目的构建阶段中,我使用以下脚本添加了一个 Run Script
阶段:
运行 脚本
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]; then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step "0". Build our project twice
echo "Building for AppleTV Device (redundantly... sigh)"
xcodebuild -workspace "${PROJECT_DIR}/${PROJECT}.xcworkspace" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk appletvos ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" OBJROOT="${OBJROOT}/DependentBuilds" OTHER_CFLAGS="-fembed-bitcode"
echo "Building for AppleTV Simulator"
xcodebuild -workspace "${PROJECT_DIR}/${PROJECT}.xcworkspace" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk appletvsimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" OBJROOT="${OBJROOT}/DependentBuilds" OTHER_CFLAGS="-fembed-bitcode"
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder
echo "Copying to output folder"
cp -R "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 2. Create universal binary file using lipo and place the combined executable in the copied framework directory
echo "Combining executables"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-appletvsimulator/${EXECUTABLE_PATH}" "${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
# Step 3. Create universal binaries for embedded frameworks
for FRAMEWORK in $( find "${TARGET_BUILD_DIR}" -type d -name "*.framework" ); do
FRAMEWORK_PATH="${FRAMEWORK##$TARGET_BUILD_DIR}"
BINARY_NAME="${FRAMEWORK##*/}"
BINARY_NAME="${BINARY_NAME%.*}"
# I don't know how to do "if not" in Bash. Sue me.
if [[ $BINARY_NAME == Pods_* ]] || [[ $BINARY_NAME == $TARGET_NAME ]]; then
echo "Skipping framework: $FRAMEWORK_PATH"
else
echo "Creating fat binary for framework: $FRAMEWORK_PATH"
cp -r "${FRAMEWORK}" "${UNIVERSAL_OUTPUTFOLDER}/"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${BINARY_NAME}.framework/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-appletvsimulator/${FRAMEWORK_PATH}/${BINARY_NAME}" "${TARGET_BUILD_DIR}/${FRAMEWORK_PATH}/${BINARY_NAME}"
fi
done
# Step 4. Manually copy some files to the output dir
cp -r "${SOURCE_ROOT}/Pods/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework" "${UNIVERSAL_OUTPUTFOLDER}"
cp "${SOURCE_ROOT}/generate-xamarin-bindings.py" "${UNIVERSAL_OUTPUTFOLDER}"
# Step 5. Convenience step
open "${UNIVERSAL_OUTPUTFOLDER}"
fi
此脚本是我在网上找到的类似脚本的修改版。它基本上执行以下操作:
- 为嵌入了位码的设备(ARM 处理器)构建我的项目
- 为嵌入了位码的模拟器(x86 处理器)构建我的项目
- 使用 lipo 为我的项目创建一个 fat 二进制文件
- 迭代使用 lipo 为我依赖的所有框架创建胖二进制文件
- 将我的框架和我依赖的所有框架复制到 "universal" 输出目录
您可能还会注意到 generate-xamarin-bindings.py
文件。出于我的目的,这几乎可以忽略。它只是使用 Objective Sharpie 来自动创建 Xamarin 绑定,然后使用 JSONModel 修复一些已知错误(定义为 @property (retain, nonatomic, nullable) NSString <Optional> *test;
的变量将作为 Optional test { get; set; }
而不是 string test { get; set; }
)
除此之外,我不记得必须对所有脚本、构建设置和配置文件进行哪些额外的配置和调整 - 但如果出现任何问题,我可以查看代码并回答它们。
我有一个使用 CocoaPods 拉入我的 SDK 的测试项目,如下所示:
Examples/tvOS/Podfile
use_frameworks!
platform :tvos, '9.1'
target 'MyFramework Example tvOS App' do
pod 'MyFramework', :path => '../../'
end
此测试项目构建完美,我可以 运行 在模拟器或真实设备上构建它。几周前,我还能够将我的测试项目部署到 Apple Store (TestFlight),但是从那以后我做了一些更改(包括从标准构建切换到胖二进制文件),所以我不确定它是否正确仍然有效(我会测试这个)
此外,当我们构建分析和创建 Xamarin 绑定时,我们可以嵌入所有四个框架(MyFramework.framework
、CocoaLumberjack.framework
、JSONModel.framework
和 GoogleInteractiveMediaAds.framework
)进入我们的 Xamarin 应用程序,它们将在模拟器和真实设备上构建和 运行。
我们 运行 遇到的问题是当我们试图将其中一个基于 Xamarin 的应用程序推送到 Apple Store (TestFlight) 时。我们收到 11 个错误:
- ERROR ITMS-90166: "Missing Code Signing Entitlements. No entitlements found in bundle 'com.xxx.MyFramework' for executable 'Payload/InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework.bundle/MyFramework'."
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework-tvOS' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/CocoaLumberjack.framework/CocoaLumberjack' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/JSONModel.framework/JSONModel' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/CocoaLumberjack.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/MyFramework-tvOS.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/JSONModel.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90171: "Invalid Bundle Structure - The binary file 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework.bundle/MyFramework' is not permitted. Your app can't contain standalone executables or libraries, other than a valid CFBundleExecutable of supported bundles. Refer to the Bundle Programming Guide at https://developer.apple.com/go/?id=bundle-structure for information on the tvOS app bundle structure."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework-tvOS' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/CocoaLumberjack.framework/CocoaLumberjack' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/JSONModel.framework/JSONModel' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
所以我开始尝试手动跟踪构建过程以找出哪里出了问题或出了什么问题。我已经在使用最新的 Xcode,并且我们已经尝试了 Product > Archive
和 Product > Build For > Profiling
,所以我不认为 ITMS-90668 的建议解决方案 或 ITMS-90209 是准确的。
在手动查看构建过程时,我注意到一些东西是 st运行ge:
- 当我构建我的应用程序的 tvOS 版本时,在
~/Library/Developer/Xcode/DerivedData/MyFramework-xxxxxx/Build/Products
(此后:${BUILD_ROOT}
)iOS 和 正在构建我的依赖项的 tvOS 版本:
${BUILD_ROOT}/Release-appletvos/JSONModel-iOS.framework
${BUILD_ROOT}/Release-appletvos/JSONModel-tvOS.framework
${BUILD_ROOT}/Release-appletvsimulator/JSONModel-iOS.framework
${BUILD_ROOT}/Release-appletvsimulator/JSONModel-tvOS.framework
- 等(与 CocoaLumberjack 相同)
- 果然,如果我查看
${BUILD_ROOT}/Release-universal/MyFramework-tvOS.framework/MyFramework.bundle
,里面有一个名为 MyFramework
的可执行文件。我 no 知道是谁或什么在创建这个可执行文件,它来自哪里,或者为什么它被包含在我的包中。在任何构建阶段中都没有 引用它,但这似乎是 ITMS-90171[ 的原因=240=]
GoogleInteractiveMediaAds 框架是唯一没有启用位码的框架,这让我怀疑位码不是 向商店提交应用程序所必需的,因为它也是唯一没有抛出任何错误的框架(尽管我之前被告知您需要包含位码)
我正在尝试做的事情听起来并不难。我只是在创建一个库(如 CocoaLumberjack),它可以是 运行 在 iOS 或 tvOS(如 CocoaLumberjack)上,但它具有依赖性(不像 CocoaLumberjack)和它可以作为 .framework
文件而不是仅仅作为 CocoaPod 提供(不确定 CocoaLumberjack 是否有这个?)。但是我找不到我遇到的问题的任何答案或一些奇怪的构建行为的任何解释。
具体来说:
- 为什么我的
.bundle
中出现一个可执行文件?
- 当我尝试构建我的 tvOS 版本时,为什么它会构建依赖框架的 iOS 版本?
我需要位码吗? 或者我应该禁用它吗?
更新
- Google 互动媒体广告框架 有位码,只是没有
__LLVM
段
- 从我的
.bundle
中删除二进制文件将 ITMS-90171 替换为一个关于 Info.plist 引用不存在的 CFBundleExecutable 的新错误(我忘记了 ITMS-X 代码)。删除此二进制文件 也 处理了 ITMS-90166
- 通过转到资源包的目标并选择 "Info" 我可以删除 "Executable file" 键以删除输出 Info.plist 中的这一行,但是它没有修复正在运行的可执行文件首先创建。当手动删除二进制文件时,它确实防止了新错误
因此,通过修改 Info.plist 并删除该可执行文件(目前手动删除,因为我不知道是什么生成了它),11 个错误中的 2 个消失了,留下 3 个错误的 3 个实例(总共共 9 个):
- CocoaLumberjack(ITMS-90668、ITMS-90635 和 ITMS-90209)
- JSONModel(ITMS-90668、ITMS-90635 和 ITMS-90209)
- MyFramework(ITMS-90668、ITMS-90635 和 ITMS-90209)
更新 2
- 将
BITCODE_GENERATION_MODE=bitcode
标志添加到我的 xcodebuild 命令中 似乎 可以改善一些事情。我的输出二进制文件从 1.2 MB 变为 3.4 MB(大概包含 "complete" 位码)。然而奇怪的事情发生了。 ITMS-90668 的三个实例并没有消失,所有三个错误(ITMS-90668、ITMS-90635 和 ITMS-90209)都消失了 对于单个框架:JSONModel
我现在看到 6 个错误(CocoaLumberjack 和 MyFramework 各抛出 3 个错误)。但我不明白 JSONModel 是如何或为什么被修复的,而其他问题却没有
另一个附录:当我添加位码生成模式标志时,我的包中的可执行文件开始出现链接器错误(它不应该存在,我仍然不知道是什么在创建它)。我注意到 OTHER_CFLAGS
被传递给 clang 的每次调用,而不仅仅是相关的调用,所以我删除了 OTHER_CFLAGS="-fembed-bitcode"
并修复了链接器错误
一个最后的附录:我注意到当我不使用我的运行脚本来生成一个胖二进制文件时,只有*-tvOS
版本我的依赖关系已经建立。我的 运行 脚本正在构建 *-iOS
版本。这似乎对我在 Apple Store 看到的错误没有影响,但它会减慢我的构建速度,创建额外的文件,并发出一些关于隐式依赖检测发现两个候选者的警告。我不知道如何解决这个问题。
更新 3
我已经解决了一个问题!
因此出现在我的包中的 st运行ge 二进制文件是由于 VERSIONING_SYSTEM
构建设置默认为 apple-generic
。 Apple Generic Versioning 系统创建一个源文件调用 <ProjectName>_vers.c
,对其进行编译,然后将此文件链接到某些库(我猜是系统库?)
这是 this 文件,当我有 OTHER_CFLAGS="-fembed-bitcode"
时导致我的链接器错误,因为这个二进制文件正在设置 -bundle
这是不兼容的。通过将版本控制系统设置为 None
我能够在没有任何链接器错误的情况下恢复此标志!
我刚刚编译并尝试再次推送到 Apple store 这次一切正常!
总结
- 为了修复 ITMS-90166 和 ITMS-90171,我必须将任何资源包目标的
Versioning System
设置为 None
- 修复此问题后,您必须确保资源包的 info.plist 不 包含
Executable File
键(如果存在则将其删除)
- 为了修复 ITMS-90668、ITMS-90635 和 ITMS-90209,我必须将标志
BITCODE_GENERATION_MODE=bitcode
和 ENABLE_BITCODE=YES
添加到我的 xcodebuild
命令(连同现有的 OTHER_CFLAGS="-fembed-bitcode"
)
在所有这一切之后,我的构建系统仍然有一个奇怪的怪癖,但它只会抛出警告(而不是错误),我可以短期忍受它。这是事实,当我 运行 xcodebuild 而不是仅仅构建 tvOS 版本时,它正在构建我的依赖项的 iOS 版本。
我不知道这个问题/答案对其他偶然发现它的人有多大用处,因为我有很多问题和非常具体的设置。但我想继续 post 我 确实 找到了我的问题的解决方案。
首先,我需要设置 ENABLE_BITCODE=YES
和 BITCODE_GENERATION_MODE=bitcode
标志。但是,设置这两个标志可防止我的代码因链接器错误而构建。此错误是因为我的资源包目标使用的是 Apple 通用版本控制 -- 资源包不支持
通过将 Versioning System
构建设置设置为 "None",它修复了所有问题
我有一个需求稍微复杂的项目,所以让我们从期望的目标开始:
- 该项目是一个 SDK,用于连接我们的后端服务(不是应用程序)
- 该项目必须输出一个
.framework
可以包含在 Xamarin 应用程序中的文件(我们所有的内部应用程序都是用 Xamarin 构建的) - 该项目必须是具有
.podspec
文件的适当 CocoaPod,以便第三方可以将 SDK 包含在他们自己的应用程序中(客户请求) - 该项目依赖三个第三方依赖项,其中一个与其他依赖项的交付方式不同:
- CocoaLumberjack 和 JSONModel,当在
Podfile
或.podspec
中作为依赖项列出时,将下载它们各自的项目并将它们作为子项目包含在您自己的工作区中,待构建 - GoogleInterativeMediaAds,当在
Podfile
或.podspec
中被列为依赖项时,将下载.framework
文件并且是完全闭源的
- CocoaLumberjack 和 JSONModel,当在
- 项目必须能够为 iOS 和 构建tvOS(我们有两者的应用程序和客户端)
- 请注意,虽然 JSONModel 和 CocoaLumberjack 都宣传他们有 iOS 和 tvOS 支持,但有 separate CocoaPods 用于GoogleInteractiveMediaAds(
GoogleAds-IMA-iOS-SDK ~> 3.9
和GoogleAds-IMA-tvOS-SDK ~> 4.0
)
- 请注意,虽然 JSONModel 和 CocoaLumberjack 都宣传他们有 iOS 和 tvOS 支持,但有 separate CocoaPods 用于GoogleInteractiveMediaAds(
- 项目输出的任何
.framework
文件必须同时包含嵌入式位码 和 是胖二进制文件 - 该项目有几个静态资产(PNG 文件和 XIB 文件)需要作为
.bundle
包含在.framework
中
现在,然后:我已经完成了所有这些工作。这花了很长时间并进行了大量试验,但我有一个满足上述所有要求的项目。我的 Podfile 看起来像这样:
播客文件
use_frameworks!
def common()
pod 'CocoaLumberjack', '~> 3.5'
pod 'JSONModel', '~> 1.8'
end
target 'MyFramework-iOS' do
platform :ios, '9.0'
common()
pod 'GoogleAds-IMA-iOS-SDK', '~> 3.9'
end
target 'MyFramework-tvOS' do
platform :tvos, '9.1'
common()
pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.0'
end
我的 .podspec
看起来像这样:
MyFramework.podspec
Pod::Spec.new do |spec|
spec.name = "MyFramework"
spec.version = "1.0.0"
spec.summary = "A framework for interfacing with our back-end"
spec.description = <<-DESC
A longer description
DESC
spec.homepage = "https://example.com/"
spec.license = "MIT"
spec.authors = {
"Steven Barnett" => "xxx@example.com"
}
spec.ios.deployment_target = "9.0"
spec.tvos.deployment_target = "9.1"
spec.source = { :git => "https://bitbucket.org/xxx/MyFramework.git", :tag => "#{spec.version}" }
spec.source_files = "Sources/Common/**/*.{h,m}", "Sources/MyFramework.h"
spec.ios.source_files = "Sources/iOS/**/*.{h,m}"
spec.tvos.source_files = "Sources/tvOS/**/*.{h,m}"
spec.ios.resource_bundles = {
"MyFramework" => ["Assets.xcassets", "Sources/iOS/**/*.{xib}"]
}
spec.tvos.resource_bundles = {
"MyFramework" => ["Assets.xcassets", "Sources/tvOS/**/*.{xib}"]
}
spec.frameworks = "Foundation", "UIKit", "AVKit"
spec.requires_arc = true
spec.dependency "CocoaLumberjack", "~> 3.5"
spec.dependency "JSONModel", "~> 1.8"
spec.ios.dependency "GoogleAds-IMA-iOS-SDK", "~> 3.9"
spec.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "~> 4.0"
end
我的源代码分为三个文件夹:
./Sources/Common
包含我们 99% 的代码,在 iOS 和 tvOS 中很常见
./Sources/iOS
包含一些仅在 iOS 上可用的功能。具体来说,iOSg运行在自定义视频播放器的布局方面更加灵活,所以我们有自定义控件./Sources/tvOS
包含一些仅在 tvOS 上可用的功能。具体来说,XIB 和.m
播放器控件文件的配对集合
在我项目的构建阶段中,我使用以下脚本添加了一个 Run Script
阶段:
运行 脚本
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
if [ "true" == ${ALREADYINVOKED:-false} ]; then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step "0". Build our project twice
echo "Building for AppleTV Device (redundantly... sigh)"
xcodebuild -workspace "${PROJECT_DIR}/${PROJECT}.xcworkspace" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk appletvos ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" OBJROOT="${OBJROOT}/DependentBuilds" OTHER_CFLAGS="-fembed-bitcode"
echo "Building for AppleTV Simulator"
xcodebuild -workspace "${PROJECT_DIR}/${PROJECT}.xcworkspace" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk appletvsimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" OBJROOT="${OBJROOT}/DependentBuilds" OTHER_CFLAGS="-fembed-bitcode"
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder
echo "Copying to output folder"
cp -R "${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 2. Create universal binary file using lipo and place the combined executable in the copied framework directory
echo "Combining executables"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-appletvsimulator/${EXECUTABLE_PATH}" "${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
# Step 3. Create universal binaries for embedded frameworks
for FRAMEWORK in $( find "${TARGET_BUILD_DIR}" -type d -name "*.framework" ); do
FRAMEWORK_PATH="${FRAMEWORK##$TARGET_BUILD_DIR}"
BINARY_NAME="${FRAMEWORK##*/}"
BINARY_NAME="${BINARY_NAME%.*}"
# I don't know how to do "if not" in Bash. Sue me.
if [[ $BINARY_NAME == Pods_* ]] || [[ $BINARY_NAME == $TARGET_NAME ]]; then
echo "Skipping framework: $FRAMEWORK_PATH"
else
echo "Creating fat binary for framework: $FRAMEWORK_PATH"
cp -r "${FRAMEWORK}" "${UNIVERSAL_OUTPUTFOLDER}/"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${BINARY_NAME}.framework/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-appletvsimulator/${FRAMEWORK_PATH}/${BINARY_NAME}" "${TARGET_BUILD_DIR}/${FRAMEWORK_PATH}/${BINARY_NAME}"
fi
done
# Step 4. Manually copy some files to the output dir
cp -r "${SOURCE_ROOT}/Pods/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework" "${UNIVERSAL_OUTPUTFOLDER}"
cp "${SOURCE_ROOT}/generate-xamarin-bindings.py" "${UNIVERSAL_OUTPUTFOLDER}"
# Step 5. Convenience step
open "${UNIVERSAL_OUTPUTFOLDER}"
fi
此脚本是我在网上找到的类似脚本的修改版。它基本上执行以下操作:
- 为嵌入了位码的设备(ARM 处理器)构建我的项目
- 为嵌入了位码的模拟器(x86 处理器)构建我的项目
- 使用 lipo 为我的项目创建一个 fat 二进制文件
- 迭代使用 lipo 为我依赖的所有框架创建胖二进制文件
- 将我的框架和我依赖的所有框架复制到 "universal" 输出目录
您可能还会注意到 generate-xamarin-bindings.py
文件。出于我的目的,这几乎可以忽略。它只是使用 Objective Sharpie 来自动创建 Xamarin 绑定,然后使用 JSONModel 修复一些已知错误(定义为 @property (retain, nonatomic, nullable) NSString <Optional> *test;
的变量将作为 Optional test { get; set; }
而不是 string test { get; set; }
)
除此之外,我不记得必须对所有脚本、构建设置和配置文件进行哪些额外的配置和调整 - 但如果出现任何问题,我可以查看代码并回答它们。
我有一个使用 CocoaPods 拉入我的 SDK 的测试项目,如下所示:
Examples/tvOS/Podfile
use_frameworks!
platform :tvos, '9.1'
target 'MyFramework Example tvOS App' do
pod 'MyFramework', :path => '../../'
end
此测试项目构建完美,我可以 运行 在模拟器或真实设备上构建它。几周前,我还能够将我的测试项目部署到 Apple Store (TestFlight),但是从那以后我做了一些更改(包括从标准构建切换到胖二进制文件),所以我不确定它是否正确仍然有效(我会测试这个)
此外,当我们构建分析和创建 Xamarin 绑定时,我们可以嵌入所有四个框架(MyFramework.framework
、CocoaLumberjack.framework
、JSONModel.framework
和 GoogleInteractiveMediaAds.framework
)进入我们的 Xamarin 应用程序,它们将在模拟器和真实设备上构建和 运行。
我们 运行 遇到的问题是当我们试图将其中一个基于 Xamarin 的应用程序推送到 Apple Store (TestFlight) 时。我们收到 11 个错误:
- ERROR ITMS-90166: "Missing Code Signing Entitlements. No entitlements found in bundle 'com.xxx.MyFramework' for executable 'Payload/InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework.bundle/MyFramework'."
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework-tvOS' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/CocoaLumberjack.framework/CocoaLumberjack' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90668: "Invalid Bundle Executable. The executable file 'InternalApp.app/Frameworks/JSONModel.framework/JSONModel' contains incomplete bitcode. To compile binaries with complete bitcode, open Xcode and choose Archive in the Product menu"
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/CocoaLumberjack.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/MyFramework-tvOS.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90635: "Invalid Mach-O Format. The Mach-O in bundle "InternalApp.app/Frameworks/JSONModel.framework" isn't consistent with the Mach-O in the main bundle. The main bundle Mach-O contains arm64(bitcode), while the nested bundle Mach-O contains arm64(machine code). Verify that all of the targets for a platform have a consistent value for the ENABLE_BITCODE build setting."
- ERROR ITMS-90171: "Invalid Bundle Structure - The binary file 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework.bundle/MyFramework' is not permitted. Your app can't contain standalone executables or libraries, other than a valid CFBundleExecutable of supported bundles. Refer to the Bundle Programming Guide at https://developer.apple.com/go/?id=bundle-structure for information on the tvOS app bundle structure."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/MyFramework-tvOS.framework/MyFramework-tvOS' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/CocoaLumberjack.framework/CocoaLumberjack' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
- ERROR ITMS-90209: "Invalid Segment Alignment. The app binary at 'InternalApp.app/Frameworks/JSONModel.framework/JSONModel' does not have proper segment alignment. Try rebuilding the app with the latest Xcode version."
所以我开始尝试手动跟踪构建过程以找出哪里出了问题或出了什么问题。我已经在使用最新的 Xcode,并且我们已经尝试了 Product > Archive
和 Product > Build For > Profiling
,所以我不认为 ITMS-90668 的建议解决方案 或 ITMS-90209 是准确的。
在手动查看构建过程时,我注意到一些东西是 st运行ge:
- 当我构建我的应用程序的 tvOS 版本时,在
~/Library/Developer/Xcode/DerivedData/MyFramework-xxxxxx/Build/Products
(此后:${BUILD_ROOT}
)iOS 和 正在构建我的依赖项的 tvOS 版本:${BUILD_ROOT}/Release-appletvos/JSONModel-iOS.framework
${BUILD_ROOT}/Release-appletvos/JSONModel-tvOS.framework
${BUILD_ROOT}/Release-appletvsimulator/JSONModel-iOS.framework
${BUILD_ROOT}/Release-appletvsimulator/JSONModel-tvOS.framework
- 等(与 CocoaLumberjack 相同)
- 果然,如果我查看
${BUILD_ROOT}/Release-universal/MyFramework-tvOS.framework/MyFramework.bundle
,里面有一个名为MyFramework
的可执行文件。我 no 知道是谁或什么在创建这个可执行文件,它来自哪里,或者为什么它被包含在我的包中。在任何构建阶段中都没有 引用它,但这似乎是 ITMS-90171[ 的原因=240=] GoogleInteractiveMediaAds 框架是唯一没有启用位码的框架,这让我怀疑位码不是 向商店提交应用程序所必需的,因为它也是唯一没有抛出任何错误的框架(尽管我之前被告知您需要包含位码)
我正在尝试做的事情听起来并不难。我只是在创建一个库(如 CocoaLumberjack),它可以是 运行 在 iOS 或 tvOS(如 CocoaLumberjack)上,但它具有依赖性(不像 CocoaLumberjack)和它可以作为 .framework
文件而不是仅仅作为 CocoaPod 提供(不确定 CocoaLumberjack 是否有这个?)。但是我找不到我遇到的问题的任何答案或一些奇怪的构建行为的任何解释。
具体来说:
- 为什么我的
.bundle
中出现一个可执行文件? - 当我尝试构建我的 tvOS 版本时,为什么它会构建依赖框架的 iOS 版本?
我需要位码吗? 或者我应该禁用它吗?
更新
- Google 互动媒体广告框架 有位码,只是没有
__LLVM
段 - 从我的
.bundle
中删除二进制文件将 ITMS-90171 替换为一个关于 Info.plist 引用不存在的 CFBundleExecutable 的新错误(我忘记了 ITMS-X 代码)。删除此二进制文件 也 处理了 ITMS-90166 - 通过转到资源包的目标并选择 "Info" 我可以删除 "Executable file" 键以删除输出 Info.plist 中的这一行,但是它没有修复正在运行的可执行文件首先创建。当手动删除二进制文件时,它确实防止了新错误
因此,通过修改 Info.plist 并删除该可执行文件(目前手动删除,因为我不知道是什么生成了它),11 个错误中的 2 个消失了,留下 3 个错误的 3 个实例(总共共 9 个):
- CocoaLumberjack(ITMS-90668、ITMS-90635 和 ITMS-90209)
- JSONModel(ITMS-90668、ITMS-90635 和 ITMS-90209)
- MyFramework(ITMS-90668、ITMS-90635 和 ITMS-90209)
更新 2
- 将
BITCODE_GENERATION_MODE=bitcode
标志添加到我的 xcodebuild 命令中 似乎 可以改善一些事情。我的输出二进制文件从 1.2 MB 变为 3.4 MB(大概包含 "complete" 位码)。然而奇怪的事情发生了。 ITMS-90668 的三个实例并没有消失,所有三个错误(ITMS-90668、ITMS-90635 和 ITMS-90209)都消失了 对于单个框架:JSONModel
我现在看到 6 个错误(CocoaLumberjack 和 MyFramework 各抛出 3 个错误)。但我不明白 JSONModel 是如何或为什么被修复的,而其他问题却没有
另一个附录:当我添加位码生成模式标志时,我的包中的可执行文件开始出现链接器错误(它不应该存在,我仍然不知道是什么在创建它)。我注意到 OTHER_CFLAGS
被传递给 clang 的每次调用,而不仅仅是相关的调用,所以我删除了 OTHER_CFLAGS="-fembed-bitcode"
并修复了链接器错误
一个最后的附录:我注意到当我不使用我的运行脚本来生成一个胖二进制文件时,只有*-tvOS
版本我的依赖关系已经建立。我的 运行 脚本正在构建 *-iOS
版本。这似乎对我在 Apple Store 看到的错误没有影响,但它会减慢我的构建速度,创建额外的文件,并发出一些关于隐式依赖检测发现两个候选者的警告。我不知道如何解决这个问题。
更新 3
我已经解决了一个问题!
因此出现在我的包中的 st运行ge 二进制文件是由于 VERSIONING_SYSTEM
构建设置默认为 apple-generic
。 Apple Generic Versioning 系统创建一个源文件调用 <ProjectName>_vers.c
,对其进行编译,然后将此文件链接到某些库(我猜是系统库?)
这是 this 文件,当我有 OTHER_CFLAGS="-fembed-bitcode"
时导致我的链接器错误,因为这个二进制文件正在设置 -bundle
这是不兼容的。通过将版本控制系统设置为 None
我能够在没有任何链接器错误的情况下恢复此标志!
我刚刚编译并尝试再次推送到 Apple store 这次一切正常!
总结
- 为了修复 ITMS-90166 和 ITMS-90171,我必须将任何资源包目标的
Versioning System
设置为None
- 修复此问题后,您必须确保资源包的 info.plist 不 包含
Executable File
键(如果存在则将其删除) - 为了修复 ITMS-90668、ITMS-90635 和 ITMS-90209,我必须将标志
BITCODE_GENERATION_MODE=bitcode
和ENABLE_BITCODE=YES
添加到我的xcodebuild
命令(连同现有的OTHER_CFLAGS="-fembed-bitcode"
)
在所有这一切之后,我的构建系统仍然有一个奇怪的怪癖,但它只会抛出警告(而不是错误),我可以短期忍受它。这是事实,当我 运行 xcodebuild 而不是仅仅构建 tvOS 版本时,它正在构建我的依赖项的 iOS 版本。
我不知道这个问题/答案对其他偶然发现它的人有多大用处,因为我有很多问题和非常具体的设置。但我想继续 post 我 确实 找到了我的问题的解决方案。
首先,我需要设置 ENABLE_BITCODE=YES
和 BITCODE_GENERATION_MODE=bitcode
标志。但是,设置这两个标志可防止我的代码因链接器错误而构建。此错误是因为我的资源包目标使用的是 Apple 通用版本控制 -- 资源包不支持
通过将 Versioning System
构建设置设置为 "None",它修复了所有问题