跳至主要内容

Mac dylib动态库加载路径问题(以OpenCV为例)

在自己的Mac上写了一个基于OpenCV的简单程序;需要传给其他人共同调试,但是可执行文件在他人的Mac上无法运行;执行时会提示:
dyld: Library not loaded: /usr/local/Cellar/opencv/2.4.12/lib/libopencv_features2d.2.4.dylib
这样就引申出来一个问题:在xcode下编译出的程序,在开发机器上运行是没有问题的。但是给其他用户用,就可能出问题。因为用户不一定有这个库。
有两种方法可以解决这个问题;一是给其他用户也安装依赖的库文件;二是将所有的dylib随行发布,消除依赖。
第一种方案不考虑,大部分时候这样做并不现实;下面说说如何随行发布dylib。
单纯将依赖的dylib文件拷贝到可执行文件目录下一同传输过去是不能消除依赖的;执行的时候还是报错;
在编译一个动态库的时候, 你需要指定 INSTALL_PATH. 也就是它的安装路径;编译完成后如果一个可执行程序使用了该动态库, 那么在编译可执行程序的时候, 动态库的 INSTALL_PATH 会被记录到可执行程序中, 用来定位这个动态库。
因此我们首先需要将用到的dylib文件都拷贝到可执行文件目录下,然后改变动态库的INSTALL_PATH;将其改到可执行文件所在目录;
需要注意的是:如果依赖多个动态库,用到的动态库已会依赖其他动态库,因此用到的所有的动态库的依赖动态库路径都需要修改。
以OpenCV为例子,假设最终编译出来的可执行文件为 macimgproc;执行命令:otool -L macimgproc可看到如下的输出:
macimgproc:
    /usr/local/opt/opencv/lib/libopencv_calib3d.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_contrib.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_core.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_features2d.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_flann.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_gpu.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_highgui.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_imgproc.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_legacy.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_ml.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_nonfree.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_objdetect.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_ocl.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_photo.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_stitching.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_superres.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_video.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_videostab.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /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)
说明macimgproc依赖所有的OpenCV动态库文件;因此首先需要将所有动态库文件拷贝到macimgproc所在目录,然后需要将macimgproc文件中的所有/usr/local/opt/opencv/lib/libopencv_xxx修改为@executable_path/libopencv_xxx
@executable_path表示可执行文件所在目录;指示所有OpenCV动态库从可执行文件所在目录查找;
更多@executable_path的介绍以及其他变量参见文章 @rpath, @loader_path, @executable_path
使用命令install_name_tool -change {old.dylib} {new.dylib} {filename}修改动态库的INSTALL_PATH,例如:
install_name_tool -change /usr/local/Cellar/opencv/2.4.12/lib/libopencv_flann.2.4.dylib @executable_path/libopencv_flann.2.4.dylib macimgproc
执行后重新otool -L macimgproc可看到如下的输出:
macimgproc:
    ...
    /usr/local/opt/opencv/lib/libopencv_features2d.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    @executable_path/libopencv_flann.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    /usr/local/opt/opencv/lib/libopencv_gpu.2.4.dylib (compatibility version 2.4.0, current version 2.4.12)
    ...
依次修改所有依赖即可。
整个OpenCV库大概有19个dylib文件,因此写了一个简单的批量修改脚本:
#!/usr/bin/ruby

Preview=0

def fix_opencv_lib_link(file,rlib)
    if(rlib.include?('libopencv'))
        name = rlib.split('/').last
        cmd = "install_name_tool -change #{rlib} @executable_path/#{name} #{file}"
        if Preview == 1
            puts "Preivew: #{cmd}"
        else
            `#{cmd}`
        end
    else 
        puts "ignore rlib: #{rlib}";
    end
end

def fix_file_rely_lib(file)
  puts "===============start change #{file}==============="
  linklibs = `otool -L #{file}`.split("\n")
  linklibs.delete_at(0)
  linklibs.each_with_index do |rlib,i|
    rlib = rlib.split()[0]
    fix_opencv_lib_link(file,rlib)
  end
end

def doopencvlist
  # puts "Preview: #{Preview}"
  `ls |grep libopencv`.split().each_with_index do |file,i|
    fix_file_rely_lib(file)
  end
end

def viewlib(file)
    puts `otool -L #{file}`
end

if __FILE__ == $0
  if ARGV[0] == 'preview'
    Preview = 1;
    doopencvlist();
  elsif ARGV[0] == 'view'
    `ls |grep libopencv`.split().each_with_index do |file,i|
      # puts "===============start view #{file}==============="
      viewlib(file)
    end
  elsif ARGV[0] == 'do'
    doopencvlist();
  elsif ARGV[0] == 'previewfile'
    Preview = 1;
    fix_file_rely_lib(ARGV[1])
  elsif ARGV[0] == 'file'
    fix_file_rely_lib(ARGV[1])
  else
    puts "please input command [preview,do,previewfile,file,view] "
  end
end

参考链接

http://blog.csdn.net/openglnewbee/article/details/17783909
如何使用第三方的dylib
@rpath, @loader_path, @executable_path
Apple Developer:Overview of Dynamic Libraries
Apple Developer: Run-Path Dependent Libraries


作者:DeanWang
链接:https://www.jianshu.com/p/cbfe2e74230a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论

此博客中的热门博文

ubuntu 添加root登录

Login to your server as root. As the root user, edit the sshd_config file found in  /etc/ssh/sshd_config : vim /etc/ssh/sshd_config ( For details on working with Vim check out our article here !) Add the following line to the file, you can add it anywhere but it’s good practice to find the block about authentication and add it there. PermitRootLogin yes Save and exit the file. Restart the SSH server: systemctl restart sshd or service sshd restart