CMake简单教程

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会链接项B
  • PUBLIC: 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都可见
图片名称

原文链接: https://www.cnblogs.com/shmilyt/p/17033786.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    CMake简单教程

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/310271

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 上午11:27
下一篇 2023年2月16日 上午11:28

相关推荐