gcc/g++/make/cmake

GCC、G++

编译过程

c/c++编译会经历如下过程:

  • 预编译:将.c.h文件预编译为.i文件,删除注释,将头文件引用内容汇总到一个文件,宏展开也在这一阶段。
  • 编译:将临时文件.i文件编译为汇编代码.s文件,如果有语法错误会输出。
  • 汇编:将汇编代码.s文件翻译为机器指令.o文件。
  • 链接:将需要的库文件.lib和机器指令文件.o链接起来得到可执行文件.out.exe

GCC和G++的区别

GCC(GNU Compiler Collection,GNU编译器集合)是一个集成了预编译,编译,汇编,链接的编译工具,它支持c/c++/fortran/java等语言的编译。
G++(GUN C++ complier),与GCC类似,是一个c/c++的编译器,主要区别如下:

  • G++在编译c和c++时会自动连接STL标准库,GCC则需要手动加-lstdc++这个flag。
  • G++在编译.c和.cpp文件时,统一当做cpp文件处理,可以使用extern "C"来让G++按C的方式进行调用,GCC对于C和C++是分开处理的。
  • G++在编译c和cpp时,都会加上一些宏,而GCC只会在编译cpp时加。
#define__GXX_WEAK__ 1
#define __cplusplus 1
#define __DEPRECATED 1
#define __GNUG__ 4
#define __EXCEPTIONS 1

#define __private_extern__ extern

GCC/G++工具使用

GCC包括三个主要部分

  • gcc-core:即GCC编译器
  • Binutils:一些二进制工具,位于/usr/bin文件下,一般有gcc程序调用,也可以手动调用。
    • as汇编器:将汇编语言代码转为机器码.o文件。
    • ld链接器:将编译生成的多个目标文件组织成最终的可执行程序。
    • readelf:查看目标文件或可执行程序的文件信息。
    • nm:查看目标文件的符号。
    • size:查看目标文件代码段、数据段、静态区等的尺寸。
    • objcopy:目标文件格式转换,如bin转elf等。
    • objdump:查看目标文件信息,反汇编。
  • glibc:使GUN实现的c标准库,提供诸如read、write、open、printf等这些函数的实现。

gcc/g++命令行参数
gcc和g++参数都是一样的,可以通过gcc/g++ --help查看具体参数

参数说明实例
-save-temps保持各个阶段产生的中间文件gcc -save-temps -o hello hello.c
-o各个阶段输出文件gcc -o hello hello.c
-E仅预编译gcc -E hello.c -o hello.i
-S执行到编译阶段gcc -S -o hello.s hello.c
-c执行到汇编阶段gcc -c -o hello.o hello.c
-std=<standard>指定输入文件的standardgcc -o hello -std=c++11 hello.cpp
-Wp,<options>指定preprocesser阶段的选项
-Wa,<options>指定(汇编)assembler阶段的选项
-Wl,<options>指定链接阶段的选项
-shared创建共享库
–static使用静态链接(gcc默认是动态链接)gcc hello.c -o hello_static --static
-g生成带debug信息的可执行程序gcc -g hello.c -o hello_debug

交叉编译

# 安装arm gcc交叉编译工具
sudo apt-get install gcc-arm-linux-gnueabihf
# 查看版本
arm-linux-gnueabihf-gcc -v
# 编译过程和本地编译没有区别
arm-linux-gnueabihf-gcc hello.c -o hello_arm
# 使用readelf查看目标信息
readelf -a hello_arm

make和makefile

make是一个批量构建工具,类似于shell脚本但是比shell智能得多,通过makefile描述的编译构建规则来构建需要的目标,构建过程包括但不限于目标编译、打包等。

  • 它能根据makefile来自动建立依赖,并根据依赖决定构建顺序
  • 能够根据修改的文件来决定哪些组件需要重新构建,而不是重新构建所有组件。

make命令参数

Makefile20分钟入门 b站视频

命令参数长命令说明实例
-f filename–file --makefile指定makefile文件make -f makefile.txt
-C dir–directory=dir执行器先切到dir目录
-I dir–include-dir在dir中搜索被包含的makefile
-L–check-symlink-times如果是软连接,使用软连接和连接目标中修改时间较晚的一个 ??
-j n–jobs指定同时跑几个任务,一般一个job用一个核心跑
-i–ignore-errors忽略命令返回的错误信息
-k–keep-going当某个目标构建出错是继续构建后续目标
-b -m-忽略兼容性
-B–always-make无条件make所有目标
-d-debug=[FLAGS]打印大量调试信息
-e–environment-overrides覆盖makefile中的环境变量make -e USE_GLIB=true
- s–silentsilent模式,不会输出相应的命令行信息
- r–no-builtin-rules禁止使用build-in规则
-R–no-builtin-variables禁用内置变量设置
-n–dry-run – just-print --recon非执行模式,打印所有执行命令但不执行
- t–touchtouch目标而不是重新构建他们
-w–print-diredotry打印当前目录

makefile语法

版本一

# makefile中以#开头为注释

# 指定构建目标和规则
# [目标]: 依赖1 依赖2 ...
# 目标下面用tab键(注意不能是空格)表示构建规则开始,后面写构建规则

