Make, Makefiles and a Little about CMake

		##########################################
		Some notes about make, Makefiles and cmake.
		##########################################

* "make" was invented as a tool for building a software project, but it
  can be used to manage many kinds of projects.  It uses a set of rules
  (usually stored in a file called "Makefile") to compile, link, or otherwise
  process the files comprising the project.  "make" is useful because it
  can determine which files need re-building, and rebuild only those.

* Two important things to remember:

	1. Makefiles aren't programs, they're configuration files for the "make" program.
	2. Make reads each Makefile twice (we'll come back to that later).

* Makefiles consist of rules made up of target, dependencies and commands.
  These appear in sections that look like this:

	myprogram: myprogram.c myprogram.h
		gcc -o myprogram mprogram.c
		echo Done.

  Here, "myprogram:" specifies a "target", the list "mprogram.c
  myprogram.h" specifies "dependencies" (also known as
  "prerequisites") and the two lines below are a set of commands that
  tell "make" how to create the target, given these dependencies.

* Makefiles can contain any number of such sections, for different
  targets.

* Commands can be any list of shell commands, as many as you need.

* In the end, even the most complicated Makefile boils down to a list
  of sections like this, for one or more targets.  (As we'll see
  later, "make" gives us a lot of tools to help write complicated
  Makefiles.)

* How do we run make?  Typing "make myprogram" will cause "make" to
  look through the Makefile to find a target called "myprogram".
  "make" will then look to see if a file called "myprogram" already
  exists.  

  If it doesn't exist, then "make" will execute the commands specified
  for this target in the Makefile in order to create "myprogam".

  If the file "myprogram" already exists, make will look at each of
  the dependencies to see if any of these files are newer than
  "myprogam".  If any of them are newer, "make" will execute the
  specified commands in order to create a new version of "myprogram".

  What if one of the dependencies of "myprogram" doesn't exist?  In
  that case, "make" will look through the Makefile and try to find
  instructiions for creating each of the missing dependencies.

* If we don't specify a target on the command line (if we just type
  "make"), then "make" will use the first target in the Makefile.

* "make" can be used with any programming language, or even for non-
  programming tasks.  Make's purpose is to keep targets up-to-date
  with the things upon which they depend, using commands you specify to
  do so.  For example, "make" is often used with large LaTeX projects.
  If you have a large LaTeX document that's created from several *.tex
  (or other) files, you can use "make" to do what's necessary to
  create a new version of your document whenever you edit one of the
  files.  (E.g., re-creating bibliographies, etc.)

* The targets in a Makefile don't even need to correspond to real
  files.  For example, consider the following section of a Makefile:

	help:
		echo "Here is how you use this Makefile:"
		echo "  - Thing 1..."
		echo "  - Thing 2..."
		echo "  - Thing 3..."

  If you typed "make help", "make" would look for a file called
  "help", which it presumably wouldn't find.  Then these commands
  would be executed and perhaps they'd give you some useful
  information.

* But what if you, at some later time, accidentally created a file
  called "help"?  This would cause "make help" to do nothing, since
  "make" would find that the target already exists.  We can avoid this
  by explicitly telling "make" that a target doesn't correspond to an
  actual file.  We do this by adding a line like the following to the
  Makefile:

	.PHONY: help

  If "make" sees that a target is marked as "phony", it won't bother to
  check to see if a file by that name exists.

  In addition to "make help", some other common phony targets are
  "make all" and "make install".  The first of these might correspond to
  lines like the following in a Makefile:

	.PHONY: all
	all: myprogram libmylib.a


* Why would you use "make"?  Obviously, for the trivial "myprogram"
  example above, you probably wouldn't use "make" at all.  You'd just
  type the "gcc" command directly.  "make" is useful when you have a
  software project that comprises lots of files.  When you make a
  change to one of the files, "make" rebuilds anything that depends on
  the modified file.  By using "make" you no longer have to keep track
  of which files need to be rebuilt when you make a change.  "make"
  also only rebuilds things that need to be rebuilt, allowing you to
  update your project's files more quickly when you make a change.

* Here's a more complicated Makefile:

	all: energy range

	energy: energy.c libparticle.a
		gcc -o energy energy.c -L. -lparticle

	range: range.c libparticle.a
		gcc -o range range.c -L. -lparticle

	libparticle.a: electron.o muon.o tau.o
		ar -rcs libparticle.a electron.o muon.o tau.o

	electron.o: electron.c
		gcc -c electron.c

	muon.o: muon.c
		gcc -c muon.c

	tau.o: tau.c
		gcc -c tau.c


* This file builds two programs, called "range" and "energy".  It also
  builds a library called "libparticle.a" that's necessary to make the
  two programs.  The library contains functions that live in several
  different source files (electron.c, etc.)

