生成构建文件Makefile
表示输出makefile文件的目录
表示试用clang++作为编译器
cmake -Bbuild -DCMAKE_CXX_COMPILER=clang++
cmake -Bbuild -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17构建可执行文件,编译
cd build;make
或者make -C build,优点是:跨平台
或者cmake --build build
add_executable(输出的可执行文件 输入的多个源文件)
add_executable(a.out main.cpp hello.cpp)add_library(test STATIC source1.cpp source2.cpp) # 生成静态库 libtest.a
add_library(test SHARED source1.cpp source2.cpp) # 生成动态库 libtest.so创建库以后,要在某个可执行文件中使用该库,只需要:
target_link_libraries(myexec PUBLIC test) # 为 myexec 链接刚刚制作的库 libtest.a
有时候我们会有多个可执行文件,他们之间用到的某些功能是相同的,我们想把这些共用的功能做成一个库,方便大家一起共享.
Windows:
Linux:
CMake 除了 add_executable 可以生成可执行文件外,还可以通过 add_library 生成库文件。
add_library 的语法与 add_executable 大致相同,除了他需要指定是动态库还是静态库:
add_library(test STATIC source1.cpp source2.cpp) # 生成静态库 libtest.a
add_library(test SHARED source1.cpp source2.cpp) # 生成动态库 libtest.so
target_link_libraries(myexec PUBLIC test) # 为 myexec 链接刚刚制作的库 libtest.a
其中 PUBLIC 的含义稍后会说明(CMake 中有很多这样的大写修饰符)
什么是符号隐藏?
符号隐藏的作用
安全,去掉不必要的符号,可以增加逆向破解的难度。
压缩空间,符号实际上是放在 dll 中的,去掉这些符号可以缩减 dll 的大小
性能,符号隐藏掉意味着它不会参与到动态链接过程,编译器可以有更大的优化空间,可能会产生更好的性能。
做法:
set_target_properties(mylibPROPERTIES CXX_VISIBILITY_PRESET hidden
)or
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
然后将公开的接口设置__attribute__ ((visibility (“default”)))即可
#pragma oncevoid __attribute__((visibility("default"))) say_hello();
通常会定义宏来协助处理这一部分内容
// Generic helper definitions for shared library support
#if defined _WIN32 || defined __CYGWIN__#define FOX_HELPER_DLL_IMPORT __declspec(dllimport)#define FOX_HELPER_DLL_EXPORT __declspec(dllexport)#define FOX_HELPER_DLL_LOCAL
#else#if __GNUC__ >= 4#define FOX_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))#define FOX_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))#define FOX_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden")))#else#define FOX_HELPER_DLL_IMPORT#define FOX_HELPER_DLL_EXPORT#define FOX_HELPER_DLL_LOCAL#endif
#endif// Now we use the generic helper definitions above to define FOX_API and FOX_LOCAL.
// FOX_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
// FOX_LOCAL is used for non-api symbols.#ifdef FOX_DLL // defined if FOX is compiled as a DLL#ifdef FOX_DLL_EXPORTS // defined if we are building the FOX DLL (instead of using it)#define FOX_API FOX_HELPER_DLL_EXPORT#else#define FOX_API FOX_HELPER_DLL_IMPORT#endif // FOX_DLL_EXPORTS#define FOX_LOCAL FOX_HELPER_DLL_LOCAL
#else // FOX_DLL is not defined: this means FOX is a static lib.#define FX_API#define FOX_LOCAL
#endif // FOX_DLL
使用方法:
class FOX_API Fox {};
position-independent code (PIC):
有-fPIC选项好处:
1)添加 -fPIC 选项实现真正意义上的多个进程共享 .so 库。多个进程引用同一个 -fPIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,操作系统会把它们映射到同一块物理内存上。
2)不添加 -fPIC 选项,加载 .so 库时,需要对代码段引用的数据对象重定位,重定位会修改代码段的内容,造成每个使用这个 .so 文件代码段的进程在内核里都会生成这个 .so 文件代码段的 copy,每个 copy 都不一样,取决于这个 .so 文件代码段和数据段内存映射的位置。不添加 -fPIC 选项,消耗内存,编译的.so文件的优点是加载速度快。
用法:
add_library(mylib SHARED ##)
set_target_properties(mylib PROPERTIES POSITION_INDEPENDENT_CODE ON)其他:若编译成静态库,也不会报错:
add_library(mylib STATIC ##)
set_target_properties(mylib PROPERTIES POSITION_INDEPENDENT_CODE ON)
Linux共享库的文件命名规则必须如下:
最前面使用前缀lib,中间为库的名字,后缀为.so,后面跟着 3 个数字组成的版本号。"x"表示**主版本号,"y"表示次版本号,"z"表示发布版本号。各版本号含义如下:
主版本号表示库的重大升级,不同主版本号的库之间是不兼容的;
次版本号表示库的增量升级,即增加一些新的接口符号,且保持原有符号不变;
发布版本号表示库的一些错误修正、性能的改进等,并不增加任何新的接口,也不对接口进行更改。相同主、次版本号,不同发布版本号的库之间完全兼容,依赖于某个发布版号的程序可以在任何一个其他发布版本号中正常运行,而无需做任何修改。
现在的 Linux 中也存在不少不遵守上述规定的"顽固份子",比如最基本的 C 语言库——Glibc。
SO-NAME作用:
用法:
project(test)
cmake_minimum_required(VERSION 3.16)add_library(mylib SHARED ${CMAKE_CURRENT_SOURCE_DIR}/lib/mylib.cpp)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(xxPROPERTIES CXX_VISIBILITY_PRESET hidden
)set_target_properties(mylib PROPERTIES POSITION_INDEPENDENT_CODE ON)set_target_properties(mylib PROPERTIES VERSION 1.0.0 SOVERSION 1)
测试:
wangji@script-wang:~/code/test/my_course/course/11/03_library/04$ ll build/
total 96
drwxr-xr-x 3 wangji wangji 4096 Feb 13 10:11 ./
drwxr-xr-x 5 wangji wangji 4096 Feb 13 10:10 ../
-rw-r--r-- 1 wangji wangji 14172 Feb 13 10:
drwxr-xr-x 8 wangji wangji 4096 Feb 13 10:11 CMakeFiles/
-rw-r--r-- 1 wangji wangji 9698 Feb 13 10:11 Makefile
-rw-r--r-- 1 wangji wangji 2740 Feb 13 10:11 ake
lrwxrwxrwx 1 wangji wangji 13 Feb 13 10:11 libmylib.so -> libmylib.so.1*
lrwxrwxrwx 1 wangji wangji 17 Feb 13 10:11 libmylib.so.1 -> libmylib.so.1.0.0*wangji@script-wang:~/code/test/my_course/course/11/03_library/04$ ldd build/main linux-vdso.so.1 (0x00007ffc7ffb5000)libmylib.so.1 => /home/wangji/code/test/my_course/course/11/03_library/04/build/libmylib.so.1 (0x00007f8aeca92000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8aec888000)/lib64/ld-linux-x86-64.so.2 (0x00007f8aeca9e000)
INCLUDE_DIRECTORIES(添加头文件目录)
它相当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用
比如:include_directories(“/opt/MATLAB/R2012a/extern/include”)
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:$MATLAB/extern/include
LINK_DIRECTORIES(添加需要链接的库文件目录)
link_directories(directory1 directory2 …)
它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用。
LINK_DIRECTORIES(“/opt/MATLAB/R2012a/bin/glnxa64”)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/MATLAB/R2012a/bin/glnxa64
LINK_LIBRARIES(添加需要链接的库文件路径,注意这里是库的全路径)
LINK_LIBRARIES(“/opt/MATLAB/R2012a/bin/glnxa64/libmx.so”)
TARGET_LINK_LIBRARIES (设置要链接的库文件的名称)
TARGET_LINK_LIBRARIES(myProject hello),连接libhello.so库
DSO missing from command line问题:
libA.so在编译过程中显式的链接了libB.so
可执行文件中使用了libB.so的函数
binuntils版本 ≥ 2.22
#include <stdio.h>int funB1(){printf("in funB1");return 0;
}int funB2(){printf("in funB2");return 0;
}
编译libB.so:
$ gcc libB.cpp -fPIC -shared -o libB.so
libA.so的源码:
#include <stdio.h>int funB1();int funA1(){printf("in funA1 n");funB1();return 0;
}
编译libA.so:
$ gcc libA.cpp -fPIC -shared -o libA.so -Wl,-rpath=./ -L./ -lB
main.cpp
int funA1();
int funB2();int main(){funA1();funB2();return 0;
}
编译main.cpp
gcc main.cpp -L./ -lA
/usr/bin/ld: /tmp/ccDQXTKy.o: undefined reference to symbol '_Z5funB2v'
.//libB.so: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status$ ldd libA.so
linux-vdso.so.1 => (0x00007ffd09def000)libB.so => ./libB.so (0x00007fc513d7d000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc5139b3000)/lib64/ld-linux-x86-64.so.2 (0x00007fc514181000)这里已经显示链接了libB.so,但是编译器要我们显式的链接libB.so?
这是binutils在2.22版本以后,默认把–no-copy-dt-needed-entries这个选项打开了。当打开了这个选项的时候,编译器在链接的时候是不会递归的去获取依赖动态库的依赖项的,于是就会出现上述的问题。
$ gcc main.cpp -L./ -Wl,--copy-dt-needed-entries -lA
Cmake用法:
cmake使用方法“set(CMAKE_EXE_LINKER_FLAGS “-Wl,–copy-dt-needed-entries”)
在多文件编译章中,说到了需要在 main.cpp 声明 hello() 才能引用。为什么?
其实,C++ 是一种强烈依赖上下文信息的编程语言,
问题:如果能只写一遍,然后自动插入到需要使用hello()的地方
头文件 - 批量插入几行代码的硬核方式
递归地使用头文件
复杂的工程中,我们需要划分子模块,通常一个库一个目录,比如:
因为 hello.h 被移到了 hellolib 子文件夹里,因此 main.cpp 里也要改成
add_executable(a.out main.cpp)
target_link_libraries(a.out PUBLIC hellolib)
target_include_directories(a.out PUBLIC hellolib)
#include <cstdio>//方式1
#include "hellolib/hello.h"//方式2
//#include "hello.h" or #include <hello.h>
//CMake中增加# target_include_directories(a.out PUBLIC hellolib)int main() {hello();return 0;
}
add_library(hellolib STATIC hello.cpp)
target_include_directories(hellolib PUBLIC .)
my_course/course/01/
cmake_minimum_required(VERSION 3.12)
project(hellocmake LANGUAGES CXX)add_subdirectory(hellolib)add_executable(a.out main.cpp)
target_link_libraries(a.out PUBLIC hellolib)
除了头文件搜索目录以外,还有这些选项,PUBLIC 和 PRIVATE 对他们同理:
target_include_directories(myapp PUBLIC /usr/include/eigen3) # 添加头文件搜索目录
target_link_libraries(myapp PUBLIC hellolib) # 添加要链接的库
target_add_definitions(myapp PUBLIC MY_MACRO=1) # 添加一个宏定义,等价于#define MY_MACRO 1
target_add_definitions(myapp PUBLIC -DMY_MACRO=1) # 与 MY_MACRO=1 等价
target_compile_options(myapp PUBLIC -fopenmp) # 添加编译器命令行选项
target_sources(myapp PUBLIC hello.cpp other.cpp) # 添加要编译的源文件
以及可以通过下列指令(不推荐使用),把选项加到所有接下来的目标去:
include_directories(/opt/cuda/include) # 给所有的目标添加头文件搜索目录
link_directories(/opt/cuda) # 给所有的目标添加库文件的搜索路径
add_definitions(MY_MACRO=1) #给所有的目标添加一个宏定义
add_compile_options(-fopenmp) #给所有的目标添加编译器命令行选项
有时候我们不满足于 C++ 标准库的功能,难免会用到一些第三方库。
nothings/stb - 大名鼎鼎的 stb_image 系列,涵盖图像,声音,字体等,只需单头文件!
Neargye/magic_enum - 枚举类型的反射,如枚举转字符串等(实现方式很巧妙)
g-truc/glm - 模仿 GLSL 语法的数学矢量/矩阵库(附带一些常用函数,随机数生成等)
Tencent/rapidjson - 单纯的 JSON 库,甚至没依赖 STL(可定制性高,工程美学经典)
ericniebler/range-v3 - C++20 ranges 库就是受到他启发(完全是头文件组成)
fmtlib/fmt - 格式化库,提供 std::format 的替代品(需要 -DFMT_HEADER_ONLY)
gabime/spdlog - 能适配控制台,安卓等多后端的日志库(和 fmt 冲突!)
第二友好的方式则是作为 CMake 子模块引入,也就是通过 add_subdirectory。
fmtlib/fmt - 格式化库,提供 std::format 的替代品
gabime/spdlog - 能适配控制台,安卓等多后端的日志库
ericniebler/range-v3 - C++20 ranges 库就是受到他启发
g-truc/glm - 模仿 GLSL 语法的数学矢量/矩阵库
abseil/abseil-cpp - 旨在补充标准库没有的常用功能
bombela/backward-cpp - 实现了 C++ 的堆栈回溯便于调试
google/googletest - 谷歌单元测试框架
google/benchmark - 谷歌性能评估框架
glfw/glfw - OpenGL 窗口和上下文管理
libigl/libigl - 各种图形学算法大合集
可以通过 find_package 命令寻找系统中的包/库:
find_package(fmt REQUIRED)
target_link_libraries(myexec PUBLIC fmt::fmt)
现代 CMake 认为一个包 (package) 可以提供多个库,又称组件 (components),比如 TBB 这个包,就包含了 tbb, tbbmalloc, tbbmalloc_proxy 这三个组件。因此为避免冲突,每个包都享有一个独立的名字空间,以 :: 的分割(和 C++ 还挺像的)。
你可以指定要用哪几个组件:
find_package(TBB REQUIRED COMPONENTS tbb tbbmalloc REQUIRED)
target_link_libraries(myexec PUBLIC TBB::tbb TBB::tbbmalloc)
fmt::fmt
spdlog::spdlog
range-v3::range-v3
TBB::tbb
OpenVDB::openvdb
Boost::iostreams
Eigen3::Eigen
OpenMP::OpenMP_CXX
Linux 可以用系统自带的包管理器(如 apt)安装 C++ 包。
pacman -S fmt
set -egit clone .git --depth=1rm -rf build
cmake -B build -DCMAKE_TOOLCHAIN_FILE="$PWD/vcpkg/scripts/ake"
cmake --build build --target a.outcd vcpkg
sh bootstrap-vcpkg.sh
./vcpkg install fmt:x64-linux
cd ..build/a.out
-ref:VCPKG 特性 - Versioning
以jsoncpp 为例,介绍几种引入第三方库的方式
(1)方法1:代码依赖
这种方式是把第三方库的完整代码直接添加到我们的项目中,当做项目代码的一部分进行编译,这种方式会把第三方代码和我们的代码混在一起,并不推荐使用。首先我们需要到 jsoncpp 下载需要的头文件和实现代码,放到项目当中。
工程文件目录
├──
├── jsoncpp
│ ├── include
│ │ └── json
│ │ ├── autolink.h
│ │ ├── config.h
│ │ ├── features.h
│ │ ├── forwards.h
│ │ ├── json.h
│ │ ├── reader.h
│ │ ├── value.h
│ │ └── writer.h
│ ├── json_batchallocator.h
│ ├── json_internalarray.inl
│ ├── json_internalmap.inl
│ ├── json_reader.cpp
│ ├── json_value.cpp
│ ├── json_valueiterator.inl
│ └── json_writer.cpp
└──
cmake_minimum_required(VERSION 3.17)
project(includes_full_code)
set(CMAKE_CXX_STANDARD 14)
# 包含头文件
include_directories(./jsoncpp/include)
set(jsoncpp jsoncpp/json_reader.cpp jsoncpp/json_writer.cpp jsoncpp/json_value.cpp)
# 添加可执行代码
add_executable(includes_full_code main.cpp ${jsoncpp})main.cpp
#include <iostream>
#include "json/json.h"
int main() {Json::Value json;json["name"] = "Wiki";json["age"] = 18;std::cout << StyledString() << std::endl;return 0;
}
(2)方法2:优化代码依赖
├──
├── jsoncpp
│ ├──
│ ├── include
│ │ └── json
│ │ ├── autolink.h
│ │ ├── config.h
│ │ ├── features.h
│ │ ├── forwards.h
│ │ ├── json.h
│ │ ├── reader.h
│ │ ├── value.h
│ │ └── writer.h
│ ├── json_batchallocator.h
│ ├── json_internalarray.inl
│ ├── json_internalmap.inl
│ ├── json_reader.cpp
│ ├── json_value.cpp
│ ├── json_valueiterator.inl
│ └── json_writer.cpp
└── main.
cmake_minimum_required(VERSION 3.17)
project(jsoncpp)
add_library(${PROJECT_NAME} json_reader.cpp json_value.cpp json_writer.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/
cmake_minimum_required(VERSION 3.17)
project(multi_cmakelists)
# 添加子工程
add_subdirectory(jsoncpp)
add_executable(${PROJECT_NAME} main.cpp)
# 链接子工程
target_link_libraries(${PROJECT_NAME} jsoncpp)
(3)find_library:编译库方式引入
├──
├── jsoncpp
│ ├── include
│ │ └── json
│ │ ├── autolink.h
│ │ ├── config.h
│ │ ├── features.h
│ │ ├── forwards.h
│ │ ├── json.h
│ │ ├── reader.h
│ │ ├── value.h
│ │ └── writer.h
│ └── libjsoncpp.a
└── main.cppcmake_minimum_required(VERSION 3.17)
project(find_library_example)
include_directories(jsoncpp/include)
add_executable(${PROJECT_NAME} main.cpp)
find_library(jsoncpp_lib NAMES jsoncpp PATHS ./jsoncpp)
target_link_libraries(${PROJECT_NAME} ${jsoncpp_lib})
(4)FetchContent
工程文件目录
├──
└──
cmake_minimum_required(VERSION 3.17)
project(fetch_content_example)
include(FetchContent)
#FetchContent_Declare(jsoncpp
# GIT_REPOSITORY .git
# GIT_TAG 1.9.4)
# 建议使用压缩包的方式依赖,下载速度更快
FetchContent_Declare(jsoncppURL .9.)
FetchContent_MakeAvailable(jsoncpp)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)
(5) CPM
工程文件目录
├──
├── cmake
│ ├── ake
│ ├── ake
│ └── ake
└──
cmake_minimum_required(VERSION 3.17)
project(cpm_example)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
include(CPM)
#CPMAddPackage(
# GIT_REPOSITORY .git
# GIT_TAG 1.9.4)
# 建议使用压缩包的方式依赖,下载速度更快
CPMAddPackage(NAME jsoncppURL .9.)add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)
(6)find_package
# 拉取代码
git clone
cd jsoncpp
mkdir -p build/debug
cd build/debug
# 生成Makefile
cmake -DCMAKE_BUILD_TYPE=release -DBUILD_STATIC_LIBS=OFF -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -DCMAKE_INSTALL_INCLUDEDIR=include -G "Unix Makefiles" ../..
# 安装
make && make install
<
cmake_minimum_required(VERSION 3.17)
project(find_package_example)
find_package(jsoncpp REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} jsoncpp_lib)
(7)git submodule
ref:git submodules
参考:学C++从Cmake学起,PPT和源码地址
本文发布于:2024-02-04 13:19:26,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170708182955930.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |