使用make来编译C语言的Makefile设置

5

我正在学习Zed Shaw的《学习C语言的困难之路》课程,但在第2课中,作者在视频和书籍中只使用make ex1来编译他的C代码,而我需要运行gcc -Wall -g ex1.c -o ex1。这是我的Makefile,似乎第一部分应该让make命令工作来编译,但是"make"命令并没有编译它。

CFLAGs=-Wall -g

clean:
    rm -f ex1.exe

all:
    gcc -Wall -g ex1.c -o ex1.exe





CFLAGs,小写的s - Joseph Quinsey
@JosephQuinsey,如果在Makefile的其余部分中没有使用CFLAG[sS](无论大小写如何),那么它的原因是什么?在该Makefile中没有依赖信息,因此不会选择任何自动规则,并且不会使用CFLAGS变量。 - Luis Colorado
4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
4

默认目标是第一个目标,因此请更改它们的顺序,使all成为默认值。

有关目标的详细信息,请参阅目标文档

默认情况下,目标是makefile中的第一个目标(不包括以句点开头的目标)。因此,makefile通常是这样编写的,即第一个目标用于编译它们描述的整个程序或程序集。如果makefile中的第一条规则有多个目标,则只有规则中的第一个目标变成默认目标,而不是整个列表。您可以通过使用.DEFAULT_GOAL变量来管理默认目标的选择。

此外,ex1.exe 应该成为 all 的先决条件 (all: ex1.exe 而不仅仅是 all:),否则 make 无法检测它是否是最新的。另外,可以选择添加 .PHONY: all 来避免与一个名为 all 的文件混淆。 - anatolyg
@tadman,这不是我要找的,我的意思是只使用makemake all仅适用于ex1,而我想只使用make。 - Jerry D
1
我的错!这个答案大部分都是有效的!谢谢! - Jerry D
@anatolyg,不需要将ex1.exe作为任何东西的先决条件,因为只需要在命令行中使用all目标来构建ex1.exe。虽然我不太喜欢Makefile的编写方式,但只需输入make all即可生成可执行文件。它没有任何依赖信息。 - Luis Colorado

4

您的Makefile没有包含形式为规则的依赖关系信息。 规则是以左列(在第0列)开头并在目标(要构建的对象)和必备条件(提供给规则的对象)之间具有:分隔符的行。

Make会链接规则,因此在其前提条件构建之前不能构建任何目标,因此一个好的起点是:

ex1.exe: ex1.o
这意味着Makefile中的第一个目标ex1.exe依赖于编译后的对象ex1.o......但是如何实现呢?您可以在下面添加命令,但是要在命令行前插入一个制表符。
[tab char]gcc -Wall -g ex1.o -o ex1.exe
首先,看一下这个命令如何将 ex1.o 作为输入传递给 gcc 并通过选项 -o 生成文件 ex1.exe。 所有这些的意义是,只要你缺少 ex1.exe 或者它在前提条件(ex1.o)之前被修改(从文件的修改日期中可以看出),那么该命令就会被使用。 现在,我们如何编译对象文件呢?(在您的 Makefile 中,它是直接生成的)
ex1.o: ex1.c
    $(CC) $(CFLAGS) -c -o $@ $<

这行代码需要更多的解释(尽管如果您不使用它,make程序会自动提供)。它使用替换变量:$(CC)扩展为默认的C编译器(通常是cc,但您可以更改它)。$(CFLAGS)是您在Makefile顶部定义的变量,并扩展为通常用于编译程序的编译器选项。 $@由目标文件(在本例中为ex1.o文件)扩展,而$<则扩展为规则右部的左侧先决条件。 这个规则,将C文件编译成目标文件非常常见,由make自动生成,更多的规则可以在make高级用法中创建(由于所需空间的数量,我不会在此输入),只是说它可以写成这样:

.c.o:
    $(CC) $(CFLAGS) -c -o $@ $<
这意味着:通过使用下面的命令行,可以从具有相同名称但扩展名为.c的文件创建任何扩展名为.o的文件。 这使每个.o文件自动依赖于具有相同名称但扩展名为.c的文件,并预加载在make中。 因此,您最简单的Makefile可能是:
CFLAGS=-Wall -g
src=ex1.c
ex1.exe: $(src)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(src)

正如您所看到的,我使用了一个变量src来描述用于编译我的程序的C源文件。只有当您修改任何一个前提条件(现在只有一个,在ex1.c上,但可能会更多)时,依赖关系才会被发出。由于我正在进行编译和链接的同一编译,因此我需要传递编译标志和链接标志(此处未使用,您可以删除对$(LDFLAGS)的引用)。实际上,您可以这样写:

