今天在Fedora20系统上编译VPP项目时遇到了一个问题,在最终编译并且使用rpmbuild生成的rpm安装包不正确,我们需要对生成rpm包的spec文件做一些小的修改。
首先我们来看看VPP自带的生成rpm包的spec描述文件,该文件为${VPP_TOPDIR}/build-root/rpm/vpp.spec, 我们可以打开看看该文件的内容:
其中在%install 段里面有如下的命令
1 #
2 # libraries
3 #
4 mkdir -p -m755 %{buildroot}%{_libdir}
5 for file in $(find %{_vpp_install_dir}/*/lib* -type f -name ‘*.so.*.*.*‘ -print )
6 do
7 install -p -m 755 $file %{buildroot}%{_libdir}
8 done
9 for file in $(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e ‘s/^\.\///‘)
10 do
11 # make lib symlinks
12 ( cd %{buildroot}%{_libdir} &&
13 ln -fs $file $(echo $file | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) )
14 done
这里首先在%{buildroot}目录里面创建lib目录,然后调用install命令将编译出来的所有的VPP项目的lib文件拷贝到%{buildroot}目录里面的lib目录里面。
现在我们来看9~14行的一个 for循环命令,该命令主要是完成这样的一个功能:将%{buildroot}目录里面的lib目录里面的所有的库文件建立一个软链接文件。
比如:假如现在在%{buildroot}目录里面的lib目录里面有一个库文件libvnet.so.18.0.0, 那么上述for循环完成之后就在同一目录下建立了一个软链接文件libvnet.so.18并指向libvnet.so.18.0.0库文件。
现在我们来一步步的分析上面的9~14行的这个for循环语句的工作过程,我们假设在/usr/lib64/目录下有2个库文件,一个是libvpp.so.0.0.0, 一个是libvnet.so.18.0.0.
首先我们来看第9行的for语句后面的一个shell命令:
$(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e ‘s/^\.\///‘)
那么等价于执行下面的一条命令:
$(cd /usr/lib64/ && find . -type f -print | sed -e ‘s/^\.\///‘)
这条命令是两条命令组合而成,首先执行cd /usr/lib64/切换工作目录到/usr/lib64/目录下,然后执行后续的find命令。
随后执行 find . -type f -print 命令的结果如下:
./libvpp.so.0.0.0
./libvnet.so.18.0.0
这里find命令的查找输出结果前面都带有路径的。然后将find命令查找的输出结果通过管道重定向到sed命令中,再来分析后面的sed命令。
sed -e ‘s/^\.\///‘: 查找行首以./开头的行并将./替换为空, 这里^表示匹配行首, 后面有两个转义符分别转义"点(.)"和"斜杠(/)", s动作表示查找替换
那么经过sed命令处理之后的find查找结果就变为如下的结果了:
find . -type f -print | sed -e ‘s/^\.\///‘ 经过sed处理后的find查找结果全部去掉了前面的路径了,该命令最终结果如下了:
libvpp.so.0.0.0
libvnet.so.18.0.0
至此,for循环中的file变量值就有了,file变量的值是集合{libvpp.so.0.0.0, libvnet.so.18.0.0}了。
下面我们再来分析for循环中的循环体语句。
( cd %{buildroot}%{_libdir} && ln -fs $file $(echo $file | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) )
我们可以在现在举的这个例子里面将上面的命令进行替换展开:
( cd /usr/lib64/ && ln -fs libvpp.so.0.0.0 $(echo libvpp.so.0.0.0 | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) )
( cd /usr/lib64/ && ln -fs libvnet.so.18.0.0 $(echo libvnet.so.18.0.0 | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) )
我们具体来分析其中的sed命令,这里有一个正则表达式我们假设将正则表达式展开,那么得到如下的一个sed命令:
$(echo libvpp.so.0.0.0 | sed -e ‘s/(.so.[0-9]+).*/\1/‘)
注意:上述sed命令的正则表达式没有加转义字符啊。
(.so.[0-9]+).* // (...)表示匹配子串并保存匹配的字符,[0-9]表示匹配1个数字字符,[0-9]+表示匹配1个或者多个数字字符,.表示匹配一个非换行符的任意字符,*表示匹配0个或者多个字符
\1//表示在这里直接引用前面查找匹配子串时保存的匹配字符
经过上面的分析,我们可以得出下面的shell命令运行的结果:
$(echo libvpp.so.0.0.0 | sed -e ‘s/(.so.[0-9]+).*/\1/‘) //得到的结果字符串是: libvpp.so.0
$(echo libvnet.so.18.0.0 | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) //得到的结果字符串是:libvnet.so.18
然后调用的ln -sf命令直接就创建了2个软链接文件:
libvpp.so.0-------->libvpp.so.0.0.0
libvnet.so.18--------->libvnet.so.18.0.0
虽然上面的shell命令创建了2个软链接文件,但是我们知道在Linux下程序引用动态链接库文件的时候一般习惯性使用-lvpp, -lvnet等来指定,这样一来依赖的库文件形式就是libvpp.so和libvnet.so了,而不是libvpp.so.0和libvnet.so.18了。
因此我们还需要创建2个软链接文件libvpp.so和libvnet.so, 也分别软链接到libvpp.so.0.0.0和libvnet.so.18.0.0。
我们只需要将上面的for循环修改一下即可,我们修改如下:
for file in $(cd %{buildroot}%{_libdir} && find . -type f -print | sed -e ‘s/^\.\///‘)
do
# make lib symlinks
( cd %{buildroot}%{_libdir} &&
ln -fs $file $(echo $file | sed -e ‘s/\(\.so\.[0-9]\+\).*/\1/‘) &&
ln -sf $file $(echo $file | sed -e ‘s/\(\.so\).*/\1/‘) )
done
这样在for循环执行完之后,就在/usr/lib64目录里面分别创建了4个软链接文件了,其关系如下:
libvpp.so-------->libvpp.so.0.0.0
libvpp.so.0-------->libvpp.so.0.0.0
libvnet.so--------->libvnet.so.18.0.0
libvnet.so.18-------->libvnet.so.18.0.0
具体的关于sed命令的使用和正则表达式的详细说明,请参考如下的links:
http://www.infoq.com/cn/news/2011/07/regular-expressions-6-POSIX