Define target for recursive makefiles invocation - makefile

What I need: make LAUNCHMAKES target dependent on something in order to not invoke sub makefiles when no source file has been changed.
SUB MakeFile contains collecting of all .cpp files in the dir and stores them to the obj/. Then no src file changed it still invokes and engenders "Entering directory - Leaving directory". I need to get rid of it when there is no need.
I've read about --no-print-directory but it's not the case.
The project's structure:
Project
|----project
| |----dir1
| | |----src1.cpp
| | `----Makefile
| |----dir2
| | |----src2.cpp
| | `----Makefile
`----main.cpp
|----obj
| |----src1.o
| |----src2.o
| `----main.o
|----MakeFile
The code:
release: LAUNCHMAKES obj/main.o
$(CXX) $(CXXFLAGS) main.o src1.o src2.o -o result
LAUNCHMAKES: (?)
for i in $(SUBDIRS); do \
$(MAKE) -C $$i CXXFLAGS=$(CXXFLAGS); \
done
obj/main.o: project/main.cpp
$(CXX) $(CXXFLAGS) -c $< -o $#

Not a solution to your problem but an example of a non-recursive Makefile, just to show you how simple it could be for your simple example project:
SUBDIRS := dir1 dir2
OBJDIR := obj
SRCS := $(shell find . -type f -name '*.cpp')
OBJS := $(patsubst %.cpp,$(OBJDIR)/%.o,$(notdir $(SRCS)))
VPATH := $(SUBDIRS)
.PHONY: release clean
release: result
result: $(OBJS)
$(CXX) $(LDFLAGS) $^ -o $#
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $#
clean:
rm -f $(OBJDIR)/*.o result
Demo:
$ make
g++ -c dir2/src2.cpp -o obj/src2.o
g++ -c dir1/src1.cpp -o obj/src1.o
g++ -c main.cpp -o obj/main.o
g++ obj/src2.o obj/src1.o obj/main.o -o result

Related

ERROR: rm: cannot remove 'kernel.img': No such file or directory

I've been trying to get my Raspberry Pi 4 OS (not Linux or anything, I'm making an OS from scratch) to work.
The Makefile has this error when I use the command "make":
rm -rf objects
rm -rf SuperPiOS.elf
rm SuperPiOS.img
rm: cannot remove 'SuperPiOS.img': No such file or directory
make: *** [Makefile:110: clean] Error 1
I can't figure out why it wouldn't work though.
Here's the Makefile:
CFLAGS= -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
CXXFLAGS= -ggdb3 -O0 -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72+nosimd
CSRCFLAGS= -O2 -Wall -Wextra
LFLAGS= -ffreestanding -O2 -nostdlib
IMG_PATH= ../
CFILES= $(wildcard *.c)
OFILES= $(CFILES:.c=.o)
GCCFLAGS= -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
GCCPATH= gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf
GCCPATHAARCH= $(GCCPATH)/aarch64-none-elf/bin
GCCPATHBIN= $(GCCPATH)/bin
ASMCFLAGS= -f elf32 -F dwarf -g -w+all
ASM= -s
# Location of the files
KER_SRC = ../src/kernel
KER_MENU_SRC = ../src/kernel/menu
KER_HEAD = ../include
COMMON_SRC = ../src/common
UI_IMAGES = ../images/ui
SPE_GAMES = ../spe_games
DINOBYTE = $(SPE_GAMES)/dinobyte
OBJ_DIR = objects
ASMSOURCES = $(wildcard $(KER_SRC)/*.s)
KERSOURCES = $(wildcard $(KER_SRC)/*.c)
#KERSOURCES = $(wildcard $(KER_SRC)/$(ARCHDIR)/*.c)
COMMONSOURCES = $(wildcard $(COMMON_SRC)/*.c)
KERSOURCESCPP = $(wildcard $(KER_SRC)/*.cpp)
DINOBYTESOURCES = $(wildcard $(DINOBYTE)/src/*.cpp)
#KERSOURCESCPP = $(wildcard $(KER_SRC)/$(ARCHDIR)/*.cpp)
#KERMENUSOURCESC = $(wildcard $(KER_MENU_SRC)/*.c)
#KERMENUSOURCESCPP = $(wildcard $(KER_MENU_SRC)/*.cpp)
UISOURCES = $(wildcard $(UI_IMAGES)/*.png)
OBJECTS = $(patsubst $(KER_SRC)/%.s, $(OBJ_DIR)/%.o, $(ASMSOURCES))
#OBJECTS += $(patsubst $(KER_SRC)/%.s, $(OBJ_DIR)/%.o, $(ASMSOURCES))
OBJECTS += $(patsubst $(KER_SRC)/%.c, $(OBJ_DIR)/%.o, $(KERSOURCES))
OBJECTS += $(patsubst $(KER_SRC)/%.cpp, $(OBJ_DIR)/%.o, $(KERSOURCESCPP))
OBJECTS += $(patsubst $(COMMON_SRC)/%.c, $(OBJ_DIR)/%.o, $(COMMONSOURCES))
#OBJECTS += $(patsubst $(KER_MENU_SRC)/%.c, $(OBJ_DIR)/%.o, $(KERMENUSOURCESC))
#OBJECTS += $(patsubst $(KER_MENU_SRC)/%.cpp, $(OBJ_DIR)/%.o, $(KERMENUSOURCESCPP))
#OBJECTS += $(patsubst $(UI_IMAGES)/%.png, $(OBJ_DIR)/%.o, $(UISOURCES))
#Dinobyte objects [include Dinobyte headers here] (do later)
#OBJECTS += $(patsubst $(DINOBYTESOURCES)/src/%.cpp, $(OBJ_DIR)/%.o, $(DINOBYTESOURCES))
#Headers
HEADERS = $(wildcard $(KER_HEAD)/*.h)
IMG_NAME=SuperPiOS
#build: $(OBJECTS) $(HEADERS)
#$(CC) -T linker.ld -o $(IMG_NAME).elf $(LFLAGS) $(OBJECTS) #needs indent
#$(OBJCOPY) $(IMG_NAME).elf -O binary $(IMG_NAME).img #needs indent
#$(OBJ_DIR)/%.o: $(KER_SRC)/%.s
#mkdir -p $(#D) #needs indent
#$(CC) $(CFLAGS) -I$(KER_SRC) -c $< -o $# #needs indent
$(OBJ_DIR)/%.o: $(KER_SRC)/%.s
$(GCCPATHBIN)/aarch64-none-elf-gcc $(GCCFLAGS) -c $(KER_SRC) -o $(OBJ_DIR)
$(OBJ_DIR)/%.o: $(KER_SRC)/%.c
$(GCCPATHBIN)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $#
$(OBJ_DIR)/%.o: $(KER_SRC)/$(ARCHDIR)/%.c
$(GCCPATHBIN)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $#
$(OBJ_DIR)/%.o: $(KER_SRC)/%.cpp
$(GCCPATHBIN) arm-none-eabi-cpp $(GCCFLAGS) -c $< -o $#
$(OBJ_DIR)/%.o: $(KER_SRC)/$(ARCHDIR)/%.cpp
$(GCCPATHBIN)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $#
$(OBJ_DIR)/%.o: $(COMMON_SRC)/%.c
$(GCCPATHBIN)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $#
#$(OBJ_DIR)/%.o: $(KER_MENU_SRC)/%.c
# mkdir -p $(#D)
# $(CC) $(CFLAGS) -I$(KER_SRC) -I$(KER_HEAD) -c $< -o $# $(CSRCFLAGS)
#$(OBJ_DIR)/%.o: $(KER_MENU_SRC)/%.cpp
# mkdir -p $(#D)
# $(CC) $(CXXFLAGS) -I$(KER_SRC) -I$(KER_HEAD) -c $< -o $# $(CSRCFLAGS)
$(IMG_NAME)%.img: $(OBJECTS) $(HEADERS)
$(GCCPATHBIN)/aarch64-none-elf-ld -nostdlib -nostartfiles $(OBJECTS) -T linker.ld -o $(IMG_NAME).elf
$(GCCPATHBIN)/aarch64-none-elf-objcopy -O binary $(IMG_NAME).elf $(IMG_NAME).img
clean:
rm -rf $(OBJ_DIR)
rm -rf $(IMG_NAME).elf
rm $(IMG_NAME).img
run: build
qemu-system-arm -m 128 -no-reboot -M raspi4 -serial stdio -kernel kernel.elf
dbg:
$(GDB) kernel.elf
dbgrun: build gdbinit
qemu-system-arm -m 128 -no-reboot -M raspi4 -serial stdio -kernel kernel.elf -S -s
.PHONY: gdbinit
gdbinit:
echo "target remote localhost:1234" > .gdbinit
echo "break kernel_main" >> .gdbinit
You have two problems. The most obvious one is that this command:
rm SuperPiOS.img
is failing because that's how the rm program is defined: if the file you ask it to delete doesn't exist then rm will fail. If you don't want that to happen, add the -f option:
rm -f SuperPiOS.img
Now if the file doesn't exist, rm will silently succeed. You can examine the man pages for the rm program.
However, the higher level question you are probably asking is, why when I run make is it running the clean rule?
That's because make, if you don't specify what to build on the command line, always builds the first explicit target. In this makefile the first explicit target you defined is clean, so that's what's built. This is almost certainly not what you want. You should figure out what target you want to build when you run make with no arguments and put the rule for that target as the first explicit target in your makefile.
Other than this rule it normally doesn't matter much what order rules come in (if you have multiple pattern rules that could all build the same target, for example multiple pattern rules to build %.o, then order can matter there).

How to ensure `make` rebuilds all objects after deletion of a file

Consider the following Makefile skeleton:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS)
These rules ensure that OBJECTS and executable are recompiled if any of the following files is modified:
Declaration files (*.h)
Implementation files (*.c)
The Makefile itself
This works very nicely. It also covers the case of adding a new source code file to a directory of the project (assuming the new file wasn't added using cp -a or mv). The case that isn't covered is the deletion of a file.
Recompilation on deletion is useful because it catches leftovers in the remaining source code and because it removes superfluous data from the executable.
What is a succinct and efficient way of ensuring the make command rebuilds all objects after a source code file is deleted from the project?
The answer can use any common Linux command via $(shell ...).
The reply provided by MadScientist is very good (particularly regarting the use of a modern auto-generated dependencies method).
If the modern auto-generated dependencies method is not an option, an alternative to using echo and cmp would be to have the dependencies handled by make, using include and $(file ...) (note the extra dependency for $(OBJECTS)):
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS) deps.mk
include deps.mk
deps.mk: $(HEADERS) $(SOURCES)
$(file >$#,$#: $(HEADERS) $(SOURCES))
$(file >>$#,$(HEADERS) $(SOURCES):)
That is only one example and any combination of behavior is possible. For instance if your requirements were:
recompile everything when
a header file is created or modified; or
a header or source file is deleted
otherwise, recompile only the added or modified source files - if any
you could capture the list of deleted files in a MISSING using the filter-out function:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
include missing.mk
ifneq (,$(MISSING))
$(MISSING):
endif
$(OBJECTS): Makefile $(HEADERS) $(MISSING)
missing.mk: $(MISSING) $(HEADERS) $(SOURCES)
$(file >$#,MISSING=$$(filter-out $$(HEADERS) $$(SOURCES),$(HEADERS) $(SOURCES)))
You can create a listfile (name listfile) which contains the list of headers and C sources. If you delete a file from source tree you'll need to re-create this listfile. The listfile should be dependency in Makefile.
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
genlist:
find . -name \*.c -or -name \*.h > listfile
$(OBJECTS): Makefile $(HEADERS) listfile
.PHONY: genlist
After deletion you should run make genlist.
Of course you can expand this idea: you'll create a listfile and every make will generate a temporary listfile (for example use mktemp) and compares to the "official" listfile. If they differ will overwrite the "official" listfile - and don't have to run make genlist after deletion.
Everybody suggests to generate auxiliary makefile. I quite support these suggestions. However I'd prefer to keep isolated generation of this makefile. Here is solution to keep it in a single $(shell ...) command:
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
$(if $(shell find -name '*.h' -or -name '*.c' | \
awk '\
/.*\.h$$/ { h=h " " $$0 } \
/.*\.c$$/ { c=c " " $$0 } \
END { \
print "HEADERS :=" h; \
print "SOURCES :=" c \
}' > $(THIS_MAKEFILE).include.new && \
diff -q $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include 2> /dev/null || mv $(THIS_MAKEFILE).include.new $(THIS_MAKEFILE).include),)
include $(THIS_MAKEFILE).include
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS); $(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c; rm -f $# && $(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): $(THIS_MAKEFILE) $(THIS_MAKEFILE).include $(HEADERS)
$(shell) is wrapped in $(if) to suppress any output from it.
This solution has one $(shell) call instead of two and doesn't depend on relatively new $(file) GNU Make function.
Good for small project, but doesn't scale well.
One way to handle this would be to use a modern auto-generated dependencies method such as the one described here. These methods have built-in properties that handle deleted files correctly.
If you don't want to do that, then something similar to what #uzsolt suggests is what you need to do, but you have to play a trick if you want to avoid the need to run make genlist explicitly before make realizes a file is missing:
HEADERS := $(shell find . -name "*.h" | sort)
SOURCES := $(shell find . -name "*.c" | sort)
OBJECTS := $(patsubst %.c, %.o, $(SOURCES))
executable: $(OBJECTS)
$(CC) $(CFLAGS) -o $# $(OBJECTS)
%.o: %.c
rm -f $#
$(CC) $(CFLAGS) -c -o $# $< || { rm -f $#; exit 1; }
$(OBJECTS): Makefile $(HEADERS) sourcelist
sourcelist: FORCE
#for f in $(SOURCES) $(HEADERS); do echo "$$f"; done > $#.tmp
#[ `comm -23 $# $#.tmp | wc -l` -eq 0 ] || mv $#.tmp $#
FORCE:
The idea here is that you compare the old list to the new list and only modify the list if the old list contains something that the new list doesn't. This ensures that unless something is deleted the timestamp for sourcelist doesn't change, and so it won't force the object files to be out of date.

I want to generate objects in subdirectory by makefile

How to specify a subdirectory for the generated .o files, e.g. objsdir, for this code in the make file
cc = gcc
CFLAGS = -o6 -Wall
LIBS = -lm
SOURCES = main.c\
extra.c
OBJECTS = $(SOURCES: .c = .o)
.c.o :; $(cc) -c $(CFLAGS) $<
all : $(OBJECT)
$(cc) -o ...
Actually this is a part of the mkefile code.
I usually use something like this:
cc=gcc
SOURCES=main.c extra.c
OBJECTS=$(patsubst %.c, objsdir/%.o, $(SOURCES))
objsdir/%.o: %.c
echo $^
all: $(OBJECTS)
echo $(OBJECTS)
I actually use this Makefile in general
CC := g++ # This is the main compiler
SRCDIR := src
BUILDDIR := build
TARGETDIR :=bin/
TARGET := pddlcrate
DATADIR := data
SRCEXT := cpp
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))
CFLAGS := -g # -Wall
#LIB := -pthread -lmongoclient -L lib -lboost_thread-mt -lboost_filesystem-
mt -lboost_system-mt
INC := -I include
$(TARGET): $(OBJECTS)
#echo " Linking..."
#echo " $(CC) $^ -o $(TARGETDIR)$(TARGET) $(LIB)"; $(CC) $^ -o
$(TARGETDIR)$(TARGET) $(LIB)
$(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)
#mkdir -p $(BUILDDIR)
#echo " $(CC) $(CFLAGS) $(INC) -c -o $# $<"; $(CC) $(CFLAGS) $(INC) -c - o $# $<
clean:
#echo " Cleaning...";
#echo " $(RM) -r $(BUILDDIR) $(TARGET)"; $(RM) -r $(BUILDDIR) $(TARGET)
for any c++ project with this tree structure
$ tree .
├── Makefile
├── bin
>exefile
├── include
> *.h files
├── obj
> *.o files
├── src
>*.cpp

Automatic dependency processing in Makefile

I have a simple makefile.
IDIR =./include
CC=gcc
CFLAGS=-I$(IDIR)
SRCDIR = ./src
ODIR=obj
LDIR =./lib
LIBS=-lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ hellomake
What I don't like about this Makefile is that the dependency generation.
.c.o
Is it possible to teach make file that the .c is in src directory, and .o is in obj directory to make this simple one?
.c.o:
$(CC) -c -o $# $< $(CFLAGS)
header dependency
Is it possible to teach make file that all the c files are recompiled automatically when header files that the c file contains is changed?
Preliminaries
Generating dependencies
gcc's -MM -MG can be used to create the dependencies: gcc -MM -MG src/hellofunc.c -I./include will create hellofunc.o: src/hellofunc.c include/hellomake.h. However, we need to include the .d file itself into the .d file, so we use sed to achieve that goal.
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's#^\(.*\)\.o:#\1.d \1.o:#'
The results will be as follows:
hellofunc.d hellofunc.o: src/hellofunc.c include/hellomake.h
When we want to change the content to include the different directory location, we can modify the sed script.
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's#^\(.*\)\.o:#obj/\1.d obj/\1.o:#'
The results will be as follows:
obj/hellofunc.d obj/hellofunc.o: src/hellofunc.c include/hellomake.h
include
-include $(DEPS) will include the files in DEPS directory, the - in front of the include teaches the make to ignore when the directories does not exits.
Pattern matching and replacement
We can substitute any pattern (%) or pattern that ends with c (%.c) as follows:
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
We also can have shorthand form.
DEPS := $(OBJS:.o=.d)
filter
We can select only some of the files with filter, this is an example:
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
Method 1
In this method, we specify the dependency (.d depends on .c), and include the created dependency file with include.
.PHONY: clean depend
CC := gcc
CXX := g++
LD := g++
CP := cp
PROG := hellomake
MODULES := src
OBJDIR := obj
default: $(PROG)
OPTFLAGS := -g -O
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
GARBAGE := core core.* *.stackdump ./tags $(PROG)
include $(patsubst %, %/module.make, $(MODULES))
OBJ := \
$(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
# implicit rules
%.d: %.c
./depends.sh $(CC) `dirname $*.c` $(CFLAGS) $*.c > $#
-include $(DEP)
# Actual targets
depend: $(DEP)
clean:
rm -rf $(PROG) $(OBJ) $(GARBAGE) $(DEP) depends
$(PROG): $(OBJ)
$(LD) -o $# $^ $(LIBS)
Each module should contain the make file (module.make) to specify what files are included in the SRC variable.
`SRC += src/hellofunc.c \
src/hellomake.c`.
This is the script to generate the dependency file:
#!/bin/sh
#echo "## Got: $*"
CC="$1"
DIR="$2"
shift 2
case "$DIR" in
"" | ".")
$CC -MM -MG "$#" | sed -e 's#^\(.*\)\.o:#\1.d \1.o:#'
;;
*)
$CC -MM -MG "$#" | sed -e "s#^\(.*\)\.o:#$DIR/\1.d $DIR/\1.o:#"
;;
esac
This method is simple, but the object files and dependency files are created in the same src directory with these statements.
SRC += src/hellofunc.c src/hellomake.c
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
Method 2
This method creates the object directory, and puts all the generated (intermediate) files into the directory.
It enlists all the modules to specify the actions both for object file and dependency file generation.
In the example, we have only one module, but with multiple modules, we need to duplicate the statements.
obj/src/%.o: src/%.c
$(CC) -c $< -o $# $(CFLAGS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's#^\(.*\)\.o:#obj/src/\1.d obj/src/\1.o:#' > $#
This is the makefile:
.SUFFIX = .o .c
.PHONY: clean
CC := gcc
LD := gcc
PROG := hellomake
OBJDIR = obj
MODULES := src
SRCS := src/hellofunc.c src/hellomake.c
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
DEPS := $(OBJS:.o=.d)
default: $(PROG)
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
CXXFLAGS += $(CFLAGS)
obj/src/%.o: src/%.c
$(CC) -c $< -o $# $(CFLAGS)
$(PROG): $(OBJDIRS) $(OBJS)
$(LD) $(filter %.o, $^) -o $(PROG)
-include $(DEPS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's#^\(.*\)\.o:#obj/src/\1.d obj/src/\1.o:#' > $#
depend: $(DEPS)
GARBAGE := core core.* *.stackdump ./tags $(PROG)
clean:
rm -rf $(PROG) obj/*
$(OBJDIRS):
mkdir -p $#
References
Create directories when generating object files in gcc
How to place object files in separate subdirectory
make deleting dependency files
http://scottmcpeak.com/autodepend/autodepend.html
IDIR = ./include
CC = gcc
CFLAGS = -I$(IDIR)
SRCDIR = src
ODIR = obj
LDIR = ./lib
LIBS = -lm
SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}
_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}
%.o: ../$(SRCDIR)/%.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
hellomake: $(OBJ) $(DEPS)
gcc -o $# $^ $(CFLAGS) $(LIBS)

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.
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'
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
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)

Resources