C++ 编译相关

概念

  • GNU 是一个自由操作系统项目,其中包括了与操作系统相关的工具和应用程序,包括 GCC。

  • Linux 是一个类 Unix 的操作系统内核,与 GNU 工具一起用于创建完整的 Linux 操作系统。

  • GCC 是 GNU 组织开发的一套编译器集合(如:C、C++、Objective-C、Java 等),其中包括 g++(C++ 编译器)。

一、构建过程工具介绍

各类工具之间的关系图

image

1、项目构建生成工具

  首先cmake是项目构建生成工具, cmake 的代码可以与平台系统和编译器无关。类似 cmake 的工具还有autotools 、qmake、GN,其中 qmake 已基本由 cmake 替代。也就是说 cmake 是用来根据 CMakeLists.txt 文件内容自动生成不同种类项目构建系统所需要使用的 构建过程描述文件 如 make (Makefile)、Ninja(build.ninja)、msvc (.vcxproj) 等 ,这些配置文件通常是 Makefile 或 IDE 需要的项目文件。

2.项目构建工具

Makefile 可以理解为是 make 工具使用的构建过程描述文件,make 读取 Makefile 中的配置信息来实现编译、链接和部署。

类似 make 作用的工具还有 Ninja 、nmake 、devenv(vs),mingw32-make.exe 是 windows 版本的 make 工具

