不知道从什么地方开始写起,后面慢慢补吧。CMake 系列学习源自CMake-Cookbook,一本翻译来的书。作者提供了书中的源代码,我自己学习的过程中手动码了一遍,放在了MyCmakeExample

中文版在线网站CMake Cookbook,本书中的绝大部分内容源自该中文版翻译,在此只是为了做个学习上的记录,以后项目中用到的时候方便查找,可自由转载。

Chapter 1

chapter 1.1 对单个源文件编译

  1. 配置项目

生成构建器的命令:

1
2
3
$ mkdir -p build
$ cd build
$ cmake ..

等效于

1
$ cmake -H. -Bbuild
  1. 构建项目

构建命令:

1
$ cmake --build .

后面可以带参数,详情如下

1
2
3
4
5
6
7
8
9
10
11
12
$ cmake --build . --target help

The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... rebuild_cache
... hello-world
... edit_cache
... hello-world.o
... hello-world.i
... hello-world.s

chapter 1.2 切换生成器

Tips: 何为生成器呢?cmake会通过CMakeLists.txt文件,生成适用于不同项目类型的makefile文件,然后makefile文件被不同的编译器使用进行编译,考虑到C/C++的开发环境之多,有非常多的种类的项目开发环境,但是cmake基本上都考虑到了,这里有一个小的汇总:cmake的generator详解

使用命令 cmake --help 可以查看本机支持的生成器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Generators

The following generators are available on this platform (* marks default):
* Unix Makefiles = Generates standard UNIX makefiles.
Green Hills MULTI = Generates Green Hills MULTI files
(experimental, work-in-progress).
Ninja = Generates build.ninja files.
Watcom WMake = Generates Watcom WMake makefiles.
CodeBlocks - Ninja = Generates CodeBlocks project files.
CodeBlocks - Unix Makefiles = Generates CodeBlocks project files.
CodeLite - Ninja = Generates CodeLite project files.
CodeLite - Unix Makefiles = Generates CodeLite project files.
Sublime Text 2 - Ninja = Generates Sublime Text 2 project files.
Sublime Text 2 - Unix Makefiles
= Generates Sublime Text 2 project files.
Kate - Ninja = Generates Kate project files.
Kate - Unix Makefiles = Generates Kate project files.
Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.

* 的是默认的生成器,如果想切换成别的,命令如下

1
2
3
$ mkdir -p build
$ cd build
$ cmake -G Ninja ..

chapter 1.3 静态库和动态库

add_library(message STATIC Message.hpp Message.cpp):生成必要的构建指令,将指定的源码编译到库中。add_library的第一个参数是目标名。整个CMakeLists.txt中,可使用相同的名称来引用库。生成的库的实际名称将由CMake通过在前面添加前缀lib和适当的扩展名作为后缀来形成。生成库是根据第二个参数(STATICSHARED)和操作系统确定的。

target_link_libraries(hello-world message): 将库链接到可执行文件。此命令还确保hello-world可执行文件可以正确地依赖于消息库。因此,在消息库链接到hello-world可执行文件之前,需要完成消息库的构建。

编译成功后,构建目录包含libmessage.a一个静态库(在GNU/Linux上)和hello-world可执行文件

CMake接受其他值作为add_library的第二个参数的有效值,本书会用到的值:

  • STATIC:用于创建静态库,即编译文件的打包存档,以便在链接其他目标时使用,例如:可执行文件。
  • SHARED:用于创建动态库,即可以动态链接,并在运行时加载的库。可以在CMakeLists.txt中使用add_library(message SHARED Message.hpp Message.cpp) 从静态库切换到动态共享对象(DSO)。
  • OBJECT:可将给定add_library的列表中的源码编译到目标文件,不将它们归档到静态库中,也不能将它们链接到共享对象中。如果需要一次性创建静态库和动态库,那么使用对象库尤其有用。我们将在本示例中演示。
  • MODULE:又为DSO组。与SHARED库不同,它们不链接到项目中的任何目标,不过可以进行动态加载。该参数可以用于构建运行时插件。

chapter 1.4 条件语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 设置CMake所需的最低版本,低于该版本将报错
cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR)
# 声明项目名称recipe-04,编程语言CXX表示C++
project(recipe-04 LANGUAGES CXX)
# 引入一个逻辑变量,值为OFF
set(USE_LIBRARY OFF)
# 打印变量的值
message(STATUS "Compile sources into a library? ${USE_LIBRARY}")
# 定义BUILD_SHARED_LIBS变量
set(BUILD_SHARED_LIBS OFF)

