美文网首页
关于makefile

关于makefile

作者: 404Not_Found | 来源:发表于2021-09-21 01:50 被阅读0次
  • 作者: 雪山肥鱼
  • 时间:20210921 11:33
  • 目的: 了解makefile

传统编译

三个文件,main.c print.c print.h


main.c.png print.h.png print.c.png

即使源文件很少,频繁的使用命令行编译也很烦

gcc main.c print.c -o a.out

不用指定头文件的原因是 print.h 文件在当前目录。
更别提有上百上千个 源文件 和头文件。

makefile 简单规则

文件名:可以Makefile, 也可以makefile, 但不能是MakeFile.

all(目标):所需要的文件
(只能是tab) gcc print.c main.c -o mytest
            echo "gcc success"            

改进一:

这是最简单的makefile,其实并不实用。
原因1: 即使源文件没有改动,依然会执行编译,文件少无所谓,文件多则会消耗时间。

编译原理:只会执行第一个目标。

all:print.c main.c
  gcc print.c main.c -o myTest
  1. 判断 all 是不是一个文件,如果是文件,则会与后面的依赖文件进行对比。
  2. print.c main.c 也有时间,all 与 这两个文件的时间进行对比
  3. all 比 后面 所有依赖的时间都要新,则没必要重新编译
  4. 如果 后面依赖的文件比all 还要新,则会重新编译。

现在目标文件不存在,则文件时间为0, 永远老于后面的依赖文件,所以每次都会重新生成。

myTest: print.c  main.c
  gcc print.c main.c -o myTest 

这样即可。

改进二

只要修改一个文件,就会编译整个工程,这样也不是一个合格的makefile。
阶段分开,生成.o 文件是一个阶段,链接是一个阶段。

myTest:print.o main.o
        gcc print.o main.o -o myTest

print.o:print.c
        gcc -c print.c

main.o:main.c
        gcc -c main.c

这样只修改一个main.c ,就避免了修改一个文件,需要重新编译整个项目的问题。

  1. 还是会优先找第一个 目标文件,发现会依赖2个.o 文件。去找.o 的目标文件,对比main.c 与main.o 的时间。
  2. print.c 与 print.o 时间相同,说明没有更新
  3. main.o 的时间更新了,所以 myTest 与 main.o 对比,发现自己老了,则需要重新编译。

改进三

变量的替换

objs=printf.o main.o
target=myTest

$target:$(objs)
  gcc $(objs) -o $(target)

print.o:print.c
  gcc -c print.c
main.o:main.c
  gcc -c main.c

改进四

重新编译的话,我们需要手动去删除.o 文件,这也不行的。

objs=printf.o main.o
target=myTest

$target:$(objs)
  gcc $(objs) -o $(target)

print.o:print.c
  gcc -c print.c
main.o:main.c
  gcc -c main.c

clean:
  rm -r $(objs) $(target)
  echo "clean success"

直接 make clean 即可
但还是有问题,如果 文件不存在 就会报错,无法删除xxx.o 文件,没有哪个文件或者目录
如果失败,则后续的echo 命令都不执行
@隐藏掉echo 命令本身

clean:
  -rm -r $(objs) $(target)
  @echo "clean success"

make clean 失效

如果此时touch clean, 有一个clean 的目标文件
clean 这个目标: 无依赖文件, 则可以认为 clean 依赖的文件时间为0,永远最旧, 所以相对于 已经存在的 clean 文件,这个clean文件永远是最新的。所以没必要执行 下面 rm rf 的动作了。
没有 clean 文件, 则相当于 目标clean 也为0,则会重新生成。
所以进行修改,增加 phony 假的 虚假的目标

.PHONY:
clean:
  -rm -rf $(obj) $(target)
  @echo "clean success"

改进四

有很多的 .c 文件,该怎么办,难道依赖的.o 文件都要写一遍?

objs=printf.o main.o
target=myTest

$target:$(objs)
  gcc $(objs) -o $(target)

%.o:%.c
  gcc -c $^ -o $@

.PHONY
clean:
  rm -r $(objs) $(target)
  echo "clean success"

%o:.%c 对应的规则,一个.c 生成一个.o 的规则。
^: .o 对应的所有 .c 文件,也就是所有的依赖项<: .o 对应的第一个.c 文件,也就是第一个所依赖项。
当前因为只有1个依赖c文件 所以两者一样,如果有两个,则不同。一个取全部,一个取一个
$@: 变量替换
符合这个条件的目标与依赖项,执行gcc 命令

备注

如果不写 构建规则:%.o : %.c
也会编译成功但是 是cc 进行编译。
什么是cc呢? 是一种内建规则。

make -p|less
/%.c

