Makefile怪异的变量行为

时间:2018-09-29 14:39:41

标签: makefile

在下面的Makefile中,我根据目标规则是否为“ test”来修改$(SRC)的内容。该规则应该用于单元测试。
我的对象是使用隐式规则构建的
OBJ = $(SRC:.cpp=.o)
但是在构建对象时,即使SRC变量发生更改,它也始终使用其默认值。这是Makefile:

CC =              g++

SRC_DIR =         src
SRC_TEST_DIR =    tests/src

INC_DIR =         include/
test: INC_DIR +=  tests/include/

SRC_MAIN =        $(SRC_DIR)/main.cpp
test: SRC_MAIN =  $(SRC_TEST_DIR)/main.cpp

SRC =             $(SRC_MAIN) \             # <=== The value that changes in SRC
                  $(SRC_DIR)/minicalc.cpp \
                  $(SRC_DIR)/io.cpp

OBJ =             $(SRC:.cpp=.o)

CPPFLAGS +=       -I$(INC_DIR) -g -Wall -Wextra

test: LDFLAGS +=  -lcppunit

OUT =             minicalc
test: OUT =       tests/tests


all: $(OBJ)
# DEBUG :
    @echo SRC: '$(SRC)'
    @echo OBJ: '$(OBJ)'

    $(CC) $(OBJ) -o $(OUT) $(LDFLAGS)

test: all
    ls $(OUT) && ./$(OUT)

以下是输出
make使用默认值SRC给出预期输出:

g++  -Iinclude/ -g -Wall -Wextra  -c -o src/main.o src/main.cpp
g++  -Iinclude/ -g -Wall -Wextra  -c -o src/minicalc.o src/minicalc.cpp
g++  -Iinclude/ -g -Wall -Wextra  -c -o src/io.o src/io.cpp
SRC: src/main.cpp src/minicalc.cpp src/io.cpp 
OBJ src/main.o src/minicalc.o src/io.o
g++ src/main.o src/minicalc.o src/io.o -o minicalc 

但是make test即使在SRC中也不会生成test / src / main.o:

g++  -Iinclude/ -g -Wall -Wextra  -c -o src/main.o src/main.cpp
g++  -Iinclude/ -g -Wall -Wextra  -c -o src/minicalc.o src/minicalc.cpp
g++  -Iinclude/ -g -Wall -Wextra  -c -o src/io.o src/io.cpp
SRC: tests/src/main.cpp src/minicalc.cpp src/io.cpp 
OBJ: tests/src/main.o src/minicalc.o src/io.o
g++ tests/src/main.o src/minicalc.o src/io.o -o tests/tests -lcppunit
g++: error: tests/src/main.o: No such file or directory
make: *** [Makefile:41: all] Error 1

all需要$(OBJ),但是创建的对象与$(SRC)变量的内容不对应


不通过手动操作来使用隐式.cpp.o规则
相同的Makefile,但用规则$(OBJ)代替了create_obj

CC =              g++

SRC_DIR =         src
SRC_TEST_DIR =    tests/src

INC_DIR =         include/
test: INC_DIR +=  tests/include/

SRC_MAIN =        $(SRC_DIR)/main.cpp
test: SRC_MAIN =  $(SRC_TEST_DIR)/main.cpp

SRC =             $(SRC_MAIN) \
                  $(SRC_DIR)/minicalc.cpp \
                  $(SRC_DIR)/io.cpp    

OBJ =             $(SRC:.cpp=.o)

CPPFLAGS +=       -I$(INC_DIR) -g -Wall -Wextra

test: LDFLAGS +=  -lcppunit

OUT =             minicalc
test: OUT =       tests/tests


all: create_obj
# DEBUG :
    @echo   SRC: '$(SRC)'
    @echo   obj: '$(OBJ)'

    $(CC) $(OBJ) -o $(OUT) $(LDFLAGS)

create_obj:
    @echo Crafting Files:
    @$(foreach file, $(SRC), \
        echo  $(file),;  \
        $(CC) -c $(file) -o $(file:.cpp=.o) $(CPPFLAGS) $(LDFLAGS); \
    )

test: all
    ls $(OUT) && ./$(OUT)

以下是输出
make

Crafting Files:
src/main.cpp,
src/minicalc.cpp,
src/io.cpp,
SRC: src/main.cpp src/minicalc.cpp src/io.cpp 
OBJ: src/main.o src/minicalc.o src/io.o
g++ src/main.o src/minicalc.o src/io.o -o minicalc 

make test

Crafting Files:
tests/src/main.cpp,
src/minicalc.cpp,
src/io.cpp,
SRC: tests/src/main.cpp src/minicalc.cpp src/io.cpp 
OBJ tests/src/main.o src/minicalc.o src/io.o
g++ tests/src/main.o src/minicalc.o src/io.o -o tests/tests -lcppunit
ls tests/tests && ./tests/tests
tests/tests


OK (0)

为什么更改源时却不更改对象的隐式构建,而手动执行却可以?

2 个答案:

答案 0 :(得分:1)

为了使make最初找到所有dep关系,它必须在初始扫描中将vars扩展到此时的任何状态。因此,all : $(OBJ)是用全局值评估的,而不是在test : all的上下文中重新评估的。抱歉。 Make的惰性评估在与上下文相关的var分配中不能很好地发挥作用。

答案 1 :(得分:1)

冒着过度杀伤的危险,这就是我写的方式。 个人准则:

  • 请谨慎使用var。他们有一个地方,但往往使文件 更少可读。
  • 依靠隐式规则/操作(例如%.o: %.cpp; ...),直到被迫不这样做为止。
  • 与目标相关的var设置仅适用于操作中使用的var。
  • 使真的想要在cwd中构建目标文件和中间文件。 任何其他方案都弯曲。这就是为什么您看到如下脚本: make -C${PLATFORM}/${CONFIG} ...
    
    all         : minicalc            ;@:     # ... or ".PHONY: all"
    test        : mctests             ; ls $^ && ./$^

    minicalc    : minicalc.o  io.o  main.o    # minicalc.o is subtly redundant here.
    mctests     : minicalc.o  io.o  mctests.o # ... likewise mctests.o is redundant.
    mctests.o   : tests/src/main.cpp          # Name difference forces an explicit action below.

    vpath %.cpp src
    CC = ${CXX}                               # Roundabout fix for MAKE using ${CC} for linking
                                              # ... but cc/gcc cannot handle C++ object files.
    CXXFLAGS += -Iinclude -g -Wall -Wextra
    mctests.o   : CXXFLAGS += -Itests/include
    mctests     : LDLIBS   += -lcpptest       # More accurately LDLIBS not LDFLAGS

    mctests.o   :; ${COMPILE.cpp} -o $@ $^