* As you can see, this could get very long, very quickly if we keep
  adding files for other particles.


* "make" gives you several ways to shorten Makefiles.  One of them
  is wild-card expressions for targets and dependencies.  For example,
  we could replace all of the sections for electron.o, muon.o and
  tau.o with a single section like this:

	%.o: %.c
		gcc -c $<

  The expressions %.c and %.o are wildcard expressions that will match
  any file ending in ".c" and ".o", respectively.  In the commands for
  the rule, we can use one of "make's" "automatic variables", "$<".
  The expression $< means "the first dependency in the preceding
  list".  This section of the Makefile tells "make" how to make an
  object file from a C file.  It says "use the command gcc -c on the C
  file".

* The $< expression is just one of many automatic variables
  that are available for use inside Makefiles.  Here's a short
  list:

	$<  The first dependency of the current section.
	$@  The target of the current section.
	$^  The whole list of dependencies for the current section.

  You can find an extensive table of automatic variables here:

  https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html

* We can shorten Makefiles even more by taking advantage of "make's"
  built-in rules.  In the example Makefile above, we don't really need
  a rule that tells make how to create a .o file from a .c file,
  because "make" already has a predefined rule to do that.  "make"
  knows a little about C and Fortran and some other languages.  If it
  sees that it needs to create a .o file, and there's a .c file with a
  corresponding name, make knows that it can generate the .o file
  using "gcc -c".

  You can see "make's" built-in rules by typing "make -p".  You'll
  notice a lot of odd notation in these rules, but don't despair.
  Much of this will become clear when we talk about Makefile
  variables.

  Note that you can always override the built-in rules by specifying
  rules of your own in the Makefile.


