构建OSX App Bundle

Yog*_*ogi 80 macos gcc otool

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

是否有任何工具存在,它通过首先制作所需的目录结构,然后递归复制/检查/修复链接来确保所有动态依赖关系也在应用程序包中,从而制作OSX App包?

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

hyp*_*gic 127

有两种方法可以在MacOSX上创建应用程序包,即Easy和Ugly.

简单的方法就是使用XCode.完成.

问题有时你不能.

在我的情况下,我正在构建一个构建其他应用程序的应用程序.我不能假设用户已安装XCode.我也使用MacPorts来构建我的应用所依赖的库.在分发之前,我需要确保这些dylib与应用程序捆绑在一起.

免责声明:我完全没有资格写这篇文章,其中的所有内容都是从Apple文档中剔除,挑选现有的应用程序和反复试验.它对我有用,但很可能是错的.如果您有任何更正,请给我发电子邮件.

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

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

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

您需要包含的关键事项是:

CFBundleName - 应用程序的名称.

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

CFBundleExecutable - 假定在Contents/MacOS /子文件夹中的可执行文件的名称.

还有更多选项,上面列出的选项只是最低限度.以下是Info.plist 文件和 App捆绑结构的一些Apple文档 .

另外,这是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指向启动脚本而不是先前的可执行文件.

好的,现在都完成了.幸运的是,一旦你知道所有这些东西,你就把它埋在一个构建脚本中.

  • +1.然而,僵尸狗吓坏了我.你能用一张不那么疤痕的图片来说明DLL地狱吗?(更不用说术语"DLL hell"不适用.分发动态库的首选方法是通过避免大多数问题的框架.但是,.dylib方法对UNIX风格的库很有用.) (7认同)

Chr*_*ris 18

我实际上找到了一个非常方便的工具,值得一些信任...不 - 我没有发展这个;)

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

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

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


F'x*_*F'x 8

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

  • 好吧,有了这个,你只需要在你的生活中使用一次XCode :)或者让别人为你做这件事. (5认同)
  • 这个问题明确地询问如何在没有xcode的情况下做到这一点.我在同一条船上,想要一个真正的答案. (3认同)

小智 6

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

"它适用于我的电脑"......我在小牛队的多个应用中使用它...

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/icon_16x16@2x.png
    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/icon_32x32@2x.png
    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/icon_128x128@2x.png
    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/icon_256x256@2x.png
    sips -z 512 512   macosx/$(APPNAME)Icon.png --out macosx/$(APPNAME).iconset/icon_512x512.png
    cp macosx/$(APPNAME)Icon.png macosx/$(APPNAME).iconset/icon_512x512@2x.png
    iconutil -c icns -o macosx/$(APPNAME).icns macosx/$(APPNAME).iconset
    rm -r macosx/$(APPNAME).iconset
Run Code Online (Sandbox Code Playgroud)

的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>
Run Code Online (Sandbox Code Playgroud)

PKGINFO

APPLMyAp
Run Code Online (Sandbox Code Playgroud)