target := exe

source_code = hello.c

OBJS = $(source_code:.c=.o)

$(target):$(OBJS)
gcc $^ -o $@ clean:
$(RM) $(target) $(OBJS)

一上来直接展示一份makefile代码。

功能: 从功能角度来说,这份makefile代码可以编译出对应的目标文件,并且顺利执行。

思考: 这其实是一份质量不合格的makefile代码。请仔细观察。

。。。。 什么 ?  你看着这个makefile却一点都不觉得奇怪吗?(好吧,难道你平时写makefile都是这样写的?  )

下面我们通过实验来讲解。

做点简单修改,还是上面这份makefile,只修改编译器, 更换为交叉编译器来试试,贴代码:

target := exe

source_code = hello.c

OBJS = $(source_code:.c=.o)

$(target):$(OBJS)
/usr/external-toolchain/bin/arm-linux-gnueabi-gcc $^ -o $@ clean:
$(RM) $(target) $(OBJS)

功能:请问现在更换为交叉编译器,还能编译出正确的目标文件吗?

编译观察:

令人吃惊的答案:编译报错!

解答:

 当前截图make报错显示:不是有效格式的.o文件。 
这里的玄机是makefile的一个暗黑操作:隐式规则。 

做一下简单修改即可:使用预定义的CC 这会改变隐式规则的行为。修改为如下图所示代码:

target := exe

CC := /usr/external-toolchain/bin/arm-linux-gnueabi-gcc

source_code = hello.c

OBJS = $(source_code:.c=.o)

$(target):$(OBJS)
$(CC) $^ -o $@
clean:
$(RM) $(target) $(OBJS)

这样就能顺利编译出目标文件了。

虽然可以通过增加CC变量进行简单修改,达到顺利生成目标文件的目的,但是这样的makefile仍旧不被推荐使用,因为使用了有可能产生隐式规则行为的代码

这里通过使用CC变量能够顺利编译出目标文件, 本质还是使用了隐式规则,即makefile在执行make对应的规则内的命令时,发现却没有有效的汇编文件:

于是通过默认的gcc编译器尝试将本地的XX.c文件去编译为XX.o文件。

如果本地没有XX.c文件,有一个XX.p或者XX.cpp文件,make的这种隐式规则也会去将其编译为汇编文件。

如果XX.p和XX.cpp同时存在,是选择XX.p还是XX.cpp,这由make的隐式规则决定。

可以通过 make -p|grep "文件格式" 查看隐式规则的这部分相关内容:

 上面是正面讲解makefile隐式规则, 下面我侧面证明一下makefile隐式规则的存在。 
还是使用这份代码来分析。注意:我们使用的编译器是交叉编译器哦!
 
 虽然make这个makefile时报错,但还是生成了hello.o(我自己先写一个hello.c放在与makefile的同一路径下), 我们来打印一下这个hello.o的部分二进制:
 
 完成了上面的步骤,我们直接手动使用gcc编译出一个汇编文件: 
 
 观察总结:
我们这份makefile内根本就没有出现使用gcc的代码啊,我们使用的是交叉编译器啊!,竟然出现了gcc编译出来的汇编文件! 这说明什么?
这说明当前的这份makefile在make后发生了隐式规则的执行!
makefile的隐式规则是大型makefile工程应注意极力避免的,这很可能导致无法解决的bug!大家需要了解一下makefile的隐式规则,避免写下的makefile出现隐式规则的行为
 
这也是本博客最开头的例子使用gcc没暴露出问题的原因,换成交叉编译器去尝试后,就会发现问题所在。
 
总结:
 
重点是要先生成有效的依赖。本例的最佳改进是再增加一条包含%.o:%.c 的规则去得到有效的汇编文件。参考我之前的makefile博客,那里有正规的makefile的写法。

.

