首页 > 编程语言 >C++ 编译相关

C++ 编译相关

时间:2023-01-30 20:47:27浏览次数:60  
标签:SRC CMAKE demo LIST C++ 编译 相关 cmake

目录

一、cmake

前言

  CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。它能生成各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txtCmake 并不直接建构出最终的软件,而是产生标准的建构档,然后再依一般的建构方式使用。 —— 百度百科

安装步骤

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

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

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

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

二、cmake与make

两者区别

要说明区别先要区分下面三类工具:

  • 1.项目构建生成工具

首先cmake是项目构建生成工具,cmake的代码可以与平台系统和编译器无关。类似cmake的工具还有autotools、qmake、GN,其中qmake已基本由cmake替代。也就是说cmake是用来根据CMakeLists文件内容生成makefile和其他项目生成工具配置的。

  • 2.项目构建工具

makefile 可以理解为是make工具使用的代码,make读取makefile中的配置信息来实现编译、链接和部署。

类似make的工具有 Ninja 、nmake 、devenv(vs)。

  • 3.项目编译链接工具

比如有:gcc、 cl.exe 、link 、ar

各类工具之间的关系图

img

梳理一下 cmakemakegcc 以及的关系:

  • cmake :根据指定的 CMakeLists.txt 文件,跨平台来生成对应平台的 makefile 文件;

  • make :可以看作一个智能的批处理工具,本身没有编译和链接功能,而是通过调用 makefile 文件中用户指定的命令来进行编译和链接的;

  • gcc:即 GNU Compiler Collection(GNU 编译器套件),也可以理解为编译器,可用于编译很多种编程语言(如:C、C++、Objective-C、Java 等);

总结一下:gcc 是用于编译和链接和工具,编译少量文件可以直接使用 gcc 命令完成,但当源文件很多,用 gcc 命令去逐个编译则是很混乱且工作量巨大。因此,需要借助 make 来来管理整个编译过程,make 安装 makefile 中的命令进行编译和链接,而 makefile 命令中就包含了调用 gcc 去编译某个源文件的命令。而当工程规模非常大时,且需要跨平台时,手写 makefile 也开始变得麻烦,这是可以借助 cmake 来生成 makefile 。

为什么不直接使用项目编译链接工具(gcc/g++ ...)

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

那如果换成make:

INCLUDE=-I../include

LIBS=-lpthread

first_main:first_main.o

${CC} -o $@ $< ${INCLUDE} $

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

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

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

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

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

cmake_minimum_required(VERSION 3.20)

project(xlog)

add_executable(first_main first_main.cpp)

cmake指定编译器(cmake -G)

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

cmake指定主要的编译器示例

  • 1.指定是用 MinGW Makefiles 编译器

cmake -S . -B build -G "MinGW Makefiles"

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

  • 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文件介绍

命令介绍

1.指定cmake最小版本:cmake_minimum_required(VERSION 3.0)

这行命令是可选的,我们可以不写这句话,但在有些情况下,如果 CMakeLists.txt 文件中使用了一些高版本 cmake 特有的一些命令的时候,就需要加上这样一行,提醒用户升级到该版本之后再执行 cmake。

2.设置项目名称:project(demo)

这个命令不是强制性的但最好都加上。它会引入两个变量 demo_BINARY_DIRdemo_SOURCE_DIR,同时,cmake 自动定义了两个等价的变量 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR

3.设置编译类型

add_executable(demo demo.cpp) # 生成可执行文件
add_library(common STATIC util.cpp) # 生成静态库
add_library(common SHARED util.cpp) # 生成动态库或共享库

add_library 默认生成是静态库,通过以上命令生成文件名字,

  • 在 Linux 下以此是:
    demo
    libcommon.a
    libcommon.so
  • 在 Windows 下以此是:
    demo.exe
    common.lib
    common.dll

4.指定编译时包含的源文件

  • 明确指定包含哪些源文件

add_library(demo demo.cpp test.cpp util.cpp)

  • 搜索所有的cpp文件
aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件,并将搜索到的源文件相对路径列表存储在变量 SRC_LIST 中。
add_library(demo ${SRC_LIST})
  • 自定义搜索规则
