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!!!

引用:

http://www.ruanyifeng.com/blog/2015/02/make.html

posted on 2017-08-09 14:39  chaunceyctx  阅读(158)  评论(0编辑  收藏  举报

导航