Best practice for writing reusable code

What is the best practice for writing reusable code in Makefile s?

Suppose I have a Makefile:

 .PHONY: all task01-all task01-clean task01-run task02-all task02-clean task02-run all: task01-all task02-all ############################################################################### task01-all: task01-clean task01 task01-run task01-clean: rm task01 task01.{exi,o} -f task01: compiler task01.ext -O2 --make task01-run: ./task01 ############################################################################### task02-all: task02-clean task02 task02-run task02-clean: rm task02 task02.{exi,o} -f task02: compiler task02.ext -O2 --make task02-run: ./task02 

Now I want to add a new family of tasks (task03), and I need to copy the whole package, make s/02/03/ .PHONY s/02/03/ for it and add them to the .PHONY section - it's noisy, disgusting and just not right.

How can i avoid this? Can I redefine all tasks using templates to have a good mechanism for adding a new group of tasks on one line?

+6
source share
2 answers

Since the question is how to write reusable code in Make files, I will give an example of using template rules in GNU Make (it seems that this is what you use, since you mention the .PHONY target), However, if you do not use any or dependency control, it might be easier to do this with a shell script - something like:

 #!/bin/sh TASKS="task01 task02 task03" for i in $TASKS; do rm $i $i.ext $io -f; compiler $i.ext -O2 --make; ./$i; done 

But, to extend the principle of Make, we have another problem to solve. Lines of the form:

 task01-all: task01-clean task01 task01-run 

Do not tell Make in what order to create the preconditions - you just need to ensure that they all run before task01-all is built. Instead, each step to start should depend on the step in front of it. Something like that:

 TASKS=task01-run task02-run task03-run .PHONY: all $(TASKS) $(TASKS:run=clean) all: $(TASKS) $(TASKS:run=clean): %-clean: rm $* $*.ext $*.o -f %: %.ext | %-clean compiler $< -O2 --make $(TASKS): %-run: % ./$< 

Rules with % are called "template rules" and they are a great tool to avoid re-writing the same rule several times for different purposes. One caveat is that Make does not usually check template rules for the .PHONY target; we tell Make to do this explicitly, a prefix of these rules with a list of goals and a second colon (for example, $(TASKS): .

Since task01-run needs task01 to work, we make %-run dependent on % . In addition, your Makefile shows that you want to run cleanup every time, so we make % dependent on %-clean . Since %-clean does not actually produce any output, we make this order-only dependency. Make will not look for a timestamp or anything else; it first runs the %-clean rule anytime it is necessary to run the % rule. Dependencies "Order only" are placed after | :

 %: %.ext | %-clean 

It is worth mentioning that one of the strengths is that it can save time by not repeating work that does not need to be repeated - i.e. it only triggers a rule if the dependencies are newer than the target. Thus, you can leave the dependency on %-clean , which will cause make to run only compiler $< -O2 --make if %.ext newer than % :

 %: %.ext compiler $< -O2 --make 

Then you can add a rule only to run all %-clean targets:

 .PHONY: all $(TASKS) $(TASKS:run=clean) clean clean: $(TASKS:run=clean) 

Last thing: I use some special variables in recipes. $@ means the goal is being built. $< denotes the first dependency. $* denotes the "core" of the pattern rule (that is, the part matched by % ).

+9
source

It looks like what I'm looking for:

 ALLS=task01-all task02-all BUILDS=${ALLS:-all=-build} CLEANS=${ALLS:-all=-clean} RUNS=${ALLS:-all=-run} .PHONY: all $(ALLS) $(CLEANS) $(BUILDS) $(RUNS) all: $(ALLS) ############################################################################### $(ALLS): $(CLEANS) $(BUILDS) $(RUNS) $(CLEANS): rm ${@:-clean=} ${@:-clean=}.{ext,o} -f $(BUILDS): compiler ${@:-build=}.ext -O2 --make $(RUNS): ./${@:-run=} 
+2
source

All Articles