如何使Xcode项目带有自制库?

uqdfh47h  于 2023-01-31  发布在  其他
关注(0)|答案(3)|浏览(155)

我已经在我的mac上安装了FreeType使用Brew。代码在我的mac上工作正常,但当我试图运行该项目在其他mac上我得到下面提到的链接错误。

dyld: Library not loaded: /usr/local/opt/freetype/lib/libfreetype.6.dylib
Referenced from: /Users/ashutosh/Library/Developer/Xcode/DerivedData/InstrumentChamp- 
etytleaabhxmokadgrjzbgtmwxfl/Build/Products/Debug/instrumentchamp.app/Contents/MacOS/instrumentchamp
Reason: image not found (lldb)

当我尝试在其他Mac上运行代码时,Freetype的所有库目录和包含目录都包含在项目的“$SRCROOT/”目录中。
你在library的链接错误中看到的路径是brew在我创建这个项目的mac中安装freetype的地方。

/usr/local/opt/freetype/lib/libfreetype.6.dylib

我已经将所有需要的lib/ include/目录复制到了项目的主文件夹中。
而且我已经设置了库并在Xcode中包含了路径。
我在这里错过了什么?我还需要做什么才能让我的代码在任何其他Mac上都是可移植的。我通过安装Brew让这个项目在其他Mac上运行,但我想在不需要安装Brew的情况下完成它。
PS:我不得不使用brew安装freetype,因为我无法为32位处理器的freetype编译.dylib,一个64位的.dylib副本给了我错误,如“错误的架构!”

4urapxun

4urapxun1#

我在评论中得到的基本想法是,OSX在搜索库的位置上相当愚蠢,它将使用编译期间使用的相同绝对路径在运行时解析它们。
通常当你想把你的应用程序部署/分发到一台与构建它的机器不同的机器上时,你会把你的库包含在安装包/包中,但是你可能希望它们在运行时使用一个相对于你的应用程序的路径,因此install_name_tool -change允许你用一些相对的东西来替换讨厌的绝对路径。
希望这是有意义的,苹果让在OS X上使用系统级框架变得非常容易,定制库就不那么容易了。如果你使用系统级框架编译,/System/Library/Frameworks/...在所有OS X安装上都是通用的(给定相同的目标发布版本)。
要解决您的问题,我将执行以下操作:
install_name_tool -change /usr/local/opt/freetype/lib/libfreetype.6.dylib @executable_path/lib/libfreetype.6.dylib <executable_name_here>
然后,它将停止在您编译软件时所在的位置查找libfreetype.6.dylib,而是在运行时相对于可执行文件的位置(在本例中,在子目录lib/中)搜索它。

dwbf0jvd

dwbf0jvd2#

  • 最后,这并不像我最初想象的那么容易。它起作用了,但也有警告。*

基于Andon M.科尔曼和 Alexandria Klimetschek的回答,这里有一个脚本函数,它从Homebrew中获取dylib名称,将它们复制到应用包的Contents/Frameworks目录中。并使用install_name_tool设置可执行文件对应的搜索路径。dylib位置与Xcode链接器在将可执行文件放在一起时找到的位置不同,这也可以通过使用otool -L查询exe来处理。
下面的脚本将liblo库的.dylib复制到app bundle中。添加新库应该像在脚本底部添加一个新的copy lib ###.dlyib行一样简单。

# exit on error
set -e

# paths
LOCALLIBS_PATH="$SRCROOT/libs"
HOMEBREW_PATH="/usr/local"
FRAMEWORKS_PATH="$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH"
EXE_PATH="$BUILT_PRODUCTS_DIR/$EXECUTABLE_FOLDER_PATH/$EXECUTABLE_NAME"

# code signing identity
IDENTITY="$EXPANDED_CODE_SIGN_IDENTITY_NAME"
if [ "$IDENTITY" == "" ] ; then
    IDENTITY="$CODE_SIGN_IDENTITY"
fi

# copy lib into Contents/Frameworks and set lib search path
# $1: library .dylib
# $2: optional path to libs folder, otherwise use Homebrew
copylib() {
    LIB=$1
    if [ "$2" == "" ] ; then
        # use homebrew
        LIB_PATH=$(find "$HOMEBREW_PATH" -type f -name $LIB)
    else
        # use given path
        LIB_PATH="$2/$LIB"
    fi
    echo "$LIB:"
    echo "  $LIB_PATH -> $FRAMEWORKS_FOLDER_PATH/$LIB"

    # copy lib, set permissions, and sign
    mkdir -p "$FRAMEWORKS_PATH"
    cp "$LIB_PATH" "$FRAMEWORKS_PATH/"
    chmod 755 "$FRAMEWORKS_PATH/$LIB"
    codesign --verify --force --sign "$IDENTITY" "$FRAMEWORKS_PATH/$LIB"

    # set new path within executable
    # note: grep --max 1 as multi-arch builds will print path once *per arch*
    OLD=$(otool -L "$EXE_PATH" | grep --max 1 $LIB | awk '{print $1}')
    NEW="@executable_path/../Frameworks/$LIB"
    install_name_tool -change "$OLD" "$NEW" "$EXE_PATH"

    # print to confirm new path
    otool -L "$EXE_PATH" | grep $LIB
}

