Parallel makefile execution causes a library to be built twice - makefile

I have two binaries (bin_a, bin_b) and a library (lib) shared by these two binaries.
The binaries depend on the library.
main makefile:
include makeenv.mk
all: $(BIN_A) $(BIN_B)
.PHONY: clean
clean:
$(MAKE) -C lib clean
$(MAKE) -C bin_a clean
$(MAKE) -C bin_b clean
makeenv.mk:
BIN_A=$(ROOT)/bin_a/bin_a
BIN_B=$(ROOT)/bin_b/bin_b
LIB=$(ROOT)/lib/liblib.so
first_target: all
$(BIN_A):
$(MAKE) -C $(ROOT)/bin_a
$(BIN_B):
$(MAKE) -C $(ROOT)/bin_b
$(LIB):
$(MAKE) -C $(ROOT)/lib
bin_a/makefile:
include ../makeenv.mk
all: bin_a
bin_a: $(LIB)
gcc -I $(ROOT) -L $(ROOT)/lib -l lib -o $# bin_a.c
.PHONY: clean
clean:
rm -f bin_a
bin_b/makefile:
include ../makeenv.mk
all: bin_b
bin_b: $(LIB)
gcc -I $(ROOT) -L $(ROOT)/lib -l lib -o $# bin_b.c
.PHONY: clean
clean:
rm -f bin_b
lib/makefile:
include ../makeenv.mk
TARGET=liblib.so
all: $(TARGET)
$(TARGET): lib.c
gcc -shared -fPIC -o $# $^
sleep 5
.PHONY: clean
clean:
rm -f liblib.so
If I type make, everything is built just fine:
make -C /tmp/mak/bin_a
make[1]: Entering directory `/tmp/mak/bin_a'
make -C /tmp/mak/lib
make[2]: Entering directory `/tmp/mak/lib'
gcc -shared -fPIC -o liblib.so lib.c
sleep 5
make[2]: Leaving directory `/tmp/mak/lib'
gcc -I /tmp/mak -L /tmp/mak/lib -l lib -o bin_a bin_a.c
make[1]: Leaving directory `/tmp/mak/bin_a'
make -C /tmp/mak/bin_b
make[1]: Entering directory `/tmp/mak/bin_b'
gcc -I /tmp/mak -L /tmp/mak/lib -l lib -o bin_b bin_b.c
make[1]: Leaving directory `/tmp/mak/bin_b'
However if I want to build it parallely, the library is built twice:
make -C /tmp/mak/bin_a
make -C /tmp/mak/bin_b
make[1]: Entering directory `/tmp/mak/bin_a'
make -C /tmp/mak/lib
make[1]: Entering directory `/tmp/mak/bin_b'
make -C /tmp/mak/lib
make[2]: Entering directory `/tmp/mak/lib'
gcc -shared -fPIC -o liblib.so lib.c
make[2]: Entering directory `/tmp/mak/lib'
gcc -shared -fPIC -o liblib.so lib.c
sleep 5
sleep 5
make[2]: Leaving directory `/tmp/mak/lib'
make[2]: Leaving directory `/tmp/mak/lib'
gcc -I /tmp/mak -L /tmp/mak/lib -l lib -o bin_a bin_a.c
gcc -I /tmp/mak -L /tmp/mak/lib -l lib -o bin_b bin_b.c
make[1]: Leaving directory `/tmp/mak/bin_a'
make[1]: Leaving directory `/tmp/mak/bin_b'
The question is: why?
And how to fix these makefiles to make the parallel run work?
Thanks!

The toplevel makefile doesn't know that the sub-makefiles are going to each build $(LIB) and the sub-makefiles, naturally, don't know anything about what the other makefile is doing.
So you have a race condition wherein they both detect that $(LIB) needs to be rebuilt and then both schedule it for building. This is one of the main arguments in the Recursive Make Considered Harmful (pdf) paper.
Changing makeenv.mk to read:
BIN_A=$(ROOT)/bin_a/bin_a
BIN_B=$(ROOT)/bin_b/bin_b
LIB=$(ROOT)/lib/liblib.so
first_target: all
$(BIN_A): $(LIB)
$(MAKE) -C $(ROOT)/bin_a
$(BIN_B): $(LIB)
$(MAKE) -C $(ROOT)/bin_b
$(LIB):
$(MAKE) -C $(ROOT)/lib
might do enough to get you what you want in this case (but I would have to try it and examine the prereq listings a bit more carefully to be sure).
Alternatively, you could read the paper I listed before and stop using recursive make for this sort of thing.