3.项目编译链接工具

  1. 预处理器 (Preprocessor):
    • 工具:cpp(C/C++ 预处理器)
    • 步骤:展开宏、处理条件编译指令 (#ifdef, #ifndef, #include 等)。
  2. 编译器前端 (Compiler Frontend):
    • 工具:clang, g++(GCC 的 C++ 编译器)、MSVC(Microsoft Visual C++ 编译器)等。
    • 步骤:将源代码翻译成中间表示(例如 LLVM IR)。
  3. 编译器后端 (Compiler Backend):
    • 工具:llc(LLVM 编译器)、as(汇编器)。
    • 步骤:将中间表示编译成汇编代码。
  4. 汇编器 (Assembler):
    • 工具:as(GNU 汇编器)、masm(Microsoft 汇编器)等。
    • 步骤:将汇编代码翻译成二进制目标文件。
  5. 链接器 (Linker):
    • 工具:ld(GNU 链接器)、link(Microsoft 链接器)等。
    • 步骤:将多个目标文件和库文件链接成可执行文件或共享库。
  6. 库工具 (Library Tool):
    • 工具:ar(GNU 归档工具)、lib(Microsoft 静态库工具)等。
    • 步骤:创建和管理静态库文件(.a, .lib 等)。
  7. 动态链接器 (Dynamic Linker):
    • 工具:系统特定,如 Linux 上的 ld.so
    • 步骤:在运行时加载共享库,并解析符号引用。
  8. 其他工具:
    • objdump(目标文件分析工具):用于查看目标文件的内容和信息。
    • nm(符号表工具):用于查看目标文件或可执行文件中的符号表信息。
    • strip(符号剥离工具):用于删除目标文件或可执行文件中的符号信息,减小文件大小。

二、工具链构成

  • 编译器(Compiler)
  • 汇编器(Assembler)
  • 链接器(Linker)
  • 标准库(Standard Library)
  • 头文件和库文件(Header Files and Library Files)
  • 构建工具(Build Tools)
  • 调试器(Debugger)
  • 性能分析工具(Performance Profiling Tools)
  • 交叉编译工具链(Cross-Compilation Toolchain)

三、为什么不直接使用项目编译链接工具

  为什么要有上面说的三类工具,首先说下“项目编译链接工具” 只是使用这些工具其实就能够编译出所有的目标,但由于命令过于复杂,编译的流程不好控制。比如编译一个执行程序 g++ a.cpp -o a -I../include -L../../lib -lpthread这种方式在涉及到多个多项目、多库、多参数和各种依赖关系是维护难度很大。还有涉及到换编译器时,比如从 g++ 换成 clang 或者 cl.exe,也需要改动很多内容。

那如果换成 make:

INCLUDE=-I../include
LIBS=-lpthread
a:a.o
${CC} -o $@ $< ${INCLUDE} ${LIBS}

依赖关系和所有目标都可以很清晰的管理。

四、为什么不直接使用make或者Ninja

  其实很多公司和项目就是直接使用 make ,但 make 代码规则严格,语法过于复杂,在做跨平台和跨编译器时的管理更加复杂,移植到不同环境的成本过大。这是就要引入 cmake 来生成 make 或者 NInja 使用的文件。Android 的 NDK 开发和鸿蒙 native SDK 都使用了 cmake 生成 Ninja 项目文件。

  cmake 本身配置就与环境和编译器完全无关了,可以由生成时指定

  cmake 直接编译一个程序的配置也就一个函数add_executable的调用

cmake_minimum_required(VERSION 3.20)
project(项目名称)
add_executable(项目名称  a.cpp)

五、make、Ninja 和 Visual Studio 区别

  makeNinjaVisual Studio 是不同的构建工具,它们用于管理和构建项目的方式有所不同。以下是它们之间的主要区别:

  1. make
    • make 是最常见的构建工具之一,广泛用于 Unix 和类 Unix 系统。
    • make 使用 Makefile 文件来定义项目的构建规则和依赖关系。
    • Makefile 使用 Make 编程语言,可以在其中编写自定义构建规则。
    • make 的优点是广泛支持,几乎在所有 POSIX 兼容的系统上都可用。
  2. Ninja
    • Ninja 是一种轻量级的构建系统,旨在提供快速和高效的构建。
    • Ninja 的配置文件通常比 Makefile 更简洁和易于理解,这使得构建速度更快。
    • Ninja 不支持像 Makefile 那样复杂的条件构建规则,但对于许多项目来说,速度和简单性是更重要的。
  3. Visual Studio
    • Visual Studio 是 Microsoft 开发的集成开发环境 (IDE),内置了强大的构建和调试工具。
    • 在 Windows 平台上,Visual Studio 提供了完整的开发环境,包括代码编辑、调试、版本控制和构建。
    • 使用 Visual Studio 构建 C/C++ 项目通常需要创建项目文件(如 .vcproj.sln 文件),并在 IDE 中配置构建选项。

选择使用哪个工具取决于项目的需求和开发环境:

  • 如果你在 Unix 或类 Unix 系统上工作,make 是一个常见的选择,尤其对于开源项目。
  • 如果你追求构建速度和简洁性,可以考虑使用 Ninja
  • 如果你在 Windows 上工作,Visual Studio 提供了一体化的解决方案,包括 IDE 和构建工具。

对于跨平台项目,可以使用 CMake 来生成适用于不同构建工具的配置文件,这样可以保持项目的可移植性。

六、cmake

1、前言

  CMake是一个跨平台的项目构建生成工具。它能生成各种项目构建工具所需要的 构建过程描述文件(例如 Makefile,build.ninja,.vcxproj 等文件),能测试编译器所支持的C++特性,类似UNIX下的automake。Cmake 并不直接建构出最终的软件,而是产生标准的建构档,然后再依一般的建构方式使用。 —— 百度百科

2、安装步骤

1、官网下载cmake压缩包例如

2、解压压缩包到指定目录(路径不要有中文)

3、将bin目录添加至用户环境变量

4、验证安装成功,打开cmd,cmake --version

3、cmake指定编译器

注意:在 windows 下一般使用 cmake-gui.exe 工具指定编译器会更加方便

在 cmd 中输入cmake -G可以看到当前版本支持的各种编译器

cmake指定主要的编译器示例

# -S 指定源码文件路径,-B指定编译输出文件夹名称,-G 指定编译工具

# 1、指定使用 `MinGW Makefiles` 编译器
cmake -S . -B build -G "MinGW Makefiles"


# 2、Visual Studio 编译器支持(生成项目文件和解决方案)
cmake -S . -B build -G "Visual Studio 17 2022"
cmake -S . -B build -G "Visual Studio 16 2019"
cmake -S . -B build -G "Visual Studio 15 2017"
cmake -S . -B build -G "Visual Studio 10 2010"

# 3、windows下nmake支持(vs控制台编译)
cmake -S . -B build -G "NMake Makefiles"

# 4、Ninja (安卓和鸿蒙方案)
cmake -S . -B build -G "Ninja"

七、CMakeLists文件介绍

编辑该文件时一定要注意,这个文件的指令是自上到下执行的,指令顺序不同会导致各种各样的问题出现

cmake官网命令大全:https://cmake.org/cmake/help/latest/

CMakeLists.txt语法规则小结:https://blog.csdn.net/weixin_46135347/article/details/122172470

命令介绍

cmake_minimum_required			# 用于指定能够正确执行CMakeLists.txt文件中指令的cmake最小版本
project							# 指定项目名称,会引入一些变量
set(CMAKE_CXX_STANDARD 14)		# 设置C++语法标准
set(CMAKE_VERBOSE_MAKEFILE ON)	# 开启生成Makefile时的详细日志输出
add_subdirectory				# cmake子目录继续编译,这个目录中必须含有CMakeLists.txt文件
aux_source_directory			# 搜索指定目录中的cpp文件,将搜索到的所有文件相对路径存储在变量中
file							# 将匹配指定正则的所有文件信息保存到一个变量中
include_directories				# 添加头文件搜索目录
link_directories				# 添加库文件搜索目录
find_library
find_package					# 设置依赖库
link_libraries					# 用在add_executable之前,主要用来链接静态库
add_compile_options				# 添加一个g++编译选项
add_definitions					# 添加一个g++编译宏定义选项
add_dependencies				# 用于描述两个target之间的依赖关系
add_library						# 添加一个生成库文件的 target
add_executable					# 添加一个生成可执行文件的 target
target_link_libraries			# 用在add_executable之后,主要用来链接导入库或者动态库
set								# 设置一个变量的值
list							# 对一个列表变量进行一些操作
message							# 生成构建文件时输出一些信息
install							# 复制当前工程中的一些文件到指定的目录下
include							# 执行某个文件中的cmake指令

CMakeLists.txt中的条件控制语句

# 逻辑判断和比较
if (expression)						# expression 不为空(0,N,NO,OFF,FALSE,NOTFOUND)时为真
if (not exp)						# 与上面相反
if (var1 AND var2)
if (var1 OR var2)
if (COMMAND cmd)					# 如果 cmd 确实是命令并可调用时为真
if (EXISTS dir) if (EXISTS file)	# 如果目录或文件存在为真
# 当 file1 比 file2 新,或 file1/file2 中有一个不存在时为真,文件名需使用全路径
if (file1 IS_NEWER_THAN file2)
if (IS_DIRECTORY dir)				# 当 dir 是目录时为真
if (DEFINED var)					# 如果变量被定义为真
# 给定的变量或者字符串能够匹配正则表达式 regex 时为真,此处 var 可以用 var 名,也可以用 ${var}
if (var MATCHES regex)				
if (string MATCHES regex)

# 数字比较 #
if (variable LESS number)			# LESS 小于
if (string LESS number)
if (variable GREATER number)		# GREATER 大于
if (string GREATER number)
if (variable EQUAL number)			# EQUAL 等于
if (string EQUAL number)

# 字母表顺序比较 #
if (variable STRLESS string)
if (string STRLESS string)
if (variable STRGREATER string)
if (string STRGREATER string)
if (variable STREQUAL string)
if (string STREQUAL string)
  • 示例
if(MSVC)
    set(LINK_LIBS common)
else()
    set(boost_thread boost_log.a boost_system.a)
endif()
target_link_libraries(demo ${LINK_LIBS})
# 或者
if(UNIX)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -g")
else()
    add_definitions(-D_SCL_SECURE_NO_WARNINGS
    D_CRT_SECURE_NO_WARNINGS
    -D_WIN32_WINNT=0x601
    -D_WINSOCK_DEPRECATED_NO_WARNINGS)
endif()
 
if(${CMAKE_BUILD_TYPE} MATCHES "debug")
    ...
else()
    ...
endif()

CMakeLists.txt中的循环控制语句

# 方式一
while(condition)
    # TODO
endwhile()

# 方式二
# start 表示起始数,stop 表示终止数,step 表示步长
foreach(loop_var RANGE start stop [step])
    # TODO
endforeach(loop_var)

# 示例 #
foreach(i RANGE 1 9 2)
    message(${i})	# 依次输出:13579
endforeach(i)

常见变量

# 预定义变量 #
PROJECT_SOURCE_DIR					# 工程的根目录
PROJECT_BINARY_DIR					# 运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build
PROJECT_NAME						# 返回通过 project 命令定义的项目名称
CMAKE_CURRENT_SOURCE_DIR			# 当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR			# target 编译目录
CMAKE_CURRENT_LIST_DIR				# CMakeLists.txt 的完整路径
CMAKE_CURRENT_LIST_LINE				# 当前所在的行
EXECUTABLE_OUTPUT_PATH				# 重新指定目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH					# 重新定义目标链接库文件的存放位置
CMAKE_MODULE_PATH					# 定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块

# 系统环境变量 #
$ENV{Path}							# 获取系统环境变量Path的值
set(ENV{Name} value) 				# 临时设置一个环境变量,注意这里没有“$”符号

# 系统信息 #
CMAKE_MAJOR_VERSION					# cmake 主版本号,比如 3.4.1 中的 3
CMAKE_MINOR_VERSION					# cmake 次版本号,比如 3.4.1 中的 4
CMAKE_PATCH_VERSION					# cmake 补丁等级,比如 3.4.1 中的 1
CMAKE_SYSTEM						# 系统名称,比如 Linux-2.6.22
CMAKE_SYSTEM_NAME					# 不包含版本的系统名,比如 Linux
CMAKE_SYSTEM_VERSION				# 系统版本,比如 2.6.22
CMAKE_SYSTEM_PROCESSOR				# 处理器名称,比如 i686
UNIX								# 在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin
WIN32								# 在所有的 win32 平台下该值为 TRUE,包括 cygwin

# 主要开关选项 #
CMAKE_C_FLAGS						# 设置 C 编译选项,也可以通过指令 add_definitions() 添加
CMAKE_CXX_FLAGS						# 设置 C++ 编译选项,也可以通过指令 add_definitions() 添加
BUILD_SHARED_LIBS					# 这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库
列如:add_definitions(-DENABLE_DEBUG -DABC)					# 参数之间用空格分隔

八、Makefile入门

一般该文件是用 cmake 工具生成的,但有时需要了解其中符号有何种用途,所以在这里留个传送门,将来可能要查阅

Makefile文件中的命令有一定规范,一旦该文件编写好以后在 Linux 命令行中执行一条 make 命令即可自动编译整个工程。不同厂家的make可能会稍有不同,并且语法上也有区别,不过基本思想都差不多,主要还是落在目标依赖上,最广泛使用的是 GNUmake

Makefile入门(超详细一文读懂)

九、Windows环境 C++ 编译工具

什么是 MinGW-w64

  MinGW 的全称是:Minimalist GNU on Windows 。它实际上是将经典的开源 C语言 编译器 GCC 移植到了 Windows 平台下,并且包含了 Win32API ,因此可以将源代码编译为可在 Windows 中运行的可执行程序。而且还可以使用一些 Windows 不具备的,Linux平台下的开发工具。一句话来概括:MinGW 就是 GCC 的 Windows 版本 。
  以上是 MinGW 的介绍,MinGW-w64 与 MinGW 的区别在于 MinGW 只能编译生成32位可执行程序,而 MinGW-w64 则可以编译生成 64位 或 32位 可执行程序。
  正因为如此,MinGW 现已被 MinGW-w64 所取代,且 MinGW 也早已停止了更新,内置的 GCC 停滞在了 4.8.1 版本,而 MinGW-w64 内置的 GCC 则更新到了 6.2.0 版本。

为什么使用 MinGW-w64

  • MinGW-w64 是开源免费的软件
  • MinGW-w64 由一个活跃的开源社区在持续维护,因此不会过时
  • MinGW-w64 支持最新的 C语言 标准
  • MinGW-w64 使用 Windows 的C语言运行库,因此编译出的程序不需要第三方 DLL ,可以直接在 Windows 下运行。那些著名的开源 IDE 实际只是将 MinGW-w64 封装了起来,使它拥有友好的图形化界面,简化了操作,但内部核心仍然是 MinGW-w64
  • MinGW-w64 是稳定可靠的、持续更新的 C/C++ 编译器,使用它可以免去很多麻烦,不用担心跟不上时代,也不用担心编译器本身有bug,可以放心的去编写程序

安装MinGW-W64

下载:https://sourceforge.net/projects/mingw-w64/files/

版本截图:

image

image

十、交叉编译参考

msys2安装及简单使用

MSYS2详解

https://blog.csdn.net/u010251191/article/details/79118468

为什么是msys2,而不是cygwin

https://segmentfault.com/a/1190000002789600
https://blog.51cto.com/u_15067227/4549473

参考文章

cmake安装参考:https://blog.csdn.net/m0_55048235/article/details/122277696

cmake与make的区别:https://blog.csdn.net/jiedichina/article/details/126676349

CMakeLists文件指令详解:https://blog.csdn.net/afei__/article/details/81201039

Makefile入门(超详细一文读懂):https://blog.csdn.net/ZBraveHeart/article/details/123187908

posted @ 2023-01-30 20:28  黄河大道东  阅读(499)  评论(0编辑  收藏  举报