构建 OSX 应用程序包

发布于 2024-08-08 13:55:00 字数 217 浏览 5 评论 0原文

假设我在没有使用 Xcode 的情况下制作了一个 osX 应用程序。使用 GCC 编译后,我得到一个链接到其他几个库的可执行文件。其中一些库可能会再次动态链接到其他非标准系统库

是否存在任何工具可以通过首先制作所需的目录结构然后递归地复制/检查/修复链接以确保所有动态来制作 OSX 应用程序捆绑包依赖项也在应用程序包中吗?

我想我可以尝试写这样的东西,但我想知道这样的东西是否已经存在。

Suppose I have have made a an osX app without using Xcode. After compiling with GCC I get an executable which is linked to several other libraries. Some of those libraries might again be dynamically linked to other non-standard system libraries

Is there any tool which exists which makes an OSX App bundle by first making the required directory structures and then recursively copying/checking/fixing links to make sure all the dynamic dependencies are also in the app bundle?

I guess I can try writing something like this but I was wondering if something like this exists already.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

筱武穆 2024-08-15 13:55:00

在 MacOSX 上创建应用程序包有两种方法:简单方法和丑陋方法。

最简单的方法就是使用 XCode。
完毕。

问题是有时你做不到。

就我而言,我正在构建一个可以构建其他应用程序的应用程序。
我不能假设用户已经安装了 XCode。
我还使用 MacPorts 来构建我的应用程序所依赖的库。
我需要确保在分发应用程序之前将这些 dylib 与应用程序捆绑在一起。

免责声明:我完全没有资格写这篇文章,一切都在
苹果文档中已经透露了这一点,对现有的应用程序进行了拆解,
反复试验。它对我有用,但很可能是错误的。请
如果您有任何更正,请给我发电子邮件。

您应该知道的第一件事是应用程序包只是一个目录。
让我们检查一下假设的 foo.app 的结构。

foo.app/
    Contents/
        Info.plist
        MacOS/
            foo
        Resources/
            foo.icns

Info.plist 是一个纯 XML 文件。
您可以使用文本编辑器或与 XCode 捆绑在一起的属性列表编辑器应用程序对其进行编辑。
(它位于 /Developer/Applications/Utilities/ 目录中)。

您需要包括的关键内容是:

CFBundleName - 应用程序的名称。

CFBundleIcon - 假定位于 Contents/Resources 目录中的图标文件。
使用图标编辑器应用程序创建图标。 (它也在 /Developer/Applications/Utilities/ 目录中)
您只需将 png 拖放到其窗口上,就会自动为您生成 mip 级别。

CFBundleExecutable - 假定位于 Contents/MacOS/ 子文件夹中的可执行文件的名称。

还有很多选择,上面列出的只是最低限度的选择。
这是一些有关的 Apple 文档
Info.plist
文件和
应用包结构

另外,这是一个示例 Info.plist。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleGetInfoString</key>
  <string>Foo</string>
  <key>CFBundleExecutable</key>
  <string>foo</string>
  <key>CFBundleIdentifier</key>
  <string>com.your-company-name.www</string>
  <key>CFBundleName</key>
  <string>foo</string>
  <key>CFBundleIconFile</key>
  <string>foo.icns</string>
  <key>CFBundleShortVersionString</key>
  <string>0.01</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>IFMajorVersion</key>
  <integer>0</integer>
  <key>IFMinorVersion</key>
  <integer>1</integer>
</dict>
</plist>

在完美的世界中,您只需将可执行文件放入
Contents/MacOS/ dir 就完成了。但是,如果您的应用程序有任何
非标准 dylib 依赖项将无法工作。像 Windows、MacOS
带有它自己特殊的DLL Hell

如果您使用 MacPorts 来构建链接的库,则 dylib 的位置将很难 -编码到您的可执行文件中。
如果您在 dylib 位于完全相同位置的计算机上运行该应用程序,它将运行良好。
然而,大多数用户不会安装它们;当他们双击您的应用程序时,它就会崩溃。

在分发可执行文件之前,您需要收集它加载的所有 dylib 并将它们复制到应用程序包中。
您还需要编辑可执行文件,以便它在正确的位置查找 dylib。即您将它们复制到的位置。

手动编辑可执行文件听起来很危险,对吗?
幸运的是,有命令行工具可以提供帮助。

otool -L executable_name

