使用CMake构建库 Build library with CMake.
在大多数时候我们无法仅依赖一个源码树就完成整个项目,当你熟悉抽象之后,你的项目会逐渐变得越来越复杂,或是为了向其他开发人员隐去实现细节,通常会将项目的具体实现封装成库,供他人调用。
一个简单的类
在PROJECT_SOURCE_DIR
下新建lib/math
目录,定义在math
空间的operations
类定义了四个数学运算:加减乘除。
Copy // operations.hpp
#ifndef CMAKEHELLO_OPERATIONS_HPP
#define CMAKEHELLO_OPERATIONS_HPP
namespace math {
class operations{
public:
int sum(const int &a, const int &b);
int mult(const int &a, const int &b);
int div(const int &a, const int &b);
int sub(const int &a, const int &b);
};
}
#endif //CMAKEHELLO_OPERATIONS_HPP
// operations.cpp
#include <exception>
#include <stdexcept>
#include <iostream>
#include "operations.hpp"
int math::operations::sum(const int &a, const int &b){
return a + b;
}
int math::operations::mult(const int &a, const int &b){
return a * b;
}
int math::operations::div(const int &a, const int &b){
if(b == 0){
throw std::overflow_error("Divide by zero exception");
}
return a/b;
}
int math::operations::sub(const int &a, const int &b){
return a - b;
}
这时我们修改main.cpp
让他包含operations.hpp
并调用sum
函数以便不需要再次实现它。
Copy #include <iostream>
#include "lib/math/operations.hpp"
int main() {
std::cout<<"Hello CMake!"<<std::endl;
math::operations op;
int sum = op.sum(3, 4);
std::cout<<"Sum of 3 + 4 :"<<sum<<std::endl;
return 0;
}
Library和Target一起编译
想要让main.cpp
编译通过,最简单的方法就是将operations.cpp
和operations.hpp
作为add_executable
要编译的源码的一部分,一起编译:
Copy cmake_minimum_required(VERSION 3.9.1)
project(CMakeHello)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
add_executable(cmake_hello main.cpp lib/math/operations.cpp lib/math/operations.hpp)
当要编译的源码变多之后,add_executable
的参数列表将变得非常的长,更推荐的做法是将你要编译的源码文件定义为一个变量SOURCES
,然后在add_executable
命令中直接引用:
Copy cmake_minimum_required(VERSION 3.9.1)
project(CMakeHello)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(SOURCES main.cpp
lib/math/operations.cpp
lib/math/operations.hpp)
add_executable(cmake_hello ${SOURCES})
单独编译math::operations
除了同时编译,更常见的做法是将operatioins.cpp
编译成一个单独的库:
Copy cmake_minimum_required(VERSION 3.9.1)
project(CMakeHello)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
# 设置编出的库位于build/lib下
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
message(${CMAKE_BINARY_DIR})
add_library(math SHARED lib/math/operations.cpp) # 动态库
#add_library(math STATIC lib/math/operations.cpp) # 静态库
add_executable(cmake_hello main.cpp)
target_link_libraries(cmake_hello math)
.so VS .a
不同平台下动态库文件的拓展名:
不同平台下静态库文件的拓展名:
动态库主要放置在编译主机的共享资源中,以确保多个应用程序能够使用它们。编译器的构建系统假设,动态库在执行期间在一个共享文件夹中,它将在执行期间处理这些来自动态库的资源,因此应用程序二进制大小将更小。但这也将导致一些性能上的损耗,因为每次执行都需要从动态库中加载指令。
静态库用于编译器将指令直接获取到应用程序二进制文件中,因此库中需要的所有代码都已经注入到最终的应用程序二进制文件中。这增加了目标对象的大小,但是性能得到了提高。并且使用静态库构建的应用程序不需要依赖于运行平台。
编译成sub-module
另外一种编译库的方法是,将库当作当前项目的一个子模块,在编译主程序之前先单独编译这个库。
在lib/math
目录下新建一个CMakeLists.txt
:
Copy cmake_minimum_required(VERSION 3.9.1)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
add_library(math SHARED operations.cpp)
在主程序的CMakeLists.txt
中添加add_subdirectory
命令:
Copy cmake_minimum_required(VERSION 3.9.1)
project(CMakeHello)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
message(${CMAKE_BINARY_DIR})
add_subdirectory(lib/math)
add_executable(cmake_hello main.cpp)
target_link_libraries(cmake_hello math)
add_subdirectory
命令使得CMake进入该目录并依照该目录下的CMakeLists.txt
单独进行构建。
注: 在lib/math/CMakeLists.txt
中指定的库输出路径为${CMAKE_BINARY_DIR/lib}
这意味着编译输出的libmath.so
将位于主程序构建目录的lib
目录下,假如你使用外部编译并将编译目录命名为build
,那libmath.so
将位于build/lib
目录下,并且会为lib/math
创建自己的构建空间:
Copy .
├── bin
│ └── cmake_hello
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.21.0-rc3
│ │ ├── CMakeCCompiler.cmake
│ │ ├── CMakeCXXCompiler.cmake
│ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ ├── CMakeSystem.cmake
│ │ ├── CompilerIdC
│ │ │ ├── a.out
│ │ │ ├── CMakeCCompilerId.c
│ │ │ └── tmp
│ │ └── CompilerIdCXX
│ │ ├── a.out
│ │ ├── CMakeCXXCompilerId.cpp
│ │ └── tmp
│ ├── cmake.check_cache
│ ├── CMakeDirectoryInformation.cmake
│ ├── cmake_hello.dir
│ │ ├── build.make
│ │ ├── cmake_clean.cmake
│ │ ├── compiler_depend.internal
│ │ ├── compiler_depend.make
│ │ ├── compiler_depend.ts
│ │ ├── DependInfo.cmake
│ │ ├── depend.make
│ │ ├── flags.make
│ │ ├── link.txt
│ │ ├── main.cpp.o
│ │ ├── main.cpp.o.d
│ │ └── progress.make
│ ├── CMakeOutput.log
│ ├── CMakeTmp
│ ├── Makefile2
│ ├── Makefile.cmake
│ ├── progress.marks
│ └── TargetDirectories.txt
├── cmake_install.cmake
├── lib
│ ├── libmath.so
│ └── math
│ ├── CMakeFiles
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── math.dir
│ │ │ ├── build.make
│ │ │ ├── cmake_clean.cmake
│ │ │ ├── compiler_depend.internal
│ │ │ ├── compiler_depend.make
│ │ │ ├── compiler_depend.ts
│ │ │ ├── DependInfo.cmake
│ │ │ ├── depend.make
│ │ │ ├── flags.make
│ │ │ ├── link.txt
│ │ │ ├── operations.cpp.o
│ │ │ ├── operations.cpp.o.d
│ │ │ └── progress.make
│ │ └── progress.marks
│ ├── cmake_install.cmake
│ └── Makefile
└── Makefile