BSD Make substitution fails in rule $(objdir)/%o: %c - makefile

I'm trying to write a portable BSD and GNU Makefile that does an out-of-source build and automatic prerequisites. GNU Make works fine but BSD Make does not.
OBJDIR := objdir
SRCS := foo.c bar.c baz.c
OBJS := $(SRCS:%.c=$(OBJDIR)/%.o)
CPPFLAGS += -MMD -MP
all: $(OBJDIR) $(OBJS)
$(CC) $(LDFLAGS) -o main $(OBJS) $(LDLIBS)
$(OBJDIR)/%.o: %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(OBJDIR):
mkdir $(OBJDIR)
-include $(OBJDIR)/$(SRCS:%.c=%.d)
clean:
rm -rf main $(OBJDIR)
This is the output:
$ gmake
mkdir objdir
cc -MMD -MP -c -o objdir/foo.o foo.c
cc -MMD -MP -c -o objdir/bar.o bar.c
cc -MMD -MP -c -o objdir/baz.o baz.c
cc -o main objdir/foo.o objdir/bar.o objdir/baz.o
$ bmake
--- objdir ---
bmake: bmake: don't know how to make objdir/foo.o. Stop
bmake: stopped in /home/antonin/tests
My guess is that BSD Make does not understand substitution within a path (is that the right term?) $(OBJDIR)/%.o: %.c, because bmake OBJDIR=. works fine. If so, how can I write a simple, portable Makefile that achieves the same functionality?
Example for testing.
Thank you!

Related

How can I store my .o files in a sepparate folder?

