gcc & make & cmake
编译
安装 gcc (多版本共存)
阿里云镜像:http://mirrors.aliyun.com/gnu/gcc/
参考博客: Linux下编译安装GCC 4.9.4 - Caosiyang's Blog
# 下载gcc源码解压后的目录假定为gcc-4.9.4
cd gcc-4.9.4
sh ./contrib/download_prerequisites
cd ..
mkdir build-gcc-4.9.4
cd build-gcc-4.9.4
# gcc-4.9.4将会安装至/usr/local/gcc-4.9.4/目录下
../gcc-4.9.4/configure --prefix=/usr/local/gcc-4.9.4/ --enable-checking=release --enable-languages=c,c++ --disable-multilib
make -j4
make install关于头文件与库的搜索路径
C_INCLUDE_PATH # C 头文件库搜索路径, 备注: 系统本身的不在这个变量里
CPLUS_INCLUDE_PATH # C++ 头文件库搜索路径, 备注: 系统本身的不在这个变量里
LD_LIBRARY_PATH # 动态链接库搜索路径, 备注: 系统本身的不在这个变量里
LIBRARY_PATH # # 静态链接库搜索路径, 备注: 系统本身的不在这个变量里为什么通常会需要配置 LD_LIBRARY_PATH 而不需要配置 C_INCLUDE_PATH 和 CPLUS_INCLUDE_PATH, 例如:
安装 CUDA
安装 openCV
安装 tensorRT
关于 ldconfig
ldconfig注:以下均为简单理解,并非准确理解
在 Linux 下,默认动态链接库的搜索路径保存在 /etc/ld.so.conf 中,默认动态链接库的访问使用缓存机制,存放在 /etc/ld.so.cache (二进制格式),如果在默认路径下添加了动态链接库,则需要使用 ldconfig 更新默认路径里的动态链接库至 /etc/ld.so.cache。具体来说,可能会遇到需要使用 ldconfig 命令的场景例如安装 mysql 时,默认会将 mysql 的库文件安装到 /usr/local/mysql/lib,这个目录一般是在默认的动态链接库搜索路径下,因此由于缓存机制的存在,如果在不执行 ldconfig 时,在需要使用 mysql 相关的动态链接库时,会报找不到库的错误。
ldconfig -v 用于查看已经缓存的动态链接库。
另外,ldconfig 是系统层面的一些机制,在用户层面,也可以配置 LD_LIBRARY_PATH 来添加库目录
示例(杂录)
例子1
目录结构及文件内容
编译方法
单条指令编译
-I选项用于增加include目录-o选项用于指定编译输出的文件位置
先编译链接库,再利用链接库编译应用
-L用于增加静态/动态链接库的搜索路径,-l用于指定静态/动态链接库的具体名称(注意不含lib前缀及文件扩展名)。stackoverflow问答的解释可以使用 gcc 的组件 nm 命令查看 .so 文件中的符号。stackoverflow
编译步骤、动态链接库与静态链接库
编译步骤拆解为:
编译预处理(pre-processing):将
#include处理好,宏展开等。使用-E指定,生成文件后缀名习惯用.i。编译(compiling):转换为汇编代码。使用
-S指定,生成文件后缀名习惯用.s。汇编(assembling):将汇编代码转换为目标文件。使用
-c指定,生成文件后缀名习惯用.o。链接(linking):将目标文件进行链接,最终生成可执行文件
可以把多个目标文件打包为一个作为函数库,这一过程可以借助
ar命令来完成。函数库分为动态链接库与静态链接库
参考资料:
不确定好坏的资料:https://tldp.org/HOWTO/Program-Library-HOWTO/index.html
Make
CMake
资源:
项目结构与 cmake 基本命令
CMake 用于生成 Makefile (如果使用 make 的话), 典型的编译步骤如下
一种典型的项目组织如下
备注: 有些项目源文件和头文件不会分离, 而是放在同一级目录下, 也是常见的
CMakeLists.txt 语法
重要语法索引 (cheetsheet)
初学先概览, 回头再复习. TODO: 此部分内容来自 GPT, 回头再做确认(似乎不太靠谱, 很多出现在 CMake 官方 tutorial 中的命令这里没有, 许多这里出现的, CMake 官方 tutorial 中没有).
项目信息
cmake_minimum_required(VERSION X.Y): 指定 CMake 的最低版本要求。project(<project_name>): 定义项目的名称,可以可选地指定语言(C、C++等)。
源文件与目标
add_executable(<target> <source1> <source2> ...): 创建一个可执行目标。add_library(<target> <source1> <source2> ...): 创建一个库目标(静态库或共享库)。aux_source_directory(<directory> <variable>): 查找指定目录中的所有源文件,并将它们的名称存入变量。
依赖关系
target_link_libraries(<target> <library1> <library2> ...): 指定目标所依赖的库。add_subdirectory(<directory>): 添加子目录,通常用于包含其他 CMakeLists.txt 文件。
变量和选项
set(<variable> <value>): 定义或设置变量。option(<option_name> <description> <default>): 定义一个布尔选项,用户可以选择该选项。if(<condition>) ... endif(): 条件语句,用于控制构建时的行为。foreach(<var> <items>) ... endforeach(): 迭代语句,用于遍历列表中的每一项。
包含和查找模块
find_package(<package_name> [REQUIRED]): 查找并加载指定的包。include_directories(<directory>): 添加包含目录。link_directories(<directory>): 添加链接目录。
生成规则和安装
install(TARGETS <target> DESTINATION <directory>): 指定安装目标及其安装位置。file(<command> <args>): 处理文件的多种操作,例如复制、删除、生成等。
配置和构建选项
set(CMAKE_BUILD_TYPE <type>): 设置构建类型,例如 Debug 或 Release。set(CMAKE_CXX_STANDARD <version>): 设置 C++ 标准。
脚本与宏
macro(<name> <args>) ... endmacro(): 定义一个宏,可以多次调用。function(<name> <args>) ... endfunction(): 定义一个函数,具有局部作用域。
自定义命令和目标
add_custom_command(...): 定义自定义构建命令。add_custom_target(...): 定义自定义构建目标。
其他
message(<message>): 打印消息到标准输出。include(<module>): 包含其他 CMake 文件。
add_library, add_executable 分别表示创建一个库/可执行文件以及其所需要的源文件, 它们之后可能会跟上 target_link_libraries, target_include_directories, target_compile_definitions, 分别表示为了生成这个库/可执行文件需要的其他库名,头文件目录,编译时宏定义.
这类 target_xxx 的命令还有:
target_compile_features: 用于指定 C++ 标准, 例如:target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)target_compile_options: 直接指定编译选项, 但需要手动解决跨平台问题. 例如:target_compile_options(tutorial_compiler_flags INTERFACE -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused). 所谓跨平台, 指的是例如指定 C++ 标准, 使用gcc编译器, 写法是gcc -std=c++11 ..., 而MSVC编译器的写法是cl /std:c++11. 因此一般来说, 使用target_compile_options时可能会见到下面的写法:
特殊变量: PROJECT_SOURCE_DIR, PROJECT_BINARY_DIR, CMAKE_CURRENT_SOURCE_DIR
官方 Tutorial 笔记
链接 (本文写作时的版本为cmake==3.31.0-rc2): https://cmake.org/cmake/help/latest/guide/tutorial
Step1: A Basic Starting Point
主要介绍了单个源文件和头文件的情形, 但内容其实比较多
(1) 首先介绍了 project, cmake_minimum_required, add_executable 命令, 以及使用 cmake 命令行工具进行配置和构建
(2) 然后使用 set 命令配置 cmake 内置的变量 CMAKE_CXX_STANDARD 和 CMAKE_CXX_STANDARD_REQUIRED, 从而实现对 C/C++标准 的管控
(3) 最后补充介绍了 project 中可以定义项目的版本号, 在这之后的 CMakeLists.txt 内容里就可以使用 <PROJECT-NAME>_VERSION_MAJOR 和 <PROJECT-NAME>_VERSION_MINOR 内置变量, 为了将这两个变量变得可以在源码中使用(源码中用宏来获取这两个变量), 可以采用如下方案: 配置 config.h.in, 在这个模板文件中使用 @<PROJECT-NAME>_VERSION_MAJOR@ 的语法用于宏值的定义, 并且在 CMakeLists.txt 中使用 configure_file 命令使得 cmake 在构建项目时生成真正的 config.h 文件, 最后使用 target_include_directories 让可执行文件的编译过程正确地 include 出于 PROJECT_BINARY_DIR 的 config.h 文件.
Step2: Adding a Library
主要介绍了多个源文件的情形, 且库代码位于单独的目录内, 且包含有自己的 CMakeLists.txt 文件.
(1) 内层 CMakeLists.txt 引入了 add_library 命令, 而外层的 CMakeLists.txt 为了使得最终的 main 程序能链接库, 需要使用 add_subdirectory 用于执行得到库, 使用 target_link_libraries 来让 main 链接到库, 使用 target_include_directories 使得 main 在编译时能识别到库的头文件
(2) 演示了如何利用 option 来为 cmake 在配置阶段增加 -D<OPTION_NAME>=<value> 来控制编译行为. 在官方的例子中, 场景是这样的, 我们的库是一个计算 sqrt 的函数, 而 main 程序希望实现如下功能: 如果希望使用我们自己实现的数学库, 那么就编译出该数学库, 并且 main 程序链接并使用其实现; 如果希望使用官方的数学库 cmath, 那么就不编译我们自己的数学库. 具体实现方式如下: TODO
Step3: Adding Usage Requirements for a Library
主要是优化 Step1 和 Step2 中的一些写法: 在 Step1 中, C/C++标准 的管控是使用 set 命令全局管控 CMAKE_CXX_STANDARD 和 CMAKE_CXX_STANDARD_REQUIRED 来实现的, 这样就不能实现不同模块采用不同的 C/C++标准; 在 Step2 中, 外层的 main 不仅需要链接库, 还需要通过 target_include_directories 添加库文件目录.
Step4: Adding Generator Expressions
本部分感觉不是核心用法, Generator Expressions 是 CMakeLists.txt 的脚本特性
Step5: Installing and Testing
引入 cmake 项目安装, 以及单元测试, TODO: 此处代码移除
Step6: Adding Support for a Testing Dashboard
本部分感觉不是核心用法, 单元测试可以更花哨, 可以有个仪表盘来展示
Step7: Adding System Introspection
本部分与 Step6 都使用了 include 语法, 来引入 /path/to/cmake/Modules 中的其他“脚本”文件
Step8: Adding a Custom Command and Generated File
本部分实现了一个特殊功能: 首先编写一段 C 代码, 其执行后会生成一个头文件, 整个项目的构建过程是先得到这个头文件 (通过自定义命令来实现: add_custom_command), 然后其他的库/可执行文件可能需要使用这个头文件
Step9: Packaging an Installer
本部分介绍了项目打包, 具体介绍了使用 cpack 的打包方式, 打包为 .tar.gz 文件(二进制安装), 操作比较简单, CMakeLists.txt 加几行配置即可. 其他的项目打包和发布方式待探索
Step10: Selecting Static or Shared Libraries
前序步骤所有的库都是使用的静态库(当然也可以在 add_library 是设定为动态库, 但前面没有明确提到), 本部分介绍怎么“优雅”地配置以生成动态链接库
Step11: Adding Export Configuration
TODO: 似乎是让本项目在其他项目中能使用, TODO
Step12: Packaging Debug and Release
TODO
单文件
引入: cmake_minimum_required, project, add_executable, aux_source_directory
最简单的情况:
一个源文件和头文件, 用于编译得到一个可执行文件(引入
cmake_minimum_required,project,add_executable)多个源文件和头文件在同一目录, 最终得到一个可执行文件(引入
aux_source_directory)
库与链接
引入: add_library, target_link_libraries, target_include_directories, add_subdirectory
注意: add_executable 用于指定可执行文件依赖的源文件, 而 target_link_libraries 用于指定可执行文件依赖的库文件. 并且 add_executable 必须在 target_link_libraries 之前. 虽然按照标准的 gcc 编译流程, 编译步骤确实是:
(1) 先编译并打包得到库文件 libMathFunctions.a (2) 使用源文件 main.c 编译得到 main.o (3) 使用 main.o 和 libMathFunctions.a 链接得到 Demo
但 CMakeLists.txt 的语法里不关心 main.o 这种中间产物, 它只关系为了生成 Demo, 它需要哪些源文件 (对应于 add_executable), 哪些库文件 (对应于 target_link_libraries)
add_subdirectory 命令表示“执行”子目录的 CMakeLists.txt: 父目录的 CMakeLists.txt 在 add_subdirectory 之前通过 set 定义的变量, 在子目录中的 CMakeLists.txt 是可见的, 并且在子目录 CMakeLists.txt 中定义的变量, 在父目录 add_subdirectory 之后, 对父目录的 CMakeLists.txt 也是可见的
configure_file
configure_file: 根据模板文件生成宏定义头文件, 模板文件名通常是 config.h.in, 而宏定义头文件名一般是 config.h, 在使用 cmake 生成 Makefile 时, 会自动得到这个 config.h (与编译选项无关). 在 C/C++ 源代码中可以去通过 #include "config.h" 从而确定 config.h 中的宏定义
CMakeLists.txt:
demo.cxx
config.h.in 的写法如下:
自定义编译选项
TODO: 搞完整
option+add_definitionsoption+config.h.in+include "config.h"
引入: option, add_definition, if
前面已经看到过 cmake 像这样 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 的选项, 事实上, cmake 可以自定义编译选项. 涉及到的相关 cmake 语法有
option: 增加一个 cmake 的编译选项, 也就是增加一个这种用法cmake -D<选项名>=<值>add_definitions: 将编译选项直接传递给编译器, 也就是利用 gcc 的-D<宏名>=<值>参数, 例如:gcc -DDEBUG main.c -o main
为此需要在 CMakeLists.txt 中包含如下内容
C 代码中可以这样使用
安装与测试
CMake 官方 Tutorial-5
TODO: 基本已确认
关于 cmake 命令与 CMakeLists.txt 内的相对路径问题: CMakeLists.txt 的写法只需关注它本身与源码的相对路径, 而不必关心 cmake 命令行的参数. 这一点和 python 有区别 (python 代码里的 import 需要与 python 作为启动脚本时的当前目录需要相配)
CMakeLists.txt 的文件内容如下:
使用 cmake . 和 mkdir build && cd build && cmake .. 或者 cmake -B build -S . 均可以成功, 后两者的 Makefile 在 build/Makefile
CMAKE_CURRENT_SOURCE_DIR 总是指向 CMakeLists.txt 所在目录, 而 CMAKE_CURRENT_BINARY_DIR 总是指向生产的文件目录
CMAKE_CURRENT_SOURCE_DIR 总是指向 CMakeLists.txt 所在目录, 而 CMAKE_CURRENT_BINARY_DIR 总是指向生产的文件目录Last updated
Was this helpful?