How to write a good and efficient makefile - c

I have following folder structure:
TOPDIR
|
├── a
│   ├── a.c
│   ├── a.h
│   └── a.mk
├── b
│   ├── b.c
│   ├── b.h
│   └── b.mk
├── c
│ ├── c.c
│ ├── c.h
│ └── c.mk
├── include
│   └── common.h
├── root
│    ├── main.c
│    └── root.mk
└── Makefile
per-condition
My target is to write main Makefile under TOPDIR and sub-makefile, *.mk in sub folder, the include folder contain some common defines. root folder contain my main file(main function located here). Meanwhile, in main.c, it will call function from a.c and b.c, c.c is driver related, and will be called from a.c and b.c
Problem
I wrote sub-makefile like(I use one a.mk for example, others are same, ONLY root.mk has little different):
#MODULE will be modified for each sub folder
MODULE = a
LIB = $(MAKE_DIR)/libs/lib$(MODULE).a
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))
#generate lib file from obj file
$(LIB): $(OBJS)
#mkdir -p ../libs
#$(AR) cr $# $^
#echo " Archive $(notdir $#)"
#compile obj file from source file
$(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))"
I wrote root.mk like:
PROG = ../prog/DEMO
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))
#generate finial target file for run
$(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))"
I wrote main Makefile 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
CC = gcc
LD = ld
#problem happan here, if I change the sequence of LIB,
#during the finial link, it will find some function un-referenced,
#why can I put liba first?
LIBS := -lc -lb -la
CFLAGS :=
CFLAGS += $(INC_SRCH_PATH) $(LIB_SRCH_PATH)
CFLAGS += -Wall -O -ggdb
CFLAGS += -DDEBUG -D_REENTRANT
LDFLAGS :=
export MAKE_DIR CC LD CFLAGS LDFLAGS LIBS LINT INC_SRCH_PATH
all:
#$(MAKE) -C a -f a.mk
#$(MAKE) -C b -f b.mk
#$(MAKE) -C c -f c.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
Question
In main Makefile, I define which LIB file I will use, if need move it to root.mk for better?
In sub-makefile, I did NOT use -MM to generate depend file, if this cause the problem I can NOT change the sequence of my lib*, which I also described in Makefile comments.
Seems my makefile system can NOT detect I update some head file, for example, I first compiled whole code, and then, I modified one head file, when I try to re-compile, none of source is compiled
if:
#Automatic dependency magic:
%.d: src/%.c
$(CC) -MM -o$# $<
-include (MYPROG_OBJECTS:%.o=%.d)
need add into each sub-makefile?

This rule is definitely wrong:
$(OBJS): $(SRCS)
#$(CC) $(CFLAGS) -c $^
#echo " CC $(OBJS)"
The target line will expand to something like:
a.o b.o c.o d.o : a.c b.c c.c d.c
That's not right. It is identical to writing this:
a.o : a.c b.c c.c d.c
...
b.o : a.c b.c c.c d.c
...
c.o : a.c b.c c.c d.c
...
d.o : a.c b.c c.c d.c
...
This means that whenever you change any source file, ALL the object files will be rebuilt. You should use a pattern rule here:
%.o : %.c
#$(CC) $(CFLAGS) -o $# -c $<
#echo " CC $#"
to compile the object files one at a time.
As far as your questions go, I don't understand question #1.
Questions #2 and #3 (if I understand correctly) are the same thing: the reason for #3 (no files are recompiled when you change a header file) is that you're not declaring any prerequisites on header files. Make doesn't have any built-in support for this, so you either have to do it by hand (add a.o : a.c b.h c.h g.h to your makefiles) or else automatically generate the dependencies.
The dependency generation will typically use the -MM or similar flags, assuming your compiler supports these flags.

Related

Makefiles do not have recursive build subdirectories

