【问题标题】:How to build cocoa touch framework for all architectures dependent on other frameworks added using cocoapods?如何为依赖于使用 cocoapods 添加的其他框架的所有架构构建可可触摸框架?
【发布时间】:2016-12-04 21:19:45
【问题描述】:

我想创建一个为模拟器和设备构建的输出框架的私有 cocoapod。

我创建了一个 Cocoa 触摸框架,在其中我使用 Cocoapods 集成了 CocoaLumberjack。 我想为所有可能的架构(模拟器和设备)构建框架。

默认情况下构建设置,

 'Build Active Architectures Only' is set to (Debug - Yes, Release - No).

只要我将此调试设置设置为否,构建就会失败并出现以下链接器错误:

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_DDASLLogger", referenced from:
  objc-class-ref in MyManager.o
"_OBJC_CLASS_$_DDLog", referenced from:
  objc-class-ref in MyManager.o
"_OBJC_CLASS_$_DDTTYLogger", referenced from:
  objc-class-ref in MyManager.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我知道 CocoaLumberjack 仅适用于调试版本中的活动架构。

所以我将 Build active architectures for debug 切换回 Yes 并成功构建了框架。

为了为所有架构构建框架,我使用了在构建阶段添加的运行脚本,该脚本还声称将 ios-device 和 ios-simulator 构建合并为一个。这是脚本:

    set -e
    set +u
    # Avoid recursively calling this script.
    if [[ $SF_MASTER_SCRIPT_RUNNING ]]
    then
    exit 0
    fi
    set -u
    export SF_MASTER_SCRIPT_RUNNING=1


    # Constants
    SF_TARGET_NAME=${PROJECT_NAME}
    UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

    # Take build target
    if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
    then
    SF_SDK_PLATFORM=${BASH_REMATCH[1]}
    else
    echo "Could not find platform name from SDK_NAME: $SDK_NAME"
    exit 1
    fi

    if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
    then
    echo "Please choose iPhone simulator as the build target."
    exit 1
    fi

    IPHONE_DEVICE_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos

    # Build the other (non-simulator) platform
    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/arm64" SYMROOT="${SYMROOT}" ARCHS='arm64' VALID_ARCHS='arm64' $ACTION

    xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk iphoneos BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}"  CONFIGURATION_BUILD_DIR="${IPHONE_DEVICE_BUILD_DIR}/armv7" SYMROOT="${SYMROOT}" ARCHS='armv7 armv7s' VALID_ARCHS='armv7 armv7s' $ACTION

    # Copy the framework structure to the universal folder (clean it first)
    rm -rf "${UNIVERSAL_OUTPUTFOLDER}"
    mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
    cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"

    # Smash them together to combine all architectures
    lipo -create  "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/arm64/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/armv7/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}"

我选中了运行脚本下方的“仅在安装时运行脚本”复选框。现在我为通用 iOS 设备构建框架。 现在我单击项目层次结构中的产品组并选择 MyFramework.framework 文件,右键单击并选择在查找器中显示。它会在 finder 中打开 ~/Library/Developer/Xcode/DerivedData/MyFramework-dlpsipmxkqmemwgqrfeovlzgyhca/Build/Products/Debug-iphoneos/MyFramework.framework。

Now, I create a new tag 'MyFramework-v0.0.1' containing the commit where I added MyFramework.framework file. 

我去~/.cocoapods/repos/MyRepoName/创建一个podspec文件MyFramework.podspec如下:

Pod::Spec.new do |s|
s.name         = "MyFramework"
s.version      = "0.0.1"
s.summary      = "A pod for MyFramework"
s.description  = "A pod designed for MyFramework"

s.homepage     = "My_Private_Repo_Path"
s.license      = { :type => "MIT", :file => "FILE_LICENSE" }
s.authors             = { "My_Username" => "my_email_address"
}
s.platform     = :ios, "8.0"
s.ios.deployment_target = "8.0"

s.source       = { :git => "My_Private_Repo_Path", :tag => 'MyFramework-v'+String(s.version) }

