How to have gnu make treat intermediate files as out of date - c

I've been following http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#combine to create a Makefile that will generate a dependency list too, so the list of header files a .o target depends on (from the related .c file) is automatically generated.
The Makefile I have ended up with is
DEPDIR := .deps
$(shell mkdir -p $(DEPDIR))
DEPFLAGS = -MT $# -MMD -MP -MF $(DEPDIR)/$*.Td
INCDIR = ../includes
CFLAGS = -I$(INCDIR)
CC = gcc
SRCS = main.c chunk.c memory.c
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(TARGET_ARCH) -c
POSTCOMPILE = mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $#
%.o: %.c
%.o: %.c $(DEPDIR)/%.d
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
$(DEPDIR)/%.d: ;
.PRECIOUS: $(DEPDIR)/%.d
include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS))))
running make main.o generates main.o and .deps/main.d as expected, and changing one of the header files included in main.c causes main.o to be out of date and regenerated as desired.
However, main.o should also be out of date if .deps/main.d does not exist, but deleting .deps/main.d doesn't cause make to see main.o as out of date.
What needs to be changed to make deleting .deps/main.d cause make to see main.o as out of date?

I've been meaning to update my blog post. You need to ensure that the dependency files are listed as prerequisites somewhere so they're not considered intermediate. Change this line:
include $(wildcard $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS))))
to something like:
ALLDEPS := $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS)))
build-deps: $(ALLDEPS)
.PHONY: build-deps
include $(wildcard $(ALLDEPS))
I'm not 100% sure why these are considered intermediate and yet make doesn't delete them; I'll have to look into it more closely.

Related

How to redirect the output of object files to another directory?

