【Linux】CMake编译C/C++工程文件

【Linux】CMake编译C/C++工程文件


文章目录

  • 【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中

1.2 两种构建方式

  • 内部构建(in-source build):内部构建会在同级目录下产生一大堆中间文件,与源文件放在一起显得杂乱无章
  • 外部构建(out-of-source build):将编译输出文件与源文件放在不同目录中,常创建一个build目录,将编译输出文件放在build目录下

1.3 CMake构建C/C++工程流程

  • 手动编写CMakeLists.txt
  • 执行cmake PATH生成Makefile(PATH是顶层CMakeLists.txt所在的目录)
  • 执行命令make进行编译

二、CMake重要指令及常用变量

2.1 CMake语法特性

  • 基本语法格式:指令(参数1 参数二…)
  • 参数使用括号括起来
  • 指令名不区分大小写,参数或变量区分大小写
  • 指令名不区分大小写无关,参数和变量区分大小写

2.2 CMake重要指令

  • 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++11add_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 library library2…)

    #将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})
    

2.3 CMake常用变量

  • CMAKE_C_FLAGS:gcc编译选项
  • CMAKE_CXX_FLAGS:g++编译选项
  • CMAKE_BUILD_TYPE:编译类型(Debug、Release)
  • CMAKE_BINARY_DIRPROJECT_BINARY_DIR_BINARY_DIR:如果为in source build,指工程顶层目录;如果是out of source,指工程编译发生的目录
  • CMAKE_SOURCE_DIRPROJECT_SOURCE_DIR_SOURCE_DIR:无论采用何种编译方式都指工程顶层目录
  • CMAKE_C_COMPILER:指定C编译器
  • CMAKE_CXX_COMPILER:指定C++编译器
  • EXECUTABLE_OUTPUT_PATH:可执行文件输出的存放路径
  • LIBRARY_OUTPUT_PATH:库文件输出的存放路径

2.4 生成静态库/共享库

库分为静态库(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生成静态库以及共享库。并列举了一些例子展示了如何进行内部构建和外部构建,以及如何对多源文件进行直接编译以及生成库编译。

参考资料

  1. 静态库与动态库区别
  2. 利用CMakeLists.txt文件添加库
  3. Linux下CMake简明教程
  4. CMake官网

你可能感兴趣的:(Linux,C++,linux,c语言,c++,vscode,开发语言)