##### GO

# use lib from homebrew
copy lib liblo.7.dylib

# or use local lib
#copylib liblo.7.dylib "$LOCALLIBS_PATH/liblo/lib"

将其添加为Xcode项目中的运行脚本构建阶段。您也可以将其另存为外部文件,并在运行脚本阶段中使用sh调用它:

sh $PROJECT_DIR/path-to/copy-libs.sh

构建报告输出示例:

liblo.7.dylib:
    /usr/local/Cellar/liblo/0.29/lib/liblo.7.dylib ->> YOURAPP.app/Contents/Frameworks/liblo.7.dylib
    @executable_path/../Frameworks/liblo.7.dylib (compatibility version 11.0.0, current version 11.0.0)

$(find $HOMEBREW_PATH -type f -name $LIB)可能可以通过在/usr/local/lib中使用.dylib符号链接来改进。

UPDATE:您可能还需要确保新的dylib已签名,否则代码签名步骤将在构建时失败,至少最终对我来说是这样的。在SO post之后,将--deep添加到项目Build Settings中的 Other Code Signing Flags 选项。

也许有一个更好的方法可以只对脚本添加的文件进行签名,但我不想手动运行codesign。然而,据我所知,苹果建议使用--deep只用于临时修复,因此这可能不是一个最佳实践。

更新2:运行--deep并没有真正解决这个问题,而且这个应用程序仍然不能在其他机器上运行。最后,我需要手动对复制的.dylib进行代码签名,这相对容易。

注意:我在运行codesign时遇到了一个模糊的“Mac开发人员”身份错误,似乎被Keychain中同名的两个证书混淆了:即当前的开发者证书和之前过期的开发者证书。打开Keychain Access并删除过期的证书解决了这个问题。

更新3:带引号的路径变量用于修复带有空格的路径。
更新4:这个解决方案可以很好地为最新的系统构建应用,但我在macOS 10.10上运行应用时遇到了代码签名问题。从this SO post来看,旧版本的macOS使用了不同的codesign哈希算法,该应用在10.12系统上运行良好,但在10.10上运行失败,出现代码签名错误。在这两个系统上,codesign都验证了所有内容都正确签名。

你基本上需要用-mmacosx-version-min集合来构建库,这样codesign就能知道使用哪种算法来支持min和最近的macOS版本。在从源代码构建liblo的时候,我试着把自定义的CFLAGS/LDFLAGS传递给Homebrew,但是如果不编辑brew公式,这是不可能的。我决定自己写一个构建脚本来从源代码构建liblo,我修改了复制脚本,这样它也可以使用本地位置。现在一切都 * 终于 * 工作了。
TLDR:Homebrew库对于初始开发和测试非常有效,但是手动构建库对于部署效果更好。

hpxqektj

hpxqektj3#

下面是一个基于Andon答案的自动化脚本,它会将所有的@executable_path/../Frameworks/替换为@executable_path/,这样所有的框架都可以和可执行文件在同一个文件夹中。如果框架之间有依赖关系,这也会调整框架本身。
请注意,这只对命令行应用程序有意义,如果是一个常规的OSX捆绑应用程序,可能不应该改变“../Frameworks”约定。

说明

  • 使用Shell = /bin/sh在Xcode项目构建阶段末尾将其添加为“运行脚本”。
  • 还要确保所有框架的“复制文件”步骤都有一个“目标=产品目录”。

脚本

# change the hardcoded ../Frameworks relative path that Xcode does by rewriting the binaries after the build
# check with:
#   otool -L <binary>

# this is the new location
# make sure this is the same Destination the Frameworks are copied to in the "Copy Files" step
# the default "@executable_path/" would be Destination = Products Directory
NEW_FRAMEWORKS_PATH="@executable_path/"

# the one we want to replace
DEFAULT_FRAMEWORKS_PATH="@executable_path/../Frameworks/"

function change_binary {
    local libpaths=`otool -L "$1" | grep "$DEFAULT_FRAMEWORKS_PATH" | tr '\t' ' ' | cut -d " " -f2`
    local lib
    for lib in $libpaths; do
        if [ "$2" == "recursive" ]; then
            local libbinary=`echo $lib | sed "s,$DEFAULT_FRAMEWORKS_PATH,,"`
            change_binary "$libbinary"
        fi
        local newlib=`echo $lib | sed "s,$DEFAULT_FRAMEWORKS_PATH,$NEW_FRAMEWORKS_PATH,"`;
        echo "changing library path in '$1': '$lib' => '$newlib'"
        install_name_tool -change "$lib" "$newlib" "$1"
    done
}

cd $BUILT_PRODUCTS_DIR
change_binary "$EXECUTABLE_NAME" recursive

请注意,将NEW_FRAMEWORKS_PATH更改为相对子路径是不起作用的,脚本需要变得更复杂一些才能处理此问题。

结果结构

$BUILT_PRODUCTS_DIR/
     executable
     Some.framework/
     Another.framework/
     ...

otool -L executable看起来像这样:

executable:
    @executable_path/Some.framework/Versions/A/Some (...)
    @executable_path/Another.framework/Versions/A/Another (...)
    ...

相关问题