多目标

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. CKEditor的安装与基本使用(JSP)

    文章分类:Web前端 一.下载CKEditor 1. 直接下载地址.当前最新版本号为3.5: http://download.cksource.com/CKEditor/CKEditor/CKEdit ...

  2. Git使用笔记2

    工作必备: [更新master] git checkout master git pull git checkout zyb/FirstCommit git merge master //git re ...

  3. 使用apxs安装apache模块

    使用apxs安装apache模块                 ---by石锅拌饭 1.缘由 前几天迁移系统.发现配置了fastcgi的一个脚本下载文件总是提示类似Connection reset ...

  4. PLSQL配置数据库的方式

    1.直接连接的方式   2.修改客户端D:\app\Administrator\product\11.2.0\client_1\network\admin\tnsnames.ora文件的方式. ora ...

  5. mybatis执行多条sql语句

    1,mybatis执行多条sql语句,有以下几种思路, a,存储过程 b,修改jdbc的参数,允许执行多条语句,如下所示: sqlserver可以直接使用begin,end来执行多条语句, mysql ...

  6. Windows中搭建ftp服务器

    使用工具Quick Easy FTP Server Windows中搭建FTP服务器有什么用呢? 确实没有用,直到有一次,我在VM中安装了Linux虚拟机,但是文件怎么也上传不到这个虚拟机中. 然后用 ...

  7. mysql 京东

    DROP TABLE IF EXISTS `jd_admin`;CREATE TABLE `jd_admin` ( `id` int(10) unsigned NOT NULL AUTO_INCREM ...

  8. 把world转成html

    本来用php转的 效果不太理想 很不稳定 最后试了下java 效果不错 只记录java的方法好了 其实他们的原理都是一样的啊,都是用到了微软的com 首先是准备工作 下载(明确dll的版本是64位的还 ...

  9. 第一百九十八节,jQuery EasyUI,ProgressBar(进度条)组件

    jQuery EasyUI,ProgressBar(进度条)组件 学习要点: 1.加载方式 2.属性列表 3.事件列表 4.方法列表 本节课重点了解 EasyUI 中 ProgressBar(进度条) ...

  10. Servlet 处理日期

    使用 Servlet 的最重要的优势之一是,可以使用核心 Java 中的大多数可用的方法.本章将讲解 Java 提供的 java.util 包中的 Date 类,这个类封装了当前的日期和时间. Dat ...