# 引入一个变量_source,包括Message.hpp和Message.cpp
list(APPEND _sources Message.hpp Message.cpp)

# 引入一个基于USE_LIBRARY的if-else语句,如果逻辑为真
# 则Message.hpp和Message.cpp将打包成一个库
if(USE_LIBRARY)
add_library(message ${_sources})
add_executable(hello-world hello-world.cpp)
target_link_libraries(hello-world message)
else()
add_executable(hello-world hello-world.cpp ${_sources})
endif()

TIPS:_sources变量是一个局部变量,不应该在当前范围之外使用,可以在名称前加下划线。

BUILD_SHARED_LIBS是CMake的一个全局标志。因为CMake内部要查询BUILD_SHARED_LIBS全局变量,所以add_library命令可以在不传递STATIC/SHARED/OBJECT参数的情况下调用;如果为false或未定义,将生成一个静态库。

chapter 1.5 通过外部传参修改变量

通过option替代set,可从外部传值

1
option(USE_LIBRARY "Compile sources into a library" OFF)
1
2
3
$ mkdir -p build
$ cd build
$ cmake -D USE_LIBRARY=ON ..

-D开关用于为CMake设置任何类型的变量:逻辑变量、路径等等。

chapter 1.6 指定编译器

CMake将语言的编译器存储在 CMAKE_<LANG>_COMPILER变量中,建议通过下面的命令显式地设置。

1
$ cmake -D CMAKE_CXX_COMPILER=clang++ ..

CMake提供--system-information标志,它将把关于系统的所有信息转储到屏幕或文件中。可以看到CMAKE_CXX_COMPILERCMAKE_C_COMPILERCMAKE_Fortran_COMPILER的默认值。

1
$ cmake --system-information information.txt

CMake提供了额外的变量来与编译器交互:

  • CMAKE_<LANG>_COMPILER_LOADED :如果为项目启用了语言<LANG>,则将设置为TRUE
  • CMAKE_<LANG>_COMPILER_ID:编译器标识字符串,编译器供应商所特有。例如,GCC用于GNU编译器集合,AppleClang用于macOS上的Clang, MSVC用于Microsoft Visual Studio编译器。注意,不能保证为所有编译器或语言定义此变量。
  • CMAKE_COMPILER_IS_GNU<LANG> :如果语言<LANG>是GNU编译器集合的一部分,则将此逻辑变量设置为TRUE。注意变量名的<LANG>部分遵循GNU约定:C语言为CC, C++语言为CXX, Fortran语言为G77
  • CMAKE_<LANG>_COMPILER_VERSION:此变量包含一个字符串,该字符串给定语言的编译器版本。版本信息在major[.minor[.patch[.tweak]]]中给出。但是,对于CMAKE_<LANG>_COMPILER_ID,不能保证所有编译器或语言都定义了此变量。

尝试使用不同的编译器,下面是个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 设置CMake所需的最低版本,低于该版本将报错
cmake_minimum_required(VERSION 3.12.0 FATAL_ERROR)
# 声明项目名称recipe-04,编程语言CXX表示C++
project(recipe-06 LANGUAGES CXX)

message(STATUS "Is the C++ compiler loaded? ${CMAKE_CXX_COMPILER_LOADED}")

if(CMAKE_CXX_COMPILER_LOADED)
message(STATUS "The C++ compiler ID is: ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Is the C++ from GNU? ${CMAKE_COMPILER_IS_GNUCXX}")
message(STATUS "The C++ compiler version is: ${CMAKE_COMPILER_VERSION}")

endif()

message(STATUS "Is the C compiler loaded? ${CMAKE_C_COMPILER_LOADED}")
if(CMAKE_C_COMPILER_LOADED)
message(STATUS "The C compiler ID is: ${CMAKE_C_COMPILER_ID}")
message(STATUS "Is the C from GNU? ${CMAKE_COMPILER_IS_GNUCC}")
message(STATUS "The C compiler version is: ${CMAKE_COMPILER_VERSION}")
endif()

chapter 1.7 切换构建类型

控制生成构建系统使用的配置变量是CMAKE_BUILD_TYPE。该变量默认为空,CMake识别的值为:

  1. Debug:用于在没有优化的情况下,使用带有调试符号构建库或可执行文件。
  2. Release:用于构建的优化的库或可执行文件,不包含调试符号。
  3. RelWithDebInfo:用于构建较少的优化库或可执行文件,包含调试符号。
  4. MinSizeRel:用于不增加目标代码大小的优化方式,来构建库或可执行文件。

