天天看點

Makefile

https://blog.atime.me/note/makefile.html

總結GNU Make的一些基礎知識和技巧,以下内容均基于Ubuntu 14.04 x86_64平台的GNU Make 3.81。

變量

變量類型

make支援兩種風格的變量定義方式:

  1. 遞歸擴充變量(recursively expanded variables)
    • 使用

      =

      或define指令定義
    • CFLAGS = $(CFLAGS) -O

      會導緻無限遞歸
    • 變量每次被展開的時候,所使用的函數都會重新被執行,會降低make的效率,更嚴重的是,wildcard等函數會傳回預料之外的結果。
  2. 簡單擴充變量(simply expanded varialbes)
    • :=

      ::=

      定義
    • 在變量定義的時候展開一次

shell變量

shell變量應使用

@

轉義,比如變量

${var}

要改為

$${var}

自動變量

下面是一些常用的自動變量

  • $<

    : 第一個依賴
  • $@

    : 目标
  • $?

    : 修改時間在目标之後的依賴
  • $^

    : 所有的依賴
  • $|

    : 所有的order-only依賴

預定義變量

Makefile中預定義了一些變量,通常用于Implicit Rules中,詳細清單見Implicit Variables。其中常用的有:

  • CC

    : C編譯器,預設是

    cc

  • CXX

    : C++編譯器,預設是

    g++

  • CFLAGS

    : 傳遞給C編譯器的編譯參數
  • CXXFLAGS

    : 傳遞給C++編譯器的編譯參數
  • LDFLAGS

    : 傳遞給編譯器的連結參數

函數和指令

wildcard

wildcard函數和直接使用通配符

*

的差別在于,wildcard函數可以在變量定義的時候立即展開(即搜尋相應的比對項),而

*

隻有在規則中才會被展開。1

subst

sust函數用于替換字元串,格式是

$(subst from,to,text)

,其中from是要被替換的子串,to是要替換成的子串,text是需要被替換的字元串。注意from和to之間的逗号左右不要随意添加空格,否則也會被視為需要替換的内容。

patsubst

patsubst函數可以比對并保留第一個沒被

\

轉義的

%

字元,然後在替換成的字元串裡使用,比如

$(patsubst %.css,%.min.css,hello.css)

替換的結果就是

hello.min.css

,同樣的,逗号左右不要随意添加空白字元。類似的功能還可以用Substitution References實作,文法更簡潔一些。

filter

filter函數可以按一定的規則過濾目标,然後傳回符合規則的資料。

規則

order-only依賴

a: b | c
    command
      

上面的例子中,a是目标,b是正常依賴,c是order-only依賴。當a存在時,即便c的修改時間晚于a,該規則也不會更新a。

order-only依賴定義于規則的右側,與正常依賴用

|

隔開。當目标存在時,不管其是否因order-only依賴而過期,均不更新目标。

static pattern rules

當有大量類似的目标時,static pattern rules會很有用,格式如下

targets ...: target-pattern: prereq-patterns ...
    recipe
    ...
      

用下面的一個簡單例子說明

a.o b.o c.o: %.o: %.c
    gcc -c -o $@ $<
      

這裡targets是

a.o b.o c.o

,target pattern是

%.o

,prereq-patterns(隻有一個)是

%.c

。target pattern首先從targets裡比對出一個個的目标,然後按照類似于patsubst的方式替換prereq-patterns中的

%

字元,生成相應的依賴。在規則的指令中,可以像普通的規則一樣使用

$<

(第一個依賴)和

$@

(目标)等自動變量。

上面的規則可以拆成幾條簡單的規則:

a.o:a.c
    gcc -c -o $@ $<

b.o:b.c
    gcc -c -o $@ $<

c.o:c.c
    gcc -c -o $@ $<
      

或者使用字元串替換,更友善一些

source = ${wildcard *.c}
objects = ${source:%.c=%.o}

all: ${objects}

${objects}: %.o: %.c
    gcc -c -o $@ $<
      

其它

特殊的符号

  • @

    : 用于規則中的指令之前,可以在make時隻顯示指令的輸出而不顯示指令的内容,比如
    all:
        @echo hello
          
    隻輸出hello,不會輸出

    echo hello

    這條指令的内容。

傳遞變量

可以在指令行裡設定Makefile的變量值,對如下的makefile

TARGET=test
create:
    mkdir -p ${TARGET}
      

可以通過如下的指令修改TARGET的值:

make create TARGET=another
      

.PHONY僞目标

盡可能使用

.PHONY

标示所有的僞目标,以避免潛在的問題。

常見的一例問題如下:

debug:
    mkdir -p debug
    cd debug && make -f ../Makefile
      

make debug

指令時可能會提示

Nothing to be done for debug

,即debug目标已是最新。然而debug是僞目标,理論上每次執行都應該運作才對。問題出在debug目标的名稱和本地的debug檔案夾同名,使用

.PHONY

辨別debug為僞目标即可解決此問題。

.PHONY: debug
      

不要濫用空白字元

  1. objects = a.o b.o # object files

    ,這裡${objects}變量的值是`a.o b.o '(包含後面的一個空格)。
  2. str = ${subst abc, 123,abcde}

    ,這裡${str}變量的值是` 123de'(包含開頭的一個空格)。
  3. str = ${subst abc ,123,abcde}

    ,這裡${str}變量的值還是

    abcde

    ,因為要替換的子串

    abc

    (包含最後的空格)沒有找到。

例子

假設有以下的場景,在

src

目錄下有許多css檔案,我們需要用yui-compressor将其壓縮并輸出到

output

目錄,壓縮後css的檔案名不變,檔案名字尾由

.css

改為

.min.css

,可以如下定義makefile

# 查找所有的css源檔案
CSS_SOURCE_FILES = ${wildcard src/*.css}
# 建構目标名
CSS_OUTPUT_FILES = ${subst src/,output/,${patsubst %.css,%.min.css,${CSS_SOURCE_FILES}}}

all: ${CSS_OUTPUT_FILES}

${CSS_OUTPUT_FILES}: output/%.min.css: src/%.css
    @[ -d output ] || mkdir -p output
    yui-compressor -o $@ $<

clean:
    rm -rf output

.PHONY: all clean
      

閱讀資料

  • GNU Make自動變量,函數和指令索引
  • GNU Make函數說明和清單
  • Passing additional variables from command line to make

腳注

  1. GNU Make Manual: The Function wildcard,引用于2014-05-12。