Provided by: makepp_2.0.98.5-2.1_all bug

NAME

       makepp_tutorial -- Tutorial on writing makefiles

DESCRIPTION

       A makefile is the set of instructions that you use to tell makepp how to build your program.  Makepp can
       accept most makefiles written for the standard Unix make, but if you're starting from scratch, it is
       often much simpler to use some of makepp's advanced features.  This is an introduction for writing
       makefiles that are specific to makepp.

       If you already know a lot about writing makefiles, you might want to at least peruse the later sections
       of this file because they show the preferred way to do things with makepp, which is often different from
       the traditional way to do it with make.  Another source of examples and advice on writing makefiles for
       makepp is makepp_cookbook.

       Building a program from its source files can be a complicated and time-consuming operation.  The commands
       are too long to be typed in manually every time.  However, a straightforward shell script is seldom used
       for compiling a program, because it's too time-consuming to recompile all modules when only one of them
       has changed.

       However, it's too error-prone to allow a human to tell the computer which files need to be recompiled.
       Forgetting to recompile a file can mean hours of frustrating debugging.  A reliable automatic tool is
       necessary for determining exactly which modules need recompilation.

       Makepp (short for Make-plus-plus, or make++) is a tool for solving exactly this problem.  It is an
       improvement on the make program, a standard tool that has been around for many years.  It relies either
       on its own builtin knowledge (in very simple cases), or on a file called a Makefile that contains a
       detailed recipe for building the program.

       Usually, the input files are program source code, and the output files are executables, but makepp
       doesn't care what they are.  You can use a makefile to control any kind of procedure where you need to
       selectively execute certain commands depending on which files have changed.  You could, for example, use
       makepp to do data analysis, where your input files are raw data and analysis programs, and your output
       files are processed data or graphs or whatever.  Makepp will figure out which of the processed data files
       need to be updated whenever some of the data files or analysis programs change.  The examples in this
       introduction will assume you are building an executable program from source code, but you can do a lot
       more with makepp than just that if you use your imagination.

       If your program consists of a single module, you probably don't need makepp, because you know that any
       change that you make requires recompiling that module.  However, if your program consists of even just
       two modules, then you will definitely want to use a program like makepp.

   Do I need a makefile?
       If your program is relatively simple and doesn't require anything particularly special, makepp may
       already know how to build it without your explicitly giving instructions.  For example, suppose you have
       a program in a single source file, called "test.c".  You can just type "makepp test" and your program
       will build like this:

           % makepp test
           makepp: Entering directory `/somewhere/or/other'
           gcc -g -Wall -c test.c -o test.o
           gcc -g -Wall test.o     -o test
           Warning: on Unix, to run a program called 'test', you must type
              ./test
           rather than just 'test'.

       These are the basic commands needed to compile a program on Unix.  If these commands don't make any sense
       to you, see makepp_tutorial_compilation.

       Makepp contains builtin rules for C, C++, and Fortran.

       Makepp can sometimes figure out how to compile programs that are contained in more than one source file,
       or programs that must be linked with various system libraries.  It does this by guessing which source
       files and libraries you need based on the files that you include.  The actual algorithm is too
       complicated to discuss here in a tutorial (but see makepp_builtin); you can try it, and if it doesn't
       work automatically for you, you need to write your own makefile.

       By default, for C and C++, makepp compiles the program with debug information and without optimization.
       If you want to turn on optimization so that your program runs faster, change the command line to:

           makepp CFLAGS=-O2 test

       If you're compiling C++ instead of C, use "CXXFLAGS=-O2" instead of "CFLAGS=-O2".  For a complete list of
       other options you can configure without writing a makefile, see makepp_builtin.

       Makepp's builtin rules are somewhat more powerful than the standard Unix make, but if you write programs
       of any complexity, it's likely that you'll need a makefile eventually to tell makepp what to do.

       If you are not familiar with Unix compilation commands, it may be helpful at this point to read
       makepp_tutorial_compilation for a description of what these cryptic Unix compilation commands do.

   A simple makefile
       Suppose you are writing a C++ program which has two source modules, "processing.cxx" and "gui.cxx", along
       with numerous include files.  If you were to build your program from scratch, you would need to execute
       something like these commands:

           c++ -c processing.cxx -o processing.o
           c++ -c gui.cxx -o gui.o
           c++ processing.o gui.o -o my_program

       The first two commands are compilation commands, and the third invokes the linker to combine the two
       object files into a single executable.  If you make changes to "gui.cxx" but not to "processing.cxx",
       then you don't need to reexecute the first command, but you do need to execute the last two commands.
       makepp can figure this out for you automatically.

       (If you've never worked with make before, you may be thinking that you could combine the above three
       commands into a single command, like this:

           c++ processing.cxx gui.cxx -o my_program

       When you omit the "-c" option to the compiler, it combines the compilation and linking step.  This is
       often quite convenient when you are not writing a makefile.  However, it's not a good idea to do this in
       a makefile, because it always recompiles both modules even if one of them hasn't changed, and this can
       take a significant amount of extra time.)

       In order to use makepp to control the build process, you'll need to write a makefile.  The makefile is a
       text file that contains the recipe for building your program.  It usually resides in the same directory
       as the sources, and it is usually called "Makeppfile", "Makefile" or, only at the root of your build tree
       "RootMakeppfile".

       Each one of these commands should be a separate rule in a makefile.  A rule is an instruction for
       building one or more output files from one or more input files.  Makepp determines which rules need to be
       reexecuted by determining whether any of the input files for a rule have changed since the last time the
       rule was executed.

       A rule has a syntax like this:

           output_filenames : input_filenames
               actions

       The first line of the rule contains a space-separated list of output files, followed by a colon, followed
       by a space-separated list of input files.  The output files are also called targets, and the input files
       are also called dependencies; we say that the target file depends on the dependencies, because if any of
       the dependencies change, the target must be rebuilt.

       The remaining lines of the rule (the actions) are shell commands to be executed.  Each action must be
       indented with at least one space (traditional make requires a tab character).  Usually, there's just one
       action line, but there can be as many as you want; each line is executed sequentially, and if any one of
       them fails, the remainder are not executed.  The rule ends at the first line which is not indented.

       Some or all of the actions may also be Perl statements.  These may be on a single line and surrounded by
       "perl { ... }".  Or they may span several lines, in which case the braces must be doubled, "perl {{ ...
       }}".

       You can place the rules in any order in the makefile, but it is traditional to write the rule that links
       the program first, followed by the compilation rules.  One reason for this is that if you simply type
       ""makepp"", then makepp attempts to build the first target in the file, which means that it will build
       your whole program and not just a piece of it.  (If you want to build something other than the first
       target, you have to specify the name of the target on the command line, e.g., ""makepp processing.o"".)

       The above compilation commands should be written as three separate rules.  A makefile for building this
       program could look like this:

           # Link command:
           my_program: processing.o gui.o
               c++ processing.o gui.o -o my_program
               perl {{
                 print "my_program is built\n";
               }}

           # Compilation commands:
           processing.o: processing.cxx
               c++ -c processing.cxx -o processing.o

           gui.o: gui.cxx
               c++ -c gui.cxx -o gui.o

       (Characters on a line following a "#" are ignored; they are just comments.  You do not need the
       ""# Link command:"" comment in the makefile at all.)

       To use this makefile, simply cd to the directory and type ""makepp"".  Makepp will attempt to build the
       first target in the makefile, which is "my_program".  (If you don't want it to build the first target,
       then you have to supply a the name of the target you actually want to build on the command line.)

       When makepp attempts to build "my_program", it realizes that it first must build "processing.o" and
       "gui.o" before it can execute the link command.  So it looks at the other rules in the makefile to
       determine how to build these.

       In order to build "processing.o", makepp uses the second rule.  Since "processing.o" depends on
       "processing.cxx", makepp will also try to make "processing.cxx".  There is no rule to make
       "processing.cxx"; it must already exist.

       Makepp checks whether "processing.cxx" has changed since the last time "processing.o" was built.  By
       default, it determines this by looking at the dates on the file.  Makepp remembers what the date of
       "processing.cxx" was the last time "processing.o" was made by storing it in a separate file (in a
       subdirectory called ".makepp").  Makepp will execute the actions to build the target if any of the
       following is true:

       •   The target does not exist.

       •   The target exists, but makepp does not have any information about the last build.

       •   The date on any input file has changed since the last build, and there is a significant difference in
           the input file.  For C or C++ files this means a change other than whitespace or within comments.

       •   The  date  on  any  target  has changed since the last build, and there is a difference in the target
           file.

       •   The actions have changed since the last build.

       •   The last build occurred on a different architecture (different CPU type or operating system type).

       •   Any environment variable listed on the rule has changed in value.

       It might seem a little funny that makepp executes the action if either the output file or the input files
       have changed since the last build.  Makepp is designed to guarantee that your build is correct, according
       to the commands in the makefile.  If you go and modify the file yourself,  then  makepp  can't  guarantee
       that  the  modified  file is actually correct, so it insists on rebuilding.  (For more information on how
       makepp  decides  whether  to  rebuild,  and  how  you  can  control  this,  see   makepp_signatures   and
       makepp_command.)

       Now  "processing.o"  might  not  depend  only  on "processing.cxx"; if "processing.cxx" includes any ".h"
       files, then it needs to be recompiled if any of those ".h" files has changed,  even  if  "processing.cxx"
       itself has not changed.  You could modify the rule like this:

           # Unnecessary listing of .h files
           processing.o: processing.cxx processing.h simple_vector.h list.h
               c++ -c processing.cxx -o processing.o

       However,  it  is  a real nuisance to modify the makefile every time you change the list of files that are
       included, and it is also extremely error prone.   You  would  not  only  have  to  list  the  files  that
       "processing.cxx"  includes,  but  also all the files that those files include, etc.  You don't have to do
       this. Makepp is smart enough to check for include files automatically.  Any time it sees a  command  that
       looks  like  a  C or C++ compilation (by looking at the first word of the action), it reads in the source
       files looking for "#include" directives.  It knows where to  look  for  include  files  by  parsing  your
       compiler  command  line  for  "-I" options .  Any files which are included are automatically added to the
       dependency list, and any files which those include.  If any  of  them  has  changed,  the  file  will  be
       recompiled.

       Once  makepp  knows  that  "processing.o"  is  up to date, it then determines whether "gui.o" needs to be
       rebuilt by applying the same procedure to the third rule.  When both "processing.o" and "gui.o" are known
       to be built correctly, then makepp applies the same procedure to see if the  link  command  needs  to  be
       reexecuted.

       The  above  makefile  will  work,  but even for this simple problem, an experienced user is not likely to
       write his makefile this way.  Several improvements are discussed in the next sections.

       Using variables

       So far, our makefile for compiling our program of two modules looks like this:

           # Link command:
           my_program: processing.o gui.o
               c++ processing.o gui.o -o my_program

           # Compilation commands:
           processing.o: processing.cxx
               c++ -c processing.cxx -o processing.o

           gui.o: gui.cxx
               c++ -c gui.cxx -o gui.o

       This works wonderfully, but suppose now we want to change some compilation options.  Or maybe we want  to
       use a different compiler.  We'd have to change all three compilation lines.

       Similarly,  suppose  we  want  to  change  the list of modules to compile.  We'd have to change it in two
       places.

       Duplication of information like this is a recipe for disaster.  If you go and change your makefile,  it's
       pretty  much  guaranteed that at some point, you or someone else will forget to change one of the places.
       Depending on what the change is (especially if it affects preprocessor definitions),  this  can  lead  to
       subtle and hard-to-debug problems in your program.

       The  way  to  avoid  duplication of information is to specify the information only once and store it in a
       variable, which can be accessed each time the information is needed.

           # Define the symbols we might want to change:
           CXX := c++
           CXXFLAGS := -g

           OBJECTS     := processing.o gui.o

           my_program: $(OBJECTS)
               $(CXX) $(OBJECTS) -o my_program

           processing.o: processing.cxx
               $(CXX) $(INCLUDES) $(CXXFLAGS) -c processing.cxx -o processing.o

           gui.o: gui.cxx
               $(CXX) $(CXXFLAGS) -c gui.cxx -o gui.o

       Here "$(CXX)" expands to be the value  of  the  variable  "CXX",  and  similarly  for  "$(CXXFLAGS)"  and
       "$(OBJECTS)".  Now we can just change one line in the makefile, and all relevant compilation commands are
       affected.

       In  fact, we don't even need to change the makefile to change compilation options.  Assignments specified
       on the command line override assignments in the makefile.  For example, we could type this to the shell:

           makepp CXXFLAGS="-g -O2"

       which overrides the setting of "CXXFLAGS" in the makefile.  It is as if the makefile contained the line

           CXXFLAGS := -g -O2

       instead of the definition it does contain.

       It might not at all be useful to be able to override these things for your own development,  but  if  you
       distribute your sources to other people, they might appreciate it.

       Variable  names  are  case sensitive (e.g., "OBJECTS" is different from "objects").  Usually people write
       most variables in upper case only, but you don't have to.

       If you need to put a literal dollar sign into a rule action, write it with a  double  dollar  sign,  like
       this:

           test:
               for testfile in *.test; do run_test $$testfile; done

       Conventionally,  there  are a few variables which you might want to set.  These are just conventions, but
       you will see them in a lot of makefiles.

           CC      := cc               # The C compiler.
           CFLAGS  := -g               # C compilation options which relate to
                                       # optimization or debugging (usually
                                       # just -g or -O).  Usually this wouldn't
                                       # include -I options to specify the
                                       # include directories, because then you
                                       # couldn't override it on the command line
                                       # easily as in the above example.
           CXX      := c++             # The C++ compiler.  (Sometimes "CPP" instead
                                       # of CXX.)
           CXXFLAGS := -g              # C++ compilation options related to
                                       # optimization or debugging (-O or -g).
           F77      := f77             # The Fortran compiler.
           FFLAGS   :=                 # Optimization flags for Fortran.

       Makepp will guess appropriate values for  some  of  these  variables  if  you  don't  specify  them  (see
       makepp_builtin), but it is usually best to set them explicitly--it makes it easier on anyone reading your
       makefile.

       There  are  a  lot more extremely powerful things you can do with variables, but first we need to explain
       some more things about makefiles.

       Pattern rules

       Having one rule for each compilation command is fine when there are only a few files, but  what  if  your
       program consists of dozens of source files?  Most of them have to be compiled with very similar commands.
       It  is  tedious  to  type  in  a separate rule for each source file, and then if you decide to change the
       rules, you have to change the makefile in a dozen places.  A better solution to this problem is to use  a
       pattern rule.

       A pattern rule is a concise way of specifying a rule for many files at once.  The rule will depend on the
       file  names,  but  usually  it  depends  on them in a simple way.  You specify a pattern by using the "%"
       wildcard.  When present in the dependency list, "%" matches any string of any length; when present in the
       list of targets, "%" stands for the string that "%" in the dependency list matched.

       The following pattern rule will take any ".c" file and compile it into a ".o" file:

           %.o: %.c
               $(CC) $(CFLAGS) $(INCLUDES) -c $(input) -o $(output)

       (This assumes that you have the  variables  "CC",  "CFLAGS",  and  "INCLUDES"  defined  to  be  something
       suitable.  Makepp will guess a value for "CC" and "CFLAGS".)

       The  first  line  of  the rule says that it applies to every possible input file that matches the pattern
       "%.c".  These ".c" files can be transformed into the corresponding ".o" file using the specified actions.

       The action of rule is quite similar to the other actions we've  seen  previously,  except  that  it  uses
       automatic  variables.   An  automatic  variable  is a variable whose value is automatically set by makepp
       depending on the rule that it appears in.  The most useful automatic variables are:

       "$(input)"
           The name of the first input file.  In this rule, this would  be  the  file  that  matches  the  "%.c"
           pattern.   "$(dependency)"  is  a  synonym for "$(input)".  In older makefiles, you will also see the
           cryptic symbol $< used as well.

       "$(output)"
           The name of the first output file.  In this rule, this would be  the  file  that  matches  the  "%.o"
           pattern.  "$(target)" and $@ are synonyms.

       "$(inputs)"
           The name of all explicitly listed input files.  In this case, since there is only one, "$(inputs)" is
           equivalent to "$(input)".  "$(dependencies)" and $^ are synonyms.

       "$(outputs)"
           The  name  of  all explicitly listed targets.  In this case, since there is only one, "$(outputs)" is
           equivalent to "$(output)".  "$(targets)" is a synonym for "$(outputs)".

       Note that these variables are lower case.

       You can use these  automatic  variables  even  for  non-pattern  rules.   This  avoids  repeating  target
       filenames.

       You can actually do considerably more complicated things with pattern rules.  For example,

           # Put the object files into a separate directory:
           objects/%.o: %.cpp
               $(CXX) $(CXXFLAGS) -c $(input) -o $(output)

           # Run a preprocessor to make source files:
           moc_%.cxx: %.h
               $(MOC) $(input) -o $(output)

       Using  pattern  rules  and automatic variables, we'd probably rewrite our makefile for our simple program
       like this:

           CXX := c++
           CXXFLAGS := -g
           INCLUDES := -I.             # This would contain any -I options to the
                                       # compiler, if there are any.
           LIBS     := -L/usr/X11R6/lib -lX11 # Contains libraries we need to link in.
           OBJECTS  := processing.o gui.o

           my_program: $(OBJECTS)
               $(CXX) $(inputs) -o $(output) $(LIBS)

           %.o: %.cxx
               $(CXX) $(INCLUDES) $(CXXFLAGS) -c $(input) -o $(output)

       Now we don't have to have an explicit rule for each object file we need to produce.  If we  want  to  add
       another  module  to our program, we only have to change the one line that defines the "OBJECTS" variable.
       Note that this makefile is now much more concise than our original makefile.  Each piece  of  information
       occurs  only once so there is no possibility of making a mistake by changing information in one place and
       forgetting to change it in others.

       When you use pattern rules, it's not uncommon for there to be two different rules that  can  produce  the
       same  file.   If both rules are pattern rules, then the one that occurs later in the makefile is actually
       used.  If one rule is a pattern rule, and the other is an explicit rule  (one  that  actually  names  the
       target  file  explicitly),  then the explicit rule is used.  This is often helpful if you want to compile
       most modules with the same command, but there is one module that  needs  slightly  different  compilation
       options, as shown in this makefile fragment:

           CXXFLAGS := -g -O2
           FAST_CXXFLAGS := -DNO_DEBUG -O6 -malign-double -funroll-all-loops

           %.o: %.cpp
               $(CXX) $(CXXFLAGS) -c $(input) -o $(output)

           time_critical_subs.o: time_critical_subs.cpp
               $(CXX) $(FAST_CXXFLAGS) -c $(input) -o $(output)

       There  is  also another syntax that can be more convenient for affecting compilation options for just one
       or a few targets.  It is possible to tell makepp that a  variable  should  have  a  different  value  for
       certain specific targets.  In this example, it would look like this:

           CXXFLAGS := -g -O2
           FAST_CXXFLAGS := -DNO_DEBUG -O6 -malign-double -funroll-all-loops

           %.o: %.cpp
               $(CXX) $(CXXFLAGS) -c $(input) -o $(output)

           time_critical_subs.o: CXXFLAGS := $(FAST_CXXFLAGS)

       In  general, if you specify a variable name after a list of targets, then it takes a different value when
       the build command for those targets is being determined.

       If you find yourself wanting to do something with patterns that isn't  expressed  easily  using  the  "%"
       wildcard,  makepp  has  another  syntax which is somewhat harder to read, but considerably more powerful.
       See the foreach clause in rules for more details.

       Makepp actually has builtin rules for compiling C or C++ or Fortran code,  which  are  available  if  you
       don't  override  them with your own rules.  The builtin rules are almost identical to the examples above.
       Most makefiles contain pattern rules for compilation, but you can depend on  the  builtin  rules  if  you
       want.

   Phony targets
       Often  it  is  convenient  to  put  commands  into the makefile that don't actually build a file, but are
       somehow logically associated with the build process.  For example, a very common procedure  in  makefiles
       is something like this:

           prefix=/usr/local

           install: our_program
               &install -m 0755 our_program $(prefix)/bin
               &install -m 0644 $(wildcard *.png) $(prefix)/share/our_program/icons

           .PHONY: install

       When  someone  types  "makepp install",  then  makepp  first builds "our_program", then runs the commands
       associated with the install target.  The "install" command simply copies its arguments to  the  specified
       directory,  and  sets  the  file's  protection  to  the  indicated  value.  So it copies our_program into
       /usr/local/bin, and some associated data files into /usr/local/share/our_program/icons.  But this doesn't
       create a file called "install" in the current directory.

       The command itself, &install, is preceded by an ampersand.  This means that it is not  a  Shell  command,
       but  a  similar  builtin  command.   (You see the difference in that you need the "$(wildcard)" function,
       because the Shell is not expanding it for you.)  Makepp has quite a few of these,  both  for  portability
       reasons -- "install", when it is at all present, differs wildly, and even trivial commands like "echo" do
       -- and performance.  See the builtin commands for more details.

       The  "install"  target  here is called a phony target because makepp treats it as if it were a real file,
       but it is not actually a file, it's just a trick for forcing makepp to build its  dependencies  and  then
       run some commands.

       That's what the line

           .PHONY: install

       is for.  It tells makepp that it really shouldn't expect the file "./install" to exist after the commands
       have  executed.  If you forget the phony declaration, then makepp will expect the file "install" to exist
       after executing the commands, and it will complain loudly if it does not.

       You can also write the phony declaration like this:
           $(phony install): our_program
             ...

       and then omit the ".PHONY: install" line.  This means that you can declare the target  as  phony  on  the
       same line as you define it, which may make your makefiles more readable.

       Phony  targets are extremely common in makefiles.  In almost all makefiles, the first target is the phony
       target "all", like this:

           $(phony all): program1 program2 program3

       If no target is specified on the command line, makepp attempts to build the first target in the file.  If
       your makefile makes more than just one program, you most likely want to build  all  of  the  programs  by
       default.   In  this example, if the programmer just types "makepp" without any arguments, makepp attempts
       to build "all", which forces it to build all three programs from this directory.

       Here is a sample makefile fragment that illustrates some commonly used phony targets:

           PROGRAMS    := combobulator discombobulator

           $(phony all): $(PROGRAMS)   # All is the first target, so it's the default.

           combobulator: $(COMBOBULATOR_OBJS)
               $(CXX) $(inputs) -o $(output)

           discombobulator: $(DISCOMBOBULATOR_OBJS)
               $(CXX) $(inputs) -o $(output)

           #
           # This target makes sure everything is compiled, and then puts the
           # programs into a place where everyone can access them.  We make the
           # directories if they don't exist yet.  We don't use &mkdir, because
           # &install keeps track of everything it does, so &uninstall can later
           # undo this.
           #
           prefix      := /usr/local

           $(phony install): all
               &install -d $(prefix)/bin $(prefix)/share/combobulate
               &install -m 0755 $(PROGRAMS) $(prefix)/bin
               &install -m 0644 $(wildcard *.xbm) $(prefix)/share/combobulate

           #
           # This target makes a source distribution for shipping out to someone.
           #
           VERSION := 3.14

           $(phony distribution):
               rm -rf combobulate-$(VERSION)   # Get rid of previous junk, if any.
               &mkdir combobulate-$(VERSION)
               &cp -l $(wildcard *.c *.h) Makefile README INSTALL combobulate-$(VERSION)
               tar cf - combobulate-$(VERSION) | gzip -9c > combobulate-$(VERSION).tar.gz
               rm -rf combobulate-$(VERSION)

           #
           # This target runs regression tests to make sure the program(s) are
           # doing what they are supposed to do.
           #
           $(phony test): $(PROGRAMS)
               noecho for testfile in *.test; do \
                  ./combobulate $$testfile | ./discombobulate - > junk_output; \
                 if cmp -s junk_output $$testfile; then \
                   echo passed $$testfile; \
                 else \
                   echo failed $$testfile; \
                 fi; \
               done
           #
           # If "noecho" is the first word of the action, the action itself is not
           # printed before it is executed.  In this case, printing the action
           # would merely clutter up the screen so it is very common to suppress
           # printing for such long commands.
           #
           # Note the use of the double dollar sign to pass a single dollar sign to
           # the shell.  Note also the backslashes at the end of a line to indicate
           # that a shell command continues to the next line.
           #

   Working with several directories
       If your program grows to a substantial size, or if it uses libraries that need to be built but should  be
       kept  separate,  it is quite likely that you have split up your sources into several directories.  One of
       the main motivations for writing makepp was to make dealing with several  directories  much  easier  than
       with  the standard make utility.  If you're familiar with the standard Unix make, you'll notice that with
       makepp, you don't have to mess around with ugly complexities like recursive invocations of make.

       With makepp, you simply put a separate makefile in each directory that builds the relevant files in  that
       directory.   When  a  makefile  refers  to  files whose build commands are in different makefiles, makepp
       automatically finds the appropriate build rules in the other makefiles.  All actions in each makefile are
       executed with the current directory set to be the directory containing the makefile, so each makefile can
       be written independently of all the others.  No makefile has to know anything about the other  makefiles;
       it does not even have to tell makepp to load the rules from those other makefiles.

       When  you've  written  your  makefiles,  cd  to  the  directory that contains your main program, and type
       "makepp" just like you usually would.  Makepp will load in the makefile from  that  directory.   It  will
       notice  that  this  makefile  refers  to  files  in  other  directories,  and it will examine those other
       directories to see if there is a makefile in them.  In this way, all relevant makefiles will be loaded.

       As a simple example, suppose your top level directory contains the following makefile (suggested name  is
       "RootMakeppfile", but "Makeppfile" will also work):

           # Top level makefile:

           CXX := c++
           CXXFLAGS := -O2
           my_program: main.o goodies/libgoodies.so
               $(CXX) $(inputs) -o $(output)

           %.o: %.cxx
               $(CXX) $(CXXFLAGS) -c $(input) -o $(output)

       You would need to write a makefile in the directory "goodies" which builds "libgoodies.so", like this:

           # goodies/Makefile

           CXX := c++
           CXXFLAGS := -O2

           MODULES = candy.o chips.o licorice.o cookies.o popcorn.o spinach.o

           libgoodies.so: $(MODULES)
               $(CXX) -shared $(inputs) -o $(output)
                               # Note that the command is written assuming that
                               # the current directory is the subdirectory
                               # "goodies", not the top level subdirectory.
                               # Makepp cds into this directory before executing
                               # any commands from this makefile.

           %.o: %.cxx
               $(CXX) $(CXXFLAGS) -fpic -c $(input) -o $(output)

       And that's all you need to do.

       Any  variables  which  you  specify  on  the  command line override the definition of the variable in all
       makefiles.  Thus, for example, if you type "makepp CXXFLAGS="-g"", all modules  will  be  recompiled  for
       debug because the definition of "CXXFLAGS" in both makefiles is overridden.

       The  directories  containing other sources need not be subdirectories of the top-level directory (as they
       are in this example).  They can be anywhere in the file system; makepp will automatically load a makefile
       from any directory that contains a file which is a dependency of some target it is trying to  build.   It
       will also load a makefile from any directory that is scanned by a wildcard.

       Automatic  loading works if files built by your makefile all reside in the same directory as the makefile
       itself.  If you write your makefile so that its rules produce files in a  different  directory  than  the
       makefile  itself,  then  you  might have to tell makepp where to look for the makefiles, since it doesn't
       have any way of guessing.  You can do this using the "load_makefile" statement  in  your  makefile.   For
       more  information  about  this and other issues related to multi-directory builds, see "Tips for multiple
       directories" in makepp_cookbook.

       One caveat: if you reference the variable "$(MAKE)" in your  makefile,  makepp  automatically  goes  into
       backward compatibility mode and turns off automatic loading.

       Template or boilerplate files

       Makepp  has several other features which make life slightly easier for programmers who have to maintain a
       program spanning several directories.  In the above examples, you'll notice that the definitions  of  the
       variables "CXX" and "CXXFLAGS" have to be repeated in each makefile.  It can be a nuisance to reenter the
       same  information into every makefile, and it could be a problem if you ever decide to change it--you may
       have to modify dozens of different makefiles.

       What you can do instead is to put all of the information that's common to each makefile into  a  separate
       file,  located  perhaps  at  the top of the directory tree.  Common information usually includes variable
       definitions, and sometimes also pattern rules.  (In the above example, however, the pattern rules are not
       the same in both makefiles.)  Let's suppose  you've  called  this  file  "standard_defs.mk".   Then  each
       makefile simply needs to contain a statement like this:

           include standard_defs.mk

       When  makepp  sees  this  statement, it inserts the contents of the file into the makefile at that point.
       The "include" statement first looks for the file in the current directory, then  in  the  parent  of  the
       current  directory,  and  so  on  up  to  the top level of the file system, so you don't actually need to
       specify "../standard_defs.mk" or "../../../../standard_defs.mk".

       So we could rewrite the above makefiles to look like this.  "standard_defs.mk" would  exist  in  the  top
       level directory, and it might contain the following definitions:

           # standard_defs.mk
           CXX := c++
           CXXFLAGS := -O2

           #
           # We've also included a pattern rule that might be useful in one or more
           # subdirectories.  This pattern rule is for C compilation for putting
           # things into a shared library (that's what the -fpic option is for).
           #
           %.o: %.cxx
               $(CXX) $(CXXFLAGS) -fpic -c $(input) -o $(output)

       Note that since the included file is actually inserted into each makefile, rules in the included file are
       applied  with  the default directory set to the directory containing the makefile that included the file,
       not the directory containing the include file.

       The top level "Makefile" might look like this:

           # Top level makefile
           include standard_defs.mk

           my_program: main.o goodies/libgoodies.so
               $(CXX) $(inputs) -o $(output)

           #
           # Note that this pattern rule overrides the one found in standard_defs.mk,
           # because makepp sees it later.  This pattern rule is for compilation for
           # a module that doesn't belong in a shared library.
           #
           %.o: %.cxx
               $(CXX) $(CXXFLAGS) $(input) -o $(output)

       And the subdirectory's makefile might look like this:

           # goodies/Makefile
           include standard_defs.mk

           MODULES = candy.o chips.o licorice.o cookies.o popcorn.o spinach.o

           libgoodies.so: $(MODULES)
               $(CXX) -shared $(inputs) -o $(output)

           # We don't need the pattern rule for compilation of .cxx to .o files,
           # because it's contained in standard_defs.mk.

       The -F compilation option

       If you run makepp from within an editor such as emacs, and you are editing sources from several different
       directories, you may find that the default directory for makepp differs depending on which file you  were
       most recently editing.  As a result, makepp may not load the correct makefile.

       What  you  can  do  to  ensure that makepp always loads the correct makefile(s), no matter what directory
       happens to be your current directory, is to use the "-F" command line option, like this:

           makepp -F ~/src/my_program

       Makepp will first cd to the directory "~/src/my_program" before it attempts to load a makefile.

   Using Wildcards
       Up until this point, we've had to explicitly list all of the modules that go into a program or a library.
       The previous makefile, for example, contained this line:

           MODULES = candy.o chips.o licorice.o cookies.o popcorn.o spinach.o

           libgoodies.so: $(MODULES)
               $(CXX) -shared $(inputs) -o $(output)

       In this case, listing all of the modules that go into "libgoodies.so" is not such a big deal since  there
       aren't  very  many  of  them.   But  sometimes it can be a real nuisance to list all of the object files,
       especially if this list is changing rapidly during development.  Frequently, you want every single module
       in the whole directory to be compiled into your program or library.  It would be  a  lot  easier  if  you
       could just tell makepp to do that without listing them all.

       Well, you can.  The above lines could be rewritten as:

           libgoodies.so: *.o
               $(CXX) -shared $(inputs) -o $(output)

       The  "*.o"  wildcard matches any existing ".o" files, or any ".o" files which do not yet exist but can be
       made by any of the rules that makepp knows about from any makefiles that it has read.   So  the  wildcard
       will  return  the same list of files, no matter whether you haven't compiled anything yet, or whether all
       the modules have been compiled before.

       Of course, if you contaminate your directories with extra files that shouldn't be compiled directly  into
       your  library,  (e.g.,  if you write little test programs and leave them in same directory as the library
       source files), then these modules will be incorrectly included into your library.  If you choose  to  use
       wildcards, it's up to you to keep the directory clean enough.

       Makepp supports the usual Unix wildcards and one additional one:

       " *"
           Matches any string of 0 or more characters.  It will not match the "/" character.  For example, "a*c"
           matches "ac", "abc", and "aaaaabc", but not "aa/bc".

       "?" Matches  exactly  one character (not including "/").  For example, "???.o" matches all filenames that
           have 3 characters before the ".o" extension.

       [chars]
           Matches any of a list of characters at that position.  For example, "[abc].o" matches  "a.o",  "b.o",
           "c.o", but not "abc.o" or "d.o".  You can also specify a range of characters, e.g., "data_[0-9]" will
           match "data_0", "data_1", etc.

       " **"
           This  is a special wildcard, found only in makepp (and the zsh shell, from which I stole the idea, as
           bash has done since).  It matches any number  of  intervening  directories.   For  example,  "**/*.o"
           matches "xyz.o", "test_programs/abc.o", and "a/deeply/nested/subdirectory/def.o".

           If  your sources are contained in several subdirectories, and you want to link all the object modules
           together, you could write it like this:

               liboodles.so: **/*.o
                   $(CXX) -shared $(inputs) -o $(output)

   Functions and Advanced Variable Usage
       Makepp has a number of extremely powerful ways of manipulating text. This tutorial shows  a  few  of  the
       more  useful  ways, but you might want to glance through makepp_variables and makepp_functions for a more
       complete list.

       Lists of corresponding files

       A common problem in makefiles is the maintenance of two lists of files which  correspond.   Consider  the
       following two variables:

           SOURCES := a.cpp bc.cpp def.cpp
           OBJS := a.o bc.o def.o

       We might want to have a list of sources if the makefile can build source distributions, and we might need
       a  list  of  objects  for  the  link command.  It's tedious to change both lines whenever a new module is
       added, and it's not unlikely that a programmer will change one line and forget to change the other.  Here
       we will show four different ways to avoid the duplication.

       The patsubst function
           The first is to use makepp's functions to convert one list into another.  A function invocation looks
           a little like a variable, except that a function can take arguments:

               $(function arg1 arg2 arg3 ...)

           Makepp supplies many powerful functions, but probably the most  useful  of  them  is  the  "patsubst"
           function.  You could write the above lines like this:

               SOURCES = a.cpp bc.cpp def.cpp
               OBJS = $(patsubst %.cpp, %.o, $(SOURCES))

           The  "patsubst"  function  applies  a pattern to every word in a list of words, and performs a simple
           textual substitution.  Any words in the list that match the pattern in the  first  argument  are  put
           into  the  output  after  making the substitution indicated by the second argument.  The "%" wildcard
           matches any string of 0 or more characters.  In this example, the pattern "%.cpp" is applied to every
           word in "$(SOURCES)".  The first word, "a.cpp" matches the pattern, and the "%" wildcard matches  the
           string  "a".   The  "%" in the second argument is then replaced by "a", and the result is "a.o".  For
           the second argument, "%" matches "bc", so the result is "bc.o".

           Makepp's functions can strip directory names, remove extensions, filter out  matching  words,  return
           the  output  from  shell commands, and other useful tricks.  In addition, you can also write your own
           functions in perl that can be called from other parts of  the  makefile.   See  makepp_extending  for
           details.

       Substitution references
           Since  the  "patsubst"  function  is  so  common,  there  is  an  abbreviated  syntax for it called a
           substitution reference.  We could have written the above lines like this:

               SOURCES = a.cpp bc.cpp def.cpp
               OBJS = $(SOURCES:%.cpp=%.o)
               OBJS = $(SOURCES:.cpp=.o)   # Shorthand, when both begin with %.

       rc-style substitution
           Sometimes invocations of "patsubst"  or  the  equivalent  substitution  references  can  be  somewhat
           cryptic.   Makepp  provides  another option which is sometimes more convenient: rc-style substitution
           (so called because it was pioneered by the rc shell).

               MODULES := a bc def
               SOURCES := $(MODULES).cpp
               OBJS := $(MODULES).o

           What happened here is that when it evaluated "$(MODULES).cpp", makepp appended ".cpp" to  every  word
           in  "$(MODULES)",  and  similarly  for  "$(MODULES).o".   In  general,  any  characters preceding the
           "$(variable)" (up to a word delimiter)  are  placed  before  each  word  in  "$(variable)",  and  any
           characters  following  "$(variable)" are placed after each word in "$(variable)".  Thus the result of
           evaluating "x$(MODULES)y" would be "xay xbcy xdefy".

       Inline Perl code
           If you know Perl, you can insert Perl code to perform arbitrary manipulations on variables into  your
           makefile.  This is best illustrated by an example:

               SOURCES := a.cpp bc.cpp def.cpp
               perl_begin
               ($OBJS = $SOURCES) =~ s/\.cpp/.o/g;
               perl_end

           Any  text  between  the "perl_begin" statement and the "perl_end" statement is passed off to the perl
           interpreter.  You can also use an equivalent more perlish syntax of "perl { ... }".  All variables in
           the makefile (except automatic variables) are accessible as Perl scalars.  Any variables you set with
           Perl code will be accessible in the makefile.

           So what the above example does is to copy the text from  $SOURCES  to  $OBJS,  then  substitute  each
           occurrence of ".cpp" with ".o".

           In  this  example,  using inline Perl code is probably unnecessary since there are easier and clearer
           ways of doing the same manipulation.  But the full power of the perl interpreter is available if  you
           need it.

       Source/Object Separation and Variant Builds

       Up  to  this  point  all  of the makefiles we have seen put the object files in the same directory as the
       source files.  This is usually the way makefiles are written, and it's certainly the simplest way  to  do
       things.  However, suppose you have to compile your program on both a Linux machine and a Solaris machine.
       The  binaries  from the two machines are incompatible, of course.  Unlike the traditional make, makepp is
       smart enough to know that if the last compilation was  on  Linux,  and  the  current  compilation  is  on
       Solaris, a recompilation of everything is necessary.

       But  this  still leaves a problem: when you recompile on Solaris, you wipe out your Linux binaries.  Then
       when you switch back to Linux, you have to recompile everything again, even though the source files  that
       haven't changed.

       A  related problem is if you build your program with several different options.  Suppose for example that
       you usually compile your program with optimization:

           CFLAGS      := -O2

           %.o: %.c
               $(CC) $(CFLAGS) -c $(input) -o $(output)

           my_program: *.o
               $(CC) $(inputs) -o $(output)

       However, you discover a bug, and you want to enable debugging on all files, so you do change "CFLAGS":

           CFLAGS      := -g -DMALLOC_DEBUG

       Makepp realizes that the build commands have changed, and it needs to recompile everything.   But  again,
       recompiling  with debugging enabled wipes out your old binaries, so if you want to turn optimization back
       on, everything must be recompiled again, even the files that haven't changed.

       The obvious solution to these problems is to put the  architecture-dependent  or  build-variant-dependent
       files  in  a separate subdirectory.  There are two basic techniques for doing this: explicitly specifying
       an alternate directory, or using repositories.

       Explicit specifications of alternate directories

       You could rewrite the rules in your makefile to dump the objects into a different directory, like this:

           ARCH        ;= $(shell uname -s)-$(shell uname -m)-$(shell uname -r)
                               # ARCH becomes the output from the uname commands.
                               # Sometimes people use only $(shell uname -m), but
                               # this will be the same for FreeBSD and Linux on an
                               # x86.  The -r is not really useful on Linux, but is
                               # important for other OSes: binaries for SunOS 5.8
                               # typically won't run on SunOS 5.7.  Note the ;= which
                               # means to evaluate this at most once, when first
                               # needed.
           CFLAGS      := -O2
           OBJDIR      ;= $(ARCH)-optim

           $(OBJDIR)/%.o: %.c
               $(CC) $(CFLAGS) -c $(input) -o $(output)

           $(OBJDIR)/my_program: $(OBJDIR)/*.o
               $(CC) $(inputs) -o $(output)

       Now when you run makepp, "ARCH" is automatically set to something different for  each  architecture,  and
       all  of  the  objects  are placed in a different directory for each architecture, so they don't overwrite
       each other.  If you want to recompile turning on debugging, then you would have to change  both  "CFLAGS"
       and "OBJDIR".

       One  problem with this approach is that implicit loading will no longer work.  The only place that makepp
       knows to look for a makefile when it needs to build something is in the directory of the file it's trying
       to build.  If this is a problem for you, then you can explicitly tell makepp  where  to  look  using  the
       "load_makefile" statement.

       Repositories

       Repositories  are a magical way of using a makefile that is written to put objects in the same directory,
       but having makepp automatically put the objects in a different directory.   Suppose  we  start  with  the
       original  makefile  above  (before we modified it to put the objects in a different directory), and we've
       been working on Linux so our source directory is filled with Linux binaries.  When we want  to  recompile
       our code on Solaris instead of Linux, we use the following command instead of just typing "makepp":

           % mkdir solaris
           % cd solaris
           % makepp -R ..

       What  the "-R" option to makepp does in this case is to declare the directory ".." (which is the original
       source directory) as a repository.  A repository is just a way of getting makepp  to  trick  all  of  the
       actions into believing that all files in one directory tree are actually located in a different directory
       tree  in  the  file  system.   In  the above example, makepp pretends that all the files in ".." (and all
       subdirectories of "..") are actually in the current directory (and corresponding subdirectories).

       More precisely, a repository is a place where makepp looks if it needs to find a file that doesn't  exist
       in  the  current  directory  tree.   If  the file exists in the current directory tree, it is used; if it
       doesn't exist, but a file exists in the repository, makepp makes a temporary symbolic link from the  file
       in  the  repository  to the current directory.  (A symbolic link is an alias for the original file.  It's
       like a copy, except that trying to access the link  actually  accesses  the  original  file.)   The  rule
       actions  then  act  on  the  file  in  the  current  directory,  but  actually reference the files in the
       repository.

       In this example, initially we start off with a blank new directory  solaris.   (It  doesn't  have  to  be
       blank,  of course, and it won't be the second time you run makepp.)  Makepp is run in this directory, and
       it sees that there is no makefile there.  However, there is a makefile in the repository, so it links  in
       the  one  from  the  repository, and reads it.  The pattern rule in the makefile that converts ".c" files
       into ".o" files causes makepp to link all the ".c" files that it needs from the repository, and  run  the
       compilation  command  from  the  solaris  subdirectory.  Therefore the ".o" files are now placed into the
       solaris subdirectory, not in the top level directory.  When the build  command  is  finished,  any  files
       linked  from  the  repository are deleted, so the solaris subdirectory will contain only the binary files
       for Solaris.  Any ".o" files that exist in the repository are unmodified, so when you  go  back  to  your
       Linux machine and rerun makepp, most of your program will not have to be recompiled.

       Sometimes it might be more convenient to use a different form of the repository command.  The above three
       shell commands could be entirely replaced by the following one command:

           % makepp -R solaris=. -F solaris

       What  this  does  is  to  say  that  the files in the current directory are to be linked into the solaris
       subdirectory as necessary.  (The solaris subdirectory will  be  created  automatically  if  it  does  not
       exist.)  Then the "-F" option causes makepp to cd to the solaris directory and execute the makefile there
       (which will be linked from the repository).

       Using  a  repository  does  not  have  the  same  drawbacks as explicitly specifying an object directory;
       makefiles will be implicitly loaded as expected, since as  far  as  makepp  is  concerned,  the  makefile
       actually  is in the same directory as the target files.  However, if your build involves not just one but
       several directory trees, using repositories can become quite complicated.

       Repositories are just a way of pretending that things located  at  one  place  in  the  file  system  are
       actually  in a different place for the duration of the build.  This is a very powerful technique that can
       be  used  for  more  than  just  separating  your  sources  and  binaries.    For   more   details,   see
       makepp_repositories.

   Debugging Makefiles
       Log File

       If  you have a complicated build procedure, you find that makepp is rebuilding things more often than you
       think they need to be rebuilt.  Or you may find that it is not rebuilding things  when  it  should.   You
       don't  have to keep staring at your makefiles until you see the problem.  On every build, makepp produces
       a log file that explains which rule it thought it was supposed to use to build each target, what files it
       thought each target depended on, and (if it  did  decide  to  rebuild)  why  it  thought  a  rebuild  was
       necessary.  This binary file is viewed makepplog, mppl utility.

       The  output  format  is  more  or less self-explanatory.  Indentation conveys depth in makepp's inference
       tree.  Suppose the target is "all", and "all" depends on "my_program", and "my_program" depends on "*.o",
       which depend on the corresponding ".c" files.  Log messages related to "all" will not  be  indented,  log
       messages related to building the target "my_program" will be indented two spaces, log messages related to
       building  any  of the object files will be indented 4 spaces, and log messages related to building any of
       the source files will be indented 6 spaces.

       If you're doing a parallel make (using the "-j" command line option), the order of the  messages  in  the
       log  file  will not make nearly as much sense since messages from different targets will be interspersed.
       You might try debugging a serial make first.

       Common errors in makefiles

       Not specifying all dependencies
           Makepp is designed to be extremely clever about finding dependencies, and if you just use a  standard
           Unix  C  or  C++ compiler command, it is actually somewhat difficult to get makepp to miss something.
           (Please send me examples if you find that it  missed  something,  so  I  can  make  makepp  smarter.)
           However,  if  you are running commands other than compilation, or dealing with languages other than C
           or C++, it is much easier to run into problems.

           If you don't tell makepp all of the dependencies of a file, and it can't infer them  by  parsing  the
           command  line or scanning the files for includes, then it may not rebuild a file when it should.  You
           can make this kind of error less likely by using only automatic variables  in  your  actions,  rather
           than repeating the dependency lists.  For example,

               combined_file: a b c
                    do_something a b c d > combined_file

           has an error because d is mentioned in the action but not in the dependency list.  If the command had
           been written using automatic variables like this:

               combined_file: a b c d
                   do_something $(inputs) > combined_file

           then it would have been impossible to make this mistake.

           Another way that a missing dependency can occur is if a program actually uses a file but doesn't take
           the  file's  name  on the command line.  For example, if you're compiling Fortran code, makepp at the
           moment doesn't know how to scan for included files.  Thus you must explicitly list any files that are
           included.

           One thing that is sometimes helpful for testing is to start with a completely  clean  directory--just
           the  bare  minimum you think should be necessary--and rebuild absolute everything from scratch.  This
           can be most conveniently done by using repositories, like this:

               % rm -rf test-build-dir
               % makepp -R test-build-dir=. -F test-build-dir

           If the build fails because some file is not present, it means that makepp didn't  realize  some  file
           was  a  dependency,  because  it  only  links  files from the repository that it thought were needed.
           Performing this test occasionally may save hours of debugging later.  I have worked on projects where
           this was never done for months because recompilation took so long.  As a result, many little problems
           crept in.  There were some object files that didn't have source files any  more,  some  source  files
           that were never properly rebuilt by a preprocessing command, etc.

           Of course, this won't catch all missing dependencies, but it will catch some of them.

       Not specifying all targets
           You  must  specify  all  files  that a given command modifies as targets, or else makepp may not have
           realized they have changed.  You can specify more than one target.  For example,

               y.tab.h y.tab.c: parse.y
                   yacc -d parse.y

           If you had forgotten to specify y.tab.h as a target, then makepp would not know  to  rebuild  y.tab.h
           using  this  command, and files that depend on y.tab.h might not be recompiled after the yacc command
           is run.

       Please suggest things that you have found confusing or dangerous, and I'll either note them or try to fix
       makepp so they aren't a danger any more.

perl v5.32.0                                       2021-01-06                                 MAKEPP_TUTORIAL(1)