目录:
- 1.GCC是什么?
- 2.在容器(Linux系统)中安装GCC
- 3.GCC的编译过程
- 4.Makefile简易教程
1.GCC是什么?
说到GCC,就不得不提GNU计划。GNU全称GNU's Not UNIX,又被称为“革奴计划”,由理查德·斯托曼于1983年发起。GNU计划的最终目标是打造出一套完全自由(即自由使用、自由更改、自由发布)、开源的操作系统GNU。
GNU计划的实施可谓一波三折,最重要的一点是,虽然该计划为GNU操作系统量身定做了名为Hurd的系统内核,但由于其性能比不上同时期(1991年)诞生的Linux内核,最终GNU计划放弃Hurd而选用Linux作为GNU操作系统的内核。GNU计划最终实现了“打造一套自由、开源的操作系统”的初衷,但该操作系统并非完全产自GNU计划,因此其被称为GNU/Linux操作系统(人们更习惯称为Linux操作系统)。
早期GCC全称为GNU C Compiler,是GNU计划开发的众多部件之一,只用于编译C语言。随着不断迭代,GCC可以编译C、C++、Go、Java等多种语言,GCC的全称被重新定义为GNU Compiler Collection(GNU编译器套件)。
GCC编译器,可简单理解为“翻译器”,就是将日常编写的C语言代码、C++代码、Go代码等,翻译成计算器能够识别的二进制指令。几乎所有的Linux发行版都默认装有GCC编译器。
2.在容器(Linux系统)中安装GCC
进入容器:docker exec -it 79d441b7655a /bin/bash
(安装前,建议先使用gcc -v
命令查看是否已安装)
扫描软件源服务器,建立更新软件包列表:apt-get update
Linux上安装GCC,以下两种方式均可:
- 1)一个命令搞定:
apt-get install build-essential gdb
自动安装相关必要的软件包,包括:gcc(c编译器)、g++(c++编译器)、gdb、make等。 - 2)一个一个地安装相关软件包:
- 1.安装gcc和g++(安装g++后系统会连带安装gcc):
apt-get install g++
- 2.安装GDB(GNU symbolic debugger,项目调试器):
apt-get install gdb
- 3.安装make(一个解释makefile中指令的命令工具):
apt-get install make
- 1.安装gcc和g++(安装g++后系统会连带安装gcc):
安装成功,如下图所示:
GCC环境搭建成功后,将容器保存为镜像,随时备份当前环境。
退出容器:exit
从容器创建一个新的镜像:docker commit 79d441b7655a lyf/ubuntu_autostartssh_curl_gcc_make:18.04
保存镜像成功,如下图:
3.GCC的编译过程
(注意:有时也将预处理、编译、汇编三个过程统称为编译。)
GCC的编译过程,分为4小步:
- 1)预处理(Preprocess):又称预编译,编译器会执行“以#开头”的预处理指令,是做些代码文本替换工作。比如对于#include包含命令,预处理会将要包含的文件插入原文件中。
将结果输出为一个.i文件,gcc预处理命令:gcc -E hello.c -o hello.i
- 2)编译(Compile):对预处理后的文件进行一系列的词法分析、语法分析、语义分析及优化,并翻译成汇编代码文件。
将.i文件转换为.s文件,gcc编译命令:gcc -S hello.i -o hello.s
- 3)汇编(Assemble):将汇编代码翻译成机器能够识别的二进制指令,生成目标文件Object File(如Windows下的.obj文件,UNIX下的.o文件)。
将.s文件转换为.o文件,gcc汇编命令:gcc -c hello.s -o hello.o
- 4)链接(Link):将上步生成的目标文件与系统的目标文件、库文件链接起来,包括按序叠加、相似段合并、符号地址的确定、符号解析与重定位、指令修正、全局构造与解析等,生成一个可执行文件。
连接可分为动态连接和静态连接:- 动态链接:使用动态链接库进行链接,生成的程序体积小,但在执行的时候需要加载所需的动态库才能运行。
gcc默认是动态链接,命令:gcc hello.o -o hello
- 静态链接:使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过体积较大。
加上-static
参数采用静态连接,命令:gcc -static hello.o -o hello
- 动态链接:使用动态链接库进行链接,生成的程序体积小,但在执行的时候需要加载所需的动态库才能运行。
gcc/g++命令参数,如下图:
4.Makefile简易教程
一个大型的项目往往有成百上千个文件,使用命令行进行编译是十分费时和低效的。可以将编译命令写入Shell脚本,来实现自动编译的效果,但Shell脚本将重新编译所有源文件,包括那些不必要重新编译的源文件。所以,学会使用make工具来构建和管理自己的工程是十分重要的,make工具可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
make是一个命令工具,它解释执行Makefile文件中的命令,即需要一个Makefile文件,来告诉它具体的编译和链接规则。如此,当我们编写完Makefile文件后,只需一个make命令,即可实现自动化编译,极大提高软件开发的效率。显然,Makefile文件需要定义一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
4.1Makefile的规则
格式:
target ... : prerequisites ...
command
...
...
- target是生成项,可以是Object File、执行文件或标签(Label)。
- prerequisites是依赖项,要生成target所需要的文件或目标。
- command是make需要执行的命令(任意的Shell命令)。
注意:command一定要以Tab键开始,否则编译器无法识别。
规则:
- 1.生成一个target的时候,会优先生成prerequisites,target的生成规则定义在command中。
- 2.如果target的修改时间比所有prerequisites更新,不执行command。否则,只要prerequisites中有一个以上的文件比target文件更新,command命令就会被执行。
4.2编写一个简单的Makefile文件
目录结构如下图所示,在test目录下新建一个src目录(存放源文件)、一个objs目录(存放.o二进制文件)和一个Makefile文件。
a.cpp:
#include <iostream>
void echo() {
printf("echo from a.cpp.\n");
}
b.cpp:
#include <stdio.h>
void echo();
int main() {
echo();
printf("hello world from b.cpp.\n");
return 0;
}
Makefile:
# 1.变量定义区域
src_directory := src
obj_directory := objs
srcs := $(shell find $(src_directory) -name "*.cpp")
objs := $(patsubst %.cpp,%.o,$(srcs))
# 将.o文件保存到objs目录下,这里进行替换:src/a.o --> objs/a.o
objs := $(subst $(src_directory)/,$(obj_directory)/,$(objs))
# Makefile的target默认是文件。
# 用PHONY关键字指定的target表示是伪造的,不是一个文件。
# 下面例子中,不管debug文件存不存在,都认为永远不存在,这样debug中的指令永远会执行。
.PHONY : debug clean
# 2.依赖关系区域
debug :
# 3.指令区域,执行shell指令
@echo $(srcs)
@echo $(objs)
run : pro
@echo Compile Success
@./pro
pro : $(objs)
@echo link
g++ $^ -o $@
$(obj_directory)/%.o : $(src_directory)/%.cpp
g++ -c $< -o $@
clean :
@rm -f $(obj_directory)/*.o pro
patsubst和subst均可用于字符串替换,patsubst支持通配符。
make命令测试
make pro
:编译和链接操作,生成.o文件和pro可执行文件。
make run
:执行pro可执行文件。
make clean
:清除.o文件和pro可执行文件。