PACKAGE = hw0
VERSION = 1
RELEASE = $(COURSE)-$(PACKAGE)-$(VERSION)
-PROGRAM = hello_world
+RUN_PROGRAM = hello_world
+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
-# 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
.PHONY: all help clean dist run print- printvars
# target: all - the default target
-all: $(PROGRAM)
+all: $(PROGRAMS)
# target: help - display callable targets
# Use `grep` to search this file for target comments
# 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
# 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
+# targets ...: target-pattern: prereq-patterns ...
+# syntax, 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
+# gcc -o hello_world hello_world.o $(hello_world_LIBS)
+# which expands to (if hello_world_LIBS was set to `-lm`)
+# gcc -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
# There is an implicit rule for this in GNU make
--- /dev/null
+/*
+A simple "hello world" example in C++.
+
+Copyright (C) 2012 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
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+
+using namespace std;
+
+int main() {
+ cout << "goodbye, world\n";
+ return 0;
+}