How can I use .NOTPARALLEL in makefile only on specific targets?
Create a target specifying the four targets that can be executed in parallel & include this and last_label
in the all
target:
intermediate: label1 label2 label3 label4
all:
$(MAKE) intermediate
$(MAKE) last_label
This would execute the targets specified within intermediate
in parallel, but intermediate
and last_label
would be forced consecutively.
(Note that the leading space before $(MAKE)
is a TAB
character.)
How can I use .NOTPARALLEL in makefile only on targets from list?
One possible approach is to use a synchronization on a file level within the recipe. It's not of course the best (as it will still consume make
's job), but should not overload the system by running n multiple-threaded programs.
Sample Makefile:
$ cat Makefile
all: $(foreach number,1 2 3 4 5 6 7 8 9 10,$(addsuffix /$(number),slow_target fast_target))
slow_target/%:
@echo $@
@python3 -c 'import time; time.sleep(5)'
fast_target/%:
@( \
flock 9 || exit 1; \
echo $@; \
python3 -c 'import time; time.sleep(5)' \
) 9> fast_target.lock
Output:
$ make -j8 | ts
May 19 23:45:06 slow_target/1
May 19 23:45:06 slow_target/2
May 19 23:45:06 fast_target/1
May 19 23:45:06 slow_target/3
May 19 23:45:06 slow_target/4
May 19 23:45:11 fast_target/2
May 19 23:45:11 slow_target/5
May 19 23:45:11 slow_target/6
May 19 23:45:11 slow_target/7
May 19 23:45:16 fast_target/3
May 19 23:45:16 slow_target/8
May 19 23:45:16 slow_target/9
May 19 23:45:21 fast_target/4
May 19 23:45:21 slow_target/10
May 19 23:45:26 fast_target/5
May 19 23:45:31 fast_target/6
May 19 23:45:36 fast_target/7
May 19 23:45:41 fast_target/8
May 19 23:45:46 fast_target/9
May 19 23:45:51 fast_target/10
Note that slow_targets are started in parallel, but only one fast_target at a time is started.
EDIT
flock
acquires a lock on a given file or a given file descriptor. The lock by default is exclusive, meaning that only one flock
call can continue and all others will block until the lock is released. Therefore if multiple targets are invoked in parallel, only one flock
will proceed, while others will wait till the lock is released.
The statement in the recipe is based on flock
's manual. It opens up file descriptor 9 on a fast_target.lock
file for a given block. At the beginning flock
acquires an exclusive lock on this descriptor (thus fast_target.lock
file), which will automatically be released when the descriptor is closed (that is when the block with redirection ends). When that happens, one of other flock
s will proceed, effectively allowing only one fast_target
recipe execution.
EDIT 2
If all fast_targets are known and always executed, they can be dynamically scheduled in a sequence, like so:
$ cat Makefile2
SLOW_TARGETS := $(addprefix slow_target/,1 2 3 4 5 6 7 8 9 10)
FAST_TARGETS := $(addprefix fast_target/,1 2 3 4 5 6 7 8 9 10)
all: $(SLOW_TARGETS) $(FAST_TARGETS)
slow_target/%:
@echo $@
@python3 -c 'import time; time.sleep(5)'
fast_target/%:
@echo $@: $^
@python3 -c 'import time; time.sleep(5)'
$(foreach target,$(FAST_TARGETS), \
$(eval evaluated_targets += $(target)) \
$(eval next_target := $(word 2,$(wordlist $(words $(evaluated_targets)),$(words $(FAST_TARGETS)),$(FAST_TARGETS)))) \
$(eval $(if $(next_target),$(next_target): $(target))) \
)
This will iterate the list of known fast_targets and for every target it will define a dependency to the previous one. This will result in a strict sequence to run, so none of them will be run in parallel. Additionally it will not block make
jobs, so the executors will be available for other targets instead of being blocked on the flock
.
$ make -f Makefile2 -j8 | ts
May 20 22:38:35 slow_target/1
May 20 22:38:35 slow_target/2
May 20 22:38:35 slow_target/3
May 20 22:38:35 slow_target/4
May 20 22:38:35 slow_target/5
May 20 22:38:35 slow_target/6
May 20 22:38:35 slow_target/7
May 20 22:38:35 slow_target/8
May 20 22:38:40 slow_target/9
May 20 22:38:40 slow_target/10
May 20 22:38:40 fast_target/1:
May 20 22:38:45 fast_target/2: fast_target/1
May 20 22:38:51 fast_target/3: fast_target/2
May 20 22:38:56 fast_target/4: fast_target/3
May 20 22:39:01 fast_target/5: fast_target/4
May 20 22:39:06 fast_target/6: fast_target/5
May 20 22:39:11 fast_target/7: fast_target/6
May 20 22:39:16 fast_target/8: fast_target/7
May 20 22:39:21 fast_target/9: fast_target/8
May 20 22:39:26 fast_target/10: fast_target/9
Note that due to dependency, it is not possible to run e.g. only fast_target/10
without further logic manipulation, as all previous targets will also be run. This would not be the case with the flock
approach.
How can I use Target-specific Variable Values and call other rules whilst using parallel execution?
Why not just pass through the values as well?
debug: CFLAGS += $(DEBUGFLAGS)
debug:
$(MAKE) clean CFLAGS='$(CFLAGS)'
$(MAKE) all CFLAGS='$(CFLAGS)'
make: disable parallel building in subdirectory for single target only
If I understood you correctly, then when you run the recursive make that builds the check
target you can just pass -j1
specifically, to ensure that it runs serially:
check: ; $(MAKE) -j1 ...
Execute a Makefile in parallel except for some rules
This is really not right:
MyObjects: $(OBJ)
$(MAKE) -j $(OBJ)
This means that before make tries to build the MyObjects
target, it will first try to update all the $(OBJ)
files. Once that's all done, then it will try to build the MyObjects
target by recursively invoking make to rebuild them again. Obviously that's not what you want. Plus you're using -j
which is basically "infinitely parallel" and is likely (if you have enough object files) to bring your system to its knees.
You want something like this:
MyObjects:
$(MAKE) -j5 $(OBJ)
As for your second question about trying to rebuild targets, there's no way we can help without some kind of specific example. Typically this happens because your rules are written incorrectly, and they don't actually update the target you told make they would. So for example, you have a target recipe_a
but the rule for recipe_a
updates some other target, not recipe_a
.
I'll add a few notes based on your second question. Probably if you don't get it after this you should take this off of StackOverflow and ask on the help-make@gnu.org mailing list, or else consider breaking this up and asking several specific StackOverflow questions.
First, why you see make[1]: '15.o' is up to date. for every file in your recursive make: because make always prints that message for every target on the command line, so if you run make 1.o 2.o 3.o ...
(doesn't matter whether you use -j
or not or what value of -j
you use) you'll get that message for every target which doesn't need to be rebuilt. Just as if you ran that same make
command from the command line yourself.
Second, why you don't get a.out is up to date
, because a.out
is NOT up to date. It depends on the build
target, and the file build
doesn't exist, and thus it's out of date, and so it must be rebuilt every time. And that means anything that depends on the build
target, like a.out
, must be rebuilt every time. Which explains why it always re-runs the zip
command.
Third, the behavior with all.c
is because if you create a pattern rule like %.c:
with no prerequisites, that tells make that it can create ANY file with a .c
extension by running that command. Well, one of the targets you asked make to build is the all
target. Since you didn't declare that as a .PHONY
target, make tries to build it. Normally that attempt fails because make can't find any rules that know how to build all
so nothing happens, but after you tell make how to build a .c
file out of nothing (no prerequisites), then when make wants to build all
it looks in its internal database of predefined rules and sees a pattern rule % : %.c
, which tells make how to build an executable from a source file with the same name (on UNIX systems executables don't have any suffix like .exe
: they're just make
or cc
etc.) So, make
tries to run those rules and they fail.
For any target which you don't expect to actually be created, like all
, clean
, etc. you should declare them to be .PHONY
so make won't try to build them.
As for your problem. I think the simplest thing to do is push the entire build of the zip file down into the recursive make, rather than trying to build the objects only in the recursive make. Something like this:
NAMES = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
SRC = $(addsuffix .c,$(NAMES))
OBJ = $(patsubst %.c,%.o,$(SRC))
all: recurse
recurse: non-parallel-targets
$(MAKE) -j8 a.out PARALLEL=true
ifneq($(PARALLEL),true)
.NOTPARALLEL:
endif
%.o: %.c
cp $< $@
a.out: $(OBJ)
zip $@ $(OBJ)
init: $(SRC)
clean:
-rm *.o
-rm *.out
.PHONY: all clean init
How can I force gnu make to not build recipe in parallel?
This is a horrible kludge, but it will do the job:
b.cpp: a.cpp
c.cpp: b.cpp
Or if there are actually a lot of these, you can have a few stiff drinks and do this:
c-sources = $(sources:.xxx=.cpp)
ALLBUTFIRST = $(filter-out $(firstword $(c-sources)), $(c-sources))
ALLBUTLAST = $(filter-out $(lastword $(c-sources)), $(c-sources))
PAIRS = $(join $(ALLBUTLAST),$(addprefix :,$(ALLBUTFIRST)))
$(foreach pair,$(PAIRS),$(eval $(pair)))
(This works in GNUMake, I don't know about other versions.)
Parallel makefile requires dependency ordering
My preference is for
release:
$(MAKE) clean
$(MAKE) test1
This forces the two targets to be made consecutively without disturbing their inner parallelism (if any).
Related Topics
How to Identify Multiple Usb-Serial Adapters Under Ubuntu 10.1
Unable to Start Rstudio in Centos Getting Error "Unable to Connect to Service"
Nasm Linux Assembly Printing Integers
Bash Print Stderr Only, Not Stdout
What's the Best Way to Find a String/Regex Match in Files Recursively? (Unix)
Running Shell Script Using .Env File
How to Use Awk for a Compressed File
How to Find Which Process Is Leaking Memory
Git- How to Kill Ssh-Agent Properly on Linux
Search Ms Word Files in a Directory for Specific Content in Linux
How to Redirect Nohup Output to a Specified File
Elf Dynamic Loader Symbol Lookup Ordering
What Does the Gcc Error Message, "Error: Unsupported for 'Mov'", Mean
How to Check If a Files Exists in a Specific Directory in a Bash Script
How to Delete Duplicated Rows Based in a Column Value