[英]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 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
的目標RUN1和FLIST_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運行則RUN1和RUN2命令將並行運行。 make的全部意義。
最后一個問題是擺脫 shell 循環,並用單獨的命令替換它。 這樣, make將為您檢查echo的退出代碼,如果合適,甚至可以並行運行它們。 當然,在這個 noddy 示例中沒有幫助,但它們可以是例如編譯命令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.