Makefile Stops Building Files After First .o In Out-of-Source Build - c

I have a bit of a huge Makefile that basically works as I want it to.
Issue: The problem I'm having is that the makefile only checks if the first .o needs updating and not if any others do. I'm not sure what part of my makefile is in error.
Context: I have project structure like this:
quendor
src
main.c
options.c
quendor.h
Makefile
When my Makefile builds, it constructs a build directory and things look as follows:
quendor
build
src
main.d
main.o
options.d
options.o
src
main.c
options.c
quendor.h
Makefile
To See the Problem: Now let's say I don't change my main.c but I do change my options.c file. In that case, when I run make again I get this:
make: 'build/./src/main.o' is up to date.
I'm not sure if this is because it's building into a build/src directory rather than just build as I intended.
Here is the full Makefile and I'm including all of it just because I'm not sure what might be a problem and I don't want to make unwarranted assumptions.
.PHONY : all clean
NAME := quendor
PLATFORM := windows
CC := gcc
LINK := gcc
BUILD_DIR ?= ./build
SRC_DIR ?= ./src
ifeq ($(PLATFORM), windows)
TARGET ?= quendor.exe
else
TARGET ?= quendor
endif
ifeq ($(CC), gcc)
CFLAGS += -std=c11 -Wall -Wextra -Wpedantic -Wconversion -Wmissing-prototypes -Wshadow -MMD -MP
LDFLAGS +=
OPT +=
endif
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:%.o=%.d)
MKDIR_P ?= #mkdir -p $(dir $#)
-include $(DEPS)
all : $(TARGET)
#echo "Building $(TARGET)"
$(TARGET) : $(OBJS)
$(LINK) $(OPT) -o $# $^ $(LDFLAGS)
$(BUILD_DIR)/%.o : %.c
$(MKDIR_P)
$(CC) $(CFLAGS) -c $< -o $#
clean:
$(RM) $(TARGET) -r $(BUILD_DIR)
This may be an artifact of how StackOverflow is parsing my Makfile but I do notice that it's showing different syntax highlighting after this line:
SRCS := $(wildcard $(SRC_DIR)/*.c)

The problem is that you are including the dependencies before you define the all rule:
-include $(DEPS)
all : $(TARGET)
If you don't specify a particular target to build on the command line (e.g., if you don't run make all) then make chooses the first explicit target in the makefile (and any included makefiles!!) as the target to build.
I assume that the dependency definitions in the $(DEPS) variable define main.o as a target and since that comes before all, it's the only thing that's run by default.
Move the -include statement later in the makefile (I typically put these all at the end of the makefile) and it will work.

Related

Unexpected prerequisite skip in GNU Make 4.2.1

I'm trying to create a generic Makefile to use with most of my projects. It should work as follows: only rebuild and link those .o files whose .c or .h dependency has changed. The .o and .d files are stored in a separate directory called 'build'.
With the help of the official GNU Make manual and some googling I've managed to achieve the desired behavior except for one thing: when I run make re I get the error:
Assembler messages: Fatal error: can't create build/ft_build_buffer.o: No such file or directory — the reason for this is that the 'build' directory only gets created whenever the .d files are generated, but for some reason the re rule simply skips this step and goes on to compile .o files straight away! Note: if I run make clean && make fclean && make all (which should be the exact same thing) everything works fine.
A few other things: I've tried using the -MMD option to generate dependencies on the fly but on my machine that causes the .d files to only contain .c dependencies. Of course I could just make all .c files depend on all .h files but that seems like a very sloppy solution.
Feel free to share any other advice/improvements that will make this file more clean and readable, thanks! :)
# Define the C compiler to use.
CC := gcc
# Define any compile-time flags.
CFLAGS := -I./include -Wall -Wextra -Werror -g
#CFLAGS := -I./include -march=native -O2 -pipe
# Define the executable file.
BIN := ft_hexdump
# Define build directory.
BUILD_DIR := build
# Define source files directory.
SRC_DIR := src
# Define the C source files.
SRCS := $(wildcard $(SRC_DIR)/*.c)
# Define the C object files.
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
# Define the prerequisite files.
DEPS := $(OBJS:%.o=%.d)
.PHONY: all clean fclean re
.DELETE_ON_ERROR:
all: $(BIN)
-include $(DEPS)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $^ -o $#
$(BUILD_DIR)/%.o: $(BUILD_DIR)/%.d
$(CC) $(CFLAGS) -c $(SRC_DIR)/$*.c -o $#
$(BUILD_DIR)/%.d: $(SRC_DIR)/%.c
#mkdir -p $(#D)
#set -e; rm -f $#; \
$(CC) $(CFLAGS) $(INCLUDE) -MM $< > $#.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $# : ,g' < $#.$$$$ > $#; \
rm -f $#.$$$$
clean:
-rm -rf $(BUILD_DIR)
fclean: clean
-rm -f $(BIN)
re: fclean all
Here is the modified working version as suggested by #M.M
# Define the C compiler to use.
CC := gcc
# Define any compile-time flags.
CFLAGS := -I./include -Wall -Wextra -Werror -g
#CFLAGS := -I./include -march=native -O2 -pipe
# Define the executable file.
BIN := ft_hexdump
# Define build directory.
BUILD_DIR := build
# Define source files directory.
SRC_DIR := src
# Define the C source files.
SRCS := $(wildcard $(SRC_DIR)/*.c)
# Define the C object files.
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
# Define the prerequisite files.
DEPS := $(OBJS:%.o=%.d)
.PHONY: all clean fclean re
.DELETE_ON_ERROR:
all: $(BIN)
-include $(DEPS)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) $^ -o $#
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
#mkdir -p $(#D)
$(CC) $(CFLAGS) -MMD -c $(SRC_DIR)/$*.c -o $#
clean:
-rm -rf $(BUILD_DIR)
fclean: clean
-rm -f $(BIN)
re:
$(MAKE) fclean
$(MAKE) all

Makefile: wildcard and patsubst does not change file source names

I am trying to write a Makefile for my project, all the *.c and *.h files are in a folder called src, and the Makefile looks like this --
CC := gcc
CFLAGS := -g -Wall -ansi -pedantic -std=gnu99
LDFLAGS := -lm
INCLUDES := $(wildcard src/*.h)
IFLAGS := $(addprefix -I/,$(INCLUDES))
SRC := $(wildcard src/*.c)
OBJS := $(patsubst %.c, %.o, $(SRC))
APP := app
all: $(OBJS)
$(APP): $(OBJS)
$(CC) $(CFLAGS) $< -o $# $(LDFLAGS)
$(OBJS): $(SRC) $(INCLUDES)
$(CC) $(CFLAGS) $(IFLAGS) -c $< -o $#
clean:
rm -rf $(OBJS)
rm -rf *.out
rm -f $(APP)
At this point I am not building the executable, just trying to compile them to object files, so when I run, I am getting this output --
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/allocate.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/auxiliary.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/decode.o
gcc -g -Wall -ansi -pedantic -std=gnu99 -I/src/structure.h -I/src/rng.h -c src/allocate.c -o src/display.o
You can see that in each gcc invocation, the source file names do not change, they are all always src/allocate.c why ? However, the object names are correctly expanded like src/allocate.o, src/auxiliary.o and src/decode.oetc.
It seems you've mixed up some things here.
They are basically two type of rules you need to use here, and they both share the same syntax:
targets : prerequisites
recipe
When you write this:
$(APP): $(OBJS)
$(CC) $(CFLAGS) $< -o $# $(LDFLAGS)
You're saying to make that you want to create $(APP), and to do that you need $(OBJS) to exist or to be created.
Now when you write this:
$(OBJS): $(SRC) $(INCLUDES)
$(CC) $(CFLAGS) $(IFLAGS) -c $< -o $#
You're telling make you want to create a list of .o files, and for each individual file that you need all $(SRC) and $(INCLUDES).
Since in the recipe you're using $<, which is a shortcut for the first entry in the prerequisites list, you always end up with the same source file being compiled.
To do what you want, you must abstract things a little bit and tell make "Here is how I want you to build any .o file that depends on a corresponding .c". That is the job of pattern rules:
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -o $# -c $<
Ultimately, your Makefile should look like this:
APP := app
SRC := $(wildcard src/*.c)
OBJ := $(SRC:.c=.o)
CFLAGS := -W -Wall -g -std=c99 -pedantic
LDLIBS := -lm
all: $(OBJS)
$(APP): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
clean:
$(RM) $(APP) $(OBJ)
Note another couple of things here that you missed:
The -I preprocessor flag (that should be placed in the CPPFLAGS variable) accept a directory, not a file.
The -ansi compiler flag is a synonym of -std=c89. You're using -std=gnu99 right after so that one will be picked ultimately
You don't need to list your header files at all. Don't bother.
Don't use the -r flag of the rm command without care, you'll end up removing folders. It is not used to remove multiple files but to remove recursively, read up your man.
You used $< instead of $^ at the linking phase, so your executable will miss many object files.
To address the comments:
GNU make has a lot of predefined rules, functions and variables that you should be using before rolling your own. It has basic rules for compiling and linking C and C++ programs, among other, this is why your Makefile does not need te redefine the %.o: %c rule that already exists.
You can see all of these by typing this in your favorite shell:
$ make -p > predefined.mk
$(RM), $(CC) are one of these predefined variables, you can see by yourself what they actually contain.
Now, as many users are concerned with header files dependencies, let's adress this issue. You won't have to manually do that, modern compilers like GCC and Clang do this for you once you set them up.
The dependencies for each .c file will be generated in a .d file that must be included in the Makefile.
To tell the compiler to generate these files while compiling, you need to pass a preprocessor flag:
CPPFLAGS := -MMD
Now the dependencies are auto-generated, we need to include them:
DEP := $(OBJ:.o=.d)
-include $(DEP)
You'd also want to clean them:
clean:
$(RM) $(APP) $(OBJ) $(DEP)
Now your Makefile looks like this:
APP := app
SRC := $(wildcard src/*.c)
OBJ := $(SRC:.c=.o)
DEP := $(OBJ:.o=.d)
CPPFLAGS := -MMD
CFLAGS := -W -Wall -g -std=c99 -pedantic
LDLIBS := -lm
all: $(OBJS)
$(APP): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
clean:
$(RM) $(APP) $(OBJ) $(DEP)
-include $(DEP)
Last point: the syntax $(SRC:.c=.o) is a shortcut for $(SRC:%.c=%.o) which is also a shortcut for $(patsubst %.c,%.o,$(SRC)).

Makefile to convert all *.c to *.o

I am writing a Makefile for compiling all *.c files in a directory into
*.o . There are many *.c files so I don't want to do it on individual basis,
I tried
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $# $<
but this isn't working ... please help me understand what's going wrong here ...
You told make how to generate a *.o out of a corresponding *.c file. (Not that you needed to, because make already knows as much, at least as long as you don't try anything more specific than what you wrote in your rule.)
You didn't tell make you wanted any specific foo.o or bar.o, so make does nothing.
Either add a list of object files you want generated to the Makefile, or call make foo.o specifically.
----
For what it's worth, have a look at this... there are some tricks and lessons in there, which I trust your curiosity will find out about. Most importantly, it adds compiler warnings, and automated header dependency handling.
This is assuming GNU make for e.g. patsubst, the existence of find, and GCC (for the dependency handling via -MMD -MP). If you want to go cross-platform, I'd suggest a meta-build system like CMake, which I am using myself.
PROJNAME := MyProject
PROJDIRS := subdir1 subdir2
SRCFILES := $(shell find $(PROJDIRS) -type f -name "\*.c")
OBJFILES := $(patsubst %.c,%.o,$(SRCFILES))
DEPFILES := $(patsubst %.c,%.d,$(SRCFILES))
WARNINGS := -Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align \
-Wwrite-strings -Wmissing-prototypes -Wmissing-declarations \
-Wredundant-decls -Wnested-externs -Winline \
-Wuninitialized -Wconversion -Wstrict-prototypes
CFLAGS := -g -std=c99 $(WARNINGS)
.PHONY: all clean
all: $(PROJNAME)
clean:
-#$(RM) $(wildcard $(OBJFILES) $(DEPFILES) $(PROJNAME))
-include $(DEPFILES)
$(PROJNAME): $(OBJFILES)
#$(CC) $(LDFLAGS) $^
%.o: %.c Makefile
#$(CC) $(CFLAGS) -MMD -MP -c $< -o $#
Your rule maps a single .c file to a single .o, and mirrors an existing implicit rule.
In order to generate all the .o files corresponding to a set of .c files, you can create a list of object file names from the list of .c files, and then make a target that depends on that list:
SRC = $(wildcard *.c) # list of source files
OBJS = $(patsubst %.c, %.o, $(SRC)) # list of object files
objs : $(OBJS) # target
Then generate the object files with
make objs
This will build a .o file for each .c one.
You can add a rule OBJS, it will do it for you
OBJS = $(SRCS:.c=.o)
With you .c files in the SRCS, and if they are too many do:
SRCS = $(wildcard *.c)
Then add it when you'll run make
$(NAME) : $(OBJS)
[...]

GNU make implicit archive rule with stem

I am trying to setup a Makefile to build either static (.a) and dynamic (.so) libraries depending on the target file extension.
I previously used the following Makefile for static libraries only :
NAME := config
LIB := lib$(NAME).a
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
CFLAGS += -W -Wall
.PHONY: all clean fclean re
all: $(LIB)
clean:
#$(RM) $(OBJ)
fclean: clean
#$(RM) $(LIB)
re: fclean all
$(LIB): $(LIB)($(OBJ))
ranlib $#
My main goal is to be able to compile multiple libraries by only changing the LIB and NAMEvariables.
Everything worked fine, so I added the following for the dynamic libraries :
LDFLAGS += -shared
%.so: CFLAGS += -fPIC
%.so: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^
And changed the $(LIB): rule by the following generic rule :
%.a: %.a($(OBJ))
ranlib $#
If I change LIB to lib$(NAME).so everything works as expected, but with the .a extension, make prints me this error :
make: *** No rule to make target 'libconfig.a', needed by 'all'. Stop.
The only solution I found was to add another explicit rule like that :
%.a($(OBJ)): $(OBJ)
$(AR) rv $# $^
And now everything works.
But adding this explicit rule prevents me from relying only upon GNU make's implicit rules, and makes me call explicitly ar, which I wanted to avoid.
Is this some kind of bug or am I missing something ?
GNU Make 3.82
Built for x86_64-unknown-linux-gnu
Some quick testing seems to indicate that this can't be done. It appears that the specialized make archive support is a makefile parse time feature. That is the literal archive name must exist in the actual makefile for it to take effect.
I tried a couple of workarounds but wasn't able to get any of them to work correctly. The closest I could manage was:
$(foreach a,$(filter %.a,$(MAKECMDGOALS) $(.DEFAULT_GOAL)),$(eval $a: $a($$(OBJ)); ranlib $$#))
which doesn't work for a default goal of all but would if the default was the library name and/or if the library name is an explicit make target. You could stick any other known library names in there too and then they should work even as implicit requirements of other targets but that's a manual process.
You need to tell make which .o files a given .a file depends on. You can do that with a simple dpendency with no action:
libconfig.a: libconfig.a($(OBJ))
make will then invoke the default rule for putting .o files into a .a file to actually build libconfig.a
Archives and shared libraries don't share the same prerequisites syntax, so it is not possible to have a single rule that handle both.
The simplest solution is to use a conditional on the target extension:
ifeq "$(suffix $(LIB))" ".a"
$(LIB): $(LIB)($(OBJ))
else ifeq "$(suffix $(LIB))" ".so"
$(LIB): override CFLAGS += -fPIC
$(LIB): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
endif
A working Makefile that meets the requirements looks now like this:
NAME := config
LIB := lib$(NAME).so
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(OBJ:.o=.d)
CPPFLAGS := -MMD -MP
CFLAGS := -W -Wall
LDFLAGS := -shared
ARFLAGS := rs
.PRECIOUS: $(OBJ)
.PHONY: all clean fclean re
all: $(LIB)
clean:
$(RM) $(OBJ) $(DEP)
fclean: clean
$(RM) $(LIB)
re: fclean all
ifeq "$(suffix $(LIB))" ".a"
$(LIB): $(LIB)($(OBJ))
else ifeq "$(suffix $(LIB))" ".so"
$(LIB): override CFLAGS += -fPIC
$(LIB): $(OBJ)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
endif
ifeq "$(MAKECMDGOALS)" ""
-include $(DEP)
endif
Note that the ARFLAGS variable controls which flags are passed to the invocation of ar. Here I use the r flag to replace existing object if it exists and the s flag to build or update the index (ranlib is not necessary anymore with this).

How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?

A few months ago, I came up with the following generic Makefile for school assignments:
# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon#gmail.com
# Date : 2010-11-05
#
# Changelog :
# 0.01 - first version
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc -std=c99 -c
# compiling flags here
CFLAGS = -Wall -I.
LINKER = gcc -o
# linking flags here
LFLAGS = -Wall
SOURCES := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS := $(SOURCES:.c=*.o)
rm = rm -f
$(TARGET): obj
#$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
obj: $(SOURCES) $(INCLUDES)
#$(CC) $(CFLAGS) $(SOURCES)
#echo "Compilation complete!"
clean:
#$(rm) $(TARGET) $(OBJECTS)
#echo "Cleanup complete!"
This will basically compile every .c and .h file to generate .o files and the executable projectname all in the same folder.
Now, I'd like to push this a little. How can I write a Makefile to compile a C project with the following directory structure?
./
./Makefile
./src/*.c;*.h
./obj/*.o
./bin/<executable>
In other words, I'd like to have a Makefile that compiles C sources from ./src/ into ./obj/ and then link everything to create the executable in ./bin/.
I've tried to read different Makefiles, but I simply can't make them work for the project structure above; instead, the project fails to compile with all sorts of errors. Sure, I could use full blown IDE (Monodevelop, Anjuta, etc.), but I honestly prefer to stick with gEdit and the good ol' terminal.
Is there a guru who can give me a working solution, or clear information about how this can be done? Thank you!
** UPDATE (v4) **
The final solution :
# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon#gmail.com
# Date : 2011-08-10
#
# Changelog :
# 2010-11-05 - first version
# 2011-08-10 - added structure : sources, objects, binaries
# thanks to http://stackoverflow.com/users/128940/beta
# 2017-04-24 - changed order of linker params
# ------------------------------------------------
# project name (generate executable with this name)
TARGET = projectname
CC = gcc
# compiling flags here
CFLAGS = -std=c99 -Wall -I.
LINKER = gcc
# linking flags here
LFLAGS = -Wall -I. -lm
# change these to proper directories where each file should be
SRCDIR = src
OBJDIR = obj
BINDIR = bin
SOURCES := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm = rm -f
$(BINDIR)/$(TARGET): $(OBJECTS)
#$(LINKER) $(OBJECTS) $(LFLAGS) -o $#
#echo "Linking complete!"
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
#$(CC) $(CFLAGS) -c $< -o $#
#echo "Compiled "$<" successfully!"
.PHONY: clean
clean:
#$(rm) $(OBJECTS)
#echo "Cleanup complete!"
.PHONY: remove
remove: clean
#$(rm) $(BINDIR)/$(TARGET)
#echo "Executable removed!"
First, your $(OBJECTS) rule is problematic, because:
it's kind of indiscriminate, making all sources prerequisites of every object,
it often uses the wrong source (as you discovered with file1.o and file2.o)
it tries to build executables instead of stopping at objects, and
the name of the target (foo.o) is not what the rule will actually produce (obj/foo.o).
I suggest the following:
OBJECTS := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $#
#echo "Compiled "$<" successfully!"
The $(TARGET) rule has the same problem that the target name does not actually describe what the rule builds. For that reason, if you type make several times, Make will rebuild the target each time, even though there is no reason to. A small change fixes that:
$(BINDIR)/$(TARGET): $(OBJECTS)
$(LINKER) $# $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
Once that's all in order, you might consider more sophisticated dependency handling; if you modify one of the header files, this makefile will not know which objects/executables must be rebuilt. But that can wait for another day.
EDIT:
Sorry, I omitted part of the $(OBJECTS) rule above; I've corrected it. (I wish I could use "strike" inside a code sample.)
You can add the -I flag to the compiler flags (CFLAGS) to indicate where the compiler should look for source files , and the -o flag to indicate where the binary should be left:
CFLAGS = -Wall -I./src
TARGETPATH = ./bin
$(TARGET): obj
#$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
#echo "Linking complete!"
In order to drop the object files into the obj directory, use the -o option when compiling. Also, look at the $# and $< automatic variables.
For example, consider this simple Makefile
CFLAGS= -g -Wall -O3
OBJDIR= ./obj
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$#
Update>
By looking at your makefile, I realize you are using the -o flag. Good. Continue using it, but add a target directory variable to indicate where the output file should be written.
I have stopped writing makefiles these days, if your intention is to learn go ahead, else you have good makefile generator that comes with eclipse CDT. If you want some maintainability / multiple project support with in your build tree, have a look at the following -
https://github.com/dmoulding/boilermake I found this pretty good..!

Resources