Makefile tutorial

这篇文章主要内容来自这里。GNU Make是用于控制产生可执行文件以及像项目中其他非源文件的。

GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program’s source files. ———–GNU make 官网

基本语法

makefile的基本格式如下:

targets: prerequisites
   command
   command
   command

表示的意思是:

  • targets: 需要产生的文件的文件名,通常以空格隔开,但是往往都只有一个文件
  • command:用于产生targets的一串命令(当然可能也只有一条),需要以tab为起始字符,而不是空格
  • prerequisites:也是文件,以空格分隔。这些文件需要在运行命令之前 就存在了。也叫做依赖。可以理解为targets依赖于prerequisites,通过运行命令command来,以prerequisites为源文件来生成targets。

一个例子:

blah: blah.o
    cc blah.o -o blah # Runs third

blah.o: blah.c
    cc -c blah.c -o blah.o # Runs second

blah.c:
    echo "int main() { return 0; }" > blah.c # Runs first

这里的关系是,运行make blah,会发现它依赖于blah.o,于是需要运行cc blah.o -o blah来生成blah.o。blah.o又依赖于blah.c。所所以又需要编译blah.c来s生成blah.o。

默认目标:

第一个出现的target是默认target,直接输入make,会直接运行第一个target。如下:

hello:
    @echo "Hello World"

会在终端输出Hello World。

Make很重要的一点就是,它可以自动地去了解是否对文件有过更新,下面的例子可能不是十分的恰当:

some_file:
    @echo "This line will only print once"
    touch some_file

在第一次执行的时候会创建一个some_file文件,在第二次的时候会发现,文件已经存在,所以会提示make: ‘some_file’ is up to date。

通常都会创建一个clean的target来完成删除文件。运行make clean即可删除文件

clean:
    rm -f some_file

变量

变量只能为string类型,在使用变量的时候用$()或者${},但是变量的定义还有好几种,如 :=,?=等等暂时先不涉及。

files = file1 file2

some_file: $(files)
    @echo "Look at this variable" $(files)
    touch some_file

file1:
    touch file1

file2:
    touch file2
clean:
    rm file1 file2 some_file

define

在普通的赋值语句中值不能换行,但是经过由define包裹的都视为值的一部分。define后面紧跟着的是变量的名字,接下来行当中的都将视为是变量的值。最后以endef表示define的结束。

define foo
    @echo "Hello world"
    @echo "beautiful"
endef

all:
    $(foo)

上面的语句等同于foo = @echo “Hello World”;@echo “beautiful”。define可以让复杂的变量定义看起来更加的简洁。

All Targets

使用all来一次性make 多个target,all并不是强制性的。不是说Makefile中不存在all就无法编译。根据GNU make标准只是should ,而不是must。

all: one two three

one:
    @echo "one"

two:
    @echo "two"

three:
    @echo "three"

multiple targets

当targets是多个文件名的时候,此时手动输入很麻烦,可以使用自动变量(automatic variable)。

all:file1.o file2.o

file1.o file2.o:
    @echo $@

通配符

*和%都是Make中的通配符,但是他们非常不一样。复杂一些。*用在文件名匹配中,比如说我们都知道*.c表示所有的.c文件,但是在Make中这样使用是不对的。因为*.c在prerequisites和变量中出现的时候会被当作是字符。所以我们一般都是按照如下使用的:

#下面的一句可以显示当前路径所有的.c文件名
CFILES=$(wildcard *.c)

hello:
    @echo $(CFILES)

%有些比较复杂,待会再说。

自动变量

自动变量有很多。官网文档

下面是几个常用的自动变量:

  • $@:The file name of the target of the rule.
  • $<:The name of the first prerequisite.
  • $?:The names of all the prerequisites that are newer than the target, with spaces between them.
  • $^:The names of all the prerequisites, with spaces between them.

Make中的shell命令

在命令之前加上@会使得在命令执行的时候不输入命令的内容。

hello:
    echo "Hello World" #会输出这条命令的内容
    @echo "Hello World" #不会输出,只会输出Hello World

Shell命令的执行

普通的命令,在效果上而言是执行在不同的一个shell当中的,所以两条命令之间不会相互印象

all: 
    cd ..
    # The cd above does not affect this line, because each command is effectively run in a new shell
    echo `pwd`

    # This cd command affects the next because they are on the same line
    cd ..;echo `pwd`

    # Same as above
    cd ..; \
    echo `pwd`

递归的make

不能使用make来实现递归的make。要使用$(MAKE)来进入到子文件夹中make。

all:
    @echo "foo"
    cd subdir && $(MAKE)

subdir中的Makefile

hello:
    @echo "Hello World"

export 变量

可以将父文件夹中的变量使用export关键词来使得子文件夹中的Makefile可以调用。

NAME="jack"
all:
    @echo "foo"
    cd subdir && $(MAKE)

export NAME

subdir中的Makfile

hello:
    @echo "Hello World"
    @echo $(NAME)

使用来自命令行的变量

很简单。make NAME=jack

all:
    @echo "Hello $(NAME)"

Makefile中的条件语句

基本的条件语句在Makefile中的形式如下:

ifeq true 
    command 
else 
    command
endif

下面是几个常用的例子:

foo = ok

all:
ifeq ($(foo), ok)
    echo "foo equals ok"
else
    echo "nope"
endif

判断是否变量为空

nullstring =
foo = $(nullstring) # end of line; there is a space here

all:
ifeq ($(strip $(foo)),)
    echo "foo is empty after being stripped"
endif
ifeq ($(nullstring),)
    echo "nullstring doesn't even have spaces"
endif

判断变量是否定义

bar =
foo = $(bar)

all:
ifdef foo
    echo "foo is defined"
endif
ifdef bar
    echo "but bar is not"
endif

函数

替换:

  • $(subst from,to,text) 将text中的from替换为to.
  • $(patsubst pattern,replacement,text) text中以空格分隔的字符满足pattern替换为replacement。 这种方法有更加简单的形式来表示,假设有一个变量var,$(patsubst pattern,replacement,$(var)) 可以写作$(var:pattern=replacement)。一个非常常用的用法是。
  objects = foo.o bar.o baz.o
  $(objects:.o=.c)

将所有的.c都替换为了.o

  • $(foreach var,list,text) 遍历list中的元素,list中的元素都是以空格为分隔符的。每一个当前被遍历的元素为var,可以附加一些才做在text上。最后也返回一个一个空格分隔的字符串。
  CFILES=$(wildcard *.c)
  RESULT=$(foreach file,$(CFILES),$(file).bat)
  all:
      @echo $(RESULT)

在每个.c后面追加一个.bat。然后放到RESULT中。

  • $(shell )可以直接运行shell命令
  all: 
      @echo $(shell ls -la)

.phony

.phony可以用于避免target和文件名的重复。比如说可能存在一个clean和一个clean的target。编译的时候就会出问题。

some_file:
    touch some_file
    touch clean

.PHONY: clean
clean:
    rm -f some_file
    rm -f clean
暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