此命令将列出您的应用程序依赖的所有 dylib。
如果您看到任何不在 System/Library 或 usr/lib 文件夹中的内容,则需要将这些内容复制到应用程序包中。
将它们复制到 /Contents/MacOS/ 文件夹中。
接下来,您需要编辑可执行文件以使用新的 dylib。

首先,您需要确保使用 -headerpad_max_install_names 标志进行链接。
这只是确保如果新的 dylib 路径比前一个路径长,会有空间。

其次,使用 install_name_tool 更改每个 dylib 路径。

install_name_tool -change existing_path_to_dylib @executable_path/blah.dylib executable_name

作为一个实际示例,假设您的应用程序使用 libSDL,并且 otool 将其位置列为“/opt/local/ lib/libSDL-1.2.0.dylib”。

首先将其复制到应用程序包中。

cp /opt/local/lib/libSDL-1.2.0.dylib foo.app/Contents/MacOS/

然后编辑可执行文件以使用新位置(注意:确保使用 -headerpad_max_install_names 标志构建它)

install_name_tool -change /opt/local/lib/libSDL-1.2.0.dylib @executable_path/libSDL-1.2.0.dylib foo.app/Contents/MacOS/foo

哇,我们快完成了。
现在当前工作目录有一个小问题。

当您启动应用程序时,当前目录将是应用程序所在目录的上一级目录。
例如:如果将 foo.app 放在 /Applcations 文件夹中,则启动应用程序时的当前目录将是 /Applications 文件夹。
不是您所期望的 /Applications/foo.app/Contents/MacOS/ 。

您可以更改您的应用程序来解决此问题,或者您可以使用这个神奇的小启动器脚本来更改当前目录并启动您的应用程序。

#!/bin/bash
cd "${0%/*}"
./foo

确保调整 Info.plist 文件,以便 CFBundleExecutable 指向启动脚本而不是先前的可执行文件。

好的,现在一切都完成了。
幸运的是,一旦您了解了所有这些内容,您就可以将其埋入构建脚本中。

There are two ways to create an app bundle on MacOSX, the Easy and the Ugly.

The easy way is just to use XCode.
Done.

The problem is sometimes you can't.

In my case I'm building an app that builds other apps.
I can't assume the user has XCode installed.
I'm also using MacPorts to build the libraries my app depends on.
I need to make sure that these dylibs get bundled with the app before I distribute it.

Disclaimer: I'm totally unqualified to write this post, everything in
is has been gleamed from Apple docs, picking apart existing apps and
trial and error. It works for me, but is most likely wrong. Please
email me if you have any corrections.

First thing you should know is that an app bundle is just a directory.
Let's examine the structure of a hypothetical foo.app.

foo.app/
    Contents/
        Info.plist
        MacOS/
            foo
        Resources/
            foo.icns

