gcc与make

前备知识

  1. gcc是开源编译器,功能强大支持交叉编译
  2. make是项目管理工具,方便自定义源、依赖、与目标 类似于批处理。参考链接:http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E6%A6%82%E8%BF%B0
  3. .so与.a动态与静态链接库
  4. /xxx表示根目录下的xxx,xxx表示当前目录下的xxx,XXX/表示XXX目录下的什么,.表示当前目录

gcc的使用

gcc 文件.c -o 目标名
直接编译

分步有4步:
预处理:-E 生成.i .h都在这里处理
编译:-S 生成.s汇编代码
汇编:-c 生成.o目标文件,一个c文件对应生成一个o
链接: 可执行文件 这步导入库,实现所使用函数定义的查找

多个文件:gcc -o 目标 1.o 2.o 3.o 一个c对应一个o

静态库

编译:
为了在编译程序中正确找到库文件,静态库必须按照 lib[name].a 的规则命名,如下例中[name]=pr

先用gcc -c 编译出 .o文件
用ar进行打包:.a就是.o的打包

ar -rsv libpr.a pr1.o pr2.o
ar参数意义:
r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。
s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。
v:该选项用来显示执行操作选项的附加信息。
t:显示库的模块表清单。一般只显示模块名。
使用:
-l连接
gcc -o main main.c -L./ -lpr
-L 及 -l 参数放在后面。其中,-L 加载库文件路径,-l 指明库文件名字,-l会对名字加前后缀。默认为动态连接找libxx.so,无时静态用.a
直接连接
gcc main.c libpr.a 将俩文件链接

动态库

编译:
gcc -fPIC -shared -o xxx.so pr1.c
-shared 表示生成动态链接库 不加默认生成可执行文件
源需要.c
-fPIC表示位置独立的代码,实现完全动态
隐式使用:
直接连接
gcc -o main main.c xxx.so
-l连接,同样省略了前后缀,.a与.so同时存在的话默认.so
gcc main.c -L. -lmylib

如果目录下同时有static与shared library的话,会以shared为主。 使用-static参数可以使用静态连结。注意-Bstatic就是-static,用于直接全部静态连接。但-shared却是生成动态库。

默认是动态连接,使用-static是强制为静态连接。是连接方式
默认是生成可执行文件,-shared是生成动态连接文件(so)。是生成目的
所以这俩不是你字面理解的全针对连接,而是针对文件形态,不代替默认。

这种运行时去哪里寻找.so全看系统设置的查找路径,当然可自己针对linker连接时设置

make的使用

make通过当前目录下的makefile中指定的方式编译项目
makefile的名字可以自己设定make -f

使用时明确目标在每个规则中的概念,一开始只有一个目标,后来逐渐检查中产生多目标

make规则

基本示例

1
2
3
4
5
6
7
8
myprog : foo.o bar.o
gcc foo.o bar.o -o myprog
foo.o : foo.c foo.h bar.h
gcc -c foo.c -o foo.o
bar.o : bar.c bar.h
gcc -c bar.c -o bar.o

A:B C
(注意是tab) 指令
A是目标文件,文件中最上层的是最终目标,如果B,C中的任意一个比A新就执行下面的处理语句。在检查B、C时,会往下查找把B、C作为目标的规则。
也就是说B、C的检查是必须的,A是基准,检查不断向下,检查结果都是语句是否执行,检查结果再层层返回向上。

类似树状结构,规则是否被处理全看上层有没有依赖它
规则查找时是全文件查找,不是只向下。

其它用法:
all : exec1 exec2
下面不加生成all的语句,用于生成多个可执行文件。
不论all存不存在,all作为目标时每次都会检查1与2的最新,只不过不存在all时最终结果一定是执行语句,可没有语句所以在不在无所谓

规则如果不位于文件最开始,能不能被执行全看上层的规则是否依赖这个规则,但如果名确make XXXX 。就会把XXXX作为主目标而不是第一个,可用来提供些命令

特殊下规则被需要检查时
A:B C
当A不存在时,一定执行。
当A存在时,正常比较
A:
当A不存在时,一定执行
当A存在时,一定不执行

.PHONY : A用于指定A用于假想不用检查是否存在一定更新

make命令

每条规则中的命令和操作系统Shell的命令行是一致的。make会按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。
当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来
当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔

make变量

基本可以理解为存了一串字符
环境变量在 make 过程中被解释成 make 的变量。这些变量是大小写敏感的
声明:A=XXXXXX
使用:$(A)

make与c之间define变量互通的
定义好的内部变量:
$@:当前规则的目的文件名
$<:依靠列表中的第一个依靠文件
$^:整个依靠的列表
在命令中可替代,比如上面的:

1
2
3
4
5
6
7
8
9
10
11
12
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g
myprog : $(OBJS)
$(CC) $^ -o $@
foo.o : foo.c foo.h bar.h
$(CC) $(CFLAGS) -c $< -o $@
bar.o : bar.c bar.h
$(CC) $(CFLAGS) -c $< -o $@

同时,可以在使用make命令时指定参数的值:

1
2
3
AGE = 12
all:
@echo "age=$(AGE)"

make的时候,打印信息是age=12
当你使用make AGE=20时,打印的信息就是:age=20

make函数

使用函数:
$(函数名 参1,参2,….)
常用的:
$(wildcard *.c)
返回所有.c结尾的文件

$(patsubst %.c,%.o,变量X)
第一个是一个需要匹配的 式样,第二个表示用什么来替换它,第三个是一个需要被处理的由空格分隔的字列。在这里.o替换.c。注意这里的 % 符号将匹 配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。在第二个参数里, % 被解读成用第一参数所匹配的那个柄。

make隐含规则

“隐含规则”会使用一些我们系统变量,我们可以改变这些系统变量的值来定制隐含规则的运行时的参数。
当目标规则不存在时,make会在自己的“隐含规则”库中寻找可以用的规则,如果找到,那么就会使用。如果找不到规则且文件存在,就会判断,否则报错。

1
2
3
4
foo.o : foo.c
gcc –c foo.c $(CFLAGS)
bar.o : bar.c
gcc –c bar.c $(CFLAGS)

这俩句不必要写,自动使用

其它

include
include 文件名
用于包含一个文件,同时检查文件(纳入目标)。

gcc -M
gcc -M main.c
输出一个用于make的规则,该规则描述了这个源文件的依赖关系。预编译器输出的这个make规则包含名字与原文件相同的目标文件,冒号和自己与所有include文件的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gcc -M main.c
main.o: main.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/i386-linux-gnu/sys/cdefs.h \
/usr/include/i386-linux-gnu/bits/wordsize.h \
/usr/include/i386-linux-gnu/gnu/stubs.h \
/usr/include/i386-linux-gnu/gnu/stubs-32.h \
/usr/lib/gcc/i686-linux-gnu/4.8/include/stddef.h \
/usr/include/i386-linux-gnu/bits/types.h \
/usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/i686-linux-gnu/4.8/include/stdarg.h \
/usr/include/i386-linux-gnu/bits/stdio_lim.h \
/usr/include/i386-linux-gnu/bits/sys_errlist.h main.h
gcc -MM main.c
main.o: main.c main.h

\用于换行
-MM
与-M相似,只是不包含系统头文件

:=
A = foo
B = $(A)
现在 B 是 $(A) ,而 $(A) 是 ‘foo’ 。
A = bar
现在 B 仍然是 $(A) ,但它的值已随着变成 ‘bar’ 了。
B := $(A)
现在 B 的值是 ‘bar’ 。
A = foo
B 的值仍然是 ‘bar’ 。

#
make 会忽略在 # 符号后面直到那一行结束的所有文字。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
### Customising
# 用户设定
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
# 如果需要,调整下面的东西。 EXECUTABLE 是目标的可执行文件名, LIBS
# 是一个需要连接的程序包列表(例如 alleg, stdcx, iostr 等等)。当然你
# 可以在 make 的命令行覆盖它们,你愿意就没问题。
#
EXECUTABLE := mushroom.exe
LIBS := alleg
# Now alter any implicit rules' variables if you like, e.g.:
#
# 现在来改变任何你想改动的隐含规则中的变量,例如
CFLAGS := -g -Wall -O3 -m486
CXXFLAGS := $(CFLAGS)
# The next bit checks to see whether rm is in your djgpp bin
# directory; if not it uses del instead, but this can cause (harmless)
# `File not found' error messages. If you are not using DOS at all,
# set the variable to something which will unquestioningly remove
# files.
#
# 下面先检查你的 djgpp 命令目录下有没有 rm 命令,如果没有,我们使用
# del 命令来代替,但有可能给我们 'File not found' 这个错误信息,这没
# 什么大碍。如果你不是用 DOS ,把它设定成一个删文件而不废话的命令。
# (其实这一步在 UNIX 类的系统上是多余的,只是方便 DOS 用户。 UNIX
# 用户可以删除这5行命令。)
ifneq ($(wildcard $(DJDIR)/bin/rm.exe),)
RM-F := rm -f
else
RM-F := del
endif
# You shouldn't need to change anything below this point.
#
# 从这里开始,你应该不需要改动任何东西。(我是不太相信,太NB了!)
SOURCE := $(wildcard *.c) $(wildcard *.cc)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \
$(patsubst %.d,%.cc,$(MISSING_DEPS)))
CPPFLAGS += -MD
.PHONY : everything deps objs clean veryclean rebuild
everything : $(EXECUTABLE)
deps : $(DEPS)
objs : $(OBJS)
clean :
@$(RM-F) *.o
@$(RM-F) *.d
veryclean: clean
@$(RM-F) $(EXECUTABLE)
rebuild: veryclean everything
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM-F) $(patsubst %.d,%.o,$@)
endif
-include $(DEPS)
$(EXECUTABLE) : $(OBJS)
gcc -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))