makefile

makefile抽象层面的理解

学习某一样东西之前一定要明确学习的目的,即学习了这项工具能解决一些什么问题,其优势是什么?

makefile的优势就是能够动态根据文件的新旧来决定是否编译对应的文件,倘若每次编译一个项目都重新编译,特别是大项目的时候,岂不是很浪费时间?makefile能自动根据依赖关系解决这个问题,对于已经编译过的文件不再重新编译,而只选择编译尚未编译(或者新)的文件。

那么如何实现自动编译新文件呢?makefile是如何识别哪些是新的文件哪些是旧的文件的呢?

这就是makefile的核心--依赖关系

makefile中的语句就是为描述各种依赖关系而建立的。

例如下面这个例子

main:arithmetic.o main.o myprint.o
main.o:main.c
gcc -c main.c -o main.o
arithmetic.o:arithmetic.c
gcc -c arithmetic.c -o arithmetic.o
myprint.o:myprint.c
gcc -c myprint.c -o myprint.o

翻译过来,用通俗的话说就是:

  • 如果main的修改时间在arithmetic.o main.o myprint.o的修改时间之前,那么就更新arithmetic.o main.o myprint.o (实际上就是确保main是最新的,如果不是,就更新冒号右边的文件。下面的语句也是类似的)
  • 保证main.o是最新的
  • 保证arithmetic.o是最新的
  • 保证myprint.o是最新的

那么如何更新这个文件呢?就通过冒号:下面一行的语句(有时可省略)来进行更新的操作(gcc编译出新文件覆盖掉旧文件)

显然,如果每一个源程序都需要一一给出依赖关系那么这个工作量无疑是巨大的。为此makefile提供了一些更简便的操作方法来管理,如提供变量,嵌入shell语句等。这些操作方法使得makefile管理文件变得简单的同时,也使得makefile的学习成本增加。但是只要理解了上面makefile的基本原理,那么makefile中的其他高级操作不过是对这种建立依赖关系的方法进行了扩充,只要多背几条语句/语法规则即可。

makefile简单例子

下面,通过一个具体的实例来说明更新的过程。


main.c

#include <stdio.h>
#include "arithmetic.h"
#include "myprint.h" int main()
{
int a=1;
int b=1; printf("a+b=%d\n",add(a,b));
myprint();
printf("hello world!\n");
return 0;
}

myprint.h

#ifndef __MYPRINT
#define __MYPRINT #include <stdio.h>
void myprint(void); #endif

myprint.c

#include "myprint.h"

void myprint(void)
{
printf("my print\n");
}

arithmetic.h

#ifndef __ARITHMETIC
#define __ARITHMETIC int add(int a ,int b); #endif

arithmetic.c

#include "arithmetic.h"

int add(int a ,int b)
{
return a+b;
}

makefile

版本1

main:arithmetic.o main.o myprint.o
main.o:main.c
gcc -c main.c -o main.o
arithmetic.o:arithmetic.c
gcc -c arithmetic.c -o arithmetic.o
myprint.o:myprint.c
gcc -c myprint.c -o myprint.o
.PHONY:clean
clean:
rm -rf *.o

版本2

main:arithmetic.o main.o myprint.o
main.o:main.c
gcc -c $^ -o $@
arithmetic.o:arithmetic.c
gcc -c $^ -o $@
myprint.o:myprint.c
gcc -c $^ -o $@
.PHONY:clean
clean:
rm -rf *.o

版本3

main:arithmetic.o main.o myprint.o

%.o:%.c
gcc -c $^ -o $@
.PHONY:clean
clean:
rm -rf *.o

版本4

OBJS = 	main.o\
myprint.o\
arithmetic.o main:$(OBJS) %.o:%.c
gcc -c $^ -o $@
.PHONY:clean
clean:
rm -rf *.o

语法讲解:

%.o:表示当前目录下所有的.o文件

%.c:表示当前目录下所有的.c文件

$^:表示目标文件,即:左边的文件

$@:表示所有的依赖文件,即:右边的所有文件

.PHONY:clean:命令行输入make clean时会执行clean中的语句,但不会生成clean目标

一些常用的结论:

  • 可以将所有的目标-依赖语句(不包括前面部分的变量初始化语句)都抽象称为如下的表示

    目标文件:依赖文件
    执行语句

    那么所有的执行语句执行与否取决于目标文件和依赖文件的更新时间的比较(为了简化思考可以直接认为是一定会执行的)

  • 执行语句实际上就是shell命令


一般能理解并能手动写出版本4的makefile就足够用了。上面例程是仿照下面的博客做的,并加入了自己的一些理解和修改,感谢下面博客博主的热心分享。

博客连接,由此入

具体的语法这里也不再给出具体说明,因为这些资源网络上各处都有。更重要的是对makefile思想以及抽象层面上的理解。

实战:对实际的代码进行分析

要分析的代码:

KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m := kernel_hello.o
modules:
$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
@make clear
clear:
@rm -f *.o *.cmd *.mod.c
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
@rm -f .*ko.cmd .*.o.cmd .*.o.d
@rm -f *.unsigned
clean:
@rm -f hello.ko

分析:

  • 等号的区别

    var ?= will_not_excute
    var = first_assign
    var = second_assign
    result1 = $(var)
    result2 := $(var)
    result3 += $(var)
    result3 += $(var)
    var = final_assign
    main:
    @echo var final val: $(var)
    @echo result1: $(result1)
    @echo result2: $(result2)
    @echo result3: $(result3) 结果:
    $ make
    var final val: final_assign
    result1: final_assign
    result2: second_assign
    result3: final_assign final_assign

    从代码的执行结果分析,可以得出几点结论.

    1. ?=只有该变量在整个makefile都没有被赋值的时候才进行赋值
    2. :=右边部分调用$(var)会得到var上一次的值
    3. =右边部分调用$(var)会得到var在整个makefile中最后的值
    4. +=不用多说,就是追加操作
  • @ 语句

    表示不把正在执行的语句打印出来(但仍然会打印语句执行的结果)

    main:
    echo will display this statement
    @echo will not display this statement $ make
    echo will display this statement
    will display this statement
    will not display this statement
  • $(MAKE)解析出来就是"make"

  • -C  指明跳转到内核源码目录下读取那里的Makefile

    M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile

    这两个参数怎么理解呢?

    一般来说,一些大的工程会有几百个文件夹,而且每个文件夹里面都可能存在Makefile,这些Makefile记录着它所在文件夹下的源文件的编译步骤,但仅仅这样是不够的,这些Makefile只是做着自己当前目录下的事情,它们之间还需要进行一定程度的交流,那么,顶层Makefile就出现了,这个顶层Makefile不断地通过-C M=参数执行其管理的子Makefile,就像一个统筹全局的军师.有了顶层Makefile那么不管多大工程都能顺利编译出来了,而且还对一些模块进行了解耦,方便管理与修改.

makefile个人理解的更多相关文章

  1. Linux下对于makefile的理解

    什么是makefile呢?在Linux下makefile我们可以把理解为工程的编译规则.一个工程中源文件不计数,其按类型.功能.模块分别放在若干个目录中,makefile定义了一系列的规则来指定,那些 ...

  2. Makefile 13——理解make的解析行为

    make是以从上到下的顺序读入Makefile中的内容的.然而,处理Makefile中的语句却并非完全从上到下. 大体上,make处理一个Makefile分为两个阶段.第一个阶段包含: 1.make读 ...

  3. Makefile学习(一)----初步理解

    一.我对makefile的理解: 经过一段时间对makefile的学习,我理解的makefile就是将程序员手动编译源文件的过程用一个脚本执行,这对于小型项目来说,程序员手动执行和用makefile来 ...

  4. Makefile 编译生成多个可执行文件

    CC = gcc CXX = g++ CFLAGS = -O -DDBG -D_REENTRANT -Wall -DUSE_PTHREAD -pthread LDFLAGS = -ldl -lnsl ...

  5. (二)u-boot2013.01.01 for TQ210:《Makefile分析》

           当时写的时候看的是2012-10版本的,但是略对比了一遍和2013.01.01没什么改动,所以这不影响对2013.01.01版本的makefile的理解.本文比较侧重于语法句意的分析,框 ...

  6. 一个通用的Makefile (转)

    据http://bbs.chinaunix.net/thread-2300778-1-1.html的讨论,发现还是有很多人在问通用Makefile的问题,这里做一个总结.也作为以后的参考.       ...

  7. linux 下makefile

    linux下c编程中makefile是必须会的,我刚开始学,将我对makefile的理解记录下来. 通常我们在windows下编写c程序,有各种ide工具为我们执行makefile工作但在linux下 ...

  8. OpenWRT添加模块 Makefile和Config.in

    添加模块编译 在网上找了一下,很多关于编译Openwrt系统的资料,不过这些事情芯片厂商提供的开发包都已经办得妥妥了,但是没有找到系统介绍的资料,添加一个包的介绍有不多,其中有两个很有参考价值: ht ...

  9. Ubuntu下比较通用的makefile实例

    本文转自http://blog.chinaunix.net/uid-20608849-id-360294.html  笔者在写程序的时候会遇到这样的烦恼:一个项目中可能会有很多个应用程序,而新建一个应 ...

随机推荐

  1. git提交空目录的方法

    大家有时候希望从git仓库中拉取代码时目录就已经存在了,不需要再手动创建,但是git并不允许git提交空目录,应该怎么办呢?这篇文章通过可能遇到的两个情况来分别介绍如何解决,有需要的朋友们可以参考借鉴 ...

  2. error LNK2001: unresolved external symbol __imp___rt_sdiv

    这个问题搞了我 5 天(包括双休日), 我一定要记录下来. 问题描述 用 Visual Studio 2008 编译 WinCE 7 平台的应用程序,编译没问题,链接时出现了一堆 Link error ...

  3. Python--day65--模板语言之filter

    参考的原文链接:http://www.cnblogs.com/liwenzhou/p/7931828.html Filters(过滤器) 在Django的模板语言中,通过使用 过滤器 来改变变量的显示 ...

  4. 一次操作系统报错OutOfMemory Error的处理记录

    在启动公司内嵌的tomcat容器时出现报错, 如下: # There is insufficient memory for the Java Runtime Environment to contin ...

  5. phpstorm 有的单词下有下划线,怎么去掉?

    settings -> Editor -> Colors & Fonts -> General ->Errors and Warnings然后你会看见下面的示例代码.点 ...

  6. Laravel报错Call to undefined function Illuminate\Encryption\openssl_cipher_iv_length()

    nginx: 在phpstudy中运行Laravel一键安装包时报错:Call to undefined function Illuminate\Encryption\openssl_cipher_i ...

  7. 【js】vue 2.5.1 源码学习 (十一) 模板编译compileToFunctions渲染函数

    大体思路(九) 本节内容: 1. compileToFunctions定位 1. compileToFunctions定位 ==> createCompiler = createCompiler ...

  8. 2018-2-13-win10-UWP-ListView-模仿开始菜单

    title author date CreateTime categories win10 UWP ListView 模仿开始菜单 lindexi 2018-2-13 17:23:3 +0800 20 ...

  9. Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

    Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/c ...

  10. vue-learning:7-template-v-bind-with-class-and-style

    绑定元素样式的指令v-bind:class 和v-bind:style 在HTML元素结构中,class和style特性(attribute)是非常突出的,可以为元素添加样式属性(property). ...