INCDIR=include
SRCDIR=src
SRC = $(wildcard $(SRCDIR)/*.cpp)
DEPS = $(wildcard $(INCDIR)/*.h)
OBJ = $(SRC:.cpp=.o)
CFLAGS = -I$(INCDIR) -Wall -Weffc++ -Wextra -Wsign-conversion
CC=g++
preprocessor: $(OBJ)
$(CC) $(OBJ) -o $# $(CFLAGS)
$(SRCDIR)/%.o: $(SRCDIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $#
clean:
rm $(SRCDIR)/*.o preprocessor
This is my current makefile, if I wanted to store my .o files in a sepparate directory, src/obj for example, how would I have to modify it?
Many ways to do it, but according to your own code, you could do:
INCDIR=include
SRCDIR=src
OBJDIR=obj
SRC= $(wildcard $(SRCDIR)/*.cpp)
DEPS= $(wildcard $(INCDIR)/*.h)
OBJ=$(patsubst %.cpp, $(OBJDIR)/%.o, $(notdir $(SRC)))
CFLAGS= -I$(INCDIR) -Wall -Weffc++ -Wextra -Wsign-conversion
CC=g++
$(OBJDIR):
#if ! [ -d $(#) ]; then\
echo "==> creating dir: $(#)";\
mkdir $(#);\
fi
preprocessor: $(OBJDIR) $(OBJ)
$(CC) $(OBJ) -o $# $(CFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $#
clean:
rm $(OBJDIR)/*.o preprocessor
printobj: $(OBJDIR)
#echo "$(OBJ)"
will output:
$ gmake preprocessor
g++ -c -Iinclude -Wall -Weffc++ -Wextra -Wsign-conversion src/a.cpp -o obj/a.o
g++ -c -Iinclude -Wall -Weffc++ -Wextra -Wsign-conversion src/b.cpp -o obj/b.o
g++ obj/a.o obj/b.o -o preprocessor -Iinclude -Wall -Weffc++ -Wextra -Wsign-conversion
NOTE: the target printobj is just here to output what you could expect, the target $(OBJDIR) ensure your directory exist before creating object files.
The are just a few things to change:
INCDIR := include
SRCDIR := src
OBJDIR := $(SRCDIR)/obj
SRC := $(wildcard $(SRCDIR)/*.cpp)
DEPS := $(wildcard $(INCDIR)/*.h)
OBJ := $(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(SRC))
CFLAGS := -I$(INCDIR) -Wall -Weffc++ -Wextra -Wsign-conversion
CC := g++
preprocessor: $(OBJ)
$(CC) $^ -o $# $(CFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
$(CC) -c $(CFLAGS) $< -o $#
$(OBJDIR):
mkdir -p "$#"
clean:
rm -f $(OBJ) preprocessor
Note some other minor modifications:
the | $(OBJDIR) order-only prerequisite and the $(OBJDIR): rule to ensure the objects directory exists before compiling,
:= instead of = for all make variable assignments because you don't need recursively expanded variables here,
$^ instead of $(OBJ) in the link recipe because using automatic variables in recipes makes them more generic,
$< instead of $^ in the compile recipe because you compile only the first prerequisite, not all of them,
rm -f $(OBJ) preprocessor instead of rm $(SRCDIR)/*.o preprocessor to remove only the object files of the project and avoid errors if none exists.

makefile error: opening dependency file .d/file_name.Td: No such file or directory

I am trying to adapt a makefile (that I found here) that automatically generates dependencies. However, when I run make I get the following error message:
t#t-XPS-13-9365:~/pf/test$ make
g++ -MT .o/test_resamplers.o -MD -MP -MF .d/test_resamplers.Td -std=c++11 -g -Wall -Wextra -pedantic -I/usr/local/include/UnitTest++ -I/usr/include/eigen3 -I../include -c -o .o/test_resamplers.o test_resamplers.cpp
test_resamplers.cpp:155:1: fatal error: opening dependency file .d/test_resamplers.Td: No such file or directory
}
^
compilation terminated.
Makefile:66: recipe for target '.o/test_resamplers.o' failed
make: *** [.o/test_resamplers.o] Error 1
Perhaps I am using the wrong DEPFLAGS variable in my makefile. I did switch the compiler from clang++ in the example to g++. Here's my makefile:
# output binary
BIN := run_tests
# source files
SRCS := \
main.cpp test_cf_filters.cpp test_resamplers.cpp test_rv_eval.cpp \
test_rv_samp.cpp test_utils.cpp
# intermediate directory for generated object files
OBJDIR := .o
# intermediate directory for generated dependency files
DEPDIR := .d
# object files, auto generated from source files
OBJS := $(patsubst %,$(OBJDIR)/%.o,$(basename $(SRCS)))
# compilers (at least gcc and clang) don't create the subdirectories automatically
$(shell mkdir -p $(dir $(OBJS)) >/dev/null)
# C++ compiler
CXX := g++
# linker
LD := g++
# C++ flags
CXXFLAGS := -std=c++11
# C/C++ flags
CPPFLAGS := -g -Wall -Wextra -pedantic -I/usr/local/include/UnitTest++ -I/usr/include/eigen3 -I../include
# linker flags
LDFLAGS := "-L../bin" "-L/usr/local/lib"
# flags required for dependency generation; passed to compilers
DEPFLAGS = -MT $# -MD -MP -MF $(DEPDIR)/$*.Td
# libraries
LDLIBS := -lpf -lUnitTest++
# compile C++ source files
COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o $#
# link object files to binary
LINK.o = $(LD) $(LDFLAGS) $(LDLIBS) -o $#
# precompile step
PRECOMPILE =
# postcompile step
POSTCOMPILE = mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
all: $(BIN)
.PHONY: clean
clean:
$(RM) -r $(OBJDIR) $(DEPDIR)
.PHONY: help
help:
#echo available targets: all dist clean distclean install uninstall check
$(BIN): $(OBJS)
$(LINK.o) $^
$(OBJDIR)/%.o: %.c
$(OBJDIR)/%.o: %.c $(DEPDIR)/%.d
$(PRECOMPILE)
$(COMPILE.c) $<
$(POSTCOMPILE)
$(OBJDIR)/%.o: %.cpp
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d
$(PRECOMPILE)
$(COMPILE.cc) $<
$(POSTCOMPILE)
$(OBJDIR)/%.o: %.cc
$(OBJDIR)/%.o: %.cc $(DEPDIR)/%.d
$(PRECOMPILE)
$(COMPILE.cc) $<
$(POSTCOMPILE)
$(OBJDIR)/%.o: %.cxx
$(OBJDIR)/%.o: %.cxx $(DEPDIR)/%.d
$(PRECOMPILE)
$(COMPILE.cc) $<
$(POSTCOMPILE)
.PRECIOUS = $(DEPDIR)/%.d
$(DEPDIR)/%.d: ;
-include $(DEPS)
You create the object directory with this line:
$(shell mkdir -p $(dir $(OBJS)) >/dev/null)
but you don't create the DEPDIR anywhere, so when the compiler tries to create a file there it fails.
You could add this line to make the dependency directory:
$(shell mkdir -p $(DEPDIR))
Or, add it into the previous shell function.

Makefile with generic intermediate rules

I have a makefile that works that looks like this:
TARGET:=prog
SOURCES:=a.c b.c
CFLAGS:=-Wall -g -O2
OBJECTS:=$(SOURCES:%.c=%.o)
$(TARGET): $(OBJECTS)
gcc -o $# $^
%.o: %.c
gcc $(CFLAGS) -c -o $# $<
clean:
rm -f $(TARGET) $(OBJECTS)
The only variables that the user needs to change are the target name and the sources required to build it. The objects that need to be generated are automatically determined from the sources. I would like to extend this to support multiple targets, each with its own list of sources. I'm having trouble getting the syntax right, though. This is the general idea:
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
OBJECTS_$#=$(SOURCES_$#:%.c=%.o)
$(TARGETS): $(OBJECTS_$#)
gcc -o $# $^
%.o: %.c
gcc $(CFLAGS) -c -o $# $<
clean:
rm -f $(TARGET) $(OBJECTS)
But I can't get the object list to be generated correctly. I'm also unsure how to write the clean rule to clean all of the objects. Is this possible?
There are two primary ways that I see to do this.
The first involves dynamically creating the target/prerequisite mappings using the $(eval) function.
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
$(TARGETS):
gcc -o $# $^
$(foreach t,$(TARGETS),$(eval OBJECTS_$t := $(SOURCES_$t:.c=.o))$(eval $t: $(OBJECTS_$t)))
The second involves using Secondary Expansion.
TARGETS:=prog1 prog2
SOURCES_prog1:=a.c b.c
SOURCES_prog2:=a.c c.c
CFLAGS:=-Wall -g -O2
.SECONDEXPANSION:
$(TARGETS): $$(OBJECTS_$$#)
gcc -o $# $^
$(foreach t,$(TARGETS),$(eval OBJECTS_$t := $(SOURCES_$t:.c=.o)))
In either case the clean target becomes:
clean:
rm -f $(TARGETS) $(foreach t,$(TARGETS),$(OBJECTS_$t))

gcc cannot specify -o with -c or -S with Multiple files

Whenever I am trying to build something like this in my Makefile -
gcc -o main.o -IStarterWare_Files -c main.c StarterWare_Files/test.h StarterWare_Files/add.h
It throws me error that gcc: cannot specify -o with -c or -S with multiple files. Basically I want my makefile to build the target again if I change for example some macro in one of my header files. My current Makefile is -
EXE = nextgenrsm
CC = gcc
LIBS = StarterWare_Files/
CPPFLAGS = _IStarterWare_Files/
MAIN_OBS = $(patsubst %.c,%.o,$(wildcard *.c))
LIB_OBS = $(patsubst %.c,%.o,$(wildcard StarterWare_Files/*.c))
all: $(EXE)
$(EXE): $(MAIN_OBS) $(LIB_OBS)
$(CC) -o $# $(LDFLAGS) $(MAIN_OBS) $(LIB_OBS) $(LDLIBS)
%.o: %.c
$(CC) -o $# -MD -MP $(CPPFLAGS) $(CFLAGS) -c $^
ALL_DEPS = $(patsubst %.o,%.d,$(MAIN_OBS), $(LIB_OBS))
-include $(ALL_DEPS)
clean:
rm -f $(LIB_OBS) $(EXE) $(MAIN_OBS) $(ALL_DEPS)
.PHONY: all clean
I can't figure out what changes to make to build my executable again if one of the header files is modified. I don't want to do make clean and make again.
The way the automake system handles this is to not use %.o: %.c but instead list the C file and all of the headers in the C file.
So for example:
main.o: main.c StarterWare_Files/test.h StarterWare_Files/add.h
$(CC) -o $# -MD -MP $(CPPFLAGS) $(CFLAGS) -c $^
See makedepends for a tool that will read C files and figure out the make dependencies.

Making a better Makefile

so I learned what a Makefile was some time ago, created a template Makefile and all I do is copy and alter the same file for every program I'm doing. I changed it a few times, but it's still a very crude Makefile. How should I improve it? This is an example of my current version:
CC = g++
CFLAGS = -std=gnu++0x -m64 -O3 -Wall
IFLAGS = -I/usr/include/igraph
LFLAGS = -ligraph -lgsl -lgslcblas -lm
DFLAGS = -g -pg
# make all
all: run test
# make a fresh compilation from scratch
fresh: clean test
#makes the final executable binary
run: main.o foo1.o foo2.o
$(CC) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes the test executable with debugging and profiling tags
test: test.o foo1.o foo2.o
$(CC) $(DFLAGS) $(CFLAGS) $(LFLAGS) $^ -o $#
#makes teste.o
teste.o: teste.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#makes main.o
main.o: main.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo1
foo1.o: foo1.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
#file foo2
foo2.o: foo2.cpp
$(CC) $(CFLAGS) $(IFLAGS) -c $^ -o $#
clean: clean-test clean-o clean-annoying
clean-test:
rm test-rfv
clean-o:
rm *.o -rfv
clean-annoying:
rm *~ -rfv
Just by visually comparing with other makefiles I saw around in the web, this seems to be not a very bright Makefile. I don't know how they work, but I can see there's significantly less boilerplate and more generic code in them.
Can this can be made better, safer, and easier to particularize for each project?
You don't want to name specific files in a makefile if you can get away with it, and 99% of the time you can. This page shows how to develop a very general makefile. The following is my own makefile, based on that page's info:
SHELL := bash
PROG := pathed.exe
OUTDIRS := bin/debug bin/rel obj/debug obj/rel
PROG_REL := bin/rel/$(PROG)
PROG_DEBUG := bin/debug/$(PROG)
SRCFILES := $(wildcard src/*.cpp)
OBJFILES_REL := $(patsubst src/%.cpp,obj/rel/%.o,$(SRCFILES))
OBJFILES_DEBUG := $(patsubst src/%.cpp,obj/debug/%.o,$(SRCFILES))
DEPFILES := $(patsubst src/%.cpp,obj/%.d,$(SRCFILES))
CFLAGS := -Iinc -Wall -Wextra -MMD -MP
DBFLAGS := -g
RELFLAGS :=
CC := g++
.PHONY: default all testmake debug release clean dirs
default: debug
all: dirs clean debug release
dirs:
#mkdir -p $(OUTDIRS)
debug: $(PROG_DEBUG)
release: $(PROG_REL)
testmake:
#echo OBJFILES_REL = $(OBJFILES_REL)
#echo OBJFILES_DEBUG = $(OBJFILES_DEBUG)
#echo SRCFILES = $(SRCFILES)
#echo DEPFILES = $(DEPFILES)
clean:
rm -f $(OBJFILES_REL) $(OBJFILES_DEBUG) $(DEPFILES) $(PROG)
$(PROG_REL): $(OBJFILES_REL)
$(CC) $(OBJFILES_REL) -o $(PROG_REL)
strip $(PROG_REL)
#echo "---- created release binary ----"
$(PROG_DEBUG): $(OBJFILES_DEBUG)
$(CC) $(OBJFILES_DEBUG) -o $(PROG_DEBUG)
#echo "---- created debug binary ----"
-include $(DEPFILES)
obj/rel/%.o: src/%.cpp
$(CC) $(RELFLAGS) $(CFLAGS) -MF $(patsubst obj/rel/%.o, obj/%.d,$#) -c $< -o $#
obj/debug/%.o: src/%.cpp
$(CC) $(DBFLAGS) $(CFLAGS) -MF $(patsubst obj/debug/%.o, obj/%.d,$#) -c $< -o $#
Do NOT use CC for the C++ compiler. The standard convention is that CC is the C compiler, CXX is the C++ compiler. CFLAGS are flags for the C compiler, CXXFLAGS are flags for the C++ compiler, and CPPFLAGS are flags for the pre-processor (eg, -I or -D flags). Use LDFLAGS for -L flags to the linker, and LDLIBS (or LOADLIBES) for -l flags.
Using the standard conventions is good not just because it makes things easier for others to understand, but also because it allows you to take advantage of implicit rules. If make needs to make a .o file from a .c file and you have not provided a rule, it will use a standard rule and honor the settings of CC, CFLAGS, and CPPFLAGS. If CC is a C++ compiler, things will probably not work.

Resources