深入理解计算机基础第七章-CMake学习笔记

说明

对Cmake的学习笔记
CMake官网
CMake手册详解
CMake学习例子
个人路径显示工具
CMake基本语法
CMake内置环境变量

一些技巧

file命令获取路径下所有的文件

预定义变量

PROJECT_SOURCE_DIR 工程的根目录
PROJECT_BINARY_DIR 运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/build
CMAKE_INCLUDE_PATH 环境变量,非cmake变量
CMAKE_LIBRARY_PATH 环境变量
CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIR target编译目录
使用ADD_SURDIRECTORY(src bin)可以更改此变量的值
SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对此变量有影响,只是改变了最终目标文件的存储路径
CMAKE_CURRENT_LIST_FILE 输出调用这个变量的CMakeLists.txt的完整路径
CMAKE_CURRENT_LIST_LINE 输出这个变量所在的行
CMAKE_MODULE_PATH 定义自己的cmake模块所在的路径
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置
PROJECT_NAME 返回通过PROJECT指令定义的项目名称
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 用来控制IF ELSE语句的书写方式

cmake语法

多行注释

#[=3[  (我是...]]....注释) ]===]
#[===[ (我是...]]....注释) ]===]
#[=3[  (我是...]]....注释) ]===]
#[[    (我是注释)          ]]             

常量意义

PROJECT_BINARY_DIR /demo/build
PROJECT_SOURCE_DIR 包含peojrct的最顶层CMakeLists.txt的路径

变量类型

所有值都会转换成String。所有的参数会被封装成List
这里有点混乱,好像布尔变量也是字符串

类型
true 1, ON, YES, TRUE, Y, 非0的值
false 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串””, 以-NOTFOUND 结尾的字符串
set(var hello)
message(${var}) // ${var}   $ENV{var}
```IF(DEFINEDENV{ARM_ARCHITECTURE}) #如果是ARM机器```
set(var 1)
unset(var) // 移除变量 
set(list_var  1 2 3 4)  # list_var = 1;2;3;4
set(list_foo "5;6;7;8") # list_foo = 5;6;7;8  ;分割列表

list操作

list

list(LENGTH <list> <output variable>)
list(GET <list> <element index> [<element index> ...] <output variable>)
list(APPEND <list> [<element> ...])
list(FIND <list> <value> <output variable>)
list(INSERT <list> <element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)

if

if(VAR1 MATCHES "Hello")
    message("this is hello")
    message("this is hello2")
elseif(VAR1 MATCHES "world")
    message("this is world")
    message("this is world2")
else()
    message("else")
endif()

while

支持break()和continue()

while(condition)
  COMMAND1(ARGS ...)
  COMMAND2(ARGS ...)
  ...
endwhile()  // 括号里面不用管,是为了兼容保留

for

支持break()和continue()

set(mylist "a" "b" "c" "d")
foreach(_var ${mylist})
     message("当前变量是:${_var}")
endforeach()

经典\(\sum_{i = 0}^{100}{i}\)

set(result 0)
foreach(_var RANGE 0 100)
     math(EXPR result "${result}+${_var}")
endforeach()
message("from 0 plus to 100 is:${result}")

自动变量

$@ 规则的生成目标
$% 档案文件成员结构中的文件名元素
$< 第一个依赖文件名
$^ 所有的依赖文件名(已经消重),以空格分隔
$+ 所有的依赖文件名(未经消重),以空格分隔
$* 所有除掉后缀的依赖文件名,以空格分隔,仅适用于模式规则。注:文件名包含stem和suffix,去掉suffix就剩下了stem。比如hello.cpp的stem是hello,suffix就是cpp。
$? 比目标文件新的依赖文件。

缓存变量

1.可以缓存用户选择,下次不必再输入
2.缓存高消耗时间的额值

创建缓存变量并放在缓存文件中

option (USE_JPEG "Hello world")
set (USE_JPEG ON CACHE BOOL "Hello world") # set需要指定变量类型

变量作用域 + 函数定义
set(var1 1)
add_subdirectory(dir1) #dir1中可见var1
set(var 2)
add_subdirectory(dir2) #dir2中可见var1,var2
# 传址调用
function (foo)
  message(${test}) #输出 1
  set(test 2 PARENT_SCOPE) #影响父作用域中的值
  message(${test}) #输出 2
