My code works, I don’t know why.

國王的耳朵是驢耳朵

Makefile Header File Dependency問題

| Comments

自幹Makefile的時候常常忘記把header files加到Prerequisite然後編譯的時候就發生非預期的錯誤。之前整理GNU: The C Preprocess導讀有看到gcc/cpp(C Preprocessor)的-MMD參數,但是還不清楚怎麼使用。直到看到Using g++ with -MMD in makefile to automatically generate dependencies才知道怎麼玩。手癢自己也來弄一個驗證一下。

測試檔案及程式碼如下 不想看code只要知道每個檔案都有參考到某個自訂的header file就好了。

liba.h
1
2
3
4
5
#ifndef LIBA_H_2013
#define LIBA_H_2013
void test_liba(void);
void from_liba(void);
#endif
liba.c
1
2
3
4
5
6
7
8
9
10
11
12
#include "libb.h"
#include <stdio.h>

void test_liba(void)
{
    from_libb();
}

void from_liba()
{
    printf("%s\n", __PRETTY_FUNCTION__);
}
libb.h
1
2
3
4
5
#ifndef LIBB_H_2013
#define LIBB_H_2013
void test_libb(void);
void from_libb(void);
#endif
libb.c
1
2
3
4
5
6
7
8
9
10
11
12
#include "liba.h"
#include <stdio.h>

void test_libb(void)
{
    from_liba();
}

void from_libb()
{
    printf("%s\n", __PRETTY_FUNCTION__);
}
test.c
1
2
3
4
5
6
7
8
9
10
#include "libb.h"
#include "liba.h"
#include <stdlib.h>
int main(void)
{
    test_liba();
    test_libb();

    return 0;
}

先來複習一下-MMD的效果

執行-MMD 結果
1
2
3
4
5
$ gcc -MMD -c test.c
$ ls test.*
test.c  test.d  test.o
$ cat test.d
test.o: test.c libb.h liba.h

剩下就是想辦法讓Makefile吃到*.d的內容,因此Makefile長的就像這樣

Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CFLAGS=-g -MMD

SRCS= test.c liba.c libb.c
OBJS = $(patsubst %.c, %.o, $(SRCS))
DEPS = $(patsubst %.o, %.d, $(OBJS))

TARGET=test

$(TARGET): $(OBJS)
  gcc -g -o $@ $^

clean:
  rm -rf *.o *.a *~ $(TARGET) $(OBJS) $(DEPS)

-include $(DEPS)

Makefile說明:

  • patsubst
    • Makefile 提供的函數,用在pattern 替換,所以上面做了下列的代換
    • .c轉成.o
    • .o轉成.d
  • $@
    • Makefile的內建巨集,代表target
  • $^
    • Makefile的內建巨集,代表prerequisite
  • -
    • 告訴make 忽略失敗,trick就在這邊

Trick就是上面提到的-,第一次make時一定.d檔案不存在,因此不使用-就一定編不過。第一次編過之後因為.d已經產生,所以header file的dependecy也被處理好了。

其實上面的Makefile可以做更多改進,當project檔案數量大到某種程度就可以感覺改進的效果,例如

  • .o 和.d 指定產生到特定目錄,而不是散落各處
  • 加上distclean,只有在這種情況下才去刪除*.d的檔案

Comments