簡體   English   中英

如何將 makefile 目標更改為函數?

[英]How do I change a makefile target into a function?

我正在尋找一種干凈的方法來將我在 makefile 中創建的一些目標聲明更改為更具功能性的類型聲明,在該聲明中我能夠傳遞變量並且輸出將保持一致。

例如:

default: clean run1 run2

run1:
    for entity in $(FLIST_01); do \
        echo $entity ; \
    done

run2:
    for entity in $(FLIST_02); do \
         echo $entity ; \
    done

理想情況下,我想刪除重復的運行目標聲明並且只有 1 個。

FLIST_01 = my_flist.txt
FLIST_02 = other.txt

default: clean run

run:
    $(run_func $(FLIST_01))
    $(run_func $(FLIST_02))

如何在make創建自定義函數來執行 run_func 應該執行的操作(傳遞給它的文件列表變量的 for 循環讀取?

更新:

我的嘗試是這樣的:

run:
    runfunc() { \
        echo "test" \
        for entity in $1; do \
            echo $(entity); \
        done \
    }

    runfunc $(FLIST_01)
    runfunc $(FLIST_02)

但是我在 do 行出現語法錯誤:syntax error near unexpected token `do'

通常,在 Makefile 中維護 shell 函數並不容易。 主要原因:

  • 每個 shell '$' 需要轉義為 '$$'
  • $(...) 構造在 shell 和 Make 中具有不同的含義。
  • Makefile 中的每一行都由新的 shell 實例執行,這意味着變量分配、函數定義等在行之間不共享。

考慮幾個選項:編寫 shell helper 腳本(推薦),使用 make 用戶定義的函數或將函數內聯到 Make 中。 對於“回聲”的具體情況,可能可以使用其他方法之一,請參閱“替代解決方案”

從個人經驗來看,對於任何重要的函數,最好使用 shell 幫助腳本。 它們更容易開發和測試。

使用 Shell 幫助腳本

編寫一個小助手腳本“print-list.sh”

#! /bin/bash
echo "test" \
for entity ; do
    echo $entity
done

然后從目標調用它

FLIST_01 = a.txt b.txt c.txt

t1:
        print-list.sh ${FLIST_01)

內聯shell函數

如上所述,將 shell 函數嵌入 Makefile 需要匹配有關引用、轉義等的 make 規則。調試腳本也需要付出很多努力,因為在此設置中錯誤消息非常神秘。

請注意,必須在使用該命令的每個操作行之前包含 RUNFUNC。

RUNFUNC = runfunc() { \
        echo "test" ; \
        for entity in $$*; do \
            echo $$entity; \
        done \
    } ;

FLIST_01 = a.txt b.txt c.txt

t2:
        $(RUNFUNC) runfunc ${FLIST_01)

請注意,該函數可以編寫為一個 liner,或者使用 'define'/'endef' 來簡化行尾轉義。

使用 make 用戶定義函數

可以使用 make 創建簡單的函數。 此選項需要經驗和時間。

UFUNC = \
        echo "test" ; \
        for entity in $1; do \
            echo $$entity ; \
        done


FLIST_01 = a.txt b.txt c.txt

t3:
        $(call UFUNC, ${FLIST_01))

替代方案

最后一個選項是使用現有的構造(假設該函數的唯一目標是將空格分隔的文件列表轉換為換行符

t4:
        echo ${FLIST_01} | tr " " "\n"

首先,除非您指定.ONESHELL ,否則目標中的命令將在單獨的子 shell 中運行。 因此,您的runfunc()聲明將在聲明該函數的 shell 中運行,然后退出。 下一條語句將在一個新的 shell 中運行,該 shell 對聲明的函數一無所知,然后基本上被遺忘了。

其次,

echo "test" \
for entity in $1;

將被make擴展為

echo "test" for entity in ;

這顯然包含多個錯誤。 你的意思是

echo "test"; \
for entity in $$1;

正確地將美元符號傳遞給外殼。 但總的來說,我會說你的方法是有缺陷的。 您可以將其重構為您最初希望的make函數;

define run
   for entity in $(1); do \
       echo $$entity; \
   done
endef

現在你可以這樣稱呼它

run:
    $(call run,$(FLIST_01))
    $(call run,$(FLIST_02))

但是這個特定的循環可以很容易地用一個 shell 語句替換。

run:
    printf '%s\n' $(FLIST_01)
    printf '%s\n' $(FLIST_02)

另一個雙彭北。

首先,我們要擴大$FLIST_01的目標RUN1FLIST_02為目標RUN2。 一個干凈的方法可能是:

FLIST_run1 := a b c
FLIST_run2 := 1 2 3

.PHONY: run1 run2
run1 run2:
    for entity in ${FLIST_$@}; do echo $entity; done

對於稍微模糊的解決方案,我們注意到您嘗試做的事情可以用make的 noddy 模式匹配來描述:

.PHONY: run1 run2
run%: run1 run2:
    for entity in ${FLIST_$*}; do echo $entity; done

這里我們使用靜態模式規則。 在配方中, $*擴展為與模式中的%匹配的任何內容。 這個解決方案對我黃疸的眼睛來說看起來很干凈。

我敦促你尋找慣用的make而不是慣用的外殼。 您的 makefile 幾乎總是會為此感謝您。

在第一種情況下,保留您的兩個運行目標,而不是將它們合並到同一個配方中。

.PHONY: run1 run2
run%: run1 run2:
    for entity in ${FLIST_$*}; do echo $entity; done

.PHONY: run
run: run1
run: run2
run: ; echo $@ Success

在這里,你的實例運行首先進行了RUN1的配方,那么幾招RUN2,終於為運行的配方。

這有什么好處嗎? 當然。 如果你運行make -j2運行RUN1RUN2命令將並行運行。 make的全部意義。

最后一個問題是擺脫 shell 循環,並用單獨的命令替換它。 這樣, make將為您檢查echo的退出代碼,如果合適,甚至可以並行運行它們。 當然,在這個 noddy 示例中沒有幫助,但它們可以是例如編譯命令。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM