多目錄工程的makefile示例

本文來源於:http://www.360doc.com/content/12/0403/08/1317564_200410720.shtml轉載請註明出處

本文代碼雖簡單,但涉及比較複雜的各種調用關係,欲研究者需有耐心及清醒頭腦。 切切!背景交待:

1、正在移植U-Boot,並對其源代碼進行了一些分析,感覺它的Makefile十分強勁;
2、以前寫的Makefile模板不合適多層目錄;
3、研究一下多個庫之間相互調用的問題。
平臺及測試環境介紹:
1、fc9系統,i386平臺,gcc版本4.3.2,使用Secure CRT連接linux系統,數據均由該軟件複製而得;
2、工程目錄名稱爲lib-test,其下有3個代碼目錄及一個頭文件目錄,分別是foo(foo.c及common.c文件)、bar(bar.c及common.c文件)、bt(backtrace.c文件)和configs(foo.h、bar.h及backtrace.h文件);其中bt目錄爲backtrace代碼,它是作者的工程庫的一部分。
目錄路徑、文件分佈如下
lib-test
|-->main.c
|-->example.c
|-->Makefile
|              |-->foo.c
|-->foo-->|-->common.c
|              |-->Makefile
|
|               |-->bar.c
|-->bar-->|-->common.c
|               |-->Makefile
|
|              |-->backtrace.c
|-->bt--->|
|              |-->Makefile
|
|                   |-->foo.h
|-->configs->|-->bar.h
|                   |-->backtrace.h

Makefile組織
1、各個子目錄單獨使用Makefile,主要生成相關的庫(這裏特指靜態庫);
2、頂層Makefile負責將本目錄下源代碼文件編譯成目標文件(如果有的話),並依次進入各種子目錄編譯生成相關庫文件,最後進行鏈接,生成可執行文件。該Makefile關鍵語句如下:
for dir in $(SUBDIRS); /
do $(MAKE) -C $$dir all || exit 1; /
done
意思是進入指定子目錄,並執行子目錄的Makefile文件(子目錄只負責生成庫文件)。
庫相互調用測試(詳見代碼):
1、foo.c文件中hello_foo函數調用bar.c文件中的bar函數以及同一目錄下common.c文件的common_bar函數;
2、bar.c文件中hello_bar函數調用foo.c文件中的foo函數以及同一目錄下common.c文件的common_foo函數。
代碼示例:
1、foo目錄下:
1)、foo.h
#ifndef FOO_H_
#define FOO_H_
void foo(int i);
int hello_foo(void);
#endif
2)、foo.c
#include <stdio.h>
void foo(int i)
{
        printf("hell from %s() in file: %s, num:%d/n", __func__, __FILE__, i);
}
int hello_foo(void)
{
        printf("in func: %s()/n", __func__);
        bar(100);
        common_foo();
        printf("=========================================/n");
        return 0;
}
3)、common.c
#include <stdio.h>
int common_foo(void)
{
        printf("this is a common function in foo/common.c/n");
        return 0;
}
2、bar目錄
1)、bar.h
#ifndef BAR_H_
#define BAR_H_
void bar(int i);
int hello_bar(void);
#endif
2)、bar.c
#include <stdio.h>
void bar(int i)
{
        printf("hell from %s() in file: %s, num:%d/n", __func__, __FILE__, i);
}
int hello_bar(void)
{
        printf("in func: %s()/n", __func__);
        foo(200);
        common_bar();
        printf("=========================================/n");
        return 0;
}
3)、common.c
#include <stdio.h>
int common_bar(void)
{
        printf("this is a common function in bar/common.c/n");
        return 0;
}
3、example.c
#include <stdio.h>
void foo1(int a)
{
        int i;
        i = a;
}
int fun(int a, int b, int c)
{
        char buf[14];
        int sum;
        sum = a + b + c;
        foo1(a);
        return sum;
}
4、main.c
#include <stdio.h>
#include <backtrace.h>
#include <foo.h>
#include <bar.h>
int main(void)
{
        printf("hello from %s()/n/n", __func__);
        hello_foo();
        hello_bar();
       
        print_trace(11);
}
(題外話:
這裏的3個自定義頭文件使用了尖括號(<>),如果按教科書的說法是不正確的,因爲尖括號是從標準庫的路徑搜索頭文件的。然而作者經常看到一些代碼中標準庫的頭文件是使用雙引號來包含的(如#include "stdio.h"),這裏反其道而行之。因爲在Makefile中使用-I選項指定了額外的頭文件路徑,使用
[latelee@latelee lib-test-latelee.org]$ gcc -g -c main.c example.c --verbose -Iconfigs
命令來編譯時顯示了gcc搜索頭文件路徑的過程,如下:
#include "..." search starts here: 
#include <...> search starts here: 
 configs
 /usr/local/include 
 /usr/lib/gcc/i386-redhat-linux/4.3.2/include 
 /usr/include 
End of search list.
當然,實際開發中不提倡這種方法,然而在U-Boot中卻是十分常見的,只是有些人不明白其中的道理罷了。
題外話 結束)
庫目錄的Makefile(這裏顯示的是foo目錄下的Makefile,其它者修改庫名稱及代碼文件名稱即可)
# A simple Makefile for lib(libxxx.a)  
# By Late Lee(http://www.latelee.org)  
AR = ar
ARFLAGS = cr
LIB = libfoo.a
RM = -rm -rf
OBJS := foo.o common.o

all: $(LIB)
$(LIB)$(OBJS)
        $(AR) $(ARFLAGS) $@ $(OBJS)
clean:
        $(RM) $(OBJS) $(LIB) *.bak *~
.PHONY: all clean
頂層目錄的Makefile文件
#################################################################  
# A simple Makefile  
# By Late Lee(http://www.latelee.org)  

#   
# bugs:  
#      1. 需要顯式指定庫位置、名稱;  
#      2. make 及 make clean處理得不好(對於庫,要麼刪除再編譯,要麼無操作);  
#################################################################  

CC=gcc
CFLAGS = -Wall
DEBUG = y

ifeq ($(DEBUG), y)
CFLAGS += -g
else
CFLAGS += -O2
endif

SUBDIRS := foo bar bt

LIBS := bt/libbt.a foo/libfoo.a bar/libbar.a

LDFLAGS = $(LIBS)

RM = -rm -rf

__OBJS = main.o
__OBJS += example.o

__SRCS = $(subst .o,.c,$(__OBJS))

target = a.out

MAKE = make

#all: clean $(target)  
all: $(target)

$(__OBJS)$(__SRCS)
        $(CC) $(CFLAGS) -c $^ -I ./configs/

$(target)$(__OBJS)
        for dir in $(SUBDIRS); /
        do $(MAKE) -C $$dir all || exit 1; /
        done
        $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)

