SunnyOS准备2
Makefile:
我们如何编译链接程序,很简单按F9或ctrl+F5,这是绝大多数IDE帮我们做的工作,但是在unix系统下却没有这么幸运,当我们要编译链接程序时,我们使用gcc编译器:
gcc -E test.c -o test.i gcc -S test.i -o test.s gcc -c test.s -o test.o gcc test.o -o test
实际上:gcc test.c -o test一步就可,上面只是对其的拆解
但是对于多个文件(一个大型项目来说),总不能gcc file1 file2......(GG ,所以编写Makefile文件就很有必要,本文只介绍一些Makefile规则,具体的请参考
http://scc.qibebt.cas.cn/docs/linux/base/%B8%FA%CE%D2%D2%BB%C6%F0%D0%B4Makefile-%B3%C2%F0%A9.pdf
Makefile主要规则:
<target> : <prerequisites>
[tab] <cmmand>
其中:目标(target)不能省略,前置条件(prerequisites)和命令(command)是可选的,但是两者必须存在一个
目标:可以为文件名
前置条件:构建目标的前置条件
命令:如何构建
1.目标(target):
①
clean :
rm *.o
当我们执行make clean时,make会先检查当前目录下是否有名为clean文件,如果有那么就无需再构建了,make clean什么也不执行。但是我们希望clean并不是一个文件名而是一个操作名,此时代码要改成:
.PHONY : clean clean : rm *.c
此时执行make clean,make就不会去检查有没有clean这个文件,而是直接执行command。clean此处就成了伪目标
②当执行仅仅只有make,那么默认执行第一个target
2.前置条件(prerequisites)
通常为一组文件名,中间用空格分隔,可以用 '\' 表示下一行与上一行相连
对于目标(target)为文件名情况下,该目标文件不存在或者前置文件比目标文件新时执行重构
对于目标为伪目标时,有个前置条件不存在就重构,生成多个前置条件
例子1:
result.txt : source.txt cp source.txt result.txt
当result.txt不存在时或者source.txt比result.txt新就执行重构
若source.txt文件不存在,那么就需要再写:
source.txt : echo "this is source " > source.txt
那么执行:
make result.txt
make result.txt
第一个执行后,先生成source.txt再生成result.txt,第二个由于source.txt比result.txt晚不执行重构
例子2:
想要生成file1.txt,file2.txt,file3.txt
方法一:
make file1 make file2 make file3 file1 : touch file1.txt #其他类似
方法二:
make source source : file1.txt file2.txt file3.txt
由于前置文件不存在所以重构
3.命令(command):
①<command>前面一定要加[tab]
可以使用其他符号代替[tab],申明:
.RECIPEPREFIX = > all : > echo helloworld
②<command>每行命令各在一个shell中执行
var-lost : export foo = bar #生成shell变量 echo "foo = [$$foo]" #取不到foo值,因为上下两行在不同shell中执行
解决方案:
Ⅰ.
var-lost : export foo = bar;echo "foo = [$$foo]"
Ⅱ
var-lost : export foo = bar\ echo "foo = [$$foo]"
Ⅲ
.ONESHELL var-lost : export foo = bar echo "foo = [$$foo]"
原理都是将两行放在同一个shell中执行
③“#”表示注释
④回声(echoing)问题
make会打印每条命令(command)再执行,包括了#注释,为了不打出注释内容改成:
#注释 -----> @#注释
test:
@# 这是测试
@echo TODO
由于在构建过程中,需要了解当前在执行哪条命令,所以通常只在注释和纯显示的echo命令前面加上@。
⑤通配符:
*.o:表示当前目录下所有后缀为.o文件
file?:表示当前目录下file为前缀文件例如:file1,file2....
对于目标(target),可以 "{文件1名,文件2名....}.o: " 表示
⑥模式匹配:
当前目录有f1.c和f2.c文件,需要将他们编译成.o文件
%.o : %.c =
f1.o : f1.c
f2.o : f2.c
使用匹配符%,可以将大量同类型的文件,只用一条规则就完成构建。
⑦变量:
txt = hello world test : echo $(txt)
上面代码中,变量 txt 等于 hello world。调用时,变量需要放在 $( ) 之中。
调用Shell变量,需要在美元符号前,再加一个美元符号,因为Make命令会对"$"转义。
特殊点!!!:
v1 = $(v2)
v2是变量,v1也是变量,这时会产生一个问题,v1 的值到底在定义时扩展(静态扩展),还是在运行时扩展(动态扩展)?如果 v2 的值是动态的,这两种扩展方式的结果可能会差异很大。(就像c中是程序编译时赋值还是运行时赋值)
规定:
VARIABLE = value
# 在执行时扩展,允许递归扩展。
VARIABLE := value
# 在定义时扩展。
VARIABLE ?= value
# 只有在该变量为空时才设置值。
VARIABLE += value
# 将值追加到变量的尾端。
内置变量:"$(cc)"指向当前使用的编译器默认是cc,"$(cxx)"默认“g++”,$(make)指向当前使用make工具
自动变量:
$@ :指代当前的目标(target)挨个值
下面这个例子表示了,把所有的[.c]文件都编译成[.o]文件.
%.o : %.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中,"$@"表示所有的目标的挨个值,"$<"表示了所有依赖目标的挨个值。
a.txt b.txt:
touch $@
=
a.txt:
touch a.txt
b.txt:
touch b.txt
$<:指代第一个前置条件(prerequisites)
a.txt: b.txt c.txt
cp $< $@
=
a.txt: b.txt c.txt
cp b.txt a.txt
$?:指代比目标文件更加新的所有前置条件
$^:指代所有前置条件挨个值
综合运用:
dest/%.txt: src/%.txt @[ -d dest ] || mkdir dest cp $< $@
实现的是将src中.txt拷贝到dest目录中,首先前置条件是src种.txt文件,目标文件是dest种.txt文件,先判断dest目录是否存在,不存在则mkdir创建,之后拷贝。$< 指代前置文件(src/%.txt), $@ 指代目标文件(dest/%.txt)。
[-d 目录名] :判断目录是否存在
[-f 文件名]:判断文件是否存在
更多详见bash
⑧判断和循环
判断例子:
ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif
上面代码判断当前编译器是否 gcc ,然后指定不同的库文件
循环例子:
LIST = one two three all: for i in $(LIST); do \ echo $$i; \ done # 等同于 all: for i in one two three; do \ echo $i; \ done 最终输出: one two three
⑨函数:
格式:
$(function arguments)
# 或者
${function arguments}
内置函数:
Ⅰ:shell函数
shell 函数用来执行 shell 命令
srcfiles := $(shell echo src/{00..99}.txt)
Ⅱ:wildcard函数
在变量定义和函数引用时,通配符使用失效,此时要用wildcard代替通配符
格式:
$(wildcard PATTERN)
例如
$(wildcard *.c)” 来获取工作目录下的所有的.c文件列表
例子:
objects := $(patsubst %.c,%.o,$(wildcard *.c)) source := $(wildcard *.c) $(objects) : $(source) cc $(source) -o $(objects)
过程:
$(patsubst %.c,%.o,$(wildcard *.c))将$(wildcard *.c)找到的.c文件的后缀.c改成.o(但是原先.c文件并没有变)赋值给objects(好比source为file.c,objects为file.o),之后编译链接
Ⅲ:subst 函数
例子:
$(subst ee,EE,feet on the street) 替换后为fEEt on the strEEt
复杂例子:
comma:= , empty:= # space变量用两个空变量作为标识符,当中是一个空格 space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo)) # bar is now `a,b,c'.
把a b c中空格换成“,”
Ⅳ:patsubst函数
格式
$(patsubst pattern(text后缀),replacement,text)
例子:
$(patsubst %.c,%.o,x.c.c bar.c)
将文件名"x.c.c bar.c",替换成"x.c.o bar.o"
Ⅴ:替换后缀名
替换后缀名函数的写法是:变量名 + 冒号 + 后缀名替换规则。它实际上patsubst函数的一种简写形式。
min: $(OUTPUT:.js=.min.js)
将变量OUTPUT中的后缀名 .js 全部替换成 .min.js
Done!!!
引用:
posted on 2017-08-09 14:39 chaunceyctx 阅读(164) 评论(0) 编辑 收藏 举报