title: CMake学习
date: 2023-01-07T20:29:55Z
lastmod: 2023-01-07T22:33:31Z
CMake学习
主要就是学习如何写CMakeList.txt, 直接从项目实战
项目结构
- common: 制作动态库, 将整数数转为二进制形式的字符串, 生成dll给main调用
- dependencies/lib_pow: 自定义幂函数
- dependencies/lib_sqrt: 自定义开方函数
- lib: 动态/静态库生成目录(.dll: 动态 .a: 静态(默认))
CMakeList书写
下面看看各个CmakeList怎么写的:
common:
LIBRARY_OUTPUT_PATH
: 库的输出目录PROJECT_SOURCE_DIR
: 项目的根目录, 即到Tutorial目录add_library
: 制作库(动态SHARED , 静态STATIC)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设置库的输出目录
add_library(common SHARED num2Binary.cpp)
#add_library(common STATIC num2Binary.cpp) # 默认为static
lib_pow
lib_pow和lib_sqrt基本一样, 就制作了个静态库
target_include_directories
: 包含依赖的头文件, 这个例子里没啥用)
add_library(pow mypow.cpp)
#target_include_directories(lib_pow INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
dependencies
add_subdirectory增加编译子目录
add_subdirectory(lib_sqrt)
add_subdirectory(lib_pow)
Main
cmake_minimum_required
: 设置Cmake版本project
: 项目名, 版本(一般不用设置)set(CMAKE_CXX_STANDARD 11)
: 设置C++版本为11, 开始构建项目可以设置, 但这里设置更灵活set(CMAKE_CXX_STANDARD_REQUIRED ON)
: 必须使用设置的C++11版本, 若为OFF, 则C++11版本为首选项, 不可用时会使用上一个版本- configure_file(Main/TutorialConfig.h.in TutorialConfig.h): 基于配置文件TutorialConfig.h.in构建头文件TutorialConfig.h, 其中TutorialConfig.h.in为:
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ //项目的子版本号0
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ // 项目的主版本号1
#define USE_MYMATH // 这个是源代码中的一个开关
option
: 设置当前CMakeList里的开关, 注意CLion中有bug, 需要删除cmake-build-release(debug)目录, 然后点击顶层CmakeList重新加载, 改变才生效.PROJECT_BINARY_DIR
: 工程输出目录, 如cmake-build-release, cmake-build-debug
cmake_minimum_required(VERSION 3.5)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 动态(静态)库输出目录
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # 可执行文件输出目录
configure_file(Main/TutorialConfig.h.in TutorialConfig.h)
option(USE_MYMATH "use tutorial provided math implementations" ON)
if (USE_MYMATH)
add_subdirectory(dependencies)
list(APPEND EXTRA_LIBS pow)
list(APPEND EXTRA_LIBS sqrt)
endif (USE_MYMATH)
include_directories(dependencies/lib_pow)
include_directories(dependencies/lib_sqrt)
include_directories(lib/include)
add_executable(Tutorial Main/main.cpp)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
target_link_libraries(Tutorial PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/lib/libcommon.dll)
add_subdirectory(common)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}")
其中开关的内容:
if (USE_MYMATH)
add_subdirectory(dependencies) # 添加子文件夹
list(APPEND EXTRA_LIBS pow) # 将子文件夹下的库文件pow和sqrt的名字保存为EXTRA_LIBS变量
list(APPEND EXTRA_LIBS sqrt)
endif (USE_MYMATH)
include_directories
: 添加项目头文件搜索路径, 那么项目中该目录下的头文件可以直接include
, 而不用写出完整的相对路径add_executable(Tutorial Main/main.cpp):
添加可执行文件, 名字为Tutorial, 后面是其包含的头文件- target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS}): 可执行文件链接依赖库, 之前依赖库已经保存在变量EXTRA_LIBS中
link时的选项PUBLIC, INTERFACE, PRIVATE区别
结论
先说结论, 以target_link_libraries(A xx B)
为例
PRIVATE
: B仅链接到A, 若C链接到A, C不会链接依赖项B (即链接无传递性)INTERFACE
: B并不链接到A, 当C链接了A, C会链接项BPUBLIC
: B链接A, 若C链接A, C也会链接依赖项B
实操
以具体例子说明:
CMake_link_test
├── cmake-build-release
├── bar.cpp
├── bar.h
├── CMakeLists.txt
├── foo.cpp
├── foo.h
└── app.cpp
foo.h
int foo();
foo.cpp
#include "foo.h"
int foo(){
return 3;
}
bar.h
#include "foo.h"
int bar();
bar.cpp
#include "bar.h"
int bar() {
return 5;
//return foo() + 3;
}
main.cpp
#include <iostream>
#include "bar.h"
int main() {
std::cout << bar() << std::endl;
std::cout << foo() << std::endl;
return 0;
}
CMakeList为:
cmake_minimum_required(VERSION 3.16)
project(foobar)
set(CMAKE_SKIP_RPATH TRUE)
include_directories(${CMAKE_SOURCE_DIR})
add_library(foo SHARED foo.cpp)
add_library(bar SHARED bar.cpp)
target_link_libraries(bar INTERFACE foo)
add_executable(app app.cpp)
target_link_libraries(app bar)
- 链接方式为PRIVATE , 发现app报错: app.cpp:6: undefined reference to `foo()'
target_link_libraries(bar PRIVATE foo)
- 链接方式为PRIVATE, app貌似可以正常执行
target_link_libraries(bar INTERFACE foo)
但修改bar的实现为: return foo() + 3;
, 即bar中使用了foo.h中定义的函数, 报错: bar.cpp:9: undefined reference to `foo()'
- 链接方式为PUBLIC: 无论bar.cpp中是否引用foo.h中定义的符号, 都正常执行.
总结
假设库的依赖关系: linux下静态库为.so, windows下.a
flowchart LR app-->libbar.so libbar.so-->foo.so- PRIVATE: foo.so对app不可见
- NTERFACE: foo.so对bar不可见, 但可将foo.h头文件传递给app, 换句话说foo只提供给bar接口, 而不提供具体实现. 故foo.so对app可见,对bar不可见
- PUBLIC: 等于PRIVATE + INTERFACE, foo对bar和app都可见
标签:教程,include,bar,lib,add,简单,cpp,CMake,foo From: https://www.cnblogs.com/shmilyt/p/17033786.html