clean:
        @for dir in $(SUBDIRS)do make -C $$dir clean|| exit 1; done
        $(RM) $(__OBJS) $(target) *.bak *~

.PHONY: all clean 
執行make的過程:
[latelee@latelee lib-test]$ make 
gcc -Wall -g -c main.c example.c -I ./configs/ 
example.c: In function ‘fun’:
example.c:10: warning: unused variable ‘buf’
for dir in foo bar bt  ; / 
        do make -C $dir all || exit 1  ; / 
        done
make[1]: Entering directory `/home/latelee/linux-c/lib-test/foo' 
cc    -c -o foo.o foo.c
cc    -c -o common.o common.c
ar cr libfoo.a foo.o common.o
make[1]: Leaving directory `/home/latelee/linux-c/lib-test/foo' 
make[1]: Entering directory `/home/latelee/linux-c/lib-test/bar' 
cc    -c -o bar.o bar.c
cc    -c -o common.o common.c
ar cr libbar.a bar.o common.o
make[1]: Leaving directory `/home/latelee/linux-c/lib-test/bar' 
make[1]: Entering directory `/home/latelee/linux-c/lib-test/bt' 
cc    -c -o backtrace.o backtrace.c
ar cr libbt.a backtrace.o
make[1]: Leaving directory `/home/latelee/linux-c/lib-test/bt' 
gcc -Wall -g main.o example.o -o a.out bt/libbt.a foo/libfoo.a bar/libbar.a
運行結果:
[latelee@latelee lib-test]$ ./a.out 
hello from main()
in func: hello_foo()
hell from bar() in file: bar.c, num:100
this is a common function in foo/common.c 
=========================================
in func: hello_bar()
hell from foo() in file: foo.c, num:200
this is a common function in bar/common.c 
=========================================
Obtained 4 stack frames.
./a.out [0x8048733] 
./a.out [0x80486cf] 
/lib/libc.so.6(__libc_start_main+0xe5) [0x7ea6d5] 
./a.out [0x8048601]


發佈了27 篇原創文章 · 獲贊 12 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章