從零開始學Makefile(五)

六、多目標

Makefile的規則中的目標可以不止一個,其支持多目標,有可能我們的多個目標同時依賴於一個文件,並且其生成的命令大體類似。於是我們就能把其合併起來。當然,多個目標的生成規則的執行命令是同一個,這可能會可我們帶來麻煩,不過好在我們的可以使用一個自動化變量“$@”(關於自動化變量,將在後面講述),這個變量表示着目前規則中所有的目標的集合,這樣說可能很抽象,還是看一個例子吧。

    bigoutput littleoutput : text.g
            generate text.g -(substoutput,, @) > $@

    上述規則等價於:

    bigoutput : text.g
            generate text.g -big > bigoutput
    littleoutput : text.g
            generate text.g -little > littleoutput

    其中,-(substoutput,, @)中的“Makefilesubst @”表示目標的集合,就像一個數組,“$@”依次取出目標,並執於命令。


七、靜態模式

靜態模式可以更加容易地定義多目標的規則,可以讓我們的規則變得更加的有彈性和靈活。我們還是先來看一下語法:

    <targets …>: <target-pattern>: <prereq-patterns …>
            <commands>
            …


    targets定義了一系列的目標文件,可以有通配符。是目標的一個集合。

    target-parrtern是指明瞭targets的模式,也就是的目標集模式。

    prereq-parrterns是目標的依賴模式,它對target-parrtern形成的模式再進行一次依賴目標的定義。

這樣描述這三個東西,可能還是沒有說清楚,還是舉個例子來說明一下吧。如果我們的<target-parrtern>定義成“%.o”,意思是我們的<target>集合中都是以“.o”結尾的,而如果我們的<prereq-parrterns>定義成“%.c”,意思是對<target-parrtern>所形成的目標集進行二次定義,其計算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]這個結尾),併爲其加上[.c]這個結尾,形成的新集合。

所以,我們的“目標模式”或是“依賴模式”中都應該有“%”這個字符,如果你的文件名中有“%”那麼你可以使用反斜槓“/”進行轉義,來標明真實的“%”字符。

看一個例子:

    objects = foo.o bar.o

    all: $(objects)

    (objects): (CC) -c (CFLAGS) < -o $@


上面的例子中,指明瞭我們的目標從object object集合的模式,而依賴模式“%.c”則取模式“%.o”的“%”,也就是“foo bar”,併爲其加下“.c”的後綴,於是,我們的依賴目標就是“foo.c bar.c”。而命令中的“&lt;”和“ @”則是自動化變量,“&lt;”表示所有的依賴目標集(也就是“foo.c bar.c”),“ @”表示目標集(也就是“foo.o bar.o”)。於是,上面的規則展開後等價於下面的規則:

    foo.o : foo.c
            (CC)c (CFLAGS) foo.c -o foo.o
    bar.o : bar.c
            (CC)c (CFLAGS) bar.c -o bar.o

試想,如果我們的“%.o”有幾百個,那種我們只要用這種很簡單的“靜態模式規則”就可以寫完一堆規則,實在是太有效率了。“靜態模式規則”的用法很靈活,如果用得好,那會一個很強大的功能。再看一個例子:


    files = foo.elc bar.o lose.o

    (filter (files)): %.o: %.c
            (CC)c (CFLAGS) &lt; -o @
    (filter (files)): %.elc: %.el
            emacs -f batch-byte-compile $<


(filter (files))表示調用Makefile的filter函數,過濾“$filter”集,只要其中模式爲“%.o”的內容。其的它內容,我就不用多說了吧。這個例字展示了Makefile中更大的彈性。


八、自動生成依賴性

在Makefile中,我們的依賴關係可能會需要包含一系列的頭文件,比如,如果我們的main.c中有一句“#include “defs.h””,那麼我們的依賴關係應該是:

    main.o : main.c defs.h

但是,如果是一個比較大型的工程,你必需清楚哪些C文件包含了哪些頭文件,並且,你在加入或刪除頭文件時,也需要小心地修改Makefile,這是一個很沒有維護性的工作。爲了避免這種繁重而又容易出錯的事情,我們可以使用C/C++編譯的一個功能。大多數的C/C++編譯器都支持一個“-M”的選項,即自動找尋源文件中包含的頭文件,並生成一個依賴關係。例如,如果我們執行下面的命令:

    cc -M main.c

其輸出是:

    main.o : main.c defs.h

於是由編譯器自動生成的依賴關係,這樣一來,你就不必再手動書寫若干文件的依賴關係,而由編譯器自動生成了。需要提醒一句的是,如果你使用GNU的C/C++編譯器,你得用“-MM”參數,不然,“-M”參數會把一些標準庫的頭文件也包含進來。

    gcc -M main.c的輸出是:

    main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h /
         /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h /
         /usr/lib/gcc-lib/i486-suse-Linux/2.95.3/include/stddef.h /
         /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h /
         /usr/include/bits/sched.h /usr/include/libio.h /
         /usr/include/_G_config.h /usr/include/wchar.h /
         /usr/include/bits/wchar.h /usr/include/gconv.h /
         /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h /
         /usr/include/bits/stdio_lim.h


    gcc -MM main.c的輸出則是:

    main.o: main.c defs.h

那麼,編譯器的這個功能如何與我們的Makefile聯繫在一起呢。因爲這樣一來,我們的Makefile也要根據這些源文件重新生成,讓Makefile自已依賴於源文件?這個功能並不現實,不過我們可以有其它手段來迂迴地實現這一功能。GNU組織建議把編譯器爲每一個源文件的自動生成的依賴關係放到一個文件中,爲每一個“name.c”的文件都生成一個“name.d”的Makefile文件,[.d]文件中就存放對應[.c]文件的依賴關係。

於是,我們可以寫出[.c]文件和[.d]文件的依賴關係,並讓make自動更新或自成[.d]文件,並把其包含在我們的主Makefile中,這樣,我們就可以自動化地生成每個文件的依賴關係了。

這裏,我們給出了一個模式規則來產生[.d]文件:

    %.d: %.c
            @set -e; rm -f @; /<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (CC) -M (CPPFLAGS) < >

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章