-
-
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; | |
} |
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
?
A million times--thank you! Starting from this makefile template saved me a TON of headaches.