Info.plist is a plain XML file.
You can edit it with a text editor or the Property List Editor app that comes bundled with XCode.
(It's in /Developer/Applications/Utilities/ directory).

The key things you need to include are:

CFBundleName - The name of the app.

CFBundleIcon - An Icon file assumed to be in Contents/Resources dir.
Use the Icon Composer app to create the icon. (It's also in the /Developer/Applications/Utilities/ directory)
You can just drag and drop a png onto it's window and should automatically generate the mip-levels for you.

CFBundleExecutable - Name of the executable file assumed to be in Contents/MacOS/ sub-folder.

There are lots more options, the ones listed above are only the bare minimum.
Here's some Apple documentation on the
Info.plist
file and
App bundle structure.

Also, Here's a sample Info.plist.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleGetInfoString</key>
  <string>Foo</string>
  <key>CFBundleExecutable</key>
  <string>foo</string>
  <key>CFBundleIdentifier</key>
  <string>com.your-company-name.www</string>
  <key>CFBundleName</key>
  <string>foo</string>
  <key>CFBundleIconFile</key>
  <string>foo.icns</string>
  <key>CFBundleShortVersionString</key>
  <string>0.01</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>IFMajorVersion</key>
  <integer>0</integer>
  <key>IFMinorVersion</key>
  <integer>1</integer>
</dict>
</plist>

In a perfect world you could just drop your executable into the
Contents/MacOS/ dir and be done. However, if your app has any
non-standard dylib dependencies it won't work. Like Windows, MacOS
comes with it's own special kind of DLL Hell.

If you're using MacPorts to build libraries that you link against, the locations of the the dylibs will be hard-coded into your executable.
If you run the app on a machine that has the dylibs in the exact same location, it will run fine.
However, most users won't have them installed; when they double-click your app it will just crash.

Before you distribute you executable you'll need to collect all the dylibs it loads and copy them into the app bundle.
You will also need to edit the executable so that it will look for the dylibs in the correct place. i.e. where you copied them to.

Hand editing an executable sounds dangerous right?
Luckily there are command line tools to help.

otool -L executable_name

This command will list all the dylibs that your app depends on.
If you see any that are NOT in the System/Library or usr/lib folder, those are the ones you'll need to copy into the app bundle.
Copy them into the /Contents/MacOS/ folder.
Next you'll need to edit the executable to use the new dylibs.

First, you need to make sure that you link using the -headerpad_max_install_names flag.
This just makes sure that if the new dylib path is longer then the previous one, there will be room for it.

Second, use the install_name_tool to change each dylib path.

install_name_tool -change existing_path_to_dylib @executable_path/blah.dylib executable_name

As a practical example, Let's say your app uses libSDL, and otool lists it's location as "/opt/local/lib/libSDL-1.2.0.dylib".

First copy it into the app bundle.

cp /opt/local/lib/libSDL-1.2.0.dylib foo.app/Contents/MacOS/

Then edit the executable to use the new location (NOTE: make sure you built it with the -headerpad_max_install_names flag)

install_name_tool -change /opt/local/lib/libSDL-1.2.0.dylib @executable_path/libSDL-1.2.0.dylib foo.app/Contents/MacOS/foo

Whew, we're almost done.
Now there's a small issue with the current working directory.

When you start your app the current directory will be the directory above where the application is located.
For example: If you place the foo.app in the /Applcations folder then the current directory when you launch the app will be the /Applications folder.
Not the /Applications/foo.app/Contents/MacOS/ as you might expect.

You can alter your app to account for this, or you can use this magic little launcher script that will change the current directory and launch your app.

#!/bin/bash
cd "${0%/*}"
./foo

Make sure you adjust the Info.plist file so that CFBundleExecutable points to the launch script and not to the previous executable.

Ok, all done now.
Luckily, once you know all this stuff you bury it in a build script.

天荒地未老 2024-08-15 13:55:00

我实际上发现了一个非常方便的工具,值得一些赞扬...不 - 我没有开发这个;)

https:// /github.com/auriamg/macdylibbundler/

它将解决所有依赖项并“修复”您的可执行文件以及 dylib 文件,以便在您的应用程序包中顺利运行。

...它还将检查依赖动态库的依赖关系:D

I actually found a very handy tool that deserves some credit ... NO - I did not develop this ;)

https://github.com/auriamg/macdylibbundler/

It will resolve all the dependencies and "fix" your executable as well as your dylib files to work smoothly in your app bundle.

... it will also check for the dependencies of your dependent dynamic libs :D

盗梦空间 2024-08-15 13:55:00

我在我的 Makefile 中使用它...它创建了一个应用程序包。阅读并理解它,因为您需要 macosx/ 文件夹中的 png 图标文件以及我在此处包含的 PkgInfo 和 Info.plist 文件...

“它可以在我的计算机上运行”...我将其用于多个Mavericks 上的应用程序...

APPNAME=MyApp
APPBUNDLE=$(APPNAME).app
APPBUNDLECONTENTS=$(APPBUNDLE)/Contents
APPBUNDLEEXE=$(APPBUNDLECONTENTS)/MacOS
APPBUNDLERESOURCES=$(APPBUNDLECONTENTS)/Resources
APPBUNDLEICON=$(APPBUNDLECONTENTS)/Resources
appbundle: macosx/$(APPNAME).icns
    rm -rf $(APPBUNDLE)
    mkdir $(APPBUNDLE)
    mkdir $(APPBUNDLE)/Contents
    mkdir $(APPBUNDLE)/Contents/MacOS
    mkdir $(APPBUNDLE)/Contents/Resources
    cp macosx/Info.plist $(APPBUNDLECONTENTS)/
    cp macosx/PkgInfo $(APPBUNDLECONTENTS)/
    cp macosx/$(APPNAME).icns $(APPBUNDLEICON)/
    cp $(OUTFILE) $(APPBUNDLEEXE)/$(APPNAME)

