多目标

Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。但是如果多个目标的生成规则的执行命令是同一个,这会给我们带来很多的工作量。在makefile中可以使用$@。这个变量表示目前规则中的所有目标的集合。类似的变量还有$^,$<,$?

$@  表示目标文件
$^  表示所有的依赖文件
$<  表示第一个依赖文件
$?  表示比目标还要新的依赖文件列表

来看下面的例子:

假设目录里面有hello.c hi.c main.c makefile

按照makefile规则写的话应该如下:

main: main.o hello.o hi.o

gcc -o main main.o hello.o hi.o

main.o: main.c

cc -c main.c

hello.o: hello.c

cc -c hello.c

hi.o: hi.c

cc -c hi.c

clean:

rm *.o

rm main

改用符号进行替代

main: main.o hello.o hi.o

gcc -o $@ $^

main.o: main.c

gcc -c $<

hello.o: hello.c

gcc -c $<

hi.o: hi.c

gcc -c $<

clean:

rm *.o

rm main

静态规则

静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。语法如下:

<targets ...>: <target-pattern>: <prereq-patterns ...>

<commands>

targets 定义了一系列的目标文件, 可以有通配符。是目标的一个集合。target-parrtern 是指明了 targets 的模式,也就是的目标集模式。prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。

如果我们的<target-parrtern>定义成 “%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾) ,并为其加上[.c]这个结尾,形成的新集合

来看一个例子:

objects = foo.o bar.o

$(objects): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@

上面的例子中,指明了我们的目标从$object 中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object 集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量, “$<”表示所有的依赖目标集(也

就是“foo.c bar.c”) ,“$@”表示目标集(也就是“foo.o bar.o”) 。

于是,上面的规则展开后等价于下面的规则:

foo.o : foo.c

$(CC) -c $(CFLAGS) foo.c -o foo.o

bar.o : bar.c

$(CC) -c $(CFLAGS) bar.c -o bar.o

自动生成依赖性

在 Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的 main.c 中有一句“#include "defs.h"”,那么我们的依赖关系应该是:

main.o : main.c defs.h

但是,如果是一个比较大型的工程,你必需清楚哪些 C 文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用 C/C++编译的一个功能。大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令:

cc -M main.c

其输出是:

main.o : main.c defs.h

于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用 GNU 的 C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

gcc -M main.c 的输出是:

main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \

/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \

/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \

/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \

/usr/include/bits/sched.h /usr/include/libio.h \

/usr/include/_G_config.h /usr/include/wchar.h \

/usr/include/bits/wchar.h /usr/include/gconv.h \

/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \

/usr/include/bits/stdio_lim.h

gcc -MM main.c 的输出则是:

main.o: main.c defs.h

那么,编译器的这个功能如何与我们的 Makefile 联系在一起呢。因为这样一来,我们的 Makefile 也要根据这些源文件重新生成, 让 Makefile自已依赖于源文件?这个功能并不现实, 不过我们可以有其它手段来迂回地实现这一功能。GNU 组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的 Makefile 文件,[.d]文件中就存放对应[.c]文件的依赖关系。

于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让 make 自动更新或自成[.d]文件,并把其包含在我们的主 Makefile 中,这样,我们就可以自动化地生成每个文件的依赖关系了。这里,我们给出了一个模式规则来产生[.d]文件:

%.d: %.c

@set -e; rm -f $@; \

$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \

sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \

rm -f $@.$$$$

这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm -f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”, 也就是[.c]文件生成依赖文件, “$@”表示模式“%.d”文件,如果有一个 C 文件是 name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用

sed 命令做了一个替换,关于 sed 命令的用法请参看相关的使用文档。第四行就是删除临时文件。

