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)