s.requires_arc = true
s.vendored_frameworks = "MyFramework.framework"
s.dependency "CocoaLumberjack"
end

现在当我在终端中运行以下命令时:

pod repo push MyRepoName MyFramework.podspec

我收到以下错误:

Validating spec
-> MyFramework (0.0.1)
- ERROR | [iOS] xcodebuild: Returned an unsuccessful exit code. You can use `   --verbose` for more information.
- NOTE  | [iOS] xcodebuild:  ld: warning: ignoring file  MyFramework/MyFramework.framework/MyFramework, missing required architecture i386 in file   MyFramework/MyFramework.framework/MyFramework (2 slices)
- NOTE  | [iOS] xcodebuild:  ld: warning: ignoring file MyFramework/MyFramework.framework/MyFramework, missing required architecture x86_64 in file MyFramework/MyFramework.framework/MyFramework (2 slices)
- NOTE  | [iOS] xcodebuild:  fatal error: lipo: -remove's specified would result in an empty fat file

[!] The `MyFramework.podspec` specification does not validate.

如何为所有设备和模拟器构建可可触摸框架,它依赖于使用 cocoapods 添加的另一个框架(CocoaLumberjack)?我需要创建一个输出框架的私有 pod。

【问题讨论】:

标签: ios objective-c cocoapods podspec cocoa-touch


【解决方案1】:

所以基本上我发现我只需要遵循这些简单的步骤。

1. Create a cocoa touch framework.
2. Set bitcode enabled to No.
3. Select your target and choose edit schemes. Select Run and choose Release from Info tab. 
4. No other setting required.
5. Now build the framework for any simulator as simulator runs on x86 architecture.
6. Click on Products group in Project Navigator and find the .framework file. 
7. Right click on it and click on Show in finder. Copy and paste it in any folder, I personally prefer the name 'simulator'.
8. Now build the framework for Generic iOS Device and follow the steps 6 through 
9. Just rename the folder to 'device' instead of 'simulator'.
10. Copy the device .framework file and paste in any other directory. I prefer the immediate super directory of both.

所以目录结构现在变成了:

 - Desktop
   - device
     - MyFramework.framework
   - simulator
     - MyFramework.framework
   - MyFramework.framework

现在打开终端并 cd 到桌面。现在开始输入以下命令:

lipo -create 'device/MyFramework.framework/MyFramework' 'simulator/MyFramework.framework/MyFramework' -output 'MyFramework.framework/MyFramework'

就是这样。在这里,我们将 MyFramework.framework 中存在的 MyFramework 二进制文件的模拟器和设备版本合并。我们得到了一个通用框架,可以为包括模拟器和设备在内的所有架构构建。

现在,为这个框架创建一个 pod 没有任何区别。它就像一个魅力。另请注意,也有可用的运行脚本来实现相同的功能,但我花了很多时间来寻找正确的脚本。所以我建议你使用这种方法。

【讨论】:

  • 没有第9步
  • 非常感谢 muuuuuuuch 这就是我要搜索的内容!我也使用了您提到的脚本,但是当您使用 Development pods 项目时它们不起作用(在 pod 项目中向框架添加方案等。在发布时不使用 post action 脚本)
  • 我试过这个方法,执行 lipo -create 命令会丢失bitcode。
  • 我按照上述步骤操作但收到以下错误致命错误:device/MyFramework.framework/MyFramework 和simulator/MyFramework.framework/MyFramework 具有相同的架构(arm64)并且不能在相同的胖输出文件如何克服这个错误?
【解决方案2】:

XCode 11

首先请注意,您不能使用支持模拟器的胖框架(x84_64 架构)发布到 AppStore,因此您需要制作两个胖框架:一个用于带有 ARM 架构的发布(仅限设备),一个用于调试 - ARM 和x86_64 拱门。

您可以将下一个脚本放到项目的文件夹中,以从命令行创建胖框架:

  1. 生成文件
BUILD_DIR = build
BUILD = @sh build.sh ${BUILD_DIR}

default:
    @echo "Build framework makefile"
    @echo "usage: make (release | debug | all | rebuild | clean)"