makefile的隐式规则的更多相关文章

  1. makefile如果没有符合的显式规则将会使用隐式规则

    举例: 当前目录下有个Makefile和jello.c文件,其中有这样的规则jello.o:%.c %.h Makefile (静态模式规则),表明的含义为:要生成的jello.o目标依赖jello. ...

  2. 第十五篇 make中的隐式规则概述

      前面我们讲到了makefile的依赖拆分的知识,现在可以引申出这样一个问题,如果同一个目标的不同命令拆分的写到不同地方会发生什么?下面我们给出程序和执行结果:   可见后面的命令会覆盖前面的命令, ...

  3. 第15课 - make的隐式规则(上)

    第15课 - make的隐式规则(上) 1. 问题 如果把同一个目标的命令拆分的写到不同地方,会发生什么? 执行make all 这个实验表明了:如果同一个目标的命令拆分的写到不同地方,那么 make ...

  4. 第16课 - make的隐式规则(下)

    第16课 - make的隐式规则(下) 1

  5. 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则未经解析。

    SELECT CONVERT(VARCHAR(100), 列名) FROM Table 提示错误: 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则 ...

  6. Android学习笔记_17_Intent匹配规则(隐式意图)

    Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供了Intent (意图) ,Intent提供了一种通用的消息系统,它允许在你的应用程序与其它的应用程序间传递Intent来执行 ...

  7. MySQL隐式转化整理

    MySQL隐式转化整理 前几天在微博上看到一篇文章:价值百万的 MySQL 的隐式类型转换感觉写的很不错,再加上自己之前也对MySQL的隐式转化这边并不是很清楚,所以就顺势整理了一下.希望对大家有所帮 ...

  8. 利用UICollectionViewFlowLayout的隐式动画实现UICollectionView的layout的动画调整(外加放大指定cell效果)

    前几天在gitHub看到个不错的效果,就是DaiExpandCollectionView,效果如图:   所以赶紧下下来源码看看他怎么实现的,打开源码看了半天,发现他没写什么关于动画的代码啊... 经 ...

  9. 深入理解javascript原型和闭包(4)——隐式原型

    注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看<javascript高级程序设计(第三版)>第6章:面向对象的程序设计. 上节已经提到 ...

随机推荐

  1. Mysql启动后停止的解决方法

    安装mysql后,服务无法正常启动,报错如下: 解法方法: 1           以管理员身份运行命令提示符 2           用命令进行mysql安装目录的bin目录: cd C:\Prog ...

  2. 【java学习笔记】LongAdder

    目录 1.背景 2.LongAdder 3.Striped64内部结构 4.LongAdder的add方法解析 5.Striped64的longAccumulate方法解析 6.总结 LongAdde ...

  3. 利用Decorator和SourceMap优化JavaScript错误堆栈

    配合源码阅读体验更佳. 最近收到用户吐槽 @cloudbase/js-sdk(云开发Cloudbase的JavaScript SDK)的报错信息不够清晰,比如下面这条报错: 这属于业务型报错,对于熟悉 ...

  4. Unity 打AssetBundle和加载方案

    一.如何组织assetBundle: unity5以前,打包需要自己去找依赖,然后需要按照拓扑图顺序压入AB栈,这样在最后打AB时才能有效利用依赖(栈内已有的AB才能作为依赖). unity5.x后, ...

  5. log4j日志文件输出保存

    og4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=app.log log4j.appe ...

  6. 剑指 Offer 43. 1~n整数中1出现的次数

    题目描述 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次. 示例 1: 输入:n = 12 ...

  7. Ubuntu 16.04 安装Python 3.6

    1.配置软件仓库,因为python 3.6 新版没有发布到ubuntu的正式仓库中,咱们通过第3方仓库来做.在命令行中输入: sudo add-apt-repository ppa:jonathonf ...

  8. Java使用Filter用户权限控制

    package com.mvc.test; import javax.servlet.ServletException; import javax.servlet.annotation.WebServ ...

  9. Vue 事件的高级使用方法

    Vue 事件的高级使用方法 事件方法 在Vue中提供了4中事件监听方法,分别是: $on(event: string | Array, fn) $emit(event: string) $once(e ...

  10. 判断语句 、 while循环 、 for循环

    判断语句 语法结构 if 条件1: 如果条件1为真,执行语句块 elif 条件2: 如果条件2为真,执行语句块 elif 条件3: 如果条件2为真,执行语句块 elif 条件n: 如果条件n为真,执行 ...