我是C的相对初学者,我需要学习makefile的工作方式,而且我对C文件组合的工作方式有点困惑。假设我们有一个main.c,一个foo.c和一个bar.c.如何编写代码以便main.c识别其他文件中的函数?另外,在foo.c和bar.c中,是在main函数中编写的所有代码还是我们需要编写其他函数来完成我们需要它们做的事情?我已经阅读了关于如何编写makefile的教程,这在很大程度上是有意义的,但我对它的基本后勤仍然有点困惑。
答案 0 :(得分:27)
通常情况下,您将为头文件中的其他文件定义函数,然后可以将其包含在main.c中。例如,请考虑以下代码段:
main.c中:
#include "foo.h"
int main(int argc, char *argv[]) {
do_foo();
return 0;
}
foo.h中:
void do_foo();
foo.c的:
#include <stdio.h>
#include "foo.h"
void do_foo() {
printf("foo was done\n");
}
将会发生什么是将main.c转换为目标文件(main.o),并将foo.c转换为目标文件(foo.o)。然后链接器将这两个文件链接在一起,这就是main.c中的do_foo()
函数与foo.o中的函数“关联”的地方。
示例GCC命令: gcc -o myprogram main.c foo.c
示例makefile
myprogam: main.o foo.o
gcc -o myprogram main.o foo.o
main.o: main.c foo.h
gcc -c main.c
foo.o: foo.c foo.h
gcc -c foo.c
答案 1 :(得分:16)
如果您有一组文件,通常会做两件事:
源文件是独立的 - 您需要头文件才能提供有关给定模块中的功能的“信息”(声明),以便让任何其他模块使用它们。头文件不是自己编译的 - 它们是#include
d源文件的一部分。
请看下面make
处理命令的外观和处理方式。
Makefile是一组用于构建它们的目标和规则。 目标是“可以构建并生成给定文件的内容”。 (还存在“虚假”目标,这些目标不会生成文件而只是执行命令 - 常见的目标称为clean
以删除编译结果。)
每个目标有两个部分:
:
之后,逗号 - 分离), 考虑这个例子:
main: main.o module1.o module2.o
g++ main.o module1.o module2.o -o main
这意味着:“要构建文件main
,我需要先确保目标main.o
,module1.o
和module2.o
是最新的;然后我需要调用以下命令......“。
这也可以改写为:
main: main.o module1.o module2.o
gcc $^ -o $@
变量(以$
开头的所有内容都是变量)将按照您的预期扩展到依赖项列表和目标名称。
您可以定义自己的变量并按如下方式展开它们:
OBJS = main.o module1.o module2.o
main: $(OBJS)
# code goes here
您可以按如下方式编译单个翻译单元:
main.o: main.c
gcc -c $< -o $@
# note the -c option, which means: "compile, but don't link"
# $< will expand to the first source file
当main.c或其任何标头发生变化时,您可以添加标头依赖项来重建main.o:
main.o: main.c module1.h module2.h
gcc -c $< -o $@
为了不反复编写相同的命令,您可以定义一般规则并提供依赖项(如果需要):
%.o: %.c
gcc -c $< -o $@
main.o: main.c module1.h module2.h
module1.o: module1.c module1.h module2.h
生成dependencies automatically (see link)还有一些魔力。使用Make的一个缺点是它不能单独执行(就像某些构建系统那样 - 就像我更喜欢C / C ++的SCons)。
答案 2 :(得分:2)
foo.c
中需要从外部foo.c
调用的函数应该在foo.h
中包含原型。然后需要调用这些函数的外部文件#include "foo.h"
。 foo.c
和bar.c
如果与main()
属于同一计划,则不应具有main.c
功能。
Makefile定义目标。对于简单程序,您可以只使用一个目标来编译整个程序。更复杂(读取:更大)的程序可以有中间目标(如foo.o),这将使make
避免不必要的重新编译。 make
确定是否需要重新编译给定目标的方式是查看所有先决条件(冒号后的内容)的修改时间以及它们是否在目标的最后更改时间之后文件本身,它被重建。
这是一个非常简单的例子:
main.c中:
#include "foo.h"
int main()
{
fooprint(12);
return 0;
}
foo.c的:
#include "stdio.h"
#include "foo.h"
void fooprint(int val)
{
printf("A value: %d\n", val);
}
foo.h中:
void fooprint(int val);
生成文件:
main: main.c foo.o
gcc -o main main.c foo.o
foo.o: foo.c
gcc -c foo.c
然后你可以运行make main
并将foo.c
编译成foo.o
然后编译main.c并将其与foo.o链接。如果您修改main.c
,则只需重新编译main.c
并将其与已构建的foo.o
相关联。
答案 3 :(得分:2)
本质上,makefile包含以下形式的规则:
<this file> : <needs these files>
<and is created by this command>
您通常至少有一个高级别目标,如果其任何依赖项不存在,请查找将该文件作为目标的规则。在执行顶级命令之前,它会递归执行此操作,直到它解决了顶级目标的所有依赖关系(如果有一个 - 依赖关系和命令都是规则中的可选字段)
make文件可以基于模式具有“默认规则”,并且有各种文件匹配方案的内置宏以及用户定义宏和嵌套的makefile文件。
我已将上述规则表格简化为最常见的情况。事实上,命令根本不需要创建目标,它只是一旦存在依赖项中的所有文件就要执行的命令。此外,目标也不必是文件。通常,顶级目标是称为“全部”或类似的“虚拟”目标。
当然有许多微妙之处和细微差别,详见the manual(GNU特别指出,还有其他make工具)。
答案 4 :(得分:1)
Make与C程序的结构关系不大。所有make都会定义一个依赖树,并在发现依赖关系不完整时执行命令。我的说法,在makefile中:
foo.exe : foo.c bar.c baz.c
简单地说sez:foo.exe依赖于foo.c,bar.c和baz.c.这个 sotto vocce 使用make的默认规则集扩展为:
foo.exe : foo.obj bar.obj baz.obj
foo.obj : foo.c
bar.obj : bar.c
baz.obj : baz.c
从根目录开始简单地遍历依赖关系树(在本例中为foo.exe)。如果目标不存在或者其所依赖的对象之一比目标更新,则执行相关联的命令。使依赖正确。
请参阅O'Reilly的Managing Projects with Make,了解更多信息。
就问题的第二部分而言,答案只有两个字母:K and R。他们的The C Programming Language可以说是有史以来最好的计算机编程书之一。
答案 5 :(得分:1)
Makefile如何工作?
=&GT;当在终端上执行make命令时,它会在当前目录中查找名为makefile或Makefile的文件,并构造一个依赖树。
如果您有多个Makefile,那么您可以使用以下命令执行特定的:
make -f MyMakefile
=&GT;根据makefile中指定的make目标,检查该目标的依赖项文件是否存在。如果它们存在,通过比较文件时间戳,它们是否比目标本身更新。
Here our first and default target is “all” which looks for main.o and function.o file dependencies. Second and third target is main.o and function.o respectively which have dependencies of main.c and function.c respectively.
=&GT;在执行相应目标的命令之前,必须满足其依赖关系,当它们不满足时,这些依赖关系的目标在给定的make目标之前执行,以提供缺少的依赖关系。
=&GT;当目标是文件名时,make会比较目标文件及其依赖项文件的时间戳。如果依赖项文件比目标文件更新,则目标执行否则不执行。
In our case, when first target “all” start executing it looks for main.o file dependency, if its not met. Then it goes to second target main.o which check for its dependency main.c and compare time-stamp with it. If target found main.c dependency is updated, then target execute else not. Same process is follow for next target function.o.
=&GT;因此,它逐步向下检查依赖树中的源代码文件。通过这个过程,通过执行仅需要执行的命令,节省时间,基于哪些源文件(列为依赖项)已更新,并且具有比其目标更新的时间戳。
=&GT;现在,当目标不是文件名(我们称之为“特殊目标”)时,make显然无法比较时间戳来检查目标的依赖关系是否更新。因此,总是执行这样的目标。
In our Makefile, special targets are “all” and “clean”. As we discussed target “all” earlier, but we not discuss target clean. Target clean removes the all object files created during compilation and binary executable files according to command.
为了执行每个目标,make会在执行时打印操作。请注意,由于安全执行,每个命令都在单独的子shell环境中执行,因此它们无法更改可能影响其他目标执行的当前shell环境。例如,如果一个命令包含cd newdir,则仅对该行命令更改当前目录,对于下一行命令,当前目录将保持不变。
来源: - http://www.firmcodes.com/linux/write-first-makefile-c-source-code-linux-tutorial/