3

我已经使用 Brew 在我的 Mac 上安装了 FreeType。我的 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/”目录中。

您在库的链接错误中看到的路径是 brew 在我创建此项目的 mac 中安装 freetype 的位置。

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

我已将所需的所有 lib/include/ 目录复制到项目的主文件夹中。
我已经在 Xcode 中设置了库并包含路径。

我在这里想念什么?我还需要做什么才能使我的代码在任何其他 Mac 上可移植。我通过安装 Brew 让项目在其他 Mac 上运行,但我想在不需要安装 brew 的情况下这样做。

PS:我必须使用 brew 安装 freetype,因为我无法为 32 位处理器的 freetype 编译 .dylib,.dylib 的 64 位副本给了我错误,例如“错误的架构!”

4

3 回答 3

4

我在评论中得到的基本想法是 OS X 在搜索库的位置上非常愚蠢,它将使用编译期间使用的相同绝对路径在运行时解析它们。

通常,当您想将应用程序部署/分发到与构建它的机器不同的机器时,您将在安装包/捆绑包中包含您的库。但是您可能希望他们在运行时使用相对于您的应用程序的路径,因此 install_name_tool -change 允许您将讨厌的绝对路径替换为相对路径。

希望这是有道理的,Apple 让在 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/)搜索它。

于 2013-10-07T17:08:16.857 回答
0

这是一个基于 Andon 答案的自动脚本,它将全部替换@executable_path/../Frameworks/@executable_path/,以便所有框架都可以与可执行文件位于同一文件夹中。如果它们相互依赖,这也会调整框架本身。

请注意,这仅对命令行应用程序有意义。如果它是一个常规的 OSX 捆绑应用程序,则可能不应该更改“../Frameworks”约定。

指示

  • /bin/sh在 Xcode 项目构建阶段的末尾使用 Shell = 将其添加为“运行脚本” 。

  • 还要确保所有框架都有“复制文件”步骤和“目标 = 产品目录”。

脚本

# 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 (...)
    ...
于 2017-12-20T01:25:18.993 回答
0

最后,这并没有我最初想象的那么容易。它有效,但有一些警告。

基于 Andon M. Coleman 和 Alexander Klimetschek 的答案,这里有一个脚本函数,它从 Homebrew 中获取 dylib 名称,将它们复制到应用程序包的Contents/Frameworks目录中,并使用install_name_tool. 我注意到有时实际的 .dylib 位置与 Xcode 链接器在将可执行文件放在一起时发现的位置不同,这也可以通过使用 .dylib 查询 exe 来处理otool -L

下面的脚本将 liblo 库的 .dylib 复制到应用程序包中。添加新库应该像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
    OLD=$(otool -L "$EXE_PATH" | grep $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)可能可以通过遵循 .dylib 符号链接来改进/usr/local/lib

更新您可能还需要确保对新的 dylib 进行签名,否则代码签名步骤将在构建时失败,至少它最终对我有用。在此SO post 之后,添加--deep到项目 Build Settings 中的Other Code Signing Flags选项有效。

可能有更好的方法来仅对脚本添加的文件进行签名,但我不想深入研究codesign手动运行。然而,从我所读到的内容来看,--deepApple 并未建议仅用于临时修复,因此这可能不是最佳实践。

更新2:运行--deep实际上并没有解决问题,应用程序仍然无法在其他机器上运行。最后,我需要手动对复制的 .dylib 进行代码签名,结果证明相对容易。

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

更新 3:引用路径变量使用来修复带有空格的路径。

更新 4:此解决方案适用于为最近的系统构建应用程序,但我在 macOS 10.10 上运行应用程序时遇到了代码签名问题。从这个 SO post判断,旧的 macOS 版本使用不同的代码签名哈希算法,该应用程序可以在 10.12 系统上正常运行,但在 10.10 上会因代码签名错误而失败。在这两个系统上,codesign 都验证了所有内容都已正确签名。

您基本上需要使用该-mmacosx-version-min集合构建库,以便 codesign 能够判断使用哪些算法来支持最小和最近的 macOS 版本。在从源代码构建 liblo 时,我尝试将自定义 CFLAGS/LDFLAGS 传递给 Homebrew,但如果不编辑 brew 公式,这是不可能的。最后,我决定自己编写一个构建脚本来从源代码构建 liblo,并修改了复制脚本,使其也可以获取本地位置。现在一切终于正常了。

TLDR:自制库非常适合初始开发和测试,但手动构建库更适合部署。

于 2018-04-10T12:17:46.440 回答