makefile execute another target - makefile

I have a makefile structured something like this:
all :
compile executable
clean :
rm -f *.o $(EXEC)
I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:
fresh :
rm -f *.o $(EXEC)
clear
make all
This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?

Actually you are right: it runs another instance of make.
A possible solution would be:
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : clean clearscr all
clearscr:
clear
By calling make fresh you get first the clean target, then the clearscreen which runs clear and finally all which does the job.
EDIT Aug 4
What happens in the case of parallel builds with make’s -j option?
There's a way of fixing the order. From the make manual, section 4.2:
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).
Hence the makefile becomes
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : | clean clearscr all
clearscr:
clear
EDIT Dec 5
It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.
log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)
install:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
command1 # this line will be a subshell
command2 # this line will be another subshell
#command3 # Use `#` to hide the command line
$(call log_error, "It works, yey!")
uninstall:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
....
$(call log_error, "Nuked!")

You already have a sequential solution which could be rewritten as:
fresh:
$(MAKE) clean
clear
$(MAKE) all
This is correct and a very safe approach.
Sequential target execution is possible in GNU make with a proper dependency graph:
fresh: _all
_all: _clear
Recipe for all
_clear: _clean
Recipe for clear
_clean:
Recipe for clean
The above rules define the following dependency graph: fresh <- _all <- _clear <- _clean which guarantees the following recipe execution order: Recipe for clean, Recipe for clear, Recipe for all.
Recipes can be shared with multiple targets using:
target1 target2 target…:
recipe1
Merging your script with the above concepts results in:
all _all :
compile executable
clean _clean :
rm -f *.o $(EXEC)
clear _clear :
clear
fresh: _all
_all: _clear
_clear: _clean
With syntactic sugar using chains.mk from https://github.com/pkoper/mk/ you can write:
all all#fresh :
compile executable
clean clean#fresh :
rm -f *.o $(EXEC)
clear clear#fresh :
clear
#fresh = clean clear all
include chains.mk
fresh: #fresh
Or better:
all: compile
#fresh = clean clear compile
include chains.mk
fresh: #fresh
compile compile#fresh:
compile executable
clear clear#fresh:
clear
clean clean#fresh:
rm -f *.o $(EXEC)

If you removed the make all line from your "fresh" target:
fresh :
rm -f *.o $(EXEC)
clear
You could simply run the command make fresh all, which will execute as make fresh; make all.
Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.

Related

Is there a way to run a job from another job in GNU Make? [duplicate]

I have a makefile structured something like this:
all :
compile executable
clean :
rm -f *.o $(EXEC)
I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:
fresh :
rm -f *.o $(EXEC)
clear
make all
This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?
Actually you are right: it runs another instance of make.
A possible solution would be:
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : clean clearscr all
clearscr:
clear
By calling make fresh you get first the clean target, then the clearscreen which runs clear and finally all which does the job.
EDIT Aug 4
What happens in the case of parallel builds with make’s -j option?
There's a way of fixing the order. From the make manual, section 4.2:
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).
Hence the makefile becomes
.PHONY : clearscr fresh clean all
all :
compile executable
clean :
rm -f *.o $(EXEC)
fresh : | clean clearscr all
clearscr:
clear
EDIT Dec 5
It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.
log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)
install:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
command1 # this line will be a subshell
command2 # this line will be another subshell
#command3 # Use `#` to hide the command line
$(call log_error, "It works, yey!")
uninstall:
#[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
....
$(call log_error, "Nuked!")
You already have a sequential solution which could be rewritten as:
fresh:
$(MAKE) clean
clear
$(MAKE) all
This is correct and a very safe approach.
Sequential target execution is possible in GNU make with a proper dependency graph:
fresh: _all
_all: _clear
Recipe for all
_clear: _clean
Recipe for clear
_clean:
Recipe for clean
The above rules define the following dependency graph: fresh <- _all <- _clear <- _clean which guarantees the following recipe execution order: Recipe for clean, Recipe for clear, Recipe for all.
Recipes can be shared with multiple targets using:
target1 target2 target…:
recipe1
Merging your script with the above concepts results in:
all _all :
compile executable
clean _clean :
rm -f *.o $(EXEC)
clear _clear :
clear
fresh: _all
_all: _clear
_clear: _clean
With syntactic sugar using chains.mk from https://github.com/pkoper/mk/ you can write:
all all#fresh :
compile executable
clean clean#fresh :
rm -f *.o $(EXEC)
clear clear#fresh :
clear
#fresh = clean clear all
include chains.mk
fresh: #fresh
Or better:
all: compile
#fresh = clean clear compile
include chains.mk
fresh: #fresh
compile compile#fresh:
compile executable
clear clear#fresh:
clear
clean clean#fresh:
rm -f *.o $(EXEC)
If you removed the make all line from your "fresh" target:
fresh :
rm -f *.o $(EXEC)
clear
You could simply run the command make fresh all, which will execute as make fresh; make all.
Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.

