为何用Cmake?
在linux环境编写程序,因没有类似于visual studio、vs code等IDE(集成开发环境),那么如何编译、运行程序呢?一种方法是编写makefile文件,用makefile文件管理程序脚本之间的相互依赖关系,其语法相对比较复杂。另一种有效的方法就是利用cmake工具,自动生成makefile文件。
CMake是什么?
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。Linux/Ubuntu上基本都预先安装好了cmake工具,用 sudo apt-get install 也可以自动安装。要使用cmake必须在工程的每个文件夹中都编写CMakeLists.txt,尤其注意C、M、L是大写,并且是复数形式。
CMake编译流程
编写文件CMakeLists.txt
执行命令“cmake PATH”或者“ccmake PATH”生成Makefile (PATH是CMakeLists.txt所在的目录)
使用make命令进行编译源码生成可执行程序或共享库(so(shared object))
cmake 指向CMakeLists.txt所在的目录,例如 cmake … 表示CMakeLists.txt在当前目录的上一级目录。cmake后会生成很多编译的中间文件以及makefile文件,所以一般建议新建一个新的目录,专门用来编译
mkdir build
cd build
cmake ..
make
make根据生成makefile文件,编译程序
示例
cmake_minimum_required
设置项目所需的cmake最低版本。
CMAKE_MINIMUM_REQUIRED(VERSION <min>[...<max>] [FATAL_ERROR])
---
cmake_minimum_required(VERSION 3.10)
project
PROJECT(projectname [CXX] [C] [Java])
---
project(proj_name LANGUAGES CXX) //proj_name是自定义的
定义工程名称并指定工程支持的语言;
隐式定义了两个cmake变量:<projectname>_BINARY_DIR
以及<projectname>_SOURCE_DIR
,如果采用的是内部编译,两个变量指的将是工程所在的路径,如果是外部编译,两者所指代的内容会有所不同。
报错:CMake Error: CMAKE_LANGUAGE_COMPILER not set, after EnableLanguage
– Configuring incomplete, errors occurred!
原因: LANGUAGES误写成了LANGUAGE
option
CMake中的option用于把持编译流程,相称于C语言中的宏前提编译。
正如他名字,此命令为用户提供选择ON/OFF
的选项
option(<variable> "<help_text>" [value])
第一个参数<variable>为选项变量名
第二个参数<help_text>为选项的文字描述便于只管描述其用途,类似于注释
第三个参数[value]是选项的默认值,只能为ON/OFF,如果不设定默认值则为OFF。如果已经为此变量赋值则option命令会被忽略
---
option(WITH_CUDA "Build with CUDA support" OFF)
option(BUILD_TESTS "Build tests" ON)
option(BUILD_EXAMPLES "Build examples" ON)
//上述代码定义了三个选项:WITH_CUDA、BUILD_TESTS和BUILD_EXAMPLES,分别用于控制是否加入CUDA支持、是否编译测试代码以及是否编译示例代码。
在CMakeLists.txt中读取CMake Option的值非常简单,使用${OPTION_NAME}
即可,比如:
if(${WITH_CUDA})
# do something with CUDA support
endif()
if(${BUILD_TESTS})
# do something with tests
endif()
if(${BUILD_EXAMPLES})
# do something with examples
endif()
add_definitions
add_definitions(-DFOO -DBAR ...)
---
add_definitions(-Dtx2)//就是添加了一个tx2的宏定义
但是好像add_definitions
没法通过cmake命令行
来进行控制。所以这里在cmake中再声明一个option
进行选择,如下:
cmake_minimum_required(VERSION 3.2)
project(test)
option(ONTX2 "build project on tx2" OFF)
IF(ONTX2)
add_definitions(-Dtx2)
ENDIF(ONTX2)
set(source net.cpp)
add_executable(test ${source})
编译的时候通过以下两种方式进行选择:
cmake -DONTX2=1 .. //注意是-Dxxxx,有个-D的前缀
or
cmake -DONTX2=0 ..
【c++】——通过cmake定义宏add_definitions
add_subdirectory
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
---
add_subdirectory(src)
用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。
利用cmake的add_subdirectory管理内部和外部库
include_directories
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
---
include_directories(include_1 include_2)
添加头文件路径。这条指令可以用来向工程添加多个特定的头文件路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面,你可以通过两种方式来进行控制搜索路径添加的方式:
CMAKE_INCLUDE_DIRECTORIES_BEFORE
,通过 SET 这个 cmake 变量为on,可以将添加的头文件搜索路径放在已有路径的前面。- 通过
AFTER
或者BEFORE
参数,也可以控制是追加还是置前。
add_library
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
---
add_library(hello SHARED ./src/printhello.cpp ./src/factorial.cpp)
此指令的作用为构建库,库的类型有三种:SHARED(共享库【动态库】),STATIC(静态库),MODULE(模块);
假如创建共享库libhello.so
,libname
项只需写hello
,而不需要写全libhello.so
,cmake 系统会自动为你生成libhello.X
。(共享库的后缀为.so
,静态库的后缀为.a
)
aux_source_directory
AUX_SOURCE_DIRECTORY(dir VARIABLE)
---
aux_source_directory(. SRC_LIST) //.代表当前目录
message(STATUS "source code : " ${SRC_LIST})
add_executable(main ${SRC_LIST})
发现一个目录下所有的源代码文件,并将文件路径列表存储在一个变量中,这个指令临时被用来自动构建源文件列表。因为目前 cmake 还不能自动发现新添加的源文件。
比如:AUX_SOURCE_DIRECTORY(. SRC_LIST)
,ADD_EXECUTABLE(main ${SRC_LIST}
file
Reading
file(READ <filename> <out-var> [...])
file(STRINGS <filename> <out-var> [...])
file(<HASH> <filename> <out-var>)
file(TIMESTAMP <filename> <out-var> [...])
Writing
file({WRITE | APPEND} <filename> <content>...)
file({TOUCH | TOUCH_NOCREATE} [<file>...])
file(GENERATE OUTPUT <output-file> [...])
Filesystem
file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
file(RENAME <oldname> <newname>)
file({REMOVE | REMOVE_RECURSE } [<files>...])
file(MAKE_DIRECTORY [<dir>...])
file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
file(SIZE <filename> <out-var>)
file(READ_SYMLINK <linkname> <out-var>)
file(CREATE_LINK <original> <linkname> [...])
Path Conversion
file(RELATIVE_PATH <out-var> <directory> <file>)
file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)
Transfer
file(DOWNLOAD <url> <file> [...])
file(UPLOAD <file> <url> [...])
Locking
file(LOCK <path> [...])
file(GLOB variable [RELATIVE path] [globbingexpressions]...)
---
file(GLOB C_SOURCES ./src/*.c)
file(GLOB CPP_SOURCES ./src/*.cpp)
set(SOURCES ${C_SOURCES} ${CPP_SOURCES})
add_executable(test ${SOURCES})
GLOB 会产生一个由所有匹配globbing表达式
的文件组成的列表,并将其保存到变量中。Globbing 表达式与正则表达式类似,但更简单。如果指定了RELATIVE
标记,返回的结果将是与指定的路径相对的路径构成的列表。
(通常不推荐使用GLOB命令来从源码树中收集源文件列表。原因是:如果CMakeLists.txt文件没有改变,即便在该源码树中添加或删除文件,产生的构建系统也不会知道何时该要求CMake重新产生构建文件。)
意思就是在该源码树中添加或删除了文件之后,识别不到。比如添加或删除了文件之后,直接
make
是会报错的,需要重新cmake ..
之后,再make
。
globbing 表达式包括:
*.cxx - match all files with extension cxx
*.vt? - match all files with extension vta,...,vtz
f[3-5].txt - match files f3.txt,f4.txt, f5.txt
add_executable
add_executable(main main.cpp)
target_link_libraries
target_link_libraries(main hello)
target_include_directories
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
---
target_include_directories(hello
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include
)
该命令可以指定目标(exe或者so文件)需要包含的头文件路径。
命名的<目标>必须是由add_executable()
或add_library()
之类的命令创建的,并且不能是ALIAS目标。通过显式地使用AFTER
或BEFORE
,就可以在附加和预挂之间进行选择,而不是依赖默认值。
需要INTERFACE、PUBLIC
和PRIVATE
关键字来指定以下参数的范围。PRIVATE和PUBLIC项将填充的INCLUDE_DIRECTORIES属性。PUBLIC和INTERFACE项将填充的INTERFACE_INCLUDE_DIRECTORIES属性。具体的介绍详见private,public,interface的使用
同时,target_include_directories的参数可以使用语法为$<…>的“生成器表达式”。
target_include_directories() 的功能完全可以使用 include_directories() 实现。但是我还是建议使用 target_include_directories()。为什么?保持清晰!
include_directories(header-dir) 是一个全局包含,向下传递。什么意思呢?就是说如果某个目录的 CMakeLists.txt 中使用了该指令,其下所有的子目录默认也包含了header-dir 目录。
注意
target_include_directories()必须在add_executable()命令之后,这样才能识别到add_executable()的执行文件名,不然会报错:
CMake Error at src/CMakeLists.txt:2 (target_include_directories): Cannot specify include directories for target "spd_test_exe" which is not built by this project.
message
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)
---
message(STATUS "This is BINARY dir" ${xxx_BINARY_DIR}) # 这里是cmake默认隐式帮忙定义的xxx_BINARY_DIR 和 xxx_SOURCE_DIR,xxx代表proj_name
message(STATUS "This is SOURCE dir" ${xxx_SOURCE_DIR}) # message是用于向终端输出信息。
message用于向终端输出用户定义的信息。
包含以下三种类型:
- SEND_ERROR:产生错误,生成过程被跳过
- STATUS:输出前缀为--的信息
- FATAL_ERROR:立即终止所有的CMake过程
find_file
FIND_FILE(<VAR> name1 [path1 path2 ...] [DOC "docstring"])
---
set(CMAKE_INCLUDE_PATH /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include)
find_file(MY_FUNC functions.h )
message(STATUS "MY_FUNC: " ${MY_FUNC})
if (MY_FUNC)
message(STATUS "Found header file: ${MY_FUNC}")
else()
message(STATUS "Could not find functions.h")
endif()
---
output:
-- MY_FUNC: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include/functions.h
-- Found header file: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include/functions.h
该命令用于查找指定文件的完整路径。
创建一个名为< VAR >
的缓存条目(如果指定了NO_CACHE,则是一个普通变量)来存储此命令的结果。
如果找到文件的完整路径,则结果存储在变量中,并且搜索不会重复,除非该变量被清除。
如果没有找到,结果将是< VAR >-NOTFOUND
。
add_compile_options
add_compile_options(-Wall -g -std=c++11)
---
make之后会显示warning:
/home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/src/printhello.cpp: In function ‘void printhello()’:
/home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/src/printhello.cpp:7:6: warning: unused variable ‘i’ [-Wunused-variable]
int i;
^
[ 40%] Building CXX object src/CMakeFiles/hello.dir/src/factorial.cpp.o
[ 60%] Linking CXX shared library ../lib/libhello.so
[ 60%] Built target hello
[ 80%] Building CXX object src/CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable ../bin/main
[100%] Built target main
install
---
//需要在顶层的CMakeLists.txt里修改安装路径
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install)
message("CMAKE_SOURCE_DIR: " ${CMAKE_BINARY_DIR})
message("CMAKE_INSTALL_PREFIX: " ${CMAKE_INSTALL_PREFIX})
//然后在内部的CMakeLists.txt里install
install(TARGETS main hello
ARCHIVE DESTINATION lib #静态库
LIBRARY DESTINATION lib #动态库
RUNTIME DESTINATION bin #可执行文件
)
以上命令会安装一个动态库,一个静态库,一个可执行文件。CMAKE会根据target类型自动推断。TARGETS 表示我们现在要安装的是TARGETS,除此之外你还可以安装FILES、DIRECTORY等。这里不讲。
通过ARCHIVE/LIBRARY/RUNTIME关键字分别指定静态库、动态库和可执行文件的属性。比如指定安装后缀DESTINATION。例如静态库安装路径就是前缀+后缀=${CMAKE_INSTALL_PREFIX}/install/lib。还可以从/开始直接指定绝对路径。
其中CMAKE_INSTALL_PREFIX可以通过顶层目录中的CMakeLists.txt来定义(注意,需要是顶层目录)。你也可以通过-D CMAKE_INSTALL_PREFIX=xxx来从外部命令行传递
execute_process
execute_process
命令将从当前正在执行的CMake进程中派生一个或多个子进程,从而提供了在配置项目时运行任意命令的方法。可以在一次调用execute_process时执行多个命令。但请注意,每个命令的输出将通过管道传输到下一个命令中。
execute_process(COMMAND <cmd1> [<arguments>]
[COMMAND <cmd2> [<arguments>]]...
[WORKING_DIRECTORY <directory>]
[TIMEOUT <seconds>]
[RESULT_VARIABLE <variable>]
[RESULTS_VARIABLE <variable>]
[OUTPUT_VARIABLE <variable>]
[ERROR_VARIABLE <variable>]
[INPUT_FILE <file>]
[OUTPUT_FILE <file>]
[ERROR_FILE <file>]
[OUTPUT_QUIET]
[ERROR_QUIET]
[COMMAND_ECHO <where>]
[OUTPUT_STRIP_TRAILING_WHITESPACE]
[ERROR_STRIP_TRAILING_WHITESPACE]
[ENCODING <name>]
[ECHO_OUTPUT_VARIABLE]
[ECHO_ERROR_VARIABLE]
[COMMAND_ERROR_IS_FATAL <ANY|LAST>])
该命令可以一次性执行多条指令,并且每条指令执行的结果作为下一条指令的输入。下面介绍几个重要的参数:
- WORKING_DIRECTORY:指定应该在哪个目录中执行命令;
- TIMEOUT:超时时间。如果指令在超时时间(单位:秒)内没有执行完成,那么所有的指令会被中断。同时,参数 RESULT_VARIABLE 的值会被设置为 “timeout”;
- RESULT_VARIABLE将包含进程运行的结果。这要么是一个整数,表示执行成功,要么是一个带有错误条件的字符串。
- OUTPUT_VARIABLE和ERROR_VARIABLE将包含执行命令的标准输出和标准错误。由于命令的输出是通过管道传输的,因此只有最后一个命令的标准输出才会保存到OUTPUT_VARIABLE中。
- INPUT_FILE指定标准输入重定向的文件名
- OUTPUT_FILE指定标准输出重定向的文件名
- ERROR_FILE指定标准错误输出重定向的文件名
- 设置OUTPUT_QUIET和ERROR_QUIET后,CMake将静默地忽略标准输出和标准错误。
- 设置OUTPUT_STRIP_TRAILING_WHITESPACE,可以删除运行命令的标准输出中的任何尾随空格
- 设置ERROR_STRIP_TRAILING_WHITESPACE,可以删除运行命令的错误输出中的任何尾随空格。
ex:
获取源码顶层目录中的所有文件名
cmake_minimum_required(VERSION 3.15)
project(cmake-exe-test)
execute_process(COMMAND ls ..
OUTPUT_VARIABLE ls_ret)
message(STATUS "ls output:\n${ls_ret}")
ex:
set(_module_name "cffi")
execute_process(
COMMAND
${PYTHON_EXECUTABLE} "-c" "import ${_module_name}; print(${_module_name}.__version__)"
OUTPUT_VARIABLE _stdout
ERROR_VARIABLE _stderr
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
)
if(_stderr MATCHES "ModuleNotFoundError")
message(STATUS "Module ${_module_name} not found")
else()
message(STATUS "Found module ${_module_name} v${_stdout}")
endif()
该命令检查python -c "import cffi; print(cffi.__version__)"
的输出。如果没有找到模块,_stderr
将包含ModuleNotFoundError
,我们将在if语句中对其进行检查。本例中,我们将打印Module cffi not found
。如果导入成功,Python代码将打印模块的版本,该模块通过管道输入_stdout
,这样就可以打印如下内容:
message(STATUS "Found module ${_module_name} v${_stdout}")
参考:
配置时运行自定义命令
find_path、find_library、find_program
【cmake教程】find_path、find_library、find_progra
list()
CMake变量
cmake的内置变量
有个脚本打印大部分cmake内置变量。这是从CMake wiki上抄来的。
原脚本位置
# ------------------------- Begin Generic CMake Variable Logging ------------------
# /* C++ comment style not allowed */
# if you are building in-source, this is the same as CMAKE_SOURCE_DIR, otherwise
# this is the top level directory of your build tree
MESSAGE( STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR} )
# if you are building in-source, this is the same as CMAKE_CURRENT_SOURCE_DIR, otherwise this
# is the directory where the compiled or generated files from the current CMakeLists.txt will go to
MESSAGE( STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR} )
# this is the directory, from which cmake was started, i.e. the top level source directory
MESSAGE( STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR} )
# this is the directory where the currently processed CMakeLists.txt is located in
MESSAGE( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} )
# contains the full path to the top level directory of your build tree
MESSAGE( STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR} )
# contains the full path to the root of your project source directory,
# i.e. to the nearest directory where CMakeLists.txt contains the PROJECT() command
MESSAGE( STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR} )
# set this variable to specify a common place where CMake should put all executable files
# (instead of CMAKE_CURRENT_BINARY_DIR)
MESSAGE( STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH} )
# set this variable to specify a common place where CMake should put all libraries
# (instead of CMAKE_CURRENT_BINARY_DIR)
MESSAGE( STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH} )
# tell CMake to search first in directories listed in CMAKE_MODULE_PATH
# when you use FIND_PACKAGE() or INCLUDE()
MESSAGE( STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH} )
# this is the complete path of the cmake which runs currently (e.g. /usr/local/bin/cmake)
MESSAGE( STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND} )
# this is the CMake installation directory
MESSAGE( STATUS "CMAKE_ROOT: " ${CMAKE_ROOT} )
# this is the filename including the complete path of the file where this variable is used.
MESSAGE( STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE} )
# this is linenumber where the variable is used
MESSAGE( STATUS "CMAKE_CURRENT_LIST_LINE: " ${CMAKE_CURRENT_LIST_LINE} )
# this is used when searching for include files e.g. using the FIND_PATH() command.
MESSAGE( STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH} )
# this is used when searching for libraries e.g. using the FIND_LIBRARY() command.
MESSAGE( STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH} )
# the complete system name, e.g. "Linux-2.4.22", "FreeBSD-5.4-RELEASE" or "Windows 5.1"
MESSAGE( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} )
# the short system name, e.g. "Linux", "FreeBSD" or "Windows"
MESSAGE( STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME} )
# only the version part of CMAKE_SYSTEM
MESSAGE( STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION} )
# the processor name (e.g. "Intel(R) Pentium(R) M processor 2.00GHz")
MESSAGE( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} )
# is TRUE on all UNIX-like OS's, including Apple OS X and CygWin
MESSAGE( STATUS "UNIX: " ${UNIX} )
# is TRUE on Windows, including CygWin
MESSAGE( STATUS "WIN32: " ${WIN32} )
# is TRUE on Apple OS X
MESSAGE( STATUS "APPLE: " ${APPLE} )
# is TRUE when using the MinGW compiler in Windows
MESSAGE( STATUS "MINGW: " ${MINGW} )
# is TRUE on Windows when using the CygWin version of cmake
MESSAGE( STATUS "CYGWIN: " ${CYGWIN} )
# is TRUE on Windows when using a Borland compiler
MESSAGE( STATUS "BORLAND: " ${BORLAND} )
# Microsoft compiler
MESSAGE( STATUS "MSVC: " ${MSVC} )
MESSAGE( STATUS "MSVC_IDE: " ${MSVC_IDE} )
MESSAGE( STATUS "MSVC60: " ${MSVC60} )
MESSAGE( STATUS "MSVC70: " ${MSVC70} )
MESSAGE( STATUS "MSVC71: " ${MSVC71} )
MESSAGE( STATUS "MSVC80: " ${MSVC80} )
MESSAGE( STATUS "CMAKE_COMPILER_2005: " ${CMAKE_COMPILER_2005} )
# set this to true if you don't want to rebuild the object files if the rules have changed,
# but not the actual source files or headers (e.g. if you changed the some compiler switches)
MESSAGE( STATUS "CMAKE_SKIP_RULE_DEPENDENCY: " ${CMAKE_SKIP_RULE_DEPENDENCY} )
# since CMake 2.1 the install rule depends on all, i.e. everything will be built before installing.
# If you don't like this, set this one to true.
MESSAGE( STATUS "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY: " ${CMAKE_SKIP_INSTALL_ALL_DEPENDENCY} )
# If set, runtime paths are not added when using shared libraries. Default it is set to OFF
MESSAGE( STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH} )
# set this to true if you are using makefiles and want to see the full compile and link
# commands instead of only the shortened ones
MESSAGE( STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE} )
# this will cause CMake to not put in the rules that re-run CMake. This might be useful if
# you want to use the generated build files on another machine.
MESSAGE( STATUS "CMAKE_SUPPRESS_REGENERATION: " ${CMAKE_SUPPRESS_REGENERATION} )
# A simple way to get switches to the compiler is to use ADD_DEFINITIONS().
# But there are also two variables exactly for this purpose:
# the compiler flags for compiling C sources
MESSAGE( STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} )
# the compiler flags for compiling C++ sources
MESSAGE( STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS} )
# Choose the type of build. Example: SET(CMAKE_BUILD_TYPE Debug)
MESSAGE( STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} )
# if this is set to ON, then all libraries are built as shared libraries by default.
MESSAGE( STATUS "BUILD_SHARED_LIBS: " ${BUILD_SHARED_LIBS} )
# the compiler used for C files
MESSAGE( STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} )
# the compiler used for C++ files
MESSAGE( STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER} )
# if the compiler is a variant of gcc, this should be set to 1
MESSAGE( STATUS "CMAKE_COMPILER_IS_GNUCC: " ${CMAKE_COMPILER_IS_GNUCC} )
# if the compiler is a variant of g++, this should be set to 1
MESSAGE( STATUS "CMAKE_COMPILER_IS_GNUCXX : " ${CMAKE_COMPILER_IS_GNUCXX} )
# the tools for creating libraries
MESSAGE( STATUS "CMAKE_AR: " ${CMAKE_AR} )
MESSAGE( STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB} )
#
#MESSAGE( STATUS ": " ${} )
# ------------------------- End of Generic CMake Variable Logging ------------------
打印如下:
# xxx @ xxx-Latitude-7490 in ~/Desktop/makefile_tuto/demo5_sub_directory/build [16:11:38]
$ cmake ..
-- The CXX compiler identification is GNU 7.5.0
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMAKE_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/build
-- CMAKE_CURRENT_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/build/src
-- CMAKE_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory
-- CMAKE_CURRENT_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/src
-- PROJECT_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/build
-- PROJECT_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory
-- EXECUTABLE_OUTPUT_PATH:
-- LIBRARY_OUTPUT_PATH:
-- CMAKE_MODULE_PATH:
-- CMAKE_COMMAND: /usr/bin/cmake
-- CMAKE_ROOT: /usr/share/cmake-3.10
-- CMAKE_CURRENT_LIST_FILE: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/src/CMakeLists.txt
-- CMAKE_CURRENT_LIST_LINE: 64
-- CMAKE_INCLUDE_PATH:
-- CMAKE_LIBRARY_PATH:
-- CMAKE_SYSTEM: Linux-5.10.76-xenomai
-- CMAKE_SYSTEM_NAME: Linux
-- CMAKE_SYSTEM_VERSION: 5.10.76-xenomai
-- CMAKE_SYSTEM_PROCESSOR: x86_64
-- UNIX: 1
-- WIN32:
-- APPLE:
-- MINGW:
-- CYGWIN:
-- BORLAND:
-- MSVC:
-- MSVC_IDE:
-- MSVC60:
-- MSVC70:
-- MSVC71:
-- MSVC80:
-- CMAKE_COMPILER_2005:
-- CMAKE_SKIP_RULE_DEPENDENCY:
-- CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:
-- CMAKE_SKIP_RPATH: NO
-- CMAKE_VERBOSE_MAKEFILE: FALSE
-- CMAKE_SUPPRESS_REGENERATION:
-- CMAKE_C_FLAGS:
-- CMAKE_CXX_FLAGS:
-- CMAKE_BUILD_TYPE:
-- BUILD_SHARED_LIBS:
-- CMAKE_C_COMPILER:
-- CMAKE_CXX_COMPILER: /usr/bin/c++
-- CMAKE_COMPILER_IS_GNUCC:
-- CMAKE_COMPILER_IS_GNUCXX : 1
-- CMAKE_AR: /usr/bin/ar
-- CMAKE_RANLIB: /usr/bin/ranlib
-- Configuring done
-- Generating done
-- Build files have been written to: /home/xxx/Desktop/makefile_tuto/demo5_sub_directory/build
补充:
${CMAKE_CURRENT_DIR}: /home/xxx/Desktop/spdlog_tuto/spdlog_test
环境变量
cmake调用系统环境变量的方式:$ENV{变量名}
,如:
MESSAGE(STATUS “HOME dir: $ENV{HOME}”)`
输出:
“HOMEdir:/home/xxx” //xxx代表用户名
设置环境变量的方式:SET(ENV{变量名} 值)
。
自定义变量
cmake定义与调用自定义变量的方式:定义:SET(变量名 值)
,调用:${变量名}
CMake中包含了大量的内置变量,这些变量的使用方法与自定义变量的是一样的。
工程路径
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
---
messae(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR})
messae(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR})
messae(STATUS "proj_name_SOURCE_DIR: " ${main_test_SOURCE_DIR})
output:
-- CMAKE_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib
-- PROJECT_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib
-- proj_name_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
---
messae(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR})
messae(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR})
messae(STATUS "proj_name_BINARY_DIR: " ${main_test_BINARY_DIR})
output:
-- CMAKE_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/build
-- PROJECT_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/build
-- proj_name_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/build
这三个变量指代的内容是一致的,如果是 in-source
编译,指的就是工程顶层目录,如果是out-of-source
编译(创建独立的build文件夹),指的是工程编译发生的目录(即buid文件夹)
CMAKE_CURRENT_SOURCE_DIR // 指的是当前处理的 CMakeLists.txt文件所在的路径
CMAKE_CURRENT_BINARY_DIR // 如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致,如果是out-ofsource编译,它指的将是 target 编译目录
CMAKE_CURRENT_LIST_FILE // 输出调用这个变量的 CMakeLists.txt 的完整路径。
---
message(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR})
message(STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE})
output:
-- CMAKE_CURRENT_SOURCE_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src
-- CMAKE_CURRENT_BINARY_DIR: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/build/src
-- CMAKE_CURRENT_LIST_FILE: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/CMakeLists.txt
编译器
CMAKE_C_COMPILER # 指定C编译器
CMAKE_CXX_COMPILER # 指定C++编译器
---
message(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER})
message(STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER})
output:
-- CMAKE_C_COMPILER: /usr/bin/cc
-- CMAKE_CXX_COMPILER: /usr/bin/c++
头文件、库文件和cmake模块路径
CMAKE_INCLUDE_PATH
---
set(CMAKE_INCLUDE_PATH /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include)
find_file(MY_FUNC functions.h )
message(STATUS "MY_FUNC: " ${MY_FUNC})
if (MY_FUNC)
message(STATUS "Found header file: ${MY_FUNC}")
else()
message(STATUS "Could not find functions.h")
endif()
message(STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH})
---
output:
-- MY_FUNC: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include/functions.h
-- Found header file: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include/functions.h
-- CMAKE_INCLUDE_PATH: /home/xxx/Desktop/makefile_tuto/demo4_dynamic_lib/src/include
头文件路径。配合FIND_FILE()
以及FIND_PATH()
使用。如果头文件没有存放在如/usr/include
, /usr/local/include
等的常规路径中,则可以通过这个变量进行弥补。但如果不使用 FIND_FILE
和FIND_PATH
,则变量CMAKE_INCLUDE_PATH
是没有任何作用的。
CMAKE_LIBRARY_PATH
CMAKE_LIBRARY_PATH
库文件路径。配合FIND_LIBRARY()
使用,否则将没有任何作用。
CMAKE_MODULE_PATH
CMAKE_MODULE_PATH
cmake模块路径。cmake为上百个软件包提供了查找器(finder):FindXXXX.cmake。当使用非cmake自带的finder时,需要指定finder的路径,这就是CMAKE_MODULE_PATH,配合FIND_PACKAGE()
使用
CMAKE_MODULE_PATH 与 CMAKE_PREFIX_PATH的区别
程序安装
CMAKE_INSTALL_PREFIX
---
//需要在顶层的CMakeLists.txt里修改安装路径
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install)
message("CMAKE_SOURCE_DIR: " ${CMAKE_BINARY_DIR})
message("CMAKE_INSTALL_PREFIX: " ${CMAKE_INSTALL_PREFIX})
控制make install
,把make产生的文件安装到指定的地方。此变量默认的程序安装路径前缀为/usr/local
或%PROGRAMFILES%
。
可执行文件与库文件存放的路径:LIBRARY_OUTPUT_PATH、EXECUTABLE_OUTPUT_PATH
set(LIBRARY_OUTPUT_PATH ${xxx_BINARY_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${xxx_BINARY_DIR}/bin)
分别用来重新定义最终结果(可执行文件和库文件)的存放目录。
private、public、interface
具体的介绍详见private,public,interface的使用
编译类型(Debug & Release)
#设定编译类型为Debug,调试时需要选择为Debug
set(CMAKE_BUILD_TYPE Debug)
#设定编译类型为Release,调试时需要选择为Release
set(CMAKE_BUILD_TYPE Release)
指令解析
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/home/OpenCV ..
此指令分四部分:
一是cmake,
二是-D CMAKE_BUILD_TYPE=RELEASE,
三是-D CMAKE_INSTALL_PREFIX=/home/OpenCV,
四是…
cmake表示对项目进行分析构建,-D相当于定义,-D可以理解为告诉cmake后边要设置一个属性的值,每设置一个属性,就需在属性前面加上一个-D。CMAKE_BUILD_TYPE表示要编译的类型,类型有debug,release。CMAKE_INSTALL_PREFIX是安装路径。…表示要分析的CMakeLists.txt文件在上一层目录中。
所谓安装,即是是将需要的头文件和库文件拷贝到指定的目录中,默认情况下,会是/usr
目录下,有时是/usr/local
目录下。