Parallel make generates code multiple times - makefile

let's assume an easy project consisting of two static sources and three dynamic ones, which are generated.
The makefile looks like this:
SRC_DIR = src
OUTPUT = output
TARGET = $(OUTPUT)/lib.so
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
SRCS = static_1.c static_2.c $(GEN_SRCS)
OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)
all: $(TARGET)
$(TARGET): $(OBJS)
gcc -shared -o $# $^
$(OUTPUT)/%.o: $(SRC_DIR)/$*%.c | $(OUTPUT)
#echo "Creating object $# from $^"
gcc -c -o $# $<
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
#echo "---------------------------------------"
#echo "Generating dynamic Cs"
#echo "---------------------------------------"
./generate.sh $<
$(OUTPUT):
mkdir -p $(OUTPUT)
.PHONY: clean
clean:
rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c
While building using one core only, it builds just fine:
mkdir -p output
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c
However, with parallel building (-j 2), the dynamic_* files are generated twice:
mkdir -p output
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
Creating object output/dynamic_1.o from src/dynamic_1.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o
rm src/dynamic_1.c src/dynamic_2.c src/dynamic_3.c
How should I change the makefile to generate the files only one?
A clue can be here, but I haven't found a way to use it:
multiple targets from one recipe and parallel execution
The project to download is here: http://stuff.pitris.info/make.tar.gz
Thanks!

