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

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

问题描述

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



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



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

解决方案


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




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




问题有时你不能。




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



免责声明:我完全没有资格写这篇文章,所有
都有从Apple文档中闪现出来,挑选现有的应用程序和
试用版和错误版。它适用于我,但很可能是错误的。如果您有任何更正,请
给我发电子邮件。



您应该知道的第一件事是应用程序包仅仅是一个目录。

让我们来看看假设foo.app的结构。

 
foo.app/
内容/
Info.plist
MacOS /
foo
资源/
foo.icns





Info.plist是一个普通的XML文件。
您可以使用文本编辑器或附带XCode的Property List Editor应用程序对其进行编辑。
(它位于/ Developer / Applications / Utilities /目录中)。




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



CFBundleName - 应用的名称。



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



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




有很多选项,上面列出的只是最低限度。
以下是关于
的一些苹果文档 Info.plist
文件和
应用程序包结构




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



 
<?xml version =1.0encoding =UTF-8? >
<!DOCTYPE plist PUBLIC - // Apple Computer // DTD PLIST 1.0 // ENhttp://www.apple.com/DTDs/PropertyList-1.0.dtd>
< plist version =1.0>
< dict>
< key> CFBundleGetInfoString< / key>
< string> Foo< / string>
< > CFBundleExecutable
foo
CFBundleIdentifier
com.your-company-name.www< / string>
CFBundleName< / key>
< string> foo< / string>
< key> CFBundleIconFile<
CFBundleInfoDictionaryVersion< / key>
6.0
CFBundlePackageType< / key>
< string> APPL< / string>
< key> IFMajorVersion< / key&
< integer> 0< / integer>
< key> IFM inorVersion< / key>
< integer> 1< / integer>
< / dict>
< / plist>
< / pre& b


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




如果您使用 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可执行文件名称
pre>




作为一个实际的例子,假设您的应用使用 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





确保您调整了信息.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天全站免登陆