The following steps are executed when ppremake is invoked:

  1. Look in the current directory for a file called Package.pp.  If
     it is not found, but Sources.pp is present in the current
     directory, then look for Package.pp in the parent directory.
     Repeat until Package.pp is located; this is the root of the
     source directory hierarchy.

  2. Read and interpret the Package.pp file.  This should, in turn,
     read in (via the #include directive; see below) whatever other
     configuration scripts are relevant to the current package.  The
     exact behavior of Package.pp is not hardcoded into ppremake, but
     the Package.pp scripts for each of the Panda directories will
     ultimately include the following files in order:

       Any Package.pp for a dependent tree (e.g. $PANDA/Package.pp, etc.)
       $DTOOL/Package.pp
       $DTOOL/Config.pp
       $DTOOL/Config.Platform.pp
       The user's Config.pp file, as specified by $PPREMAKE_CONFIG.
       $DTOOL/pptempl/System.pp

     The various Package.pp files are designed to cascade down, such
     that when you invoke ppremake from a high-level tree, such as
     Direct (for instance), ppremake invokes $DIRECT/Package.pp, which
     in turn invokes $PANDA/Package.pp, which in turn invokes
     $DTOOL/Package.pp.  At the bottom level, $DTOOL/Package.pp does
     most of the remaining setup work, in particular including the
     various Config.pp files, and System.pp.

     The Config.pp files are intended to define the variables the user
     might customize for a particular build environment; the user's
     personal Config.pp file is included last, and may override any of
     the default values defined in the system Config.pp files.

     One particularly critical variable that is defined by Config.pp
     is $[BUILD_TYPE].  This represents the type of makefile rules
     that are to be generated, e.g. "unix" to generate standard
     Unix-style makefile syntax, or "msvc" to generate syntax specific
     to Microsoft's nmake.  The particular string specified here
     controls the selection of which Template files are to be loaded
     in a later step to actually generate output files in each source
     directory.

     The System.pp file is also essential and defines a few more
     variables that ppremake will be looking for during its execution.
     This tells ppremake the names of certain critical files that it
     needs to include.  The use of System.pp to define these
     filenames, instead of hardcoding them into ppremake, is
     consistent with the ppremake philosophy of letting as much of its
     behavior as possible be controlled from within the scripting
     language itself.
        
  3. Once Package.pp has been read, traverse the entire source
     hierarchy, and read each Sources.pp file.  A separate named scope
     is defined for each Sources.pp file; the name of the scope is
     taken from the name of the directory containing the file.

  4. Read in the global variable declarations.  This is loaded from a
     particular file specified by System.pp; this file defines global
     declarations that will be useful when processing each Template
     file later.  To save time, this file is read once and stored in a
     global scope, rather than re-read with each Template file.  The
     particular filename is specified by the ppremake variable
     $[GLOBAL_FILE], which was set by System.pp; normally this is
     $DTOOL/pptempl/Global.pp.

     $DTOOL/pptempl/Global.pp will, in turn, also read in the file
     $DTOOL/pptempl/Global.$[BUILD_TYPE].pp if it exists, which may
     specify further global declarations that are specific to the
     particular build type in effect.  Remember, $[BUILD_TYPE] was
     specified when Config.pp was read, and controls the particular
     type of makefiles or build scripts that are to be generated.

  5. Build the inter-directory dependency chain.  This is the chain of
     relationships between different directories within the source
     hierarchy.  Each directory may depend on code from zero or more
     other directories within the same package.  (That is, a directory
     may include header files defined within another directory, and/or
     require linking with a library built in that directory.)

     This inter-directory dependency is critical to determine things
     such as build order.  If one directory must be built before
     another directory, the makefiles must be constructed with this
     relationship explicit.

     This relationship is determined for each directory by executing
     the script named by $[DEPENDS_FILE] (this variable was set by
     System.pp) within each source directory scope.  Remember, each
     Sources.pp file has already been read in, and assigned its own
     unique named scope.  The DEPENDS_FILE script is responsible for
     defining the variable $[DEPEND_DIRS] to contain the
     space-separated list of directories (that is, named scopes) that
     the files in this current directory depend on.

     Normally, System.pp sets $[DEPENDS_FILE] to
     $DTOOL/pptempl/Depends.pp.  This script builds up $[DEPEND_DIRS]
     based on the $[get_depend_libs] function which is defined in
     Global.pp, which is in turn based on variables like $[LOCAL_LIBS]
     defined within the Sources.pp file.

     The inter-directory dependency information can be accessed from
     within ppremake scripts simply by examining the $[DEPEND_DIRS]
     variable within the appropriate scope.  In addition, the
     directory relationships are automatically used to sort the
     variables $[SUBDIR] and $[SUBTREE] in order, such that each
     directory is listed after the directories it depends on.

     There is one other variable which should be defined by the
     DEPENDS_FILE script: $[DEPENDABLE_HEADERS] should list the source
     files within the current directory that may be named as #include
     files in other files.  This is used to build file-level
     dependencies to support the $[dependencies] built-in function,
     and is not related to inter-directory dependencies.

  6. Finally, generate the actual output files in each source
     directory.  This is done by executing the script named by
     $[TEMPLATE_FILE] (set by System.pp) within each source directory
     scope.

     Normally, System.pp sets $[TEMPLATE_FILE] to
     $DTOOL/pptempl/Template.$[BUILD_TYPE].pp.  Recall again that
     $[BUILD_TYPE] was specified when Config.pp was read, and controls
     the particular type of makefiles or build scripts that are to be
     generated, so there is a different Template script file for each
     supported build type.

     The Template file is responsible for generating the actual
     makefile (or whatever build script is required) as appropriate to
     each source directory.  It now has all the information required:
     all source directories have been read, so the relationships
     between different source directories can control the output;
     also, the inter-directory and inter-file dependencies are
     available.

     Generally, a Template file will include at least one #output
     directive to generate the appropriate makefile for this
     directory.  Since there may be different kinds of source
     directories, each with very different makefile requirements, the
     Template files usually have one large #if condition based on the
     value of $[DIR_TYPE], which is set within each Sources.pp (and
     is set in System.pp to default to "src").
