Makefile 执行意外行为

Makefile executed unexpected behavior

我使用以下 Makefile 编译 C++ 文件,但发生了一些意外行为。环境是 MacOS X Mojave。 Makefile 如下:

CC=gcc
CXX=g++
CXXFLAGS=-std=c++11
RM=rm -f

all: clean sort_test ds_test

sort_test: data_structure sort .sort_test.o .sort_test

.sort_test: sort_test.o sort.o ds.o
        $(CXX) $(CXXFLAGS) -o sort_test sort_test.o sort.o ds.o

.sort_test.o: sort_test.cpp ../include/io.hpp
        $(CXX) $(CXXFLAGS) -c -o sort_test.o sort_test.cpp

sort: ../include/sort.hpp ../include/data_structure.hpp ../src/sort.cpp
        $(CXX) $(CXXFLAGS) -c -o sort.o ../src/sort.cpp

data_structure: ../include/data_structure.hpp ../src/data_structure.cpp ../include/io.hpp
        $(CXX) $(CXXFLAGS) -c -o ds.o ../src/data_structure.cpp

ds_test: data_structure .ds_test.o .ds_test

.ds_test: ds.o ds_test.o
        $(CXX) $(CXXFLAGS) -o ds_test ds.o ds_test.o

.ds_test.o: ds_test.cpp ../include/io.hpp ../include/data_structure.hpp
        $(CXX) $(CXXFLAGS) -c -o ds_test.o ds_test.cpp

clean:
        $(RM) *.o sort_test ds_test

当我 运行 "make ds_test" 在同一目录下时,发生了一些奇怪的事情:

g++ -std=c++11   -c -o ds_test.o ds_test.cpp
g++ -std=c++11 -c -o ds.o ../src/data_structure.cpp
g++ -std=c++11 -c -o ds_test.o ds_test.cpp
g++ -std=c++11 -o ds_test ds.o ds_test.o
gcc   ds_test.o data_structure .ds_test.o .ds_test   -o ds_test
clang: error: no such file or directory: 'data_structure'
clang: error: no such file or directory: '.ds_test.o'
clang: error: no such file or directory: '.ds_test'
make: *** [ds_test] Error 1

此输出中的第一行和第五行绝对不是命令 "make ds_test",因为它应该只调用 "data_structure"、“.ds_test.o”和“.ds_test ”。 任何人,请解释为什么会发生这些额外的意外行为以及如何避免?谢谢!

你的 Makefile 有点奇怪。基本的 make 规则是这样的:

file-to-build: files-it-depends-on
    command-to-build

当你写下这样的东西时:

.ds_test: ds.o ds_test.o
        $(CXX) $(CXXFLAGS) -o ds_test ds.o ds_test.o

目标不是配方生成的文件。此外,您在没有适当扩展的情况下重命名事物(data_structureds.o)。最后,您对同一事物使用不同的名称(同样是 data_structureds.o)。如果您是从 C++ 和 make 开始,您可能应该避免所有这些花哨的东西。

你的 Makefile 失败的主要原因是因为 make 正在尝试构建一个名为 ds_test 的文件(这是你在键入 make ds_test 时所要求的)。 make 知道很多构建文件的方法。在这种特定情况下,它使用其默认规则,即使用 $(CC) 到 link 一起 ds_test.o 和所有其他文件 ds_test 取决于,即 data_structure , .ds_test.o.ds_test.

如果您是新手,我建议您首先坚持其最基本的原则。类似于:

CC       := gcc
CXX      := g++
CXXFLAGS := -std=c++11
RM       := rm -f

.PHONY: all clean

all: clean sort_test ds_test

sort_test: sort_test.o sort.o data_structure.o
    $(CXX) $(CXXFLAGS) -o $@ $^

sort_test.o: sort_test.cpp ../include/io.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

sort.o: ../src/sort.cpp ../include/sort.hpp ../include/data_structure.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

data_structure.o: ../src/data_structure.cpp ../include/data_structure.hpp ../include/io.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

ds_test: data_structure.o ds_test.o
    $(CXX) $(CXXFLAGS) -o $@ $^

ds_test.o: ds_test.cpp ../include/io.hpp ../include/data_structure.hpp
    $(CXX) $(CXXFLAGS) -c -o $@ $<

clean:
    $(RM) *.o sort_test ds_test

解释:

  • $@$<$^make automatic variables,分别展开为目标、第一个前提和所有前提的列表。它们不仅方便,而且比在目标、先决条件和配方中重新键入相同的文件名更不容易出错。
  • .PHONY 是一个 special target,你可以用它发出信号,让哪些目标不是真实文件。

EDIT:将 LDLIBS 添加到 link C++ 对象文件,其中包含 gcc 和备用 linking 规则。

注意:由于 make 非常聪明,默认情况下知道如何编译和 link C++ 文件,您可以简化所有这些。特别是如果您还使用 VPATH make variable:

CC       := gcc
CXX      := g++
CXXFLAGS := -std=c++11
LDLIBS   := -lstdc++
RM       := rm -f
EXEC     := sort_test ds_test

.PHONY: all clean

all: clean sort_test ds_test

VPATH    := ../src:../include

sort_test.o: io.hpp
sort.o: sort.hpp data_structure.hpp
data_structure.o: data_structure.hpp io.hpp
ds_test.o: io.hpp data_structure.hpp

sort_test: sort_test.o sort.o data_structure.o
ds_test: data_structure.o ds_test.o

clean:
    $(RM) *.o $(EXEC)

注意:由于 make 将使用 gcc 到 link,我们必须将 -lstdc++ 添加到 linker 标志 (LDLIBS)。另一种选择是指定 linking 规则而不是让 make 使用默认值:

$(EXEC):
    $(CXX) $(CXXFLAGS) -o $@ $^

请注意,在最后一种情况下,指定先决条件的规则和指定配方的规则是不同的。