.gitignore: Ignore the generated distribution tarball
[assignment-template.git] / Makefile
index 74a07e0f1d18cf6dd89d9f024b23ed5bef306b88..af2d15bbbc003d4a322be1236b70dfab14e2abb0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 2012  W. Trevor King
+# Copyright (C) 2012-2013  W. Trevor King
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -24,18 +24,29 @@ COURSE = phys405
 PACKAGE = hw0
 VERSION = 1
 RELEASE = $(COURSE)-$(PACKAGE)-$(VERSION)
-PROGRAM = hello_world
+RUN_PROGRAM = hello_world
+SCRIPTS =
+C_PROGRAMS = hello_world
+CXX_PROGRAMS = goodbye_world
+PROGRAMS = $(C_PROGRAMS) $(CXX_PROGRAMS)
 
 # Define the source files that will be distributed in the tarball
-SOURCE = *.c COPYING Makefile README
+SOURCE = *.c *.cpp *.h COPYING Makefile README $(SCRIPTS)
 
-# Define a list of object files needed to link PROGRAM
-OBJECTS = $(PROGRAM).o
+# Define a list of object files needed to link PROGRAM.  We're setting
+# things up here so that a program % depends on the object file %.o.
+# For details on `foreach` and `eval`, see
+#   http://www.gnu.org/software/make/manual/html_node/Foreach-Function.html
+#   http://www.gnu.org/software/make/manual/html_node/Eval-Function.html
+$(foreach PROGRAM, $(PROGRAMS), $(eval $(PROGRAM)_OBJECTS = $(PROGRAM).o))
+
+# You can override a paricular case here with something like:
+#hello_world_OBJECTS = hello_world.o utility_code.o
 
 # You may want to link agains external libraries.  For example, to
 # link against the system math library, use.
-#   LIBS = -lm
-LIBS =
+#   hello_world_LIBS = -lm
+$(foreach PROGRAM, $(PROGRAMS), $(eval $(PROGRAM)_LIBS =))
 
 # Define useful programs (this makes it easy to swap in alternates)
 CP = cp
@@ -46,10 +57,12 @@ TAR = tar
 
 # Declare targets that do not generate files of the same name.
 #   http://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
-.PHONY: all help clean dist run
+.PHONY: all help clean dist run print- printvars
 
 # target: all - the default target
-all: $(PROGRAM)
+# The target of the first rule is the default goal.  See:
+#   http://www.gnu.org/software/make/manual/html_node/Rules.html
+all: $(PROGRAMS)
 
 # target: help - display callable targets
 # Use `grep` to search this file for target comments
@@ -58,7 +71,7 @@ help:
 
 # target: clean - remove automatically generated files
 clean:
-       $(RM) -rf $(PROGRAM) *.o $(RELEASE)*
+       $(RM) -rf $(PROGRAMS) *.o $(RELEASE)*
 
 # target: dist - generate a tarball packaging the source
 # Here, we move the source into a temporary release directory, tar the
@@ -71,23 +84,66 @@ dist:
 
 # target: hello_world - compile the hello_world program
 # Use GCC to link the program from object files.
-$(PROGRAM): $(OBJECTS)
-       $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+# For an explanation of the static pattern rule syntax:
+#   targets ...: target-pattern: prereq-patterns ...
+# see
+#   http://www.gnu.org/software/make/manual/html_node/Static-Usage.html
+# For an explanation of $@, $^, and other special variables, see
+#   http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
+# For an explanation of .SECONDEXPANSION, see
+#   http://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html
+# `$$` escapes make-variable expansion for the first pass through the
+# recipe.  See
+#   http://www.gnu.org/software/make/manual/html_node/Variables-in-Recipes.html
+#
+# What's going on here?  During the read-in phase, Make expands the
+# rule to
+#   hello_world goodbye_world: % : $($(@)_OBJECTS)
+# Because we're using .SECONDEXPANSION, Make expands the prerequisites
+# again during the target-update phase.  If we're building
+# hello_world, $(@) will expand to hello_world, and we'll have
+#   hello_world goodbye_world: hello_world : $(hello_world_OBJECTS)
+# as the variable expansion continues, we end up with
+#   hello_world goodbye_world: hello_world : hello_world.o
+# which is the final rule used to determine the prerequisites.
+#
+# The recipe expands to
+#   cc  -o hello_world hello_world.o $(hello_world_LIBS)
+# which expands to (if hello_world_LIBS was set to `-lm`)
+#   cc  -o hello_world hello_world.o -lm
+#
+# Striking the right balance between "everything handled
+# automatically" (i.e. "complicated") and "everything handled
+# manually" (i.e. tedious) is difficult, and maybe this rule crosses
+# the line.  The simpler alternative would be to define your own
+# prerequisites for each program you wish to compile, and you're
+# certainly allowed to go that route if you wish.
+.SECONDEXPANSION:
+$(C_PROGRAMS): % : $$($$(@)_OBJECTS)
+       $(CC) $(LDFLAGS) -o "$@" $^ $($(@)_LIBS)
+
+.SECONDEXPANSION:
+$(CXX_PROGRAMS): % : $$($$(*)_OBJECTS)
+       $(CXX) $(LDFLAGS) -o "$@" $^ $($(@)_LIBS)
 
 # target: run - use the program for its intended purpose
