-
-
Save maxtruxa/4b3929e118914ccef057f8a05c614b0f to your computer and use it in GitHub Desktop.
# output binary | |
BIN := test | |
# source files | |
SRCS := \ | |
test.cpp | |
# files included in the tarball generated by 'make dist' (e.g. add LICENSE file) | |
DISTFILES := $(BIN) | |
# filename of the tar archive generated by 'make dist' | |
DISTOUTPUT := $(BIN).tar.gz | |
# 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))) | |
# dependency files, auto generated from source files | |
DEPS := $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS))) | |
# compilers (at least gcc and clang) don't create the subdirectories automatically | |
$(shell mkdir -p $(dir $(OBJS)) >/dev/null) | |
$(shell mkdir -p $(dir $(DEPS)) >/dev/null) | |
# C compiler | |
CC := clang | |
# C++ compiler | |
CXX := clang++ | |
# linker | |
LD := clang++ | |
# tar | |
TAR := tar | |
# C flags | |
CFLAGS := -std=c11 | |
# C++ flags | |
CXXFLAGS := -std=c++11 | |
# C/C++ flags | |
CPPFLAGS := -g -Wall -Wextra -pedantic | |
# linker flags | |
LDFLAGS := | |
# linker flags: libraries to link (e.g. -lfoo) | |
LDLIBS := | |
# flags required for dependency generation; passed to compilers | |
DEPFLAGS = -MT $@ -MD -MP -MF $(DEPDIR)/$*.Td | |
# compile C source files | |
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) -c -o $@ | |
# 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) | |
dist: $(DISTFILES) | |
$(TAR) -cvzf $(DISTOUTPUT) $^ | |
.PHONY: clean | |
clean: | |
$(RM) -r $(OBJDIR) $(DEPDIR) | |
.PHONY: distclean | |
distclean: clean | |
$(RM) $(BIN) $(DISTOUTPUT) | |
.PHONY: install | |
install: | |
@echo no install tasks configured | |
.PHONY: uninstall | |
uninstall: | |
@echo no uninstall tasks configured | |
.PHONY: check | |
check: | |
@echo no tests configured | |
.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) |
#include <iostream> | |
int main() { | |
std::cout << "Hello, World!\n"; | |
return 0; | |
} |
What is the postcompile step used for?
Suppose I have test1.cpp, test2.cpp, ..., testn.cpp and I need to build an executable for each. How do I change this generic make file? I would like to have the choice of running "make all" or "make test1", "make test2", etc.
Thanks!
A million times--thank you! Starting from this makefile template saved me a TON of headaches.
Your dist
target contradicts the usual practice: it's supposed to build a source tarball. If someone wants to produce a binary tarball, he will use make install DESTDIR=/opt/foo
for that.
This is awesome - thanks very much for publishing this.
I have spent hours trying to fiddle with a makefile for a new project, wanting my obj and dep files to be hidden away from the sources - this works perfectly. I have one minor bit of feedback - I needed to rearrange LINK.o
to get it to work for me, as g++ was being order-specific - I needed to do this:
$(BIN): $(OBJS)
$(LD) -o $@ $^ $(LDFLAGS) $(LDLIBS)
The normal order for your makefile is this:
$(BIN): $(OBJS)
$(LD) $(LDFLAGS) $(LDLIBS) -o $@ $^
However in my circumstance, I then found undefined references.
Thanks again.
On line 116, I believe .PRECIOUS =
should actually be .PRECIOUS:
to avoid deleting .d intermediate files, see https://stackoverflow.com/a/56424855/150884
@hertzsprung Thanks for catching that typo!
I have maybe an issue here, where running make twice it makes something more, is this supposed to work like that?
buildlog: https://pastebin.com/TqRtgQ7q (2 build commands in a row, no changes to sources)
makefile: https://pastebin.com/iKYuAsCF
@maxtruxa Am I doing something wrong?
@maxsupermanhd That is strange. Some of the prerequisites must have changed, otherwise make wouldn't have run the recipes a second time.
Two questions: Does this happen reproducibly on every fresh build? And does this happen when running make sequentially (i.e. without -j
) as well? I tried it on a sample project of mine and it works fine with and without -j4
.
Two questions: Does this happen reproducibly on every fresh build? And does this happen when running make sequentially (i.e. without
-j
) as well? I tried it on a sample project of mine and it works fine with and without-j4
.
- Yes, every build. But first one builds all and target works fine, second run makes no sense and just making already ready to link files.
- With or without
-j
this is not affecting build (except time).
Any help will be very useful.
@maxtruxa
@maxsupermanhd Depending on your version of make, you can run make with the --trace
option which tells you why make is running stuff.
@maxtruxa Ok, I found the main reason of this, after building target make will update objects because of .d files updated. Maybe somehow implement make depend
?
Traced compile log: https://pastebin.com/aktN5rVq
Can you fix this or there is my side issue?
LDLIBS is actually not defined anywhere
@MartinZeman17 That's correct. LDLIBS
is one of the predefined variables used by implicit Make rules (see here). I used LDLIBS
in the linker rules as well, to mirror the behavior of implicit rules but by default there are no libs specified. To make this more obvious, I added an empty assignment to LDLIBS
.
@johan-boule Yes, thank you for pointing that out. Sadly, I haven't had the time to fix that.
@maxsupermanhd I had the same problem and got the makefile to handle repeated builds by treating $(DEPDIR)/%.d
as an order-only dependancy, so that the creation date of the dependancy files doesn't matter.
Note to future readers: Save yourself the trouble and just use CMake.
How do I specify a source directory? Do I have to list out all of the files that need to be compiled and then make
will calculate dependencies? Or can I just tell it to "compile ./src/main.cpp
" and then it will look at all of the files in ./src
?
Generic Makefile
Features
For basic usage just set
BIN
to the desired output binary name andSRCS
to your input source files.This is achieved by mirroring the actual source tree in the object file directory. A lot of simple makefiles flatten the file hierarchy, so
foo.cpp
andbar/foo.cpp
both end up producingfoo.o
, which obviously won't work.OBJDIR
andDEPDIR
, respectively).Less clutter in your project directory, yay!
make dist
.Included files are modified through
DISTFILES
and the tarball name throughDISTOUTPUT
.make help
.make
features are kept intact:CC
,CXX
,LD
, ...CFLAGS
,CXXFLAGS
,CPPFLAGS
,LDFLAGS
, ...Warning: Do _not_ point
OBJDIR
orDEPDIR
to a directory that contains files that should be kept when runningmake clean
/make distclean
. Theclean
anddistclean
targets provided in this makefile delete those directories recursively.If you want to point
OBJDIR
and/orDEPDIR
to the current directory (or any directory containing files that shouldn't be deleted), change theclean
target like this:clean: $(RM) $(OBJS) $(DEPS)
This solution has one (minor) drawback: When a source file is renamed or deleted, the corresponding object file is not being removed.
Examples
Note: Extra newlines inserted between shell commands for better readability.
Compile Binary
make
/make all
:Create Distribution Tar File
make dist
: