GNU make 添加了一个不在我的 Makefile 中的额外步骤,它会导致各种链接器错误。这是怎么回事?

GNU make is adding an extra step not in my Makefile that causes all sorts of linker errors. What's going on?

给定以下 GNU make 生成文件:

# TODOs so I don't forget:
# - make debugging an option
# - make 64 below an actual option
# - figure out why make test seems to rebuild the DLL
# - __declspec(dllimport)

ifeq ($(MAKECMDGOALS),64)
    CC = x86_64-w64-mingw32-gcc
    RC = x86_64-w64-mingw32-windres
    mflag = -m64
else
    CC = i686-w64-mingw32-gcc
    RC = i686-w64-mingw32-windres
    mflag = -m32
endif

OBJDIR = .objs
OUTDIR = out

BASENAME = wintable
DLLFILE = $(OUTDIR)/$(BASENAME).dll
LIBFILE = $(OUTDIR)/$(BASENAME).lib
TESTEXEFILE = $(OUTDIR)/$(BASENAME).exe

CFILES = \
    alloc.c \
    api.c \
    checkboxdraw.c \
    checkboxevents.c \
    children.c \
    coord.c \
    debug.c \
    draw.c \
    events.c \
    header.c \
    hscroll.c \
    main.c \
    metrics.c \
    modelhelpers.c \
    nullmodel.c \
    resize.c \
    scroll.c \
    select.c \
    update.c \
    util.c \
    visibility.c \
    vscroll.c

HFILES = \
    table.h \
    tablepriv.h

TESTCFILES = \
    test.c

OFILES = $(CFILES:%.c=$(OBJDIR)/%.o)
TESTOFILES = $(TESTCFILES:%.c=$(OBJDIR)/%.o)

xCFLAGS = \
    --std=c99 \
    -Wall \
    -Wextra \
    -Wno-unused-parameter \
    $(mflag) \
    $(CFLAGS)

xLDFLAGS = \
    -static-libgcc \
    -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lole32 -loleaut32 -loleacc -luuid -lmsimg32 \
    $(mflag) \
    $(LDFLAGS)

default:
    $(MAKE) clean
    $(MAKE) it
    $(MAKE) test

it: $(DLLFILE)

$(DLLFILE): $(OFILES)
    $(CC) -g -o $(DLLFILE) -shared -Wl,--out-implib,$(LIBFILE) $(OFILES) $(xLDFLAGS)

test: $(TESTEXEFILE)

$(TESTEXEFILE): $(DLLFILE) $(TESTOFILES)
    $(CC) -g -o $(TESTEXEFILE) $(TESTOFILES) $(LIBFILE) $(xLDFLAGS)

$(OBJDIR)/%.o: %.c $(HFILES) dirs
    $(CC) -g -o $@ -c $< $(xCFLAGS)

dirs:
    mkdir -p $(OBJDIR) $(OUTDIR)

clean:
    rm -rf $(OBJDIR) $(OUTDIR)

make -n test 产生

mkdir -p .objs out
i686-w64-mingw32-gcc -g -o .objs/alloc.o -c alloc.c --std=c99 -Wall -Wextra -Wno-unused-parameter -m32 
i686-w64-mingw32-gcc -g -o .objs/api.o -c api.c --std=c99 -Wall -Wextra -Wno-unused-parameter -m32 
(and so on for all the other C files)
i686-w64-mingw32-gcc -g -o out/wintable.dll -shared -Wl,--out-implib,out/wintable.lib .objs/alloc.o .objs/api.o .objs/checkboxdraw.o .objs/checkboxevents.o .objs/children.o .objs/coord.o .objs/debug.o .objs/draw.o .objs/events.o .objs/header.o .objs/hscroll.o .objs/main.o .objs/metrics.o .objs/modelhelpers.o .objs/nullmodel.o .objs/resize.o .objs/scroll.o .objs/select.o .objs/update.o .objs/util.o .objs/visibility.o .objs/vscroll.o -static-libgcc -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lole32 -loleaut32 -loleacc -luuid -lmsimg32 -m32 
i686-w64-mingw32-gcc -g -o .objs/test.o -c test.c --std=c99 -Wall -Wextra -Wno-unused-parameter -m32 
i686-w64-mingw32-gcc -g -o out/wintable.exe .objs/test.o out/wintable.lib -static-libgcc -luser32 -lkernel32 -lgdi32 -lcomctl32 -luxtheme -lole32 -loleaut32 -loleacc -luuid -lmsimg32 -m32 
i686-w64-mingw32-gcc     test.c out/wintable.exe   -o test