-# Here we just execute PROGRAM, but you could also use something like
-#   run: $(PROGRAM) plot.gp
-#     ./$(PROGRAM) > data
+# Here we just execute RUN_PROGRAM, but you could also use something
+# like
+#   run: $(RUN_PROGRAM) plot.gp
+#     ./$(RUN_PROGRAM) > data
 #     gnuplot plot.gp
 # where plot.gp was a gnuplot script for plotting data generated by
-# PROGRAM.
-run: $(PROGRAM)
-       ./$(PROGRAM)
+# RUN_PROGRAM.
+run: $(PROGRAMS)
+       ./$(RUN_PROGRAM)
 
-# Matching rule for compiling object files from C++ source
+# Pattern rule for compiling object files from C++ source
 # There is an implicit rule for this in GNU make
 #   http://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html
-# but I redefine it here for clarity.
+# but I redefine it here for clarity.  For an explanation of the
+#   %.o: %.cpp
+# syntax, see
+#   http://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html
 #
 # CXX and CXXFLAGS have defaults defined by make
 #   http://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
@@ -96,9 +152,41 @@ run: $(PROGRAM)
 # for example, try
 #    $ make CXX=/usr/local/bin/g++ CXXFLAGS=-Wall
 %.o: %.cpp
-       $(CXX) $(CXXFLAGS) -c $<
+       $(CXX) $(CXXFLAGS) -c "$<"
 
-# Matching rule for compiling object files from C source
+# Pattern rule for compiling object files from C source
 # The comments from the C++ rule above also apply here
 %.o: %.c
-       $(CC) $(CFLAGS) -c $<
+       $(CC) $(CFLAGS) -c "$<"
+
+# target: print-% - display a variable value (e.g. print-PROGRAMS)
+# Take some of the mystery out of variable manipulation.  For example,
+#   $ make print-hello_world_OBJECTS
+# The `@` suppresses recipe echoing.  See
+#   http://www.gnu.org/software/make/manual/html_node/Echoing.html
+# The `info` function acts like the shell `echo` command.  See
+#   http://www.gnu.org/software/make/manual/html_node/Make-Control-Functions.html
+# For an explanation of $* and other special variables, see
+#   http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
+print-%:
+       @$(info $* = $($*))
+
+# target: printvars - display all non-default variables
+# .VARIBALES holds a list of all global variables.  See
+#   http://www.gnu.org/software/make/manual/html_node/Special-Variables.html
+# The functions---`sort`, `if`, `filter-out`, `origin`, `warning`, and
+# `value`--- are discussed, respectively, here:
+#   http://www.gnu.org/software/make/manual/html_node/Text-Functions.html#index-sort-580
+#   http://www.gnu.org/software/make/manual/html_node/Conditional-Functions.html
+#   http://www.gnu.org/software/make/manual/html_node/Text-Functions.html#index-filter_002dout-577
+#   http://www.gnu.org/software/make/manual/html_node/Origin-Function.html
+#   http://www.gnu.org/software/make/manual/html_node/Value-Function.html
+printvars:
+       @$(foreach V, \
+               $(sort $(.VARIABLES)), \
+               $(if \
+                       $(filter-out environment default automatic, \
+                               $(origin $V)), \
+                       $(info $V=$($V) (origin: $(origin $V), value: $(value $V))) \
+                       ) \
+               )