<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    so true

    心懷未來,開創未來!
    隨筆 - 160, 文章 - 0, 評論 - 40, 引用 - 0
    數據加載中……

    make學習

    權威資料:http://www.gnu.org/software/make/manual/make.html,到這個頁面的尾部有index,可以快速查找一些關鍵詞來定位你想要找的東西,例如automatic variable
    一個不錯的中文總結:http://www.cnblogs.com/liangxiaxu/archive/2012/07/31/2617384.html
    自我學到的一些知識點:
    1。makefile中的每一個target都是一個文件,make的最終目標都是生成這個文件,target可以帶路徑,例如dir1/dir2/test;
    2。%.o: %.c中的%可以匹配路徑,例如test1/test2/*.o都可以從test1/test2/*.c來推導;
    3。關于PHONY目標,能成為PHONY的target應該是不能對應具體文件的,例如clean,例如我們經常用all來收集一批需要產生的真正的target,偽目標的特性就是:當make ABC時,不管當前目錄是否有ABC文件,都會去分析其依賴項并執行recipes;
    4。如果一個target沒有任何依賴,那么這個target永遠都是最新的,因此只要這個target文件存在,那么永遠不會去執行它的recipes,如果我們把這個target聲明為偽目標,那么不論這個target文件是否存在,執行其recipes;
    5。一個target依賴多項時,可以分開寫,例如:
    A: B1
    A:
    A:;
    A: B2
    echo $@, $^
    最終A依賴于B1和B2
    6。可以一次性寫多個target的依賴,例如:A B C: D
    7。A: PARAM := test.cpp這種寫法表明PARAM這個變量的作用域僅僅是生成A時有用
    8。A: B C | D E,這里D E被稱為order-only依賴,就是說對于D E,和B C一樣,該更新就更新,而且還是先處理依賴項D,再處理依賴項E,只不過到最后即便D或者E更新了,也不會因此而更新A,除非B或C更新了,千萬不要誤以為只要D存在就關心D的死活了(即是否要更新);
    9。make test -n會打印出make過程所有需要執行的命令,但并不會執行;
    10。make test -d會打印出make過程所有的分析過程,例如怎么解決的依賴,自動推導規則,為什么有些target需要更新,有些不需要;
    11。make test -p會打印出make過程中所有的變量和規則的推導結果,即所有變量都展開了,而且會告訴你所執行的命令都來自于哪個makefile的哪一行;
    12。make --debug[=FLAGS]
                Print debugging information in addition to normal processing.  If the FLAGS are omitted, then the behavior is the same as if -d was specified.  FLAGS may be a for all debugging output (same as using -d),
                b for basic debugging, v for more verbose basic debugging, i for showing implicit rules, j for details on invocation of commands, and m for debugging while remaking makefiles.
    13。make V=1的含義是:定義了一個變量,名字是V,值是1,可以在Makefile中通過$(V)來得到V的值;
    14。make -j 8的含義是:用8個進程同時build,即速度加快8倍;
    15。$+和$^差不多,區別是$^去重了,$+都保留下來了;
    16。想查看一個target的所有依賴項,有時候一個target的依賴項會分散在多個文件中,例如對于target all,可以增加如下代碼即可:
    all:
    #$^
    這里當然也可以用@echo $^,但上面這種簡單寫法也可以,反正recipe就是寫shell命令
    recipe可以由換行,也可以有make的一些條件控制語句,例如:
    all:
        @echo "hello"

        @echo "world"

    ifeq "$(abc)" "hello"
        @echo "equal"
    else
        @echo "not equal"
    endif
    總結:尤其是make -d/-p,基本可以搞定所有你弄不清楚的make問題,仔細分析就好了。

    $(if ifeq  "foo" "bar", @echo match is broken, @echo match works)
    $(if ifneq "foo" "bar", @echo match works, @echo match is broken)
    $(if ...) conditional function evaluates to true when the first argument passed to it is non-empty. In you case the condition is literal text: ifeq "foo" "bar", which is, obviously, non-empty.
    ifeq/ifneq conditionals are in fact directives, not functions. They can't be used inside variable definition and in functions.
    Back to your example, to test string for equality inside the condition use functions like filter, filter-out and findstring:
    $(if $(filter-out foo,bar),@echo not equal,@echo equal)

    ====================================================================
    下面是之前學習make時寫的一些東西,現在看來好像很垃圾:
    [make的規則]
    在定義好依賴關系后,后續的那一行定義了如何生成目標文件的操作系統命令,一定要以一個Tab鍵作為開頭。記住,make并不管命令是怎么工作的,他只管執行所定義的命令。
    make會比較targets文件和prerequisites文件的修改日期(mtime,而不是ctime或atime),如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的話,那么,make就會執行后續定義的命令。
    也可以像下面這樣把命令放在后面,通過一個分號來搞定
    targets : prerequisites ; command
                command
    比如你的第一條命令是cd命令,你希望第二條命令得在cd之后的基礎上運行,那么你就不能把這兩條命令寫在兩行上,而應該把這兩條命令寫在一行上,用分號分隔。也就是說:一行內的命令是在一個子shell環境中執行的。

    make會一按順序一條一條的執行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規則后面的分號后的。在命令行之間中的空格或是空行會被忽略,但是如果該空格或空行是以Tab鍵開頭的,那么make會認為其是一個空命令。

    當我們用“@”字符在命令行前,那么,這個命令將不被make顯示出來,最具代表性的例子是,我們用這個功能來像屏幕顯示一些信息。如:

        @echo 正在編譯XXX模塊......

    當make執行時,會輸出“正在編譯XXX模塊......”字串,但不會輸出命令,如果沒有“@”,那么,make將輸出:

        echo 正在編譯XXX模塊......
        正在編譯XXX模塊......

    如果不給make命令指定target,那么make會將makefile文件中出現的第一個target作為此次執行make的目標,如果該target不是一個,那么取第一個.其實make一次只對一個target負責,除非你在執行make時指定多個target,如make target1 target2

    [變量定義]
    =定義的時候不會被展開,在具體引用時才會被展開
    :=定義的時候就會被展開,建議使用該種變量定義

    [自動推導]
    main.o : defs.h
    make會自動為你添加main.cpp這個依賴文件
    [引用其他Makefile]
    include foo.make *.mk $(bar)
    [make的工作方式]
    GNU的make工作時的執行步驟入下:(想來其它的make也是類似)

       1. 讀入所有的Makefile。
       2. 讀入被include的其它Makefile。
       3. 初始化文件中的變量。
       4. 推導隱晦規則,并分析所有規則。
       5. 為所有的目標文件創建依賴關系鏈。
       6. 根據依賴關系,決定哪些目標要重新生成。
       7. 執行生成命令。

    1-5步為第一個階段,6-7為第二個階段。第一個階段中,如果定義的變量被使用了,那么,make會把其展開在使用的位置。但make并不會完全馬上展開,make使用的是拖延戰術,如果變量出現在依賴關系的規則中,那么僅當這條依賴被決定要使用了,變量才會在其內部展開。

    當然,這個工作方式你不一定要清楚,但是知道這個方式你也會對make更為熟悉。有了這個基礎,后續部分也就容易看懂了。

    [make命令的參數]
    make -i 忽略出錯命令,等同于在makefile中對應命令前加上‘-’符號
    make -k 如果某規則中的命令出錯了,那么就終目該規則的執行,但繼續執行其它規則
    make -n 那么其只是顯示命令,但不會執行命令
    make -s 或“--slient”則是全面禁止命令的顯示
    make -w 在“嵌套執行”中比較有用的參數,“-w”或是“--print-directory”會在make的過程中輸出一些信息,讓你看到目前的工作目錄。

    [文件搜尋]
    Makefile文件中的特殊變量“VPATH”就是完成這個功能的,如果沒有指明這個變量,make只會在當前的目錄中去找尋依賴文件和目標文件。如果定義了這個變量,那么,make就會在當當前目錄找不到的情況下,到所指定的目錄中去找尋文件了。

        VPATH = src:../headers

    上面的的定義指定兩個目錄,“src”和“../headers”,make會按照這個順序進行搜索。目錄由“冒號”分隔。(當然,當前目錄永遠是最高優先搜索的地方)

    另一個設置文件搜索路徑的方法是使用make的“vpath”關鍵字(注意,它是全小寫的),這不是變量,這是一個make的關鍵字,這和上面提到的那個 VPATH變量很類似,但是它更為靈活。它可以指定不同的文件在不同的搜索目錄中。這是一個很靈活的功能。它的使用方法有三種:

       1. vpath < pattern> < directories>
          為符合模式< pattern>的文件指定搜索目錄< directories>。
       2. vpath < pattern>
          清除符合模式< pattern>的文件的搜索目錄。
       3. vpath
          清除所有已被設置好了的文件搜索目錄。

    vapth使用方法中的< pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”結尾的文件。< pattern>指定了要搜索的文件集,而< directories>則指定了的文件集的搜索的目錄。例如:

        vpath %.h ../headers

    該語句表示,要求make在“../headers”目錄下搜索所有以“.h”結尾的文件。(如果某文件在當前目錄沒有找到的話)

    [偽目標]
    “偽目標”并不是一個文件,只是一個標簽,由于“偽目標”不是文件,所以make無法生成它的依賴關系和決定它是否要執行。我們只有通過顯示地指明這個“目標”才能讓其生效。
    沒有依賴項的target自動成為偽目標,放在.PHONY的依賴項中的target自動成為偽目標.
    澄清一個錯誤觀點: 放在.PHONY中的依賴項并不是make后就自動執行,而僅僅只是聲明該target是個偽目標而已,make命令依然還是執行makefile中的第一個target
    偽目標的更新時間永遠都是最新的,因此只要執行到了偽目標,那么必然會被執行,時間戳在它面前無效.
    因此對于通過make命令就期望強制執行很多target的話,可以在makefile開頭這么寫:
    all : prog1 prog2 prog3
    .PHONY : all

    [靜態規則]
    語法:
    <targets ...>: <target-pattern>: <prereq-patterns ...>
    <commands>
    ...
    看一個例子:

        objects = foo.o bar.o

        all: $(objects)

        $(objects): %.o: %.c
                $(CC) -c $(CFLAGS) $< -o $@
    使用filter:
    files = foo.elc bar.o lose.o

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




    [makefile中的函數]
    形式為:$(函數名 參數)
    自定義函數:
    define 函數名
        函數體
    endef

    [gcc技巧]
    如果你使用GNU的C/C++編譯器,你得用“-MM”參數,不然,“-M”參數會把一些標準庫的頭文件也包含進來。

    [嵌套執行 & 變量或參數向下傳遞]
    嵌套執行make
    在一些大的工程中,我們會把我們不同模塊或是不同功能的源文件放在不同的目錄中,我們可以在每個目錄中都書寫一個該目錄的Makefile,這有利于讓我們的Makefile變得更加地簡潔,而不至于把所有的東西全部寫在一個Makefile中,這樣會很難維護我們的Makefile,這個技術對于我們模塊編譯和分段編譯有著非常大的好處。

    例如,我們有一個子目錄叫subdir,這個目錄下有個Makefile文件,來指明了這個目錄下文件的編譯規則。那么我們總控的Makefile可以這樣書寫:

        subsystem:
                cd subdir && $(MAKE)

    其等價于:

        subsystem:
                $(MAKE) -C subdir

    定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數,所以定義成一個變量比較利于維護。這兩個例子的意思都是先進入“subdir”目錄,然后執行make命令。

    我們把這個Makefile叫做“總控Makefile”,總控Makefile的變量可以傳遞到下級的Makefile中(如果你顯示的聲明),但是不會覆蓋下層的Makefile中所定義的變量,除非指定了“-e”參數。

    如果你要傳遞變量到下級Makefile中,那么你可以使用這樣的聲明:

    export <variable ...>

    如果你不想讓某些變量傳遞到下級Makefile中,那么你可以這樣聲明:

    unexport <variable ...>

    如:
            export variable := value #定義的同時也export了

            export variable += value

    如果你要傳遞所有的變量,那么,只要一個export就行了。后面什么也不用跟,表示傳遞所有的變量。

    需要注意的是,有兩個變量,一個是SHELL,一個是MAKEFLAGS,這兩個變量不管你是否export,其總是要傳遞到下層Makefile 中,特別是MAKEFILES變量,其中包含了make的參數信息,如果我們執行“總控Makefile”時有make參數或是在上層Makefile中定義了這個變量,那么MAKEFILES變量將會是這些參數,并會傳遞到下層Makefile中,這是一個系統級的環境變量。

    但是make命令中的有幾個參數并不往下傳遞,它們是“-C”,“-f”,“-h”“-o”和“-W”(有關Makefile參數的細節將在后面說明),如果你不想往下層傳遞參數,那么,你可以這樣來:

       
        subsystem:
                cd subdir && $(MAKE) MAKEFLAGS=

    如果你定義了環境變量MAKEFLAGS,那么你得確信其中的選項是大家都會用到的,如果其中有“-t”,“-n”,和“-q”參數,那么將會有讓你意想不到的結果,或許會讓你異常地恐慌。

    還有一個在“嵌套執行”中比較有用的參數,“-w”或是“--print-directory”會在make的過程中輸出一些信息,讓你看到目前的工作目錄。比如,如果我們的下級make目錄是“/home/hchen/gnu/make”,如果我們使用“make -w”來執行,那么當進入該目錄時,我們會看到:

       
        make: Entering directory `/home/hchen/gnu/make'.

    而在完成下層make后離開目錄時,我們會看到:

       
        make: Leaving directory `/home/hchen/gnu/make'

    當你使用“-C”參數來指定make下層Makefile時,“-w”會被自動打開的。如果參數中有“-s”(“--slient”)或是“--no-print-directory”,那么,“-w”總是失效的。

    [自動生成依賴關系]
    自動生成依賴性

    在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 $@; \
                 $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
                 sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
                 rm -f $@.$$$$

    這個規則的意思是,所有的[.d]文件依賴于[.c]文件,“rm -f $@”的意思是刪除所有的目標,也就是[.d]文件,第二行的意思是,為每個依賴文件“$<”,也就是[.c]文件生成依賴文件,“$@”表示模式 “%.d”文件,如果有一個C文件是name.c,那么“%”就是“name”,“$$$$”意為一個隨機編號,第二行生成的文件有可能是 “name.d.12345”,第三行使用sed命令做了一個替換,關于sed命令的用法請參看相關的使用文檔。第四行就是刪除臨時文件。

    總而言之,這個模式要做的事就是在編譯器生成的依賴關系中加入[.d]文件的依賴,即把依賴關系:

        main.o : main.c defs.h

    轉成:

        main.o main.d : main.c defs.h

    于是,我們的[.d]文件也會自動更新了,并會自動生成了,當然,你還可以在這個[.d]文件中加入的不只是依賴關系,包括生成的命令也可一并加入,讓每個[.d]文件都包含一個完賴的規則。一旦我們完成這個工作,接下來,我們就要把這些自動生成的規則放進我們的主Makefile中。我們可以使用Makefile的“include”命令,來引入別的Makefile文件(前面講過),例如:

        sources = foo.c bar.c

        include $(sources:.c=.d)

    上述語句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一個替換,把變量$(sources)所有[.c]的字串都替換成[.d],關于這個“替換”的內容,在后面我會有更為詳細的講述。當然,你得注意次序,因為include是按次來載入文件,最先載入的[.d]文件中的目標會成為默認目標

    posted on 2009-05-17 17:14 so true 閱讀(1471) 評論(0)  編輯  收藏 所屬分類: C&C++

    主站蜘蛛池模板: 激情内射亚洲一区二区三区爱妻| 免费99精品国产自在现线| 国产精品久久香蕉免费播放| 亚洲另类春色校园小说| 日本zzzzwww大片免费| 久久亚洲精品成人av无码网站| 免费毛片在线看不用播放器| 久久久无码精品亚洲日韩软件 | 曰曰鲁夜夜免费播放视频| 久久精品国产亚洲AV高清热| 性xxxx视频免费播放直播| 亚洲色四在线视频观看| 国产成人精品免费视频大全麻豆| 亚洲成aⅴ人片在线影院八| 999国内精品永久免费观看| 久久精品国产亚洲AV蜜臀色欲| 成年人在线免费看视频| 日日摸日日碰夜夜爽亚洲| 亚洲av中文无码| 97在线视频免费公开视频| 亚洲国产综合精品中文第一区| 3344免费播放观看视频| 亚洲日本久久久午夜精品| 午夜时刻免费入口| 日产久久强奸免费的看| 亚洲人成人网站色www| 99re在线视频免费观看| 亚洲AV成人影视在线观看| 一本色道久久88亚洲综合| 久久WWW免费人成—看片| 亚洲高清在线视频| 成人免费无码大片A毛片抽搐| 国产精品亚洲а∨天堂2021| 中文字幕亚洲无线码| 真实国产乱子伦精品免费| 亚洲精品天堂成人片AV在线播放 | 日本在线免费观看| 亚洲av乱码一区二区三区香蕉| 国产午夜鲁丝片AV无码免费| 伊人久久大香线蕉免费视频| 亚洲欧洲另类春色校园网站|