file(GLOB SRC_LIST "*.cpp" "dir_name/*.cpp")      # 其中GLOB命令将所有正则匹配(可选,不写则匹配到空)的文件挑选出来,默认以字典顺序排序。
add_library(demo ${SRC_LIST})
# 或者
file(GLOB SRC_LIST "*.cpp")
file(GLOB SRC_LIST_2 "dir_name/*.cpp")
add_library(demo ${SRC_LIST} ${SRC_LIST_2})
# 或者
aux_source_directory(. SRC_LIST)
aux_source_directory(dir_name SRC_LIST_2)
add_library(demo ${SRC_LIST} ${SRC_LIST_2})

5.查找指定的库文件

find_library(VAR name path) 查找到指定的预编译库,并将它的路径存储在变量中。

默认的搜索路径为 cmake 包含的系统库,因此如果是 NDK 的公共库只需要指定库的 name 即可,如下。

find_library( log-lib log )
# Sets the name of the path variable.
# Specifies the name of the NDK library that you want CMake to locate.

类似的命令还有 find_file()find_path()find_program()find_package()

6.设置包含的目录

include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_BINARY_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/include
)


# Linux 下还可以通过如下方式设置包含的目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")

7.设置链接库搜索目录

link_directories(${CMAKE_CURRENT_SOURCE_DIR}/libs)

# Linux 下还可以通过如下方式设置包含的目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs")

8.设置需要链接的库

在cmake语法中,link_librariestarget_link_libraries是很重要的两个链接库的方式,虽然写法上很相似,但是功能上有很大区别:

link_libraries用在add_executable之前,target_link_libraries用在add_executable之后。
link_libraries用来链接静态库,target_link_libraries用来链接导入库,即按照header file + .lib + .dll方式隐式调用动态库的.lib库。

# log-lib 是上面 find_library 指定的变量名
# ${log-lib}无需含有文件后缀,下同
link_libraries(${log-lib})

# 目标库
# 目标库需要链接的库
target_link_libraries(demo ${log-lib} )

在 Windows 下,系统会根据链接库目录,搜索 xxx.lib 文件,Linux 下会搜索 xxx.so 或者 xxx.a 文件,如果都存在会优先链接动态库(so 后缀)。

  • 指定链接动态库或静态库
target_link_libraries(demo libface.a) # 链接libface.a
target_link_libraries(demo libface.so) # 链接libface.so
  • 指定全路径
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a)
target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.so)
  • 指定链接多个库
target_link_libraries(demo
    ${CMAKE_CURRENT_SOURCE_DIR}/libs/libface.a
    boost_system.a
    boost_thread
    pthread)

9.设置变量

# set 直接设置变量的值
set(SRC_LIST main.cpp test_fun.cpp)
add_executable(demo ${SRC_LIST})

# set 追加设置变量的值
set(SRC_LIST main.cpp)
set(SRC_LIST ${SRC_LIST} test.cpp)
add_executable(demo ${SRC_LIST})

# list 追加或者删除变量的值
set(SRC_LIST main.cpp)
list(APPEND SRC_LIST test_fun.cpp)			# SRC_LIST中追加 test_fun.cpp
list(REMOVE_ITEM SRC_LIST main.cpp)			# SRC_LIST中删除 main.cpp
add_executable(demo ${SRC_LIST})

10.条件控制if…elseif…else…endif

# 逻辑判断和比较 #
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)	# 如果目录或文件存在为真
if (file1 IS_NEWER_THAN file2)		# 当 file1 比 file2 新,或 file1/file2 中有一个不存在时为真,文件名需使用全路径
if (IS_DIRECTORY dir)				# 当 dir 是目录时为真
if (DEFINED var)					# 如果变量被定义为真
if (var MATCHES regex)				# 给定的变量或者字符串能够匹配正则表达式 regex 时为真,此处 var 可以用 var 名,也可以用 ${var}
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()

11.循环控制语句

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

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

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

12.打印信息

message(${PROJECT_SOURCE_DIR})
message("build with debug mode")
message(WARNING "this is warnning message")				# 输出报警信息
message(FATAL_ERROR "this build has many error")		# FATAL_ERROR 会导致编译失败

13.包含其它 cmake 文件

include(./common.cmake) # 指定包含文件的全路径
include(def) # 在搜索路径中搜索def.cmake文件
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # 设置include的搜索路径

常见变量

# 预定义变量 #
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 + CMake)

安装MinGW-W64

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

版本截图:

image.png

image.png

版本选择总结

系统类型 系统架构(Architecture) 线程模型(Threads) 异常类型(Exception)
x86(32位系统) i686 win32 dwarf
x64(64位系统) x86_64 posix seh

1、系统架构(Architecture)

