4
votes


I have one set of source files from which I need to generate multiple variants of an executable. For example I need to generate app1.elf, app2.elf, app3.elf from the same main.c and comm.c. The difference between each of the apps is a node address, a parameter that is passed down in the invocation of gcc. i.e.:

gcc -DNODE=1 -oapp1.elf main.c 
gcc -DNODE=2 -oapp2.elf main.c 
gcc -DNODE=3 -oapp3.elf main.c 

Let's assume that I have the following files:

  • src/main.c
  • src/comm.c

When I run the Makefile as so:

make all_nodes

make only builds app1.elf with the following output:

Built app1
Built app2
Built app3

FAIL! The output seems to suggest that it worked, however it only generates one executable, namely app1.elf. Anybody care to point out what I'm doing wrong?

To further explain my Makefile, I've created a cleanobjs target to clean out the objects in the ./obj subdirectory. This is my attempt at having 'make' rebuild the obj files with the new node address, but it fails. Am I using 'make' in a way it wasn't intended to be used? I know I can also create a batch file to run make (something I've done sucessfully) but I'd like to know what I'm doing wrong. My Makefile is below:

obj/%.o: src/%.c
    gcc -DNODE=$(NODE) -o$@ $<

app.elf : ./obj/main.o ./obj/comm.o
    gcc -oapp$(NODE).elf main.o comm.o

node1 : NODE=1
node1 : cleanobj app.elf
    @echo 'Built app1'

node2 : NODE=2
node2 : cleanobj app.elf
    @echo 'Built app2'

node3 : NODE=3
node3 : cleanobj app.elf
    @echo 'Built app3'

all_nodes : node1 node2 node3

cleanobj :
    rm -rf obj/main.o obj/comm.o

.PHONY : cleanobj
5
Just wanted to add that the reasons this is failing is because make only runs a target once per instance. So both cleanobj and app.elf get built for the node1 target and for node2 and node3 targets it is optimized out.gravitron

5 Answers

6
votes

You're going to need multiple targets for the different versions. Each target is going to need its own main.o and comm.o targets as well, meaning you need to call them something else or they'll end up confusing with each other. Then your all should function to build all three.

4
votes

This is what I would do:

SRC     = src/main.c
OBJ     = $(patsubst src%,obj$(VERSION)%,$(patsubst %.c,%.o,$(SRC)))

C_FLAGS = -DNODE=$(VERSION)


all:    app.1 app.2 app.3

#
# For each application just call make with VERSION set correctly    
app.%:  dir.%
    $(MAKE) VERSION=$* app$*.elf

#
# Build each applications objects into a seprate directory
# So we need to make sure the obj directory exists.    
dir.%:
    @- if ! test -e obj$*; then mkdir obj$*; fi

app%.elf:   $(OBJ)
    $(CC) -o$@ $(C_FLAGS) $(OBJ)


obj$(VERSION)/%.o:  src/%.c
    $(CC) -c -o obj$(VERSION)/$*.o $(C_FLAGS) $<
3
votes

a similar approach:

ifeq ($(NODE),)

all:
    make NODE=1
    make NODE=2
    make NODE=3

cleanobj:
    make NODE=1 cleanobj
    make NODE=2 cleanobj
    make NODE=3 cleanobj

else

OBJS = main.o comm.o
objs = $(patsubst %.o,obj/$(NODE)/%.o,$(OBJS))

app$(NODE).elf: $(objs)
    gcc -o app$(NODE).elf $^
    @echo 'Built app$(NODE)'

obj/$(NODE)/%.o: src/%.c
    gcc -c -DNODE=$(NODE) -o $@ $<

cleanobj :
    rm -rf $(objs)

endif

.PHONY: all cleanobj
1
votes

The easy way is to make separate build directories and use VPATH to allow your (mostly unmodified) Makefile to build the original sources in the separate directory.

The slightly harder way is to modify your Makefile to rewrite target .o and executable files to be in a subdirectory. For example:

NODE = 1
OBJDIR = obj.node$(NODE)
SRC = main.c comm.o
OBJ = $(SRC:%.c=$(OBJDIR)/%.o)

Then your targets re-invoke your Makefile with NODE set.

0
votes

create a batch file or shell script which can create all three builds. Something like:

make node1
<if needed copy your executable to a different directory>
<clean all objs from make node1>
make node2
<if needed copy your executable to a different directory>
<clean all objs from make node1>
make node3
<if needed copy your executable to a different directory>
<clean all objs from make node1>

Call this batch file or shell script for all_node build rule.