I have three directories obj/, inc/ and src/ . The directories inc/ and src/ contains all the .c files. I would like to redirect all the .o files generated in src/ and inc/ to obj/
This is a simple example of my makefile
NAME = push_swap
SRC = $(wildcard ./src/*c)
INC = $(wildcard ./inc/*c)
OBJ1 = $(SRC:.c=.o)
OBJ2 = $(INC:.c=.o)
CC = gcc
CFLAGS = -Wall -Werror -Wextra
$(NAME): $(OBJ1) $(OBJ2)
$(CC) $(CFLAGS) $(OBJ1) $(OBJ2) -o $#
all: $(NAME)
clean:
rm -f */*.o
fclean: clean
rm -f $(NAME)
re: fclean all
The makefile works perfectly, but all the object files are generetad in its src folder, making hard to search for .c files and debbug the code.
By default, Make will build the object file in the directory where it finds the source. If you want the object file to be built somewhere else, there is more than one way to do it.
You could write a rule for Make to use instead of the default:
obj/%.o: src/%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
And another for source files in inc/, if you want to keep source files there.
Or you could write a more general rule, and use 'vpath` to find the sources:
vpath %.c src inc
obj/%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $#
EDIT: Did you remember to change the names of the files you ask Make to build (as #MadScientist pointed out in his comment (and as I ought to have pointed out in my Answer))?
Try this:
SRC = $(wildcard ./src/*c)
OBJ1 = $(patsubst ./src/%.c, ./obj/%.o, $(SRC))
CC = gcc
CFLAGS = -Wall -Werror -Wextra
vpath %.c src
obj/%.o: %.c
$(CC) $(CFLAGS) -c -o $# $<
all: $(OBJ1)

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

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.

Compiling and linking subfolders using different Makefiles

I have a client/server application in C. The server has its own folder dserver, the same for the client dclient. Using both of them some files containing utility functions, I created another directory at the same level of the previously ones, named common.
My idea is to create each Makefile in each subfolder (one in dserver, one in dclient and another in common) and then one Makefile in the main directory which will run the other Makefiles which looks like:
all:
+$(MAKE) -C common
+$(MAKE) -C dserver
+$(MAKE) -C dclient
The first problem is that the common/Makefile should not create an executable but only create the object files that will be needed to create the executable for the client and for the server. In my case it is:
CXX = gcc
SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR=$(SOURCEDIR)/obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))
# ADD MORE WARNINGS!
WARNING := -Wall -Wextra
$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
$(CXX) $(WARNING) -MMD -MP -c $< -o $#
$(OBJDIR):
mkdir -p $(OBJDIR)
My problem is that it is creating the object directory specified by OBJDIR but not the object files *.o: how should it be?
Secondly in the client and server Makefiles I should both include path to the files in common and then referencing the object files in the resulting from the compilation of common to build the executables. So taking for example the dserver/Makefile I added the line INC_PATH = -I../common/ and referencing it in the compilation as $(CXX) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $#. However in the code I had to do #include "../common/utilities.h".
Is there a way to include the path in the Makefile so that in the code it allows to do just: #include "utilities.h"?
And also, supposing that common has its own object directory containing the object files needed both by the client and server, how build, for example the server executable referencing the object files both in the common directory and the ones specific and contained in the server directory?
The dserver/Makefile is something like (and the dclient/Makefile has the same structure):
CXX = gcc
INC_PATH = -I../common/
SOURCEDIR := ./
SOURCES := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR=./obj
OBJECTS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))
# ADD MORE WARNINGS!
WARNING := -Wall -Wextra
# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean
# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: server
clean:
$(RM) $(OBJECTS) $(DEPENDS) server
# Linking the executable from the object files
# $^ # "src.c src.h" (all prerequisites)
../server: $(OBJECTS)
$(CXX) $(WARNING) $(INC_PATH) $^ -o $#
#$(CXX) $(WARNING) $(CXXFLAGS) $(INC_PATH) $^ -o $# $(LIBS)
-include $(DEPENDS)
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
$(CXX) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $#
You don't specify any rules for building the objects in your "common" Makefile - this is the only rule you have.
$(OBJDIR):
mkdir -p $(OBJDIR)
You want to put a rule before that to all get it to build the objects, maybe something along the lines of:
all: $(OBJDIR) $(OBJECTS)
It has to go before the original rule as if you don't specify what is being built, make will do the first rule it finds.
Including header files from "common" in your other directories should be working just fine by using -I../common/.
Using the objects from "common" should just be a case of adding them to the list of objects ie:
COMMON_OBJECTS=../common/obj/utilities.o
../server: $(OBJECTS) $(COMMON_OBJECTS)
Or having them built into a library so you don't need to know what object files there are.
Also it's worth noting that $(CXX) is the variable used to store the C++ compiler - for building with the C compiler you want to be using $(CC) and $(CFLAGS).

Makefile executes the command only once

I am using the Makefile of MinGW (Windows 8.1, GCC 7.3.0) to build a medium-sized project automatically detecting all source files under the folder src and compiling all object files into the obj folder, but unfortunately it is only executing the command over the first detected file and stops there.
This is the first time I write a Makefile script for anything beyond one source file and maybe I am getting some rule wrongly. Thanks in advance!
CC := gcc
SRC := src
OBJ := obj
MAIN := main
PACK := libbundle
SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(patsubst $(SRC)/%.c,$(OBJ)/%.o, $(SOURCES))
CFLAGS := -I$(SRC)
$(OBJECTS): $(SOURCES)
$(CC) $(CFLAGS) -c $< -o $#
# build:
# ar rcs $(PACK).a $(OBJECTS)
# $(CC) -shared -o $(PACK).so $(OBJECTS)
# $(CC) -o $(MAIN).c $(PACK).so
Output:
gcc -Isrc -c src/firstsource.c -o obj/firstsource.o
...and stops there!
Problem - rule with multiple targets
Your rule
$(OBJECTS): $(SOURCES)
$(CC) $(CFLAGS) -c $< -o $#
has multiple targets. I don't believe this is appropriate here. See discussion here of where rules with multiple targets are useful.
Also, this rule specifies multiple prerequisites - but $< represents only the first prerequisite. You can use $+ to capture all prerequisites - but then you lose the ability to use the -o option. See below if you want to use multiple prerequisites.
What $(OBJECTS): $(SOURCES) means in detail
Suppose, for example, that your src/ directory contains firstsource.c and secondsource.c. Then your variables become
$(SOURCES) -> src/firstsource.c src/secondsource.c
$(OBJECTS) -> obj/firstsource.o obj/secondsource.o
(Actually - and somewhat non-intuitively - firstsource will be placed after secondsource, but let's ignore that for simplicity's sake.)
So the rule
$(OBJECTS): $(SOURCES)
$(CC) $(CFLAGS) -c $< -o $#
is equivalent to
obj/firstsource.o obj/secondsource.o: src/firstsource.c src/secondsource.c
$(CC) $(CFLAGS) -c $< -o $#
This rule, in turn, is equivalent to two rules (since it has multiple targets) - each with the same prerequisites:
obj/firstsource.o: src/firstsource.c src/secondsource.c
$(CC) $(CFLAGS) -c $< -o $#
obj/secondsource.o: src/firstsource.c src/secondsource.c
$(CC) $(CFLAGS) -c $< -o $#
Can you see the problem here?
Since $< represents only the first prerequisite, the recipe for the first rule becomes
gcc -Isrc -c src/firstsource.c -o obj/firstsource.o
which is fine for the first rule, but for the second rule it won't work
gcc -Isrc -c src/firstsource.c -o obj/secondsource.o
because you are using the wrong input file.
By the way ... You mentioned that
unfortunately it [i.e. make] is only executing the command over the first detected file and stops there.
This is because - when you invoke make without any arguments - it calls the first rule in the file and no more.
Option 1: Use multiple rules
What is more suitable here are multiple rules - each with only a single target. So try replacing the above with the following.
$(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -c $< -o $#
compile-only: $(OBJECTS)
You could invoke make on this modified Makefile as
make -B compile-only
Option 2: Single target with multiple prerequisites
If you have multiple prerequisites in your target, you can refer to them in your recipe using the special variable $+. However, you can not use the -o option in this case - so will not be able to specify the output directory for the object files. (To work around this, you could cd to the obj directory before compiling - but then you will need to tweak the SOURCES variable.)
CC := gcc
CFLAGS := -Isrc
SRC := src
SOURCES := $(wildcard $(SRC)/*.c)
myobjs: $(SOURCES)
$(CC) $(CFLAGS) -c $+
This will place all the object files in the top-level directory. As mentioned, you can tweak SOURCES and cd the obj directory if you must place the object files in a separate directory.
Aside - pre-defined recipes for pattern rules
I understand the rationale in placing the build output in a separate directory as you have done, but - if you were willing to place the build output in the same directory as the source files - you could simplify your Makefile using make's predefined pattern rules.
SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(SOURCES:.c=.o)
compile: $(OBJECTS)
You should use standard targets in your Makefile, the most important one being "all". And it should be the first target in the Makefile so that make and make all do the same thing.
all: $(OBJECTS)
With $(OBJECTS): $(SOURCES) you are telling make that each file in $(OBJECTS) depends on every file in $(SOURCES) and will execute the commands below as any of the objects fails the test of being newer than any of the sources. The command will be executed only once and stop.
What you need is to specify that each object file depends on its correspondient source file. As I see you are using GMAKE syntax, I'll show you the GNU make syntax for such a rule:
$(OBJECTS): obj/%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $#
this is as if you had a rule for each .o file that says how to compile it from its proper source file.
you will also need to say which files are your default targets, with something like:
.PHONY: all
all: $(OBJECTS)
clean:
$(RM) $(TOCLEAN)
put that rule the first one, so it will be selected by default.
This will make all your default target. It will explode into all your object files, and for each object you have a rule that says how to compile it (not neccessary, as gnu make already know how to compile a C program, but repeating it here doesn't hurt)
your final Makefile is:
CC := gcc
SRC := src
OBJ := obj
MAIN := main
PACK := libbundle
CFLAGS := -I$(SRC)
PICFLAGS := -fPIC
SOURCES := $(wildcard $(SRC)/*.c)
OBJECTS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES))
TOCLEAN += $(OBJECTS)
PICOBJECTS := $(patsubst $(OBJ)/%.o, $(OBJ)/%.pic, $(OBJECTS))
TOCLEAN += $(PICOBJECTS)
.PHONY: all
.SUFFIXES: .c .o .pic
all: $(PACK).a $(MAIN)
clean:
$(RM) $(TOCLEAN)
$(MAIN): $(MAIN).o $(PACK).so
$(CC) $(LDFLAGS) -o $# $+
TOCLEAN += $(MAIN)
$(PACK).a: $(OBJECTS)
ar r $(PACK).a $(OBJECTS)
TOCLEAN += $(PACK).a
$(PACK).so: $(PICOBJECTS)
$(LD) $(LDFLAGS) -shared -o $(PACK).so $(PICOBJECTS)
TOCLEAN += $(PACK).so
# this to create a normal .o file in $(OBJ) directory.
$(OBJECTS): $(OBJ)/%.o: $(SRC)/%.c
$(CC) $(CFLAGS) -o $# -c $<
# this to create a PIC (Position Independent Code) .pic object in $(OBJ) directory.
# (REQUIRED FOR .so SHARED OBJECT)
$(PICOBJECTS): $(OBJ)/%.pic: $(SRC)/%.c
$(CC) $(CFLAGS) $(PICFLAGS) -o $# -c $<

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)
[...]

Resources