2013年3月26日 星期二

有用的程式設計網站

GNU's Document:http://www.gnu.org/manual/manual.html
    有GCC、GNU Scientific Library(有FFT的library)

make file


粗體:語法
一般:範例


VARIABLE_NAME = VARIABLE_VALUE
$( VARIABLE_NAME )

若:
targets = foo
$(targets): common.h
    gcc -o $(targets) foo.c
則等同於:
foo: common.h
    gcc -o foo foo.c

VARIABLE_NAME := VARIABLE_VALUE
如果用"="的話,make file會將整個檔案展開後再決定最後的變數值,若中途要變更則要用":="


x = foo
y = $(x) bar
x = xyz
# y 的值為 xyz bar



x := foo
y := $(x) bar
x := xyz
# y 的值為 foo bar


Q1:如果混用會發生什麼事?

VARIABLE_NAME ?= VARIABLE_VALUE
如果未定義,則此assign成立,否則無效。

若:

targets ?= foo1
$(targets): common.h
    gcc -o $(targets) foo.c

則等同於:


foo1: common.h
    gcc -o foo foo.c




若:
targets = foo0  或  targets := foo0

targets ?= foo1
$(targets): common.h
    gcc -o $(targets) foo.c

則等同於:


foo0: common.h
    gcc -o foo foo.c




VARIABLE_NAME += VARIABLE_VALUE
把變數值加到變數的最後端


CFLAGS = -Wall -g
CFLAGS += -O2


最後  CFLAGS  為  "-Wall -g -O2"

define  ...  endef
跟"="功能類似,但可以多行

若:

define foo
uname -a
echo $$SHELL
endef

all:
        $(foo)
則等同於:
foo = uname -a; echo $$SHELL

all:
        $(foo)

上面利用了"$$",讓 "$" 能傳到 Shell 中。

Q2:是類似於"="還是類似於":="?

Q3:"target"  是什麼?為什麼要用all:在裡面重新指定?


echo $(VARIABLE_NAME)
@echo $(VARIABLE_NAME)

把值輸出到  shell
從環境變數中尋找同 VARIABLE_NAME  的變數

wildcard
一般而言,make file中的通用符號會自動展開。
但是在變數定義或函數呼叫時就會失效,所以需要用到wildcard來強制展開。

objects=$(wildcard *.o)

規則(Rule):
make file會依此決定要如何compile

主要架構:

target: dependencies
<Tab>Commands


或:
target: dependencies; Commands
<Tab>Commands



target:所要建立的檔案< /td>
dependencies:相依項目。 make 會據此決定是否要重新編譯 target。
Commands:建立 target 的指令。


target
目標檔案,必須以" : "結尾


foo.o: common.h
    gcc -c foo.c


若非檔案,則為  fake  項目,建議用"  .PHONY  "來指定他是  fake  。
如果未指定,make file會去找這個檔案。
若不巧檔案中又剛好有同名的,make file就會一直以為是up-to-date,此項目永遠不會被執行!


.PHONY: clean
clean:
    rm *.o

另外,如果某個非 fake 項目的 target 的 dependencies 包含了 fake 項目的話,因為 make 一定會執行 fake 項目,這樣一來,這個非 fake 項目的 target 一定也會被執行。這可能不是理想的做法。


dependencies
是指定在建立 target 之前,必須先檢查的項目。可以是任意檔案類型(.o,.h...),也可以不指定。
會檢查他們的日期是否比target晚,如果是,就會compile target。

Commands
必須以<tab>開頭。使用 Shell Script 語法。在 Makefile 裡,只要以 <Tab> 開頭都將會被視為 Shell Script 執行。
每條法則必須寫在同一行。每條 Command 會啟動一個新的 Shell,預設為 /bin/sh。若執行完某條 Command 但傳回了錯誤值,make 就會中斷執行。
因為每條 Command 會啟動一個新的 Shell,所以有時執行的指令必須寫在同一行,像是使用 if 來進行條件判斷,此時可以用 ; 來分隔指令。


all:
    if [ -f foo ]; then rm foo; fi


錯誤示範:


all:
    cd subdir; $(MAKE)



這時因為 make 只會檢查最後一個指令的傳回值,所以在以上指令中,即使 subdir 不存在,但 make 並不會因而中斷執行,並會繼續執行 $(MAKE) 指令,而產生了不可預期的結果。

為了避免這個問題,可以利用 && 來檢查其中某個指令是否成功執行,再決定是否執行下個指令。



all:
    cd subdir && $(MAKE)


@
不要顯示執行的指令。
-
表示即使該行指令出錯,也不會中斷執行。



.PHONY: clean
clean:
    @echo "Clean..."
    -rm *.o


特殊規則:


foo.o: common.h
    gcc -c foo.c

因為是用gcc來編譯,所以可以簡化為:

foo.o: common.h


若不想啟動這個規則,則使用空白命令放在最後一行


foo.o: common.h
<tab>


內部變數:


$?: 代表已被更新的 dependencies 的值。也就是 dependencies 中,比 targets 還新的值。
$@:代表 targets 的值。
$<:代表第一個 dependencies 的值。
$* :代表 targets 所指定的檔案,但不包含副檔名。

內部函數:
使用make時所支援的函數
http://www.gnu.org/savannah-checkouts/gnu/make/manual/html_node/Functions.html

條件判斷:
ifeq
比較變數1&2是否相等
ifneq
ifdef
檢查此變數是否為空
ifndef



ifeq (value1, value2)
    ...
else
    ...
endif




ifdef variable
    ...
else
    ...
endif


引入檔案:
把此檔案內容全部插入此make file中
可以同時引入多個檔案,也可以使用萬用字元。
include foo.in common*.in $(MAKEINCS)

子目錄:
如果該專案有多個目錄,且每一個目錄中都有 Makefile,則利用以下指令來進入子目錄並進行編譯。
cd dir && $(MAKE)

範例:

SUBDIRS = dir1 dir2 dir3

all:
        for i in $(SUBDIRS); do
                (cd $$i && make);
        done

clean:
        for i in $(SUBDIRS); do
                (cd $$i && make clean);
        done

install:
        for i in $(SUBDIRS); do
                (cd $$i && make install);
        done



呼叫Make時可用的參數:
可以在shell呼叫make時直接用指令改變參數:
make CFLAGS="-g -O2"

若不想被改變,可在make file中加入:
override CFLAGS = -Wall -g

也可以指定要編譯的 target
make clean





A1:
A2:
A3:



參考資料:
今天的 Tetralet 又在唧唧喳喳了:http://tetralet.luna.com.tw/?op=ViewArticle&articleId=185