切换构建类型:

1
$ cmake -D CMAKE_BUILD_TYPE=Debug ..

下面是对Visual Studio的CMake调用:

1
2
3
$ mkdir -p build
$ cd build
$ cmake .. -G "Visual Studio 12 2017 Win64" -D CMAKE_CONFIGURATION_TYPES="Release;Debug"

将为Release和Debug配置生成一个构建树。然后,您可以使--config标志来决定构建这两个中的哪一个:

1
$ cmake --build . --config Release

chapter 1.8 设置编译器选项

编译标志

1
2
3
4
list(APPEND flags "-fPIC" "-Wall")
if(NOT WIN32)
list(APPEND flags "-Wextra" "-Wpedantic")
endif()

添加一个新的静态库geometry

1
2
3
4
5
6
7
8
9
10
11
add_library(geometry
STATIC
geometry_circle.cpp
geometry_circle.hpp
geometry_polygon.cpp
geometry_polygon.hpp
geometry_rhombus.cpp
geometry_rhombus.hpp
geometry_square.cpp
geometry_square.hpp
)

设置编译选项

1
2
3
4
target_compile_options(geometry
PRIVATE
${flags}
)

编译选项可以添加三个级别的可见性:INTERFACEPUBLICPRIVATE

可见性的含义如下:

  • PRIVATE,编译选项会应用于给定的目标,不会传递给与目标相关的目标。我们的示例中, 即使compute-areas将链接到geometry库,compute-areas也不会继承geometry目标上设置的编译器选项。
  • INTERFACE,给定的编译选项将只应用于指定目标,并传递给与目标相关的目标。
  • PUBLIC,编译选项将应用于指定目标和使用它的目标。

这种方式添加编译选项,不会影响全局CMake变量CMAKE_<LANG>_FLAGS_<CONFIG>,并能更细粒度控制在哪些目标上使用哪些选项。

如何确定项目在CMake构建时,实际使用了哪些编译标志?一种方法是,使用CMake将额外的参数传递给本地构建工具。

1
2
3
4
$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build . -- VERBOSE=1

chapter 1.9 为语言设定标准

从CMake 3.1版本开始,引入了一个独立于平台和编译器的机制,用于为C++C设置语言标准:为目标设置 <LANG>_STANDARD属性。

1
2
3
4
5
6
7
set_target_properties(animals
PROPERTIES
CXX_STANDARD 14
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE 1
)

这些属性的含义如下

  • CXX_STANDARD会设置我们想要的标准。
  • CXX_EXTENSIONS告诉CMake,只启用ISO C++标准的编译器标志,而不使用特定编译器的扩展。
  • CXX_STANDARD_REQUIRED指定所选标准的版本。如果这个版本不可用,CMake将停止配置并出现错误。当这个属性被设置为OFF时,CMake将寻找下一个标准的最新版本,直到一个合适的标志。这意味着,首先查找C++14,然后是C++11,然后是C++98。(译者注:目前会从C++20C++17开始查找)

chapter 1.10 使用控制流

使用 1.8 中的示例代码

使用-O3编译器优化级别编译库,对目标设置一个私有编译器选项:

1
2
3
4
target_compile_options(geometry
PRIVATE
-O3
)

然后,生成一个源文件列表,以较低的优化选项进行编译:

1
2
3
4
5
list(
APPEND sources_with_lower_optimization
geometry_circle.cpp
geometry_rhombus.cpp
)

循环这些源文件,将它们的优化级别调到-O2。使用它们的源文件属性完成:

1
2
3
4
5
message(STATUS "Setting source properties using IN LISTS syntax:")
foreach(_source IN LISTS sources_with_lower_optimization)
set_source_files_properties(${_source} PROPERTIES COMPILE_FLAGS -O2)
message(STATUS "Appending -O2 flag for ${_source}")
endforeach()

为了确保设置属性,再次循环并在打印每个源文件的COMPILE_FLAGS属性:

1
2
3
4
5
message(STATUS "Querying sources properties using plain syntax:")
foreach(_source ${sources_with_lower_optimization})
get_source_file_property(_flags ${_source} COMPILE_FLAGS)
message(STATUS "Source ${_source} has the following extra COMPILE_FLAGS: ${_flags}")
endforeach()

Chapter 2

chapter 2.1 检测操作系统

根据变量CMAKE_SYSTEM_NAME判断操作系统类型