# 这种方法和手写g++没有什么区别,一旦一个文件改变,就重新执行g++重新构建所有文件
hello: main.cpp bar.cpp foo.cpp 
	g++ -o hello  bar.cpp foo.cpp main.cpp
  • make是基于时间来检测是否需要重新构建目标的,当目标的修改时间比后面依赖的修改时间都晚时,就不需要重新构建,一旦有一个文件修改时间晚于目标(表示在目标构建之后修改过),就会重新构建。
  • make命令后面如果不指定target,则会查找makefile中的第一个目标,依次往后生成。
  • 例如下面的main.cpp修改时间比目标hello晚,此时如果执行make就会重新构建hello
### root@20010e383377:/tmp/workspace/makefile# ls -l
### -rw-r--r-- 1 root root   658 Apr 26 08:03 Makefile
### -rw-r--r-- 1 root root   131 Apr 26 07:51 bar.cpp
### -rw-r--r-- 1 root root    80 Apr 26 07:51 foo.cpp
### -rw-r--r-- 1 root root    74 Apr 26 07:54 header.h
### -rwxr-xr-x 1 root root 17536 Apr 26 08:01 hello
### -rw-r--r-- 1 root root   185 Apr 26 08:03 main.cpp

版本二

这个版本定义了一些变量,并将中间文件的构建分开,这样在改变一个文件时,只需要编译依赖这个文件的中间文件即可,最好再把中间文件链接起来,大大节省了构建时间。

# 可以使用”=“来定义变量
CXX = g++  # 定义编译器变量
TARGET = hello # 定义target变量
OBJ = main.o foo.o bar.o # 定义中间文件变量

# 定义目标构建规则
# 使用”$“和()来引用变量
$(TARGET): $(OBJ)
	$(CXX) -o $(TARGET) $(OBJ)

# 定义中间文件构建规则
# gcc/g++ -c 表示只执行预编译、编译、汇编三步,不执行链接
main.o: main.cpp
	$(CXX) -c main.cpp
foo.o: foo.cpp
	$(CXX) -c foo.cpp
bar.o: bar.cpp
	$(CXX) -c bar.cpp

执行make之后

root@20010e383377:/tmp/workspace/makefile# make
g++ -c main.cpp
g++ -c foo.cpp
g++ -c bar.cpp
g++ -o hello main.o foo.o bar.o

这个版本的问题是,每个.cpp文件的编译规则还是需要手动写,有没有办法能自动完成呢?看下面的版本。

版本三

CXX = g++
TARGET = hello
OBJ = main.o foo.o bar.o 
CXXFLAGS = -c -Wall  # 定义CXXFLAGS变量,统一管理FLAG

# $@ 表示构建规则中的目标,即冒号前面的, $^ 表示依赖集合,即冒号后面的
# 在下面这个构建规则中,$@即$(TARGET) $^即$(OBJ)
$(TARGET): $(OBJ)
	$(CXX) -o $@ $^ 


# %.o :%.cpp 表示,对于每一个依赖的.o文件,查询同名的cpp文件并添加一条构建规则
# $< 表示依赖集合中的第一个,由于此时%.cpp只有一个文件,所以等价于$^
# $@表示目标,和上面是一样的
%.o: %.cpp
	$(CXX) $(CXXFLAGS) $< -o $@

# .PHONY也是一条构建规则,由于这个文件不存在,所以每次clean都会重新构建
# 这样做是为了防止make目录下有一个clean同名文件,阻止clean的运行
.PHONY: clean
clean:
	rm -f *.o $(TARGET)
  • .PHONY是一个声明,后面的成为伪目标(如上面的clean),在这个声明中的标签会按照makefile中的意图执行,没有声明的标签会寻找到makefile同级的文件。

这个版本已经比较自动化了,当有新的.cpp文件加入,只需要在OBJ变量中加入xxx.o即可,其他都不用改。有没有办法在加入新文件也不需要改makefile呢?有的,看下一个版本。

版本四

CXX = g++
TARGET = hello
# 变量定义中的通配符会失效,使用wildcard函数来使其生效
# 表示查找当前目录下的所有.cpp文件
SRC = $(wildcard *.cpp)
# patsubst函数有三个参数,对于第三个参数中的所有成员,将.cpp替换成.o
OBJ = $(patsubst %.cpp,%.o,$(SRC))

CXXFLAGS = -c -Wall

$(TARGET):$(OBJ)
	$(CXX) -o $@ $^

%.o:%.cpp
	$(CXX) $(CXXFLAGS) $^ -o $@

.PHONY: clean
clean:
	rm -f *.o $(TARGET)

这个版本使用通配符自动查找当前目录下的所有.cpp文件,并将.cpp文件名的.cpp替换成.o,自动加入到OBJ中,这样就不用每次手动加入了。

CMake

make可以很方便地构建程序,但是当跨平台或者环境(比如vs和qt creater),还是需要手动重新修改makefile,有没有办法能够用一套代码实现各个平台和环境的构建呢?有的,cmake是一个跨平台构建工具,它可以根据给定的CMakeFiles.txt文件来生成各个环境的构建文件,比如make的makefile,vs,xcode工程等,省去了手动编写构建规则的麻烦,(但CMakeLists的语法规则也挺复杂的)。同样的工具还有meson等。

最小构建文件

# 指定最小cmake版本
cmake_minimum_required(VERSION 3.15)
# 指定项目名称
project(hello)
# 添加可执行程序
add_executable(hello main.cpp foo.cpp bar.cpp)

使用set来创建变量

set(PROJECT_NAME hello)
set(SRC_FILE main.cpp foo.cpp bar.cpp)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值