macosx/$(APPNAME).icns: macosx/$(APPNAME)Icon.png
    rm -rf macosx/$(APPNAME).iconset
    mkdir macosx/$(APPNAME).iconset
    sips -z 16 16     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16.png
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32.png
    sips -z 64 64     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 128 128   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128.png
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256.png
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png
    cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/[email protected]
    iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset
    rm -r macosx/$(APPNAME).iconset

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>MyApp</string>
    <key>CFBundleGetInfoString</key>
    <string>0.48.2, Copyright 2013 my company</string>
    <key>CFBundleIconFile</key>
    <string>MyApp.icns</string>
    <key>CFBundleIdentifier</key>
    <string>com.mycompany.MyApp</string>
    <key>CFBundleDocumentTypes</key>
    <array>
    </array>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>0.48.2</string>
    <key>CFBundleSignature</key>
    <string>MyAp</string>
    <key>CFBundleVersion</key>
    <string>0.48.2</string>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright 2013 my company.</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.3</string>
</dict>
</plist>

PkgInfo

APPLMyAp

I use this in my Makefile... It creates an app bundle. Read it and understand it, because you'll need a png icon file in a macosx/ folder along with the PkgInfo and Info.plist files i include here...

"it works on my computer"... I use this for multiple apps on Mavericks...

APPNAME=MyApp
APPBUNDLE=$(APPNAME).app
APPBUNDLECONTENTS=$(APPBUNDLE)/Contents
APPBUNDLEEXE=$(APPBUNDLECONTENTS)/MacOS
APPBUNDLERESOURCES=$(APPBUNDLECONTENTS)/Resources
APPBUNDLEICON=$(APPBUNDLECONTENTS)/Resources
appbundle: macosx/$(APPNAME).icns
    rm -rf $(APPBUNDLE)
    mkdir $(APPBUNDLE)
    mkdir $(APPBUNDLE)/Contents
    mkdir $(APPBUNDLE)/Contents/MacOS
    mkdir $(APPBUNDLE)/Contents/Resources
    cp macosx/Info.plist $(APPBUNDLECONTENTS)/
    cp macosx/PkgInfo $(APPBUNDLECONTENTS)/
    cp macosx/$(APPNAME).icns $(APPBUNDLEICON)/
    cp $(OUTFILE) $(APPBUNDLEEXE)/$(APPNAME)

macosx/$(APPNAME).icns: macosx/$(APPNAME)Icon.png
    rm -rf macosx/$(APPNAME).iconset
    mkdir macosx/$(APPNAME).iconset
    sips -z 16 16     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_16x16.png
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 32 32     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_32x32.png
    sips -z 64 64     macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 128 128   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_128x128.png
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 256 256   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_256x256.png
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/[email protected]
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png
    cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/[email protected]
    iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset
    rm -r macosx/$(APPNAME).iconset

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleExecutable</key>
    <string>MyApp</string>
    <key>CFBundleGetInfoString</key>
    <string>0.48.2, Copyright 2013 my company</string>
    <key>CFBundleIconFile</key>
    <string>MyApp.icns</string>
    <key>CFBundleIdentifier</key>
    <string>com.mycompany.MyApp</string>
    <key>CFBundleDocumentTypes</key>
    <array>
    </array>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>0.48.2</string>
    <key>CFBundleSignature</key>
    <string>MyAp</string>
    <key>CFBundleVersion</key>
    <string>0.48.2</string>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright 2013 my company.</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.3</string>
</dict>
</plist>

PkgInfo

APPLMyAp
江南烟雨〆相思醉 2024-08-15 13:55:00

最简单的解决方案是:创建一次 Xcode 项目而不进行任何更改(即保留 Xcode 为您创建的简单单窗口应用程序),构建它,然后复制它为您创建的捆绑包。然后,编辑文件(尤其是 Info.plist)以适合您的内容,并将您自己的二进制文件放入 Contents/MacOS/ 目录中。

The simplest solution is: create once an Xcode project without changing anything (i.e. keep the simple one-window app that Xcode creates for you), build it, and copy the bundle it created for you. Then, edit the files (notably the Info.plist) to suit your content, and put your own binary in the Contents/MacOS/ directory.

写下不归期 2024-08-15 13:55:00

有一些开源工具可以帮助为特定环境构建具有依赖库的应用程序包,例如 py2app< /a> 用于基于 Python 的应用程序。如果您找不到更通用的,也许您可​​以根据您的需要进行调整。

There are some open source tools to help build app bundles with dependent libraries for specific environments, for example, py2app for Python-based applications. If you don't find a more general one, perhaps you can adapt it to your needs.

锦上情书 2024-08-15 13:55:00

我希望我早点找到这篇文章......