Makefile loses configuration

I'm pretty much a makefile novice so I don't even know the terminology I'm looking for. I'm trying to build the latest valgrind release alongside other 3rdparty tools my company uses. I basically have
../3rdparty/
/Makefile <- What gets called to recursively build everything
/valgrind/Makefile <- What I'm pasting below
/valgrind/valgrind-3.16.1/Makefile <- what gets configure'd
So I can go into ../3rdparty/valgrind/valgrind-3.16.0/ and call...
./configure --host=arm-linux-gnueabihf
make
...and have it succeed without issue. However, when I try to build it from the Makefile in ../3rdparty/valgrind I get errors due to configuration generated variables being lost. I can see it clean up everything, I can see configuration succeed, but when the make process starts I get warnings that aren't seen using the process above.
cc1: warning: switch -mcpu=cortex-a8 conflicts with -march=armv7ve switch
Which eventually leads to an error
<command-line>:0:5: error: expected identifier or ‘(’ before numeric constant pub_core_basics.h:78:12: note: in expansion of macro ‘ARM’
I basically copy pasted what is used for other 3rd party libs in our codebase and made changes where applicable....
include ../common.mak
VERSION=valgrind-3.16.1
all: configure build #install
configure: configure_$(TARGET)
configure_$(TARGET):
$(MAKE) distclean
#echo -e "\nConfiguring $(VERSION) for $(TARGET)...\n"
pushd $(VERSION)/ \
&& bash configure --host=${TARGET} \
&& popd
touch $#
#echo -e "\nConfiguration $(VERSION) complete for $(TARGET)...\n"
build: configure
$(MAKE) "-SC" $(VERSION)
install: build
$(MAKE) -SC $(VERSION) $#
# call folder's makefile targets verbatim
clean distclean:
test -f $(VERSION)/Makefile && $(MAKE) -SC $(VERSION) $# || :
rm -f configure_*
uninstall:
$(MAKE) -SC $(VERSION) $#
I'm guessing it's a one line thing, but I'd also be interested in any docs or websites that would be
useful. A lot of makefile tutorials go over the same super basic stuff.

Impose an order for the prerequisites of a target

I have a makefile snippet:
all: $(objects)
fresh: clean all
clean: ;rm $(objects)
Here, I want to ensure that when I do make fresh - clean should precede all.
But how can I make sure this, given that when I do make all, clean should not be made?
I can imagine that one way could be like this
fresh: clean
make all
Is it the right (or the only) way to solve this issue?
If you use GNU make:
all:
#echo $#
#sleep 1
#echo end $#
clean:
#echo $#
#sleep 1
#echo end $#
fresh:: clean
fresh:: all
.PHONY: clean fresh all
Please note the double colon after targets fresh! See the documentation:
The double-colon rules for a target are executed in the order they
appear in the makefile.
If you run make -j2 fresh it shows it works as expect:
clean
end clean
all
end all
But with fresh:: clean all doesn't work properly parallel (maybe unexpected).
With BSD make:
all:
#echo $#
#sleep 1
#echo end $#
clean:
#echo $#
#sleep 1
#echo end $#
fresh: clean all
#echo $#
.ORDER: clean all
.PHONY: clean all fresh
Note the line begin with .ORDER. It works well in parallelization too (see man make). Without parallelization the order of dependencies in line fresh: counts.
As you already suggest in your question, calling make recursively on the same makefile for the target all in a recipe whose prerequisite is clean:
# At the very beginning of the makefile
CURRENT_MAKEFILE := $(lastword $(MAKEFILE_LIST))
# ...
.PHONY: fresh
fresh: clean
$(MAKE) -f $(CURRENT_MAKEFILE) all
This imposes an order, since the target fresh depends on the prerequisite clean, clean's recipe will be executed before fresh's recipe, which in turn will execute all's recipe.
Note that I'm using here $(MAKE) instead of make for the recursion.

How to make a 'rebuild' rule out of 'build' and 'clean' in Makefile?

I have a Makefile containing rules for building a small project and cleaning it. For example:
CC:=gcc
LD:=gcc
SOURCES:=$(wildcard src/*.c)
OBJS:=$(SOURCES:src/%.c=build/%.o)
TARGET:=bin/program
all: $(TARGET)
$(TARGET): $(OBJS)
#mkdir -p bin
$(LD) $+ -o $#
build/%.o: src/%.c
#mkdir -p build
$(CC) $+ -c -o $#
clean:
rm -rf $(OBJS)
rm -rf $(TARGET)
rmdir bin
rmdir build
.PHONY: clean all
I am now interested in creating a rule rebuild which would perform clean and all in that order. I do not see how properly achieve the correct ordering.
The solutions I have seen are wrong to my knowledge.
rebuild: clean all
.PHONY: rebuild
Is naturally wrong, because there is no guarantee that dependencies are actually performed in the order of their appearance. all may execute before clean.
I have seen answers suggesting order-only dependencies, e.g.
rebuild: | clean all
.PHONY: rebuild
To my knowledge, this does not solve the problem. If you say a: | b c it means that a depends on b and c, but if b or c is taken, it does not force executing the a rule. It has nothing to do with ordering the dependencies.
The only option I see right now is launching a new instance of make, by having
rebuild : clean
make build
I would really like to avoid launching a new make instance for doing something simple like that!
I did some reasearch on SO. I have seen similar questions but no correct answer. To my knolwedge, making a target .PHONY or using order-only dependencies is not a solution.
First, it's not true that in a rule:
rebuild: clean all
that all could be built before clean when running serially (that is, without parallelism -j enabled). Make does always build prerequisites in the order they are listed in the makefile (there is a special case for the rule containing the recipe but that's not relevant here). When parallel builds are used then make still walks the dependency tree in the same order but because rules are built in parallel they may not be started in the same order.
However, you're right that this rule is not a great idea for other reasons (directory caching, etc.)
I recommend you use recursive make invocations to do this:
.PHONY: rebuild
rebuild:
$(MAKE) clean
$(MAKE) all
There is a way to do it without recursion; the price is a small amount of redundancy:
.PHONY: rebuild
$(TARGET) rebuild: $(OBJS)
#mkdir -p bin
$(LD) $+ -o $(TARGET) # note that I have replaced $# with $(TARGET)
rebuild: | clean
You can use MAKECMDGOALS to conditionally add a dependency between clean and all when the goal is rebuild. Something like this:
ifeq (rebuild,$(findstring rebuild,${MAKECMDGOALS}))
all: clean
rebuild: all
endif
Now, I don't really see the benefit of doing this in the makefile when there are other trivial and safe ways to do it (just "make clean && make all" might be a better option)

makefile parallel clean+compile issue

I have a simple makefile with 3 build rules:
clean (that cleans the .o)
debug (compiles my code with debgging stuff)
release (compiles my code with optimization stuff)
sometimes I want to switch between debug mode and release so I would issue this
make clean debug -j8
or
make clean release -j8
that has a drawback because while it's doing the clean stuff, the -j8 allows make to jump some command since the .o are still there Then those .o are removed by the clean rule and the compiler complains because it can't find those .o
I could do something like
make clean; make debug -j8
but since I use an odd makefile in another dir, the command becomes
make -C ../src -f nMakefile clean ; make -C ../src -f nMakefile -j8 release
that is more annoying. I was wondering if there was an hiddedn-guru-mode-rule that allows me to do it in one line
Hope it's clear enough...
I needed to solve this very same problem, and the solution I came up was to parse the MAKECMDGOALS for clean, and dispatch a shell command to do the actual cleaning work; RATHER than clean the build as a target. This way, any MAKECMDGOALS that include "clean" will clean the build as part of that build, first, sequentially, rather than clean running asynchronously as its own target.
-include $(deps)
bin/%.o : %.cpp
#mkdir -p $#D
g++ $(flags) $(includes) -MMD -c $< -o $#
.PHONY : clean
clean:
#echo rm -rf bin/
ifneq ($(filter clean,$(MAKECMDGOALS)),)
$(shell rm -rf bin/)
endif
As I stated above, the normal practice is to have different sub directories for the object files. As you are running in parallel I would think you need to enforce serial execution so that clean is completed before release. One way of doing it could be:
clean_release: clean
+#$(MAKE) -s --no-print-directory release
or if you prefer
clean_release:
+#$(MAKE) -s --no-print-directory clean && $(MAKE) -s --no-print-directory release

Resources