CMake 教程(待完善)

Cmake 教程

写在前面

  • 如果工程只有几个文件,直接编写Makefile更直接明了
  • 如果使用C、C++、之外的语言,请不要使用cmake
  • 如果使用的语言有非常完备的构建体系,不需要使用cmake

CGold: The Hitchhiker’s Guide to the CMake — CGold 0.1 documentation

step1,hello cmake

# 项目名称
PROJECT ("HelloCmake")

# 设置变量
SET (SRC_FILE main.c)

MESSAGE (STATUS 你好啊)
MESSAGE (STATUS "你好啊")

# 附带产生两个变量
MESSAGE (STATUS  ${HelloCmake_SOURCE_DIR})
MESSAGE (STATUS  ${HelloCmake_BINARY_DIR})

# 生成可执行文件
ADD_EXECUTABLE (proc.exe ${SRC_FILE})
# make VERBOSE=1
make VERBOSE=1 

step2,层次感

本小节的任务是让前面的 Hello World 更像一个工程,我们需要作的是:

  1. 为工程添加一个子目录 src,用来放置工程源代码;
  2. 添加一个子目录 doc,用来放置这个工程的文档 hello.txt
  3. 在工程目录添加文本文件 COPYRIGHT, README;
  4. 在工程目录添加一个 runhello.sh 脚本,用来调用 hello 二进制
  5. 将构建后的目标文件放入构建目录的 bin 子目录;
  6. 最终安装这些文件:
    1. 将 hello 二进制与 runhello.sh 安装至/usr/bin
    2. 将 doc 目录的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/t2,将

会在bulid目录中复现src的文件结构

# 在build文件生成一个src文件夹
ADD_SUBDIRCTOPYT(src)
# 在build文件中生成一个bin文件夹存放二进制文件
ADD_SUBDIRCTOPYT(src bin)


# 指定,最终目标二进制文件/库文件存放位置。不包括编译生成的中间文件
# 在哪里 ADD_EXECUTABLE 或 ADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义
SET (EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# bash 中输入
cmake -D CMAKE_INSTALL_PREFIX=/usr 
SET (CMAKE_INSTALL_PREFIX /usr1/code/code/)

#CMAKE_INSTALL_PREFIX 的默认值
# /usr/local 直接安装到系统 

# 目标文件安装
# 安装 可执行二进制RUNTIME、静态库ARCHIVE、动态库LIBRARY
INSTALL(TARGETS myrun mylib mystaticlib
	RUNTIME DESTINATION bin
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION libstatic
	)
#DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候
#CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来
#定义安装路径,就要写成相对路径,即不要以/开头	

# 二进制 myrun 安装到${CMAKE_INSTALL_PREFIX}/bin
# ...

# 普通文件安装,并指定访问权限,,默认权限644
INSTALL(FILES )


step3,生成动态库、静态库

ADD_LIBRARY (hello SHARED hello.c)  # 生成动态库libhello.so
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 12.87.2 SOVERSION 8848)
# VERSION 指代动态库版本,SOVERSION 指代 API 版本。

ADD_LIBRARY (hello_static STATIC hello.c)
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")

GET_TARGET_PROPERTIES(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS “This is the hello_static OUTPUT_NAME:”${OUTPUT_VALUE})
#如果没有这个属性定义,则返回 NOTFOUND.

# cmake 在构建一个新的 target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.a 时,就会清理掉 libhello.so.
# 使用 SET_TARGET_PROPERTIES 定义CLEAN_DIRECT_OUTPUT 属性。

SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)




SET (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/so)


SET (CMAKE_INSTALL_PREFIX /home/orange/code/cmake/usr)

INSTALL (TARGETS hello hello_static
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)

INSTALL (FILES hello.h DESTINATION include) # <prefix>/include

step4,使用库、外部头文件

# 添加头文件搜索路径
INCLUDE_DIRECTORIES ( AFTER /home/orange/code/cmake/usr/include 路径2 路径3)
# AFTER 加在当前搜索路径之后,BEFORE 加在当前搜索路径之前


# 添加共享搜索路径
LINK_DIRECTORIES ( /home/orange/code/cmake/usr/lib 路径2 路径3 )
# 使用相对路径报错???


ADD_EXECUTABLE (main main.c)

# 添加链接描述  为库或可执行二进制加入库链接
#TARGET_LINK_LIBRARIES ( main hello )
TARGET_LINK_LIBRARIES ( main libhello.so )
#TARGET_LINK_LIBRARIES ( main hello.a )

特殊环境变量

CMAKE_INCLUDE_PATHCMAKE_LIBRARY_PATH

export CMAKE_INCLUDE_PATH=/usr/include/hello

然后在头文件中将 INCLUDE_DIRECTORIES(/usr/include/hello) 替换为:

FIND_PATH(myHeader hello.h)
IF(myHeader)
INCLUDE_DIRECTORIES(${myHeader})
ENDIF(myHeader)

# CMAKE_LIBRARY_PATH 可以用在 FIND_LIBRARY 中

连接标准位置的库