ex1.exe: ex1.c
     cc -g -Wall -o ex1.exe ex1.c
clean:
     rm -f ex1.exe
所以,默认操作是创建 ex1.exe,它依赖于 ex1.c(所以只有当 ex1.c 被修改时,ex1.exe 才会被重新编译)。要编译它,只需执行以下命令:
make ex1.exe

或者

make

这将使用Makefile的默认第一规则。

clean是一个虚拟目标(没有叫做clean的文件需要生成),所以当您使用它时,它将执行规则中定义的命令,从而删除指定的文件或目录。

make clean

它的相关命令总是被执行。它让make删除您的目标文件(因为它会删除所有由make生成的文件),所以下次运行make时,它将通过进行必要的编译来重新创建所有文件。

常见用法

对于简单的程序,例如几个源代码.c文件和一些.h文件,每个文件都进行单独编译(使用编译中的-c选项)和链接,我通常使用此方法(定义每个目标程序所需的对象文件不同变量中)。

# makefile for programs foo and bar.

targets = foo bar
TOCLEAN = $(TARGETS)  # this variable stores what should be erased on clean target.  Here we store the programs to erase.

foo_objs = a.o b.o c.o
TOCLEAN += $(foo_objs)  # here, we add the objects of foo

foo: $(foo_objs)
    $(CC) $(LDFLAGS) -o $@ $(foo_objs)

bar_objs = d.o e.o f.o
bar_libs = -lm -lintl -lX
TOCLEAN += $(bar_objs)  # here we add the objects of bar

bar: $(bar_objs)
    $(CC) $(LDFLAGS) -o $@ $(bar_objs) $(bar_libs)

clean:
    rm -f $(TOCLEAN)

a.o b.o c.o: a.h b.h c.h globals.h
b.o c.o d.o e.o f.o: my_defs.h

正如您所见,对头文件的依赖关系是针对编译后的目标文件而不是.c源文件...源文件构建了依赖关系,但在修改包含的文件时必须重新创建目标文件,而不是包含.c文件。


1
你为什么要使用 $(CC) $(LDFLAGS) 而不是标准的 $(LINK.o) 呢?(这可以防止针对目标的 LINK.o = LINK.cc 用于链接 C++ 对象,例如)。同样,使用 $(RM) 而不是 rm -f 是标准做法,尽管需要覆盖它的情况较少。 - Toby Speight
1
我也会使用特定于目标的 bar: LDLIBS += -lm -lintl -lX,但这可能只是个人偏好? - Toby Speight
把编译器调用链接阶段是有充分的理由的。它会包含支持该语言所需的运行时模块。如果直接调用链接器,你就必须自己提供这些文件。同样适用于标准 C 库。 - Luis Colorado
使用makeLDLIBS全局变量不允许您为每个可执行文件指定不同的库。您可以将其用于通用项目库,然后为每个可执行文件设置特定的程序库集。这与编译标志一样重要,因为在make中通常不会默认包含链接的自动规则,但在编译时很重要。正如您所看到的,两个程序使用不同的对象和库。 - Luis Colorado
确实如此;这就是为什么我编写了一个特定于目标LDLIBS - Toby Speight
是的,但那是GNU make的扩展功能。正如你所看到的,我最注重不使用GNU make的扩展功能,并使用非首选的旧符号“ .c.o:”目标。 - Luis Colorado

1
CFLAGS = -Wall -g

all: ex1

clean:
    $(RM) ex1

.PHONY: all clean

我在开头修正了我的CFLAGS定义,并在allclean的末尾添加了.PHONY,这样make就知道不要为这些文件生成目标。


请注意,如果您将ex1作为all的前提条件,则在使用rm -f命令时,请使用相同的ex1作为参数。 - Luis Colorado

0

对于一个非常简单的 Makefile,我会这样做:

qsorttest: qsorttest.c 
     gcc -Wall -g qsorttest.c -o qsorttest -lm

clean:
     rm -f qsorttest
“qsorttest.c”是我的源文件名称,我创建的可执行文件名为“qsorttest”。请确保“program name: source file”和“clean:”下面的行已经缩进,并且没有使用空格键移动。我曾经为此苦恼了一段时间,直到通过谷歌找到解决方法。 我将其保存为“Makefile”,与程序的其他文件放在同一个目录下,这样每次都可以正常工作。如果需要,我只需更改源文件和程序名称,并添加额外的参数以链接库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,