[c++] CMakeLists基础命令(个人应用层面)

CMakeLists基础命令

根据Cjacker的《Cmake实践》一文进行学习,只摘选其中近期需要使用的指令。


文章目录

  • CMakeLists基础命令
  • 单文件工程
  • 组织工程
  • 静态库与动态库的构建
    • 构建同名动态库与静态库
    • 动态库版本号
    • 安装共享库和头文件
  • 如何使用共享库和头文件
    • 添加头文件路径
    • 添加共享库路径
    • 通过环境变量搜索头文件
  • 总结


[c++] CMakeLists基础命令(个人应用层面)_第1张图片

单文件工程

  • 示例
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变量:_BINARY_DIR_SOURCE_DIR,即工程所在路径,即HELLO_BINARY_DIRHELLO_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进行清理工程。

  • 外部编译
  1. 在根目录建立build文件夹
  2. 利用终端进入build文件夹,然后输入cmake ..,生成编译需要的中间文件
  3. 运行make进行构建工程,在build文件夹中生成目标文件

注意:

HELLO_BINARY_DIR的路径将在build路径中,而HELLO_SOURCE_DIR仍然为工程路径。

试一试,{_SOURCE_DIR} 是否更改 - - - - >>> 已更改

组织工程

  1. 为工程添加一个子目录src,用来放置工程源代码;
  2. 添加一个子目录doc,用来放置这个工程的注释文档;
  3. 为工程目录添加文本文件COPYRIGHT,README;
  4. 在工程目录添加要给runhello.sh脚本,用来调用hello二进制;
  5. 将构建后的目标文件放入构建目录的bin子目录中;
  6. 最终安装这些文件:将hello二进制与runhello.sh安装至/usr/bin,将doc目录的内容以及COPYRIGHT/README安装到/usr/share/doc/cmake/***
  1. 在子目录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_PATHLIBRARY_OUTPUT_PATH变量来指定最终的目标二进制文件存放位置(最终hello和共享库),该指令存放在add_executableadd_library定义之后

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
  1. 如何安装(先跳过)

make install:把.so.h文件cp到系统目录下,通常.so/usr/lib.h/usr/include,只有当你需要重复利用自己写的API,并且又懒得在其他项目里的cmake写地址,就在要复用的项目里的cmake写地址。

  • 从代码编译后直接make install安装
  • 打包时的指定目录安装
  1. 略(由于目前尚未做到此程度,故跳过)
  2. 略(由于目前尚未做到此程度,故跳过)
  3. 略(由于目前尚未做到此程度,故跳过)
  4. 略(由于目前尚未做到此程度,故跳过)

静态库与动态库的构建

创建文件夹构成:

  • CMakeLists.txt
  • lib
    • hello.cpp
    • hello.h
    • CMakeLists.txt

/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)

类型有三种:

  • 动态库
  • 静态库
  • MODULE,dyld系统有效

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.solibhello.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.alibhello.so.xhello.h安装到系统目录,方便开发,本例中将hello的共享库安装到/lib目录,将hello.h安装到/include/hello>

/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

    • hello.cpp
    • hello.h
    • CMakeLists.txt
  • src

    • main.cpp
    • CMakeLists.txt

/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_DIRECTORIERTARGET_LINK_LIBRARIES

LINK_DIRECTORIES(dir1 dir2 ...):添加非标准库搜索路径

TARGET_LINK_LIBRARIES(target library1 library2 ...):为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_PATHCMAKE_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、publicprivateinterface等,之后再更。

你可能感兴趣的:(艰苦跋涉的c++学习,c++,开发语言)