一、工程中遇到的问题
公司的同事提交了一个分支合并的请求,做为验证者需要更新下分支并进行编译、运行,确保相关的功能已经实现。好,问题就出在这时候。下载最新的分支,切换到其提交合并的分支上去,一切都很正常。编译,正常。就忙着干别的去了,结果一会回来一看,好,链接错误,-lwrap找不到。
脑袋有点没转过来,刚才还编译过一次呢?不会假日综合症吧。
二、解决过程
首先想到的是恢复到主分支,是不是分支引入的问题。于是迅速的切回主分支,删除缓存文件,重新再次编译,等待的结果和前面一样,库wrap找不到。这个非常奇怪,上午编译了好几次,都没有问题,为什么中午这个分支就开始出毛病呢?
然后去网上搜索,发现也没啥可用的。问了下AI,结果也那样。然后尝试着把wrap库去除,再编译,结果又报另外一个库依赖它,而那个库是开发中必须用到的。这下没办法了,只能解决这个问题了。直接用命令查看系统是否安装:
1、ldconfig -p | grep libwrap
显示:
libwrap.so.0 (libc6,x86-64) => /lib/x86_64-linux-gnu/libwrap.so.0
2、dpkg -l |grep libwrap*
ii libwrap0:amd64 7.6.q-31build2 amd64 Wietse Venema's TCP wrappers library
都有啊。而现在必须使用的网络库有源码,于是又把这个网络库的源码找到,拷贝到一个新的文件夹中,再次编译,结果大出意外,它竟然顺利完成编译和链接,要知道这个库也是需要wrap的,就是因为它使用,所以才会有上面的编译错误。
于是怀疑是不是版本的原因,然后重新安装:
sudo apt install libwrap0
结果仍然编译无法通过,重启了电脑,还是不行。有点晕菜。又怀疑是不是Ninja编译的原因,改回来直接使用默认的CMake的方法 ,仍然不行,都是报那个库找不到。
这时突然想到,是不是开发库的问题,就试着再安装:
sudo apt install libwrap0-dev
安装成功后,试着再编译,成功了。
三、分析原因
本身这个库一定是安装好的,否则不会这么长时间不能使用。那为什么突然中午就无法使用了呢?冷静下来仔细想了想,想到了问题。早晨来了,对工程进行了编译,没问题。更新后又编译了至少一次也没有问题。然后就去写文档,写着的时候,打开火狐搜索了点资料,火狐突然弹窗说版本太低,以后就不能用了。然后就开始各种情况的想更新,都没有成功。这时想到是不可以用命令来搞定,就使用apt安装了一个新的火狐。此时仍然没有问题,但安装后火狐仍然不能把原来的老版本的相关标签什么的搞过来,就放弃了。
重点来了,为了能自动升级到更新的火狐,就开始按照说明对apt进行更新:
apt update
apt upgrade
大家都知道前者是更新信息,后者根据前者来更新软件。原来吃过升级软件这个亏(主要是升级后库版本不匹配),所以第二个命令执行后说有大约一百多个软件近十G的数据可更新,果断拒绝。但这时候看到有个提示,大概意思“你的电脑上有几个库已经不再使用,可以使用sudo apt autoremove删除它”。当时觉得没用的库还不删除它,就直接运行了这个命令。然后就接着写文档了。
再后来,就是同事的分支合并出来的上面的情况。应该是在删除无用的库时,将libwrap0-dev给删除了。当时真没看到列表中有这个库,估计如果不是眼睛花了,就是有库依赖它没用就一起删除了。但看这个命令本身,并不会删除被标记手动安装的依赖库,不知道为什么会删除。
四、dev库和非dev库
从上面的修正过程可以看到,对非dev库的libwrap0找到并且在依赖其的网络库中编译成功,而且还重装了一次。为什么那个网络库可以编译成功,自己的工程却不可以呢?打开网络库源码,对照着其Readme文档,发现了忽略的部分,它上面说可能有些版本的libwrap0的头文件tcpd.h与其并不兼容。所以它自己封装了一下。果然再安装成功好dev版本时,那个封装的C文件相关部分被宏定义打开了,大意了。
那么引出本文的主要问题,dev库和非dev库有什么不同呢?即上面的情况,为什么安装dev库后就可以正常编译了呢?一般来说在dev库中会包含下面的几种情况:
1、 头文件
2、 链接库
3、静态库
4、配置文件
当然,不同的库还是有着不同的风格,有的可能还带有一些文档和示例代码之类的东西,以后大家看到得多了就会明白。
用命令看了下二者的不同:
非dev库:
dpkg -L libwrap0
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libwrap.so.0.7.6
/usr/share
/usr/share/doc
/usr/share/doc/libwrap0
/usr/share/doc/libwrap0/README.Debian
/usr/share/doc/libwrap0/README.gz
/usr/share/doc/libwrap0/changelog.Debian.gz
/usr/share/doc/libwrap0/copyright
/usr/share/man
/usr/share/man/man5
/usr/share/man/man5/hosts_access.5.gz
/usr/share/man/man5/hosts_options.5.gz
/usr/lib/x86_64-linux-gnu/libwrap.so.0
/usr/share/man/man5/hosts.allow.5.gz
/usr/share/man/man5/hosts.deny.5.gz
dev库:
dpkg -L libwrap0-dev
/.
/usr
/usr/include
/usr/include/tcpd.h
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libwrap.a
/usr/share
/usr/share/doc
/usr/share/man
/usr/share/man/man3
/usr/share/man/man3/hosts_access.3.gz
/usr/lib/x86_64-linux-gnu/libwrap.so
/usr/share/doc/libwrap0-dev
/usr/share/man/man3/hosts_ctl.3.gz
/usr/share/man/man3/request_init.3.gz
/usr/share/man/man3/request_set.3.gz
除去无关的文档等,发现主要不同的头文件和库的形式,一个是动态库一个二者皆有。用命令查看一下其动态库:
ls -al|grep libwrap.so
lrwxrwxrwx 1 root root 12 3月 25 2022 libwrap.so -> libwrap.so.0
lrwxrwxrwx 1 root root 16 5月 5 2023 libwrap.so.0 -> libwrap.so.0.7.6
-rw-r--r-- 1 root root 44872 3月 25 2022 libwrap.so.0.7.6
动态库指向是一个文件,所以区别应该是在静态库这个库了。程序应该是链接的静态库,所以报找不到这个库。大家有兴趣可以用objdump -p,readelf -a 或nm 或ldd等命令去查看相关的依赖函数和库,会更清晰。
最后说明一下:在不同的Linux系统上可能dev库的命名略有不同,大家一定要注意。
五、总结
这个小故事耽误了不到两个小时,主要是原始网络库编译成功而自己的项目不能使误导了方向。但回过头来再看,发现国外做开发的人真得想得周到。只能说自己解决问题的想法还是不够严谨,也算是一次对自己的反思吧!