1
2
3
4
5
6
7
8
9
10
11
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Configuring on/for Linux")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
message(STATUS "Configuring on/for macOS")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
message(STATUS "Configuring on/for Windows")
elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX")
message(STATUS "Configuring on/for IBM AIX")
else()
message(STATUS "Configuring on/for ${CMAKE_SYSTEM_NAME}")
endif()

chapter 2.2 处理与平台相关的源代码

hello-world.cpp,基于预处理器定义IS_WINDOWSIS_LINUXIS_MACOS的条件编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <cstdlib>
#include <iostream>
#include <string>

std::string say_hello() {
#ifdef IS_WINDOWS
return std::string("Hello from Windows!");
#elif IS_LINUX
return std::string("Hello from Linux!");
#elif IS_MACOS
return std::string("Hello from macOS!");
#else
return std::string("Hello from an unknown system!");
#endif
}

int main() {
std::cout << say_hello() << std::endl;
return EXIT_SUCCESS;
}

CMakeLists.txt,通过使用target_compile_definition在预处理阶段使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(recipe-02 LANGUAGES CXX)

add_executable(hello-world hello-world.cpp)

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(hello-world PUBLIC "IS_LINUX")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_compile_definitions(hello-world PUBLIC "IS_MACOS")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(hello-world PUBLIC "IS_WINDOWS")
endif()

PRIVATE|INTERFACE|PUBLIC限定符在 chapter 1.8 中有介绍。

chapter 2.3 处理与编译器相关的源代码

hello-world.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cstdlib>
#include <iostream>
#include <string>

std::string say_hello() {
#ifdef IS_INTEL_CXX_COMPILER
// only compiled when Intel compiler is selected
// such compiler will not compile the other branches
return std::string("Hello Intel compiler!");
#elif IS_GNU_CXX_COMPILER
// only compiled when GNU compiler is selected
// such compiler will not compile the other branches
return std::string("Hello GNU compiler!");
#elif IS_PGI_CXX_COMPILER
// etc.
return std::string("Hello PGI compiler!");
#elif IS_XL_CXX_COMPILER
return std::string("Hello XL compiler!");
#else
return std::string("Hello unknown compiler - have we met before?");
#endif
}

int main() {
std::cout << say_hello() << std::endl;
std::cout << "compiler name is " COMPILER_NAME << std::endl;
return EXIT_SUCCESS;
}

CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
target_compile_definitions(hello-world PUBLIC "COMPILER_NAME=\"${CMAKE_CXX_COMPILER_ID}\"")

if(CMAKE_CXX_COMPILER_ID MATCHES Intel)
target_compile_definitions(hello-world PUBLIC "IS_INTEL_CXX_COMPILER")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
target_compile_definitions(hello-world PUBLIC "IS_GNU_CXX_COMPILER")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES PGI)
target_compile_definitions(hello-world PUBLIC "IS_PGI_CXX_COMPILER")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES XL)
target_compile_definitions(hello-world PUBLIC "IS_XL_CXX_COMPILER")
endif()

chapter 2.4 检测处理器体系结构

通过检测空指针CMAKE_SIZEOF_VOID_P的大小来判断是32位还是64位系统,通过CMAKE_HOST_SYSTEM_PROCESSOR MATCHES来检测处理器架构。

1
2
3
4
5
6
7
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
target_compile_definitions(arch-dependent PUBLIC "IS_64_BIT_ARCH")
message(STATUS "Target is 64 bits")
else()
target_compile_definitions(arch-dependent PUBLIC "IS_32_BIT_ARCH")
message(STATUS "Target is 32 bits")
endif()
1
2
3
4
5
6
7
8
9
10
11
12
13
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i386")
message(STATUS "i386 architecture detected")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "i686")
message(STATUS "i686 architecture detected")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64")
message(STATUS "x86_64 architecture detected")
else()
message(STATUS "host processor architecture is unknown")
endif()

target_compile_definitions(arch-dependent
PUBLIC "ARCHITECTURE=${CMAKE_HOST_SYSTEM_PROCESSOR}"
)

chapter 2.5 检测处理器指令集

核心代码在CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(recipe-05 LANGUAGES CXX)

add_executable(processor-info "")

target_sources(processor-info
PRIVATE
processor-info.cpp
)

target_include_directories(processor-info
PRIVATE
${PROJECT_BINARY_DIR}
)

