CMake库搜索函数居然不搜索LDLIBRARYPATH
摘要:本文通过编译后运行找不到库文件的问题引入,首先分析了findpackage(JNI)的工作流程,而后针对cmake不搜索LDLIBRARYPATH的问题,提出了一种通用的解决办法。
本文分享自华为云社区《CMake库搜索函数居然不搜索LDLIBRARYPATH?由编译工具使用体验而引发的思考云社区华为云》,作者:蜉蝣与海。
最近产品要使用JNI技术,CMake编译C代码时需要对外链接libjvm。so库。代码编译倒是正常,系统中也有libjvm。so,然而使用时却报了如下异常:errorwhileloadingsharedlibraries:libjvm。so:cannotopensharedobjectfile:Nosuchfileordirectory
这个报错表示,操作系统并没有找到libjvm。so,我们的操作系统是从LDLIBRARYPATH中搜索这些动态链接库,很显然目前libjvm。so并不在这个目录下。
问题的解决倒是简单,直接在LDLIBRARYPATH里加入libjvm。so的库即可。但是这却引发了我的思考:为什么构建时可以找到libjvm。so,运行时却找不到呢?
这个问题的回答,既可以有简明扼要版解释,又可以刨根问底深挖。
先来看简明扼要版解释:
代码的CMakeList中使用了下列语句,在编译过程中寻找并链接libjvm。so,这个搜索方式和操作系统的搜索方式不同:findpackage(JNI)getfilenamecomponent(JVMLIBPATH{JAVAJVMLIBRARY}DIRECTORY)getfilenamecomponent(JAVALIBPATH{JVMLIBPATH}DIRECTORY)linkdirectories({JVMLIBPATH}{JAVALIBPATH})settargetproperties({NAME}PROPERTIESLINKFLAGSljvm)
其中findpackage(JNI)会搜索libjvm。so可能存在的路径,通过getfilenamecomponent来获得libjvm。so的文件夹,并把这个文件夹设为默认搜索库路径。而后settargetproperties会进行链接工作。
这个答案只能告诉我们是什么,但是作为一只程序猿,还要了解为什么,这里引申几个问题讨论:1、findpackage(JNI)的工作过程是怎样的?为什么LDLIBRARYPATH里没找到的依赖库,cmake可以找到2、cmake的库搜索函数findlibrary会搜索LDLIBRARYPATH吗,如果不会,可以通过设置来搜索LDLIBRARYPATH吗?问题一:findpackage(JNI)的工作过程是怎样的
为了方便开发者引用外部包,cmake官方预定义了许多寻找依赖包的Module,他们存储在cmake的sharecmakeModules目录下。每个以Find。cmake命名的文件都可以帮我们找到一个包〔1〕。在本地计算机执行以下指令,即可找到findpackage(JNI)使用的脚本文件。findnameFindJNI。cmake
打开自己的cmake对应的FindJNI文件,可以看到密密麻麻的注释和脚本,通过阅读这些脚本,我们得以得知FindJNI是如何工作的。
分析问题前,先看问题带来的结果,文件最上方注释有如下说明:Thismodulesetsthefollowingresultvariables:JNIINCLUDEDIRStheincludedirstouseJNILIBRARIESthelibrariestouse(JAWTandJVM)JNIFOUNDTRUEifJNIheadersandlibrarieswerefound。CacheVariablesThefollowingcachevariablesarealsoavailabletosetoruse:JAVAAWTLIBRARYthepathtotheJavaAWTNativeInterface(JAWT)libraryJAVAJVMLIBRARYthepathtotheJavaVirtualMachine(JVM)libraryJAVAINCLUDEPATHtheincludepathtojni。hJAVAINCLUDEPATH2theincludepathtojnimd。handjniport。hJAVAAWTINCLUDEPATHtheincludepathtojawt。h
这段代码表明,执行findpackage(JNI)之后,会有一系列变量被设置,其中包括表示JNI是否被找到的变量JNIFOUND,以及表示libjvm。so的变量JAVAJVMLIBRARY。这些变量在设定之后,通过FindPackageHandleStandardArgs导出,返回调用处,FindPackageHandleStandardArgs是cmake专门用来导出变量的宏〔2〕:include({CMAKECURRENTLISTDIR}FindPackageHandleStandardArgs。cmake)FINDPACKAGEHANDLESTANDARDARGS(JNIDEFAULTMSGJAVAAWTLIBRARYJAVAJVMLIBRARYJAVAINCLUDEPATHJAVAINCLUDEPATH2JAVAAWTINCLUDEPATH)
在文件中定位JAVAJVMLIBRARY,可以追踪到下述代码片段:foreach(search{JNISEARCHES})findlibrary(JAVAJVMLIBRARY{JNI{search}JVM})findlibrary(JAVAAWTLIBRARY{JNI{search}JAWT})if(JAVAJVMLIBRARY)break()endif()endforeach()
由此可知,JAVAJVMLIBRARY这个变量,是通过逐个搜索{JNI{search}JVM}里的文件夹进而确定JAVAJVMLIBRARY的。而{JNI{search}JVM}相关的定义语句如图:set(JNIFRAMEWORKJVMNAMESJavaVM)set(JNINORMALJVMNAMESjvmPATHS{JAVAJVMLIBRARYDIRECTORIES})
其中JAVAJVMLIBRARYDIRECTORIES中涉及了大量可能的libjvm。so存在的路径。set(JAVAJVMLIBRARYDIRECTORIES)foreach(dir{JAVAAWTLIBRARYDIRECTORIES})list(APPENDJAVAJVMLIBRARYDIRECTORIES{dir}{dir}client{dir}serverIBMSDK,JavaTechnologyEdition,specificpaths{dir}j9vm{dir}default)endforeach()set(JAVAAWTLIBRARYDIRECTORIES)if(JAVAHOME)JAVAAPPENDLIBRARYDIRECTORIES(JAVAAWTLIBRARYDIRECTORIES{JAVAHOME}jrelib{libarch}{JAVAHOME}jrelib{JAVAHOME}lib{libarch}{JAVAHOME}lib{JAVAHOME})endif()JAVAAPPENDLIBRARYDIRECTORIES(JAVAAWTLIBRARYDIRECTORIES{JNIJAVAAWTLIBRARYTRIES})foreach(javadirINLISTSJNIJAVADIRECTORIESBASE)list(APPENDJNIJAVAAWTLIBRARYTRIES{javadir}jrelib{libarch}{javadir}jrelib{javadir}lib{libarch}{javadir}lib{javadir})list(APPENDJNIJAVAINCLUDETRIES{javadir}include)endforeach()
如上图所示,变量依赖顺序如下:
JAVAJVMLIBRARYDIRECTORIESJAVAAWTLIBRARYDIRECTORIESJNIJAVAAWTLIBRARYTRIESJAVAHOMEJNIJAVADIRECTORIESBASE
最终发现JAVAJVMLIBRARYDIRECTORIES变量的值,是由JAVAHOME变量的值和JNIJAVADIRECTORIESBASE变量的值共同决定的。而JNIJAVADIRECTORYBASE预置了大量预定义路径:set(JNIJAVADIRECTORIESBASEusrlibjvmjavausrlibjavausrlibjvmusrlocallibjavausrlocalsharejavausrlibj2sdk1。4sunusrlibj2sdk1。5sunoptsunjdk1。5。0。04usrlibjvmjava6sunusrlibjvmjava1。5。0sunusrlibjvmjava6sun1。6。0。00canthisoneberemovedaccordingto8821?Alexusrlibjvmjava6openjdkusrlibjvmjava1。6。0openjdk1。6。0。0fedoraDebianspecificpathsfordefaultJVMusrlibjvmdefaultjavaArchLinuxspecificpathsfordefaultJVMusrlibjvmdefaultUbuntuspecificpathsfordefaultJVMusrlibjvmjava11openjdk{libarch}Ubuntu18。04LTSusrlibjvmjava8openjdk{libarch}Ubuntu15。10usrlibjvmjava7openjdk{libarch}Ubuntu15。10usrlibjvmjava6openjdk{libarch}Ubuntu15。10OpenBSDspecificpathsfordefaultJVMusrlocaljdk1。7。0usrlocaljre1。7。0usrlocaljdk1。6。0usrlocaljre1。6。0SuSEspecificpathsfordefaultJVMusrlib64jvmjavausrlib64jvmjre)
通过以上分析可以看出,JAVAJVMLIBRARY的搜索,依赖JAVAHOME和大量预定义路径。问题二:cmake库搜索函数findlibrary会搜索LDLIBRARYPATH吗
通过阅读DoesCMakesfindlibrarysearchLDLIBRARYPATH可以知道,findlibrary默认不搜索LDLIBRARYPATH,并且网上也找不到让cmake搜索LDLIBRARYPATH的文章。那cmake能搜索LDLIBRARYPATH吗?
答案是可以的,通过cmake获取LDLIBRARYPATH环境变量,并转为cmake可理解的list格式,而后注入findlibrary即可,代码如下:string(REPLACE:;RUNTIMEPATHENV{LDLIBRARYPATH})findlibrary(JVMAPINAMESjvmHINTS{RUNTIMEPATH})if(JVMAPISTREQUALJVMAPINOTFOUND)message(WARNINGfoundlibjvm。soonlyin{JAVAJVMLIBRARY}butnotinLDLIBRARYPATH。environmentvariableLDLIBRARYPATHmustincludeitsdirectory。)endif()
如果希望找不到这个库时编译失败,可以将WARNING改为fatalerror,代码如下:string(REPLACE:;RUNTIMEPATHENV{LDLIBRARYPATH})findlibrary(JVMAPINAMESjvmHINTS{RUNTIMEPATH})if(JVMAPISTREQUALJVMAPINOTFOUND)message(FATALERRORfoundlibjvm。soonlyin{JAVAJVMLIBRARY}butnotinLDLIBRARYPATH。environmentvariableLDLIBRARYPATHmustincludeitsdirectory。)endif()小结
本文通过编译后运行找不到库文件的问题引入,首先分析了findpackage(JNI)的工作流程,而后针对cmake不搜索LDLIBRARYPATH的问题,提出了一种通用的解决办法。参考文献:
〔1〕Cmake之深入理解findpackage()的用法:https:zhuanlan。zhihu。comp97369704?utmsourcewechatsession
〔2〕Cmake中findpackage命令的搜索模式之模块模式(Modulemode):https:www。jianshu。compf983a90bcf91
〔3〕DoesCMakesfindlibrarysearchLDLIBRARYPATH?:https:stackoverflow。comquestions41566316doescmakesfindlibrarysearchldlibrarypath
点击下方,第一时间了解华为云新鲜技术
华为云博客大数据博客AI博客云计算博客开发者中心华为云
壮志凌云2不会在华上映!阿汤哥公然挑衅一中原则参考消息网6月6日报道英国每日电讯报网站6月4日发表题为中国在影院发起有力的爱国主义宣传的报道。报道称,随着电影壮志凌云2独行侠的上映,电影屏幕上再次充斥着咆哮的美国战斗机和勇敢又
FPX官宣Gori离队并加入PSGFPX官宣了关于Gori选手的离队公告。内容如下经过与俱乐部及选手充分友好的协商沟通,FPX电子竞技俱乐部英雄联盟分部选手金泰佑(IDGori)即日起正式加盟PSG电子竞技俱乐部。
做空中国?国际巨头这次栽了!行贿11国,操控油价!这才是真相?很多人不知道所谓的国际巨头是什么样的情况,今天我们就来说一说曾经做空中国的国际巨头嘉能可。国际大宗巨头嘉能可国际大宗巨头嘉能可嘉能可是名副其实的瑞士国际巨头,它成立于1974年,主
2022年四川考生报考军校就看这一篇参军卫国,是很多考生的人生梦想。今年受疫情持续影响,高校研究生扩招厉害,就业竞争压力大。近几年,随着军队建设的快速发展,军人社会地位和待遇的日益提高,考军校是相当部分家长与考生理想
罗建云东汽来了又走了,这对绵竹汉旺人来说犹如一场梦大炼钢铁无终自终。天池煤矿给汉旺带来新希望。川人川风一家亲。汉旺人成都人已融为一体。俗语肥肉上添膘,这句话放在汉旺这里很合适。鬼使神差,搞三线建设,哈尔滨汽轮机厂看中了山有万重水流
因麻将馆收费贵,2017年四川小伙发明纸牌麻将,有人出50万买专利如果问大家中国的国粹有哪些?那大家可能会有很多答案,京剧武术书法中药等等不一而足,可有一种国粹至今还深受千家万户的喜爱,几乎是全国男女老少在过年期间必不可少的一种东西。没错,那就是
众筹存款人人能科普,处处有新知本人有个想法,不知可不可行。众筹一亿元后跟银行谈更高的利息收益。启动方式是众筹一亿元,一万人即限制加入,每人一万元,全部存定期为五年(以银行的起始为准),每年
明日芒种,不管多忙!芒种3宝别忘吃,顺应节气,舒舒服服过夏天芒种是夏天的第3个节气,也是仲夏的开始。芒与忙同音,杏黄麦熟忙耕种,提醒人们要忙碌起来了。因为芒种,既是北方收麦的时节,也是南方种稻的时节。江南一带更是流传着芒种插得是个宝,夏至插
芒种风吹麦成浪蝉鸣夏始忙时光匆匆,节气更替,又到一年芒种时芒种,夏季的第三个节气标志仲夏正式开始月令七十二候集解五月节,谓有芒之种谷可稼种矣。农谚有云芒种芒种,连收带种这是一年中农事最
中安时评弘扬正能量,以中国好评汇聚昂扬奋进时代洪流近日,以新时代新征程新青年为主题的2022好评中国网络评论大赛在湖南长沙正式启动。活动旨在进一步推动网络评论高质量发展,大力弘扬时代主旋律传播网络正能量,引领广大网民与祖国同呼吸共
俄乌冲突第102天!普京罕见对乌克兰让步,25国抱团反对美国作者瞭望君俄罗斯与乌克兰的战争发生后,各种媒体就掐着算俄乌冲突第一天俄乌冲突第二天大家都在关注俄乌战争的局势,各样的信息扑面而来,难辨真假。不管真假,有一点可以说明,俄乌战争在很长