这是我使用 Run script 阶段解决此问题的粗略方法,每次构建 Release 版本时都会调用该阶段我的应用程序:

# this is an array of my dependencies' libraries paths 
# which will be iterated in order to find those dependencies using otool -L
libpaths=("$NDNRTC_LIB_PATH" "$BOOST_LIB_PATH" "$NDNCHAT_LIB_PATH" "$NDNCPP_LIB_PATH" "/opt/local/lib")
frameworksDir=$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH
executable=$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH

#echo "libpaths $libpaths"
bRecursion=0
lRecursion=0

# this function iterates through libpaths array
# and checks binary with "otool -L" command for containment
# of dependency which has "libpath" path
# if such dependency has been found, it will be copied to Frameworks 
# folder and binary will be fixed with "install_name_tool -change" command
# to point to Frameworks/<dependency> library
# then, dependency is checked recursively with resolveDependencies function
function resolveDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$((lRecursion*20))
    printf "%s :\t%s\n" $prefix "resolving $binname..."

    for path in ${libpaths[@]}; do
        local temp=$path
        #echo "check lib path $path"
        local pattern="$path/([A-z0-9.-]+\.dylib)"
        while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
            local libname=${BASH_REMATCH[1]}
            otool -L ${binfile}
            #echo "found match $libname"
            printf "%s :\t%s\n" $prefix "fixing $libname..."
            local libpath="${path}/$libname"
            #echo "cp $libpath $frameworksDir"
            ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
            local installLibPath="@rpath/$libname"
            #echo "install_name_tool -change $libpath $installLibPath $binfile"
            if [ "$libname" == "$binname" ]; then
                install_name_tool -id "@rpath/$libname" $binfile
                printf "%s :\t%s\n" $prefix "fixed id for $libname."
            else
                install_name_tool -change $libpath $installLibPath $binfile
                printf "%s :\t%s\n" $prefix "$libname dependency resolved."
                let lRecursion++
                resolveDependencies "$frameworksDir/$libname" "$prefix>$libname"
                resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
                let lRecursion--
            fi
            path=$temp
        done # while
    done # for

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
} # resolveDependencies

# for some reason, unlike other dependencies which maintain full path
# in "otool -L" output, boost libraries do not - they just appear 
# as "libboost_xxxx.dylib" entries, without fully qualified path
# thus, resolveDependencies can't be used and a designated function is needed
# this function works pretty much in a similar way to resolveDependencies
# but targets only dependencies starting with "libboost_", copies them
# to the Frameworks folder and resolves them recursively
function resolveBoostDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$(((bRecursion+lRecursion)*20))
    printf "%s :\t%s\n" $prefix "resolving Boost for $(basename $binfile)..."

    local pattern="[[:space:]]libboost_([A-z0-9.-]+\.dylib)"
    while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
        local libname="libboost_${BASH_REMATCH[1]}"
        #echo "found match $libname"
        local libpath="${BOOST_LIB_PATH}/$libname"
        #echo "cp $libpath $frameworksDir"
        ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
        installLibPath="@rpath/$libname"
        #echo "install_name_tool -change $libname $installLibPath $binfile"
        if [ "$libname" == "$binname" ]; then
            install_name_tool -id "@rpath/$libname" $binfile
            printf "%s :\t%s\n" $prefix "fixed id for $libname."
        else
            install_name_tool -change $libname $installLibPath $binfile
            printf "%s :\t%s\n" $prefix "$libname Boost dependency resolved."
            let bRecursion++
            resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
            let bRecursion--
        fi
    done # while

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
}

resolveDependencies $executable $(basename $executable)
resolveBoostDependencies $executable $(basename $executable)

希望这对某人有用。

I wish I've found this post earlier....

Here's my sketchy way of solving this problem using a Run script phase which is invoked every time I build a Release version of my app:

# this is an array of my dependencies' libraries paths 
# which will be iterated in order to find those dependencies using otool -L
libpaths=("$NDNRTC_LIB_PATH" "$BOOST_LIB_PATH" "$NDNCHAT_LIB_PATH" "$NDNCPP_LIB_PATH" "/opt/local/lib")
frameworksDir=$BUILT_PRODUCTS_DIR/$FRAMEWORKS_FOLDER_PATH
executable=$BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH

#echo "libpaths $libpaths"
bRecursion=0
lRecursion=0