# 一些约定俗成的变量
# <name>_FOUND 判断模块是否被找到
# <name>_INCLUDE_DIR or <name>_INCLUDES  头文件位置
# <name>_LIBRARY or <name>_LIBRARIES  库位置。
# 因为是在标准位置所以不需要 LINK_DIRECTORIES ( <name>_LIBRARY )

FIND_PACKAGE(CURL)
IF(CURL_FOUND)
 INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
 TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
MESSAGE(FATAL_ERROR ”CURL library not found”)
ENDIF(CURL_FOUND)

# 如果<name>_FOUND 为真,则将<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,
# 将<name>_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。

编写自己的模块

FIND_PACAGE指令

FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
 [[REQUIRED|COMPONENTS] [componets...]])
 
# QUIET 参数对应  Find<name>.cmake 中的 <name>_FIND_QUIETLY
# REQUIRED 参数对应 Find<name>.cmake 中的 <name>_FIND_REQUIERED



#REQUIRED 参数,其含义是指这个共享库是否是工程必须的,如果使用了这个参数,说明这
#个链接库是必备库,如果找不到这个链接库,则工程不能编译。

工程目录/CMakeLists.txt

CMAKE_MINIMUM_REQUIRED (VERSION 3.20)
PROJECT (HELLO)
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
ADD_SUBDIRECTORY ( src )

工程目录/src/CMakeLists.txt

# HELLO_FOUND、HELLO_INCLUDE_DIR、HELLO_LIBRARY 变量在 “工程目录/cmake/FindHELLO.cmake”中定义
find_package (HELLO)
if (HELLO_FOUND)
	add_executable (proc main.c)
	include_directories (${HELLO_INCLUDE_DIR})
	target_link_libraries (proc ${HELLO_LIBRARY})
endif (HELLO_FOUND)
	

工程目录/cmake/FindHELLO.cmake

find_path ( HELLO_INCLUDE_DIR func.h  /usr1/code/code/step5/include 路径2 路径3)
# 如果找到 HELLO_INCLUDE_DIR 返回找到func.h 所在的文件夹
# 如果没找到 值为 HELLO_INCLUDE_DIR-NOTFOUND
find_library (HELLO_LIBRARY  func  /usr1/code/code/step3/lib) 
# 如果找到返回库路径,不是库所在的文件夹
# 如果没找到 值为 HELLO_LIBRARY-NOTFOUND

if (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
	#如果头文件和库文件都找到了
    set (HELLO_FOUND TRUE)
endif (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) 

if (HELLO_FOUND)
# 如果都找到了
	if (NOT HELLO_FIND_QUIETLY)
		message (STATUS "因为FIND_PACKAGE 指令中没有指定QUIET参数")
	endif (NOT HELLO_FIND_QUIETLY)
else (HELLO_FOUND)
	if (NOT HELLO_FOUND_REQUIRED)
		message (FATAL_ERROR "因为在FIND_PACKAGE 指令中没有指定了 REQUIRED 参数")
	endif (HELLO_FOUND_REQUIRED)
endif (HELLO_FOUND)	

杂记

源代码外构建 方便生成多套不同的配置

cmake -H. -B_builds/Debug -DCMAKE_BUILD_TYPE=Debug
cmake -H. -B_builds/Release -DCMAKE_BUILD_TYPE=Release

cmake -H. -B_builds/feature-on -DFOO_FEATURE=ON
cmake -H. -B_builds/feature-off -DFOO_FEATURE=OFF

cmake -H. -B_builds/xcode -G Xcode
cmake -H. -B_builds/make -G "Unix Makefiles"

CMake也会为其他工具运行测试,因此请 尽量避免在项目声明之前检查任何内容 ,并在项目声明后进行所有检查。

cmake_minimum_required(VERSION 2.8)

message("Before 'project':")
message("  C: '${CMAKE_C_COMPILER}'")
message("  C++: '${CMAKE_CXX_COMPILER}'")

project(Foo)

message("After 'project':")
message("  C: '${CMAKE_C_COMPILER}'")
message("  C++: '${CMAKE_CXX_COMPILER}'")

变量作用域

  • 每个变量都链接到定义它的作用域。add_subdirectory函数引入了它们自己的作用域。类似C语言中的代码块。

  • 创建新作用域时,将使用父作用域的变量对其进行初始化。命令 unset(abc) 可以从当前作用域中删除变量。如果在当前作用域中找不到变量,它将被取消引用为空字符串

  • include并且不要引入新的作用域,因此像 和 这样的命令会影响当前作用域:macrosetunset

    # 宏
    macro(modify_xyz)
      set(xyz "789")
    endmacro()
    # 调用宏
    modify_xyz()
    
  • set("${varname}" 16 PARENT_SCOPE)

CMakeLists.txt文件遍历顺序

  • 深度优先遍历

添加模块路径

设置此路径的正确方法是将其追加到现有值,例如,当用户出于任何原因想要使用自己的模块而不是标准模块时

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")

执行命令行

execute_process(
      COMMAND rm -rf "${dir_to_remove}"
      RESULT_VARIABLE result
  )
  
if(NOT result EQUAL 0)
  # Error
endif()
posted @ 2022-07-25 20:15  orangeQWJ  阅读(158)  评论(0编辑  收藏  举报