前言
被一个问题缠绕了很长时间,这两天花精力好好研究了一下,总算解决了,翻过来看,就是自己不注意造的很多小问题。我的想法是把一些代码封装起来,但是有些部分要求能让现场工作同事有一定的改动空间,比如接口部分、协议部分、都是可以改动的,但是有些地方怕他们改错,所以需要封装起来。
问题描述
我的工程是ROS工程,用CmakeLists.txt,基于上述想法,把一部分类封装成库,另外一部分暴露出来,在封装库的时候涉及到了静态库的多层嵌套问题。我的CmakeLists.txt架构是这样的:
CmakeLists.txt架构
find_package(Boost REQUIRED COMPONENTS system) #需要的第三方库find_package,这里只是用boost示意一下
#可以放到 THIRD_PARTY_LIBS
set( THIRD_PARTY_LIBS
${catkin_LIBRARIES}
${Boost_LIBRARIES}
${PCL_LIBRARIES}
)
link_directories(${PROJECT_SOURCE_DIR}/lib #自己封装的库的位置
)
#------------------------------本工程生成的库----------------------------------------
#封装库filter ,会生成 libfiter.a
add_library(filter STATIC ${PROJECT_SOURCE_DIR}/src/filter.cpp)
target_link_libraries(filter
${PCL_LIBRARIES}
${THIRD_PARTY_LIBS})
#封装库segment ,会生成 libsegment.a
add_library( segment STATIC ${PROJECT_SOURCE_DIR}/src/segment.cpp)
target_link_libraries(segment
${PCL_LIBRARIES}
${THIRD_PARTY_LIBS})
#-----------------------------------调用的库-----------------------------------
#***注意*** 这里的object依赖自己生成的其他静态库:libbuild.a ,libtracking.a,就是这里出现的问题
add_library( object STATIC ${PROJECT_SOURCE_DIR}/src/object.cpp)
target_link_libraries(object
build
tracking
${THIRD_PARTY_LIBS})
#--------------------------------------最后生成的库libprocess.a--------------------------------
#利用本工程生成的libfiter.a 、 libsegment.a 以及自己封装的libobject.a 还有第三方库生成的libprocess.a
add_library(process STATIC ${PROJECT_SOURCE_DIR}/src/process.cpp)
target_link_libraries(process
filter # 本工程生成的
segment # 本工程生成的
object #之前封装的,主要要把.h文件添加到本工程
${THIRD_PARTY_LIBS})# 需要的第三方库
#--------------------------------------生成可执行程序--------------------------------
add_executable(${PROJECT_NAME}_node src/${PROJECT_NAME}_node.cpp src/${PROJECT_NAME}.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
process
#
# filter
# segment
# object
${THIRD_PARTY_LIBS}
)
问题
step 1
按照上面的CmakeLists.txt 是可以编译过的,这里编译一下。
step 2
但是因为我想把object部分也封装成库,所以要把下面这部分屏蔽,把生成的库object 都放在目录${PROJECT_SOURCE_DIR}/lib下。
#-----------------------------------调用的库-----------------------------------
#***注意*** 这里的object依赖自己生成的其他静态库:libbuild.a ,libtracking.a,就是这里出现的问题
add_library( object STATIC ${PROJECT_SOURCE_DIR}/src/object.cpp)
target_link_libraries(object
build
tracking
${THIRD_PARTY_LIBS})
**这部分全部屏蔽掉**
step 3
然后再编译,可以通过。
step 4
删掉build和devel,不更改CmakeLists.txt ,再次编译,报错 `undefined reference to` 此类问题。
step 5
然后,去找论坛,发现可能是有些依赖库没有加进来,所以我在最后 可执行程序这里添加了所有的库。
#--------------------------------------生成可执行删掉build和devel,不更改CmakeLists.txt ,再次编译,报错程序--------------------------------
add_executable(${PROJECT_NAME}_node src/${PROJECT_NAME}_node.cpp src/${PROJECT_NAME}.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
process
# 下面之前是屏蔽的.....
filter
segment
object
${THIRD_PARTY_LIBS}
)
step 6
再次编译,还是出错,检查过链接次序,依赖库等,都没有问题。
奇怪的是我把step 2部分注释去掉,编译通过,再屏蔽该部分,编译通过,然后删掉build和devel,不更改CmakeLists.txt ,再次编译,报错。
就是这个问题,虐我千百遍,虐我好几年。
解决办法
库的依赖关系
这里要特别感谢这篇文章:[“undefined reference to“ 问题汇总及解决方法 ------非常非常好的一篇文章](https://blog.csdn.net/stpeace/article/details/73302833)
这篇文章说了“undefined reference to“的三个问题:
1.依赖的库没有 找到/添加,这里就是没有设置存放你自己封装库的位置,或者没有添加依赖,通常要在CmakeLists.txt中添加:
link_directories(${PROJECT_SOURCE_DIR}/lib #自己封装的库的位置
)
target_link_libraries(object
build
tracking
${THIRD_PARTY_LIBS})
2.依赖顺序有问题,库的依赖是有次序的,这个次序是从头开始,提纲切领。可以见上面的文章。
3.定义与实现不一致。这个就需要检测代码。
静态库的理解
undefined reference to 的几个问题都处理了,但是还是出问题。就去研究静态库,这里参照了多个静态库生成一个静态库。[CMake应用:合并静态库的最佳实践](https://zhuanlan.zhihu.com/p/389448385) 。在这篇文章中发现了静态库其实就是 .o文件的集合,可以用ar -x *.a来查看,但是发现,只是当前cpp文件的,o,依赖的静态库并没有被一并打包到这个库里面。
一度怀疑上面的问题是以为.o文件没有被生成,就按照合并静态库的做法好一顿捣鼓,最后没啥效果。忽然想到既然.a是有的对应的输出文件.o的,那么就不是因为需要编译生成.o文件,那为什么不屏蔽object是可以的,屏蔽了就不行?另外先放开编译,再屏蔽编译确实能通过?
为什么?
1.不屏蔽CmakeLists.txt的object生成过程是可以的,屏蔽了就不行
2.先不屏蔽,编译,再屏蔽编译确实能通过
??????????????????
把这两个操作的build文件拿出来,去对比了半天也没定位到。
解决
想一下,库既然都存在,那么素材是都有的。并且先不屏蔽,编译,再屏蔽编译确实能通过,说明是,依赖也是没有问题的。因为CmakeLists.txt 要转成cmake,cmake比较麻烦,所以这个中间过程是不可见的。
想一下自己的架构,包含下面几种库:
1.本工程生成的库,无嵌套
2.调用的库,嵌套了其他调用库,2层嵌套
3.用1 2 以及本地cpp生成的库,3层嵌套
4.调用3生成的库,生成可执行程序。
库都可以生成
库生成没有问题,并且编译有通过的情况,那么这个路就是可以走通的。那是不是链接关系的问题呢?并且发现是2这个库在报错误。
通过对静态库的理解,发现上层的静态库并没有把下层静态库打包到一块,所以在最终的可执行程序,必须要列出所有可以依赖的库。最终:
add_executable(${PROJECT_NAME}_node src/${PROJECT_NAME}_node.cpp src/${PROJECT_NAME}.cpp)
## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
process
# 下面之前是屏蔽的.....
filter
segment
object
build #添加了最底层的静态库
tracking #添加了最底层的静态库
${THIRD_PARTY_LIBS} #添加了需要的第三方库
)