说明: 是指电脑系统是32位还是64位,根据你的电脑系统做出对应选择。
操作: 我的电脑系统是 64位的,所以我选择了x86_64,如果你是32位系统,则选择i686即可。

2、线程模型(Threads)

说明: 这个世界上只有两种操作系统,符合posix协议的和windows。如果你想要开发windows程序,需要选择 win32 ,而开发 Linux、Unix、MacOS等其他操作系统下的程序,则需要选择posix。
操作: 我只开发在windows下运行的程序,所以选择了win32 。

3、异常类型(Exception)

(64位):
说明: 异常处理在开发中非常重要,你在开发的过程中,大部分的时间会耗在处理各种异常情况上。如果你之前选择了64位,则这里有两个异常处理模型供你选择,seh是新发明的,而sjlj则是古老的。seh性能比较好,但不支持32位。sjlj稳定性好,支持32位。
操作: 我这里选择了seh。

(32位):
说明: 选择了32位后,则可以用dwarf和sjlj两种异常处理模型。同样的,dwarf的性能要优于sjlj,但是它不支持64位。
操作: 建议选择dwarf。

MinGW-W64 配置

MinGW-w64 就是 Windows 平台下集成了 gcc 、g++ 、 make 等众多构建工具集合 ,为了更好地配合这两个工具来完成工程的编译,下载完成后需要再完成一点点额外的配置:

打开 MinGW-w64 的 bin 目录,拷贝一份 mingw32-make.exe 改名为make.exe

参考文章

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

MinGW-W64版本安装选择方法:https://segmentfault.com/a/1190000041394399

mingw不同版本全解析:https://www.codenong.com/cs105494164/

标签:SRC,CMAKE,demo,LIST,C++,编译,相关,cmake
From: https://www.cnblogs.com/hhddd-1024/p/17077184.html

相关文章

  • C++ STL stack
    #include<stack>头文件usingnamespacestd;作用这个很清楚了,FILO运用在:括号匹配、波兰式计算问题上(未完待续)创建一个参数,默认使用deque容器stack<typenameT,t......
  • C/C++学生成绩信息管理[2023-01-30]
    C/C++学生成绩信息管理[2023-01-30]2.学生成绩信息管理【问题描述】利用哈希表完成学生成绩信息的管理。每个学生记录包含学生学号(Number)、真实姓名(Name)、网名(Scre......
  • 离线环境解决maven编译外网下包问题
    引言近日一直忙着做持续集成,处于安全性考虑,需要在离线环境运行。项目依托Jenkins做Java/Python/Vue等工程的镜像构建,其中Java工程基本基于Maven,在外网条件下通过IDEA或者m......
  • linux基础:4、文件相关信息、链接信息、系统时间、机器克隆、定时任务、paramiko模块相
    目录一、文件相关信息二、文件索引信息三、链接信息四、系统时间五、机器克隆六、定时任务七、paramiko模块八、公钥私钥九、paramiko其他操作十、代码封装十一、面试题回......
  • C++ 模板之类模板
    使用类模板,可以事先不确定成员变量的类型,假如我们要写一个先进后出的栈,这个栈既可以放入int,也可以放入long,还可以放入string,那么就需要使用模板技术,否则,类的成员变量将难以......
  • C++ 运算符重载
    技术,是实现目标的手段,重载运算符,可以让程序看起来更优美,更优美的程序更具有可读性,如果不重载,使用函数一样可以达到目的,但那样子程序看起来很蹩脚,直接上代码    #inclu......
  • 使用GDB调试python调用的C++共享库
    目录1.首先编写python调用C++的demo2.gdb调试python程序3.全速运行程序4.查看堆栈信息当我们用Python调用C++的库,并且C++库中存在段错误崩溃时,首先想到的还是用gdb......
  • Python和C++联合调试
     python和c++分别在Linux和Windows下联合调试首先创建一个python测试项目和一个c++拓展项目一、在Windows下进行调试1.编译器安装2.C拓展模块安装3.调试......
  • python调用cpp 调试_python和C++联合调试
    python可以利用SO的方式去调用C++中的函数,但是需要一种调试方案来进行python和C++的联合调试,效果是直接在c++代码中打断点,然后python在进行c++so调用的时候,直接进入到断......
  • 一起调试 Python 和 C++
    大多数常规Python调试器支持仅调试Python代码。但是,实际上,Python结合C或C++一起使用时需要高性能或直接调用平台API的能力。(有关演练,请参阅创建适用于Pyth......