5
\$\begingroup\$

This makefile (for GNU Make) has grown slowly as I've been building code samples from this site and others.

One thing that's different from most project makefiles is that each binary tends to have its own particular compilation flags, so I've had to allow flexibility in overriding warnings, libraries, and other aspects independently.

I have subdirectories

stackexchange/review/
stackexchange/golf/
stackexchange/stackoverflow/

and so on. This common makefile resides in stackexchange/include.mak and is included by a Makefile in each subdirectory. The subdirectories each have source files named using the question number. A subdirectory makefile looks something like:

include ../include.mak

142807: CXXFLAGS += -fconcepts
142807: CXXFLAGS += -fopenmp

40762798: CXXFLAGS += -pthread -Wno-effc++
40762798: LDLIBS += -lpthread
40762798: LINK.o = $(LINK.cc)
40762798: 40762798.o gtest_main.o gtest-all.o gmock-all.o

159439.o: CXXVER = c++11

162913: PKGS += opencv

157881: CXXVER = gnu++17
157881: CXXFLAGS += -O3

164699: CXXFLAGS += -fconcepts

164972: PKGS += Qt5Widgets
164972: LINK.o = $(LINK.cc)
164972.o: 164972.cpp 164972_moc.cpp


25678481: LDLIBS += -lm
25678481.o: CFLAGS += -Wfloat-conversion

2918353: PKGS += Qt5Core

42110050: LDLIBS += -ljpeg -lX11

42504487.o: CFLAGS += -Ofast -ftree-vectorize -fopt-info-vec-missed -mavx2 -msse4

(Note: the above is an amalgamation of review, golf and overflow makefiles, so the question numbers might not match what you're expecting).

The main rules and defaults are in include.mak, and that is:

SHELL = bash

CC := gcc
CXX := g++

CXXVER := c++2a
CVER := c17
WARNINGS = -Wall -Wextra -Wwrite-strings -Wno-parentheses
WARNINGS += -Wpedantic -Warray-bounds
CXX_WARNINGS += $(if $(PKGS),,-Weffc++)

CXXFLAGS += -std=$(CXXVER) -fPIC -g $(WARNINGS) $(CXX_WARNINGS) $(INCLUDES)
CFLAGS += -std=$(CVER) -fPIC -g $(WARNINGS) $(CC_WARNINGS) $(INCLUDES)
LDLIBS += $(LIBS)

# These EXTRA_FOO varibles allow users to add to FOO (as alternative
# to completely overriding them) with Make command-line arguments.
CXXFLAGS += $(EXTRA_CXXFLAGS)
CFLAGS += $(EXTRA_CFLAGS)
WARNINGS += $(EXTRA_WARNINGS)

CXXFLAGS += $(patsubst -I%,-isystem %,$(if $(PKGS),$(shell pkg-config --cflags $(PKGS))))
CFLAGS += $(patsubst -I%,-isystem %,$(if $(PKGS),$(shell pkg-config --cflags $(PKGS))))
LDLIBS += $(if $(PKGS),$(shell pkg-config --libs $(PKGS)))

PYTHON=/usr/bin/python3

VALGRIND_ARGS := --leak-check=full
#VALGRIND_ARGS += -q

# Prevent test programs from competing too hard for resources (particularly memory, which can lead
# to thrashing swap).
LIMITS = ulimit -v 1048576 -t 60

# User can supply command-line arguments in RUNARGS (which will be
# shell-processed) and standard input in INPUT.
export INPUT

RUN = ./$< $(RUNARGS) $(if $(INPUT),<<<"$$INPUT") $(POSTPROC)

.PHONY: %.run %.time %.valgrind %.shellcheck
.DELETE_ON_ERROR:

%.run: %
    $(LIMITS); exec \
$(TOOL) $(RUN)

%.run: %.py
    $(LIMITS); exec \
$(TOOL) $(PYTHON) $(RUN)

%.run: %.sh
    $(LIMITS); if test -x $<; then exec $(TOOL) $(RUN); else exec $(TOOL) $(SHELL) $(RUN); fi

%.run: %.dc
    $(LIMITS); exec \
$(TOOL) $(RUN)

%.run: %.sed
    $(LIMITS); exec \
$(TOOL) sed $(SEDFLAGS) -f $(RUN)

c%.run: c%.class
    $(LIMITS); exec \
$(TOOL) java c$* $(wordlist 2,9999,$(RUN))


%.time:
    $(MAKE) TOOL=time $*.run

%.valgrind:
    $(MAKE) TOOL='valgrind $(VALGRIND_ARGS)' $*.run

%.shellcheck: %.sh
    shellcheck -f gcc $(SHELLCHECK_ARGS) $<


%_moc.cpp: %.h
    moc -o $@ $<

%_moc.cpp: %.cpp
    moc -o $@ $<

%_ui.h: %.ui
    uic -p -o $@ $<

%.s: %.c
    $(CC) $(OUTPUT_OPTION) $< -S $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH)