* In addition to "make's" automatic variables, you can define
  your own variables inside Makefiles.  Here are some examples:

	MYLIB  := libparticle.a
	OBJS   := electron.o muon.o tau.o
	EXTRA  := particle.o material.o geometry.o
	ALLOBJS := $(OBJS) $(EXTRA)

  The := assignment operator is one way to assign a value to a variable.
  (We'll talk about other ways soon.)  These examples should be
  self-explanatory.  Once defined, variables can be used as in the
  right-hand side of the last example, above.  The value of the
  variable created this way can be as long as you want it to be, but 
  it can only be one line.

  Makefile variables are very much like C pre-processor macros.  In
  particular, these variables can only contain character strings.  For
  example, what does this do?:


	A := 1
	B := 2
	C := $(A) + $(B)

  The variable C will contain the string "1 + 2".  No arithmetic will
  be done.  Everything is just strings.


* Now we should come back to the fact (mentioned at the beginning)
  that "make" reads a Makefile twice.  The first time it reads the
  Makefile, all of the variables are replaced with their values.  This
  gives a "pure" version of the Makefile that contains only targets,
  dependencies and commands.  (This "pure" version isn't written into
  a file, it only exists in "make's" memory.)

  Then, during the second pass, make uses those rules to decide what
  it needs to do. By the time "make" starts using the Makefile to
  compile/link, all that's left is a list of rules
  (target/dependencies/commands).

  This is very much like what happens when you use C pre-processor
  macros in a C program.  In that case, your C program is first sent
  through the pre-processor and all macros are replaced with their
  values, and then the pre-processed file is compiled.


* Here's a second way to define variables.  We can define variables
  with multi-line values using "define":

	define LONGVAR
		echo this is a test
		gcc -o junk junk.c
		echo I'm done!
        endef


* The variables we've looked at so far are what "make" calls
  "immediate" variables.  They are "immediate" because "make" sets the
  variable's value to a fixed string as soon as it sees the :=
  operator.

  "make" also offers a second kind of variable, called "deferred"
  variables.  These may seem stranger to you.

  If we set an immediate variable, C, like this:

	A := 1
	B := 2
	C := $(A) + $(B)

  then C will contain "1 + 2" as soon as we define it, and forever
  after unless we later redefine it with another "C := something"
  statement.

  But what if we'd made one small change?:

        A := 1
        B := 2
        C = $(A) + $(B)

  By omitting the colon before the equals sign when defining C, we've
  made C into a "deferred" variable.  Deferred variables act, in some
  ways, like simple-minded functions.  When "make" sees the "C = $(A)
  + $(B)" statement, it doesn't evaluate the variables A and B and
  then construct the value of C.  Instead, it stores the formula "$(A)
  + $(B)" in the variable C.  Thereafter, whenever you type $(C),
  "make" will look up the CURRENT values of A and B and construct a
  CURRENT value for C.  This means that, whenever we change the values
  of A or B, the value of C will automatically change, too.


* To continue confusing you, there are still other ways to assign
  values to variables.  For example, there's "conditional assignment":

	CC ?= gcc

  This says, set the value of C to "gcc", but only if we haven't
  already defined C somewhere above.

  This can be useful because "make" allows us to include the contents
  of other Makefiles, which may define variables.  We can also set
  Makefile variables from the command line, or from the shell
  environment.  We'll talk about all of these later.

  Note that variables defined through ?= are "deferred".

* By default, "make" imports your shell's environment variables and
  sets corresponding Makefile variables.  For example, you can use the
  variable $(HOME) in your Makefiles, and it will be replaced by the
  value of $HOME in your shell environment.

  You can turn off this behavior by using command-line
  switches when invoking "make" (see "man make").

* You can use the "export" command inside a Makefile to export Makefile
  variables to the shell environment used by your commands.  For
  example, you could say:

	ROOTSYS := /common/lib/root
	export ROOTSYS

	blarg: stuff.c stuff.h
		root blah blah blah

  The value of the Makefile variable ROOTSYS would be exported 
  to the shell environment variable $ROOTSYS, where it could
  be used by the "root" command.

  If you just want to export all Makefile variables, write
  "export" with no variable names following.

* Here's yet another weird way of setting variables: Pattern substitution:

	OBJS := electron.o muon.o tau.o
	LIST := $(OBJS:.o=.c)

  The variable LIST would now have the value "electron.c muon.c tau.c",
  having substituted ".c" for each ".o".

* As we mentioned above, you can "include" one Makefile in another.
  The syntax for this is just:

	include generic.make

  There's also a "-include", which only tries to include the
  file if it exists.

* As with the C pre-processor, Makefiles can have "ifdef" statements,
  which conditionally set values if some variable is defined.
  For example:

	DEBUG := true

	CFLAGS := -Wall
	ifdef DEBUG
		CFLAGS += -g
	else
		CFLAGS += -O3
	endif

* Note that we've also introduced another way of changing the
  value of a variable.  The "+=" operator appends text onto
  the end of a variable.

* Alternatively, we could leave off the "DEBUG :=" line and set the
  variable when we invoke make from the command line, like this:

	make myprogram DEBUG=true

  That would let us turn DEBUG on or off, as needed.

* There's also conditional statement that checks the value of
  a variable:

	ifeq ($(CC),gcc)
		LIBS = -L. -lmylib
	else
		LIBS = -lotherlib
	endif


* "make" also provides many built-in functions.  Here are
  a few examples:

  The "info" function just prints out some text:

	$(info This is an informational message)
	$(info CC is $(CC))


  The "words" function counts the number of words:

	NFILES := $(words $(OBJS))


  The "word" function returns one word from a list
  (here we get word number 2, the second one in the list):

	FILE2 := $(word 2, $(OBJS))

  We can also select certain words from a list based on pattern
  matching.  The following would return any files in the list with
  names beginning with "electron":

	EFILE := $(filter electron.%, $(OBJS))

  Finally, we can execute any shell command like this:

	OUTPUT := $(shell cat myfile.txt | grep something)

  You'll find much more information about "make's" 
  built-in functions here:

	http://www.gnu.org/software/make/manual/html_node/Functions.html


* Finally, "make" provides some command-line arguments that can
  help you find problems with your Makefiles.  You can use "make -n"
  to cause "make" to print out a list of things it WOULD do, without
  actually doing them.  You can use "make -pn" to print out the values
  of all of the macros. You can use "make -d" to cause "make" to print
  out detailed debugging information while it works.


* Here are a couple of useful books about "make":

http://my.safaribooksonline.com/book/software-engineering-and-development/0596006101

http://my.safaribooksonline.com/book/software-engineering-and-development/9780132171953

(The second one covers "make" and several other similar tools.)


* Now for a few words about "cmake".  Cmake is a tool for creating
  Makefiles and other similar things, on a variety of different
  platforms (Windows, OS X and Linux).  It has the ability to
  figure out some dependencies on its own, by reading files.

* Cmake is mainly useful when you have a large project that needs
  to be built for several different operating systems.


* Here's the simplest example showing how cmake can be used:

	mkdir testing
	cd testing

  Put some source files, say "junk.c" and "junk.h" into this directory.

	edit CMakeLists.txt

  CMakeLists.txt is cmake's equivalent of a Makefile.  It
  contains configuration information that tells cmake 
  how to do its job.  Inside this simplest CMakeLists.txt
  file, put the following line:

add_executable(junk junk.c junk.h)

  Now type the following command (note the dot):

	cmake .

  This will cause cmake to create a Makefile for you.  You
  can then build your program by typing:

	make

* For a good tutorial on Cmake, see:

http://mathnathan.com/2010/07/getting-started-with-cmake/