2023-03-14 18:17:58 +00:00
#NEWPROJECT requires refactor.sh main.c
# Taken from https://stackoverflow.com/questions/30573481/how-to-write-a-makefile-with-separate-source-and-header-directories
2023-06-06 15:05:52 +00:00
# Always build targets and unit tests together. This avoids the scenario wherein the targets are built, then the source files are edited, then the unit tests are built and, accidentally, outdated targets are packaged and shipped. I say this knowing full well that I never ship my targets to anyone ever.
MAKEFILE_VERSION := Makefile from battery log v1.0
PREFIX :=
SUFFIX :=
2023-03-14 18:17:58 +00:00
SRCDIR := sources
MAINDIR := $( SRCDIR) /main
2023-06-06 15:05:52 +00:00
INCDIR := $( abspath $( SRCDIR) /include)
2023-03-14 18:17:58 +00:00
OBJDIR := objects
MAINOD := $( OBJDIR) /main
BINDIR := build
2023-03-14 20:47:03 +00:00
INSTDIR := /usr/local/bin
2023-03-14 18:17:58 +00:00
REFACTOR := refactor.h
2023-06-06 15:05:52 +00:00
TESTCFLAGS := $( shell pkg-config --cflags criterion)
TESTLIBS := $( shell pkg-config --libs criterion)
TESTDIR := $( SRCDIR) /test
TESTOD := $( OBJDIR) /test
2023-03-14 18:17:58 +00:00
HEADERS := $( wildcard $( INCDIR) /*.h)
MAINS := $( wildcard $( MAINDIR) /*.c)
MAINOBJS := $( patsubst $( MAINDIR) /%.c, $( MAINOD) /%.o, $( MAINS) )
2023-06-06 15:05:52 +00:00
TESTS := $( wildcard $( TESTDIR) /*.c)
TESTOBJS := $( patsubst $( TESTDIR) /%.c, $( TESTOD) /%.o, $( TESTS) )
UNITTESTS := $( patsubst $( TESTDIR) /%.c, $( BINDIR) /%, $( TESTS) )
2023-03-14 18:17:58 +00:00
TARGETS := $( patsubst $( MAINDIR) /%.c, $( BINDIR) /%, $( MAINS) )
SOURCES := $( wildcard $( SRCDIR) /*.c)
OBJECTS := $( patsubst $( SRCDIR) /%.c, $( OBJDIR) /%.o, $( SOURCES) )
2023-06-06 15:05:52 +00:00
CPPFLAGS := -I$( INCDIR) -MMD -MP -DUSE_LIBNOTIFY
2024-09-29 20:34:16 +00:00
CFLAGS := -Wall -Werror -Wpedantic $( shell pkg-config --cflags libnotify) -g
2023-03-14 18:17:58 +00:00
LDFLAGS := #-Lmath or whatever
2023-06-06 15:05:52 +00:00
LDLIBS := $( shell pkg-config --libs libnotify) #-lm or whatever
d e f i n e MANGLE =
$( dir $( 1) ) $( PREFIX) $( notdir $( 1) ) $( SUFFIX)
e n d e f
.PHONY : all clean version
2023-03-14 18:17:58 +00:00
2023-06-06 15:05:52 +00:00
all : | $( TARGETS ) $( UNITTESTS )
2023-03-14 18:17:58 +00:00
2023-06-06 15:05:52 +00:00
version :
@echo $( MAKEFILE_VERSION)
2023-03-14 18:17:58 +00:00
refactor :
./refactor.sh
2023-06-06 15:05:52 +00:00
$(UNITTESTS) : $( OBJECTS ) $( TESTOBJS ) | $( BINDIR ) $( OBJDIR ) $( TESTOD )
@echo "Linking unit tests..."
$( CC) $( LDFLAGS) $( OBJECTS) $( patsubst $( BINDIR) /%, $( TESTOD) /%.o, $@ ) $( LDLIBS) $( TESTLIBS) -o $( dir $@ ) $( PREFIX) $( notdir $@ ) $( SUFFIX)
@echo "...Done linking unit tests"
2023-03-14 18:17:58 +00:00
$(TARGETS) : $( OBJECTS ) $( MAINOBJS ) | $( BINDIR ) $( OBJDIR ) $( MAINOD )
2023-06-06 15:05:52 +00:00
@echo "Linking targets..."
$( CC) $( LDFLAGS) $( OBJECTS) $( patsubst $( BINDIR) /%, $( MAINOD) /%.o, $@ ) $( LDLIBS) -o $( dir $@ ) $( PREFIX) $( notdir $@ ) $( SUFFIX)
@echo "...Done linking targets"
2023-03-14 18:17:58 +00:00
2023-06-06 15:05:52 +00:00
$(BINDIR) $(OBJDIR) $(MAINOD) $(TESTOD) :
2023-03-14 18:17:58 +00:00
mkdir --parents $@
$(OBJDIR)/%.o : $( SRCDIR ) /%.c $( HEADERS ) | $( OBJDIR )
$( CC) $( CPPFLAGS) $( CFLAGS) -c $< -o $@
$(MAINOD)/%.o : $( MAINDIR ) /%.c $( HEADERS ) | $( MAINOD )
$( CC) $( CPPFLAGS) $( CFLAGS) -c $< -o $@
2023-06-06 15:05:52 +00:00
$(TESTOD)/%.o : $( TESTDIR ) /%.c $( HEADERS ) | $( TESTOD )
$( CC) $( CPPFLAGS) $( CFLAGS) $( TESTCFLAGS) $( TESTLIBS) -c $< -o $@
2023-03-14 20:47:03 +00:00
# $| evaluates to the order-only prerequisites, in this case: $(TARGETS)
# The $(foreach ...) function will evaluate to a semicolon-separated list of 'cp <target> $instdir'
# Putting this $(foreach ...) into the recipe means that its result
# cp a /usr/local/bin; cp b /usr/local/bin;
# will be run as a shell command.
#
# Shamelessly derived from:
# https://stackoverflow.com/questions/7918511/make-execute-an-action-for-each-prerequisite
install : | $( TARGETS )
@echo " Installing executables to $( INSTDIR) "
2023-06-06 15:05:52 +00:00
@$( foreach target, $| , cp -v $( call MANGLE, $( target) ) $( INSTDIR) ; )
2023-03-14 20:47:03 +00:00
uninstall : | $( TARGETS )
@echo " Removing executables from $( INSTDIR) "
2023-06-06 15:05:52 +00:00
$( foreach target, $( notdir $( call MANGLE, $| ) ) , rm -v $( INSTDIR) /$( target) ; )
2023-03-14 20:47:03 +00:00
2023-03-14 18:17:58 +00:00
clean :
$( RM) -rv $( BINDIR) $( OBJDIR)
2023-06-06 15:05:52 +00:00
# Lazy kludge to run the first executable specified in $(TARGETS)
# Could also be accomplished by running $< but this is easier to read
run : all
$( eval MANGLEDNAME = $( call MANGLE, $( word 1,$( TARGETS) ) ) )
@echo "Running " $( MANGLEDNAME)
@$( MANGLEDNAME)
# @$(dir $(word 1,$(TARGETS)))$(PREFIX)$(notdir $(word 1,$(TARGETS)))$(SUFFIX)
# unit testing!
test : $( TARGETS ) $( UNITTESTS )
@echo "Testing " $( word 1,$( UNITTESTS) )
@$( dir $( word 1,$( UNITTESTS) ) ) $( PREFIX) $( notdir $( word 1,$( UNITTESTS) ) ) $( SUFFIX)
.PHONY : variable
variable : all
$( eval mangle2 = $( dir $( word 1,$( UNITTESTS) ) ) $( PREFIX) $( notdir $( word 1,$( UNITTESTS) ) ) $( SUFFIX) )
$( eval mangle3 = $( call MANGLE, $( UNITTESTS) ) )
@echo "variable declared within a recipe: " $( mangle2)
@echo "variable declared within a recipe using a canned recipe: " $( mangle3)
2023-03-14 18:17:58 +00:00
# Honestly still not sure what this directive does.
# It's an include directive, which processes all .d files that correspond to members of $(OBJ),
# with the hyphen in front meaning that it suppresses/ignores errors (from nonexistent .d files)
# Seems to be included under the stipulation that GNU make will auto-generate .d files based on
# a given file's #include directives?
# https://stackoverflow.com/questions/19114410/what-is-d-file-after-building-with-make
-include $(OBJ : .o =.d )