根据Cjacker的《Cmake实践》一文进行学习,只摘选其中近期需要使用的指令。
cmake_minimum_required(VERSION 3.20.0)
project(hello)
set(SRC_LIST main.c)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
add_executable(hello SRC_LIST)
cmake_minimumm_required(VERSION )
:指定所需CMAKE的最小版本
project(projectname [CXX] [C] [Java])
:定义工程名称,并指定工程支持的语言,默认支持所有语言;同时,该指令隐式定义了两个cmake
变量:
和
,即工程所在路径,即HELLO_BINARY_DIR
和HELLO_SOURCE_DIR
。
set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
:显式定义变量
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
:用于向终端输出用户定义的信息,包含三种类型:SEND_ERROR
产生错误,生成过程被跳过;STATUS
输出前缀为-的信息;FATAL_ERROR
立即终止所有cmake过程。
ADD_EXECUTABLE(hello ${SRC_LIST})
定义了这个工程会生成一个名为hello的可执行文件,相关源文件是SRC_LIST
中定义的源文件列表。
PS: 我们使用${}
来引用变量,如果在IF控制语句中,则不需要${}
;projectname
与生成可执行文件hello
并没有任何关系,hello
知识定义了可执行文件的文件名。
发布代码时,利用make clean
进行清理工程。
build
文件夹build
文件夹,然后输入cmake ..
,生成编译需要的中间文件make
进行构建工程,在build
文件夹中生成目标文件注意:
HELLO_BINARY_DIR
的路径将在build
路径中,而HELLO_SOURCE_DIR
仍然为工程路径。
试一试,{_SOURCE_DIR} 是否更改 - - - - >>> 已更改
- 为工程添加一个子目录src,用来放置工程源代码;
- 添加一个子目录doc,用来放置这个工程的注释文档;
- 为工程目录添加文本文件COPYRIGHT,README;
- 在工程目录添加要给
runhello.sh
脚本,用来调用hello二进制;- 将构建后的目标文件放入构建目录的bin子目录中;
- 最终安装这些文件:将hello二进制与runhello.sh安装至/usr/bin,将doc目录的内容以及COPYRIGHT/README安装到/usr/share/doc/cmake/***
src
中添加CMakeLists.txt
add_excutable(hello main.c)
根目录CMakeLists.txt
修改:
cmake_minimum_required(VERSION 3.20.0)
project(hello)
add_subdirectory(src bin)
add_subdirectory(source_dir [binary_dir] [exclude_from_all])
:向当前工程添加存放源文件的子目录,并指定中间二进制和目标二进制存放位置(即存放在build/bin
),exclude_from_all
表示将这个目录从编译过程中排除,比如工程的example
需要工程构建完成后,在进入单独构建。
可以通过SET
指令重新定义EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH
变量来指定最终的目标二进制文件存放位置(最终hello和共享库),该指令存放在add_executable
或add_library
定义之后
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
make install
:把.so
和.h
文件cp到系统目录下,通常.so
去/usr/lib
,.h
去/usr/include
,只有当你需要重复利用自己写的API,并且又懒得在其他项目里的cmake写地址,就在要复用的项目里的cmake写地址。
创建文件夹构成:
/CMakeLists.txt
:
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
/lib/CMakeLists.txt
:
SET(LIBHELLO_SRC hello.c)
SET(LIBRARY_OUTPUT_PATH <路径>)
ADD_LIBRARY(libhello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
类型有三种:
EXCLUDE_FROM_ALL
意思是这个库不会被默认构建,除非有其他的组件以来或者手工构建。
在/lib/CMakeLists.txt
添加命令:
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
注意:hello作为target不能重名,即不能和之前动态库重名
构建一个libhello_static.a
静态库。
若需要建立名字相同的静态库和动态库,就需要另外一个指令:SET_TARGET_PROPERTIES( target1 target2 PROPERTIES prop1 value1 prop2 value2...)
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
这个指令可以用来设置输出的名称,对于动态库,还可以指定动态库版本和API版本;
又由于cmake在构建一个新的targethello_static
的时候,即重建hello.cpp
,会尝试清理掉其他使用这个名字的库,所以需要回避这个问题。
此时,在build/lib
中同时生成了libhello.so
和libhello.a
SET_TARGET_PROPERTIES(hello PROPERITIES VERSION 1.2 SOVERSION 1)
version
:动态库版本;
soversion
:API版本;
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
将libhello.a
、libhello.so.x
和hello.h
安装到系统目录,方便开发,本例中将hello的共享库安装到
目录,将hello.h
安装到
在/lib/CMakeLists.txt
中添加:
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
注意,静态库要使用ARCHIVE
关键字
通过:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make -j8
make install
这样就可以把头文件和共享库安装到目录:/usr/include/hello
和/usr/lib
中
创建文件夹,构成目录:
CMakeLists.txt
lib
src
/CMakeLists.txt:
PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)
/src/CMakeLists.txt:
ADD_EXECUTABLE(main main.c)
/src/main.cpp
#include
int main()
{
HelloFunc();
return 0;
}
error: hello.h not found
由上文我们知道hello.h的位置位于/usr/include/hello
,为了使得工程能够找到此路径,加入命令INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
这条命令可以为工程添加多个特定头文件的搜索路径;
由于头文件有先后顺序之分,可以利用[AFTER|BEFORE]
来确定放在当前头文件搜索前面或者后面,默认后面;
INCLUDE_DIRECTORIES(/usr/include/hello)
重新编译;
error:undefind reference to ‘HelloFunc’ 即,没有将共享库连接到目标文件;
为了将共享库连接到目标文件中,我们应该加入两个命令:LINK_DIRECTORIER
和TARGET_LINK_LIBRARIES
LINK_DIRECTORIES(dir1 dir2 ...)
:添加非标准库搜索路径
TARGET_LINK_LIBRARIES(target library1
:为target添加需要链接的共享库
LINK_DIRECTORIES(/usr/lib/hello)
TARGET_LINK_LIBRARIES(main hello)
# TARGET_LINK_LIBRARIES(main libhello.so)
重新编译
输出>>>hello
输入ldd /src/main
检查main的链接情况
链接的是动态库,如果想链接静态库,则:
LINK_DIRECTORIES(/usr/lib/hello)
TARGET_LINK_LIBRARIES(main hello)
# TARGET_LINK_LIBRARIES(main libhello.a)
CMAKE_INCLUDE_PATH
、CMAKE_LIBRARY_PATH
环境变量,在~/.bashrc
中添加:
CMAKE_INCLUDE_PATH=/home/include
命令:
export CMAKE_INCLUDE_PATH=/usr/include/hell
那么头文件中,则INCLUDE_DIRECTORIES(/usr/include/hello)
可以更改为:
FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)
FIND_PATH
是用来在指定路径中,搜索文件名的作用,例如:
FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello)
如果不使用FIND_PATH
,则环境变量设置无效。
以上,仅是在个人应用层面对CMake掌握程度。更多的参数变量,以及现在cmake、public
、private
、interface
等,之后再更。