makepp_tutorial man page on DragonFly

Man page or keyword search:  
man Server   44335 pages
apropos Keyword Search (all sections)
Output format
DragonFly logo
[printable version]

MAKEPP_TUTORIAL(1)		    Makepp		    MAKEPP_TUTORIAL(1)

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.20.3			  2012-02-07		    MAKEPP_TUTORIAL(1)
[top]

List of man pages available for DragonFly

Copyright (c) for man pages and the logo by the respective OS vendor.

For those who want to learn more, the polarhome community provides shell access and support.

[legal] [privacy] [GNU] [policy] [cookies] [netiquette] [sponsors] [FAQ]
Tweet
Polarhome, production since 1999.
Member of Polarhome portal.
Based on Fawad Halim's script.
....................................................................
Vote for polarhome
Free Shell Accounts :: the biggest list on the net