makefile简明教程

makefile  主要是编译就是用来编译源文件的

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,也可以执行操作系统的命令。(百度百科)

makefile的文件命名:makefile 或 Makefile

makefile的规则或者说写法就三个部分:目标、依赖、命令

具体怎么写呢 

目标:依赖

 (tab缩进)命令

 看一个小例子

例一:

app:main.c add.c sub.c mul.c

      gcc main.c add.c sub.c mul.c -o app

app就是最终要生成的目标,所有需要的c文件就是依赖

执行gcc命令

这就是一个最简单的makefile,但是它最大问题是,如果你修改了中一个.c文件,在gcc编译的时候会把所有的c文件都编译一遍,这样效率就很低,所以可以把他们分开写,先编译成.o二进制文件。这样就是第二个例子

例二:

app:main.c add.c sub.c mul.c
      gcc main.o add.o sub.o mul.o  -o  app
main.o :main.c
       gcc main.c -c
add.o :add.c
      gcc add.c -c
sub.o :sub.c
      gcc sub.c -c
mul.o :mul.c
      gcc mul.c -c

只有app是要生成的终极目标,下面是为它服务的小目标

 这样当你更改某一个.c文件后,只会编译你修改的那个c文件,编译成.o二进制文件,而不用把所有的都编译一遍,节省很多时间。

那makefile是怎么知道要重新编译的是哪一个文件呢。

原理是

首先向下搜索下面的规则,检测下面的规则里是否有终极目标app需要的依赖,

如果依赖存在就判断是否需要更新也就是小目标是否要执行,

判断的依据是目标和依赖的时间,如果目标的时间大于依赖的时间,不更新,反之需要更新。说白了就是它会根据文件时间的变化检测你有没有更改源文件,更改了就要更新。因为一定是现有依赖才有的目标,目标.o文件时间要晚于依赖的时间也说成大于。也就是说一旦更改了依赖文件,这个文件最终时间就在目标之后了,依赖时间大于了目标时间,说明你修改了依赖文件,这时就需要更新。

但是上面这个makefile写的太冗余了。下面看下面一个例子改进一下:

例三:

前面说makefile就像一个脚本,那它就可以有变量和函数。所以先看一下变量,变量你可以自己定义,makefile本身也提供了几个固定的变量。

自定义变量:makefile里面自定义变量没有类型 直接赋值就行,例如:obj = a.o b.o c.o 或者 obj  = 10 ,不讲道理的使用,

但是变量不能直接使用要取出变量的值,用$符号,例如$(obj)

makfile提供的变量:CC 、CPPLAGS、LDFLAGS、LIBS等

这些参数都和gcc命令相关,例如让:

CC=”gcc -march=k8 -O2 -s”                           

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib

LIBS = -lmysqlclient -liconv

CPPFLAGS='-I/usr/local/libjpeg/include -I/usr/local/libpng/include'

就是在gcc编译的时候需要指定一下头文件和库文件目录,写起来太长了,你就讲路径赋值给相应的变量,这是一种规范,你随便给别的变量也能用。甚至很多makefile文件中编译都没有gcc的字样就是把gcc赋值给了CC。当然也可以把这些值设置到环境变量中去export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib" ;

自动变量:$@、$<、$^,makefile提供了几个变量表示着固定的意思,不能对他们赋值且只能在规则命令中使用。

$@:规则中的目标

$<:规则中的第一个依赖

$^:规则中的所有的依赖

改写例二

obi = main.o add.o sub.o mul.o
target = app
$(target):$(obj)
     gcc $(obj)  -o  $(target)           这一行就可以替换成 gcc $^ -o $@
%.o:%.c                                 %模式匹配代表所有的.o文件和所有的.c文件
    gcc -c $< -o $@

 

 现在这个makefile的可移植性很差

例四:

这里看一下makefile的函数

makefile中所有的函数都有返回值,下面介绍两个makefile的函数

(1)wildcard 查找指定文件夹下指定类型的文件

     src = $(wildcard ./*.c)  当前目录下所有的.c文件并赋值给src

(2)patsubst匹配替换

    obj = $(patsubst %.c, %.o,$(src))  把src变量中所有.c文件替换为.o文件

src = $(wildcard ./*.c)
obj = $(patsubst %.c,%.c,$(src))
trarget = app
$(target):$(obj)
    gcc $^ -o $@
%.o:%.c
    gcc -c $< -o $@

 

例五:

前面说的makefile最后生成的是只有第一目标,也就终极目标。其实makefile也可以生成不是终极目标的目标,

调用使用 :make 目标名   ,例如一个makefile最后会执行make clean清理项目

src = $(wildcard ./*.c)
obj = $(patsubst %.c,%.c,$(src))
trarget = app
$(target):$(obj)
    gcc $^ -o $@
%.o:%.c
    gcc -c $< -o $@
hello:
   echo "hello,makefile"

 

执行make hello 会打印hello,makefile

 如果我们加一个,清理项目的操作,表示如果执行失败删除前面生成的文件。

clean:

    -mkdir  /abc

    -rm $(obj)&(target) -f       #-f表示强制执行 ,命令前的'-'号表示当前命令失败向下执行

这里有会有个问题就是,如果当前目录下有同名的clean文件,make clean就不会执行。前面例二就说了,makefile根据依赖的时间来判断会不会更新,但是这里clean没有依赖。

解决这个问题的方法是写一个伪目标  .PHONY:clean ,注意PHONY前面有个'.'符号。

完整的就是

src = $(wildcard ./*.c)
obj = $(patsubst %.c,%.c,$(src))
trarget = app
$(target):$(obj)
    gcc $^ -o $@
%.o:%.c
    gcc -c $< -o $@
.PHONY:clean
clean:
    -mkdir /abc
    -rm $(obj)&(target) -f 

 

 好了makefile就是这么简单。

最后写一个小例子

在plus目录下有两个目录include和src,现在在plus目录下编写makefile,编译src下的源文件,需要include中的头文件。

trarget = app
CPPFLAS = ./include
src = $(wildcard ./src/*.c)
obj = $(patsubst %.c,%.c,$(src))
$(target):$(obj)
   gcc $^ -o $@
%.o:%.c
   echo $<
   gcc -c $< -I $(CPPFLAGS) -o $@

 

上面可以甚至让 CPPFLAS = '-I./include',这样gcc中就不用再加参数 -I了

 

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值