My code works, I don’t know why.

國王的耳朵是驢耳朵

Makefile的Pattern Rules小小注意事項

| Comments

Makefile 自動將產生的檔案指定到特定目錄中,我犯了一個錯誤,造成更動header檔案發生錯誤(原文已修復)。

狀況如下

Error log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ make
mkdir -p build/libs/
cc -g -MMD -I ./include -c libs/liba.c -o build/libs/liba.o
mkdir -p build/libs/
cc -g -MMD -I ./include -c libs/libb.c -o build/libs/libb.o
mkdir -p build/
cc -g -MMD -I ./include -c test.c -o build/test.o
cc -o build/test build/libs/liba.o build/libs/libb.o build/test.o

$ make
make: `build/test' is up to date.

$ touch include/*

$ make
mkdir -p build/libs/
cc -g -MMD -I ./include -c libs/liba.c include/libb.h -o build/libs/liba.o
cc: fatal error: cannot specify -o with -c, -S or -E with multiple files
compilation terminated.
make: *** [build/libs/liba.o] Error 4

從錯誤中可以看到,明明在編liba.c code怎麼會有libb.h來亂呢?請再看下面的分析。

有問題的Makefile如下

有問題的Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CFLAGS=-g -MMD -I ./include

# binaries
LIB_SRC=$(shell ls libs/*.c)
SRCS= $(LIB_SRC) test.c

OBJS = $(patsubst %.c, %.o, $(SRCS))

# Output
OUT_DIR = build
OUT_OBJS=$(addprefix $(OUT_DIR)/, $(OBJS))
DEPS = $(patsubst %.o, %.d, $(OUT_OBJS))

# Build Rules
TARGET=test

$(OUT_DIR)/$(TARGET): $(OUT_OBJS)
  $(CC) -o $@ $^

$(OUT_DIR)/%.o:%.c
  mkdir -p $(dir $@)
  $(CC) $(CFLAGS) -c $^ -o $@

clean:
  rm -rf $(OUT_DIR)

-include $(DEPS)

問題出在$(CC) $(CFLAGS) -c $^ -o $@,再次回顧一下

  • -MMD效果
有問題的Makefile
1
2
$ cat build/libs/liba.d
build/libs/liba.o: libs/liba.c include/libb.h

而在Makefile中我們會使用-include $(DEPS) include 這些*.d檔案,所以更改Makefile部份敘述。

1
2
3
$(OUT_DIR)/%.o:%.c
  mkdir -p $(dir $@)
  $(CC) $(CFLAGS) -c $^ -o $@

第一次make後,當header改變, 有的prerequisite會更動,進而觸發Pattern Rule,但是$^是所有的prerequisite,所以$(CC)會把所有的prerequisite全部帶入造成錯誤。已這邊的例子,prerequisite就是libs/liba.cinclude/libb.h

解法很簡單,把$^換成$<就可以了。而$<是什麼呢?就是第一個prerequisite,對照這邊可以看到,第一個就是第一個prerequisite就是libs/liba.c檔案了。

參考資料

Comments