Makefile的一種寫法

適用於目錄下有較多源文件的情況

makefile 式列,例如:

CC = gcc
LD = gcc
TARGET = test
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

all:$(TARGET)
$(TARGET):$(OBJS)
    $(LD) -o $@ $^

%o:%c
    $(CC) -c -o $@ $^

.PHONY:clean
clean:
    rm -f $(OBJS) $(TARGET)

使用到了GNU Make 裏的 ‘wildcard’ 和 ‘patsubst’ 函數, ‘wildcard’ 功能是展開成一列所有符合由其參數描述的文件名,文件間以空格間隔。

SRCS = $(wildcard *.c)

這行會產生一個以 ‘.c’ 結尾的文件的列表,然後存入變量 SRCS 裏。

函數 ‘patsubst’(patten substitude)。有三個參數,第一個是一個需要匹配的式樣,第二個表示用什麼來替換它,第三個是一個需要被處理的由空格分隔的字列。

OBJS = $(patsubst %.c, %.o, $(SRCS))

這行將處理所有在 SRCS中的元素,如果它的結尾是 ‘.c’ ,就用 ‘.o’ 把 ‘.c’取代。

——————-華麗的分割線——————————————-
後綴依賴:
在makefile中使用

.SUFFIXES: .c .o

來說明.c和.o是後綴。
我們可以使用後綴依賴的方式,比如:

CC = gcc
.SUFFIXES: .c .o
.c.o:
    $(CC) -c -o $@ $^
#If Target is the SO LIB , here should add '-fPIC'


# helloworld is a binary file
helloworld: test.o
    echo $@
    $(CC) -o $@ $^

test.o: test.c

我們定義.c和.o爲後綴。並有後綴依賴關係.c.o:。前者爲前提,後者爲目標。(注意,與一般的依賴關係順序不同)
上面的test.o和test.c有依賴關係,但沒有操作。make會發現該依賴關係符合.c.o的後綴依賴,並執行該後綴依賴後面的操作。
如果項目很大型的時候,後綴依賴非常有用。符合後綴依賴的文件往往有類似的操作,我們可以將這些操作用後綴依賴表示,而避免重複輸入。

——————-華麗的分割線——————————————-
Makefile選項CFLAGS,LDFLAGS,LIBS

CFLAGS 表示用於 C 編譯器的選項,
CXXFLAGS 表示用於 C++ 編譯器的選項。
這兩個變量實際上涵蓋了編譯和彙編兩個步驟。

CFLAGS: 指定頭文件(.h文件)的路徑,如:CFLAGS=-I/usr/include -I/path/include。同樣地,安裝一個包時會在安裝路徑下建立一個include目錄,當安裝過程中出現問題時,試着把以前安裝的包的include目錄加入到該變量中來。

LDFLAGS:gcc 等編譯器會用到的一些優化參數,也可以在裏面指定庫文件的位置。用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安裝一個包都幾乎一定的會在安裝目錄裏建立一個lib目錄。如果明明安裝了某個包,而安裝另一個包時,它愣是說找不到,可以抒那個包的lib路徑加入的LDFALGS中試一下。

LIBS:告訴鏈接器要鏈接哪些庫文件,如LIBS = -lpthread -liconv

簡單地說,LDFLAGS是告訴鏈接器從哪裏尋找庫文件,而LIBS是告訴鏈接器要鏈接哪些庫文件。不過使用時鏈接階段這兩個參數都會加上,所以你即使將這兩個的值互換,也沒有問題。

有時候LDFLAGS指定-L雖然能讓鏈接器找到庫進行鏈接,但是運行時鏈接器卻找不到這個庫,如果要讓軟件運行時庫文件的路徑也得到擴展,那麼我們需要增加這兩個庫給”-Wl,R”:

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

鏈接選項和路徑

現代連接器在處理動態庫時將鏈接時路徑(Link-time path)和運行時路徑(Run-time path)分開,用戶可以通過-L指定連接時庫的路徑,通過-R(或-rpath)指定程序運行時庫的路徑,大大提高了庫應用的靈活性。比如我們做嵌入式移植時#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉編譯好的zlib庫),將target編譯好後我們只要把zlib庫拷貝到開發板的系統默認路徑下即可。或者通過-rpath(或-R )、LD_LIBRARY_PATH指定查找路徑。

鏈接器ld的選項有 -L,-rpath 和 -rpath-link,看了下 man ld,大致是這個意思:

-L: “鏈接”的時候,去找的目錄,也就是所有的 -lFOO 選項裏的庫,都會先從 -L 指定的目錄去找,然後是默認的地方。編譯時的-L選項並不影響環境變量LD_LIBRARY_PATH,-L只是指定了程序編譯連接時庫的路徑,並不影響程序執行時庫的路徑,系統還是會到默認路徑下查找該程序所需要的庫,如果找不到,還是會報錯,類似cannot open shared object file。

-rpath-link:這個也是用於“鏈接”的時候的,例如你顯示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,後者你並沒有指定,而是 FOO.so 引用到它,這個時候,會先從 -rpath-link 給的路徑裏找。

-rpath: “運行”的時候,去找的目錄。運行的時候,要找 .so 文件,會從這個選項裏指定的地方去找。對於交叉編譯,交叉編譯鏈接器需已經配置 –with-sysroot 選項才能起作用。也就是說,-rpath指定的路徑會被記錄在生成的可執行程序中,用於運行時查找需要加載的動態庫。-rpath-link 則只用於鏈接時查找。

鏈接搜索順序

直接man ld。The linker uses the following search paths to locate required shared libraries:

1.  Any directories specified by -rpath-link options.
2.  Any directories specified by -rpath options.  The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.
3.  On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of the environment variable "LD_RUN_PATH".
4.  On SunOS, if the -rpath option was not used, search any directories specified using -L options.
5.  For a native linker, the search the contents of the environment variable "LD_LIBRARY_PATH".
6.  For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH" entries exist.
7.  The default directories, normally /lib and /usr/lib.
8.  For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that file.
    If the required shared library is not found, the linker will issue a warning and continue with the link.

Makefile 中:= ?= += =的區別

“=”是最基本的賦值。make會將整個makefile展開後,再決定變量的值。也就是說,變量的值將會是整個makefile中最後被指定的值。

 x = foo
 y = $(x) bar
 x = xyz

在上例中,y的值將會是 xyz bar ,而不是 foo bar 。

“:=”是覆蓋之前的值。變量的值決定於它在makefile中的位置,而不是整個makefile展開後的最終值。

 x := foo
 y := $(x) bar
 x := xyz

在上例中,y的值將會是 foo bar ,而不是 xyz bar 了。
“?=” 是如果沒有被賦值過就賦予等號後面的值
“+=” 是添加等號後面的值

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