try
subdirs=lib bin_a bin_b
.PHONY : all $(subdirs)
bin_a bin_b : lib
$(subdirs) :
$(MAKE) -C $# $(MAKECMDGOALS)
This worked for me.
Alternatively I have tried
all : makeall
bin_a bin_b : lib
makeall :
$(MAKE) -C lib
$(MAKE) -C bin_a
$(MAKE) -C bin_b
$(MAKE) -C moredirs
In my hands, the difficulty is to to both parallel and keep the make process in a defined sequence. If you have a large project you cannot wait to do single-threaded make. You almost have to do make -j 24.
Let me know if there is any improvement in this.

Related

Fortran makefile circular dependency dropped

So I am trying to run a Fortran code from someone else with the Intel Fortran compiler on a mac. But when I try to run the corresponding makefile, I get the error messages "make[1]: Circular release.makefile out <- release.makefile dependency dropped." and so on.
I have little knowledge of Fortran, so I would appreciate any help. I already tested to run some simple helloworld.f90 files and makefiles, to rule out that it is a compiler problem, and those work. However, the code I am trying to run is downloaded from a reputable scientific journal, so I assume the code (makefiles and Fortran files) should be correct too, leaving me at loss. This is my first stackoverflow post, so if I forgot something please let me know.
The makefile is:
release:
# ENV = /opt/intel/oneapi/setvars.sh intel64
make -f release.makefile
debug:
# ENV = /opt/intel/oneapi/setvars.sh intel64
make -f debug.makefile
dsymutil $(OUT).out
hpcrelease:
make -f hpcrelease.makefile
pbs:
make -f pbs.makefile
compilesubmit:
make -f hpcrelease.makefile
make -f pbs.makefile
qsub $(OUT).pbs
.PHONY: clean
clean:
rm -f *.o *.mod *.MOD *genmod* *~
rm -fr *.dSYM
and the file release.makefile writes:
FC = ifort
FCFLAGS = -m64 -traceback -O3 -qopenmp -implicitnone -Wl,-stack_size,0x100000000 -L/Users/josie/Desktop/Uni/Economics/Moll/SuiteSparse-master/lib -lumfpack -lamd -lcholmod -lcolamd -lsuitesparseconfig -lblas
LDFLAFS = -m64 -traceback -O3 -qopenmp -implicitnone -Wl,-stack_size,0x100000000 -L/Users/josie/Desktop/Uni/Economics/Moll/SuiteSparse-master/lib -lumfpack -lamd -lcholmod -lcolamd -lsuitesparseconfig -lblas
# -O3
PROG = $(OUT)
MOD = Parameters.o Globals.o umfpack.o Procedures.o
SUBR = AllocateArrays.o SetParameters.o Grids.o IterateBellman.o HJBUpdate.o cumnor.o rtsec.o StationaryDistribution.o SaveSteadyStateOutput.o DistributionStatistics.o rtbis.o rtflsp.o InitialSteadyState.o FinalSteadyState.o SolveSteadyStateEqum.o Calibration.o MomentConditions.o dfovec.o newuoa-h.o newuob-h.o update.o trsapp-h.o biglag.o bigden.o mnbrak.o golden.o sort2.o CumulativeConsumption.o FnDiscountRate.o OptimalConsumption.o FnHoursBC.o ImpulseResponses.o IRFSequence.o Transition.o SaveIRFOutput.o IterateTransitionStickyRb.o IterateTransOneAssetStickyRb.o FnCapitalEquity.o CumulativeConsTransition.o DiscountedMPC.o DiscountedMPCTransition.o
OBJ = $(MOD) $(SUBR)
$(PROG).out: $(OBJ) Main.o
$(FC) $(FCFLAGS) -o $# $^ $(LDFLAGS)
Main.o: $(MOD)
%: %.o
$(FC) $(FCFLAGS) -o $# $^ $(LDFLAGS)
%.o: %.f90
$(FC) $(FCFLAGS) -c $<
the error I get is
Fortran % make -f makefile
# ENV = /opt/intel/oneapi/setvars.sh intel64
make -f release.makefile
make[1]: Circular release.makefile.out <- release.makefile dependency dropped.
make[1]: Circular Main.f90.out <- Main.f90 dependency dropped.
make[1]: Circular Parameters.f90.out <- Parameters.f90 dependency dropped.
make[1]: Circular Globals.f90.out <- Globals.f90 dependency dropped.
make[1]: Circular umfpack.f90.out <- umfpack.f90 dependency dropped.
make[1]: Circular Procedures.f90.out <- Procedures.f90 dependency dropped.
make[1]: `Main.o' is up to date.
Since the error already occurs with the release.makefile, I refer from giving the other files since I believe the error is the same for all scripts? Correct me if I am wrong and those are necessary.

Makefile: Circular - Dependency dropped

I have designed a Makefile that compiles all the .c files individually and produces a .o respectively (I think this happens Implicitly and works perfectly fine).
The executable (.out) is not being generated from the .o files.
Makefile:
TARGET = all.out
OBJS = file1.o file2.o file3.o
CC = gcc
CFLAGS = -g -Wall
all : $(TARGET)
$(TARGET) : $(OBJS)
# gcc $^ -o $#
run : $(TARGET)
./$<
clean :
rm -rf *.o $(TARGET)
Output:
$ make
make: Circular all.out <- all dependency dropped.
gcc -g -Wall -c -o file1.o file1.c
gcc -g -Wall -c -o file2.o file2.c
gcc -g -Wall -c -o file3.o file3.c
cp file1.o all.out
Note: The Makefile works perfectly and produces the perfect results if the line no. 7 present in it is uncommented.
line no. 7:
# gcc $^ -o $#
Output when line no. 7 is uncommented (Works perfectly as intended):
gcc -g -Wall -c -o file1.o file1.c
gcc -g -Wall -c -o file2.o file2.c
gcc -g -Wall -c -o file3.o file3.c
gcc file1.o file2.o file3.o -o a.out
I am new to Makefiles.
Queries:
why does commenting line no. 7 causing this issue and uncommenting it works perfectly?
What is cp in the first output when line no.7 was commented?
What does circular - dependency dropped mean?
I can't explain how you are seeing the problem you showed to us. Either what you wrote above is not actually what you're using, or you have a buggy version of GNU make. I can't reproduce the behavior you're seeing.
But, I'm sure it's related to this: GNU make has a built-in rule that knows how to build an xx.out file from a file xx for any xx:
# make -p -f/dev/null
...
%.out: %
# recipe to execute (built-in):
#rm -f $#
cp $< $#
If you comment out your own recipe as an explicit rule, then make will search for one among the pattern rules it knows about and it will find this built-in pattern rule.
However this rule shouldn't match based on what you've shown us: in order for it to match with a target of a.out, make would have to find or know how to build a target a and that doesn't seem to be available. Also, knowing how to build a would show a circular dependency on a.out.
If your makefile was:
TARGET = all.out
THEN it would all make perfect sense because you would have:
all : all.out
all.out : file1.o file2.o file3.o
and after the implicit rule match %.out: % it would expand like this:
all : all.out
all.out : all file1.o file2.o file3.o
#rm -f all.out
cp all all.out
So I assume that when you copied the output into your question you changed it: best to not do that. You should post exactly the problem you have (and verify you still have the problem with what you posted).

makefile with two rules for the same target

I have a makefile to create a library, and I need it to create a 80% library or a complete library, which means a library with 80% of the functions or a library with all of the functions.
When I call make, the "all" rule should create the 80% library (so far so good), and when I call "make complete" the "complete" rule should create the 100% library (still easy), and it should not relink in any case, here I have a problem.
WHAT GOES WELL:
here is what I have :
all: $(NAME)
$(NAME): $(OBJS)
ar -rc $# $^
complete: $(NAME) $(MORE_OBJS)
ar -rc $^
%.o : %.c
gcc -I. -c -o $# $<
.PHONY: complete
so if I run make it goes:
>make
gcc -I. -c -o function01.o function01.c
gcc -I. -c -o function02.o function02.c
gcc -I. -c -o function03.o function03.c
gcc -I. -c -o function04.o function04.c
ar -rc libtest.a function01.o function02.o function03.o function04.o
and make again:
>make
make: Nothing to be done for 'all'.
perfect. then with make complete:
>make complete
gcc -I. -c -o function_05.o function05.c
gcc -I. -c -o function_06.o function06.c
ar -rc libtest.a function05.o function06.o
or of course:
>make fclean
rm *.o
rm libtest.a
>make complete
gcc -I. -c -o function01.o function01.c
gcc -I. -c -o function02.o function02.c
gcc -I. -c -o function03.o function03.c
gcc -I. -c -o function04.o function04.c
ar -rc libtest.a function01.o function02.o function03.o function04.o
gcc -I. -c -o function_05.o function05.c
gcc -I. -c -o function_06.o function06.c
ar -rc libtest.a function05.o function06.o
WHAT GOES WRONG:
but if I prompt make complete again:
>make complete
ar -rc libtest.a function05.o function06.o
I don't recompile but I relink the library.
I've tried a lot of approach, with the target-specific assignment, re-ordering the targets and the rules in many different combination, but I didn't find any way of doing it. am I missing something or is it indeed hard?
also, I can't put the additional functions in another file with its own makefile and use "make -C" to make it and then add it to the library, everything has to be in this makefile and the sources at the root
make complete will always run, because the target name is complete and no file named complete is ever created. So as far as make knows that file is not up to date and needs to be rebuilt.
There is no straightforward way to do this because make doesn't support multiple different recipes creating the same target.
You can fake it out by using a sentinel file to inform make whether the target exists or not. To do this, you need to create the file complete so make can use it to track whether or not it needs to be rebuilt. Try:
complete: $(NAME) $(MORE_OBJS)
ar -rc $^
#touch $#

Make did not work after I changed my file

These are my files:
add.c add.h main.c makefile
This is makefile:
main:main.o add.o
gcc -o main main.o add.o
main.o:$(#:%.o=%.c)
gcc -o main.o -c main.c
add.o:$(#:%.o=%.c) $(#:%.o=%.h)
gcc -o add.o -c add.c
.PHONY:clean
clean:
rm *.o -rf
rm main -rf
Then after I change the main.c and make.
But make told me this:
make: `main' is up to date.
If I change my makefile:
main:main.o add.o
gcc -o main main.o add.o
main.o:main.c
gcc -o main.o -c main.c
add.o:$(#:%.o=%.c) $(#:%.o=%.h)
gcc -o add.o -c add.c
.PHONY:clean
clean:
rm *.o -rf
rm main -rf
Then after I change the main.c and make.
It can work.
I donot know the reason.
The dependencies in
main.o:$(#:%.o=%.c)
add.o:$(#:%.o=%.c) $(#:%.o=%.h)
are not valid make syntax.
Replace these two rules with one pattern (generic) rule:
%.o : %.c
gcc -c -o $# ${CPPFLAGS} ${CFLAGS} $<
The above rule is actually very similar to the built-in rule for compiling .o files from .c:
Compiling C programs
n.o is made automatically from n.c with a recipe of the form $(CC) $(CPPFLAGS) $(CFLAGS) -c.
In other words, you can remove your original rules for main.o and add.o and it should build correctly.

make file : yacc command not found and [y.tab.c] error 127 after installing bison

There is a given r-tree code by Toni Guttman(it's been modified for my homework), however, if I change an parameter(the dimension of the node), then "make" will cause such errors:
yacc y.spec
make: yacc:command not found
make: *** [y.tab.c] error 127
I've installed bison and flex, and "which yacc" shows that
alias yacc='bison'
/usr/bin/bison
What should I do to solve the problem?
Here is the "Makefile":
# %W% %G%
# use flag -O for optimized code, slower compile
FLAGS=
SRC= main.c index.c newtid.c node.c rectangle.c \
printstats.c clock.c y.spec allocate.c error.c\
split.l.c \
split.q.c \
split.e.c
HEADERS= options.h macros.h index.h assert.h
ALL= $(SRC) $(HEADERS) split.l.h split.q.h split.e.h
OBJ= main.o index.o newtid.o node.o rectangle.o \
printstats.o clock.o y.tab.o allocate.o error.o
OBJLIN= split.l.o
OBJQ= split.q.o
OBJEXP= split.e.o
$(OBJ): $(HEADERS)
$(OBJLIN): $(HEADERS) split.l.h
$(OBJQ): $(HEADERS) split.q.h
$(OBJEXP): $(HEADERS) split.e.h
# assembler chokes if graphics.c is compiled with -g option, do it without.
# graphics.o: graphics.c $(HEADERS)
# cc -c graphics.c
# assembler chokes if y.tab.c is compiled with -g option, do it without.
# y.tab.o: y.tab.c $(HEADERS)
# cc -c y.tab.c
.c.o: $(HEADERS)
cc -c $(FLAGS) $*.c
linear: $(OBJ) $(OBJLIN)
cc $(FLAGS) $(OBJ) $(OBJLIN) -lm -o linear
quad: $(OBJ) $(OBJQ)
cc $(FLAGS) $(OBJ) $(OBJQ) -lm -o quad
exp: $(OBJ) $(OBJEXP)
cc $(FLAGS) $(OBJ) $(OBJEXP) -lm -o exp
y.tab.c: y.spec $(HEADERS)
yacc y.spec
edit:
sccs edit $(SRC) $(HEADERS) split.l.h split.q.h split.o.h
unedit:
sccs unedit $(ALL)
rm -f tags
delta:
sccs delta $(ALL)
rm -f tags
get:
sccs get $(ALL)
clean:
rm -f *.o core y.tab.c tags
tags: $(SRC)
ctags *.c
lint:
rm -f lint.out
lint *.c > lint.out
You don't have yacc installed, as you've seen. Changing an alias in your shell won't help, because it's make that is trying to run the yacc command, not the shell. You have to edit your makefile, and add a line like:
YACC = bison -y
(the -y flag makes bison behave like yacc)
Since you didn't show your actual makefile we can't be sure that this will do it, but it's likely.
EDIT:
I your makefile above, change the reference to yacc to say bison -y instead.
Your makefile is not following many best practices but that's for another day.
manually add this script /usr/bin/yacc is simply a script containing:
#! /bin/sh
exec '/usr/bin/bison' -y "$#"

Resources