make编译三的更多相关文章

  1. Android反编译(三)之重签名

    Android反编译(三) 之重签名 [目录] 1.原理 2.工具与准备工作 3.操作步骤 4.装X技巧 5.问题 1.原理 1).APK签名的要点 a.所有的应用程序都必须有数字证书 ,Androi ...

  2. TensorFlow Python2.7环境下的源码编译(三)编译

    一.源代码编译 这里要为仅支持 CPU 的 TensorFlow 构建一个 pip 软件包,需要调用以下命令: $ bazel build --cxxopt="-D_GLIBCXX_USE_ ...

  3. Spark-1.0.1 的make-distribution.sh编译、SBT编译、Maven编译 三种编译方法

    fesh个人实践,欢迎经验交流!本文Blog地址:http://www.cnblogs.com/fesh/p/3775343.html 本文编译方法所支持的hadoop环境是Hadoop-2.2.0, ...

  4. TensorFlow Python3.7环境下的源码编译(三)编译

    这里要为仅支持 CPU 的 TensorFlow 构建一个 pip 软件包,需要调用以下命令: $ bazel build --cxxopt="-D_GLIBCXX_USE_CXX11_AB ...

  5. java虚拟机规范(se8)——java虚拟机的编译(三)

    3.6 接受参数 如果n个参数传给一个实例的方法,按照约定,它们被接受并放在这个新方法创建的栈帧中的局部变量表里,在局部变量表中的序号从1到n.这些参数按照它们传递过来的顺序存放.例如: int ad ...

  6. Android studio 加速编译方法

    JRebel for Android 是一个Android Studio的插件,可以大大加速Android Studio的编译速度,对于小项目来说或许不明显:但是当项目达到一定的规模时,它对于Andr ...

  7. Linux C/C++的编译

    以前在Linux上面编译过C,但是没有编译过C++,今天用到了,就稍微学习了一下. 简单的介绍 linux 中最重要的编译工具是 GCC.GCC 是 GNU 的 C 和 C++ 编译器.实际上,GCC ...

  8. ubuntu 步步为营之uclinux编译和移植(完整版)

    本节主要包含(ubuntu10.04) 一,linux下的经常使用压缩解压缩命令 二,环境建立 三,内核编译 四,移植 一,linux下的经常使用压缩解压缩命令 在linux下常见的压缩文件格式有ta ...

  9. gcc 编译和链接

    1.现在对两个文件生成可执行文件 //thanks.c #include <stdio.h> int main(void) { printf("Hello World\n&quo ...

随机推荐

  1. Sublime text 3 搭建Python3 IDE

    起因:为了提高编码工作中的体验,Sublime Text:不仅具有华丽的界面,还支持插件扩展机制,用她来写代码,绝对是一种享受. Vim难于上手,Eclipse,VS 体积庞大,即便体积轻巧迅速启动的 ...

  2. oracle10g安装,卸载

    一.安装 1.因为oracle的特殊性,笔者选择通过虚拟机安装windows7旗舰版安装数据库,大家的系统假设是windows xp.windows 7,windows 8能够直接安装,windows ...

  3. vivado2016.2下系统自带DDR3 ip例程仿真运行

    背景:从ISE14.7迁移到vivado2016.2. xilinx的软件改的真是不一般的大.两个软件操作差距真是让人想骂人.由于项目需要,准备调试DDR3.对于新手来说,例化一个DDR3 ip.如果 ...

  4. python 学习方法

    依据本人的学习经验,我总结了下面十点和大家分享: 1)学好python的第一步.就是立即到www.python.org站点上下载一个python版本号.我建议刚開始学习的人,不要下载具有IDE功能的集 ...

  5. vue实现结账单基本方法

    <script> import axios from 'axios'; export default { name: 'Pos', mounted: function () { var o ...

  6. AutoLayout详解+手把手实战(转载)

    首先说一下这篇博客虽然是标记为原创,但是事实并非本人亲自写出来的,知识点和例子本人花了一天各处查 找和整理最终决定写一个汇总的详解,解去各位朋友到处盲目查找的必要,因为不是转载某一个人的内容,故此不标 ...

  7. (转)秒懂,Java 注解 (Annotation)你可以这样学

    转自:秒懂,Java 注解 (Annotation)你可以这样学 注解如同标签 回到博文开始的地方,之前某新闻客户端的评论有盖楼的习惯,于是 “乔布斯重新定义了手机.罗永浩重新定义了傻X” 就经常极为 ...

  8. 简单熟悉eclipse

  9. Android WebView ScrollBar设置

    WebView wv; wv.setVerticalScrollBarEnabled(false);  取消Vertical ScrollBar显示 wv.setHorizontalScrollBar ...

  10. ChemDraw 15支持哪些输入格式

    当我们想让我们的化学图形应用在试卷编辑.论文撰写.刊物出版等各个方面,这个时候往往都得使用ChemDraw 15.它可以与很多第三方应用灵活.本ChemDraw教程介绍新版ChemDraw Profe ...