【问题标题】:Apply gnu-make rule as a function将 gnu-make 规则应用为函数
【发布时间】:2016-12-04 08:36:58
【问题描述】:

我希望一个规则像一个函数一样运行,例如 gzip 我所有的临时目标。我在 makefile 中编写了这些规则:

%.file0:            
         touch $@    

%.file1: %    
         touch $@    

%.file2: %          
         touch $@    

%.gz: %    
         echo "zipping to $@" 
         touch $@  

我可以打电话

$ make -n dotted.file.file0.file1.file2.gz -f makefile
touch dotted.file.file0
touch dotted.file.file0.file1
touch dotted.file.file0.file1.file2
echo "zipping to dotted.file.file0.file1.file2.gz"
rm dotted.file.file0 dotted.file.file0.file1.file2 dotted.file.file0.file1

我的最后一个目标将被成功压缩。然后我可以 gzip dotted.file.file0.file1 之前:

$ make -n dotted.file.file0.file1.gz.file2 -f makefile
touch dotted.file.file0                                                   
touch dotted.file.file0.file1                                             
echo "zipping to dotted.file.file0.file1.gz"                              
touch dotted.file.file0.file1.gz.file2                                    
rm dotted.file.file0 dotted.file.file0.file1.gz dotted.file.file0.file1

该文件在被赋予规则 %.file2 之前也会被 gzip 压缩。但我不能 gzip 几个目标:

$ make -n dotted.file.file0.file1.gz.file2.gz -f makefile
make: *** No rule to make target `dotted.file.file0.file1.gz.file2.gz'.  Stop.  

我怎样才能做到这一点,即在多个目标上应用 %.gz 规则?


编辑 1

从另一个角度来看,我尝试用这种方式重写我的规则:

%.file0:                    
         touch $@            

%.file1: %.gz             
         touch $@            

%.file2: %.gz             
         touch $@            

%.gz: %                     
         echo "zipping to $@"
         touch $@ 

然后我调用make:

$ make -n dotted.file.file0.file1.file2 -f makefile
make: *** No rule to make target `dotted.file.file0.file1.file2'.  Stop.

我希望/希望 gnu-make 以这种顺序执行规则:

 - ask for the file dotted.file.file0.file1.file2
 - go to rule %.file2
 - ask for the dependency dotted.file.file0.file1.gz
 - go to rule %.gz
 - as for the dependency dotted.file.file0.file1
 - go to rule %.file1
 - as for the dependency dotted.file.file0.gz
 - go to rule %.gz
 - as for the dependency dotted.file.file0
 - go to rule %.file0
 - create file dotted.file.file0

但是,如果我只要求一条规则应用%.gz,那么这一次就可以了。

生成文件:

%.file0:                    
         touch $@            

%.file1: %.gz # !!! Notice I left this single dependency to the rule %.gz           
         touch $@            

%.file2: %  ## !!! Notice I removed the .gz here             
         touch $@            

%.gz: %                     
         echo "zipping to $@"
         touch $@ 

命令:

$make -n dotted.file.file0.file1.file2 -f makefile 
touch dotted.file.file0                      
touch dotted.file.file0.file1                      
echo "zipping to dotted.file.file0.file1.gz"          
touch dotted.file.file0.file1.gz                      
touch dotted.file.file0.file1.file2                      
rm dotted.file.file0.file1.gz dotted.file.file0 dotted.file.file0.file1

【问题讨论】:

  • 是否有某些原因需要这些复合文件名(例如 bar.file0.file1.file2 而不是 bar.file2)?
  • 唯一的原因是跟踪哪些步骤(规则)已应用于文件,这是有用的。这就是为什么 make 不会两次应用相同的规则吗?
  • 你在乞求这个问题。构建bar.file0.file1.file2 可能需要一些努力和聪明才智来完成;构建bar.file2 要容易得多,而且名称传达的信息完全相同。

标签: makefile gnu-make