endfunction()

set(test 1)
foo()
message(${test})  #输出 2
# 传值调用
function (foo)
  message(${test}) #输出 1
  set(test 2)
  message(${test}) #输出 2
endfunction()

set(test 1)
foo()
message(${test})  #输出 1

函数体内自带参数
| 名称 | 意义 |
| ---- | ---- | ---- |
| ARGC | 参数个数 |
| ARGV | 参数列表 |
| ARGV0 | 参数0 |
| ARGV1 | 参数1 |
| ARGV2 | 参数2 |
| ARGN | 超出最后一个预期参数的参数列表 |

macro(Moo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endmacro()

和define差不多,都是字符串替换

CMake常用操作

CMAKE_C(XX)_FLAGS

CMAKE_C_FLAGS存放的内容会被传给C编译器,作用在所有的编译组态上
可以只针对特定组态如
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_DEBUG

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2")

还可以在cmake命令中使用,如
cmake -DBUILD_SHARED_LIBS=OFF

CHECK_CXX_COMPILER_FLAG

检查CXX编译器是否支持某个特性
比如check支持的语言版本

CMAKE_MODULE_PATH

cmake进行搜索的modules的list

find_package

咕咕咕

添加编译选项

add_compile_options(-std=c++11) 针对所有编译器
ADD_DEFINITIONS("-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes) 针对所有编译器
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall") 针对c++编译器,
set(CMAKE_C_FLAGS "") 针对c编译器

头文件、库包含

总结

include_directories
增加头文件的搜索路径,相当于指定gcc的-I参数
在某些神秘的情况下,该命令会失效(需要包含多个目录)
此时需要在最近公共CMakeLists中包含多个路径,会递归应用到所有子CMakeLists中


link_directories()
去哪找库文件(.so/.dll/.lib/.dylib/...),-L(GCC)


LINK_LIBRARIES(库名称即可)
需要链接的库文件的名字:-l(GCC)
对于那些在 /lib, /usr/lib 和 /usr/local/lib 路径下的库, 我们可以使用更加简单的命令 −l 而无需添加库文件路径


target_include_directories 指定需要编译的目标的默认搜索路径
目标必须由 add_executable() or add_library()创建

target(也就是cmake产生的中间文件)

targer的属性可以通过set_target_property,get_target_property访问

用于指定target所需要连接的库,还可以有不同的选项

add_library(foo,STATIC/SHARED, foo.c)

没有指定STATIC/SHARED则根据BUILD_SHARED_LIBS变量的值判断,若无,默认STAIC

add_definitions

添加编译参数
add_definitions(-std=c++11)

add_executable

如果可执行目标名后面指定了WIN32,则生成入口为WinMain的windows应用程序

嵌入shell命令

execute_process(
WORKING_DIRECTORY <这句shell命令执行的工作目录>
COMMAND <一句shell命令>
)
还可以获取返回结果,比如判断git的某个设置


自定义中间文件
add_custom_target
add_custom_command
两个命令结合起来可以调用bash
cmake --build . --target xxx
其中cmake --build . = make,相当于一个平台无关的指令

CMake构建配置

默认支持
1.Debug CMake会打开一些基本的调试开关进行构建
2.Release CMake在构建时会打开一些基本的优化选项开关
3.MinSizeRel CMake会使得构建出来的目标代码体积尽可能小
4.RelWithDeblnfo CMake构建出带有调试信息并且经过编译优化后的版本
设置c++编译器
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2") // 具体的版本打开/user/bin目录查看


1.hello world!

+--- demo
   |
   +--- CMakeList.txt
   +--- build
      |
      +--- hello.exe
   +--- main.c

CMakeList.txt

project(demo)                      #不强制,但最好都加上
set(SRC_LIST main.c)               #设置变量
add_executable(hello ${SRC_LIST})  #生成一个可执行文件
                                   #add_library 生成一个库文件

注意project会引入变量<PROJECT_NAME>_BINARY_DIR(编译路径,即demo/build) 和 <PROJECT_NAME>_SOURCE_DIR(工程路径,即demo)
我们采用cmake的out-of-source方式来构建(即生成中间产物与源代码分离),并坚持这种办法
在build下调用cmake

cmake .. -G"MinGW Makefiles"  #MakeFile文件位置
make

2.拆分成多个.h .c

+--- demo
   |
   +--- CMakeLists.txt
   +--- build
      |
      +--- hello.exe
   +--- hello.c
   +--- hello.h
   +--- main.c

hello.h

#program once
void hello(const char * name);

hello.c

#include "hello.h"
void hello(const char * name)
  {
    printf("Hello %s!",name);
  }

main.c

#include "hello.h"
int main()
  {
    hello("world");
    return 0;
}

CMakeList.txt

project(demo)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})

然后cmake一下就行了

3.生成lib文件

+--- demo
   |
   +--- CMakeList.txt
   +--- build
      |
      +--- hello.exe
      +--- lib_hello.lib
   +--- hello.c
   +--- hello.h
   +--- main.c

hello.h

#program once
void hello(const char * name);

hello.c

#include "hello.h"
void hello(const char * name)
  {
    printf("Hello %s!",name);
  }

main.c

#include "hello.h"
int main()
  {
    hello("world");
    return 0;
}

CMakeList.txt

project(demo)
set(LIB_SRC hello.c)
set(APP_SRC main.c)
add_library(lib_hello ${LIB_SRC})
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)

如何给lib取名hello而不是lib_hello

set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")

4.正规的工程目录

+--- demo
   |
   +--- CMakeList.txt
   +--- build
      |
      +--- hello.exe
   +--- lib_hello
      |
      +--- CMakeList.txt
      +--- hello.c
      +--- hello.h
   +--- src
      |
      +--- CMakeList.txt
      +--- main.c

/demo/CMakeList.txt

project(demo)
add_subdirectory(src)
add_subdirectory(lib_hello)

/demo/src/CMakeList.txt

include_directories(${PROJECT_SOURCE_DIR}/lib_hello)
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)

/demo/lib_hello/CMakeList.txt

set(LIB_SRC hello.c)
add_library(lib_hello ${LIB_SRC})
set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")

然后在build下cmake,用/demo/CMakeList.txt即可

5.分类可执行文件和库文件

+--- demo
   |
   +--- CMakeList.txt
   +--- build
      |
      +--- bin
         |
         +--- hello.exe
      +--- lib_hello
         |
         +--- CMakeList.txt
         +--- hello.c
         +--- hello.h
         +--- hello.lib
   +--- src
      |
      +--- CMakeList.txt
      +--- main.c

/demo/src/CMakeList.txt

include_directories(${PROJECT_SOURCE_DIR}/lib_hello)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)

/demo/build/lib_hello/CMakeList

set(LIB_SRC hello.c)
add_library(lib_hello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")

6.最终的工程结构示范

所有的代码正常include头文件即可(静态库、动态库不需要在代码里额外的设置)

+--- CMakeTemplate
   |
   +--- CMakeLists.txt
   +--- build
      |
      +--- bin
      +--- lib 
      +--- so
   +--- src
      |
      +--- CMakeLists.txt
      +--- main.cpp
      +--- libHello
	      |
	      +--- CMakeLists.txt
	      +--- Hello.h
	      +--- Hello.cpp
      +--- soPrint
	      |
	      +--- CMakeLists.txt
	      +--- Print.h
	      +--- Print.cpp

CMakeTemplate/CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.8")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
project(CMakeTemplate)
add_subdirectory(src)

CMakeTemplate/src/CMakeLists.txt

add_subdirectory(libHello)
add_subdirectory(soPrint)
include_directories(${PROJECT_SOURCE_DIR}/src/libHello)
include_directories(${PROJECT_SOURCE_DIR}/src/soPrint)
set(APP_SRC main.cpp)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(${PROJECT_NAME} ${APP_SRC})
target_link_libraries(${PROJECT_NAME} libHello soPrint)

CMakeTemplate/src/libHello/CMakeLists.txt

set(LIB_SRC Hello.cpp)
add_library(libHello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libHello PROPERTIES OUTPUT_NAME "Hello")

CMakeTemplate/src/soPrint/CMakeLists.txt

set(SO_SRC Print.cpp)
add_library(soPrint SHARED  ${SO_SRC})
set_target_properties(soPrint PROPERTIES OUTPUT_NAME "Print")
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/so)
posted @ 2021-09-30 22:43  XDU18清欢  阅读(140)  评论(0)    收藏  举报