https://www.jianshu.com/p/bea7cd3f80d2
- 应用程序包
大多数Mac系统的应用程序(任何以.app为后缀的文件)本身是一个特殊的文件夹,目的是为了让用户看起来可以仅通过双击就可以打开程序。这些“文件夹”包含所有,或至少大部分的文件来使程序得以运行,也就是说这些文件是被包含在其中的。这样,卸载这些程序只要把程序拖到垃圾筒中就行了。如果你在这些程序图标上control+左键(或右键)并且看到显示包内容,那么这就是应用程序包。
- 附加文件
程序会留下偏好设置文件,有些时候是application support(程序支持)文件,不管是哪一种都没有储存在应用程序包里(注意是“没有”)。偏好设置文件可以被安全的删除,但他们通常只占用极小的硬碟空间,而且如果你想重装这个程序的话,在偏好设置文件没有被删掉的情况下,你的一切设定都会保留。这些偏好设置文件被存在你用户名下的“资源库”文件夹内(~/资源库/Preference),或者同时被存在位于系统根目录下的系统大“资源库”文件夹中(/资源库/Preference)。
- Application Support(程序支持)
文件会占用从几KB到甚至几GB不等的硬碟空间,这取决于程序的类型。这种有很大的支持文件的程序像是多媒体软体这类,比如:Grageband和DVD Studio Pro。支持文件被存在你用户名下的“资源库”文件夹中的Application Support文件夹内,或者在系统根目录下的“/资源库/Application Support/”。
https://www.jianshu.com/p/7f1b50b502d3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