That linked answer is the answer. Your rule:
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
#echo "---------------------------------------"
#echo "Generating dynamic Cs"
#echo "---------------------------------------"
./generate.sh $<
Tells make that a single $(SRC_DIR)/dynamic_%.c file is created by each invocation of generate.sh. However running that script actually generates multiple files (but make doesn't know that).
So when you run make normally make operates sequentially and when it sees the first dynamic_*.c file it runs the script to generate that single file (but all the files are generated). When make then goes to determine if the next dynamic_*.c file needs to be generated it sees that the file already exists and skips the rule for generating it.
When you run make -j make spawns parallel processes and those just so happen to each hit a dynamic_*.c file at the same time and so they all run the the script (even though only one of them needs to).
That's why you see it running N times. Change your target order/etc. and you may see fewer generate.sh runs as the parallelism works out differently (you might even manage to race it to only run once) but that's all coincidental.
The right solution, as in that linked answer, is to teach make that all the files are created by running the script by listing multiple patterns for that rule. Or, alternatively, modify generate.sh to only generate a single file at a time (and always run it N times).
The simplest (though somewhat ugly) way to do the multiple patterns thing would likely be to replace
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
with
GEN_SRCS = %ynamic_1.c %ynamic_2.c %ynamic_3.c
or something like that and then replace
$(SRC_DIR)/dynamic_%.c: definition_for_generator.xml
with
$(GEN_SRCS): definition_for_generator.xml

There is another issue with your code: dependency on $(OUTPUT) directory. It never works as expected.
The below makefile works as expected, please try:
SRC_DIR = src
OUTPUT = output
TARGET = $(OUTPUT)/lib.so
GEN_SRCS = dynamic_1.c dynamic_2.c dynamic_3.c
SRCS = static_1.c static_2.c $(GEN_SRCS)
ifeq ($(filter clean,$(MAKECMDGOALS)),)
-include $(OUTPUT)/.timestamp
endif
OBJS = $(SRCS:%.c=$(OUTPUT)/%.o)
mkdir_# = $(if $(wildcard $(#D)),,mkdir -p $(#D))
all: $(TARGET)
$(TARGET): $(OBJS)
gcc -shared -o $# $^
$(OUTPUT)/%.o: $(SRC_DIR)/$*%.c
$(mkdir_#)
#echo "Creating object $# from $^"
gcc -c -o $# $<
$(OUTPUT)/.timestamp: definition_for_generator.xml
$(mkdir_#)
#echo "---------------------------------------"
#echo "Generating dynamic Cs"
#echo "---------------------------------------"
./generate.sh $<
touch $#
.PHONY: clean
clean:
rm -rf $(OUTPUT) $(SRC_DIR)/dynamic_1.c $(SRC_DIR)/dynamic_2.c $(SRC_DIR)/dynamic_3.c
The idea is to inject dependency on generated empty makefile - $(OUTPUT)/.timestamp . This is to force GNU Make reread you Makefile. The rule to generate $(OUTPUT)/.timestamp runs your source generator. Thus when GNU Make starts processing your Makefile the second time, generated sources are in place already. Thus you don't need to define any dependencies for them, i.e. they are like your regular source files.
Hint: writing generated sources into source tree is not a good idea preventing use of this source tree by multiple builds at the same time. This is the most obvious issue with this approach.
Also look at mkdir_# function: it will efficiently create directory for the target if it doesn't exist first trying to use GNU Make builtin to check if the directory exists before spawning mkdir process. Makes a difference when used with Cygwin as process creation in this environment is very time consuming operation compared to the first class UNIX systems.
Output:
$ make -j
mkdir -p output
---------------------------------------
Generating dynamic Cs
---------------------------------------
./generate.sh definition_for_generator.xml
touch output/.timestamp
Creating object output/static_1.o from src/static_1.c
gcc -c -o output/static_1.o src/static_1.c
Creating object output/static_2.o from src/static_2.c
gcc -c -o output/static_2.o src/static_2.c
Creating object output/dynamic_1.o from src/dynamic_1.c
Creating object output/dynamic_2.o from src/dynamic_2.c
gcc -c -o output/dynamic_1.o src/dynamic_1.c
gcc -c -o output/dynamic_2.o src/dynamic_2.c
Creating object output/dynamic_3.o from src/dynamic_3.c
gcc -c -o output/dynamic_3.o src/dynamic_3.c
gcc -shared -o output/lib.so output/static_1.o output/static_2.o output/dynamic_1.o output/dynamic_2.o output/dynamic_3.o

Related

How to invoke command for each file using make? [duplicate]

This question already has an answer here:
Cannot get makefile to build each object from its corresponding source
(1 answer)
Closed 2 years ago.
So I am struggling a bit with the following Makefile:
src := $(wildcard *.c)
bin := $(src:%.c=out/%)
.PHONY: all
all: $(bin)
.PHONY: clean
clean:
rm -r out
out:
mkdir $#
$(bin): $(src) | out
$(CC) -Iincludes/ -Wall $< -o $#
Let's say I have the following directory:
$ ls
includes/ Makefile test1.c test2.c
Then I want the following to happen:
$ make
mkdir out
cc -Iincludes/ -Wall test1.c -o out/test2
cc -Iincludes/ -Wall test2.c -o out/test1
But what actually happens:
$ make
mkdir out
cc -Iincludes/ -Wall test2.c -o out/test2
cc -Iincludes/ -Wall test2.c -o out/test1
I kinda do not want to move files around what other solutions suggest
I am pretty sure this has to be easy but I am lost. Thank you for your help.
Consider changing the '$(bin)' rule:
out/%: %.c
$(CC) -Iincludes/ -Wall $< -o $#
The existing rule indicate that each program depends on ALL source files, and that each program can be built by compiling the ($<) FIRST source file. The revised rule indicate that echh programs depends on the .c with the same name

in makefile echoing "compiling" before the compilation

i thought it would be easy but i can't actually figure out nor find on internet, a solution to advertise "compilation of the objects file" (for instance) just before compiling, and avoiding relink in the same time
my makefile would be something like this :
libfrog.a: $(OBJS)
#echo "building the library"
ar -rc $# $^
%.o:%.c
gcc -i. -c -o $# $<
which produce, when prompt "make" :
gcc -I. -c -o file01.o file01.c
gcc -I. -c -o file02.o file02.c
gcc -I. -c -o file03.o file03.c
gcc -I. -c -o file04.o file04.c
gcc -I. -c -o file05.o file05.c
building library
ar -rc libfrog.a file01.o file02.o file03.o file04.o file05.o
but i would like to have :
compilation of the objects files
gcc -I. -c -o file01.o file01.c
gcc -I. -c -o file02.o file02.c
gcc -I. -c -o file03.o file03.c
gcc -I. -c -o file04.o file04.c
gcc -I. -c -o file05.o file05.c
building library
ar -rc libfrog.a file01.o file02.o file03.o file04.o file05.o
now :
1 - if i put an echo before the rule libfrog.a is called (say by creating another first rule), it will print even if i prompt "make" two times and nothing is to be done...
2 - if i put an echo in the rule %.o:%.c it will print for each file
3 - if i put an echo as a dependency of the first rule it will force to relink all the files when prompt "make" again, even when just one file has been modified libfrog.a: echo $(OBJS) (and a rule "echo" which echo the text)...
so I've tried to echo for each compilation but with changing the text to echo nothing, i failed... i tried to create an echo.txt file just to create a rule that would depend on its existence but i failed too. I have no idea if it's possible ?
It's not really simple to do. But if you're using GNU make you can do it with the help of order-only prerequisites:
%.o : %.c | announce
gcc -I. -c -o $# $<
.PHONY: announce
announce:
echo "compilation of the objects files"
Personally I don't think showing this message is needed and I wouldn't add even the above amount of complexity to support it in my makefiles.
But if you really want to do it and you don't want the message to be printed unless some object file needs to be made, you'll have to get really fancy and do something like this:
PRINTED :=
%.o : %.c
$(or $(PRINTED),$(eval PRINTED := :)echo "compilation of the objects files")
gcc -I. -c -o $# $<
Once it's working you'll want to add a # before the first recipe line.

Creating 2 versions of same lib using GNU make

I need to create 2 different versions of same lib (.a & .so files), using different compilation flags.
for instance,
the following debug flag should be enabled in only one of the builds:
#ifdef ENABLE_NEW_FEATURE
//some code
#endif
the lib Makefile is being called from outside project with many executables, some of them need to have the new feature and some of them not.
i wonder how can i use Makefile to create 2 different versions of same lib without suffering race condition in case of running make -j.
The usual way is to use several directories for the objects. Then you may use something like:
SOURCES=f1.cpp f2.cpp
all: libmylib-d.a libmylib-o.so
libmylib-d.a: CFLAGS=-Dd
libmylib-d.a: $(SOURCES:%.cpp=obj-d/%.o)
ar -cru $# $^
libmylib-o.so: CFLAGS=-Do -fPIC
libmylib-o.so: $(SOURCES:%.cpp=obj-o/%.o)
g++ -shared -o $# $^
obj-d/%.o: %.cpp | obj-d
$(CXX) $(CFLAGS) -c -o $# $<
obj-o/%.o: %.cpp | obj-o
$(CXX) $(CFLAGS) -c -o $# $<
obj-o obj-d:
mkdir $#

makefile automatic variable for corresponding c file

i hava a makefile something like this:
outdir = release
allsrc = aaa/a.c bbb/b.c ccc/c.c
allobjs = $(addprefix $(outdir), $(notdir $(allsrc:.c=.o))
...
test: $(allobjs)
$(allobjs): $(allsrc)
gcc -c -o $# $<
make test performs:
gcc -c -o release/a.o aaa/a.c
gcc -c -o release/b.o aaa/a.c
gcc -c -o release/c.o aaa/a.c
(automatic variable $< always takes first prerequisite)
but i want "corresponding one":
gcc -c -o release/a.o aaa/a.c
gcc -c -o release/b.o bbb/b.c
gcc -c -o release/c.o ccc/c.c
what should i change to accomplish desirable result?
i know that this will work for sure:
$(outdir)/a.o: aaa/a.c
gcc -c -o $# $<
$(outdir)/b.o: bbb/b.c
gcc -c -o $# $<
$(outdir)/c.o: ccc/c.c
gcc -c -o $# $<
and wondering how to accomplish the same in one receipe. (because in my real makefile i have ~20 different source files not just 3 like i made here for example)
You don't write your recipe like that. That's not how make works.
That recipe says that every item in $(allobjs) has every item in $(allsrc) as its prerequisite but that's not what you mean.
You mean that every .o file has the matching .c file as its prerequisite which is exactly what the built in %.o: %.c rule already does. You don't even need a makefile to do that.
Edit: Actually, you don't mean that. I had missed that the source files were in different directories. That changes and complicates things.
Chris Dodd wrote up two good solutions for this.
The usual way to do what you are asking would be something like:
outdir = release
allsrc = aaa/a.c bbb/b.c ccc/c.c
allobjs = $(addprefix $(outdir), $(notdir $(allsrc:.c=.o)))
VPATH = $(sort $(dir $(allsrc)))
...
test: $(allobjs)
$(outdir)/%.o: %.c
gcc -c -o $# $<
Of course, this will run into problems if you have a b.c in both aaa and bbb, but since you're trying to put all the object files in the same directory, you have that regardless. It might make more sense to get rid of the $(notdir and keep the same directory structure under $(outdir), in which case you don't need the VPATH
outdir = release
allsrc = aaa/a.c bbb/b.c ccc/c.c
allobjs = $(addprefix $(outdir), $(allsrc:.c=.o))
...
test: $(allobjs)
$(outdir)/%.o: %.c
mkdir -p `dirname $#`
gcc -c -o $# $<

How to restore g++/gcc compiling messages

I'm using CentOS 6.5. When I do a make, I typically see the full gcc/g++ commands that the Makefile is executing, like
...
gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I/opt/emacs/emacs-24.3/lib -I../src -I/opt/emacs/emacs-24.3/src -g3 -O2 -MT pthread_sigmask.o -MD -MP -MF .deps/pthread_sigmask.Tpo -c -o pthread_sigmask.o pthread_sigmask.c
...
But in some systems, I only see:
$ make
Building test1.o...
Building test2.o...
...
Is it possible to change the "Building ..." messages back to the full gcc/g++ command output?
The output that you see when you run make with a given makefile
depends on how the makefile is written. You will see the
output that the author of the makefile wants you to see.
If a command in a recipe in the makefile is prefixed with #,
then make will not echo the command. So if my makefile is, e.g.
foobar: foobar.o
gcc -o $# $<
foobar.o: foobar.c
gcc -c -o $# $<
then the output of make will be:
gcc -c -o foobar.o foobar.c
gcc -o foobar foobar.o
But if I change the makefile to:
foobar: foobar.o
#echo "Linking foobar"
#gcc -o $# $<
foobar.o: foobar.c
#echo "Compiling foobar"
#gcc -c -o $# $<
then the output becomes:
Compiling foobar
Linking foobar
So to see the output that you would prefer to see you will have to edit the
makefile, removing the #-prefixes from the commands you expect to see
and deleting entirely the commands that print the "Building..." messages.
At least, this is what you would need to do if the makefiles that bother
you in this way build the target using recipes that directly invoke gcc/g++. It
is possible that they build their targets using recipes that invoke some intermediate
tool that doesn't echo the compiler commands and instead emits the "Building..."
messages. Without seeing the makefile(s) I can't say.

Resources