Linux---gcc编译过程与Makefile语法规则

在这里插入图片描述


一、gcc编译过程

学习Makefile基本语法之前,我们有必要复习一下gcc的基本编译,常见的gcc选项如下:

选项功能
-v查看gcc编译器的版本,显示gcc执行时的详细过程
-o 指定输出文件名为file,这个名称不能跟源文件名同名
-E只预处理,不会编译、汇编、链接t
-S只编译,不会汇编、链接
-c编译和汇编,不会链接

一个c/c++文件要经过预处理、编译、汇编和链接才能变成可执行文件。

1)预处理
C/C++源文件中,以#开头的命令被称为预处理命令,如包含命令#include、宏定义命令#define、条件编译命令#if、#ifdef等。预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些东西输出到一个.i文件中等待进一步处理。

2)编译
编译就是把C/C++代码(比如上述的.i文件)翻译成汇编代码。

3)汇编
汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现为ELF目标文件(OBJ文件)。反汇编是指将机器代码转换为汇编代码,这在调试程序时常常用到。常使用的指令为objdump -d *.o(目标文件)对目标文件进行反汇编。

4)链接
链接就是将上步生成的OBJ文件和系统库的OBJ文件、库文件链接起来,最终生成了可以在特定平台运行的可执行文件。

整个过程为:hello.c(预处理)->hello.i(编译)->hello.s(汇编)->hello.o(链接)->hello,每个过程具体命令如下:

gcc -E -o hello.i hello.c
gcc -S -o hello.s hello.i
gcc -c -o hello.o hello.s
gcc -o hello hello.o

//上述总体可简化为
gcc -c -o hello.o hello.c
gcc -o hello hello.o

另外需要补充2点,也是比较常用的就是静态库动态库的生成。
静态库一般使用cr命令生成,常用的参数如下:

选项功能
c生成新的库文件
r替换现有的文件或插入新的文件
t显示归档中的目录表

举个例子,我们需要将a.c b.c c.c 这3个源文件编译成一个api.a的静态库:

ar -cr -o api.a a.c b.c c.c

依旧使用上面的例子进行动态库的生成:

gcc -shared -o api.so -fPIC a.c b.c c.c

其中api.so就是我们的动态库,动态库一般是.so文件,静态库后缀是.a文件,另外-fPIC就是告诉编译器去创建一个与地址无关的程序,这样才能够让动态库可以被多个应用程序共享。


二、Makefile的前世今生

  • 先解答一下Makefile是用来干什么的:Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。

  • 如何使用Makefile:一般有Makefile文件后我们直接使用make命令就可以完成编译,这里需要知道在 Linux(unix )环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个make命令就可以完成编译、链接。


三、Makfile初级语法

基本语法规则

目标(target):依赖(prerequisites)
[Tab]命令(command)

1)target:需要生成的目标文件
2)prerequisites:生成该target所依赖的一些文件
3)command:生成该目标需要执行的命令

举个例子:

main:main.c
	gcc main.c -o main

注意:Makefile文件里的命令必须要使用Tab,不能使用空格

目标文件生成规则
1)检查规则中的依赖文件是否存在。
2)若依赖文件不存在,则寻找是否有规则用来生成该依赖文件。

目标文件更新规则
1)检查目标的所有依赖,任何一个依赖有更新时,就要重新生成目标。
2)目标文件比依赖文件更新时间晚,则需要更新。

举个例子:目标文件output依赖main.o add.o sub.o,而这3个obj文件又依赖各自的.c文件,则对于这个依赖关系我们可以使用Makefile文件表述为:

output: main.o add.o sub.o
        gcc -o output main.o add.o sub.o
main.o: main.c
        gcc -c main.c
add.o: add.c
        gcc -c add.c
sub.o: sub.c
        gcc -c sub.c

clean:
        rm *.o output

通配符:上面的示例中依赖文件只有3个,而且生成依赖文件的源C文件也只有3个,当工程比较复杂的时候.c .o文件将会变得非常多,这时需要使用通配符,来解决这些问题。

%.o:表示所用的.o文件
%.c:表示所有的.c文件
$@:表示目标
$<:表示第1个依赖文件
$^:表示所有依赖文件

有了通配符,上面的示例可以简化为:

output: main.o add.o sub.o
        gcc -o output $^
%.o: %.c
        gcc -c -o  $@ $<

clean:
        rm *.o output

四、Makefile其他常见语法

1)简单变量

A := xxx   # A的值即刻确定,在定义时即确定

2)延时变量

B = xxx    # B的值使用到时才确定 

想使用变量的时候使用“$”来引用,如果不想看到命令时可以在命令的前面加上"@"符号,常用的变量的定义如下:

:=   # 即时变量
=    # 延时变量
?=   # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
+=   # 附加, 它是即时变量还是延时变量取决于前面的定义
?=: #如果这个变量在前面已经被定义了,这句话就不会起效果

3)系统自带变量
系统自定义了一些变量,通常都是大写,比如CC,PWD,CLFAG等等,有些有默认值,有些没有,如下:
1)CPPFLAGS:预处理器需要的选项,如:-l
2)CFLAGS:编译的时候使用的参数,-Wall -g -c
3)LDFLAGS:链接库使用的选项,-L -l

4)Wildcard函数

$(wildcard pattern)   # pattern定义了文件名的格式, wildcard取出其中存在的文件

例如目录下面有三个文件:a.c b.c c.c,则如下写法file的值为a.c b.c c.c:

files = $(wildcard *.c)

5)patsubst函数

$(patsubst pattern,replacement,$(var))

patsubst函数是从var变量里面取出每一个值,如果这个符合pattern格式,把它替换成replacement格式。

例如如下写法,dep_files 的值为a.d b.d c.d d.d e.d abc

files2  = a.c b.c c.c d.c e.c abc

dep_files = $(patsubst %.c,%.d,$(files2))

all:
        @echo dep_files = $(dep_files)

Makefile模板

###指定编译器,当前你也可以不指定使用系统默认的
CC = $(ARM_GCC)gcc

###添加调试信息,生成告警信息
CFLAGS = -g -Wall

###指定链接库
LD_LIBRARY = -lxxx

###添加文件搜索目录,一般.h文件对应的目录
INCLUDE = -I./aaa -I./bbb -I./ccc -I./ddd

###添加源码文件
SOURCES = $(wildcard ./aaa/*.c ./bbb/*.c ./ccc/*.c ./ddd/*.c ./*.c)

###指定目标文件
TARGET= output

OBJECTS = $(patsubst %.c,%.o,$(SOURCES))

$(TARGET) : $(OBJECTS)
	$(CC) $(CFLAGS) $^ -o $@
	rm -rf ./src/*.o
$(OBJECTS) : %.o : %.c
	$(CC) -c $(CFLAGS) $< -o $@

.PHONY: clean

clean:
	rm -rf ./src/*.o $(TARGET)
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值