![尝试运行 C 程序时 Makefile 失败](https://linux22.com/image/141352/%E5%B0%9D%E8%AF%95%E8%BF%90%E8%A1%8C%20C%20%E7%A8%8B%E5%BA%8F%E6%97%B6%20Makefile%20%E5%A4%B1%E8%B4%A5%20.png)
我有很多制作 Makefile 文件的任务,我有这些文件:
https://raw.githubusercontent.com/systems-cs-pub-ro/uso/master/tema1/boo.c
https://github.com/systems-cs-pub-ro/uso/raw/master/tema1/light.o
https://raw.githubusercontent.com/systems-cs-pub-ro/uso/master/tema1/sound.c
https://raw.githubusercontent.com/systems-cs-pub-ro/uso/master/tema1/sound.h
我必须创建一个 Makefile:“生成一个可执行文件exec
,light.o
并在先前实现的sound.o
编译时创建sound.c
。在接收任何命令时make
,或者make build
它必须生成文件。如果有必要,exec
在 Makefile 中添加一条符合整个程序的规则,然后run
运行它,添加一条clean
删除您创建的目标文件的规则。还要添加一条在编译后templates
创建中间文件(boo.s
, )的规则。boo.o
boo.c
这是我的 makefile
build: light.o sound.o
gcc light.o sound.o -o exec
sound.o: sound.c
gcc -c sound.c
templates: boo.c
gcc -c boo.c
gcc -S boo.c
run: exec
./exec
clean:
rm -f sound.o boo.o boo.s exec
但它告诉我,我没有创建“正确的”可执行文件,我做错了什么?我认为我应该添加boo.o
到 exec 文件中,但是当我尝试这样做时,我得到:boo.c multiple definition of main light.o:light.c:first defined here
。
答案1
(这听起来像是家庭作业。)
首先是错误信息:
boo.c multiple definition of main
light.o:light.c:first defined here.
它表明您尝试链接在一起的文件具有名为 的函数的多个副本main()
,这显然是不正确的。
您正在尝试重用light.o
,但只有当它具有除 之外的功能时,这才真正有效main()
。我们就这样称呼它吧light()
。您还需要一个.h
文件#include
d in boo.c
,以便编译器知道函数的属性(基本上是参数的类型和返回值的类型,如果有的话),而light()
无需查看其源代码。
在我看来,你的文件组合起来与作业中预期的有些不同。您的教授可能创建了一系列相互关联的作业:如果您在该系列的前一个作业中采取捷径,则重用前一个作业中的代码的后续作业将无法正常工作。
您的教授正在尝试教您如何安排代码以使其可重用,以及如果您不这样做会发生什么。
Makefile规则的基本形式是:
<name of target>: <names of files the creation process depends on>
<command(s) to create the target file>
文件的创建.c
不仅取决于文件本身,还取决于.h
它包含的文件。一般来说,您可以假设任何系统包含文件(您包含的文件不会#include <filename>
对您进行更改,因此您不需要将它们添加到 Makefile 规则中。但是.h
属于项目一部分的任何文件也应该包含在内。
还可能存在实际上不是文件的“虚拟目标”。它们允许您做类似的事情make clean
。您还可以使用虚拟目标作为另一个目标的依赖项:由于虚拟目标没有与其关联的实际文件,因此每次将虚拟目标列为依赖项时都会执行“创建”虚拟目标的命令为了其他东西并且需要建造其他东西。
此外,可能存在不包含任何命令的 Makefile 规则,但列出一个或多个 Makefile 目标作为其依赖项。如果只有一个依赖项,则不带命令的目标将充当作为依赖项列出的目标的另一个名称。如果它具有多个依赖项,那么无命令目标就成为使用单个命令构建多个 Makefile 目标的一种方式。
在我看来,你的教授似乎想让build:
目标成为一个没有自己命令的目标。它应该只取决于两个目标:
- 构建实际可执行文件的单独目标
- 目标
templates
light.o
你可能想知道我怎么在没有看到它的源代码的情况下知道它的内容呢?简单 - 我下载了你的light.o
然后使用了这个命令:
$ readelf -s light.o
Symbol table '.symtab' contains 12 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS light.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 5
6: 00000000 0 SECTION LOCAL DEFAULT 7
7: 00000000 0 SECTION LOCAL DEFAULT 8
8: 00000000 0 SECTION LOCAL DEFAULT 6
9: 00000000 51 FUNC GLOBAL DEFAULT 1 main
10: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts
11: 00000000 0 NOTYPE GLOBAL DEFAULT UND sound
这告诉我light.o
来自一个名为 的源文件light.c
,定义了一个名为 的函数main()
并使用名为puts()
和的函数sound()
,这些函数此处未定义。puts()
来自标准库,但sound()
需要在与此文件链接在一起的其他文件中定义以形成完整的可执行文件。