I have the following directory structure. I try to compile all files into object files.
├── Makefile
├── prime_probe.c
└── utils
├── Makefile
├── caches_info.c
├── caches_info.h
├── caches_util.c
├── caches_util.h
├── configure.h
├── list_struct.h
├── list_utils.c
└── list_utils.h
Makefile
THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
OBJDIR = obj
CFLAGS = -I./$(OBJDIR) -g -fPIC -std=gnu99
UTILS_DIR := utils
OBJS_UTL = $(addprefix $(UTILS_DIR)/$(OBJDIR)/,$(notdir $(patsubst %.c,%.o,$(shell find $(UTILS_DIR)/*.c))))
SRCS_PRIME = \
prime_probe.c
OBJS_PRIME = $(addprefix $(OBJDIR)/,$(patsubst %.c,%.o,$(SRCS_PRIME)))
all: clean utils $(OBJS_PRIME)
$(OBJDIR)/%.o: %.c | objdir
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$(notdir $#)
utils:
$(MAKE) -C $(UTILS_DIR)
objdir:
#mkdir -p $(OBJDIR)
clean:
rm -rf $(OBJDIR)
$(MAKE) clean -C $(UTILS_DIR)/
The subdirectory Makefile is
THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
##################
# Build directory
##################
OBJDIR = obj
CFLAGS = -I./$(OBJDIR) -g -fPIC -std=gnu99 -static
####################
# Files and folders
####################
SRCS_PRIME = $(shell find ./*.c)
OBJS_PRIME = $(addprefix $(OBJDIR)/,$(patsubst %.c,%.o,$(SRCS_PRIME)))
##########
# Targets
##########
all: $(OBJS_PRIME)
$(OBJDIR)/%.o: %.c | objdir
$(CC) $(CFLAGS) -c $< -g -o $#
objdir:
#mkdir -p $(OBJDIR)
clean:
rm -rf $(OBJDIR)
When I execute the make command, the files in the subdirectory are not compiled.
I do make utils alone and this shows the subdirectories are up to date. But when I enter the subdirectory and execute make, I can see that the file will be compiled.
Why is this so? thanks!!!
root#096b64b8fd50:/usr/local/src/gem5/programs/covert_channel# make
rm -rf obj
make clean -C utils/
make[1]: Entering directory '/usr/local/src/gem5/programs/covert_channel/utils'
rm -rf obj
make[1]: Leaving directory '/usr/local/src/gem5/programs/covert_channel/utils'
cc -I./obj -g -fPIC -std=gnu99 -c prime_probe.c -o obj/prime_probe.o
root#096b64b8fd50:/usr/local/src/gem5/programs/covert_channel# make utils
make: 'utils' is up to date.
root#096b64b8fd50:/usr/local/src/gem5/programs/covert_channel# cd utils/
root#096b64b8fd50:/usr/local/src/gem5/programs/covert_channel/utils# make
cc -I./obj -g -fPIC -std=gnu99 -static -c caches_info.c -g -o obj/./caches_info.o
cc -I./obj -g -fPIC -std=gnu99 -static -c caches_util.c -g -o obj/./caches_util.o
cc -I./obj -g -fPIC -std=gnu99 -static -c list_utils.c -g -o obj/./list_utils.o
If you want build utils as a non-file target then you need to specify it as such with:
.PHONY: utils
I suggest don't use recursive make, besides not providing make with a global state, it also makes it unnecessarily complicated (IMHO).

Out-of-source build with yacc/bison and tab.h best practice

I'm trying to use out-of-source builds with a project using GNU bison & flex for parsing and lexing.
Build is managed by GNU Make, and everything went well until I separated the logic from the main .y file to a new .c file.
The Makefile is adopted from this post.
The main problem is that .tab.h is generated by bison, and it is generated inside a build directory: ./build/src/parser.tab.h.
I manged to solve this problem in an ad-hoc manner, by including the .tab.h using a relative path #include "../build/src/parser.tab.h" and adding .tab.c to the dependencies for C files.
Is this considered a good practice?
Is there a way to implicitly state this in Makefile and/or including the generated .tab.h file?
Here is my C file:
#include "../build/src/parser.tab.h"
int main(int argc, char *argv[])
{
yyparse();
return 0;
}
and Makefile:
TARGET_EXEC := parser
BUILD_DIR := ./build
SRC_DIRS := ./src
SRCS := $(shell find $(SRC_DIRS) -name *.c -or -name *.y -or -name *.l)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
CC := gcc
CFLAGS := -O0 -Wall -Wextra -Wpedantic -std=c17
CPPFLAGS := $(INC_FLAGS) -MMD -MP
LDFLAGS := -ly -ll
YACC := bison
YFLAGS := -d
LEX := flex
LFLAGS :=
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $# $(LDFLAGS)
$(BUILD_DIR)/%.c.o: %.c build/src/parser.tab.c
mkdir -p $(dir $#)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
%.y.o: %.tab.c
mkdir -p $(dir $#)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
%.l.o: %.yy.c
mkdir -p $(dir $#)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
$(BUILD_DIR)/%.tab.c: %.y
mkdir -p $(dir $#)
$(YACC) $(YFLAGS) $< -o $#
$(BUILD_DIR)/%.yy.c: %.l
mkdir -p $(dir $#)
$(LEX) $(LFLAGS) -o $# $<
.PHONY: clean
clean:
rm -r $(BUILD_DIR)
-include $(DEPS)
Here are MWE lexer & parser:
%{
#include "parser.tab.h"
#include <stdio.h>
%}
ws [ \t]+
%%
{ws} { ; } // skip whitespaces
. { printf("unknown token %c\n", yytext[0]); }
%%
prgm: ;
Tree before & after:
.
├── Makefile
├── build
│   ├── parser
│   └── src
│   ├── lexer.l.d
│   ├── lexer.l.o
│   ├── lexer.yy.c
│   ├── main.c.d
│   ├── main.c.o
│   ├── parser.tab.h
│   ├── parser.y.d
│   └── parser.y.o
└── src
├── lexer.l
├── main.c
└── parser.y
.
├── Makefile
└── src
├── lexer.l
├── main.c
└── parser.y
First, your makefile is more confusing than it needs to be because you're using the $(BUILD_DIR) variable in some places and using a hardcoded build in other places: use the variable everywhere.
Second, no you should not include the path in your source file. That means whenever you change your makefile to move something you'll have to edit your source file as well.
Instead, just add the path to search for the header file to the compiler command line. You already have an INC_FLAGS variable that contains options to tell the compiler where to look for headers; just add a new one:
INC_FLAGS := $(addprefix -I,$(INC_DIRS)) -I$(BUILD_DIR)/src
Now you can just use #include "y.tab.h" in your source.

How do I make a makefile for creating a library and output file?

I want to make a library file in lib and output file in bin directory.
But my makefile does not work.
my directory tree
~/home$ tree
.
├── bin
├── include
│ └── myhead.h
├── lib
├── libsrc
│ └── myfunc.c
└── src
├── Makefile
└── main.c
Here is my makefile
.SUFFIXES = .c .o
.c.o :
$(CC) $(INC) $(CFLAGS) $<
CC = gcc
CFLAGS = -g -c
AR = ar
OBJECTS = main.o
SRCS = main.c
LIB_TARGET = ../lib/libmyfunc.a
LIB_OBJS = ../libsrc/myfunc.o
LIB_SRCS = ../libsrc/myfunc.c
LIBS = -lmyfunc
LIB_DIR = -L../lib
$(LIB_TARGET) : $(LIB_OBJS)
$(AR) -rcv $(LIB_TARGET) $(LIB_OBJS)
INC = -I../include
TARGET = ../bin/main
$(TARGET) : $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS) $(LIB_DIR) $(LIBS)
clean :
rm -rf $(OBJECTS) $(TARGET) core
I think it works but it does not work.
I get an ar: ../libsrc/myfunc.o: No such file or directory error
and myfunc.o file created in src directory.
I want to get files like
.
├── bin
│ └── main.out <-- new
├── include
│ └── myhead.h
├── lib
│ └── libmyfunc.a <-- new
├── libsrc
│ └── myfunc.c
│ └── myfunc.o <-- new
└── src
├── Makefile
└── main.c
Edit:
My build log
~/home/src$ make
gcc -I../include -g -c ../libsrc/myfunc.c
ar -rcv ../lib/libmyfunc.a ../libsrc/myfunc.o
ar: ../libsrc/myfunc.o: No such file or directory
make: *** [Makefile:20: ../lib/libmyfunc.a] Error 1
Tree after building
~/home$ tree
.
├── bin
├── include
│ └── myhead.h
├── lib
├── libsrc
│ └── myfunc.c
└── src
├── Makefile
├── main.c
└── myfunc.o
Can anyone help?
Note that this forum eats Tab indentation, so if you copy-paste the below Makefiles, do fix the indentation using e.g. sed -e 's|^ *|\t|' -i Makefile .
I would use
LIBSRC := libsrc
LIBDIR := lib
BINSRC := src
BINDIR := bin
INCDIR := include
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS :=
TARGETS := $(BINDIR)/main.out $(LIBDIR)/libmyfunc.a
LIBOBJS := $(LIBSRC)/myfunc.o
.PHONY: all clean
all: $(TARGETS)
clean:
rm -f $(TARGETS) $(BINSRC)/*.o $(LIBSRC)/*.o
$(LIBSRC)/%.o: $(LIBSRC)/%.c
$(CC) $(CFLAGS) -I$(INCDIR) $(LDFLAGS) -c $^ -o $#
$(BINSRC)/%.o: $(BINSRC)/%.c
$(CC) $(CFLAGS) -I$(INCDIR) $(LDFLAGS) -c $^ -o $#
$(LIBDIR)/libmyfunc.a: $(LIBOBJS)
$(AR) -rcv $# $^
$(BINDIR)/main.out: $(BINSRC)/main.o $(LIBDIR)/libmyfunc.a
$(CC) $(CFLAGS) -I$(INCDIR)/ $(BINSRC)/main.o $(LDFLAGS) -L$(LIBDIR) -lmyfunc -o $#
Or, if you prefer to put temporary object files into a separate directory, say build/, then
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS :=
BUILDDIR := build
INCDIR := include
SRCDIR := src
BINDIR := bin
LIBDIR := lib
MAINOBJS := $(BUILDDIR)/main.o
LIBOBJS := $(BUILDDIR)/myfunc.o
TARGETS := $(BINDIR)/main $(LIBDIR)/libmyfunc.a
.PHONY: all clean
all: $(TARGETS)
clean:
rm -f $(TARGETS) $(MAINOBJS) $(LIBOBJS) $(BUILDDIR)/*
$(LIBDIR)/libmyfunc.a: $(LIBOBJS)
$(AR) -rcv $# $(LIBOBJS)
$(BUILDDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -I$(INCDIR) -c $^ -o $#
$(BINDIR)/main: $(MAINOBJS) $(LIBDIR)/libmyfunc.a
$(CC) $(CFLAGS) -I$(INCDIR) $(MAINOBJS) $(LDFLAGS) -L$(LIBDIR) -lmyfunc -o $#
This has the benefit of keeping the source directory unchanged.

Not able to include folder for C compilation

Everything compile if the C is in outer folder, but when lib.c is in [lib] folder, it gives an error: make: *** No rule to make target 'obj/lib.o', needed by 'run'. Stop.
How should the makefile be corrected to make sure the compilation is successful?
What is the correct way to emend the makefile?
The tree is such:
├── inc
│   └── main.h
├── lib
│   └── lib.c
├── main.c
├── main_functions.sh
├── Makefile
└── test_usages.c
The makefile:
# IDIR =../include \
This is a makefile \
IDIR =./inc
CC=gcc
ODIR=obj
# LIB_SRC_DIR =./lib
LDIR =./lib
CFLAGS=-I $(IDIR) $(LDIR) ## added $(LDIR)
# header files required
_DEPS = *.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
_DEP_LIB = *.c ##
DEPS_LIB = $(patsubst %,$(LDIR)/%,$(_DEP_LIB)) ##
_OBJ = lib.o main.o test_usages.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: %.c $(DEPS) $(DEPS_LIB) ## added $(DEPS_LIB)
$(CC) -c -o $# $< $(CFLAGS)
#%.o: %.c
# $(CC) $(CFLAGS) $(INCLUDES) -c $(input) -o $(output)
# make commands options: make <options>, e.g. make hello_make
# executable name
hello_make: $(OBJ)
gcc -o $# $^ $(CFLAGS)
run: $(OBJ)
gcc -o $# $^ $(CFLAGS)
echo "=========================================================="
./run
echo "=========================================================="
.PHONY: clean
clean:
echo "cleaning ...." $(ODIR)/*.o
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ ./*.exe
Thanks in advance for the advice.
There are some quirks in your Makefile, but here is how I got it to work:
Remove trailing blank in the line LDIR =./lib
Insert VPATH=$(LDIR) at some convenient place
Now make -n run shows (but doesn't run) all expected command lines:
gcc -c -o obj/lib.o ./lib/lib.c -I ./inc ./lib
gcc -c -o obj/main.o main.c -I ./inc ./lib
gcc -c -o obj/test_usages.o test_usages.c -I ./inc ./lib
gcc -o run obj/lib.o obj/main.o obj/test_usages.o -I ./inc ./lib
echo "=========================================================="
./run
echo "=========================================================="
BTW, you could use these options to debug your Makefile:
make -npr run print all variables, rules and so on, but not the built-ins.
make -nd run print all decisions, a lot of them.

C Makefile linker error -

I'v written the Makefile below. I'm getting the following error during compilation. I'm not sure what IBS is? Is it missing the L from LIBS or something?
Follow on question:
This is the directory structure.
.
├── Makefile
└── src
├── include
│ └── utils.h
├── main.c
└── utils.c
However, when I run make, this is the output. It doesn't seem to be picking up and building the file utils.c?
cc -I./src -I./src/include -I/usr/local/include/upm -MMD -MP -c
src/main.c -o build/./src/main.c.o.o
TARGET_EXEC ?= app.out
BUILD_DIR ?= ./build
SRC_DIRS ?= ./src
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIRS)) -I/usr/local/include/upm
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $# $(LDFLAGS)
LD_FLAGS = -L/usr/local/lib/upm -L/usr/lib/rabbitmq
LIBS = -lrabbitmq -lupmc-rn2483 -lupmc-rn2903 -lupmc-utilities
# c source
$(BUILD_DIR)/%.c.o: %.c
$(MKDIR_P) $(dir $#)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#.o
$(CC) $(LD_FLAGS) $#.o -o $# $LIBS
.PHONY: clean
clean:
$(RM) -r $(BUILD_DIR)
-include $(DEPS)
MKDIR_P ?= mkdir -p
$(CC) $(LD_FLAGS) $#.o -o $# $LIBS
# wrong ^^^^^
is wrong. Makefile variables need parenthesis: $(LIBS) and your $LIBS is understood as $(L)IBS and you don't have any L variable (so $LIBS expands to IBS since $L expands to nothing)
BTW, you could have used make --trace or remake with -x to find that bug
Regarding the edited question, I believe that you might be wrong in having such a complex source tree for such a small program (generally speaking, small programs of less than a few dozen thousands lines are simpler to deal in a flat source tree, that is a single directory containing both *.c and *.h source files). However, you might consider
INC_DIRS = $(wildcard */include)

Resources