嵌入式开发--Makefile学习笔记

什么是makefile

  如果您是一个Linux环境下开发的程序猿,那么使用GNU meke来管理自己的项目工程显得尤为重要。在Linex(Unix)环境下使用 GUN 的make工具可以较容易地构建一个自己的工程,而该工程的编译可以通过一个命令就可完成项目的编译、连接直到最后的执行,但这需要投入一定的时间去完成一个或多个Makefile文件的编写。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极 大的提高了软件开发的效率也就是说Makefile是一个文件,make是一个命令工具,他解释了Makefile中的指令,在Makefile文件中描述了整个工程所有文件的编译顺序和规则,其有自己的书写格式、关键字和函数
  关于程序的编译过程可参考:Linux下gcc编译过程

语法规则

目标文件列表 分隔符 依赖文件列表 
[TAB][命令]
[TAB][命令]
····
eg:
target: depend1 depend2 depend3···
	action1
	action2
target1:
	action1
	action2

  有以下几点需要注意:

  • 一个完整的makefile文件由:显式规则(根据规则写出来的命令)、隐式规则(需要判断的逻辑命令)、使用变量、文件指示、注释五部分构成;
  • 若命令行单独成一行,则开头必须为Tab键;
  • 通常将makefile文件中的第一行目标文件作为最终目标文件;
  • 命令行的Tab键后可增加+、-、@符号在接命令:
     +:代表本行命令始终被执行;
      -:代表本行命令若遇到错误继续执行而不退出;
     @:代表本行命令执行但不打印该条命令内容。

  通过下面的小例子简单了解下makefile编写:
  首先编写test.c:

#include <stdio.h>

int main()
{
    printf(" 2 + 6 = %d\n", add(2, 6));
    printf(" 6 - 2 = %d\n", sub(6, 2));
    return 1;
}

  然后分别编写add.c和sub.c:

#include "test.h"

int add(int a, int b)
{
    return a + b;
}
#include "test.h"

int sub(int a, int b)
{
    return a - b;
}

  最后编写test.h:

#ifndef _TEST_H
#define _TEST_H

int add(int a, int b);
int sub(int a, int b);
#endif

  通过gcc编译过程我们可知道首先要将三个c文件通过gcc -c xxx命令编译程.o文件再链接成为可执行文件,因此我们的makefile可这样编写:

#以'#'开头的行表示注释 
#这里的test为整个makefile文件的第一个目标,当输入make命令时会完成该目标;
#该目标有三个依赖:add.o sub.o test.o,和一个动作make clean.
#若想执行完最终目标,则要先执行依赖目标,可以理解为递归的过程。
test: add.o sub.o test.o
    gcc -o test add.o sub.o test.o
	make clean

#在下面的动作中用 @ 符号来让make命令执行但补打印该命令,之输出结果;make命令执行过程中,会多次载入makefile文件。
add.o: add.c test.h
    @gcc -c add.c
    
sub.o: sub.c test.h
    @gcc -c sub.c    
    
test.o:
    @gcc -c test.c  
    
#该目标被称为伪目标,其不为最终目标生成的依赖,但可通过最终目标动作而执行;
#用来删除所有以.o文件结尾的文件,通过make clean来调用
clean:
    rm -rf *.o

  运行结果如下:

panghu@Ubuntu-14:~/makefile$ ls
add.c  makefile  sub.c  test.c  test.h
panghu@Ubuntu-14:~/makefile$ make
gcc -o test add.o sub.o test.o
make clean 
make[1]: 正在进入目录 `/home/panghu/makefile'
rm -rf *.o
make[1]:正在离开目录 `/home/panghu/makefile'
panghu@Ubuntu-14:~/makefile$ ls
add.c  makefile  sub.c  test  test.c  test.h
panghu@Ubuntu-14:~/makefile$ ./test 
 2 + 6 = 8
 6 - 2 = 4

Make选项

  • -k(常用):使make命令发现错误仍然执行而不是退出,用过该选项可发现未编译成功的源文件;
  • -n(常用):让make输出将要执行的操作步骤,但并不真正执行;
  • -f(常用):将文件名为filename的文件作为makefile文件,否则make将会在当前目录下寻找makefile或Makefile文件。
  • -d:打印所有的调试信息;
  • -i :忽略执行过程中产生的错误;
  • -t :把所有目标文件的最后修改时间设置为当前系统时间;
  • -q :不执行任何命令,只返回一个查询状态,0:没有目标需要重建 1:存在需要重建的目标 2:错误发生。

使用变量

makefile中的变量可理解为c/c++中的宏定义,通常起到一种替换的作用,为了看起来方便些在使用时我们会在变量名前加上“$”符号,最好用小括号或者大括号括起来,而我们如果需要用到$符号则需要用$$来表示,定义变量的形式:变量名 赋值符 变量值
 经常用到的有如下三种形式:

#定义变量VAR,强制赋值为test 
VAR=$(test)
   
#在VAR之前定义的值后面再追加app这个值,这时该变量值扩展为testapp   
VAR+=$(app)
    
#如果之前VAR没有被定义,则定义并使用testapp;否则使用之前的值。 
VAR?=&(testapp) 

  下面这个例子:

foo = $(bar)
bar = $(ugh)
ugh = hello

all:
echo $(foo)

  我们执行make all会打印出hello由此可见变量可使用后面的变量来定义,这可能会使make陷入循环中,因此我们引入另一种操作:=用来避免使用后面的变量定义:

x := foo
y := $(x) bar
x := later
等价于:
y := foo bar
x := later

  下面列出一些系统内的预定义变量:

宏名 初始值 说明
CFLAGS -o 编译器使用的选项
CC cc 默认使用的编译器
MAKE make make命令
PWD 运行make命令时的当前目录
AR ar 库管理命令
ARFLADS -ruv 库管理命令选项
LIBSUFFIXE .a 库后缀
A a 库扩展名

条件判断
通过ifeq条件判断来让make根据不同情况执行不同的分支,表达式可以为变量的值或者变量与常量的比较例如:

libs_for_gcc = -lgnu
normal_libs =

foo: $(test)
ifeq ($(CC),gcc)
$(CC) -o foo $(test) $(libs_for_gcc)
else
$(CC) -o foo $(test) $(normal_libs)
endif

巨人的肩膀:Makefile经典教程(掌握这些足够)

猜你喜欢

转载自blog.csdn.net/weixin_42647166/article/details/105338174