release:
    ${BUILD} Release YourTargetScheme # <- your target scheme

debug:
    ${BUILD} Debug YourTargetScheme

clean:
    rm -r ${BUILD_DIR}

all: release debug

rebuild: clean all

  1. build.sh

# Debug
set -x

# Params
BUILD_DIR=$1
CONFIGURATION=$2
SCHEME=$3

WORKSPACE=YourWorkspace.xcworkspace # <- your workspace file
DERIVED_DATA_PATH=$BUILD_DIR/DerivedData

# Destinations
IPNONEOS="generic/platform=iOS"
IPNONESIMULATOR="platform=iOS Simulator,name=iPhone 8"

# Build
if [ $CONFIGURATION = "Release" ]; then
    xcodebuild \
        -quiet \
        -showBuildTimingSummary \
        -workspace $WORKSPACE \
        -configuration $CONFIGURATION \
        -scheme $SCHEME \
        -derivedDataPath $DERIVED_DATA_PATH \
        -destination "$IPNONEOS"
else
    xcodebuild \
        -quiet \
        -showBuildTimingSummary \
        -workspace $WORKSPACE \
        -configuration $CONFIGURATION \
        -scheme $SCHEME \
        -derivedDataPath $DERIVED_DATA_PATH \
        -destination "$IPNONEOS" \
        -destination "$IPNONESIMULATOR"
fi

# Move
FRAMEWORK=$SCHEME.framework
FRAMEWORK_PATH=$BUILD_DIR/$CONFIGURATION/$FRAMEWORK

mkdir $BUILD_DIR/$CONFIGURATION
rm -r $FRAMEWORK_PATH

if [ $CONFIGURATION = "Release" ]; then
    mv $DERIVED_DATA_PATH/Build/Products/Release-iphoneos/$FRAMEWORK $FRAMEWORK_PATH
else
    mv $DERIVED_DATA_PATH/Build/Products/Debug-iphoneos/$FRAMEWORK $FRAMEWORK_PATH

    BINARY_FILE=$FRAMEWORK_PATH/$SCHEME
    ARMV7=$FRAMEWORK_PATH/armv7
    ARM64=$FRAMEWORK_PATH/arm64
    x86_64=$FRAMEWORK_PATH/x86_64

    lipo $BINARY_FILE -extract armv7 -o $ARMV7
    lipo $BINARY_FILE -extract arm64 -o $ARM64
    cp $DERIVED_DATA_PATH/Build/Products/Debug-iphonesimulator/$FRAMEWORK/$SCHEME $x86_64

    lipo -create $ARMV7 $ARM64 $x86_64 -o $BINARY_FILE

    # Clean
    rm -rf $ARMV7 $ARM64 $x86_64
fi

在您的项目文件夹中运行以下命令之一来构建所需的框架:

make all # Debug and Release frameworks
make release # Release only for devices and AppStore (armv7 and arm64 archs)
make debug # Debug with simulator support (armv7, arm64 and x86_64 archs)

然后您可以在项目文件夹内的build 目录中找到您的胖框架。

【讨论】:

  • 我知道它只是部分相关,但我从这个答案开始(为了让我的框架构建继续进行,它有效,谢谢!)但我还需要使用故事板和 coreData 进行调查框架。对我来说并不明显的一件事是 - 使用特定的捆绑包来访问故事板/coreData 模型。 [NSBundle bundleWithIdentifier:@"com.your.framework.bundle.id.here"] 也许对某人有帮助。
  • 嗨@iUrii,你能详细解释一下吗?我们需要在哪里添加这个脚本。在目标->存档->发布操作->运行脚本?
  • @Ravi 只需在项目目录中创建 makefilebuild.sh 文件,然后从命令行运行 make
  • @iUrii Makefile 的扩展名是什么?这也是 Makefile.sh 吗?
  • Makefile 没有扩展名,所以它是文件的全名。
猜你喜欢
  • 2016-02-02
  • 2014-01-22
  • 1970-01-01
  • 2015-09-28
  • 2023-03-11
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多