如何在makefile中使用LDFLAGS

91

我是Linux操作系统的新手,我正在尝试使用makefile编译一个.c文件。需要链接数学库。我的makefile如下:

CC=gcc
CFLAGS=-Wall -lm

all:client

.PHONY: clean
clean:
    rm *~ *.o client
当我运行make时,我会得到以下错误:
"undefined reference to rint"

因此它无法链接数学库。

但是,当我使用显式编译时

gcc client.c -lm -o client

编译成功。

那么我该如何修改我的makefile以使它正常工作呢?我已经尝试添加LDFLAGS=-lm,但是我收到了相同的错误。

需要补充一点的是,当我运行make时,它会被扩展为

gcc -Wall -lm client.c -o client

请注意,当我在显式地在gcc命令末尾加上-lm时,它可以正常工作。

4个回答

73

你的链接器(ld)显然不喜欢make安排GCC参数的顺序,因此你需要稍微更改一下Makefile:

CC=gcc
CFLAGS=-Wall
LDFLAGS=-lm

.PHONY: all
all: client

.PHONY: clean
clean:
    $(RM) *~ *.o client

OBJECTS=client.o
client: $(OBJECTS)
    $(CC) $(CFLAGS) $(OBJECTS) -o client $(LDFLAGS)

在定义client目标的行中,根据需要更改$(LDFLAGS)的顺序。


83
LDLIBS 用于指定库,而 LDFLAGS 应该用于标志和搜索路径(-L)。 - falstaff
3
我也曾认为-lm应该在LDFLAGS中,但事实证明(请参见laindir的答案和https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html),它应该在LOADLIBES中,然后一切都可以直接使用(即无需定义显式规则)! - Emil Vatai
2
你的规则中在哪里使用源代码?它已经被定义了,但是没有被使用。我很难理解这个问题。 - MrPickles
@MrPickles:你说得对,SOURCE 实际上没有被使用,可以删除。 - Makkes
13
根据https://www.gnu.org/software/make/manual/make.html#index-LDLIBS的说法,LOADLIBES已被弃用,应该使用LDLIBS。 - Starfish
显示剩余2条评论

62

在更复杂的构建场景中,通常将编译分为阶段进行,首先进行编译和汇编(输出到目标文件),然后将目标文件链接成最终的可执行文件或库——这可以避免在源文件未更改时重新编译所有目标文件。这就是为什么将链接标志-lm包含在CFLAGS中不起作用(CFLAGS用于编译阶段)。

要链接库的约定是将它们放在LOADLIBESLDLIBS中(GNU make 包括两者,但你的情况可能有所不同):

LDLIBS=-lm

这样做可以让您继续使用内置规则,而无需编写自己的链接规则。对于其他制造商,应该有一个标志来输出内置规则(对于GNU make,这是-p)。如果您的make版本没有链接的内置规则(或者它没有-l指令的占位符),那么您需要编写自己的规则:

client.o: client.c
    $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

client: client.o
    $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) $(LDLIBS) -o $@

就加一条使用场景的评论,来确认我认为这个答案的核心。FWIW,在简单的foo.c -> foo隐式规则类型构建的情况下,使用LDLIBS而不是LDFLAGS绝对是一个改变游戏规则的因素(例如,设置目标default: foo,没有其他显式设置,如果所需的任何库都在LDLIBS中,则可以成功地从foo.c构建foo,但如果它们在LDFLAGS中,则不能)。 - lindes

9
当前被广泛接受的答案根据当前(2022年)make官方文档不正确,文档链接在此:https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html

LDFLAGS: 编译器在调用链接器“ld”时要使用的其它标志,如-L。 库(-lfoo)应添加到LDLIBS变量中。

LDLIBS: 编译器在调用连接器“ld”时要给出的库标志或名称。 LOADLIBES是LDLIBS的已废弃但仍受支持的替代品。 非库链接器标志,例如-L,应放在LDFLAGS变量中。

此外,一些先前回答中提到的LOADLIBES早已被弃用,不应再使用。

因此,基于上述定义,上面的示例应更正式地编写为:

CC=gcc
CFLAGS=-Wall
LDLIBS=-lm
LDFLAGS=-L/usr/local/include

.PHONY: all
all: client

.PHONY: clean
clean:
    $(RM) *~ *.o client

OBJECTS=client.o
client: $(OBJECTS)
    $(CC) $(CFLAGS) $(OBJECTS) $(LDFLAGS) $(LDLIBS)  -o client

1
似乎在旧版本的gcc中链接标志的顺序不是问题。例如,gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-16)与Centos-6.7一起使用链接器选项在输入文件之前,但Ubuntu 16.04附带的gcc gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413不允许这样做。
这不仅仅是gcc版本的问题,还与发行版有关。

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