Mac系统动态库问题描述
现在假如有一个app应用canrunfile.app,他引用了一个/usr/lib下的系统库/usr/lib/libs.1.dylib,还引用了一个第三方库"libA.1.dylib".再假如这个libA.1.dylib动态库又引用了另一个第三方的动态库libB.2.dylib.
首先canrunfile.app在Mac系统下其实是一个目录,内部目录结构是:
canrunfile.app
/Contents
/Frameworks/这里没有文件
/MacOS/canrunfile // 这是可执行的二进制程序 /Resources/canrunfile.icns //应用程序的图标文件 /Info.plist //plist应用程序的说明文件
现在假如应用和三个动态库文件的路径在系统中安装的位置分别为:
~/Documents/canrunfile.app
/usr/lib/libS.1.dylib
/usr/local/lib/A/libA.1.dylib /usr/local/lib/B/libB.2.dylib
这种情况在开发电脑上程序可以运行的很好,但把程序打包发给别人使用的时候,因为别人的电脑上没有安装最后的两个第三方动态库,程序运行时就会报动态库加载错误。
查看这种动态库错误的方法是右键canrunlife.app然后进入到MacOS目录下双击运行二进制可执行文件,就可以看到程序运行时具体的错误是什么。
install_name_tool和otool工具说明
Mac系统下的app和dylib动太库引用的第三方动态库需要手动设置引用的库路径,用的工具就是install_name_tool
查看一个可执行文件或者动态库引用的第三方库路径则使用的工具是otool
对于上边的例子,我们查看canrunfile二进制文件引用的库路径
cd ~/Documents/canrunfile.app/MacOS otool -L canrunfile #只能查动态库dylib或者framework
运行上边命令后可以看到:
canrunfile:
/usr/lib/libS.1.dylib (compatibility version , current version)
/usr/local/lib/libA.1.dylib (compatibility version , current version)
这说明canrunfile可执行程序引用了上边的两个库
这时候我们把第三方库和第三方库引用的库都复制出来放到Frameworks目录下,然使用install_name_tool修改canrunfile中的动态库引用路径
install_name_install -change /usr/local/lib/libA.1.dylib @executable_path/../Frameworks/libA.1.dylib canrunfile #修改引用路径
再运行
otool -L canrunfile
会看到
canrunfile:
/usr/lib/libS.1.dylib (compatibility version , current version)
@executable_path/../Frameworks/libA.1.dylib (compatibility version , current version)
这时canrunfile的动态库引用就改好了,如果不修改
Frameworks/LibA.1.dylib
的引用路径就会报加载/usr/local/lib/libB.1.dylib错误
查看动态库引用的第三方动态库的方法也是
otool -L LibA.1.dylib
可以看到
LibA.1.dylib:
/usr/local/lib/libA.1.dylib #名字 /usr/local/lib/libB.1.dylib #路径
注意,这里为什么会有两个呢,其实这里的第一个是这个动态库的安装名称,也叫INSTALL Name,这个安装名称都是使用动态库路径表示的. 后边的才是这个动态库引用到的第三方库。
所以,修改第三方库引用路径的时候,还要修改这个install name.
使用install_name_tool 的id参数来修改这个install name:
cd ~/Documents/canrunfile.app/Frameworks sudo install_name_tool -id @executable_path/../Frameworks/libA.1.dylib libA.1.dylib #修改库的名字install name
注意,这里要加上sudo,要不然会出现权限错误问题,同时修改第三方库加载路径:
sudo install_name_tool -change /usr/local/lib/libB.1.dylib @executable_path/../Frameworks/libB.1.dylib libA.1.dylib
到这里第二个动态库就修改完成了,接下来还要修改第三个库的install name(这里不修改install name程序能不能运行,我没有试过,大家可以试一下)。
sudo install_name_tool -id @executable_path/../Frameworks/libB.1.dylib libB.1.dylib
到些动态库修改完成
最后的目录结构
canrunfile.app
/Contents
/Frameworks
/libA.1.dylib /libB.1.dylib /MacOS/canrunfile // 这是可执行的二进制程序 /Resources/canrunfile.icns //应用程序的图标文件 /Info.plist //plist应用程序的说明文件
参考
https://www.suninf.net/2015/06/xcode-build-and-paths-config.html
===================================
https://blog.csdn.net/liangzhao_jay/article/details/72732132
@rpath, @loader_path, @executable_path
在 OSX 上初次接触到这些变量, 有些晕. 看了一些文档之后, 觉得弄明白了. 做一个总结.
在编译一个动态库比如 libfoo.dylib 的时候, 你需要指定 INSTALL_PATH. 也就是它的安装路径.
鼠标悬浮到对应的变量选项上,会自动弹出对应的环境变量
一个可执行程序比如 bar.app 使用 libfoo.dylib, 那么在编译 bar.app 的时候, libfoo.dylib 的 INSTALL_PATH 会被记录到 bar.app 中, 用来定位这个 dylib. 用如下命令可以查看:
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
/usr/local/lib/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.0.0)
…
这里的 /usr/local/lib 就是默认的 INSTALL_PATH.
要 bar.app 能正常运行, 必须先把 libfoo.dylib 拷贝到这个目录.
如果 libfoo.dylib 只是被 bar.app 使用, 那么拷贝到系统目录可能是不合适的.
一个解决问题的办法就是修改 libfoo.dylib 的 INSTALL_PATH, 使用相对路径. 因而就需要用到下面这三个变量.
看 dyld 的 manual, 有这三个变量的解释.
@executable_path 这个变量表示可执行程序所在的目录. 比如 /path/bar.app/Contents/MacOS/ .
@loader_path 这个变量表示每一个被加载的 binary (包括可执行程序, dylib, framework 等) 所在的目录. 在一个进程中, 对于每一个模块, @loader_path 会解析成不用的路径, 而 @executable_path 总是被解析为同一个路径(可执行程序所在目录).
比如一个会被多个程序调用的 plugin, 位于 /path/Myfilter.plugin/Contents/MacOS/Myfilter, 依赖 /path/Myfilter.plugin/Contents/dylib/libfoo.dylib. 那么 libfoo.dylib 的 INSTALL_PATH 可以设置为 @loader_path/../dylib, 这样设置的话, 不论 Myfilter.plugin 目录放到什么位置, libfoo.dylib 都能正确的被加载.
@rpath 和前面两个不同, 它只是一个保存着一个或多个路径的变量. 比如 libfoo.dylib 被两个 .app 使用, 且被包含的路径不同, 如下:
bar.app/
Contents/
MacOS/
bar
libfoo.dylib
baz.app
Contents/
MacOS/
baz
dylibs/
libfoo.dylib
将 libfoo.dylib 的 INSTALL_PATH 设置成 @loader_path/.. 或 @loader_path/../dylibs 都只能满足其中一个 .app 的需求.
要解决这个问题, 就可以用 @rpath. 将 libfoo.dylib 的 INSTALL_PATH 设置成 @rpath, 然后在编译 bar.app, baz.app 时分别指定 @rpath 为 @loader_path/.., @loader_path/../dylibs, 问题得到了解决.
@rpath 的另一个优点是可以设置多个路径. 如果 bar.app 还需要使用另一个 .framework (假设它的 INSTALL_PATH 也设置成了 @rpath), 位于 @loader_path/../frameworks, 把这个路径加到 @rpath 即可.
道理说清楚了, 怎么设置这些变量呢?
- 在 libfoo.dylib 的项目属性中设置 INSTALL_PATH 设置为 @rpath
- 在 bar.app 中设置 @rpath 为 @loader_path/.. @loader_path/dylibs
对于一个编译好的 binary, 有几个命令行工具可以查看, 修改 rpath:
查看 @rpath
$ otool -l bar.app/Contents/MacOS/bar
...
Load command 19
cmd LC_RPATH
cmdsize 32
path @loader_path/.. (offset 12)
Load command 20
cmd LC_RPATH
cmdsize 40
path @loader_path/../dylibs (offset 12)
…
install_name_tool 还可以删除/替换/添加 @rpath, 比如:
$ install_name_tool -add_rpath @loader_path/../frameworks bar.app/Contents/MacOS/bar
它还可以修改依赖的动态库的加载路径, 比如:
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
@rpath/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
…
$ install_name_tool -change @rpath/libfoo.dylib @loader_path/libfoo.dylib bar.app/Contents/MacOS/bar
$ otool -L bar.app/Contents/MacOS/bar
bar.app/Contents/MacOS/bar:
@loader_path/libfoo.dylib (compatibility version 1.0.0, current version 1.0.0)
...
看看 otool, install_name_tool 的帮助了解这几个命令的细节
、=================================
http://www.tanhao.me/pieces/1361.html/
http://www.cnblogs.com/csuftzzk/p/mac_run_path.html
@executable_path 这个变量表示可执行程序所在的目录. 比如 /path/QQ.app/Contents/MacOS/
@loader_path 这个变量表示每一个被加载的 binary (包括App, dylib, framework,plugin等) 所在的目录.
在一个程序中, 对于每一个模块, @loader_path 会解析成不用的路径, 而 @executable_path 总是被解析为同一个路径(可执行程序所在目录).
比如一个会被多个程序调用的 plugin, 位于 /path/Flash Player.plugin/Contents/MacOS/Flash Player, 依赖 /path/Flash Player.plugin/Contents/Frameworks/XPSSO.dylib.
那么 XPSSO.dylib 的 INSTALL_PATH 可以设置为 @loader_path/../Frameworks, 这样设置的话, 不论 Flash Player.plugin 目录放到什么位置, XPSSO.dylib 都能正确的被加载.
@rpath 和前面两个不同, 它只是一个保存着一个或多个路径的变量. 比如 XPSSO.dylib 被两个 .app 使用, 且被包含的路径不同。
比如:
softA.app/Contents/MacOS/dylib/XPSSO.dylib
softB.app/Contents/MacOS/Frameworks/XPSSO.dylib
将 XPSSO.dylib 的 INSTALL_PATH 设置成 @loader_path/../dylib 或 @loader_path/../Frameworks 都只能满足其中一个 .app 的需求.
要解决这个问题, 就可以用 @rpath.
- 将 XPSSO.dylib 的 INSTALL_PATH 设置成 @rpath,
- 然后在编译 softA.app, softB.app 时分别指定 @rpath 为 @loader_path/../dylib, @loader_path/../Frameworks, 问题得到了解决.
@rpath 的另一个优点是可以设置多个路径. 如果 softA.app 还需要使用另一个 .plugin (假设它的 INSTALL_PATH 也设置成了 @rpath), 位于 @loader_path/../plugin, 把这个路径加到 @rpath 即可.
XPSSO.dylib的Build Settings中设置Installation Directory
在 softA.app或softB.app 中设置 Runpath Search Paths(对应了@rpath)