How to make top level Makefile to call all targets in subdirectory Makefile ?
My folder structure is like this
/Makefile
/src/Makefile
I code all targets in /src/Makefile, but now I want to write another /Makefile to ease my work. How to write this top level Makefile ?
You could write in your TOP makefile use
all:
#$(MAKE) -C src
and if you do NOT use makefile in sub dir, for example, you use somename.mk, you could use
all:
#$(MAKE) -C src -f somename.mk
I show you my example, my DIR looks like:
TOPDIR-- Makefile
|
|-- debug
| |-- debug.c
| |-- debug.h
| |-- debug.mk
| |-- instrument.c
| `-- uart_print.c
|-- driver
| |-- driver.c
| |-- driver_ddi.c
| |-- driver_ddi.h
| |-- driver.h
| `-- driver.mk
|-- include
| `-- common.h
|-- Makefile
|-- mw
| |-- manager.c
| `-- mw.mk
|-- root
| |-- main.c
| `-- root.mk
and my TOP makefile looks like:
MAKE_DIR = $(PWD)
ROOT_DIR := $(MAKE_DIR)/root
DRV_DIR := $(MAKE_DIR)/driver
INCLUDE_DIR := $(MAKE_DIR)/include
DEBUG_DIR := $(MAKE_DIR)/debug
INC_SRCH_PATH :=
INC_SRCH_PATH += -I$(ROOT_DIR)
INC_SRCH_PATH += -I$(DRV_DIR)
INC_SRCH_PATH += -I$(INCLUDE_DIR)
INC_SRCH_PATH += -I$(DEBUG_DIR)
LIB_SRCH_PATH :=
LIB_SRCH_PATH += -L$(MAKE_DIR)/libs
COLOR_ON = color
COLOR_OFF =
CC = $(COLOR_ON)gcc
#CC = $(COLOR_OFF)gcc
LD = ld
LINT = splint
LIBS := -ldriver -ldebug -lmw -lm -lpthread
CFLAGS :=
CFLAGS += $(INC_SRCH_PATH) $(LIB_SRCH_PATH)
CFLAGS += -Wall -O -ggdb -Wstrict-prototypes -Wno-pointer-sign -finstrument-functions -fdump-rtl-expand
CFLAGS += -DDEBUG -D_REENTRANT
LDFLAGS :=
export MAKE_DIR CC LD CFLAGS LDFLAGS LIBS LINT INC_SRCH_PATH
all:
#$(MAKE) -C debug -f debug.mk
#$(MAKE) -C driver -f driver.mk
#$(MAKE) -C mw -f mw.mk
#$(MAKE) -C root -f root.mk
.PHONY: clean
clean:
#$(MAKE) -C debug -f debug.mk clean
#$(MAKE) -C driver -f driver.mk clean
#$(MAKE) -C mw -f mw.mk clean
#$(MAKE) -C root -f root.mk clean
.PHONY: lint
lint:
$(MAKE) -C debug -f debug.mk lint
it will call sub DIR *.mk during the compile. The sub DIR makefile, I just write a simple example for you reference:
LIB = $(MAKE_DIR)/libs/yourmodulename.a
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))
$(LIB): $(OBJS)
#mkdir -p ../libs
#$(AR) cr $# $^
#echo " Archive $(notdir $#)"
$(OBJS): $(SRCS)
#$(CC) $(CFLAGS) -c $^
#echo " CC $(OBJS)"
.PHONY: clean
clean:
#$(RM) -f $(LIB) $(OBJS)
#$(RM) -f *.expand
#echo " Remove Objects: $(OBJS)"
#echo " Remove Libraries: $(notdir $(LIB))"
.PHONY: lint
lint:
$(LINT) $(INC_SRCH_PATH) $(SRCS)
for the makefile, which generate the target file is little bit different, because I use the sub makefile to generate LIB file, and I use a root.mk to generate target:
PROG = ../prog/DEMO
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))
$(PROG): $(SRCS)
#mkdir -p ../prog
#$(CC) $^ $(CFLAGS) -Wl,-Map=$(PROG).map $(LIBS) -o $#
#echo " Generate Program $(notdir $(PROG)) from $^"
.PHONY: clean
clean:
#$(RM) -f $(OBJS) $(PROG)
#$(RM) -f *.expand
#$(RM) -rf ../prog ../libs
#echo " Remove Objects: $(OBJS)"
#echo " Remove Libraries: $(notdir $(PROG))"
Beware, note there are, at least, two ways of doing this.
GNU Changedir Option
One way is using a specific feature of GNU Make, the -C option allow to change your compile directory and reach another one:
all:
make -C dir
The make manual says:
-C dir, --directory=dir
Change to directory dir before reading the makefiles or doing anything
else. If multiple -C options are specified, each is interpreted relative
to the previous one: -C / -C etc is equivalent to -C /etc. This is
typically used with recursive invocations of make.
You can also combine this option by calling a specific target inside the targeted directory. For example, the following target will get into the src/ directory and call make with the clean target:
clean:
#rm -f *.o
make -C src/ clean
POSIX Way
The problem with the GNU way of doing is that it only works with GNU Make, and not the standard Make. If you may use another Make (for whatever reason), you better consider doing it in a more POSIX way.
In POSIX Make, you have to rely more on the cd command, like this:
all:
cd src/ && make
Note that, I used && and not ;. It is quite important to avoid infinite recursive calls to make. Indeed, cmd1 ; cmd2 will execute sequentially cmd1 and cmd2 whatever is the result of each command, where cmd1 && cmd2 will execute sequentially cmd1, and cmd2 will be executed only if cmd1 returned an EXIT_SUCCESS. In our case, imagine that the first cd failed because the directory has been removed. Then, the initial makefile will be executed again and again in an infinite recursive loop.
Anyway, this POSIX manner, is the more robust way of descending in subdirectories and execute other Makefile. I would recommend you to use it better than relying on an option linked to GNU Make.
And if you want to do multiple dirs easily:
SUBDIRS=dir1 dir2
all::
#echo make all
$(foreach var,$(SUBDIRS),echo $(var): ; cd $(var)/ && make $# && cd ..;)
clean:
#echo make clean
$(foreach var,$(SUBDIRS),echo $(var): ; cd $(var)/ && make $# && cd ..;)
Related
Since sdcc have some limitations, like compiling one file at a time, I've tried hard to write a Makefile to perfect the automation of MCS-51 development, which have some requirements:
Source file (.c) expect main.c are stored in ProjectFolder/Sources/, while main.c are stored at the root of project folder.
Headers are stored in ProjectFolder/Includes/.
Outputs through compiling, linking and locating should be stored at ProjectFolder/Builds/
Makefile should be smart enough to find all source files, instead of type their file name by hand.
Makefile should be smart enough to if there are some files in Sources/, or there's only main.c in the project.
The file structure can be depicted like:
Project Folder
|
|- Sources
| |
| |(some source files, but OPTIONAL)
|
|- Includes
| |
| |(some headers, but OPTIONAL)
|
|- Builds
| |
| |(some .rel .o .hex files. OUTPUT here)
|
|- main.c
|
|- Makefile
Here's my solution but still have a problem. It cannot be used for project only have one file main.c which means no source file in Sources/.
INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc
CFLAGS = -o $(BUILDS)
LOADER = stcgal
LOADER_FLAGS = -P stc89
$(BUILDS)main.ihx: main.c $(BUILDS)main.rel
# Link
#$(CC) main.c $(shell find $(BUILDS) -name "*.rel" -not -name "main.rel" -maxdepth 1) $(CFLAGS)
#echo Link & Locate Succeeded
$(BUILDS)main.rel: $(SOURCES) $(BUILDS)
# Compile
#for f in $(shell ls $(SOURCES)*.c) ; do \
$(CC) -c $${f} $(CFLAGS) ; \
done
#echo Compile Succeeded
$(SOURCES):
#mkdir $(SOURCES)
$(BUILDS):
#mkdir $(BUILDS)
clean:
# Remove all files in build folder
#rm $(BUILDS)*
#echo Build Folder Cleaned
load: $(BUILDS)main.ihx
# Load data to MCU via USB port
#$(LOADER) $(LOADER_FLAGS) -p $(shell ls /dev/tty.usbserial*) $(BUILDS)main.ihx
Let's try something. First note that I've not looked at the load target.
Let's start with the same definition as you:
INCLUDES = Includes/
SOURCES = Sources/
BUILDS = Builds/
CC = sdcc
We need a variable with the source files from Sources. GNU Make has a wildcard functions which does the same thing as your find. See that I'm using := to have an immediate expansion of the value, so the wildcard will not be executed several times.
SRCFILES := $(wildcard $(SOURCES)*.c)
Now a variable with the .rel files. It is build from main.rel and the SRCFILES value:
RELFILES := $(BUILDS)main.rel $(SRCFILES:$(SOURCES)%.c=$(BUILDS)%.rel)
Let's define another variable with the flag to pass so the Includes directory is searched:
CPPFLAGS = -I$(INCLUDES)
Now we can define pattern rules to describe how to build .rel files from .c files. I'm using an order-only prerequisite for the build directory:
$(BUILDS)%.rel: $(SOURCES)%.c | $(BUILDS)
$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $# -c $<
$(BUILDS)%.rel: %.c | $(BUILDS)
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
Let's define some usability targets:
.PHONY: all clean
all: $(BUILDS)main.ihx
clean:
rm $(BUILDS)*
And finally define how to build the targets which aren't handled by the pattern rules:
$(BUILDS)main.ihx: $(RELFILES) | $(BUILDS)
$(CC) $(LDFLAGS) -o $# $^ $(LOADLIBES) $(LDLIBS)
$(BUILDS):
mkdir $(BUILDS)
I've used a few variables (CC, CPPFLAGS, CFLAGS, LDFLAGS, LOADLIBES, LDLIBS) in the same way as they are used by the built-in rules of GNU Make.
I've kept your makefile behavior. There are good reasons to have Makefiles targeting to build in the current directory. Explaining them and modifying the Makefile for that is out of scope for this answer, you may look at MadScientist's GNU Make White Papers and the GNU Make Manual.
Suppose there are a.c, b.c, c.c, tt/at.c, tt/bt.c, fff/af.c, fff/bf.c. So I make Makefile like this:
OBJS=a.o b.o c.o
SRCS=$(OBJS:.o=.cc)
OBJS_TT=at.o bt.o
SRCS_TT=tt/at.c tt/bt.c
OBJS_FFF=af.o bf.o
SRCS_FFF=fff/af.c fff/bf.c
TARGET=test
.cc.o:
gcc -c $<
$(OBJS_TT):
gcc -c $(SRCS_TT)
$(OBJS_FFF):
gcc -c $(SRCS_FFF)
all:
gcc -o $(TARGET) $(OBJS) $(OBJS_TT) $(OBJS_FFF)
If C files in tt directory are added, I have to add file name in SRCS_TT & OBJS_TT.
Is there a way to improve make file?
How do I do all c files (including files in directory) using suffix rules ?
One common solution when source files reside in different directories is to build an isomorphic directory structure in the build directory. This way you can have multiple sources with the same file name (e.g. util.cc) but in different directories and the object files won't collide because they get built into different directories.
A working example that creates an isomorphic build directory structure also with auto-generated header dependencies:
build_dir := build
all : ${build_dir}/test
.SECONDEXPANSION:
.SECONDARY:
test_srcs := a.cc b.cc x/c.cc y/d.cc
${build_dir}/test : ${test_srcs:%.cc=${build_dir}/%.o} | ${build_dir}/
g++ -o $# ${LDFLAGS} ${LDLIBS} $^
# Include the auto-generated dependencies.
-include ${test_srcs:%.cc=${build_dir}/%.d}
# Compile and generate dependency files.
${build_dir}/%.o : %.cc | $$(dir $$#)
g++ -o $# -c -MD -MP ${CPPFLAGS} ${CXXFLAGS} $<
# Directory build rules. while loop to handle races on mkdir during parallel builds.
${build_dir}/%/ : | ${build_dir}/
while ! mkdir -p $#; do true; done
# Build root directory rule.
${build_dir}/ :
mkdir -p $#
# Don't try to rebuild these, these are generated when compiling.
${build_dir}/%.d : ;
clean :
rm -rf ${build_dir}
.PHONY : all clean
Usage example:
$ mkdir x y
$ touch b.cc x/c.cc y/d.cc
$ echo "int main() {}" > a.cc
$ make
mkdir -p build/
g++ -o build/a.o -c -MD -MP a.cc
g++ -o build/b.o -c -MD -MP b.cc
while ! mkdir -p build/x/; do true; done
g++ -o build/x/c.o -c -MD -MP x/c.cc
while ! mkdir -p build/y/; do true; done
g++ -o build/y/d.o -c -MD -MP y/d.cc
g++ -o build/test build/a.o build/b.o build/x/c.o build/y/d.o
$ tree build/
build/
├── a.d
├── a.o
├── b.d
├── b.o
├── test
├── x
│ ├── c.d
│ └── c.o
└── y
├── d.d
└── d.o
2 directories, 9 files
$ make
make: Nothing to be done for 'all'.
$ make clean
rm -rf build
Compiling with Makefile tells me that it can't find path to some include files.
Here's my directory layout:
build (empty directory)
include (directory)
tpu_uarch (directory)
buffer.hpp common.hpp controller.hpp cpu.hpp
dram.hpp interconnect.hpp mmu.hpp unit.hpp weightfetcher.hpp
obj (directory)
tpu_uarch (empty directory)
src (directory)
test_mmu.cpp test_tile.cpp
buffer.cpp common.cpp controller.cpp cpu.cpp
dram.cpp interconnect.cpp mmu.cpp weightfetcher.cpp
Makefile
And here's what Makefile looks like:
TESTTILE := ./build/testtile.exe
TEST3 := ./build/test3.exe
CC := g++
CPP_SUFFIX := cpp
INCLUDE_DIR := -I./include
SRC_DIR = ./src
OBJ_DIR = ./obj
CFLAGS := -g -Wall -std=c++11
LDFLAGS :=
LIBS :=
# all sources
SRC = $(wildcard $(SRC_DIR)/*.$(CPP_SUFFIX))
SRC += $(wildcard $(SRC_DIR)/**/*.$(CPP_SUFFIX))
# objects
OBJ = $(patsubst $(SRC_DIR)/%.$(CPP_SUFFIX), $(OBJ_DIR)/%.o, $(SRC))
DIR = $(dir $(OBJ))
# executables
testtile: dir $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(TESTTILE) $(LIBS)
test3: dir $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $(TEST3) $(LIBS)
dir:
mkdir -p $(DIR)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(CPP_SUFFIX)
$(CC) $(INCLUDE_DIR) $(CFLAGS) -c $< -o $#
clean:
rm -rf $(OBJ_DIR)
rm $(TESTTILE)
rm $(TEST3)
When I write make test3 in command line, I get the following error.
mkdir -p ./obj/ ./obj/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/ ./obj/tpu_uarch/
g++ -I./include -g -Wall -std=c++11 -c src/test_tile.cpp -o obj/test_tile.o
src/test_tile.cpp:1:22: fatal error: common.hpp: No such file or directory
compilation terminated.
Makefile:44: recipe for target 'obj/test_tile.o' failed
make: *** [obj/test_tile.o] Error 1
In all of the *.cpp files, they add include files like #include "common.hpp" without adding directory information. As I understand, adding the -I flag in INCLUDE_DIR should solve problems of finding the include files.
I've checked earlier that the code compiles if I put all .cpp and .hpp files in one directory and type g++ -g -Wall -std=c++11 -o test3.exe buffer.cpp common.cpp controller.cpp cpu.cpp dram.cpp interconnect.cpp mmu.cpp weightfetcher.cpp test_mmu.cpp in the command line interface.
In making test3.exe, test_tile.cpp is not used but I don't think that should be a problem.
Is there something I'm missing or should look into?
Any help or push in the right direction would be greatly appreciated.
[Moved from comments]
You're telling g++ to look in ./include but, according to the directory hierarchy shown, common.hpp and the other headers are actually under ./include/tpu_uarch. You need...
INCLUDE_DIR := -I./include/tpu_uarch
For those who came to find an answer... Even after fixing the issue that G.M. pointed out, I still had some minor issues. (Such as conflict in that both test files include the main() function, etc.)
After that, I managed to fix the code to work.
Here is the working version of Makefile.
TESTTILE := ./build/testtile.exe
TESTMMU := ./build/testmmu.exe
CC := g++
CPP_SUFFIX := cpp
INCLUDE_DIR := -I./include/tpu_uarch
SRC_DIR = ./src
OBJ_DIR = ./obj
BUILD_DIR = ./build
CFLAGS := -g -Wall -std=c++11
LDFLAGS :=
LIBS :=
# all sources
SRC = $(wildcard $(SRC_DIR)/*.$(CPP_SUFFIX))
SRC += $(wildcard $(SRC_DIR)/**/*.$(CPP_SUFFIX))
# objects
OBJ = $(patsubst $(SRC_DIR)/%.$(CPP_SUFFIX), $(OBJ_DIR)/%.o, $(SRC))
DIR = $(dir $(OBJ))
# for tests
TESTTILE_OBJ := ./obj/test_tile.o
TESTMMU_OBJ := ./obj/test_mmu.o
# executables
testtile: dir $(OBJ) $(TESTTILE_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) $(TESTTILE_OBJ) -o $(TESTTILE) $(LIBS)
testmmu: dir $(OBJ) $(TESTMMU_OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) $(TESTMMU_OBJ) -o $(TESTMMU) $(LIBS)
dir:
mkdir -p $(DIR)
obj/test_tile.o: $(BUILD_DIR)/test_tile.cpp
$(CC) $(INCLUDE_DIR) $(CFLAGS) -c ./build/test_tile.cpp -o ./obj/test_tile.o
obj/test_mmu.o: $(BUILD_DIR)/test_mmu.cpp
$(CC) $(INCLUDE_DIR) $(CFLAGS) -c ./build/test_mmu.cpp -o ./obj/test_mmu.o
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.$(CPP_SUFFIX)
$(CC) $(INCLUDE_DIR) $(CFLAGS) -c $< -o $#
clean:
rm -rf $(OBJ_DIR)
rm $(TESTTILE)
rm $(TEST3MMU)
The directory topology is as follows:
build (directory)
test_mmu.cpp test_tile.cpp
testmmu.exe testtile.exe (executables created after running make)
include (directory)
tpu_uarch (directory)
buffer.hpp common.hpp controller.hpp cpu.hpp
dram.hpp interconnect.hpp mmu.hpp unit.hpp weightfetcher.hpp
obj (directory, all contents here including directory are created after running make)
tpu_uarch (directory)
buffer.o common.o controller.o cpu.o
dram.o interconnect.o mmu.o weightfetcher.o
test_mmu.o
test_tile.o
src (directory)
tpu_uarch (directory)
buffer.cpp common.cpp controller.cpp cpu.cpp
dram.cpp interconnect.cpp mmu.cpp weightfetcher.cpp
Makefile
Hope this helps.
Currently I am trying to get a rather big project of mine to work with a Makefile. I used Make before but in a rather crude way and not really "dynamic", this means I am pretty new to good Makefiles.
My Makefile looks like this:
INCLUDE_DIR = /inc
SOURCE_DIR = /src
BUILD_DIR = /build
BUILD_NAME = build
CC = arm-none-eabi-gcc
CFLAGS = -I$(INCLUDE_DIR)
_INCLUDES = main.h pfc.h
INCLUDES = $(patsubst %, $(INCLUDE_DIR)/%, $(_INCLUDES))
_OBJ = main.o pfc.o
OBJ = $(patsubst %, $(BUILD_DIR)/%, $(_OBJ))
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(INCLUDES)
$(CC) -c -o $# $< $(CFLAGS)
$(BUILD_NAME): $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
all: $(BUILD_NAME)
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)/*
When I make the file I get this:
make: *** No rule to make target '/build/main.o', needed by 'build'. Stop.
I guess it is an error in this recipe:
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(INCLUDES)
$(CC) -c -o $# $< $(CFLAGS)
Sadly I am not able to get this done on my own.
All files a available in the correct folders.
I really appreciate all the help!
Tristan
You have a few issues here that I can see. But first off - just check (just incase) that all your rules are only indented with tabs and not spaces..... this can be a real "silent" killer of makefiles as they give crap error messages.
Ok so - lets assume you have:
INCLUDE_DIR = inc
SOURCE_DIR = src
BUILD_DIR = build
instead of /src etc.. as mentioned in the comments.
Do you really have inc/main.h and inc/pfc.h?
I copied and pasted your makefile added your src and inc folders (but I used gcc instead of arm-none-eabi-gcc. It did the compile lines correctly, but failed at the linker stage because you are trying to build an output file called build when there is already a folder called build (not allowed in linux - maybe ok for windows but I don't recommend).
I made an answer for another question - but it might be a better start point then you have here in the case where you have nested src/inc directories and you want to be able to clean your output folders - ill put it here for convenience:
# Get your source list (use wildcard or what ever, but just for clarity you should end up with a list of files with full paths to start with):
# Output folders/targets
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
OUTPUT_FILE = output
# Generate list of source files - this is a linux command - but you can do this in pure make using wildcard and such).
SOURCES := $(shell find $(SOURCEDIR) -name '*.c')
# Create your objects list in the obj directory
OBJECTS = $(addprefix $(OBJ_DIR)/,$(addsuffix .o,$(basename $(SOURCES))))
# Create list of unique folders to create
DIRS = $(sort $(dir $(OBJECTS))) $(BIN_DIR)
# Create list of include paths
INCS = $(addprefix -I,$(sort $(dir $(SOURCES))))
# Main target rule
$(BIN_DIR)/$(OUTPUT_FILE): $(OBJECTS) | $(DIRS)
#echo linker: gcc $(OBJECTS) -o $#
#touch $#
# Rule to build your object file - ensure that the folders are created first (also create a dummy obj file) - note this works for parallel builds too (make -j
$(OBJ_DIR)/%.o: %.c | $(DIRS)
#echo compile: gcc $(INCS) -c $? -o $#
#touch $#
# Create your directories here
$(DIRS):
#echo Creating dir: $#
#mkdir -p $#
# Clean if needed
.PHONY: clean
clean:
rm -rf $(OBJ_DIR) $(BIN_DIR)
Note this is just a template, you still need to fill in the gcc/makefile flags - but its a decent start point...
Debugging
$(info ...) is your friend - for example you could do:
$(info OBJ = $(OBJ))
$(info objrule = $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(INCLUDES))
To print our what make has expanded these variables / lines to be - this can yield useful debug.
Here is another version of your makefile with automatic dependency generation:
INCLUDE_DIR := inc
SOURCE_DIR := src
BUILD_DIR := build
CC := arm-none-eabi-gcc
CPPFLAGS := -I$(INCLUDE_DIR)
exes := build
build.obj := main.o pfc.o
all : ${exes:%=${BUILD_DIR}/%}
.SECONDEXPANSION:
${BUILD_DIR}:
mkdir -p $#
# Rule to link all exes.
${exes:%=${BUILD_DIR}/%} : ${BUILD_DIR}/% : $$(addprefix ${BUILD_DIR}/,$${$$*.obj}) | $${#D}
${CC} -o $# ${LDFLAGS} $^ ${LDLIBS}
# Rule to compile C sources. And generate header dependencies.
${BUILD_DIR}/%.o : ${SOURCE_DIR}/%.c | $${#D}
${CC} -o $# -c ${CPPFLAGS} ${CFLAGS} -MD -MP $<
# Include automatically generated header dependencies.
ifneq ($(MAKECMDGOALS),clean)
-include $(foreach exe,${exes},$(patsubst %.o,${BUILD_DIR}/%.d,${${exe}.obj}))
endif
clean:
rm -rf $(BUILD_DIR)
.PHONY: all clean
To add another executable target do:
exes += another
another.obj := another_main.o another_pfc.o
I have the following directory structure.
Project/
├── bin/
├── src/
│ ├── main.c
│ ├── util/
│ ├── util.c
│ ├── util.h
├── obj/
├── .depend/
All my source code are in the src folder. In the src root is my main.c file; which includes other files that are on the same level that he (or within a same level folder). I have a Makefile below that works well for all files in the same level of main.c but does not work on files in a subfolder within src
How change my Makefile to allow subfolder within the src folder?
CC := gcc
CFLAGS := -Wall -Wextra
BINDIR := bin
OBJDIR := obj
SRCDIR := src
DEPDIR := .depend
SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SRCDIR)/%.c, $(DEPDIR)/%.d, $(SOURCES))
$(BINDIR)/app: $(OBJECTS) | $(BINDIR)
#$(CC) -o $# $^
-include $(DEPENDS)
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
#$(CC) $(CFLAGS) -c -o $# $<
$(DEPDIR)/%.d: $(SRCDIR)/%.c | $(DEPDIR)
#$(CC) -MM -MG $< | sed 's!^\(.\+\).o:!$(DEPDIR)/\1.d $(OBJDIR)/\1.o:!' > $#
$(DEPDIR) $(BINDIR) $(OBJDIR):
#mkdir $#
clean:
#rm -rf $(BINDIR)/*
#rm -rf $(OBJDIR)/*
.PHONY: clean
EDIT: the .o and .d files do not need to respect the original design of the structure. And I'm using Windows (MinGW)
First, you'll have to change your SOURCES to recursively find the sources. This can be done in pure make:
subdirs = $(filter-out $1,$(sort $(dir $(wildcard $1*/))))
rfind = $(wildcard $1$2) $(foreach d,$(call subdirs,$1),$(call rfind,$d,$2))
SOURCES := $(call rfind,$(SRCDIR)/,*.c)
Everything else will work, except for directory creation. First, change your prerequisites to use $(#D) with secondary expansion:
.SECONDEXPANSION:
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $$(#D)
...
$(DEPDIR)/%.d: $(SRCDIR)/%.c | $$(#D)
...
Then, change your directory creation rule to include all the directories:
$(BINDIR) $(patsubst %/,%,$(sort $(dir $(OBJECTS) $(DEPENDS)))):
#mkdir -p $#
Like the recursive find, it uses sort to deduplicate the directories (otherwise make will warn) and strips the trailing slash (because $(#D) won't have a trailing slash). Note that -p is needed to avoid issues with order and with directories only containing other directories and no sources.
try to set your sources like this:
SOURCES := $(shell find $(SRCDIR) -type f -name "*.c")
if it does not work right a way, try to debug it by just running in your command line
find src -type f -name "*.c"
and see if it outputs the correct list of files and with correct relative path, adjust accordingly.
Note, that this approach works only in unix-like environment, if you are using MinGW from MSYS or Cygwin environment it should work.