构建 OSX 应用程序包 [英] Building OSX App Bundle

查看:31
本文介绍了构建 OSX 应用程序包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

解决方案

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

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

问题是有时你不能.

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

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

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

<前>foo.app/内容/信息表苹果系统/富资源/文件名

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

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

CFBundleName - 应用程序的名称.

CFBundleIcon - 假定位于 Contents/Resources 目录中的图标文件.使用 Icon Composer 应用程序创建图标.(它也在/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"><字典>CFBundleGetInfoStringFooCFBundleExecutable<string>foo</string>CFBundleIdentifier<string>com.your-company-name.www</string>CFBundleName<string>foo</string>CFBundleIconFile<string>foo.icns</string>CFBundleShortVersionString<字符串>0.01CFBundleInfoDictionaryVersion6.0CFBundlePackageTypeAPPLIFMajorVersion<整数>0IFMinorVersion<整数>1</dict>

在一个完美的世界中,您可以将可执行文件放入Contents/MacOS/dir 并完成.但是,如果您的应用程序有任何非标准 dylib 依赖项将不起作用.像 Windows、MacOS带有它自己的特殊类型 DLL Hell.

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

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

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

<前>otool -L 可执行文件名

此命令将列出您的应用程序依赖的所有 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/bashcd "${0%/*}"./foo

确保调整 Info.plist 文件,使 CFBundleExecutable 指向启动脚本而不是前一个可执行文件.

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

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.

解决方案

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.

这篇关于构建 OSX 应用程序包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