零.基本使用(解决90%)
include/Hello.h为头文件
对应的源文件为src/Hellp.cpp
main函数在src/main.cpp中
cmake_minimum_required(VERSION 3.7)
project(hello_project)
add_library(hello_lib src/Hello.cpp) # include的文件所对应的源代码
target_include_directories(hello_lib PUBLIC ${PROJECT_SOURCE_DIR}/include) #inlude的文件对应的.h文件的位置
add_executable(main src/main.cpp)
target_link_libraries(
main PRIVATE hello_lib # inlcude的了哪些文件
)
调包:
find_package(OpenCV 4 REQUIRED)
find_package(Sophus REQUIRED)
find_package(Pangolin REQUIRED)
include_directories(
${OpenCV_INCLUDE_DIRS}
${G2O_INCLUDE_DIRS}
${Sophus_INCLUDE_DIRS}
${Pangolin_INCLUDE_DIRS}
)
一. 用cmake安装hello world工程
相信和我一样经历过git clone下源码不会安装, 只会apt 或yum install 的痛苦过程. 学cmake的第一步就先体验一遍用源码安装软件的流程, 先写源码
mkdir src
mkdir doc
cd src
touch hello.cpp
在其中编写:
#include<iostream>
int main(){
std::cout<<"hello world"<<std::endl;
return 0;
}
之后在doc中随便创建点什么东西(文档)
cd ../doc
touch hello.txt
在根目录添加本工程的COPYRIGHT和README:
touch COPYRIGHT README
现在工程的框架已经搭建起来了, 开始写cmake. cmake要求每个目录都有一个CMakeList.txt, 根目录的CMakeList.txt用add_subdirector将他们链接过来.
在src目录下的CMakeLists.txt中:
add_executable(hello hello.cpp)
在主目录的CMakeLists.txt中:
project(HELLO)
add_subdirectory(src bin) # 表示该文件夹编译文件放在build的bin文件夹中, 否在在build/src
接下来就是大家非常熟悉的:
mkdir build
cd build
cmake ..
make
这样就编译好了, 可以运行了但是还没有满足我们的要求(通过源码安装).在安装之前需要了解一个cmake命令 install和一个变量CMAKE_INSTALL_PREFIX.
install(TARGETS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
目标文件的安装: install(TARGETS <target>... [...])
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
TARGETS后面跟着的就是我们通过add_executable或add_library定义的目标文件
runtime: 可执行二进制文件
library: 动态库
archive: 静态库
举一个例子(来自开头提到的pdf)
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
这个例子中targets是myrun mylib mystaticlib三项. 后面跟着的三行格式为:
[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION <dir>]
所以上述命令的含义为: 将myrun安装到${CMAKE_INSTALL_PREFIX}/bin, 将mylib安装到${CMAKE_INSTALL_PREFIX}/lib, 第三个同理. 我们不需要关注target的路径,只写target的名字即可.
普通文件的安装:
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
非目标文件的可执行文件(如脚本):
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
至于下面的参数有什么含义, 边实践边学习吧.接下来继续我们的安装工程:
首先在根目录的txt中添加:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
会将三个Files安装(copy)到DESTINATION后面的文件夹中. 接下来添加:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
runhello.sh中执行编译后编译后文件, 本项目中写hello即可. (提前已经设定了hello放在bin里)接下来安装doc目录:
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
CMAKE_INSTALL_PREFIX在运行的时候添加.
在build目录下执行如下命令:
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
make
make install
安装完毕. 进入对应目录即可看到安装的文件.如果相把文件安装到系统, 将CMAKE_INSTALL_PREFIX设置成/usr .. 即可. CMAKE_INSTALL_PREFIX 的默认定义是/usr/local
二. 静态库与动态库构建
C++的静态库和动态库有什么区别?
静态库: 在编译时链接到可执行文件中,在运行时不再需要。
动态库: 在程序运行时加载到内存中,并在运行时使用。静态库的优点是程序可执行文件尺寸小,缺点是程序运行时无法更新库。动态库的优点是可以在运行时更新库,缺点是程序可执行文件尺寸大,并且需要额外的加载时间。
首先创建lib目录,在其中添加hello.c和hello.h:
// hello.c:
#include "hello.h"
void HelloFunc()
{
printf("Hello World\n");
}
// hello.h:
#ifndef HELLO_H
#define HELLO_H
#include <stdio.h>
void HelloFunc();
#endif
之后在根目录的cmake里写:
cmake_minimum_required(VERSION 3.22)
project(HELLOLIB)
add_subdirectory(lib)
在lib目录下的make里写:
set(LIBHELLO_SRC hello.c)
add_library(hello SHARED ${LIBHELLO_SRC})
编译后可以卡到我们已经成功获得了动态库libhello.so. add_library中的第二个参数可以填写SHARED(动态库)和STATIC(静态库). 接下来在支持了动态库的基础上为工程添加静态库:
最容易想到的做法是:在lib下的make再添加一行
add_library(hello STATIC ${LIBHELLO_SRC})
但是会报错:add_library cannot create target "hello" because another target with the same name already exists. 所以我们需要对其进行重命名:
add_library(hello_static STATIC ${LIBHELLO_SRC})
编译成功, 不过此时静态库的名字为libhello_static.a而我们希望他的名字为libhello.a, 所以我们需要对其输出的名字进行重设, 这是需要添加这行命令:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本(一会儿尝试一下).这时再重新编译一下可以发现, 我们获得了libhello.a和libhello.so两个文件, 达到了我们的目标.接下来我们想办法把这个库安装到系统中并在系统中调用这个库把整个闭环跑通.
插一句如何指定API版本:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION 指代动态库版本,SOVERSION 指代 API 版本。
接下来我们把这个库安装到系统中, 在根目录的cmake中添加:
install(
TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(FILES lib/hello.h DESTINATION include/hello)
在build目录运行:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
sudo make install
三. 外部共享库的调用
接下来我们尝试创建一个新工程并在其中调用刚才安装的hello
mkdir build
mkdir src
cd src
touch CMakeLists.txt
touch main.c
cd ../
touch CMakeLists.txt
在main.c中编辑
#include<hello.h>
int main(){
HelloFunc();
return 0;
}
在src下的cmake中编辑:
include_directories(/usr/include/hello)
add_executable(main main.c)
TARGET_LINK_LIBRARIES(main hello)
在主目录下的cmake编辑:
project(NEWHELLO)
add_subdirectory(src)
接下来即可编译运行