0
votes

I want a rule to behave as a function, that could for instance gzip all my temporary targets. I wrote those rules in a makefile:

%.file0:            
         touch $@    

%.file1: %    
         touch $@    

%.file2: %          
         touch $@    

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

I can call

$ 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

My last target will successfully be gzipped. I can then gzip dotted.file.file0.file1 before:

$ 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

That file will also be gzipped before being given to the rule %.file2. But I can't gzip several targets:

$ 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.  

How can I do that, i.e. applying the %.gz rule on several targets?


EDIT 1

From another point of view, I tried rewriting my rules this way:

%.file0:                    
         touch $@            

%.file1: %.gz             
         touch $@            

%.file2: %.gz             
         touch $@            

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

Then I call make:

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

I expect/wish gnu-make to execute the rules this order:

 - 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

However if I ask only one rule to apply %.gz, it will work this single time.

The makefile:

%.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 $@ 

The command:

$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
1
Is there some reason you want these compound file names (e.g. bar.file0.file1.file2 instead of bar.file2)?Beta
The only reaon is to keep track of which steps (rules) have been applied to the files, this is informative. Is that why make won't apply the same rule twice?kaligne
You're begging the question. Building bar.file0.file1.file2 can probably be done with some effort and cleverness; building bar.file2 is much easier, and the name conveys exactly the same information.Beta

1 Answers

0
votes

The problem with your first version is that you would need static pattern rules, not implicit rules. The following does the job with GNU make, using shell calls to awk to build explicit lists of *.file1, *.file2 and *.gz targets from the specified make goals. It also uses foreach-eval-call combinations to instantiate as many rules (without recipes) as needed to express explicitly all dependencies. I must admit that this style of Makefile is rather unusual but your problem is unusual too...

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 $@

And:

$ 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.

Note: as all intermediates are not implicit any more, they are not automatically deleted, just as if they were declared PRECIOUS in your first version. This is the reason why there is no rm ... any more in the outputs.