有一条内置规则
%.o:%.c
# recipe to execute(内置)
  $(COMPILE.c) $(OUTOUT_OPTION) $< 

其实 cc 就是 gcc

make -p > makefile.log// 查看各种变量的意义。须在makefile 所在的文件目录。 
COMPILE.c.png

CFLAGS: 编译时刻的参数
CPPFLAGS: 预处理时刻的参数 -D -I -L
TARGET_ARCH:平台
LDFLAGS:链接阶段 (非内置,需要自己定义) -L 库路径 -l 哪个库

objs=printf.o main.o
target=myTest
CFLAGS=-g
CPPFLAGS=-DDEBUG
LDFLAGS=

$target:$(objs)
  gcc $(objs) -o $(target) $(LDFLAGS)

%.o:%.c
  gcc -c $^ -o $@

.PHONY
clean:
  rm -r $(objs) $(target)
  echo "clean success"

-DDEBUG的作用:

#ifdef DEBUG
#define LOG() puts("debug version");
#else
#define LOG()
#endif

没必要在代码里强写一个 #define DEBUG
可以在makefile 里面 对 CPPFLAGS 预编译选项进行处理

CPPFLAGS=-DDEBUG

改进五

obj:print.o main.o 多个.o 会太长。 则可以通过 makefile 函数 如:(wildcard *.c) 可以查找当前目录下的所有.c 文件 然后把所有.c文件的列表 修改成.o
(pats ubst %c, %.o,(wildcard *.c)) 将所有.c 文件 替换成.o。

objs=$(pats ubst %c, %.o, $(wildcard *.c))
target=myTest
CFLAGS=-g
CPPFLAGS=-DDEBUG
LDFLAGS=

$target:$(objs)
  gcc $(objs) -o $(target) $(LDFLAGS)

%.o:%.c
  gcc -c $^ -o $@

.PHONY
clean:
  rm -r $(objs) $(target)
  echo "clean success"

安装 与 卸载

.PHONY:clean install distclean
install:
  cp $(target) /usr/local/bin -f //库 拷贝到 /usr/lib/ 中

distclean:
  rm -rf /usr/local/bin/$(target)

改进六

目录嵌套, 子目录中也有make, 将所有的makefile 都执行一遍

.PHONY:clean install distclean all
all:
 make $(target)
 male -C subdir

make all 相当于执行 第一条 (target):(objs)
make -C subdir 进入子目录,再执行一次make。

最终:

objs=$(pats ubst %c, %.o, $(wildcard *.c))
target=myTest
CFLAGS=-g
CPPFLAGS=-DDEBUG
LDFLAGS=

$(target):$(objs)
  gcc $(objs) -o $(target) $(LDFLAGS)

%.o:%.c
  gcc -c $^ -o $@

.PHONY:clean install distclean all
all:
  make $(target)
  make -C subdir
install:
  cp $(target) /usr/local/bin -f //库 拷贝到 /usr/lib/ 中
distclean:
  rm -rf /usr/local/bin/$(target)
clean:
  rm -r $(objs) $(target)
  echo "clean success"

相关文章

  • 关于MakeFile

    #1、wildcard : 扩展通配符 #2、notdir : 去除路径 #3、patsubst :替换通配符 $...

  • 关于Makefile

    本文参考:Make 命令教程 - 阮一峰 本学期正在上一门具有“USC神课”之美誉的CSCI402 - Opera...

  • makefile入门二

    Makefile教程(绝对经典,所有问题看这一篇足够了) makefile很重要 0.1 关于程序的编译和链接 在...

  • 2022-08-25

    关于 kernel Makefile 中的技巧 MAKECMDGOALS 变量 MAKECMDGOALS 是 ma...

  • 编写Makefile及简单分析

    makefile的好处:一次编写,终身受益 makefile的命名规则: makefile Makefile ma...

  • Makefile 工程管理

    Ⅰ Makefile的用途 Ⅱ Makefile的构成 Ⅲ Makefile构成-----规则 Ⅳ Makefil...

  • win_c/c++ mess01

    1. win makefile 1.1 win makefile,eg: 1.2 makefile explai...

  • [C] Makefile

    Makefile Blog [Makefile的简便写法] [Makefile]菜鸟教程 [gcc编译声明问题] ...

  • 关于Makefile的使用

    本周主要学习在Linux系统下使用Makefile对多个C语言源程序进行编译。 对于多个C语言源程序编译的时候,可...

  • 迅为IMX6ULL开发板Ubuntu下C编程入门(二)

    本文是介绍3.3 初识 Makefile+3.4Makefile语法 3.3初识Makefile 3.3.1什么是...

网友评论

      本文标题:关于makefile

      本文链接:https://www.haomeiwen.com/subject/jymbgltx.html