foreach(key
IN ITEMS
NUMBER_OF_LOGICAL_CORES
NUMBER_OF_PHYSICAL_CORES
TOTAL_VIRTUAL_MEMORY
AVAILABLE_VIRTUAL_MEMORY
TOTAL_PHYSICAL_MEMORY
AVAILABLE_PHYSICAL_MEMORY
IS_64BIT
HAS_FPU
HAS_MMX
HAS_MMX_PLUS
HAS_SSE
HAS_SSE2
HAS_SSE_FP
HAS_SSE_MMX
HAS_AMD_3DNOW
HAS_AMD_3DNOW_PLUS
HAS_IA64
OS_NAME
OS_RELEASE
OS_VERSION
OS_PLATFORM
)
cmake_host_system_information(RESULT _${key} QUERY ${key})
endforeach()

configure_file(config.h.in config.h @ONLY)

add_library将指定的源文件生成链接文件(编译成库)并添加到工程中,add_executable生成可执行文件并与库链接。

target_sources 往目标中追加源文件

target_include_directories 指定目标包含的头文件路径

target_link_libraries 将目标文件与库文件进行链接

target_compile_options 指定目标的编译选项

CMakeLists.txt中的foreach循环会查询多个键值,并定义相应的变量。此示例的核心函数是cmake_host_system_information,它查询主机的系统信息。本例中,我们对每个键使用了一个函数调用。然后,使用这些变量来配置config.h.in中的占位符,输入并生成config.h。此配置使用configure_file命令完成。最后,config.h包含在processor-info.cpp中。编译后,它将把值打印到屏幕上。

configure_file命令的使用:

1
2
3
configure_file(<input> <output>
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])

拷贝 input 文件到其它地方 output,并更改其中的内容。

把 input 文件中@VAR@ 或者 ${VAR} 引用的变量值替换成当前的变量值。

@ONLY是啥意思?

限制变量的替换,因为configure_file命令会把输入文件中@或类型的变量都会替换成其变量值。@ONLY的意思是只替换@类型的变量,而不替换类型的变量都会替换成其变量值。@ONLY的意思是只替换@类型的变量,而不替换类型的变量。这在配置 ${VAR} 语法的脚本时是非常有用的。

configure.h.in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma once

#define NUMBER_OF_LOGICAL_CORES @_NUMBER_OF_LOGICAL_CORES@
#define NUMBER_OF_PHYSICAL_CORES @_NUMBER_OF_PHYSICAL_CORES@
#define TOTAL_VIRTUAL_MEMORY @_TOTAL_VIRTUAL_MEMORY@
#define AVAILABLE_VIRTUAL_MEMORY @_AVAILABLE_VIRTUAL_MEMORY@
#define TOTAL_PHYSICAL_MEMORY @_TOTAL_PHYSICAL_MEMORY@
#define AVAILABLE_PHYSICAL_MEMORY @_AVAILABLE_PHYSICAL_MEMORY@
#define IS_64BIT @_IS_64BIT@
#define HAS_FPU @_HAS_FPU@
#define HAS_MMX @_HAS_MMX@
#define HAS_MMX_PLUS @_HAS_MMX_PLUS@
#define HAS_SSE @_HAS_SSE@
#define HAS_SSE2 @_HAS_SSE2@
#define HAS_SSE_FP @_HAS_SSE_FP@
#define HAS_SSE_MMX @_HAS_SSE_MMX@
#define HAS_AMD_3DNOW @_HAS_AMD_3DNOW@
#define HAS_AMD_3DNOW_PLUS @_HAS_AMD_3DNOW_PLUS@
#define HAS_IA64 @_HAS_IA64@
#define OS_NAME "@_OS_NAME@"
#define OS_RELEASE "@_OS_RELEASE@"
#define OS_VERSION "@_OS_VERSION@"
#define OS_PLATFORM "@_OS_PLATFORM@"

processor-info.cpp只是把这些宏定义打印了一下。

Tips: 关于PROJECT_BINARY_DIRPROJECT_SOURCE_DIR的区别。

如果目录结构是这样:

1
2
3
4
5
.
├── build
├── CMakeLists.txt
├── config.h.in
└── processor-info.cpp

也就是把cmake编译生成的文件放在build文件夹中

那么PROJECT_BINARY_DIR的值为/home/lushuangning/CodeHub/CMake/MyCmakeExample/chapter2/2.5/build

PROJECT_SOURCE_DIR的值为/home/lushuangning/CodeHub/CMake/MyCmakeExample/chapter2/2.5/

如果让cmake编译生成的文件就生成在当前目录下(cmake .),那么PROJECT_BINARY_DIR的值为/home/lushuangning/CodeHub/CMake/MyCmakeExample/chapter2/2.5

chapter 2.6 为 Eigen 库使能向量化

暂未用到,如有需要后期补充。