解决库依赖,让你无需每次都把库文件拷贝到系统文件夹。

解决MAC运行时库依赖报错问题

本文只探讨运行时出错,编译不通过请自行google解决。

1、库依赖报错有几种,这里只介绍动态连接库 *.dylib、框架*.framwork,其余的依赖报错解决办法类似。

2、找出库依赖

xcode会报出库依赖的错误,但是可能不够详细。

命令行输入 otool -L  <object file>可以查询可执行文件所依赖的库。

例:(为了节省时间,大神们可以只看小标题)

1)生成你的app

打开xcode,编译、运行出错的工程,由于找不到依赖的库,运行时会卡住,这时候不管它,先停止运行。

2)找到该app文件中的可执行文件。

xcode左侧找到Products文件夹,选中   *.app文件(*代表你生成的产品名称) ->鼠标右键单击它,选中Show in finder->在打开的文件夹中,右键点击 *.app 文件,选中“显示包内容” -> 打开“Contents文件夹,->打开 MacOS 文件夹 ->里面住着一位(有可能多位)黑不溜秋,略带绿色荧光的家伙,它就是可执行文件了。

3)查找依赖的库

    otool -L 该可执行文件

  打开终端, 输入: otool -L [空格]

 注意, -L后面输入一个空格,然后把刚刚找到的可执行文件拖到终端来。

 拖完之后,终端显示类似的信息:

  otool -L /Users/userName/Library/Developer/Xcode/DerivedData/Bluepoint-fapwtpqiamwycgdslbokwedxpcss/Build/Products/Debug/Bluepoint.app/Contents/MacOS/Bluepoint 

 按回车开始查找。

 例子中找到如下信息:

/usr/local/lib/libPowerMeterLib.dylib (compatibility version 1.0.0, current version 1.0.0)

/Library/Frameworks/LuCamSDK.framework/Versions/A/LuCamSDK (compatibility version 1.0.0, current version 1.0.0)

@rpath/BeamProfileKit.framework/Versions/A/BeamProfileKit (compatibility version 1.0.0, current version 1.0.0)

libSMC6490_Lib.dylib (compatibility version 0.0.0, current version 0.0.0)

/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1349.25.0)

/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)

/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 307.4.0)

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)

/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit(compatibility version 45.0.0, current version 1504.75.0)

/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation(compatibility version 150.0.0, current version 1348.28.0)

每一行就是一个依赖项,

现在我来科普一下每行的内容。

第一行,/usr/local/lib/libPowerMeterLib.dylib(compatibility version 1.0.0, current version 1.0.0)

,后面括号里面的内容是版本信息,暂时不用管它。括号前面是一个文件的绝对路径,这个文件是一个.dylib结尾的文件,这是一个动态连接库(类似Windows里面的 dll )。

哎,问题就在这里,这个绝对路径是库的开发者有意无意设定的,运行时,Xcode会去该路径查找该文件,找不到该文件就报错了。聪明的小伙伴们已经想到了,工程里面是有这个文件的,(不然编译的时候极有可能报错,为什么不是必然报错?后面有机会再讲  。)既然有这个文件,我们告诉xcode去哪里找这个东西就OK了。


第二行,它依赖 /Library/Frameworks/ 下的 LuCamSDK.framework文件, LuCamSDK.framework下的可执行文件路径是/Versions/A/LuCamSDK,

这是一个可执行文件依赖框架文件的栗子。


第三行同是一个框架文件依赖,只是路径是 @rpath下的 BeamProfileKit.framework文件。 

@rpath 是一个保存了多个路径的变量,可以用编译器指定,这样在运行时,会遍历多个路径,直至找到存放指定文件为止。


第四行,libSMC6490_Lib.dylib ,木有路径?这个没关系,没有路径的,默认在/usr/local/lib/,只是修改依赖路径的时候,old 参数需直接传  libSMC6490_Lib.dylib


第五行及以下行是系统库依赖,如果工程没有对系统库进行修改,请不要更改依赖路径,

还有些情况,就是如果用到了跟目前系统版本不同的库,也可以通过修改库路径来兼容不同版本的操作系统。

3、修改依赖路径

MAC下,修改可执行文件依赖路径的命令是:

install_name_tool [-change old new] input

举个栗子:

假如一个可执行文件e1e1在电脑上的/pathC(完整路径是:  /pathC/e1  (就是input))