# this function iterates through libpaths array
# and checks binary with "otool -L" command for containment
# of dependency which has "libpath" path
# if such dependency has been found, it will be copied to Frameworks 
# folder and binary will be fixed with "install_name_tool -change" command
# to point to Frameworks/<dependency> library
# then, dependency is checked recursively with resolveDependencies function
function resolveDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$((lRecursion*20))
    printf "%s :\t%s\n" $prefix "resolving $binname..."

    for path in ${libpaths[@]}; do
        local temp=$path
        #echo "check lib path $path"
        local pattern="$path/([A-z0-9.-]+\.dylib)"
        while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
            local libname=${BASH_REMATCH[1]}
            otool -L ${binfile}
            #echo "found match $libname"
            printf "%s :\t%s\n" $prefix "fixing $libname..."
            local libpath="${path}/$libname"
            #echo "cp $libpath $frameworksDir"
            ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
            local installLibPath="@rpath/$libname"
            #echo "install_name_tool -change $libpath $installLibPath $binfile"
            if [ "$libname" == "$binname" ]; then
                install_name_tool -id "@rpath/$libname" $binfile
                printf "%s :\t%s\n" $prefix "fixed id for $libname."
            else
                install_name_tool -change $libpath $installLibPath $binfile
                printf "%s :\t%s\n" $prefix "$libname dependency resolved."
                let lRecursion++
                resolveDependencies "$frameworksDir/$libname" "$prefix>$libname"
                resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
                let lRecursion--
            fi
            path=$temp
        done # while
    done # for

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
} # resolveDependencies

# for some reason, unlike other dependencies which maintain full path
# in "otool -L" output, boost libraries do not - they just appear 
# as "libboost_xxxx.dylib" entries, without fully qualified path
# thus, resolveDependencies can't be used and a designated function is needed
# this function works pretty much in a similar way to resolveDependencies
# but targets only dependencies starting with "libboost_", copies them
# to the Frameworks folder and resolves them recursively
function resolveBoostDependencies()
{
    local binfile=$1
    local prefix=$2
    local binname=$(basename $binfile)
    local offset=$(((bRecursion+lRecursion)*20))
    printf "%s :\t%s\n" $prefix "resolving Boost for $(basename $binfile)..."

    local pattern="[[:space:]]libboost_([A-z0-9.-]+\.dylib)"
    while [[ "$(otool -L ${binfile})" =~ $pattern ]]; do
        local libname="libboost_${BASH_REMATCH[1]}"
        #echo "found match $libname"
        local libpath="${BOOST_LIB_PATH}/$libname"
        #echo "cp $libpath $frameworksDir"
        ${SRCROOT}/sudocp.sh $libpath $frameworksDir/$libname $(whoami)
        installLibPath="@rpath/$libname"
        #echo "install_name_tool -change $libname $installLibPath $binfile"
        if [ "$libname" == "$binname" ]; then
            install_name_tool -id "@rpath/$libname" $binfile
            printf "%s :\t%s\n" $prefix "fixed id for $libname."
        else
            install_name_tool -change $libname $installLibPath $binfile
            printf "%s :\t%s\n" $prefix "$libname Boost dependency resolved."
            let bRecursion++
            resolveBoostDependencies "$frameworksDir/$libname" "$prefix>$libname"
            let bRecursion--
        fi
    done # while

    printf "%s :\t%s\n" $prefix "$(basename $binfile) resolved."
}

resolveDependencies $executable $(basename $executable)
resolveBoostDependencies $executable $(basename $executable)

Hope this might be useful to someone.

流心雨 2024-08-15 13:55:00

使用 wxWidget 代码在 Mac 上使用菜单的解决方法很简单:

  1. 像往常一样在终端中启动程序 (./appname)
  2. 程序 GUI 正常启动,然后单击终端以使应用程序失去焦点
  3. 单击 GUI 以重新获得焦点并且菜单项可以工作。

我同意应用程序包是在 Mac 上构建程序的正确方法。这只是调试过程中提供帮助的简单解决方法。

编辑:这是在 Mac Catalina、wxWidgets 3.1.4 和 g++ 4.2.1 上(2020 年 11 月)

A workaround to have the menu work on Mac with wxWidget code is to simply:

  1. Start the program in a terminal as usual (./appname)
  2. The program GUI starts as normal, and click the terminal to have the app lose focus
  3. Click the GUI to regain focus and the menu items work.

I agree that an app bundle is the correct way to build a program on a Mac. This is just a simple workaround for help during debugging.

Edit: This is on Mac Catalina, wxWidgets 3.1.4, with g++ 4.2.1 (Nov 2020)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文