当前位置: 技术问答>linux和unix
谁知道Make文件的编写?
来源: 互联网 发布时间:2014-12-11
本文导语: 你好!我买了一本关于Linux的书,里面较了一些关于C++的内容.我以会c++. 但是编辑C++的Makefile文件总是通不过.我以和书上的一摸一样.给一个最简单 的Makefile例子: CC=g++ hello:hello.o ${CC} -o hello hello.o hello.o:hello.cpp ${CC} -c...
你好!我买了一本关于Linux的书,里面较了一些关于C++的内容.我以会c++.
但是编辑C++的Makefile文件总是通不过.我以和书上的一摸一样.给一个最简单
的Makefile例子:
CC=g++
hello:hello.o
${CC} -o hello hello.o
hello.o:hello.cpp
${CC} -c hello.cpp
就是这个根书上一样.我在这个目录下打make.但是不能通过.
请指教!
但是编辑C++的Makefile文件总是通不过.我以和书上的一摸一样.给一个最简单
的Makefile例子:
CC=g++
hello:hello.o
${CC} -o hello hello.o
hello.o:hello.cpp
${CC} -c hello.cpp
就是这个根书上一样.我在这个目录下打make.但是不能通过.
请指教!
|
GNU make 指南
翻译: 哈少
译者按: 本文是一篇介绍 GNU Make 的文章,读完后读者应该基本掌握了 make 的用法。而 make 是所有想在 Unix (当然也包括 Linux )系统上编程的用户必须掌握的工具。如果你写的程序中没有用到 make ,则说明你写的程序只是个人的练习程序,不具有任何实用的价值。也许这么说有点 儿偏激,但 make 实在是应该用在任何稍具规模的程序中的。希望本文可以为中国的 Unix 编程初学者提供一点儿有用的资料。中国的 Linux 用户除了学会安装红帽子以外, 实在应该尝试写一些有用的程序。个人想法,大家参考。
--------------------------------------------------------------------------------
C-Scene 题目 #2
多文件项目和 GNU Make 工具
作者: 乔治富特 (Goerge Foot)
电子邮件: george.foot@merton.ox.ac.uk
Occupation: Student at Merton College, Oxford University, England
职业:学生,默尔顿学院,牛津城大学,英格兰
IRC匿名: gfoot
--------------------------------------------------------------------------------
拒绝承诺:作者对于任何因此而对任何事物造成的所有损害(你所拥有或不 拥有的实际的,抽象的,或者虚拟的)。所有的损坏都是你自己的责任,而 与我无关。
所有权: “多文件项目”部分属于作者的财产,版权归乔治富特1997年 五月至七月。其它部分属 CScene 财产,版权 CScene 1997年,保留所有 版权。本 CScene 文章的分发,部分或全部,应依照所有其它 CScene 的文章 的条件来处理。
0) 介绍
~~~~~~~~~~~~~~~
本文将首先介绍为什么要将你的C源代码分离成几个合理的独立档案,什么时 候需要分,怎么才能分的好。然后将会告诉你 GNU Make 怎样使你的编译和连 接步骤自动化。对于其它 Make 工具的用户来说,虽然在用其它类似工具时要 做适当的调整,本文的内容仍然是非常有用的。如果对你自己的编程工具有怀 疑,可以实际的试一试,但请先阅读用户手册。
1) 多文件项目
~~~~~~~~~~~~~~~~~~~~~~
1.1为什么使用它们?
首先,多文件项目的好处在那里呢?
它们看起来把事情弄的复杂无比。又要 header 文件,又要 extern 声明,而且如果需要查找一个文件,你要在更多的文件里搜索。
但其实我们有很有力的理由支持我们把一个项目分解成小块。当你改动一行代码,编译器需要全部重新编译来生成一个新的可执行文件。但如果你的项目是分开在几个小文件里,当你改动其中一个文件的时候,别的源文件的目标文件(object files)已经存在,所以没有什么原因去重新编译它们。你所需要做的只是重现编译被改动过的那个文件,然后重新连接所有的目标文件罢了。在大型的项目中,这意味着从很长的(几分钟到几小时)重新编译缩短为十几,二十几秒的简单调整。
只要通过基本的规划,将一个项目分解成多个小文件可使你更加容易的找到一段代码。很简单,你根据代码的作用把你的代码分解到不同的文件里。当你要看一段代码时,你可以准确的知道在那个文件中去寻找它。
从很多目标文件生成一个程序包 (Library)比从一个单一的大目标文件生成要好的多。当然实际上这是否真是一个优势则是由你所用的系统来决定的。但是当使用 gcc/ld (一个 GNU C 编译/连接器) 把一个程序包连接到一个程序时,在连接的过程中,它会尝试不去连接没有使用到的部分。但它每次只能从程序包中把一个完整的目标文件排除在外。因此如果你参考一个程序包中某一个目标档中任何一个符号的话,那么这个目标文件整个都会被连接进来。要是一个程序包被非常充分的分解了的话,那么经连接后,得到的可执行文件会比从一个大目标文件组成的程序包连接得到的文件小得多。
又因为你的程序是很模块化的,文件之间的共享部分被减到最少,那就有很多好处——可以很容易的追踪到臭虫,这些模块经常是可以用在其它的项目里的,同时别人也可以更容易的理解你的一段代码是干 什么的。当然此外还有许多别的好处……
1.2 何时分解你的项目
很明显,把任何东西都分解是不合理的。象“世界,你们好”这样的简单程序根本就不能分,因为实在也没什么可分的。把用于测试用的小程序分解也是没什么意思的。但一般来说,当分解项目有助于布局、发展和易读性的时候,我都会采取它。在大多数的情况下,这都是适用的。(所谓“世界,你们好”,既 'hello world' ,只是一个介绍一种编程语言时惯用的范例程序,它会在屏幕上显示一行 'hello world' 。是最简单的程序。)
如果你需要开发一个相当大的项目,在开始前,应该考虑一下你将如何实现它,并且生成几个文件(用适当的名字)来放你的代码。当然,在你的项目开发的过程中,你可以建立新的文件,但如果你这么做的话,说明你可能改变了当初的想法,你应该想想是否需要对整体结构也进行相应的调整。
对于中型的项目,你当然也可以采用上述技巧,但你也可以就那么开始输入你的代码,当你的码多到难以管理的时候再把它们分解成不同的档案。但以我的经验来说,开始时在脑子里形成一个大概的方案,并且尽量遵从它,或在开发过程中,随着程序的需要而修改,会使开发变得更加容易。
1.3 怎样分解项目
先说明,这完全是我个人的意见,你可以(也许你真的会?)用别的方式来做。这会触动到有关编码风格的问题,而大家从来就没有停止过在这个问题上的争论。在这里我只是给出我自己喜欢的做法(同时也给出这么做的原因):
i) 不要用一个 header 文件指向多个源码文件(例外:程序包 的 header 文件)。用一个 header定义一个源码文件的方式 会更有效,也更容易查寻。否则改变一个源文件的结构(并且 它的 header 文件)就必须重新编译好几个文件。
ii) 如果可以的话,完全可以用超过一个的 header 文件来指向同 一个源码文件。有时将不可公开调用的函数原型,类型定义 等等,从它们的C源码文件中分离出来是非常有用的。使用一 个 header 文件装公开符号,用另一个装私人符号意味着如果 你改变了这个源码文件的内部结构,你可以只是重新编译它而 不需要重新编译那些使用它的公开 header 文件的其它的源文 件。
iii) 不要在多个 header 文件中重复定义信息。 如果需要, 在其中一个 header 文件里 #include 另一个,但 是不要重复输入相同的 header 信息两次。原因是如果你以后改 变了这个信息,你只需要把它改变一次,不用搜索并改变另外一 个重复的信息。
iv) 在每一个源码文件里, #include 那些声明了源码文件中的符 号的所有 header 文件。这样一来,你在源码文件和 header 文件对某些函数做出的矛盾声明可以比较容易的被编译器发现。
1.4 对于常见错误的注释
a) 定义符 (Identifier) 在源码文件中的矛盾:在C里,变量和函数的缺 省状态是公用的。因此,任何C源码档案都可以引用存在于其它源 码档中的通用 (global) 函数和通用变量,既使这个档案没有那个变 量或函数的声明或原型。因此你必须保证在不同的两个档案里不能 用同一个符号名称,否则会有连接错误或者在编译时会有警告。
一种避免这种错误的方法是在公用的符号前加上跟其所在源文件有 关的前缀。比如:所有在 gfx.c 里的函数都加上前缀“gfx_”。如果 你很小心的分解你的程序,使用有意义的函数名称,并且不是过分 使用通用变量,当然这根本就不是问题。
要防止一个符号在它被定义的源文件以外被看到,可在它的定义前 加上关键字“static”。这对只在一个档案内部使用,其它档案都 都不会用到的简单函数是很有用的。
b) 多次定义的符号: header 档会被逐字的替换到你源文件里 #include 的位置的。因此,如果 header 档被 #include 到一个以上的源文件 里,这个 header 档中所有的定义就会出现在每一个有关的源码文件 里。这会使它们里的符号被定义一次以上,从而出现连接错误(见 上)。
解决方法: 不要在 header 档里定义变量。你只需要在 header 档里声明它们然后在适当的C源码文件(应该 #include 那个 header 档的那个)里定义它们(一次)。对于初学者来说,定义和声明是 很容易混淆的。声明的作用是告诉编译器其所声明的符号应该存在, 并且要有所指定的类型。但是,它并不会使编译器分配贮存空间。 而定义的做用是要求编译器分配贮存空间。当做一个声明而不是做 定义的时候,在声明前放一个关键字“extern”。
例如,我们有一个叫“counter”的变量,如果想让它成为公用的, 我们在一个源码程序(只在一个里面)的开始定义它:“int counter;”,再在相关的 header 档里声明它:“extern int counter;”。
函数原型里隐含着 extern 的意思,所以不需顾虑这个问题。
c) 重复定义,重复声明,矛盾类型:
请考虑如果在一个C源码文件中 #include 两个档 a.h 和 b.h, 而 a.h 又 #include 了 b.h 档(原因是 b.h 档定义了一些 a.h 需要的类型),会发生什么事呢?这时该C源码文件 #include 了 b.h 两次。因此每一个在 b.h 中的 #define 都发生了两次,每一 个声明发生了两次,等等。理论上,因为它们是完全一样的拷贝, 所以应该不会有什么问题,但在实际应用上,这是不符合C的语法 的,可能在编译时出现错误,或至少是警告。
解决的方法是要确定每一个 header 档在任一个源码文件中只被包 含了一次。我们一般是用预处理器来达到这个目的的。当我们进入 每一个 header 档时,我们为这个 header 档 #define 一个巨集 指令。只有在这个巨集指令没有被定义的前提下,我们才真正使用 该 header 档的主体。在实际应用上,我们只要简单的把下面一段 码放在每一个 header 档的开始部分:
#ifndef FILENAME_H
#define FILENAME_H
然后把下面一行码放在最后:
#endif
用 header 档的档名(大写的)代替上面的 FILENAME_H,用底线 代替档名中的点。有些人喜欢在 #endif 加上注释来提醒他们这个 #endif 指的是什么。例如:
#endif /* #ifndef FILENAME_H */
我个人没有这个习
翻译: 哈少
译者按: 本文是一篇介绍 GNU Make 的文章,读完后读者应该基本掌握了 make 的用法。而 make 是所有想在 Unix (当然也包括 Linux )系统上编程的用户必须掌握的工具。如果你写的程序中没有用到 make ,则说明你写的程序只是个人的练习程序,不具有任何实用的价值。也许这么说有点 儿偏激,但 make 实在是应该用在任何稍具规模的程序中的。希望本文可以为中国的 Unix 编程初学者提供一点儿有用的资料。中国的 Linux 用户除了学会安装红帽子以外, 实在应该尝试写一些有用的程序。个人想法,大家参考。
--------------------------------------------------------------------------------
C-Scene 题目 #2
多文件项目和 GNU Make 工具
作者: 乔治富特 (Goerge Foot)
电子邮件: george.foot@merton.ox.ac.uk
Occupation: Student at Merton College, Oxford University, England
职业:学生,默尔顿学院,牛津城大学,英格兰
IRC匿名: gfoot
--------------------------------------------------------------------------------
拒绝承诺:作者对于任何因此而对任何事物造成的所有损害(你所拥有或不 拥有的实际的,抽象的,或者虚拟的)。所有的损坏都是你自己的责任,而 与我无关。
所有权: “多文件项目”部分属于作者的财产,版权归乔治富特1997年 五月至七月。其它部分属 CScene 财产,版权 CScene 1997年,保留所有 版权。本 CScene 文章的分发,部分或全部,应依照所有其它 CScene 的文章 的条件来处理。
0) 介绍
~~~~~~~~~~~~~~~
本文将首先介绍为什么要将你的C源代码分离成几个合理的独立档案,什么时 候需要分,怎么才能分的好。然后将会告诉你 GNU Make 怎样使你的编译和连 接步骤自动化。对于其它 Make 工具的用户来说,虽然在用其它类似工具时要 做适当的调整,本文的内容仍然是非常有用的。如果对你自己的编程工具有怀 疑,可以实际的试一试,但请先阅读用户手册。
1) 多文件项目
~~~~~~~~~~~~~~~~~~~~~~
1.1为什么使用它们?
首先,多文件项目的好处在那里呢?
它们看起来把事情弄的复杂无比。又要 header 文件,又要 extern 声明,而且如果需要查找一个文件,你要在更多的文件里搜索。
但其实我们有很有力的理由支持我们把一个项目分解成小块。当你改动一行代码,编译器需要全部重新编译来生成一个新的可执行文件。但如果你的项目是分开在几个小文件里,当你改动其中一个文件的时候,别的源文件的目标文件(object files)已经存在,所以没有什么原因去重新编译它们。你所需要做的只是重现编译被改动过的那个文件,然后重新连接所有的目标文件罢了。在大型的项目中,这意味着从很长的(几分钟到几小时)重新编译缩短为十几,二十几秒的简单调整。
只要通过基本的规划,将一个项目分解成多个小文件可使你更加容易的找到一段代码。很简单,你根据代码的作用把你的代码分解到不同的文件里。当你要看一段代码时,你可以准确的知道在那个文件中去寻找它。
从很多目标文件生成一个程序包 (Library)比从一个单一的大目标文件生成要好的多。当然实际上这是否真是一个优势则是由你所用的系统来决定的。但是当使用 gcc/ld (一个 GNU C 编译/连接器) 把一个程序包连接到一个程序时,在连接的过程中,它会尝试不去连接没有使用到的部分。但它每次只能从程序包中把一个完整的目标文件排除在外。因此如果你参考一个程序包中某一个目标档中任何一个符号的话,那么这个目标文件整个都会被连接进来。要是一个程序包被非常充分的分解了的话,那么经连接后,得到的可执行文件会比从一个大目标文件组成的程序包连接得到的文件小得多。
又因为你的程序是很模块化的,文件之间的共享部分被减到最少,那就有很多好处——可以很容易的追踪到臭虫,这些模块经常是可以用在其它的项目里的,同时别人也可以更容易的理解你的一段代码是干 什么的。当然此外还有许多别的好处……
1.2 何时分解你的项目
很明显,把任何东西都分解是不合理的。象“世界,你们好”这样的简单程序根本就不能分,因为实在也没什么可分的。把用于测试用的小程序分解也是没什么意思的。但一般来说,当分解项目有助于布局、发展和易读性的时候,我都会采取它。在大多数的情况下,这都是适用的。(所谓“世界,你们好”,既 'hello world' ,只是一个介绍一种编程语言时惯用的范例程序,它会在屏幕上显示一行 'hello world' 。是最简单的程序。)
如果你需要开发一个相当大的项目,在开始前,应该考虑一下你将如何实现它,并且生成几个文件(用适当的名字)来放你的代码。当然,在你的项目开发的过程中,你可以建立新的文件,但如果你这么做的话,说明你可能改变了当初的想法,你应该想想是否需要对整体结构也进行相应的调整。
对于中型的项目,你当然也可以采用上述技巧,但你也可以就那么开始输入你的代码,当你的码多到难以管理的时候再把它们分解成不同的档案。但以我的经验来说,开始时在脑子里形成一个大概的方案,并且尽量遵从它,或在开发过程中,随着程序的需要而修改,会使开发变得更加容易。
1.3 怎样分解项目
先说明,这完全是我个人的意见,你可以(也许你真的会?)用别的方式来做。这会触动到有关编码风格的问题,而大家从来就没有停止过在这个问题上的争论。在这里我只是给出我自己喜欢的做法(同时也给出这么做的原因):
i) 不要用一个 header 文件指向多个源码文件(例外:程序包 的 header 文件)。用一个 header定义一个源码文件的方式 会更有效,也更容易查寻。否则改变一个源文件的结构(并且 它的 header 文件)就必须重新编译好几个文件。
ii) 如果可以的话,完全可以用超过一个的 header 文件来指向同 一个源码文件。有时将不可公开调用的函数原型,类型定义 等等,从它们的C源码文件中分离出来是非常有用的。使用一 个 header 文件装公开符号,用另一个装私人符号意味着如果 你改变了这个源码文件的内部结构,你可以只是重新编译它而 不需要重新编译那些使用它的公开 header 文件的其它的源文 件。
iii) 不要在多个 header 文件中重复定义信息。 如果需要, 在其中一个 header 文件里 #include 另一个,但 是不要重复输入相同的 header 信息两次。原因是如果你以后改 变了这个信息,你只需要把它改变一次,不用搜索并改变另外一 个重复的信息。
iv) 在每一个源码文件里, #include 那些声明了源码文件中的符 号的所有 header 文件。这样一来,你在源码文件和 header 文件对某些函数做出的矛盾声明可以比较容易的被编译器发现。
1.4 对于常见错误的注释
a) 定义符 (Identifier) 在源码文件中的矛盾:在C里,变量和函数的缺 省状态是公用的。因此,任何C源码档案都可以引用存在于其它源 码档中的通用 (global) 函数和通用变量,既使这个档案没有那个变 量或函数的声明或原型。因此你必须保证在不同的两个档案里不能 用同一个符号名称,否则会有连接错误或者在编译时会有警告。
一种避免这种错误的方法是在公用的符号前加上跟其所在源文件有 关的前缀。比如:所有在 gfx.c 里的函数都加上前缀“gfx_”。如果 你很小心的分解你的程序,使用有意义的函数名称,并且不是过分 使用通用变量,当然这根本就不是问题。
要防止一个符号在它被定义的源文件以外被看到,可在它的定义前 加上关键字“static”。这对只在一个档案内部使用,其它档案都 都不会用到的简单函数是很有用的。
b) 多次定义的符号: header 档会被逐字的替换到你源文件里 #include 的位置的。因此,如果 header 档被 #include 到一个以上的源文件 里,这个 header 档中所有的定义就会出现在每一个有关的源码文件 里。这会使它们里的符号被定义一次以上,从而出现连接错误(见 上)。
解决方法: 不要在 header 档里定义变量。你只需要在 header 档里声明它们然后在适当的C源码文件(应该 #include 那个 header 档的那个)里定义它们(一次)。对于初学者来说,定义和声明是 很容易混淆的。声明的作用是告诉编译器其所声明的符号应该存在, 并且要有所指定的类型。但是,它并不会使编译器分配贮存空间。 而定义的做用是要求编译器分配贮存空间。当做一个声明而不是做 定义的时候,在声明前放一个关键字“extern”。
例如,我们有一个叫“counter”的变量,如果想让它成为公用的, 我们在一个源码程序(只在一个里面)的开始定义它:“int counter;”,再在相关的 header 档里声明它:“extern int counter;”。
函数原型里隐含着 extern 的意思,所以不需顾虑这个问题。
c) 重复定义,重复声明,矛盾类型:
请考虑如果在一个C源码文件中 #include 两个档 a.h 和 b.h, 而 a.h 又 #include 了 b.h 档(原因是 b.h 档定义了一些 a.h 需要的类型),会发生什么事呢?这时该C源码文件 #include 了 b.h 两次。因此每一个在 b.h 中的 #define 都发生了两次,每一 个声明发生了两次,等等。理论上,因为它们是完全一样的拷贝, 所以应该不会有什么问题,但在实际应用上,这是不符合C的语法 的,可能在编译时出现错误,或至少是警告。
解决的方法是要确定每一个 header 档在任一个源码文件中只被包 含了一次。我们一般是用预处理器来达到这个目的的。当我们进入 每一个 header 档时,我们为这个 header 档 #define 一个巨集 指令。只有在这个巨集指令没有被定义的前提下,我们才真正使用 该 header 档的主体。在实际应用上,我们只要简单的把下面一段 码放在每一个 header 档的开始部分:
#ifndef FILENAME_H
#define FILENAME_H
然后把下面一行码放在最后:
#endif
用 header 档的档名(大写的)代替上面的 FILENAME_H,用底线 代替档名中的点。有些人喜欢在 #endif 加上注释来提醒他们这个 #endif 指的是什么。例如:
#endif /* #ifndef FILENAME_H */
我个人没有这个习
|
楼上怎么还说没有错 ?!
CC=g++
hello:hello.o
${CC} -o hello hello.o
hello.o:hello.cpp
${CC} -c hello.cpp
命令行要以tab开头,你命令行的tab没有打!
CC=g++
hello:hello.o
${CC} -o hello hello.o
hello.o:hello.cpp
${CC} -c hello.cpp
命令行要以tab开头,你命令行的tab没有打!
|
2.2 编写 make 规则 (Rules)
最明显的(也是最简单的)编写规则的方法是一个一个的查 看源码文件,把它们的目标文件做为目的,而C源码文件和被它 #include 的 header 档做为依靠文件。但是你也要把其它被这些 header 档 #include 的 header 档也列为依靠文件,还有那些被 包括的文件所包括的文件……然后你会发现要对越来越多的文件 进行管理,然后你的头发开始脱落,你的脾气开始变坏,你的脸 色变成菜色,你走在路上开始跟电线杆子碰撞,终于你捣毁你的 电脑显示器,停止编程。到低有没有些容易点儿的方法呢?
当然有!向编译器要!在编译每一个源码文件的时候,它实在应 该知道应该包括什么样的 header 档。使用 gcc 的时候,用 -M 开关,它会为每一个你给它的C文件输出一个规则,把目标文件 做为目的,而这个C文件和所有应该被 #include 的 header 文 件将做为依靠文件。注意这个规则会加入所有 header 文件,包 括被角括号(`
最明显的(也是最简单的)编写规则的方法是一个一个的查 看源码文件,把它们的目标文件做为目的,而C源码文件和被它 #include 的 header 档做为依靠文件。但是你也要把其它被这些 header 档 #include 的 header 档也列为依靠文件,还有那些被 包括的文件所包括的文件……然后你会发现要对越来越多的文件 进行管理,然后你的头发开始脱落,你的脾气开始变坏,你的脸 色变成菜色,你走在路上开始跟电线杆子碰撞,终于你捣毁你的 电脑显示器,停止编程。到低有没有些容易点儿的方法呢?
当然有!向编译器要!在编译每一个源码文件的时候,它实在应 该知道应该包括什么样的 header 档。使用 gcc 的时候,用 -M 开关,它会为每一个你给它的C文件输出一个规则,把目标文件 做为目的,而这个C文件和所有应该被 #include 的 header 文 件将做为依靠文件。注意这个规则会加入所有 header 文件,包 括被角括号(`