%.s: %.cpp
    $(CXX) $(OUTPUT_OPTION) $< -S $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH)


c%.class: c%.java
    javac $^

%.o: %.s
    as -g --32 -o $@ $<

%.s: %.S
    intel2gas -o $@ $<


# Google Test objects

GTEST_DIR = /usr/src/gtest
GMOCK_DIR = /usr/src/gmock

# All Google Test headers.  Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(wildcard /usr/include/gtest/*.h \
                           /usr/include/gtest/internal/*.h)

# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(wildcard $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h) $(GTEST_HEADERS)

# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized.  This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
VPATH += $(GTEST_DIR)/src
VPATH += $(GMOCK_DIR)/src
gtest%.o: CXX_WARNINGS += -Wno-effc++
gtest%.o: CXXFLAGS += -I$(GTEST_DIR) -isystem $(GTEST_DIR)/include
gtest%.o: gtest%.cc $(GTEST_SRCS_)
    $(COMPILE.cpp) $(OUTPUT_OPTION) $<

gmock%.o: override INCLUDES += -I$(GMOCK_DIR) -isystem $(GMOCK_DIR)


clean::
    $(RM) *_moc.cpp *.class *.o *~
    find . -perm -1 \! -name '*.*' -type f -delete

I'd like a general review of the structure and the rules. Can I reduce the amount I need to copy/paste when using Qt+moc and/or GTest?

If you have any advice on whether the %.run: % dependencies can be made to work as I want, I'd appreciate that too. (I have to explicitly make 123 123.run because Make won't recognise it must build 123 in order to be able to build 123.run - am I doing something wrong, or am I hitting a limitation of the tool?)

\$\endgroup\$
  • \$\begingroup\$ How do you support a project/question, where multiple files are needed? For eg. a post where a.[hc] and b.[hc] are needed for main.c? Do you combine them all in a single file? Can your makefile support a stackexchange/review/<question>/<files> structure? \$\endgroup\$ – hjpotter92 Apr 4 '18 at 0:45
  • \$\begingroup\$ Usually, I combine the files into a single source file. Where that's not possible (because either the Makefile itself or the relationship between headers and sources is at the heart of the question), then I create a subdirectory with a Makefile that begins with include ../../include.mak to quickly get to the same baseline. \$\endgroup\$ – Toby Speight Apr 4 '18 at 8:21
  • \$\begingroup\$ Have you seen this question? It's an automated script that, among other things, creates a CMake file from a CodeReview question. \$\endgroup\$ – Edward Apr 4 '18 at 10:53
  • \$\begingroup\$ I hadn't seen that - thanks @Edward. Is there a good reason to go through CMake rather than just using Make directly? \$\endgroup\$ – Toby Speight Apr 4 '18 at 11:39
  • \$\begingroup\$ The advantage to CMake is that it creates cross-platform targets. So it creates makefiles on Linux and Visual Studio scripts on Windows, etc. \$\endgroup\$ – Edward Apr 4 '18 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.