.h和.c文件的差别(在.h声明,.c中实现)
.h:声明一个文件/declare,int add(int a, int b),编译器会根据声明来判断你用的函数对不对 .c:实现/(define | implement),int add(int a, int b) { return a + b; }
.h文件在哪里
系统目录(工具链目录),也可以自己指定目录
printf在哪里
在库里面,库在哪里 => 可以在系统目录,也可以自己指定目录
Makefile
基本规则
目标 : 依赖1 依赖2 ...
[TAB]命令
编译和链接分开执行
gcc -c -o a.o a.c
gcc -c -o b.o b.c
链接:
gcc -o test a.o b.o
比如:上面的例子,当我们修改a.c之后,a.c会重现编译然后再把它们链接在一起就可以了。b.c就不需要重新编译。
那么问题又来了,怎么知道哪些文件被更新了/被修改了?
比较a.o和a.c的时间,如果a.c的时间比a.o的时间更加新的话,就表明.ac被修改了,同理,a.c和a.o也是这样。a.o或者b.o的时间比test更加新,就表明需要重新生成test文件。
当“依赖”比“目标”新,执行它们下面的命令。我们要把上面三个命令写成makefifile规则,如下:
test :a.o b.o //test是目标,它依赖于a.o b.o文件,一旦a.o或者b.o比test新的时候,
就需要执行下面的命令,重新生成test可执行程序。
gcc -o test a.o b.o
a.o : a.c //a.o依赖于a.c,当a.c更加新的话,执行下面的命令来生成a.o
gcc -c -o a.o a.c
b.o : b.c //b.o依赖于b.c,当b.c更加新的话,执行下面的命令,来生成b.o
gcc -c -o b.o b.c
Makefile基本语法
自动变量
- $@:目标
- $^:所有目标依赖
- $<:目标依赖列表中的第一个依赖
- $?:所有目标依赖中被修改过的文件
test: a.o b.o
gcc -o test $^
%.o: %.c
gcc -c -o $@ $<
假想目标
变量(即时变量,延时变量)
即时变量:
A := xxx; // A的值即可就可以确定,定义时就已经确定
延时变量:
B = xxx; // B的值使用时才确定
E还没有赋值,所以即时变量拿到的值是空,延时变量就可以拿到abc这个值
:= 即时变量
= 延时变量
?= 延时变量, 如果是第一次定义才会有效,如果在前面该变量已定义,则忽略这句
+= 附加,及时变量还是延时变量取决于前面的定义 (C = ABC C += 123),此时就是延时变量
条件赋值?=
条件赋值是指一个变量如果没有被定义过,就直接给它赋值;如果之前被定义过,那么这条赋值语句就什么都不做。如下面的语句:
CC = gcc
CC ?= arm-linux-gnueabi-gcc
$(BIN): $(OBJS)
@echo $(CC)
$(CC) -o $(BIN) $(OBJS)
当make解析Makefile,遇到上面的条件赋值语句时,因为CC已经被定义过而且被赋值,所以这个条件语句就会什么都不做。在Makefile中使用echo (CC)的值是gcc
追加赋值
追加赋值是指一个变量,以前已经被赋值,现在想给它增加新的值,此时可以使用+=追加赋值。如下面的语句:
OBJS = hello.o
OBJS += module.o
就等价于:
OBJS = hello.o module.o
关闭执行的命令打印
如果你不想在make编译的时候打印正在执行的命令,可以在每条命令的前面加一个@
C := $(E)
D = $(E)
E = abc
alls
@echo $(C)
@echo $(D)
#环境变量
通过export传递变量
条件判断
ifeq 关键字
ifeq关键字用来判断两个参数是够相等,相等时条件成立为true,不相等为false。ifeq一般和变量结合使用:
mode = debug
hello: hello.c
ifeq ($(mode),debug)
@echo "debug mode"
gcc -g -o hello hello.c
else
@echo "release mode"
gcc -o hello hello.c
endif
ifneq 关键字
ifdef 关键字
ifndef 关键字
用户自定义函数
make提供了一系列文本处理函数:subst、patsubst、strip、findstring、filter、filer-out、sort、word、wordlist、words、fistword。
函数
list = a b C
name = $(foreach item, $(list), $(item).o)
foreach:
@echo name = $(name)
执行:make foreach
filter_list = a b c d.c
filter_value = $(filter %.c, $(filter_list))
filter_not_value = $(filter-out %.c, $(filter_list))
filter:
@echo filter_value = $(filter_value)
@echo filter_not_value = $(filter_not_value)
files = $(wildcard *.c)
file2 = a.c b.c c.c d.c e.c # 判断那些文件真实存在
file3 = $(wildcard $(file2))
wildcard:
@echo files = $(files)
@echo file2 = $(file3)
file2 = a.c b.c c.c d.c e.c
dep_file = $(patsubst %.c, %.d, $(file2)) # 替换并且生成一个新文件
patsubst:
@echo dep_file = $(dep_file)
打印依赖文件
gcc -M a.c
把依赖写入文件c.d
gcc -M -MF c.d c.d
编译c.o,把依赖写入文件c.d
gcc -c -o c.o c.c -MD -MF c.d
makefie文件可以写成这样
gcc -c -o $@ $< -MD -MF .$@.d // .c$@.d表示隐藏文件
解决包含.h的编译修改不重新编译的问题
#解决make时包含.h的编译修改不重新编译的问题
objs = a.o b.o
dep_file := $(patsubst %, .%.d, $(objs))
dep_file := $(wildcard $(dep_file))
test: $(objs)
gcc -o test $^
echo $(dep_file)
ifneq ($(dep_file),) # dep_file != 空
include $(dep_file)
endif
%.o: %.c
gcc -c -o $@ $< -MD -MF .$@.d
clean_dep:
rm $(dep_file) $(objs)
.PHONY: clean_dep
添加CFLASS
CFLASS = -Werror # 警告变错误
gcc $(CFLASS) -c -o $@ $< -MD -MF .$@.d
CFLASS = -Werror -Iinclude #指定include文件夹为默认的头文件文件夹
自定义的.h文件可以写成 #include
gcc $(CFLASS) -c -o $@ $< -MD -MF .$@.d
通用makefile使用
使用说明:
本程序的Makefile分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile
一、各级子目录的Makefile:
它最简单,形式如下:
EXTRA_CFLAGS :=
CFLAGS_file.o :=
obj-y += file.o
obj-y += subdir/
"obj-y += file.o" 表示把当前目录下的file.c编进程序里,
"obj-y += subdir/" 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
"EXTRA_CFLAGS", 它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
"CFLAGS_xxx.o", 它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
注意:
1. "subdir/"中的斜杠"/"不可省略
2. 顶层Makefile中的CFLAGS在编译任意一个.c文件时都会使用
3. CFLAGS EXTRA_CFLAGS CFLAGS_xxx.o 三者组成xxx.c的编译选项
二、顶层目录的Makefile:
它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,
主要是定义工具链前缀CROSS_COMPILE,
定义编译参数CFLAGS,
定义链接参数LDFLAGS,
这些参数就是文件中用export导出的各变量。
三、顶层目录的Makefile.build:
这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o
详细的讲解请看视频。
四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录
在各自子目录创建一个空白的Makefile
2.确定编译哪些源文件
修改顶层目录和各自子目录Makefile的obj-y :
obj-y += xxx.o
obj-y += yyy/
这表示要编译当前目录下的xxx.c, 要编译当前目录下的yyy子目录
3. 确定编译选项、链接选项
修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;
修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;
修改各自子目录下的Makefile:
"EXTRA_CFLAGS", 它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置
"CFLAGS_xxx.o", 它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置
4. 使用哪个编译器?
修改顶层目录Makefile的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)
5. 确定应用程序的名字:
修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字
6. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除
顶层Makefile
CROSS_COMPILE =
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include # 头文件:当前文件夹下面的include文件里面
LDFLAGS :=
export CFLAGS LDFLAGS
TOPDIR := $(shell pwd)
export TOPDIR
TARGET := test
# 编译顶层文件的那些文件
obj-y += main.o
obj-y += sub.o
obj-y += a/ # 要编译当前文件夹那个子目录
all : start_recursive_build $(TARGET)
@echo $(TARGET) has been built!
start_recursive_build:
make -C ./ -f $(TOPDIR)/Makefile.build
$(TARGET) : built-in.o
$(CC) -o $(TARGET) built-in.o $(LDFLAGS)
clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
distclean:
rm -f $(shell find -name "*.o")
rm -f $(shell find -name "*.d")
rm -f $(TARGET)
顶层Makefile.build
PHONY := __build
__build:
obj-y :=
subdir-y :=
EXTRA_CFLAGS :=
include Makefile
# obj-y := a.o b.o c/ d/
# $(filter %/, $(obj-y)) : c/ d/
# __subdir-y : c d
# subdir-y : c d
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y += $(__subdir-y)
# c/built-in.o d/built-in.o
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)
# a.o b.o
cur_objs := $(filter-out %/, $(obj-y))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files),)
include $(dep_files)
endif
PHONY += $(subdir-y)
__build : $(subdir-y) built-in.o
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build
built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^
dep_file = .$@.d
%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<
.PHONY : $(PHONY)