Tom
Tom

Reputation: 355

Makefile to compile lists of source files

I have lists of files that I want my Makefile to compile, one list for each source language:

CFILES=  Src/Application/main.c  Src/Core/data.c  Lib/routines.c
ASFILES= Src/Application/startup.s  Lib/sqrt.s

I want all the output in one directory:

OBJDIR= output

How do I do the equivalent of:

output/main.o     : Src/Application/main.c
    cc -c -o output/main.o Src/Application/main.c

output/data.o     : Src/Core/data.c
    cc -c -o output/data.o Src/Core/data.c

output/routines.o : Lib/routines.c
    cc -c -o output/routines.o Lib/routines.c

output/startup.o  : Src/Application/startup.s
    as -o output/startup.o Src/Application/startup.s

output/sqrt.o     : Lib/sqrt.s
    as -o output/sqrt.o  Lib/sqrt.s

The recipes are the same for every file in its list.

The input files are in all sorts of different directories and it is not acceptable to just list their filenames and use a search path to find them, their explicit paths must be used.

The output filename is the basename of the source file name with the extension changed to o. There are no duplicated basenames between the lists for the different source languages.

I do not want to have to list the object files, this should be derived from the source lists.

I am using gnu make, but bonus points for a portable solution.

Upvotes: 5

Views: 14527

Answers (3)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136208

Something like the following could do:

all :

OBJDIR := output
CFILES  := Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES := Src/Application/startup.s Lib/sqrt.s

target = ${OBJDIR}/$(patsubst %.s,%.o,$(notdir ${1}))
obj.c :=
obj.s :=
define obj
  $(call target,${1}) : ${1} | ${OBJDIR}
  obj$(suffix ${1}) += $(call target,${1})
  ${1} : ; mkdir -p `dirname $$@` && touch $$@ # Create the source for testing. Remove this.
endef

define SOURCES
  $(foreach src,${1},$(eval $(call obj,${src})))
endef

$(eval $(call SOURCES,${CFILES}))
$(eval $(call SOURCES,${ASFILES}))

all : ${obj.c} ${obj.s}

${obj.c} : % :
    @echo cc -c -o $@ $^; touch $@ # echo and touch are for testing. Remove these.

${obj.s} : % :
    @echo as -o $@ $^; touch $@ # echo and touch are for testing. Remove these.

${OBJDIR} :
    mkdir $@

.PHONY: all

Output:

$ make 
make: Entering directory '/home/max/tmp'
mkdir -p `dirname Src/Application/main.c` && touch Src/Application/main.c # Create the source for testing. Remove this.
mkdir output
cc -c -o output/main.c Src/Application/main.c
mkdir -p `dirname Src/Core/data.c` && touch Src/Core/data.c # Create the source for testing. Remove this.
cc -c -o output/data.c Src/Core/data.c
mkdir -p `dirname Lib/routines.c` && touch Lib/routines.c # Create the source for testing. Remove this.
cc -c -o output/routines.c Lib/routines.c
mkdir -p `dirname Src/Application/startup.s` && touch Src/Application/startup.s # Create the source for testing. Remove this.
as -o output/startup.o Src/Application/startup.s
mkdir -p `dirname Lib/sqrt.s` && touch Lib/sqrt.s # Create the source for testing. Remove this.
as -o output/sqrt.o Lib/sqrt.s
make: Leaving directory '/home/max/tmp'

Upvotes: 3

GreenEye
GreenEye

Reputation: 163

The following method should do what you want: compile all of your specified source files and put the object files in the directory ./output automatically. Of course, you need to provide compiler options, proper libraries necessary for linking, and so on.

OBJDIR =./output
SRCDIR1 =./Src/Application
SRCDIR2 =./Src/Core
SRCDIR3 =./Lib
SRC1 =$(SRCDIR1)/main.c
SRC2 =$(SRCDIR2)/data.c
SRC3 =$(SRCDIR3)/routines.c
SRC4 =$(SRCDIR1)/startup.s
SRC5 =$(SRCDIR3)/sqrt.s
OBJ1 =$(patsubst $(SRCDIR1)/%.c,$(OBJDIR)/%.o,$(SRC1))
OBJ2 =$(patsubst $(SRCDIR2)/%.c,$(OBJDIR)/%.o,$(SRC2))
OBJ3 =$(patsubst $(SRCDIR3)/%.c,$(OBJDIR)/%.o,$(SRC3))
OBJ4 =$(patsubst $(SRCDIR1)/%.s,$(OBJDIR)/%.o,$(SRC4))
OBJ5 =$(patsubst $(SRCDIR3)/%.s,$(OBJDIR)/%.o,$(SRC5))

vpath %.c $(SRCDIR1): $(SRCDIR2): $(SRCDIR3)
vpath %.s $(SRCDIR1): $(SRCDIR3) 

all: $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5)
       cc $^ -o executable

$(OBJDIR)/%.o: %.c | $(OBJDIR)
       cc -c $< -o $@
$(OBJDIR)/%.o: %.s | $(OBJDIR)
       cc -c $< -o $@

$(OBJDIR):
       mkdir -p $(OBJDIR)

Upvotes: -1

user184968
user184968

Reputation:

I have little experience in writing makefiles, so this is just an attempt. In my example I have C and C++ files in a few directories and build a program made of these files.

$ cat Makefile
.PHONY : clean all

CC=gcc
CXX=g++

CFILES = c/f.c c/g.c
CPPFILES = cpp/main.cpp

OUTPUT = ./output

SOURCE_DIRS := $(dir $(CFILES))
SOURCE_DIRS += $(dir $(CPPFILES))

VPATH = $(sort $(SOURCE_DIRS))

C_FILENAMES := $(notdir $(CFILES))
CPP_FILENAMES += $(notdir $(CPPFILES))

OBJ_FILES := $(patsubst %.c, $(OUTPUT)/%.o, $(C_FILENAMES) )
OBJ_FILES += $(patsubst %.cpp, $(OUTPUT)/%.o, $(CPP_FILENAMES) )

all : $(OUTPUT)/program

$(OUTPUT)/program : $(OBJ_FILES)
    g++ -o $@ $^

$(OUTPUT)/%.o : %.cpp
    $(shell mkdir -p $(OUTPUT) )
    $(CXX) $(CXXFLAGS) -c $< -o $@

$(OUTPUT)/%.o : %.c
    $(shell mkdir -p $(OUTPUT) )
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -fr $(OUTPUT)

And this is an example of using my makefile:

$ make
gcc  -c c/f.c -o output/f.o
gcc  -c c/g.c -o output/g.o
g++  -c cpp/main.cpp -o output/main.o
g++ -o output/program output/f.o output/g.o output/main.o

Upvotes: 0

Related Questions