注意 GNU make 决定做的最后一步是如何尝试用之前构建的正确输出可执行文件重新编译我的 test.c 到一个新的 test 可执行文件中,这(可以预见)会失败:

out/wintable.exe: In function `WinMainCRTStartup':
/build/buildd/mingw-w64-3.1.0/build/i686-w64-mingw32-i686-w64-mingw32-crt/../../mingw-w64-crt/crt/crtexe.c:171: multiple definition of `WinMainCRTStartup'
/usr/lib/gcc/i686-w64-mingw32/4.9-win32/../../../../i686-w64-mingw32/lib/../lib/crt2.o:/build/buildd/mingw-w64-3.1.0/build/i686-w64-mingw32-i686-w64-mingw32-crt/../../mingw-w64-crt/crt/crtexe.c:171: first defined here
out/wintable.exe: In function `mainCRTStartup':
/build/buildd/mingw-w64-3.1.0/build/i686-w64-mingw32-i686-w64-mingw32-crt/../../mingw-w64-crt/crt/crtexe.c:199: multiple definition of `mainCRTStartup'
/usr/lib/gcc/i686-w64-mingw32/4.9-win32/../../../../i686-w64-mingw32/lib/../lib/crt2.o:/build/buildd/mingw-w64-3.1.0/build/i686-w64-mingw32-i686-w64-mingw32-crt/../../mingw-w64-crt/crt/crtexe.c:199: first defined here
out/wintable.exe:cygming-crtbegin.c:(.text+0x500): multiple definition of `__gcc_register_frame'
/usr/lib/gcc/i686-w64-mingw32/4.9-win32/crtbegin.o:cygming-crtbegin.c:(.text+0x0): first defined here
out/wintable.exe:cygming-crtbegin.c:(.text+0x550): multiple definition of `__gcc_deregister_frame'
/usr/lib/gcc/i686-w64-mingw32/4.9-win32/crtbegin.o:cygming-crtbegin.c:(.text+0x50): first defined here
out/wintable.exe: In function `mainwinCreate':
/home/pietro/src/github.com/andlabs/wintable/test.c:20: multiple definition of `mainwinCreate'
/tmp/cc50VrIz.o:test.c:(.text+0x0): first defined here
out/wintable.exe: In function `mainwinDestroy':
/home/pietro/src/github.com/andlabs/wintable/test.c:66: multiple definition of `mainwinDestroy'
/tmp/cc50VrIz.o:test.c:(.text+0x2ba): first defined here
(and so on for virtually every symbol in my test.c)
/usr/lib/gcc/i686-w64-mingw32/4.9-win32/crtbegin.o:cygming-crtbegin.c:(.text+0x22): undefined reference to `_Jv_RegisterClasses'
collect2: error: ld returned 1 exit status
<builtin>: recipe for target 'test' failed
make: *** [test] Error 1

这最后一步来自哪里?据我所知,它不在我的 makefile 中。 (注意缺少编译器标志。)谷歌搜索问题在这里也无效。这只是今天才开始发生,所有规则都和以前一样(唯一的变化是 modelhelpers.cnullmodel.c 添加到 CFILES),所以我不确定发生了什么.

是否与test的规则有关?如果是这样,为什么它以前有效?

这是 Ubuntu GNOME 14.10 上的 GNU make 4.0。

谢谢。

Make 有各种内置的隐式规则,其中之一是模式规则 % : %.c,它告诉 make 如何创建可执行文件(在 UNIX 系统中,可执行文件没有任何扩展名,如 .exe 等,它们只是像 testmkdir 等的词)如果它有一个具有相同扩展名的源文件。

您的 makefile 具有:

test: $(TESTEXEFILE)

并且您没有提供构建目标 test 的方法,因此 make 查看其内置规则并找到 % : %.c,然后查看并发现您 DO 有一个 test.c 文件,因此 make 应用该默认配方来构建目标 test,这是您要求它做的。

如果你不希望这种情况发生,你应该告诉 make test 不是一个真正的目标:你只是将它用作构建其他东西的句柄,使用 .PHONY special target:

.PHONY: test

来自手册:

The implicit rule search is skipped for .PHONY targets.