它原来依赖  /usr/local/lib/下面的 A.dylib文件 ,(完整路径是:  /usr/local/lib/A.dylib(也是old))

可是,我电脑上的A.dylib文件在目录/pathB,我要把e1的依赖修改成  /pathB/A.dylib   new

完整的命令如下:

install_name_tool -change /usr/local/lib/A.dylib  /pathB/A.dylib  /pathC/e1 

改完之后,可以 otool -L  /pathC/e1 看一下改好没有。

下面我们就可以来修改依赖路径了^_^

等一下!!!我们每次编译,都会重新生成.app文件的,现在修改了依赖路径,如果重新编译它会不会(。 ́︿ ̀。) 

还有,我们工程里面有这个文件,但是编译之后,我们要把.app安装到别人的电脑上,别人的电脑没有这个工程,当然也不会存在这个文件,会不会运行报错(。 ́︿ ̀。)


为了解决以上问题,我们可以考虑

1、在每次生成.app之后修改依赖路径

2、把依赖的文件,从工程里拷贝打包到.app文件里面。


由于修改路径需要该被依赖文件事先存在,我们把顺序调换一下:

1、把依赖的文件,从工程里拷贝打包到.app文件里面。

2、在每次生成.app之后修改依赖路径。


xcode打包时,可以自动把一些文件拷贝到.app里面,具体做法是在 target Build Phases里面点击上方的 +号,选择 New Copy File Phase,增加一个文件拷贝字段,xcode在编译完成之后,会自动拷贝字段里面提到的文件。


为了规范,我们把需要的框架文件拷贝到 Contents文件夹下面的Framworks文件夹里面。

把需要的动态连接库拷贝到Contents文件夹下面的Dylib文件夹里面。

1New Copy File Phase->修改Destination Framworks ->点击下方的 +号,选择需要拷贝的*.framwork文件。 

xcode会自动建立一个 Framworks文件夹,并把需要的文件都拷贝到里面来。

2New Copy File Phase  ->修改Destination Wrapper, SubPathContents/Dylib,->选择需要拷贝的 *.dylib文件。


3 New Run Script Phase ->填上如下shell代码:

install_name_tool -change /usr/local/lib/libPowerMeterLib.dylib @executable_path/../Dylib/libPowerMeterLib.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"

install_name_tool -change libSMC6490_Lib.dylib @executable_path/../Dylib/libSMC6490_Lib.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"


install_name_tool -change /Library/Frameworks/LuCamSDK.framework/Versions/A/LuCamSDK @executable_path/../Frameworks/LuCamSDK.framework/Versions/A/LuCamSDK "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"


 install_name_tool -change /Library/Frameworks/LuCamSDK.framework/Versions/A/LuCamSDK @rpath/LuCamSDK.framework/Versions/A/LuCamSDK "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/BeamProfileKit.framework/Versions/A/BeamProfileKit"


注意,每个参数之间有空格,每句命令之间用回车分隔即可。


4)然后就可以啦,编译运行,一切正常。

解释一下:

第一句脚本,  

/usr/local/lib/libPowerMeterLib.dylib 对应 old

@executable_path/../Dylib/libPowerMeterLib.dylib 对应new

"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"对应input

old没什么好说的,原来的依赖路径,

new这里引用了一个 @executable_path变量,这个是指工程启动的执行文件所在的路径,一般固定为 XXX/*.app/Contents/MacOS/,这个路径是运行的时候产生的。

input 这里引用了两个变量, $TARGET_BUILD_DIR   $PRODUCT_NAME 。顾名思义,这两个变量分别是生成目标(.app)的目录,和(.app)的名称。

意思就是把app里入口可执行文件的  /usr/local/lib/libPowerMeterLib.dylib依赖路径改成

入口可执行文件的上一层目录的/Dylib/libPowerMeterLib.dylib目录。 ..)是上一层的意思。


第二第三句没什么说的,


第四句, input"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Frameworks/BeamProfileKit.framework/Versions/A/BeamProfileKit"

为什么要添加这句呢?

因为BeamProfileKit.framework里面的可执行文件BeamProfileKit也依赖了LuCamSDK,如果不修改依赖,也会引起报错。(每个框架文件里面也有一个可执行文件,它有可能依赖别的库)。

终于写完了,写得又长又臭,人家说如果不能用一句话把事情描述清楚的话,可能是你的理解还不到位。好吧,如果用一句话来说,它就是打包你库,修改库依赖路径。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值