【解决方案1】:

您的第一个版本的问题是您需要静态模式规则,而不是隐式规则。下面使用 GNU make 完成这项工作,使用 shell 调用 awk 来构建来自指定 make 目标的 *.file1*.file2*.gz 目标的显式列表。它还使用foreach-eval-call 组合来实例化尽可能多的规则(没有配方),以明确表达所有依赖关系。我必须承认这种风格的 Makefile 很不寻常,但你的问题也很不寻常......

SUFFIXES    := file1 file2 gz

# For goal $(1) and suffix $(2), build the list of all possible stems <S> such
# that $(1) matches the ^<S>\.$(2)(\..*)? regular expression. Concatenate this
# list to the $(2) make variable.
define SPLIT_rule
$(2)    += $$(shell echo $(1) | awk -F.$(2) '{for(i=1;i<=NF;i++){for(j=1;j<=i;j++)printf("%s",$$$$j);printf(".$(2) ")}}')
endef

# For goal $(1), instantiate SPLIT_rule for each suffix.
define GOAL_rule
$(foreach suffix,$(SUFFIXES),$(eval $(call SPLIT_rule,$(1),$(suffix))))
endef

$(foreach goal,$(MAKECMDGOALS),$(eval $(call GOAL_rule,$(goal))))

# For suffix $(1), instantiate the static pattern rule.
define SUFFIX_rule
$$($(1)): %.$(1): %
endef

$(foreach suffix,$(SUFFIXES),$(eval $(call SUFFIX_rule,$(suffix))))

%.file0:
    touch $@

%.file1: %
    touch $@

%.file2: %
    touch $@

%.gz: %
    echo "zipping to $@"
    touch $@

还有:

$ goal1=dotted.file.file0.file1.file2.gz
$ make -n $goal1
touch dotted.file.file0
touch dotted.file.file0.file1
touch dotted.file.file0.file1.file2
echo "zipping to dotted.file.file0.file1.file2.gz"
touch dotted.file.file0.file1.file2.gz

$ goal2=dotted.file.file0.file1.gz.file2
$ make -n $goal2
touch dotted.file.file0
touch dotted.file.file0.file1
echo "zipping to dotted.file.file0.file1.gz"
touch dotted.file.file0.file1.gz
touch dotted.file.file0.file1.gz.file2

$ goal3=dotted.file.file0.file1.gz.file2.gz
$ make -n $goal3
touch dotted.file.file0
touch dotted.file.file0.file1
echo "zipping to dotted.file.file0.file1.gz"
touch dotted.file.file0.file1.gz
touch dotted.file.file0.file1.gz.file2
echo "zipping to dotted.file.file0.file1.gz.file2.gz"
touch dotted.file.file0.file1.gz.file2.gz

$ goal4=dotted.file.file0.file1.file2
$ make -n $goal4
touch dotted.file.file0
touch dotted.file.file0.file1
touch dotted.file.file0.file1.file2

$ all="$goal1 $goal2 $goal3 $goal4"
$ make -n $all
touch dotted.file.file0
touch dotted.file.file0.file1
touch dotted.file.file0.file1.file2
echo "zipping to dotted.file.file0.file1.file2.gz"
touch dotted.file.file0.file1.file2.gz
echo "zipping to dotted.file.file0.file1.gz"
touch dotted.file.file0.file1.gz
touch dotted.file.file0.file1.gz.file2
echo "zipping to dotted.file.file0.file1.gz.file2.gz"
touch dotted.file.file0.file1.gz.file2.gz
make: 'dotted.file.file0.file1.file2' is up to date.

注意:由于所有中间体不再是隐式的,它们不会被自动删除,就像它们在您的第一个版本中被声明为 PRECIOUS 一样。这就是输出中不再有rm ... 的原因。

【讨论】:

    猜你喜欢
    • 2017-09-26
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-02
    • 2015-08-28
    相关资源
    最近更新 更多