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. 力扣Leetcode 572. 另一个树的子树

    另一个树的子树 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 s 的一个节点和这个节点的所有子孙.s 也可以看做它自身的一棵子树. 示例 ...

  2. 深入学习redis 的线程模型

    一.redis 的线程模型 redis 内部使用文件事件处理器 file event handler,它是单线程的,所以redis才叫做单线程模型.它采用IO多路复用机制同时监听多个 socket,将 ...

  3. Flink自定义Sink

    Flink自定义Sink Flink 自定义Sink,把socket数据流数据转换成对象写入到mysql存储. #创建Student类 public class Student { private i ...

  4. hadoop平台环境搭建

    centos01 配置静态ip vim /etc/sysconfig/network-scripts/ifcfg-eth0 见图1 修改主机名 vim /etc/sysconfig/network 见 ...

  5. iOS打电话功能的简单实现

    小功能简介 iOS中的很多小功能都是非常简单的,几行代码就搞定了,比如打电话.打开网址.发邮件.发短信等 打电话-方法3 创建一个UIWebView来加载URL,拨完后能自动回到原应用 if (_we ...

  6. 深入了解Netty【八】TCP拆包、粘包和解决方案

    1.TCP协议传输过程 TCP协议是面向流的协议,是流式的,没有业务上的分段,只会根据当前套接字缓冲区的情况进行拆包或者粘包: 发送端的字节流都会先传入缓冲区,再通过网络传入到接收端的缓冲区中,最终由 ...

  7. OpenResty 作者章亦春访谈实录

    [软件简介] OpenResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器.它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项. 通过众多 ...

  8. C016:字符串倒置

    代码: #include "stdafx.h" #include <string.h> int _tmain(int argc, _TCHAR* argv[]) { c ...

  9. 深入理解Java中的装箱与拆箱

    一.Java数据类型 1.在说装箱与拆箱之前,先说一下Java的基本数据类型,Java从数据类型上可以划分为值类型与引用类型,值类型是四类八种,分别是: 整数型:byte̵,short̵,int̵,l ...

  10. Webpack 打包优化之体积篇

    谈及如今欣欣向荣的前端圈,不仅有各类框架百花齐放,如Vue, React, Angular等等,就打包工具而言,发展也是如火如荼,百家争鸣:从早期的王者Browserify, Grunt,到后来赢得宝 ...