文章目录
- 【Linux】CMake编译C/C++工程文件
- 前言
- 一、CMake编译工程
- 1.1 两种方式设置编译规则
- 1.2 两种构建方式
- 1.3 CMake构建C/C++工程流程
- 二、CMake重要指令及常用变量
- 2.1 CMake语法特性
- 2.2 CMake重要指令
- 2.3 CMake常用变量
- 2.4 生成静态库/共享库
- 2.4.1生成静态库
- 2.4.2 生成共享库
- 三、使用案例
- 3.1 单文件直接编译(内部构建)
- 3.2 多目录工程直接编译(外部构建)
- 3.3 多目录工程生成库编译(外部构建)
- 总结
- 参考资料
前言
CMake 是一个开源、跨平台的管理源代码构建的工具。CMake 广泛用于 C 和 C++ 语言,但它也可以用于构建其他语言的源代码。makefile通常依赖于你当前的编译平台,使用CMake 可以产生自动化生成makefile,达到可以移植跨平台的目的。
一、CMake编译工程项目主目录存在一个CMakeLists.txt文件
1.1 两种方式设置编译规则- 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可
- 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中
- 内部构建(in-source build):内部构建会在同级目录下产生一大堆中间文件,与源文件放在一起显得杂乱无章
- 外部构建(out-of-source build):将编译输出文件与源文件放在不同目录中,常创建一个build目录,将编译输出文件放在build目录下
- 手动编写CMakeLists.txt
- 执行cmake PATH生成Makefile(PATH是顶层CMakeLists.txt所在的目录)
- 执行命令make进行编译
二、CMake重要指令及常用变量 2.1 CMake语法特性
- 基本语法格式:指令(参数1 参数二…)
- 参数使用括号括起来
- 指令名不区分大小写,参数或变量区分大小写
- 指令名不区分大小写无关,参数和变量区分大小写
-
cmake_minimum_required:指定CMake最小版本要求
语法:cmake_minimum_required(VERSION versionnumber [FATAL_ERROR])#设置CMake最低版本为2.5 cmake_minimum_required(VERSION 2.5)
-
project:定义工程名称,并且可指定工程支持的语言
语法:project(projectname [CXX] [C] [Java])#设置工程名为HELLO project(HELLO)
-
set:显示定义变量
语法:set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]]))#定义变量MYFRIEND_SRC,其值为include/myfriend.h src/myfriend.cpp set(MYFRIEND_SRC include/myfriend.h src/myfriend.cpp)
-
include_directories:向工程添加多个特定的头文件搜索路径
语法:include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)#将目录/usr/include/myincludefolder ./include添加进头文件搜索路径 include_directories(/usr/include/myincludefolder ./include)
-
link_directories:向工程添加多个特定库文件的搜索路径
语法:link_directories(dir1 dir2 …)#将目录/usr/lib/mylibfolder ./lib添加进库文件搜索路径 link_directories(/usr/lib/mylibfolder ./lib)
-
add_library:生成库文件
语法:add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)#通过变量SRC生成libhello.so共享库 add_library(hello SHARED ${SRC}) -
add_compile_options:添加编译参数
语法:add_compile_options(option1 option2 …)#添加编译参数-g -o2 -std=c++11(相当于g++ -g -o2 -std=c++11) add_compile_options(-g -o2 -std=c++11)
-
add_executable:生成可执行文件
语法:add_executable(exename souce1 source2 …)#生成可执行文件main_cmake,源文件为main.cpp、src/myfriend.cpp #相当于 g++ main.cpp src/myfriend.cpp -Iinclude -o main add_executable(main_cmake main.cpp src/myfriend.cpp)
-
target_link_libraries:为target添加需要链接的共享库(相当于g++编译时的-I参数)
语法:target_link_libraries(target librarylibrary2…) #将MYFRINED_SHARED动态库文件链接到可执行文件main_cmake target_link_libraries(main_cmake MYFRINED_SHARED)
-
add_subdirectories:向当前工程添加存放源文件的子目录,并且可以指定中间二进制和目标二进制文件的存放位置
语法:add_subdirectories(source_dir [binary_dir] [EXCLUDE_FROM_ALL])#添加src子目录,src需要有一个CMakeLists.txt文件 add_subdirectories(src)
-
aux_source_directory:发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令临时被用来构建源文件列表
语法:aux_source_directory(dir VARIABLE)#定义SRC变量,其值为当前目录(.)下所有的源代码文件 aux_source_directory(. SRC) #编译SRC变量所代表的所有源代码文件,生成main可执行文件 add_executable(main ${SRC})
- CMAKE_C_FLAGS:gcc编译选项
- CMAKE_CXX_FLAGS:g++编译选项
- CMAKE_BUILD_TYPE:编译类型(Debug、Release)
- CMAKE_BINARY_DIR、PROJECT_BINARY_DIR、_BINARY_DIR:如果为in source build,指工程顶层目录;如果是out of source,指工程编译发生的目录
- CMAKE_SOURCE_DIR、PROJECT_SOURCE_DIR、_SOURCE_DIR:无论采用何种编译方式都指工程顶层目录
- CMAKE_C_COMPILER:指定C编译器
- CMAKE_CXX_COMPILER:指定C++编译器
- EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径
- LIBRARY_OUTPUT_PATH:库文件输出的存放路径
库分为静态库(Static Library)和共享库(Shared library)两类。静态库文件的扩展名是.a,共享库文件的扩展名是.so(在CYGWIN环境下,分别叫做.o和.dll)。
2.4.1生成静态库静态库:可重定位目标文件以一种特定的方式打包成一个单独的文件,并且在链接生成可执行文件时,从这个单独的文件中“拷贝”它自己需要的内容到最终的可执行文件中。这个单独的文件,称为静态库。linux中通常以.a(archive)为后缀
CMakeLists.txt:
#设置CMake最低版本为2.5 cmake_minimum_required(VERSION 2.5) #设置工程名为HELLO project(HELLO) #通过hello.cpp生成静态库文件libhello.a add_library(hello hello.cpp)
[vvvcxjvvv@localhost hello]$ cd build [vvvcxjvvv@localhost build]$ cmake ../ -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 -- Configuring done -- Generating done -- Build files have been written to: /home/vvvcxjvvv/Desktop/cxj_data/mycode/hello/build [vvvcxjvvv@localhost build]$ make Scanning dependencies of target hello [100%] Building CXX object CMakeFiles/hello.dir/hello.o Linking CXX static library libhello.a [100%] Built target hello [vvvcxjvvv@localhost build]$2.4.2 生成共享库
动态库:动态库和静态库类似,但是它并不在链接时将需要的二进制代码都“拷贝”到可执行文件中,而是仅仅“拷贝”一些重定位和符号表信息,这些信息可以在程序运行时完成真正的链接过程。linux中通常以.so(shared object)作为后缀。
CMakeLists.txt编写:
#设置最低cmake版本
cmake_minimum_required(VERSION 2.5)
#设置项目名称为MYFRIEND
project(MYFRIEND)
#定义变量MYFRIEND_SRC为include/myfriend.h src/myfriend.cpp
set(MYFRIEND_SRC include/myfriend.h src/myfriend.cpp)
#将include目录添加进头文件搜索路径(重要!!!否则找不到include/myfriend.h)
include_directories(include)
#通过变量MYFRIEND_SRC生成libMYFRINED_SHARED.so共享库
add_library(MYFRINED_SHARED SHARED ${MYFRIEND_SRC})
[vvvcxjvvv@localhost temp3]$ cd build [vvvcxjvvv@localhost build]$ cmake ../ -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 [vvvcxjvvv@localhost build]$ cmake ../ -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 -- Configuring done -- Generating done -- Build files have been written to: /home/vvvcxjvvv/Desktop/cxj_data/mycode/temp3/build [vvvcxjvvv@localhost build]$ make Scanning dependencies of target MYFRINED_SHARED [100%] Building CXX object CMakeFiles/MYFRINED_SHARED.dir/src/myfriend.o Linking CXX shared library libMYFRINED_SHARED.so [100%] Built target MYFRINED_SHARED [vvvcxjvvv@localhost build]$
三、使用案例 3.1 单文件直接编译(内部构建)
CMakeLists.txt编写:
#设置CMake最低版本为2.5 cmake_minimum_required(VERSION 2.5) #设置工程名为HELLO project(HELLO) #生成可执行文件hello_cmake(源文件为hello.cpp) add_executable(hello_cmake hello.cpp)
初始文件目录:
[vvvcxjvvv@localhost hello]$ tree . . ├── CMakeLists.txt └── hello.cpp 0 directories, 2 files
执行cmake .——进行内部构建
[vvvcxjvvv@localhost hello]$ cmake . -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 -- Configuring done -- Generating done -- Build files have been written to: /home/vvvcxjvvv/Desktop/cxj_data/mycode/hello [vvvcxjvvv@localhost hello]$ tree . . ├── CMakeCache.txt ├── CMakeFiles │ ├── 2.8.12.2 │ │ ├── CMakeCCompiler.cmake │ │ ├── CMakeCXXCompiler.cmake │ │ ├── CMakeDetermineCompilerABI_C.bin │ │ ├── CMakeDetermineCompilerABI_CXX.bin │ │ ├── CMakeSystem.cmake │ │ ├── CompilerIdC │ │ │ ├── a.out │ │ │ └── CMakeCCompilerId.c │ │ └── CompilerIdCXX │ │ ├── a.out │ │ └── CMakeCXXCompilerId.cpp │ ├── cmake.check_cache │ ├── CMakeDirectoryInformation.cmake │ ├── CMakeOutput.log │ ├── CMakeTmp │ ├── hello_cmake.dir │ │ ├── build.make │ │ ├── cmake_clean.cmake │ │ ├── DependInfo.cmake │ │ ├── depend.make │ │ ├── flags.make │ │ ├── link.txt │ │ └── progress.make │ ├── Makefile2 │ ├── Makefile.cmake │ ├── progress.marks │ └── TargetDirectories.txt ├── cmake_install.cmake ├── CMakeLists.txt ├── hello.cpp └── Makefile 6 directories, 28 files
执行make进行编译:
[vvvcxjvvv@localhost hello]$ make Scanning dependencies of target hello_cmake [100%] Building CXX object CMakeFiles/hello_cmake.dir/hello.o Linking CXX executable hello_cmake [100%] Built target hello_cmake [vvvcxjvvv@localhost hello]$ tree . . ├── CMakeCache.txt ├── CMakeFiles │ ├── 2.8.12.2 │ │ ├── CMakeCCompiler.cmake │ │ ├── CMakeCXXCompiler.cmake │ │ ├── CMakeDetermineCompilerABI_C.bin │ │ ├── CMakeDetermineCompilerABI_CXX.bin │ │ ├── CMakeSystem.cmake │ │ ├── CompilerIdC │ │ │ ├── a.out │ │ │ └── CMakeCCompilerId.c │ │ └── CompilerIdCXX │ │ ├── a.out │ │ └── CMakeCXXCompilerId.cpp │ ├── cmake.check_cache │ ├── CMakeDirectoryInformation.cmake │ ├── CMakeOutput.log │ ├── CMakeTmp │ ├── hello_cmake.dir │ │ ├── build.make │ │ ├── cmake_clean.cmake │ │ ├── CXX.includecache │ │ ├── DependInfo.cmake │ │ ├── depend.internal │ │ ├── depend.make │ │ ├── flags.make │ │ ├── hello.o │ │ ├── link.txt │ │ └── progress.make │ ├── Makefile2 │ ├── Makefile.cmake │ ├── progress.marks │ └── TargetDirectories.txt ├── cmake_install.cmake ├── CMakeLists.txt ├── hello_cmake ├── hello.cpp └── Makefile 6 directories, 32 files [vvvcxjvvv@localhost hello]$ ./hello_cmake Hello World! [vvvcxjvvv@localhost hello]$3.2 多目录工程直接编译(外部构建)
CMakeLists.txt编写:
#设置最低的cmake版本为2.5 cmake_minimum_required(VERSION 2.5) #设置项目名称为MYFRIEND Project(MYFRIEND) #把源文件目录include添加到工程的头文件搜索路径 include_directories(include) #生成可执行文件main_cmake,源文件为main.cpp、src/myfriend.cpp #相当于 g++ main.cpp src/myfriend.cpp -Iinclude -o main add_executable(main_cmake main.cpp src/myfriend.cpp)
初始目录结构:
[vvvcxjvvv@localhost temp1]$ tree .
.
├── CMakeLists.txt
├── include
│ └── myfriend.h
├── main.cpp
└── src
└── myfriend.cpp
2 directories, 4 files
创建build文件夹存放输出文件——外部构建
[vvvcxjvvv@localhost temp1]$ mkdir build [vvvcxjvvv@localhost temp1]$ cd build
使用cmake ../找到顶层目录下的CMakeLists.txt
[vvvcxjvvv@localhost build]$ cmake ../ -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 -- Configuring done -- Generating done -- Build files have been written to: /home/vvvcxjvvv/Desktop/cxj_data/mycode/temp1/build
使用make进行编译,产生的可执行文件在build目录下
[vvvcxjvvv@localhost build]$ make
Scanning dependencies of target main_cmake
[ 50%] Building CXX object CMakeFiles/main_cmake.dir/main.o
[100%] Building CXX object CMakeFiles/main_cmake.dir/src/myfriend.o
Linking CXX executable main_cmake
[100%] Built target main_cmake
[vvvcxjvvv@localhost build]$ cd ../
[vvvcxjvvv@localhost temp1]$ tree .
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ │ ├── 2.8.12.2
│ │ │ ├── CMakeCCompiler.cmake
│ │ │ ├── CMakeCXXCompiler.cmake
│ │ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ │ ├── CMakeSystem.cmake
│ │ │ ├── CompilerIdC
│ │ │ │ ├── a.out
│ │ │ │ └── CMakeCCompilerId.c
│ │ │ └── CompilerIdCXX
│ │ │ ├── a.out
│ │ │ └── CMakeCXXCompilerId.cpp
│ │ ├── cmake.check_cache
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── CMakeOutput.log
│ │ ├── CMakeTmp
│ │ ├── main_cmake.dir
│ │ │ ├── build.make
│ │ │ ├── cmake_clean.cmake
│ │ │ ├── CXX.includecache
│ │ │ ├── DependInfo.cmake
│ │ │ ├── depend.internal
│ │ │ ├── depend.make
│ │ │ ├── flags.make
│ │ │ ├── link.txt
│ │ │ ├── main.o
│ │ │ ├── progress.make
│ │ │ └── src
│ │ │ └── myfriend.o
│ │ ├── Makefile2
│ │ ├── Makefile.cmake
│ │ ├── progress.marks
│ │ └── TargetDirectories.txt
│ ├── cmake_install.cmake
│ ├── main_cmake
│ └── Makefile
├── CMakeLists.txt
├── include
│ └── myfriend.h
├── main.cpp
└── src
└── myfriend.cpp
10 directories, 35 files
[vvvcxjvvv@localhost temp1]$ cd build
[vvvcxjvvv@localhost build]$ ./main_cmake
Hello Chandler
3.3 多目录工程生成库编译(外部构建)
CMakelists.txt编写:
#设置最低cmake版本
cmake_minimum_required(VERSION 2.5)
#设置项目名称为MYFRIEND
project(MYFRIEND)
#添加编译参数-g -o2 -std=c++11
add_compile_options(-g -o2 -std=c++11)
#定义变量MYFRIEND_SRC为include/myfriend.h src/myfriend.cpp
set(MYFRIEND_SRC include/myfriend.h src/myfriend.cpp)
#将include目录添加进头文件搜索路径(重要!!!否则找不到include/myfriend.h)
include_directories(include)
#通过变量MYFRIEND_SRC生成libMYFRINED_SHARED.so共享库
add_library(MYFRINED_SHARED SHARED ${MYFRIEND_SRC})
#生成可执行文件main_cmake,源文件为main.cpp
add_executable(main_cmake main.cpp)
#将MYFRINED_SHARED动态库文件链接到可执行文件main_cmake
target_link_libraries(main_cmake MYFRINED_SHARED)
创建build文件夹以把输出文件放入build中
进入build文件夹后执行cmake ../——外部构建
[vvvcxjvvv@localhost temp2]$ mkdir build [vvvcxjvvv@localhost temp2]$ cd build [vvvcxjvvv@localhost build]$ cmake ../ -- The C compiler identification is GNU 4.8.5 -- The CXX compiler identification is GNU 4.8.5 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- 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 -- Configuring done -- Generating done -- Build files have been written to: /home/vvvcxjvvv/Desktop/cxj_data/mycode/temp2/build
执行make进行编译(libMYFRINED_SHARED.so、main_cmake都在build目录下)
[vvvcxjvvv@localhost build]$ make Scanning dependencies of target MYFRINED_SHARED [ 50%] Building CXX object CMakeFiles/MYFRINED_SHARED.dir/src/myfriend.o Linking CXX shared library libMYFRINED_SHARED.so [ 50%] Built target MYFRINED_SHARED Scanning dependencies of target main_cmake [100%] Building CXX object CMakeFiles/main_cmake.dir/main.o Linking CXX executable main_cmake [100%] Built target main_cmake [vvvcxjvvv@localhost build]$ ./main_cmake Hello Chandler
总结
本文整理了在Linux环境下使用CMake编译C/C++工程文件的一些知识点,整理了CMake编译规则,常用指令以及常用变量,如何使用CMake生成静态库以及共享库。并列举了一些例子展示了如何进行内部构建和外部构建,以及如何对多源文件进行直接编译以及生成库编译。
参考资料- 静态库与动态库区别
- 利用CMakeLists.txt文件添加库
- Linux下CMake简明教程
- CMake官网



