Browse Source

*** empty log message ***

David Rose 25 years ago
parent
commit
645751dca0

+ 0 - 40
ppremake/Depends.pp

@@ -1,40 +0,0 @@
-//
-// Depends.pp
-//
-// This file is loaded and run after each Sources.pp is read.  It
-// defines the inter-directory dependencies, which is useful for
-// determining build order.
-//
-
-#if $[or $[eq $[DIR_TYPE], src], $[eq $[DIR_TYPE], metalib]]
-#if $[eq $[DEPENDS],]
-  // Allow the user to define additional DEPENDS targets in each
-  // Sources.pp.
-  #define DEPENDS
-  #set DEPENDS $[EXTRA_DEPENDS]
-
-  #forscopes metalib_target static_lib_target lib_target noinst_lib_target bin_target noinst_bin_target
-    // We can optimize quite a bit by evaluating now several of the key
-    // deferred variables defined in Globals.pp.  This way they won't need
-    // to get repeatedly reevaluated as each directory examines each
-    // other.
-    #define build_directory $[build_directory]
-    #define build_target $[build_target]
-    #define active_local_libs $[active_local_libs]
-    #define active_component_libs $[active_component_libs]
-    #define active_libs $[active_libs]
-    #define get_sources $[get_sources]
-
-    // Report a warning for nonexisting dependencies.
-    #define nonexisting $[unmapped all_libs,$[LOCAL_LIBS]]
-    #if $[ne $[nonexisting],]
-Warning: Lib(s) $[nonexisting], referenced in $[DIRNAME]/$[TARGET], not found.
-    #endif
-
-    #set DEPENDS $[DEPENDS] $[all_libs $[DIRNAME],$[LOCAL_LIBS] $[COMPONENT_LIBS]] $[LOCAL_INCS]
-  #end metalib_target static_lib_target lib_target noinst_lib_target bin_target noinst_bin_target
-
-  #set DEPENDS $[sort $[DEPENDS]]
-#endif
-
-#endif // DIR_TYPE

+ 0 - 167
ppremake/Global.stopgap.pp

@@ -1,167 +0,0 @@
-//
-// Global.stopgap.pp
-//
-// This file is read in before any of the individual Sources.pp files
-// are read.  It defines a few global variables to assist
-// Template.stopgap.pp.
-//
-
-// This subroutine fills sources, alt_cflags, alt_ipath, alt_lpath,
-// alt_libs, and alt_ld as appropriate for the current target.
-#define sources
-#define alt_cflags
-#define alt_ipath
-#define alt_lpath
-#define alt_libs
-#define alt_ld
-#defsub get_sources
-  #set sources $[get_sources]
-  #set alt_cflags $[get_cflags]
-  #set alt_ipath $[get_ipath]
-  #set alt_lpath $[get_lpath]
-  #set alt_libs $[get_libs]
-  #set alt_ld $[get_ld]
-#end get_sources
-
-// This subroutine will set when_defer, when_no_defer, and when_either
-// correctly to the set of libs we should link with for the current
-// target.
-#define when_defer
-#define when_no_defer
-#define when_either
-#defsub get_libs
-  // For the WHEN_DEFER case, we need to know the complete set of
-  // metalibs that encapsulates each of our LOCAL_LIBS.  In the case
-  // where a particular library is not part of a metalib, we include the
-  // library itself.
-  
-  #set when_defer
-  #foreach lib $[LOCAL_LIBS]
-    // Only consider libraries that we're actually building.
-    #if $[all_libs $[build_directory],$[lib]]
-      #define modmeta $[module $[TARGET],$[lib]]
-      #if $[ne $[modmeta],]
-        #set when_defer $[when_defer] $[modmeta]
-      #else
-        #set when_defer $[when_defer] $[lib]
-      #endif
-    #endif
-  #end lib
-  #set when_defer $[unique $[when_defer]] $[patsubst %:m,%,$[filter %:m,$[OTHER_LIBS]]]
-  
-  // Also filter out the libraries we don't want from when_no_defer, although
-  // we don't need to translate these to metalibs.
-  #set when_no_defer
-  #foreach lib $[COMPONENT_LIBS] $[LOCAL_LIBS]
-    #if $[all_libs $[build_directory],$[lib]]
-      #set when_no_defer $[when_no_defer] $[lib]
-    #endif
-  #end lib
-  #set when_no_defer $[unique $[when_no_defer]] $[patsubst %:c,%,$[filter %:c,$[OTHER_LIBS]]]
-  
-  // Finally, get the set of libraries that we want in either case.  At
-  // the moment, this is just the set of libraries in OTHER_LIBS that's
-  // not flagged with either a :c or a :m.
-  #set when_either $[filter-out %:m %:c,$[OTHER_LIBS]]
-#end get_libs
-
-
-// This subroutine converts depend_libs from a list of plain library names
-// to a list of the form libname.so or libname.a, according to whether the
-// named libraries are static or dynamic.
-#defsub convert_depend_libs
-  #define new_depend_libs
-  #foreach lib $[depend_libs]
-    // Make sure the library is something we're actually building.
-    #if $[all_libs $[build_directory],$[lib]]
-      #define libname $[static_libs lib$[TARGET].a,$[lib]] $[dynamic_libs lib$[TARGET].so,$[lib]]
-      #if $[eq $[libname],]
-  Warning: No such library $[lib], dependency of $[DIRNAME].
-      #else
-        #set new_depend_libs $[new_depend_libs] $[libname]
-      #endif
-    #endif
-  #end lib
-  #set depend_libs $[sort $[new_depend_libs]]
-#end convert_depend_libs
-
-
-// This subroutine determines the set of libraries our various targets
-// depend on.  This is a complicated definition.  It is the union of
-// all of our targets' dependencies, except:
-
-// If a target is part of a metalib, it depends (a) directly on all of
-// its normal library dependencies that are part of the same metalib,
-// and (b) indirectly on all of the metalibs that every other library
-// dependency is part of.  If a target is not part of a metalib, it is
-// the same as case (b) above.
-#define depend_libs
-#defsub get_depend_libs
-  #set depend_libs
-  #forscopes lib_target noinst_lib_target
-    #define metalib $[module $[TARGET],$[TARGET]]
-    #if $[ne $[metalib],]
-      // This library is included on a metalib.
-      #foreach depend $[LOCAL_LIBS]
-        #define depend_metalib $[module $[TARGET],$[depend]]
-        #if $[eq $[depend_metalib],$[metalib]]
-          // Here's a dependent library in the *same* metalib.
-          #set depend_libs $[depend_libs] $[depend]
-        #elif $[ne $[depend_metalib],]
-          // This dependent library is in a *different* metalib.
-          #set depend_libs $[depend_libs] $[depend_metalib]
-        #else
-          // This dependent library is not in any metalib.
-          #set depend_libs $[depend_libs] $[depend]
-        #endif
-      #end depend
-    #else
-      // This library is *not* included on a metalib.
-      #foreach depend $[LOCAL_LIBS]
-        #define depend_metalib $[module $[TARGET],$[depend]]
-        #if $[ne $[depend_metalib],]
-          // This dependent library is on a metalib.
-          #set depend_libs $[depend_libs] $[depend_metalib]
-        #else
-          // This dependent library is not in any metalib.
-          #set depend_libs $[depend_libs] $[depend]
-        #endif
-      #end depend
-    #endif
-  #end lib_target noinst_lib_target
-  
-  // These will never be part of a metalib.
-  #forscopes static_lib_target bin_target noinst_bin_target metalib_target
-    #foreach depend $[LOCAL_LIBS]
-      #define depend_metalib $[module $[TARGET],$[depend]]
-      #if $[ne $[depend_metalib],]
-        // This dependent library is on a metalib.
-        #set depend_libs $[depend_libs] $[depend_metalib]
-      #else
-        // This dependent library is not in any metalib.
-        #set depend_libs $[depend_libs] $[depend]
-      #endif
-    #end depend
-  #end static_lib_target bin_target noinst_bin_target metalib_target
-
-  // In case we're defining any metalibs, these depend directly on
-  // their components as well.
-  #set depend_libs $[depend_libs] $[COMPONENT_LIBS(metalib_target)]
-
-  // Now correct all the libraries listed in depend_libs to refer to a
-  // real library name.
-  #define new_depend_libs
-  #foreach lib $[sort $[depend_libs]]
-    // Make sure the library is something we're actually building.
-    #if $[all_libs $[build_directory],$[lib]]
-      #define libname $[static_libs lib$[TARGET].a,$[lib]] $[dynamic_libs lib$[TARGET].so,$[lib]]
-      #if $[eq $[libname],]
-  Warning: No such library $[lib], dependency of $[DIRNAME].
-      #else
-        #set new_depend_libs $[new_depend_libs] $[libname]
-      #endif
-    #endif
-  #end lib
-  #set depend_libs $[sort $[new_depend_libs]]
-
-#end get_depend_libs

+ 6 - 13
ppremake/Makefile.am

@@ -1,22 +1,15 @@
 bin_PROGRAMS = ppremake
 bin_PROGRAMS = ppremake
 
 
 ppremake_SOURCES =						\
 ppremake_SOURCES =						\
-    find_searchpath.cxx find_searchpath.h			\
+    check_include.cxx check_include.h find_searchpath.cxx	\
+    find_searchpath.h						\
     gnu_getopt.c gnu_getopt.h					\
     gnu_getopt.c gnu_getopt.h					\
-    ppCommandFile.cxx ppCommandFile.h ppDirectoryTree.cxx	\
-    ppDirectoryTree.h ppMain.cxx ppMain.h			\
+    ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx	\
+    ppDependableFile.h ppDirectory.cxx				\
+    ppDirectory.h ppDirectoryTree.cxx ppDirectoryTree.h		\
+    ppMain.cxx ppMain.h						\
     ppFilenamePattern.cxx					\
     ppFilenamePattern.cxx					\
     ppFilenamePattern.h ppNamedScopes.cxx ppNamedScopes.h	\
     ppFilenamePattern.h ppNamedScopes.cxx ppNamedScopes.h	\
     ppScope.cxx ppScope.h ppSubroutine.cxx ppSubroutine.h	\
     ppScope.cxx ppScope.h ppSubroutine.cxx ppSubroutine.h	\
     ppremake.cxx ppremake.h tokenize.cxx			\
     ppremake.cxx ppremake.h tokenize.cxx			\
     tokenize.h
     tokenize.h
-
-data_DATA = \
-    System.pp Depends.pp Global.pp Template.autoconf.pp \
-    Global.stopgap.pp Template.stopgap.pp \
-    Global.unix.pp Template.unix.pp
-
-EXTRA_DIST =\
-    System.pp Depends.pp Global.pp Template.autoconf.pp \
-    Global.stopgap.pp Template.stopgap.pp \
-    Global.unix.pp Template.unix.pp

+ 0 - 33
ppremake/System.pp

@@ -1,33 +0,0 @@
-//
-// System.pp
-//
-// This is a system-wide configure file for ppremake.  It's normally
-// #included from a package-specific Config.pp in the root of the
-// source tree.  It makes variable declarations that are not normally
-// user-editable, but are required to set up the normal processing of
-// ppremake.
-//
-
-
-// Define DIR_TYPE as "src", since that's the most common kind of source
-// file.
-#if $[eq $[DIR_TYPE],]
-  #define DIR_TYPE src
-#endif
-
-// Define where to look for the various kinds of system files.
-#if $[eq $[DEPENDS_FILE],]
-  #define DEPENDS_FILE $[PPREMAKE_DIR]/Depends.pp
-#endif
-
-#if $[eq $[GLOBAL_FILE],]
-  #define GLOBAL_FILE $[PPREMAKE_DIR]/Global.pp
-#endif
-
-#if $[eq $[GLOBAL_TYPE_FILE],]
-  #define GLOBAL_TYPE_FILE $[PPREMAKE_DIR]/Global.$[BUILD_TYPE].pp
-#endif
-
-#if $[eq $[TEMPLATE_FILE],]
-  #define TEMPLATE_FILE $[PPREMAKE_DIR]/Template.$[BUILD_TYPE].pp
-#endif

+ 0 - 922
ppremake/Template.autoconf.pp

@@ -1,922 +0,0 @@
-//
-// Template.autoconf.pp
-//
-// This file defines the set of output files that will be generated to
-// support an autoconf/automake style build.  This works particularly
-// well when gcc/g++ will be used to compile, for instance on Linux.
-//
-
-#defer get_sys_libs $[subst -ldl,@libdl@,$[patsubst %,-l%,$[UNIX_SYS_LIBS]]]
-
-// First, check to see if the entire directory has been switched out.
-#define omit
-#if $[DIRECTORY_IF_GL]
-  #set omit $[not $[HAVE_GL]]
-#endif
-#if $[DIRECTORY_IF_GLX]
-  #set omit $[not $[HAVE_GLX]]
-#endif
-#if $[DIRECTORY_IF_WGL]
-  #set omit $[not $[HAVE_WGL]]
-#endif
-#if $[DIRECTORY_IF_GLUT]
-  #set omit $[not $[HAVE_GLUT]]
-#endif
-#if $[DIRECTORY_IF_SGIGL]
-  #set omit $[not $[HAVE_SGIGL]]
-#endif
-#if $[DIRECTORY_IF_DX]
-  #set omit $[not $[HAVE_DX]]
-#endif
-#if $[DIRECTORY_IF_PS2]
-  #set omit $[not $[HAVE_PS2]]
-#endif
-#if $[DIRECTORY_IF_RIB]
-  #set omit $[not $[HAVE_RIB]]
-#endif
-#if $[DIRECTORY_IF_VRPN]
-  #set omit $[not $[HAVE_VRPN]]
-#endif
-
-//////////////////////////////////////////////////////////////////////
-#if $[eq $[DIR_TYPE], src]
-//////////////////////////////////////////////////////////////////////
-
-// For a source directory, build a Makefile.am with a number of targets.
-
-#output Makefile.am
-#format makefile
-# Makefile.am generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-
-#if $[omit]
-  // If we're omitting the directory, everything becomes an extra_dist.  
-EXTRA_DIST = Sources.pp $[EXTRA_DIST] $[SOURCES(static_lib_target noinst_lib_target lib_target noinst_bin_target bin_target test_bin_target)]
-
-#else   // $[omit]
-
-// We define a map variable that allows us to look up all the libs in
-// the various directories by target name.  With this map variable, we
-// can translate the list of local_libs (which is simply a list of
-// library names, with no directory information), into a list of
-// relative filenames to each library.
-#map local_libs TARGET(*/static_lib_target */lib_target */noinst_lib_target)
-
-#define all_include_dirs
-
-lib_LTLIBRARIES = $[TARGET(static_lib_target lib_target):%=lib%.la]
-noinst_LTLIBRARIES = $[TARGET(noinst_lib_target):%=lib%.la]
-bin_PROGRAMS = $[TARGET(bin_target)]
-noinst_PROGRAMS = $[TARGET(noinst_bin_target)]
-EXTRA_PROGRAMS = $[TARGET(test_bin_target)]
-
-#if $[ne $[YACC_PREFIX],]
-YFLAGS = -d --name-prefix=$[YACC_PREFIX] $[YFLAGS]
-LFLAGS = -P$[YACC_PREFIX] -olex.yy.c $[LFLAGS]
-#else
-YFLAGS = -d $[YFLAGS]
-LFLAGS = $[LFLAGS]
-#endif
-
-#define alt_cflags @nspr_cflags@ @python_cflags@
-#define alt_lflags @nspr_lflags@
-#define alt_libs @nspr_libs@
-#if $[ne $[USE_ZLIB],]
-  #define alt_cflags $[alt_cflags] @zlib_cflags@
-  #define alt_lflags $[alt_lflags] @zlib_lflags@
-  #define alt_libs $[alt_libs] @zlib_libs@
-#endif
-#if $[ne $[USE_GL],]
-  #define alt_cflags $[alt_cflags] @gl_cflags@ @glut_cflags@
-  #define alt_lflags $[alt_lflags] @gl_lflags@ @glut_lflags@
-  #define alt_libs $[alt_libs] @gl_libs@ @glut_libs@
-#endif
-
-#define built_sources
-#define install_data
-
-#forscopes static_lib_target lib_target noinst_lib_target
-
-// This map variable lets us identify which metalib, if any, is
-// including this particular library.
-#map module LOCAL_LIBS(*/metalib_target)
-
-// And this defines the complete set of libs we depend on: the
-// LOCAL_LIBS we listed as directly depending on, plus all of the
-// LOCAL_LIBS *those* libraries listed, and so on.
-#define complete_local_libs $[closure local_libs,$[LOCAL_LIBS]]
-
-
-#if $[ne $[IF_ZLIB_SOURCES],]
-if HAVE_ZLIB
-EXTRA_ZLIB = $[IF_ZLIB_SOURCES]
-else
-EXTRA_ZLIB =
-endif
-#define SOURCES $[SOURCES] $(EXTRA_ZLIB)
-#endif
-
-#define local_incs $[local_libs $[RELDIR],$[complete_local_libs]] $[RELDIR($[LOCAL_INCS:%=%/])]
-
-// Check for interrogate.
-#if $[eq $[IGATESCAN], all]
-  #define IGATESCAN $[filter-out %.I %.lxx %.yxx %.N,$[SOURCES]]
-#endif
-#if $[ne $[IGATESCAN],]
-  #define IGATEFILE $[TARGET].in.cxx
-  #define IGATEDBFILE lib$[TARGET].in
-
-  #define IGATELIBRARY lib$[TARGET]
-  #define IGATEMODULE lib$[module $[TARGET],$[TARGET]]
-  #if $[eq $[IGATEMODULE], lib]
-    #define IGATEMODULE $[IGATELIBRARY]
-  #endif
-
-IGATESCAN = $[IGATESCAN]
-$[IGATEFILE] : $(IGATESCAN)
-	@dtool@/bin/interrogate $[IGATEFLAGS] @system_igate@ -DCPPPARSER -D__cplusplus -I@dtool@/include/parser-inc @trees_inc@ $[local_incs:%=-I%] $[alt_cflags] $[CDEFINES] -module "$[IGATEMODULE]" -library "$[IGATELIBRARY]" -oc $[IGATEFILE] -od $[IGATEDBFILE] -fnames -string -refcount -assert -promiscuous -python $(IGATESCAN)
-#set built_sources $[built_sources] $[IGATEFILE]
-#set install_data $[install_data] $[IGATEDBFILE]
-#endif
-
-  #define SOURCES $[SOURCES] $[IGATEFILE]
-
-lib$[TARGET]_la_SOURCES = $[SOURCES]
-lib$[TARGET]_la_LIBADD = $[OTHER_LIBS:%=-l%] $[alt_libs] $[get_sys_libs]
-
-#set all_include_dirs $[all_include_dirs] $[local_incs]
-  
-
-
-#end static_lib_target lib_target noinst_lib_target
-
-#forscopes bin_target noinst_bin_target test_bin_target
-#if $[ne $[IF_ZLIB_SOURCES],]
-if HAVE_ZLIB
-EXTRA_ZLIB = $[IF_ZLIB_SOURCES]
-else
-EXTRA_ZLIB =
-endif
-#define $[SOURCES] $(EXTRA_ZLIB)
-#endif
-
-// This defines the complete set of libs we depend on: the LOCAL_LIBS
-// we listed as directly depending on, plus all of the LOCAL_LIBS
-// *those* libraries listed, and so on.
-#define complete_local_libs $[closure local_libs,$[LOCAL_LIBS]]
-
-$[TARGET]_SOURCES = $[SOURCES]
-$[TARGET]_LDADD = $[local_libs $[RELDIR]/lib$[TARGET].la,$[complete_local_libs]] $[OTHER_LIBS:%=-l%] $[alt_libs] $[get_sys_libs]
-#set all_include_dirs $[all_include_dirs] $[local_libs $[RELDIR],$[complete_local_libs]]
-#set all_include_dirs $[all_include_dirs] $[RELDIR($[LOCAL_INCS:%=%/])]
-
-#end bin_target noinst_bin_target test_bin_target
-
-include_HEADERS = $[sort $[INSTALL_HEADERS(static_lib_target lib_target bin_target)] $[INSTALL_HEADERS]]
-#set install_data $[install_data] $[INSTALL_DATA]
-
-#if $[ne $[INSTALL_PARSER_INC],]
-parserincdir = @includedir@/parser-inc
-parserinc_HEADERS = $[INSTALL_PARSER_INC]
-#endif
-
-INCLUDES = $[patsubst %,-I%,$[sort $[all_include_dirs]]] @trees_inc@ $[alt_cflags]
-LDFLAGS = @ldflags@ @trees_lflags@ $[alt_lflags]
-EXTRA_DIST = Sources.pp $[EXTRA_DIST] $[INSTALL_LIBS] $[INSTALL_SCRIPTS]$[install_data]
-BUILT_SOURCES =$[built_sources]
-data_DATA =$[install_data]
-
-#endif   // $[omit]
-
-#end Makefile.am
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], metalib]
-//////////////////////////////////////////////////////////////////////
-
-// A metalib directory is similar to a regular source directory, 
-// but a little simpler.
-
-#output Makefile.am
-#format makefile
-# Makefile.am generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-
-#if $[omit]
-  // If we're omitting the directory, everything becomes an extra_dist.  
-EXTRA_DIST = Sources.pp $[EXTRA_DIST] $[SOURCES(static_lib_target noinst_lib_target lib_target noinst_bin_target bin_target test_bin_target)]
-
-#else   // $[omit]
-
-// We define a map variable that allows us to look up all the libs in
-// the various directories by target name.  With this map variable, we
-// can translate the list of local_libs (which is simply a list of
-// library names, with no directory information), into a list of
-// relative filenames to each library.
-#map local_libs TARGET(*/static_lib_target */lib_target */noinst_lib_target)
-
-#define all_include_dirs $(includedir)
-
-lib_LTLIBRARIES = $[TARGET(metalib_target):%=lib%.la]
-
-#define alt_cflags @nspr_cflags@ @python_cflags@
-#define alt_lflags @nspr_lflags@
-#define alt_libs @nspr_libs@
-#if $[ne $[USE_ZLIB],]
-  #define alt_cflags $[alt_cflags] @zlib_cflags@
-  #define alt_lflags $[alt_lflags] @zlib_lflags@
-  #define alt_libs $[alt_libs] @zlib_libs@
-#endif
-#if $[ne $[USE_GL],]
-  #define alt_cflags $[alt_cflags] @gl_cflags@
-  #define alt_lflags $[alt_lflags] @gl_lflags@
-  #define alt_libs $[alt_libs] @gl_libs@
-#endif
-
-#forscopes metalib_target
-#if $[ne $[IF_PYTHON_SOURCES],]
-if HAVE_PYTHON
-EXTRA_PYTHON = $[IF_PYTHON_SOURCES]
-else
-EXTRA_PYTHON =
-endif
-#define SOURCES $[SOURCES] $(EXTRA_PYTHON)
-#endif
-
-// This defines the complete set of libs we depend on: the LOCAL_LIBS
-// we listed as directly depending on, plus all of the LOCAL_LIBS
-// *those* libraries listed, and so on.
-#define complete_local_libs $[closure local_libs,$[LOCAL_LIBS]]
-
-lib$[TARGET]_la_SOURCES = $[SOURCES]
-lib$[TARGET]_la_LIBADD = $[complete_local_libs:%=-l%] $[get_sys_libs]
-
-#end metalib_target
-
-INCLUDES = $[patsubst %,-I%,$[sort $[all_include_dirs]]] @trees_inc@ $[alt_cflags]
-LDFLAGS = @ldflags@ -L$(libdir) -rpath $(libdir) @trees_lflags@ $[alt_lflags]
-EXTRA_DIST = Sources.pp $[EXTRA_DIST]
-
-#endif   // $[omit]
-
-#end Makefile.am
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], group]
-//////////////////////////////////////////////////////////////////////
-
-#output Makefile.am
-#format makefile
-# Makefile.am generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-
-SUBDIRS = $[SUBDIRS]
-
-EXTRA_DIST = Sources.pp $[EXTRA_DIST]
-
-#end Makefile.am
-
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], toplevel]
-//////////////////////////////////////////////////////////////////////
-
-#output Makefile.am
-#format makefile
-# Makefile.am generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-
-FIRSTBUILD_SUBDIRS = $[filter_out metalibs,$[SUBDIRS]]
-SUBDIRS = $[SUBDIRS]
-
-#define INSTALL_HEADERS $[INSTALL_HEADERS] $[CONFIG_HEADER]
-
-include_HEADERS = $[INSTALL_HEADERS]
-
-EXTRA_DIST = Sources.pp Config.pp $[EXTRA_DIST]
-
-
-# We define this custom rule for all-recursive as an ordering hack.
-# It's just like the default rule, except that it traverses through
-# only FIRSTBUILD_SUBDIRS, instead of all of SUBDIRS.  The idea is
-# that first we build everything in FIRSTBUILD_SUBDIRS, and then when
-# we're installing, we build everything in SUBDIRS as well.  This hack
-# is necessary to build targets in metalibs that link directly with
-# installed shared libraries.
-
-all-recursive:
-	@set fnord $(MAKEFLAGS); amf=$$2; \
-	dot_seen=no; \
-	target=`echo $@ | sed s/-recursive//`; \
-	list='$(FIRSTBUILD_SUBDIRS)'; for subdir in $$list; do \
-	  echo "Making $$target in $$subdir"; \
-	  if test "$$subdir" = "."; then \
-	    dot_seen=yes; \
-	    local_target="$$target-am"; \
-	  else \
-	    local_target="$$target"; \
-	  fi; \
-	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
-	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
-	done; \
-	if test "$$dot_seen" = "no"; then \
-	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
-	fi; test -z "$$fail"
-
-#end Makefile.am
-
-#output configure.in
-#format straight
-dnl configure.in generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-dnl Process this file with autoconf to produce a configure script.
-AC_INIT($[SAMPLE_SOURCE_FILE])
-AM_INIT_AUTOMAKE($[PACKAGE], $[VERSION])
-
-#if $[eq $[CONFIG_HEADER],]
-dnl This package doesn't care about a generated config.h file.  This causes
-dnl a few problems with automake, so we fake it out with this hack.
-AM_CONFIG_HEADER(ignore_config.h)
-#else
-AM_CONFIG_HEADER($[CONFIG_HEADER])
-#endif
-
-if test "${CTPROJS+set}" = set; then
-  if test "${$[upcase $[PACKAGE]]+set}" != set; then
-    echo ""
-    echo "The environment variable CTPROJS is currently set, indicating"
-    echo "you are attached to one or more trees, but you are not attached"
-    echo "to $[upcase $[PACKAGE]].  Either unattach from everything, or attach to $[upcase $[PACKAGE]]."
-    echo ""
-    exit 1
-  fi
-
-  # If we're currently attached, the install directory is the same as
-  # the source directory.
-  if test "$prefix" != "NONE"; then
-    echo ""
-    echo The environment variable CTPROJS is currently set, indicating
-    echo you are attached to one or more trees.  When you configure the
-    echo package while attached, you cannot specify a --prefix to install
-    echo the built sources--it always installs in the source directory.
-    echo ""
-    exit 1
-  fi
-
-  prefix=$$[upcase $[PACKAGE]]
-fi
-
-AC_PREFIX_DEFAULT($[INSTALL_DIR])
-AC_PROG_MAKE_SET
-AC_CANONICAL_HOST
-
-# If we have a CFLAGS variable but not a CXXFLAGS variable, let them
-# be the same.
-if test "${CXXFLAGS+set}" != set -a "${CFLAGS+set}" = set; then
-  CXXFLAGS=$CFLAGS
-fi
-
-# Save these variables for later, so we can easily append to them or
-# change them.
-user_ldflags=${LDFLAGS-}
-user_cflags=${CFLAGS-}
-user_cxxflags=${CXXFLAGS-}
-
-dnl Choose a suitable set of system-dependent interrogate flags.
-case "$host_os" in
-  irix*) system_igate="-D__mips__ -D__MIPSEB__";;
-  linux-gnu*) system_igate="-D__i386__";;
-esac
-AC_SUBST(system_igate)
-
-dnl Checks for programs.
-AC_PROG_CC
-AC_PROG_CXX
-AM_PROG_LEX
-AM_DISABLE_STATIC
-AM_PROG_LIBTOOL
-AC_PATH_XTRA
-
-dnl We require specifically Bison, not any other flavor of Yacc.
-AC_CHECK_PROGS(YACC, 'bison -y')
-
-
-AC_ARG_WITH(optimize,
-[  --with-optimize=LEVEL   Specify the optimization/debug symbol level (1-4, default 1)])
-
-if test "${with_optimize-no}" = "no"; then
-  with_optimize=$[OPTIMIZE]
-fi
-
-if test "$with_optimize" = "1"; then
-  # Optimize level 1: No optimizations, and full debug symbols.  
-  if test "${ac_cv_prog_gcc}" = "yes"; then
-    CFLAGS="$user_cflags -g -Wall"
-    CXXFLAGS="$user_cxxflags -g -Wall"
-  else
-    CFLAGS="$user_cflags -g"
-    CXXFLAGS="$user_cxxflags -g"
-  fi
-
-elif test "$with_optimize" = "2"; then
-  # Optimize level 2: Compiler optimizations, and full debug if supported.
-  if test "${ac_cv_prog_gcc}" = "yes"; then
-    CFLAGS="$user_cflags -O2 -g -Wall"
-    CXXFLAGS="$user_cxxflags -O2 -g -Wall"
-  else
-    CFLAGS="$user_cflags -O"
-    CXXFLAGS="$user_cxxflags -O"
-  fi
-
-elif test "$with_optimize" = "3"; then
-  # Optimize level 3: Compiler optimizations, without debug symbols.
-  if test "${ac_cv_prog_gcc}" = "yes"; then
-    CFLAGS="$user_cflags -O2 -Wall"
-    CXXFLAGS="$user_cxxflags -O2 -Wall"
-  else
-    CFLAGS="$user_cflags -O"
-    CXXFLAGS="$user_cxxflags -O"
-  fi
-
-elif test "$with_optimize" = "4"; then
-  # Optimize level 4: As above, with asserts removed.
-  if test "${ac_cv_prog_gcc}" = "yes"; then
-    CFLAGS="$user_cflags -O2 -Wall -DNDEBUG"
-    CXXFLAGS="$user_cxxflags -O2 -Wall -DNDEBUG"
-  else
-    CFLAGS="$user_cflags -O -DNDEBUG"
-    CXXFLAGS="$user_cxxflags -O -DNDEBUG"
-  fi
-
-else
-  echo "Invalid optimize level: $with_optimize"
-  exit 0
-fi
-
-trees_inc=
-trees_lflags=
-
-#foreach require $[REQUIRED_TREES]
-
-AC_ARG_WITH($[require],
-[  --with-$[require]=DIR        Prefix where $[upcase $[require]] is installed (same as --prefix)])
-
-
-if test "${CTPROJS+set}" = set; then
-  if test "$with_$[require]" != ""; then
-    echo ""
-    echo "The environment variable CTPROJS is currently set, indicating"
-    echo "you are attached to one or more trees.  When you configure the"
-    echo "package while attached, you cannot specify a directory to search"
-    echo "for --$[require]; it will always search in the currently attached $[upcase $[require]]."
-    echo ""
-    exit 1
-  fi
-  if test "${$[upcase $[require]]+set}" != set; then
-    echo ""
-    echo "The environment variable CTPROJS is currently set, indicating"
-    echo "you are attached to one or more trees, but you are not attached"
-    echo "to $[upcase $[require]].  Either unattach from everything, or attach to $[upcase $[require]]."
-    echo ""
-    exit 1
-  fi
-
-  $[require]='${$[upcase $[require]]}'
-else
-  # No attachments--respect the --with-$[require] parameter.
-
-  if test "$with_$[require]" != "" -a "$with_$[require]" != "no" -a "$with_$[require]" != "yes"; then
-    $[require]=$with_$[require]
-  else
-    $[require]='${prefix}'
-  fi
-  trees_inc="$trees_inc -I"'$($[require])/include'
-  trees_lflags="$trees_lflags -L"'$($[require])/lib'
-fi
-
-AC_SUBST($[require])
-
-#end require
-AC_SUBST(trees_inc)
-AC_SUBST(trees_lflags)
-
-dnl First, we'll test for C-specific features.
-AC_LANG_C
-
-dnl Checks for libraries.
-libdl=
-libm=
-AC_CHECK_LIB(dl, dlopen, libdl=-ldl)
-AC_CHECK_LIB(m, sin, libm=-lm)
-AC_SUBST(libdl)
-AC_SUBST(libm)
-
-// Only bother to make the following tests if we're actually building
-// a config.h.
-#if $[ne $[CONFIG_HEADER],]
-dnl Checks for header files.
-AC_HEADER_STDC
-AC_CHECK_HEADERS(malloc.h alloca.h unistd.h io.h minmax.h sys/types.h)
-
-dnl Checks for typedefs, structures, and compiler characteristics.
-AC_C_BIGENDIAN
-AC_GETTIMEOFDAY
-
-dnl Checks for library functions.
-AC_CHECK_FUNCS(getopt getopt_long_only)
-
-
-dnl Now we can test some C++-specific features.
-AC_LANG_CPLUSPLUS
-AC_HEADER_IOSTREAM
-AC_CHECK_HEADERS(sstream)
-AC_NAMESPACE
-AC_IOS_BINARY
-#endif
-
-AC_LANG_C
-
-AC_ARG_WITH(python,
-[  --with-python=DIR       Prefix where Python is installed (usually /usr/local)])
-
-have_python=no
-include_python=
-if test "$with_python" != "no"; then
-  if test "$with_python" = "yes" -o "$with_python" = ""; then
-    AC_SEARCH_HPACKAGE(/usr/local /usr,
-                       python1.6 python,
-	               Python.h, python)
-  else
-    AC_SEARCH_HPACKAGE($with_python,
-                       python1.6 python,
-	               Python.h, python)
-  fi
-
-  if test "$with_python" != ""; then
-    if test "$python_PKG" != "yes"; then
-      dnl  If the user specified to search for python but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate Python package.  Use --with-python=directory,"
-      echo "     e.g. --with-python=/usr/local, or just --with-python."
-      echo "     If Python is not installed, specify --without-python."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$python_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_PYTHON)
-#endif
-    have_python=yes
-    python_cflags=-I$python_INCLUDE
-  fi
-fi
-
-AC_SUBST(have_python)
-AC_SUBST(python_cflags)
-AM_CONDITIONAL(HAVE_PYTHON, test "$have_python" = "yes")
-
-
-AC_ARG_WITH(nspr,
-[  --with-nspr=DIR         Prefix where NSPR is installed (usually /usr/local/mozilla)])
-
-have_nspr=no
-include_nspr=
-if test "$with_nspr" != "no"; then
-  if test "$with_nspr" = "yes" -o "$with_nspr" = ""; then
-    AC_SEARCH_PACKAGE(/usr/local/mozilla /usr/local/mozilla/dist/*,,
-	              nspr.h, nspr3, PR_Init, nspr)
-  else
-    AC_SEARCH_PACKAGE($with_nspr $with_nspr/dist/*,,
-	              nspr.h, nspr3, PR_Init, nspr)
-  fi
-
-  if test "$with_nspr" != ""; then
-    if test "$nspr_PKG" != "yes"; then
-      dnl  If the user specified to search for NSPR but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate NSPR package.  Use --with-nspr=directory,"
-      echo "     e.g. --with-nspr=/usr/local, or just --with-nspr."
-      echo "     If NSPR is not installed, specify --without-nspr."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$nspr_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_NSPR)
-#endif
-    have_nspr=yes
-    nspr_cflags="-I$nspr_INCLUDE"
-    nspr_ldflags="-L$nspr_LIB"
-    nspr_libs="-lnspr3"
-  fi
-fi
-
-AC_SUBST(have_nspr)
-AC_SUBST(nspr_cflags)
-AC_SUBST(nspr_lflags)
-AC_SUBST(nspr_libs)
-AM_CONDITIONAL(HAVE_NSPR, test "$have_nspr" = "yes")
-
-
-AC_ARG_WITH(zlib,
-[  --with-zlib=DIR         Prefix where zlib is installed (usually /usr)])
-
-have_zlib=no
-include_zlib=
-if test "$with_zlib" != "no"; then
-  if test "$with_zlib" = "yes" -o "$with_zlib" = ""; then
-    AC_SEARCH_PACKAGE(/usr /usr/local,,
-	              zlib.h, z, gzopen, zlib)
-  else
-    AC_SEARCH_PACKAGE($with_zlib,,
-	              zlib.h, z, gzopen, zlib)
-  fi
-
-  if test "$with_zlib" != ""; then
-    if test "$zlib_PKG" != "yes"; then
-      dnl  If the user specified to search for zlib but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate zlib package.  Use --with-zlib=directory,"
-      echo "     e.g. --with-zlib=/usr/local, or just --with-zlib."
-      echo "     If zlib is not installed, specify --without-zlib."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$zlib_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_ZLIB)
-#endif
-    have_zlib=yes
-    if test "$zlib_INCLUDE" != ""; then
-      zlib_cflags="-I$zlib_INCLUDE"
-    fi
-    if test "$zlib_LIB" != ""; then
-      zlib_lflags="-L$zlib_LIB"
-    fi
-    zlib_libs="-lz"
-  fi
-fi
-
-AC_SUBST(have_zlib)
-AC_SUBST(zlib_cflags)
-AC_SUBST(zlib_lflags)
-AC_SUBST(zlib_libs)
-AM_CONDITIONAL(HAVE_ZLIB, test "$have_zlib" = "yes")
-
-
-AC_ARG_WITH(gl,
-[  --with-gl=DIR           Prefix where OpenGL is installed (usually /usr)])
-
-have_gl=no
-include_gl=
-if test "$with_gl" != "no"; then
-  if test "$with_gl" = "yes" -o "$with_gl" = ""; then
-    AC_SEARCH_PACKAGE(/usr /usr/local,,
-	              GL/gl.h, GL, glVertex3f, gl)
-  else
-    AC_SEARCH_PACKAGE($with_gl,,
-	              GL/gl.h, GL, glVertex3f, gl)
-  fi
-
-  if test "$with_gl" != ""; then
-    if test "$gl_PKG" != "yes"; then
-      dnl  If the user specified to search for OpenGL but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate OpenGL package.  Use --with-gl=directory,"
-      echo "     e.g. --with-gl=/usr/local, or just --with-gl."
-      echo "     If OpenGL is not installed, specify --without-gl."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$gl_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_GL)
-#endif
-    have_gl=yes
-    if test "$gl_INCLUDE" != ""; then
-      gl_cflags="-I$gl_INCLUDE"
-    fi
-    if test "$gl_LIB" != ""; then
-      gl_lflags="-L$gl_LIB"
-    fi
-    gl_libs="-lGL -lGLU"
-  fi
-fi
-
-AC_SUBST(have_gl)
-AC_SUBST(gl_cflags)
-AC_SUBST(gl_lflags)
-AC_SUBST(gl_libs)
-AM_CONDITIONAL(HAVE_GL, test "$have_gl" = "yes")
-
-
-AC_ARG_WITH(glu,
-[  --with-glu=DIR           Prefix where GL util library is installed (usually /usr)])
-
-have_glu=no
-include_glu=
-if test "$with_glu" != "no"; then
-  if test "$with_glu" = "yes" -o "$with_glu" = ""; then
-    AC_SEARCH_PACKAGE($gl_INCLUDE /usr /usr/local,,
-	              GL/glu.h, GLU, gluSphere, glu)
-  else
-    AC_SEARCH_PACKAGE($with_glu,,
-	              GL/glu.h, GLU, gluSphere, glu)
-  fi
-
-  if test "$with_glu" != ""; then
-    if test "$glu_PKG" != "yes"; then
-      dnl  If the user specified to search for GL util library but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate GL util library.  Use --with-glu=directory,"
-      echo "     e.g. --with-glu=/usr/local, or just --with-glu."
-      echo "     If GL util library is not installed, specify --without-glu."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$glu_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_GLU)
-#endif
-    have_glu=yes
-    if test "$glu_INCLUDE" != ""; then
-      glu_cflags="-I$glu_INCLUDE"
-    fi
-    if test "$glu_LIB" != ""; then
-      glu_lflags="-L$glu_LIB"
-    fi
-    glu_libs="-lGLU -lGLUU"
-  fi
-fi
-
-AC_SUBST(have_glu)
-AC_SUBST(glu_cflags)
-AC_SUBST(glu_lflags)
-AC_SUBST(glu_libs)
-AM_CONDITIONAL(HAVE_GLU, test "$have_glu" = "yes")
-
-
-AC_ARG_WITH(glx,
-[  --with-glx=DIR          Prefix where GLX is installed (usually /usr)])
-
-have_glx=no
-include_glx=
-if test "$with_glx" != "no"; then
-  if test "$with_glx" = "yes" -o "$with_glx" = ""; then
-    AC_SEARCH_HPACKAGE($gl_INCLUDE /usr /usr/local $x_libraries,,
-	               GL/glx.h, glx)
-  else
-    AC_SEARCH_HPACKAGE($with_glx,,
-	               GL/glx.h, glx)
-  fi
-
-  if test "$with_glx" != ""; then
-    if test "$glx_PKG" != "yes"; then
-      dnl  If the user specified to search for GLX but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate GLX package.  Use --with-glx=directory,"
-      echo "     e.g. --with-glx=/usr/local, or just --with-glx."
-      echo "     If GLX is not installed, specify --without-glx."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$glx_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_GLX)
-#endif
-    have_glx=yes
-    if test "$glx_INCLUDE" != ""; then
-      glx_cflags="-I$glx_INCLUDE"
-    fi
-  fi
-fi
-
-AC_SUBST(have_glx)
-AC_SUBST(glx_cflags)
-AM_CONDITIONAL(HAVE_GLX, test "$have_glx" = "yes")
-
-
-AC_ARG_WITH(glut,
-[  --with-glut=DIR         Prefix where glut is installed (usually /usr)])
-
-have_glut=no
-include_glut=
-if test "$with_glut" != "no"; then
-  if test "$with_glut" = "yes" -o "$with_glut" = ""; then
-    AC_SEARCH_PACKAGE($gl_INCLUDE /usr /usr/local $x_libraries,,
-	              GL/glut.h, glut, glutInit, glut, -lGL -lGLU)
-  else
-    AC_SEARCH_PACKAGE($with_glut,,
-	              GLUT/glut.h, glut, glutInit, glut, -lGL -lGLU)
-  fi
-
-  if test "$with_glut" != ""; then
-    if test "$glut_PKG" != "yes"; then
-      dnl  If the user specified to search for glut but we didn't find it,
-      dnl  abort now.
-
-      echo ""
-      echo "**** Could not locate glut package.  Use --with-glut=directory,"
-      echo "     e.g. --with-glut=/usr/local, or just --with-glut."
-      echo "     If glut is not installed, specify --without-glut."
-      echo ""
-      exit 1
-    fi
-  fi
-
-  if test "$glut_PKG" = "yes"; then
-#if $[ne $[CONFIG_HEADER],]
-    AC_DEFINE(HAVE_GLUT)
-#endif
-    have_glut=yes
-    if test "$glut_INCLUDE" != ""; then
-      glut_cflags="-I$glut_INCLUDE"
-    fi
-    if test "$glut_LIB" != ""; then
-      glut_lflags="-L$glut_LIB"
-    fi
-    glut_libs="-lglut"
-  fi
-fi
-
-AC_SUBST(have_glut)
-AC_SUBST(glut_cflags)
-AC_SUBST(glut_lflags)
-AC_SUBST(glut_libs)
-AM_CONDITIONAL(HAVE_GLUT, test "$have_glut" = "yes")
-
-
-AC_ARG_WITH(rib,
-[  --with-rib              Compile in the Renderman interface.])
-
-have_rib=no
-if test "$with_rib" = "yes"; then
-  have_rib=yes
-fi
-
-AC_SUBST(have_rib)
-#if $[ne $[CONFIG_HEADER],]
-if test "$have_rib" = "yes"; then
-AC_DEFINE(HAVE_RIB)
-fi
-#endif
-AM_CONDITIONAL(HAVE_RIB, test "$have_rib" = "yes")
-
-
-
-AC_ARG_WITH(mikmod,
-[  --with-mikmod[=libmikmod-config]  Use the mikmod interface for audio.])
-
-have_mikmod=no
-include_mikmod=
-if test "$with_mikmod" != "no"; then
-  if test "$with_mikmod" = "" -o "$with_mikmod" = "yes"; then
-    dnl search for the libmikmod-config program on the path.
-    AC_CHECK_PROG(with_mikmod, libmikmod-config, libmikmod-config, "no")
-  fi
-fi
-
-if test "$with_mikmod" != "no"; then
-  have_mikmod=yes
-  CFLAGS="$CFLAGS "`$with_mikmod --cflags`  
-  CXXFLAGS="$CXXFLAGS "`$with_mikmod --cflags`  
-  LDFLAGS="$LDFLAGS "`$with_mikmod --libs`  
-#if $[ne $[CONFIG_HEADER],]
-  AC_DEFINE(HAVE_MIKMOD)
-#endif
-fi
-
-AC_SUBST(have_mikmod)
-AM_CONDITIONAL(HAVE_MIKMOD, test "$have_mikmod" = "yes")
-
-ldflags=$LDFLAGS
-AC_SUBST(ldflags)
-
-AC_OUTPUT([$[TREE:%=%/Makefile]])
-
-#end configure.in
-
-//////////////////////////////////////////////////////////////////////
-#endif // DIR_TYPE

+ 0 - 711
ppremake/Template.stopgap.pp

@@ -1,711 +0,0 @@
-//
-// Template.stopgap.pp
-//
-// This file defines the set of output files that will be generated to
-// support our old-style Makefile system.  It is intended to aid as a
-// transition to the new system.
-//
-
-//////////////////////////////////////////////////////////////////////
-#if $[eq $[DIR_TYPE], src]
-//////////////////////////////////////////////////////////////////////
-
-// For a source directory, build a Makefile, Makefile.install, and a 
-// Makefile.target for each target.
-
-#define submakes $[TARGET(static_lib_target):%=%.a] $[TARGET(lib_target noinst_lib_target):%=%.so] $[TARGET(sed_bin_target bin_target noinst_bin_target test_bin_target)]
-#define install $[TARGET(static_lib_target):%=%.a] $[TARGET(lib_target noinst_lib_target):%=%.so] $[TARGET(sed_bin_target bin_target noinst_bin_target)]
-
-// Now iterate through the libraries we're building and see which ones
-// actually *are* being included in a metalib.  For each one that is,
-// we install the appropriate deferred file.
-#define deferred
-#forscopes lib_target
-  #define metalib $[module $[TARGET],$[TARGET]]
-  #if $[ne $[metalib],]
-    #set deferred $[deferred] Deferred.$[metalib].lib$[TARGET].so
-  #endif
-#end lib_target
-
-// Get the full set of libraries we depend on.
-#call get_depend_libs
-
-// Also get the targets we'll be installing.
-#define install_libs $[sort $[TARGET(lib_target):%=lib%.so] $[TARGET(static_lib_target):%=lib%.a] $[INSTALL_LIBS]]
-#define install_bin $[sort $[TARGET(bin_target)] $[INSTALL_BIN]]
-#define install_scripts $[sort $[INSTALL_SCRIPTS(static_lib_target lib_target bin_target)] $[TARGET(sed_bin_target)] $[INSTALL_SCRIPTS]]
-#define install_headers $[sort $[INSTALL_HEADERS(static_lib_target lib_target bin_target)] $[INSTALL_HEADERS]]
-#define install_data $[sort $[INSTALL_DATA(static_lib_target lib_target sed_bin_target bin_target)] $[INSTALL_DATA]] $[sort $[INSTALL_CONFIG(static_lib_target lib_target sed_bin_target bin_target)] $[INSTALL_CONFIG]]
-
-// Collect the set of interrogate database files we'll install,
-// possibly one for each library we build.
-#define install_igatedb
-#if $[HAVE_PYTHON]
-  #forscopes lib_target
-    #if $[ne $[IGATESCAN],]
-      #set install_igatedb $[install_igatedb] lib$[TARGET].in
-    #endif
-  #end lib_target
-#endif
-
-
-#output Makefile
-#format makefile
-#### Meta Makefile.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-
-#### Sub make targets (extension of sub Makefile, eg 'foo' for Makefile.foo):
-SUBMAKES = $[submakes]
-
-#### List the minimal set of sub makes on the list above required to install.
-INSTALL = $[install]
-
-#### Location of sub Makefiles.
-MAKEDIR = .
-
-#### The action is here.
-include $(DTOOL)/include/Makefile.meta.rules
-
-#### Sub-make build order dependencies:
-# foo: bar
-#end Makefile
-
-
-
-#output Makefile.install
-#format makefile
-#### Installation makefile
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-# Note: This file is included by the project-wide Makefile so the current
-# directory is the project root.  Also, commented-out fields are optional.
-
-#### Package name and location (if not src/all/$(PACKAGE)):
-PACKAGE = $[DIRNAME]
-PKGROOT = $[PATH]
-
-ifneq (,$(PACKAGE))
-
-#### Package dependencies (USESOTHER needs relative paths from project root):
-USESLIBS = $[depend_libs]
-# USESINCLUDE = 
-# USESOTHER = 
-
-#### Installed files:
-LIBS = $[install_libs]
-DEFERRED = $[deferred]
-INCLUDE = $[install_headers]
-BINS = $[install_bin]
-SCRIPTS = $[install_scripts]
-# SS = 
-# STK = 
-# MODELS = 
-ETC = $[install_data]
-IGATEDB =$[install_igatedb]
-# DOC =
-# MAN =
-# FONTS = 
-# ICONS = 
-# APPDEFAULTS = 
-# TCL = 
-# TELEUSE = 
-# SHADERS = 
-
-#### Other files to be installed (use relative pathname from project root):
-#if $[ne $[INSTALL_PARSER_INC],]
-PARSER_INC = $[INSTALL_PARSER_INC]
-SRC_PARSER_INC = $(addprefix $(PKGROOT)/,$(PARSER_INC))
-INST_PARSER_INC = $(addprefix include/parser-inc/,$(PARSER_INC))
-OTHER = $(INST_PARSER_INC)
-#else
-# OTHER =
-#endif
-
-#### Where the action happens.
-include $(DTOOL)/include/Makefile.install.rules
-
-#### Install actions for OTHER files (source must be in $(PKGROOT)):
-# [ installed file ] : $(PKGROOT)/[ source file ]  # Files must have same name
-#	$(INSTALL)                      # Copies from source to dest
-#
-# [ installed file ] : $(PKGROOT)/[ source file ]
-#	$(MKINSTALL)			# Also makes directory if needed
-
-#if $[ne $[INSTALL_PARSER_INC],]
-$(INST_PARSER_INC) : include/parser-inc/% : $(PKGROOT)/%
-	$(MKINSTALL)
-#endif
-
-#### Other install/uninstall actions:
-# install-$(PKGROOT): #Add dependencies here
-#	Add actions here
-#
-# uninstall-$(PKGROOT): #Add dependencies here
-#	Add actions here
-
-#### Sub-package Makefile.install inclusions:
-# include foo/Makefile.install
-
-endif
-#end Makefile.install
-
-
-
-// Now generate a suitable Makefile for each library target.
-#forscopes lib_target noinst_lib_target
-
-// Again, is this library included in a metalib?  If so, output the
-// appropriate deferred rules.
-#define metalib $[module $[TARGET],$[TARGET]]
-
-// We might need to define a BUILDING_ symbol for win32.  We use the
-// BUILDING_DLL variable name, defined typically in the metalib, for
-// this; but in some cases, where the library isn't part of a metalib,
-// we define BUILDING_DLL directly for the target.
-#define building_var $[BUILDING_DLL]
-#if $[ne $[metalib],]
-  #set building_var $[module $[BUILDING_DLL],$[TARGET]]
-#endif
-
-// Get the full set of sources for this target.
-#call get_sources
-#call get_libs
-
-// Which files will we interrogate, if any?
-#if $[HAVE_PYTHON]
-  #if $[ne $[IGATESCAN],]
-    #if $[eq $[IGATESCAN],all]
-      #define igatescan $[filter-out %.I %.lxx %.yxx %.N,$[sources]]
-    #else
-      #define igatescan $[IGATESCAN]
-    #endif
-  #endif
-#endif
-
-#output Makefile.$[TARGET].so
-#format makefile
-#### Makefile for DSO's.  Any fields commented out are optional.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-#### Target's name:
-TARGET = lib$[TARGET].so
-DEFERRED_TARGET = $[metalib]
-
-# Standard .o file conversion information.
-
-#### Lex files 
-LFILES = $[filter %.lxx,$[sources]]
-LEX = flex
-LFLAGS = $[LFLAGS] $[YACC_PREFIX:%=-P%] -olex.yy.c
-LEXTENSION = cxx
-
-#### Yacc files 
-YFILES = $[filter %.yxx,$[sources]]
-YACC = bison
-YFLAGS = -y -d $[patsubst %,--name-prefix=%,$[YACC_PREFIX]]
-YEXTENSION = cxx
-
-#### C files 
-CFILES = $[filter %.c,$[sources]]
-CFLAGS = $[building_var:%=-D%] $[alt_cflags] $[CFLAGS]
-
-#### C++ files 
-C++FILES = $[filter %.cxx,$[sources]]
-C++FLAGS = $[building_var:%=-D%] $[alt_cflags] $[C++FLAGS]
-# USETEMPLATES = TRUE
-# PTREPOSITORY = # Specify only if you want a specific name
-
-#### Interrogate info
-IGATESCAN  = $[igatescan]
-IGATEFLAGS = $[alt_ipath:%=-I%]
-# IGATEFILE  = # Specify only if you want a specific name
-
-#### Additional search directories for C/C++ header files:
-IPATH = $[alt_ipath:%=-I%]
-
-#### Location to put .o files:
-# ODIR = 
-
-#### Source file dependencies (unnecessary with clearmake)
-# foo.c: foo.h
-
-#### Other files and lib.  Include $(ODIR) in any .o names.
-# OFILES = 
-WHEN_NO_DEFER_LIBS = $[when_no_defer:%=-l%]
-WHEN_DEFER_LIBS = $[when_defer:%=-l%]
-LIBS = $[when_either:%=-l%]
-SYSLIBS = $[patsubst %.lib,%.lib,%,-l%,$[unique $[alt_libs]]]
-
-#### Additional search directories for lib:
-LPATH = $[alt_lpath:%=-L%]
-
-#### Other linker flags. 
-#if $[ne $[alt_ld],]
-LD = $[alt_ld]
-#endif
-# LDFLAGS = 
-
-#### Pull in standard .o make variables
-include $(DTOOL)/include/Makefile.o.vars
-
-#### The .o action is here.
-include $(DTOOL)/include/Makefile.o.rules
-
-#### Pull in standard binary make variables.
-include $(DTOOL)/include/Makefile.bin.vars
-
-#### The .so action is here.
-include $(DTOOL)/include/Makefile.so.rules
-#end Makefile.$[TARGET].so
-
-#end lib_target noinst_lib_target
-
-
-
-// Also generate a suitable Makefile for each static library target.
-#forscopes static_lib_target
-
-// Get the full set of sources for this target.
-#call get_sources
-#call get_libs
-
-#output Makefile.$[TARGET].a
-#format makefile
-#### Makefile for archive libraries.  Any fields commented out are optional.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-#### Target's name:
-TARGET = lib$[TARGET].a
-
-# Standard .o file conversion information.
-
-#### Lex files 
-LFILES = $[filter %.lxx,$[sources]]
-LEX = flex
-LFLAGS = $[LFLAGS] $[YACC_PREFIX:%=-P%] -olex.yy.c
-LEXTENSION = yy.cxx
-# LSUBST =
-
-#### Yacc files 
-YFILES = $[filter %.yxx,$[sources]]
-YACC = bison
-YFLAGS = -y -d $[patsubst %,--name-prefix=%,$[YACC_PREFIX]]
-YEXTENSION = tab.cxx
-# YSUBST =
-
-#### C files 
-CFILES = $[filter %.c,$[sources]]
-CFLAGS = $[building_var:%=-D%] $[alt_cflags] $[CFLAGS]
-
-#### C++ files 
-C++FILES = $[filter %.cxx,$[sources]]
-C++FLAGS = $[building_var:%=-D%] $[alt_cflags] $[C++FLAGS]
-# USETEMPLATES = TRUE
-# PTREPOSITORY = # Specify only if you want a specific name
-
-#### Additional search directories for C/C++ header files:
-IPATH = $[alt_ipath:%=-I%]
-
-#### Location to put .o files:
-# ODIR = 
-
-#### Source file dependencies (unnecessary with clearmake)
-# foo.c: foo.h
-
-#### Other .o files.
-# OFILES = 
-
-#### Libs and flags for template instantiation.
-WHEN_NO_DEFER_LIBS = $[when_no_defer:%=-l%]
-WHEN_DEFER_LIBS = $[when_defer:%=-l%]
-LIBS = $[when_either:%=-l%]
-SYSLIBS = $[patsubst %.lib,%.lib,%,-l%,$[unique $[alt_libs]]]
-
-#### Additional search directories for lib:
-LPATH = $[alt_lpath:%=-L%]
-
-#### Archiver flags
-# ARFLAGS = 
-
-#### Pull in standard .o make variables
-include $(DTOOL)/include/Makefile.o.vars
-
-#### The .o action is here.
-include $(DTOOL)/include/Makefile.o.rules
-
-#### Pull in standard binary make variables.
-include $(DTOOL)/include/Makefile.bin.vars
-
-#### The .a action is here.
-include $(DTOOL)/include/Makefile.a.rules
-#end Makefile.$[TARGET].a
-
-#end static_lib_target
-
-
-
-// And also generate a suitable Makefile for each binary target.
-#forscopes bin_target noinst_bin_target test_bin_target
-
-// Get the full set of sources for this target.
-#call get_sources
-#call get_libs
-
-#output Makefile.$[TARGET]
-#format makefile
-#### Makefile for binaries.  Any fields commented out are optional.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-#### Target's name:
-TARGET = $[TARGET]
-
-# Standard .o file conversion information.
-
-#### Lex files 
-LFILES = $[filter %.lxx,$[sources]]
-LEX = flex
-LFLAGS = $[LFLAGS] $[YACC_PREFIX:%=-P%] -olex.yy.c
-LEXTENSION = yy.cxx
-# LSUBST =
-
-#### Yacc files 
-YFILES = $[filter %.yxx,$[sources]]
-YACC = bison
-YFLAGS = -y -d $[patsubst %,--name-prefix=%,$[YACC_PREFIX]]
-YEXTENSION = tab.cxx
-# YSUBST =
-
-#### C files 
-CFILES = $[filter %.c,$[sources]]
-CFLAGS = $[building_var:%=-D%] $[alt_cflags] $[CFLAGS]
-
-#### C++ files 
-C++FILES = $[filter %.cxx,$[sources]]
-C++FLAGS = $[building_var:%=-D%] $[alt_cflags] $[C++FLAGS]
-
-#### Additional search directories for C/C++ header files:
-IPATH = $[alt_ipath:%=-I%]
-
-#### Location to put .o files:
-# ODIR = 
-
-#### Source file dependencies (unnecessary with clearmake)
-# foo.c: foo.h
-
-#### Other files and lib.  Include $(ODIR) in any .o names.
-# OFILES = 
-WHEN_NO_DEFER_LIBS = $[when_no_defer:%=-l%]
-WHEN_DEFER_LIBS = $[when_defer:%=-l%]
-LIBS = $[when_either:%=-l%]
-SYSLIBS = $[patsubst %.lib,%.lib,%,-l%,$[unique $[alt_libs]]]
-
-#### Additional search directories for lib:
-LPATH = $[alt_lpath:%=-L%]
-
-#### Other linker flags. 
-#if $[ne $[alt_ld],]
-LD = $[alt_ld]
-#endif
-# LDFLAGS =
-
-#### Pull in standard .o make variables
-include $(DTOOL)/include/Makefile.o.vars
-
-#### The .o action is here.
-include $(DTOOL)/include/Makefile.o.rules
-
-#### Pull in standard binary make variables.
-include $(DTOOL)/include/Makefile.bin.vars
-
-#### The bin action is here.
-include $(DTOOL)/include/Makefile.bin.rules
-#end Makefile.$[TARGET]
-
-#end bin_target noinst_bin_target test_bin_target
-
-
-// Finally, generate the special scripts from the sed_bin_targets.  Hopefully
-// there won't be too many of these in the tree, since these are fairly
-// Unix-specific.
-#forscopes sed_bin_target
-#output Makefile.$[TARGET]
-#format makefile
-#### This is a special makefile just to generate the $[TARGET] script.
-
-$[TARGET] : $[SOURCE]
-	sed $[COMMAND] $^ >$@
-	chmod +x $@
-
-clean :
-
-cleanall :
-	rm -f $[TARGET]
-#end Makefile.$[TARGET]
-#end sed_bin_target
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], metalib]
-//////////////////////////////////////////////////////////////////////
-
-// A metalib directory is similar to a regular source directory, 
-// but a little simpler.
-
-#define submakes $[TARGET(metalib_target):%=%.so]
-
-
-// Get the full set of libraries we depend on.
-#call get_depend_libs
-
-// Also get the targets we'll be installing.
-#define install_libs $[TARGET(metalib_target):%=lib%.so]
-#define install_headers $[INSTALL_HEADERS(metalib_target)]
-#define install_data $[INSTALL_DATA(metalib_target)] $[INSTALL_CONFIG(metalib_target)]
-
-
-#output Makefile
-#format makefile
-#### Meta Makefile.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-
-#### Sub make targets (extension of sub Makefile, eg 'foo' for Makefile.foo):
-SUBMAKES = $[submakes]
-
-#### List the minimal set of sub makes on the list above required to install.
-INSTALL = $[submakes]
-
-#### Location of sub Makefiles.
-MAKEDIR = .
-
-#### The action is here.
-include $(DTOOL)/include/Makefile.meta.rules
-
-#### Sub-make build order dependencies:
-# foo: bar
-#end Makefile
-
-
-
-#output Makefile.install
-#format makefile
-#### Installation makefile
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-# Note: This file is included by the project-wide Makefile so the current
-# directory is the project root.  Also, commented-out fields are optional.
-
-#### Package name and location (if not src/all/$(PACKAGE)):
-PACKAGE = $[DIRNAME]
-PKGROOT = $[PATH]
-
-ifneq (,$(PACKAGE))
-
-#### Package dependencies (USESOTHER needs relative paths from project root):
-USESLIBS = $[depend_libs]
-# USESINCLUDE = 
-# USESOTHER = 
-
-#### Installed files:
-LIBS = $[install_libs]
-INCLUDE = $[install_headers]
-# BINS =
-# SS = 
-# STK = 
-# MODELS = 
-# ETC =
-# DOC =
-# MAN =
-# FONTS = 
-# ICONS = 
-# APPDEFAULTS = 
-# TCL = 
-# TELEUSE = 
-# SHADERS = 
-
-#### Other files to be installed (use relative pathname from project root):
-# OTHER = 
-
-#### Where the action happens.
-include $(DTOOL)/include/Makefile.install.rules
-
-#### Install actions for OTHER files (source must be in $(PKGROOT)):
-# [ installed file ] : $(PKGROOT)/[ source file ]  # Files must have same name
-#	$(INSTALL)                      # Copies from source to dest
-#
-# [ installed file ] : $(PKGROOT)/[ source file ]
-#	$(MKINSTALL)			# Also makes directory if needed
-
-#### Other install/uninstall actions:
-# install-$(PKGROOT): #Add dependencies here
-#	Add actions here
-#
-# uninstall-$(PKGROOT): #Add dependencies here
-#	Add actions here
-
-#### Sub-package Makefile.install inclusions:
-# include foo/Makefile.install
-
-endif
-#end Makefile.install
-
-// Now generate a suitable Makefile for each metalib target.
-#forscopes metalib_target
-
-#define building_var $[BUILDING_DLL]
-
-// Get the full set of sources for this target.
-#call get_sources
-#call get_libs
-
-#if $[HAVE_PYTHON]
-  #if $[ne $[components $[IGATESCAN],$[COMPONENT_LIBS]],]
-    #define igatemscan $[TARGET]
-  #endif
-#endif
-
-#output Makefile.$[TARGET].so
-#format makefile
-#### Makefile for DSO's.  Any fields commented out are optional.
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-#### Target's name:
-TARGET = lib$[TARGET].so
-
-# Standard .o file conversion information.
-
-#### Lex files 
-LFILES = $[filter %.lxx,$[sources]]
-LEX = flex
-LFLAGS = $[LFLAGS] $[YACC_PREFIX:%=-P%] -olex.yy.c
-LEXTENSION = yy.cxx
-# LSUBST =
-
-#### Yacc files 
-YFILES = $[filter %.yxx,$[sources]]
-YACC = bison
-YFLAGS = -y -d $[patsubst %,--name-prefix=%,$[YACC_PREFIX]]
-YEXTENSION = tab.cxx
-# YSUBST =
-
-#### C files 
-CFILES = $[filter %.c,$[sources]]
-CFLAGS = $[building_var:%=-D%] $[alt_cflags] $[CFLAGS]
-
-#### C++ files 
-C++FILES = $[filter %.cxx,$[sources]]
-C++FLAGS = $[building_var:%=-D%] $[alt_cflags] $[C++FLAGS]
-# USETEMPLATES = TRUE
-# PTREPOSITORY = # Specify only if you want a specific name
-
-#### Interrogate info
-# IGATESCAN  = 
-# IGATEFLAGS = 
-# IGATEFILE  = # Specify only if you want a specific name
-IGATEMSCAN = $[igatemscan]
-
-#### Pull in deferred-target files built in other packages
-DEFERRED_FILES = $[TARGET]
-
-#### Additional search directories for C/C++ header files:
-IPATH = $[alt_ipath:%=-I%]
-
-#### Location to put .o files:
-# ODIR = 
-
-#### Source file dependencies (unnecessary with clearmake)
-# foo.c: foo.h
-
-#### Other files and lib.  Include $(ODIR) in any .o names.
-# OFILES = 
-WHEN_NO_DEFER_LIBS = $[when_no_defer:%=-l%]
-WHEN_DEFER_LIBS = $[when_defer:%=-l%]
-LIBS = $[when_either:%=-l%]
-SYSLIBS = $[patsubst %.lib,%.lib,%,-l%,$[unique $[alt_libs]]]
-
-#### Additional search directories for lib:
-LPATH = $[alt_lpath:%=-L%]
-
-#### Other linker flags. 
-#if $[ne $[alt_ld],]
-LD = $[alt_ld]
-#endif
-# LDFLAGS = 
-
-#### Pull in standard .o make variables
-include $(DTOOL)/include/Makefile.o.vars
-
-#### The .o action is here.
-include $(DTOOL)/include/Makefile.o.rules
-
-#### Pull in standard binary make variables.
-include $(DTOOL)/include/Makefile.bin.vars
-
-#### The .so action is here.
-include $(DTOOL)/include/Makefile.so.rules
-#end Makefile.$[TARGET].so
-
-#end metalib_target
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], group]
-//////////////////////////////////////////////////////////////////////
-
-// This is a group directory: a directory above a collection of source
-// directories, e.g. $DTOOL/src.  We don't need to output anything in
-// this directory.
-
-
-
-//////////////////////////////////////////////////////////////////////
-#elif $[eq $[DIR_TYPE], toplevel]
-//////////////////////////////////////////////////////////////////////
-
-// This is the toplevel directory, e.g. $DTOOL.  Here we build the
-// root makefile and also synthesize the dtool_config.h (or whichever
-// file) we need.
-
-#output Makefile
-#format makefile
-#### Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE].
-################################# DO NOT EDIT ###########################
-
-# Specify project name and project root directory.
-CTPROJECT = $[PACKAGE]
-CTPROJROOT = $($[upcase $[PACKAGE]])
-
-include $(DTOOL)/include/Makefile.project.vars
-
-// Iterate through all of our known source files.  Each src and
-// metalib type file gets its corresponding Makefile.install listed
-// here.  However, we test for $[DIR_TYPE] of toplevel, because the
-// source directories typically don't define their own DIR_TYPE
-// variable, and they end up inheriting this one dynamically.
-#forscopes */
-#if $[or $[eq $[DIR_TYPE], src],$[eq $[DIR_TYPE], metalib],$[and $[eq $[DIR_TYPE], toplevel],$[ne $[DIRNAME],top]]]
-#if $[build_directory]
-include $[PATH]/Makefile.install
-#endif
-#endif
-#end */
-
-#end Makefile
-
-// If there is a file called LocalSetup.pp in the package's top
-// directory, then invoke that.  It might contain some further setup
-// instructions.
-#sinclude $[TOPDIRPREFIX]LocalSetup.stopgap.pp
-#sinclude $[TOPDIRPREFIX]LocalSetup.pp
-
-//////////////////////////////////////////////////////////////////////
-#endif // DIR_TYPE

+ 64 - 0
ppremake/check_include.cxx

@@ -0,0 +1,64 @@
+// Filename: check_include.cxx
+// Created by:  drose (16Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "check_include.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: check_include
+//  Description: Checks to see if the given line is a C/C++ #include
+//               directive.  If it is, returns the named filename;
+//               otherwise, returns the empty string.
+////////////////////////////////////////////////////////////////////
+string
+check_include(const string &line) {
+  // Skip initial whitespace on the line.
+  size_t p = 0;
+  while (p < line.length() && isspace(line[p])) {
+    p++;
+  }
+
+  if (p >= line.length() || line[p] != '#') {
+    // No hash mark.
+    return string();
+  }
+   
+  // We have a hash mark!  Skip more whitespace.
+  p++;
+  while (p < line.length() && isspace(line[p])) {
+    p++;
+  }
+
+  if (p >= line.length() || !(line.substr(p, 7) == "include")) {
+    // Some other directive, not #include.
+    return string();
+  }
+
+  // It's an #include directive!  Skip more whitespace.
+  p += 7;
+  while (p < line.length() && isspace(line[p])) {
+    p++;
+  }
+
+  if (p >= line.length() || (line[p] != '"' && line[p] != '<')) {
+    cerr << "Ignoring invalid #include directive: " << line << "\n";
+    return string();
+  }
+
+  char close = (line[p] == '"') ? '"' : '>';
+
+  p++;
+  // Now get the filename.
+  size_t q = p;
+  while (q < line.length() && line[q] != close) {
+    q++;
+  }
+
+  if (q >= line.length()) {
+    cerr << "Ignoring invalid #include directive: " << line << "\n";
+    return string();
+  }
+
+  return line.substr(p, q - p);
+}

+ 14 - 0
ppremake/check_include.h

@@ -0,0 +1,14 @@
+// Filename: check_include.h
+// Created by:  drose (16Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef CHECK_INCLUDE_H
+#define CHECK_INCLUDE_H
+
+#include "ppremake.h"
+
+string check_include(const string &line);
+
+#endif
+

+ 1 - 1
ppremake/configure.in

@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(ppremake.cxx)
 AC_INIT(ppremake.cxx)
-AM_INIT_AUTOMAKE(ppremake, 0.26)
+AM_INIT_AUTOMAKE(ppremake, 0.50)
 AM_CONFIG_HEADER(config.h)
 AM_CONFIG_HEADER(config.h)
 
 
 AC_PREFIX_DEFAULT(/usr/local/panda)
 AC_PREFIX_DEFAULT(/usr/local/panda)

+ 34 - 3
ppremake/ppCommandFile.cxx

@@ -12,6 +12,8 @@
 #include <ctype.h>
 #include <ctype.h>
 #include <stdio.h>  // for tempnam()
 #include <stdio.h>  // for tempnam()
 #include <unistd.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <utime.h>
 
 
 static const string begin_comment(BEGIN_COMMENT);
 static const string begin_comment(BEGIN_COMMENT);
 
 
@@ -823,13 +825,31 @@ handle_format_command() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPCommandFile::
 bool PPCommandFile::
 handle_output_command() {
 handle_output_command() {
+  vector<string> words;
+  tokenize_whitespace(_scope->expand_string(_params), words);
+
+  if (words.empty()) {
+    cerr << "#output command requires one parameter.\n";
+    return false;
+  }
+
   BlockNesting *nest = new BlockNesting;
   BlockNesting *nest = new BlockNesting;
   nest->_state = BS_output;
   nest->_state = BS_output;
-  nest->_name = trim_blanks(_scope->expand_string(_params));
+  nest->_name = words[0];
   nest->_write_state = _write_state;
   nest->_write_state = _write_state;
   nest->_scope = _scope;
   nest->_scope = _scope;
   nest->_next = _block_nesting;
   nest->_next = _block_nesting;
 
 
+  // Also check the output flags.
+  nest->_flags = 0;
+  for (int i = 1; i < (int)words.size(); i++) {
+    if (words[i] == "notouch") {
+      nest->_flags |= OF_notouch;
+    } else {
+      cerr << "Invalid output flag: " << words[i] << "\n";
+    }
+  }
+
   _block_nesting = nest;
   _block_nesting = nest;
 
 
   if (!_in_for) {
   if (!_in_for) {
@@ -1036,7 +1056,8 @@ handle_end_command() {
 
 
       // Verify the output file.
       // Verify the output file.
       if (nest->_tempnam != (char *)NULL) {
       if (nest->_tempnam != (char *)NULL) {
-	if (!compare_output(nest->_tempnam, nest->_true_name)) {
+	if (!compare_output(nest->_tempnam, nest->_true_name,
+			    (nest->_flags & OF_notouch) != 0)) {
 	  return false;
 	  return false;
 	}
 	}
 	free(nest->_tempnam);
 	free(nest->_tempnam);
@@ -1503,7 +1524,8 @@ replay_formap(const string &varname, const string &mapvar) {
 //               same, remove the temporary file.
 //               same, remove the temporary file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPCommandFile::
 bool PPCommandFile::
-compare_output(const string &temp_name, const string &true_name) {
+compare_output(const string &temp_name, const string &true_name,
+	       bool notouch) {
   ifstream in_a(temp_name.c_str());
   ifstream in_a(temp_name.c_str());
   ifstream in_b(true_name.c_str());
   ifstream in_b(true_name.c_str());
 
 
@@ -1538,6 +1560,15 @@ compare_output(const string &temp_name, const string &true_name) {
     if (unlink(temp_name.c_str()) < 0) {
     if (unlink(temp_name.c_str()) < 0) {
       cerr << "Warning: unable to remove temporary file " << temp_name << "\n";
       cerr << "Warning: unable to remove temporary file " << temp_name << "\n";
     }
     }
+
+    // Even though the file is unchanged, unless the "notouch" flag is
+    // set, we want to update the modification time.  This helps the
+    // makefiles know we did something.
+    if (!notouch) {
+      if (utime(true_name.c_str(), (struct utimbuf *)NULL) < 0) {
+	cerr << "Warning: unable to touch " << true_name << "\n";
+      }
+    }
   }
   }
 
 
   return true;
   return true;

+ 7 - 1
ppremake/ppCommandFile.h

@@ -67,7 +67,8 @@ protected:
   bool replay_forscopes(const string &name);
   bool replay_forscopes(const string &name);
   bool replay_foreach(const string &varname, const vector<string> &words);
   bool replay_foreach(const string &varname, const vector<string> &words);
   bool replay_formap(const string &varname, const string &mapvar);
   bool replay_formap(const string &varname, const string &mapvar);
-  bool compare_output(const string &temp_name, const string &true_name);
+  bool compare_output(const string &temp_name, const string &true_name,
+		      bool notouch);
   bool failed_if() const;
   bool failed_if() const;
 
 
   bool is_valid_formal(const string &formal_parameter_name) const;
   bool is_valid_formal(const string &formal_parameter_name) const;
@@ -131,6 +132,10 @@ private:
     WriteFormat _format;
     WriteFormat _format;
     bool _last_blank;
     bool _last_blank;
   };
   };
+
+  enum OutputFlags {
+    OF_notouch  = 0x001,
+  };
   
   
   class BlockNesting {
   class BlockNesting {
   public:
   public:
@@ -142,6 +147,7 @@ private:
     char *_tempnam;
     char *_tempnam;
     ofstream _output;
     ofstream _output;
     vector<string> _words;
     vector<string> _words;
+    int _flags;
     BlockNesting *_next;
     BlockNesting *_next;
   };
   };
 
 

+ 413 - 0
ppremake/ppDependableFile.cxx

@@ -0,0 +1,413 @@
+// Filename: ppDependableFile.cxx
+// Created by:  drose (15Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "ppDependableFile.h"
+#include "ppDirectory.h"
+#include "ppDirectoryTree.h"
+#include "check_include.h"
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PPDependableFile::
+PPDependableFile(PPDirectory *directory, const string &filename) :
+  _directory(directory),
+  _filename(filename)
+{
+  _flags = 0;
+  _mtime = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::update_from_cache
+//       Access: Public
+//  Description: Called as the dependency cache file is being read,
+//               this asks the file to update its information from the
+//               cache file if appropriate.  This means comparing the
+//               cached modification time against the file's actual
+//               modification time, and storing the cached
+//               dependencies if they match.
+////////////////////////////////////////////////////////////////////
+void PPDependableFile::
+update_from_cache(const vector<string> &words) {
+  // Shouldn't call this once the file has actually been read.
+  assert((_flags & F_updated) == 0);
+  assert((_flags & F_updating) == 0);
+  assert((_flags & F_from_cache) == 0);
+  assert(words.size() >= 2);
+
+  // The second parameter is the cached modification time.
+  time_t mtime = strtol(words[1].c_str(), (char **)NULL, 10);
+  if (mtime == get_mtime()) {
+    // The modification matches; preserve the cache information.
+    PPDirectoryTree *tree = _directory->get_tree();
+
+    _dependencies.clear();
+    vector<string>::const_iterator wi;
+    for (wi = words.begin() + 2; wi != words.end(); ++wi) {
+      string dirpath = (*wi);
+
+      Dependency dep;
+      dep._okcircular = false;
+
+      if (dirpath.length() > 1 && dirpath[0] == '/') {
+	// If the first character is '/', it means that the file has
+	// been marked okcircular.
+	dep._okcircular = true;
+	dirpath = dirpath.substr(1);
+      }
+
+      if (dirpath.length() > 2 && dirpath.substr(0, 2) == "*/") {
+	// This is an extra include file, not a file in this source
+	// tree.
+	_extra_includes.push_back(dirpath.substr(2));
+
+      } else {
+	dep._file = 
+	  tree->get_dependable_file_by_dirpath(dirpath, false);
+	if (dep._file != (PPDependableFile *)NULL) {
+	  _dependencies.push_back(dep);
+	}
+      }
+    }
+
+    _flags |= F_from_cache;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::write_cache
+//       Access: Public
+//  Description: Writes the dependency information out as a single
+//               line to the indicated dependency cache file.
+////////////////////////////////////////////////////////////////////
+void PPDependableFile::
+write_cache(ostream &out) {
+  out << _filename << " " << get_mtime();
+
+  Dependencies::const_iterator di;
+  for (di = _dependencies.begin(); di != _dependencies.end(); ++di) {
+    out << " ";
+    if ((*di)._okcircular) {
+      out << "/";
+    }
+    out << (*di)._file->get_dirpath();
+  }
+
+  // Also write out the extra includes--those #include directives
+  // which do not reference a file within this source tree.  We need
+  // those just for comparison's sake later, so we can tell whether
+  // the cache line is still current (without having to know which
+  // files are part of the tree).
+  ExtraIncludes::const_iterator ei;
+  for (ei = _extra_includes.begin(); ei != _extra_includes.end(); ++ei) {
+    out << " */" << (*ei);
+  }
+
+  out << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_directory
+//       Access: Public
+//  Description: Returns the directory that this file can be found in.
+////////////////////////////////////////////////////////////////////
+PPDirectory *PPDependableFile::
+get_directory() const {
+  return _directory;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_filename
+//       Access: Public
+//  Description: Returns the local filename of this particular file
+//               within the directory.
+////////////////////////////////////////////////////////////////////
+const string &PPDependableFile::
+get_filename() const {
+  return _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_pathname
+//       Access: Public
+//  Description: Returns the relative pathname from the root of the
+//               source tree to this particular filename.
+////////////////////////////////////////////////////////////////////
+string PPDependableFile::
+get_pathname() const {
+  return _directory->get_path() + "/" + _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_dirpath
+//       Access: Public
+//  Description: Returns an abbreviated pathname to this file, in the
+//               form dirname/filename.
+////////////////////////////////////////////////////////////////////
+string PPDependableFile::
+get_dirpath() const {
+  return _directory->get_dirname() + "/" + _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::exists
+//       Access: Public
+//  Description: Returns true if the file exists, false if it does
+//               not.
+////////////////////////////////////////////////////////////////////
+bool PPDependableFile::
+exists() {
+  stat_file();
+  return ((_flags & F_exists) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_mtime
+//       Access: Public
+//  Description: Returns the last modification time of the file.
+////////////////////////////////////////////////////////////////////
+time_t PPDependableFile::
+get_mtime() {
+  stat_file();
+  return _mtime;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_num_dependencies
+//       Access: Public
+//  Description: Returns the number of files this file depends on.
+////////////////////////////////////////////////////////////////////
+int PPDependableFile::
+get_num_dependencies() {
+  update_dependencies();
+  return _dependencies.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_dependency
+//       Access: Public
+//  Description: Returns the nth file this file depends on.
+////////////////////////////////////////////////////////////////////
+PPDependableFile *PPDependableFile::
+get_dependency(int n) {
+  assert((_flags & F_updated) != 0);
+  assert(n >= 0 && n < (int)_dependencies.size());
+  return _dependencies[n]._file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_complete_dependencies
+//       Access: Public
+//  Description: Recursively determines the complete set of files this
+//               file depends on.  It is the user's responsibility to
+//               empty the set before calling this function; the
+//               results will simply be added to the existing set.
+////////////////////////////////////////////////////////////////////
+void PPDependableFile::
+get_complete_dependencies(set<PPDependableFile *> &files) {
+  update_dependencies();
+  Dependencies::const_iterator di;
+  for (di = _dependencies.begin(); di != _dependencies.end(); ++di) {
+    PPDependableFile *file = (*di)._file;
+    if (files.insert(file).second) {
+      file->get_complete_dependencies(files);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::is_circularity
+//       Access: Public
+//  Description: Returns true if a circular dependency exists between
+//               this file and one or more other files.
+////////////////////////////////////////////////////////////////////
+bool PPDependableFile::
+is_circularity() {
+  update_dependencies();
+  return (_flags & F_circularity) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::get_circularity
+//       Access: Public
+//  Description: If is_circularity() returns true, returns a string
+//               describing the circular dependency path for the user.
+////////////////////////////////////////////////////////////////////
+string PPDependableFile::
+get_circularity() {
+  update_dependencies();
+  return _circularity;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::was_examined
+//       Access: Public
+//  Description: Returns true if anyone ever asked this file for its
+//               list of dependencies, or false otherwise.
+////////////////////////////////////////////////////////////////////
+bool PPDependableFile::
+was_examined() const {
+  return ((_flags & F_updated) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::update_dependencies
+//       Access: Private
+//  Description: Builds up the dependency list--the list of files this
+//               file depends on--if it hasn't already been built.  If
+//               a circular dependency is detected during this
+//               process, _circularity and _circularity_detected will
+//               be updated accordingly.
+////////////////////////////////////////////////////////////////////
+void PPDependableFile::
+update_dependencies() {
+  if ((_flags & F_updated) != 0) {
+    return;
+  }  
+
+  assert((_flags & F_updating) == 0);
+  string circularity;
+  compute_dependencies(circularity);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::compute_dependencies
+//       Access: Private
+//  Description: Builds up the dependency list--the list of files this
+//               file depends on--if it hasn't already been built.
+//
+//               If a circularity is detected, e.g. two files depend
+//               on each other, a pointer to the offending file is
+//               returned and the string is updated to indicate the
+//               circularity.  Otherwise, if there is no circularity,
+//               NULL is returned.
+////////////////////////////////////////////////////////////////////
+PPDependableFile *PPDependableFile::
+compute_dependencies(string &circularity) {
+  if ((_flags & F_updated) != 0) {
+    return (PPDependableFile *)NULL;
+
+  } else if ((_flags & F_updating) != 0) {
+    // Oh oh, a circular dependency!
+    circularity = get_dirpath();
+    return this;
+  }
+
+  _flags |= F_updating;
+
+  if ((_flags & F_from_cache) == 0) {
+    // Now open the file and scan it for #include statements.
+    ifstream in(get_pathname().c_str());
+    if (!in) {
+      // Can't read the file, or the file doesn't exist.  Interesting.
+      if (exists()) {
+	cerr << "Warning: dependent file " << get_pathname() 
+	     << " exists but cannot be read.\n";
+      } else {
+	cerr << "Warning: dependent file " << get_pathname() 
+	     << " does not exist.\n";
+      }
+      return (PPDependableFile *)NULL;
+    }
+    
+    PPDirectoryTree *tree = _directory->get_tree();
+    
+    bool okcircular = false;
+    string line;
+    getline(in, line);
+    while (!in.fail() && !in.eof()) {
+      if (line.substr(0, 16) == "/* okcircular */") {
+	okcircular = true;
+      } else {
+	string filename = check_include(line);
+	if (!filename.empty() && filename.find('/') == string::npos) {
+	  Dependency dep;
+	  dep._okcircular = okcircular;
+	  dep._file = tree->find_dependable_file(filename);
+	  if (dep._file != (PPDependableFile *)NULL) {
+	    // All right!  Here's a file we depend on.  Add it to the
+	    // list.
+	    _dependencies.push_back(dep);
+	    
+	  } else {
+	    // It's an include file from somewhere else, not from within
+	    // our source tree.  We don't care about it, but we do need
+	    // to record it so we can easily check later if the cache
+	    // file has gone stale.
+	    _extra_includes.push_back(filename);
+	  }
+	}
+	okcircular = false;
+      }
+      getline(in, line);
+    }
+  }
+
+  // Now recursively expand all our dependent files, so we can check
+  // for circularities.
+  PPDependableFile *circ = (PPDependableFile *)NULL;
+
+  Dependencies::iterator di;
+  for (di = _dependencies.begin(); 
+       di != _dependencies.end() && circ == (PPDependableFile *)NULL;
+       ++di) {
+    // Skip this file if the user specifically marked it
+    // with an "okcircular" comment.
+    if (!(*di)._okcircular) {
+      circ = (*di)._file->compute_dependencies(circularity);
+      if (circ != (PPDependableFile *)NULL) {
+	// Oops, a circularity.  Silly user.
+	circularity = get_dirpath() + " => " + circularity;
+	
+	if (circ == this) {
+	  _flags |= F_circularity;
+	  _circularity = circularity;
+	}
+      }
+    }
+  }
+
+
+  _flags = (_flags & ~F_updating) | F_updated;
+  return circ;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDependableFile::stat_file
+//       Access: Private
+//  Description: Performs a stat() on the file, if it has not already
+//               been performed, to check whether the file exists and
+//               to get its last-modification time.
+////////////////////////////////////////////////////////////////////
+void PPDependableFile::
+stat_file() {
+  if ((_flags & F_statted) != 0) {
+    // Already done.
+    return;
+  }
+
+  _flags |= F_statted;
+  struct stat st;
+  if (stat(get_pathname().c_str(), &st) < 0) {
+    // The file doesn't exist!
+    return;
+  }
+
+  if (!S_ISREG(st.st_mode)) {
+    // The file exists, but it's not a regular file--we consider that
+    // not existing.
+    return;
+  }
+
+  _flags |= F_exists;
+  _mtime = st.st_mtime;
+}

+ 83 - 0
ppremake/ppDependableFile.h

@@ -0,0 +1,83 @@
+// Filename: ppDependableFile.h
+// Created by:  drose (15Oct00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PPDEPENDABLEFILE_H
+#define PPDEPENDABLEFILE_H
+
+#include "ppremake.h"
+
+#include <set>
+#include <vector>
+
+class PPDirectory;
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : PPDependableFile
+// Description : Corresponds to a single C/C++ source file, either a
+//               .c file or a .h file, that can be scanned for a
+//               number of #include statements.  This file may both
+//               depend on other files, as well as being depended upon
+//               in turn.  This is used to resolved inter-file
+//               dependencies.
+////////////////////////////////////////////////////////////////////
+class PPDependableFile {
+public:
+  PPDependableFile(PPDirectory *directory, const string &filename);
+  void update_from_cache(const vector<string> &words);
+  void write_cache(ostream &out);
+
+  PPDirectory *get_directory() const;
+  const string &get_filename() const;
+  string get_pathname() const;
+  string get_dirpath() const;
+
+  bool exists();
+  time_t get_mtime();
+
+  int get_num_dependencies();
+  PPDependableFile *get_dependency(int n);
+
+  void get_complete_dependencies(set<PPDependableFile *> &files);
+
+  bool is_circularity();
+  string get_circularity();
+
+  bool was_examined() const;
+
+private:
+  void update_dependencies();
+  PPDependableFile *compute_dependencies(string &circularity);
+  void stat_file();
+
+  PPDirectory *_directory;
+  string _filename;
+
+  enum Flags {
+    F_updating    = 0x001,
+    F_updated     = 0x002,
+    F_circularity = 0x004,
+    F_statted     = 0x008,
+    F_exists      = 0x010,
+    F_from_cache  = 0x020,
+  };
+  int _flags;
+  string _circularity;
+  time_t _mtime;
+
+  class Dependency {
+  public:
+    PPDependableFile *_file;
+    bool _okcircular;
+  };
+
+  typedef vector<Dependency> Dependencies;
+  Dependencies _dependencies;
+
+  typedef vector<string> ExtraIncludes;
+  ExtraIncludes _extra_includes;
+};
+
+#endif
+  

+ 779 - 0
ppremake/ppDirectory.cxx

@@ -0,0 +1,779 @@
+// Filename: ppDirectory.cxx
+// Created by:  drose (28Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "ppDirectory.h"
+#include "ppDirectoryTree.h"
+#include "ppScope.h"
+#include "ppNamedScopes.h"
+#include "ppCommandFile.h"
+#include "ppDependableFile.h"
+#include "tokenize.h"
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <algorithm>
+
+PPDirectory *current_output_directory = (PPDirectory *)NULL;
+
+// An STL object to sort directories in order by dependency and then
+// by name, used in get_child_dirnames().
+class SortDirectoriesByDependencyAndName {
+public:
+  bool operator () (const PPDirectory *a, const PPDirectory *b) const {
+    if (a->get_depends_index() != b->get_depends_index()) {
+      return a->get_depends_index() < b->get_depends_index();
+    }
+    return a->get_dirname() < b->get_dirname();
+  }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::Constructor
+//       Access: Public
+//  Description: Creates the root directory.
+////////////////////////////////////////////////////////////////////
+PPDirectory::
+PPDirectory(PPDirectoryTree *tree) {
+  _scope = (PPScope *)NULL;
+  _source = (PPCommandFile *)NULL;
+  _parent = (PPDirectory *)NULL;
+  _tree = tree;
+  _depth = 0;
+  _depends_index = 0;
+  _computing_depends_index = false;
+
+  _dirname = "top";
+  _tree->_dirnames.insert(PPDirectoryTree::Dirnames::value_type(_dirname, this));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::Constructor
+//       Access: Public
+//  Description: Creates a new directory level that automatically adds
+//               itself to its parent's children list.
+////////////////////////////////////////////////////////////////////
+PPDirectory::
+PPDirectory(const string &dirname, PPDirectory *parent) :
+  _dirname(dirname),
+  _parent(parent)
+{
+  assert(_parent != (PPDirectory *)NULL);
+  _scope = (PPScope *)NULL;
+  _source = (PPCommandFile *)NULL;
+  _parent->_children.push_back(this);
+  _tree = _parent->_tree;
+  _depth = _parent->_depth + 1;
+  _depends_index = 0;
+  _computing_depends_index = false;
+
+  bool inserted = 
+    _tree->_dirnames.insert(PPDirectoryTree::Dirnames::value_type(_dirname, this)).second;
+  if (!inserted) {
+    cerr << "Warning: multiple directories encountered named "
+	 << _dirname << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::Destructor
+//       Access: Public
+//  Description: When a tree root destructs, all of its children are
+//               also destroyed.
+////////////////////////////////////////////////////////////////////
+PPDirectory::
+~PPDirectory() {
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    delete (*ci);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_tree
+//       Access: Public
+//  Description: Returns the PPDirectoryTree object corresponding to
+//               the source tree that this directory is a part of.
+////////////////////////////////////////////////////////////////////
+PPDirectoryTree *PPDirectory::
+get_tree() const {
+  return _tree;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::count_source_files
+//       Access: Public
+//  Description: Returns the number of directories within the tree
+//               that actually have a Sources.pp file that was read.
+////////////////////////////////////////////////////////////////////
+int PPDirectory::
+count_source_files() const {
+  int count = 0;
+  if (_source != (PPCommandFile *)NULL) {
+    count++;
+  }
+
+  Children::const_iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    count += (*ci)->count_source_files();
+  }
+
+  return count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_dirname
+//       Access: Public
+//  Description: Returns the name of this particular directory level.
+////////////////////////////////////////////////////////////////////
+const string &PPDirectory::
+get_dirname() const {
+  return _dirname;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_depends_index
+//       Access: Public
+//  Description: Returns the dependency index associated with this
+//               directory.  It is generally true that if directory A
+//               depends on B, then A.get_depends_index() >
+//               B.get_depends_index().
+////////////////////////////////////////////////////////////////////
+int PPDirectory::
+get_depends_index() const {
+  return _depends_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_path
+//       Access: Public
+//  Description: Returns the relative path from the root to this
+//               particular directory.  This does not include the root
+//               name itself, and does not include a trailing slash.
+////////////////////////////////////////////////////////////////////
+string PPDirectory::
+get_path() const {
+  if (_parent == (PPDirectory *)NULL) {
+    return ".";
+  }
+  if (_parent->_parent == (PPDirectory *)NULL) {
+    return _dirname;
+  }
+  return _parent->get_path() + "/" + _dirname;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_rel_to
+//       Access: Public
+//  Description: Returns the relative path to the other directory from
+//               this one.  This does not include a trailing slash.
+////////////////////////////////////////////////////////////////////
+string PPDirectory::
+get_rel_to(const PPDirectory *other) const {
+  const PPDirectory *a = this;
+  const PPDirectory *b = other;
+
+  if (a == b) {
+    return ".";
+  }
+
+  string prefix, postfix;
+  while (a->_depth > b->_depth) {
+    prefix += "../";
+    a = a->_parent;
+    assert(a != (PPDirectory *)NULL);
+  }
+
+  while (b->_depth > a->_depth) {
+    postfix = b->_dirname + "/" + postfix;
+    b = b->_parent;
+    assert(b != (PPDirectory *)NULL);
+  }
+
+  while (a != b) {
+    prefix += "../";
+    postfix = b->_dirname + "/" + postfix;
+    a = a->_parent;
+    b = b->_parent;
+    assert(a != (PPDirectory *)NULL);
+    assert(b != (PPDirectory *)NULL);
+  }
+
+  string result = prefix + postfix;
+  assert(!result.empty());
+  return result.substr(0, result.length() - 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_source
+//       Access: Public
+//  Description: Returns the source file associated with this level
+//               of the directory hierarchy.  This *might* be NULL.
+////////////////////////////////////////////////////////////////////
+PPCommandFile *PPDirectory::
+get_source() const {
+  return _source;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_num_children
+//       Access: Public
+//  Description: Returns the number of subdirectories below this
+//               level.
+////////////////////////////////////////////////////////////////////
+int PPDirectory::
+get_num_children() const {
+  return _children.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_child
+//       Access: Public
+//  Description: Returns the nth subdirectory below this level.
+////////////////////////////////////////////////////////////////////
+PPDirectory *PPDirectory::
+get_child(int n) const {
+  assert(n >= 0 && n < (int)_children.size());
+  return _children[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_child_dirnames
+//       Access: Public
+//  Description: Returns a single string listing the names of all the
+//               subdirectories of this level, delimited by spaces.
+//
+//               The list is sorted in dependency order such that a
+//               directory is listed after the other directories it
+//               might depend on.
+////////////////////////////////////////////////////////////////////
+string PPDirectory::
+get_child_dirnames() const {
+  Children copy_children = _children;
+  sort(copy_children.begin(), copy_children.end(),
+       SortDirectoriesByDependencyAndName());
+
+  vector<string> words;
+  Children::const_iterator ci;
+  for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
+    words.push_back((*ci)->get_dirname());
+  }
+
+  string result = repaste(words, " ");
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_complete_subtree
+//       Access: Public
+//  Description: Returns a single string listing the relative path
+//               from the source root to each source directory at this
+//               level and below, delimited by spaces.
+////////////////////////////////////////////////////////////////////
+string PPDirectory::
+get_complete_subtree() const {
+  Children copy_children = _children;
+  sort(copy_children.begin(), copy_children.end(),
+       SortDirectoriesByDependencyAndName());
+
+  vector<string> words;
+  words.push_back(get_path());
+
+  Children::const_iterator ci;
+  for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
+    words.push_back((*ci)->get_complete_subtree());
+  }
+
+  string result = repaste(words, " ");
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_dependable_file
+//       Access: Public
+//  Description: Returns a PPDependableFile object corresponding to
+//               the named filename, creating one if it does not
+//               already exist.  This can be used to determine the
+//               inter-file dependencies between source files.
+//
+//               If is_header is true, then the file will be added to
+//               the index at the top of the directory tree, so that
+//               other directories may include this file.  In this
+//               case, if the filename is not unique, a warning
+//               message will be issued.
+////////////////////////////////////////////////////////////////////
+PPDependableFile *PPDirectory::
+get_dependable_file(const string &filename, bool is_header) {
+  Dependables::iterator di;
+  di = _dependables.find(filename);
+  if (di != _dependables.end()) {
+    return (*di).second;
+  }
+
+  // No such file found; create a new definition.
+  PPDependableFile *dependable = new PPDependableFile(this, filename);
+  _dependables.insert(Dependables::value_type(filename, dependable));
+
+  if (is_header) {
+    bool unique = _tree->_dependables.insert
+      (PPDirectoryTree::Dependables::value_type(filename, dependable)).second;
+    
+    if (!unique) {
+      cerr << "Warning: source file " << dependable->get_pathname()
+	   << " may be confused with "
+	   << _tree->find_dependable_file(filename)->get_pathname()
+	   << ".\n";
+    }
+  }
+
+  return dependable;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::report_depends
+//       Access: Public
+//  Description: Reports all the directories that the current
+//               directory depends on.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+report_depends() const {
+  if (_i_depend_on.empty()) {
+    cerr << _dirname << " depends on no other directories.\n";
+
+  } else {
+    // Get the complete set of directories we depend on.
+    Depends dep;
+    get_complete_i_depend_on(dep);
+
+    // Copy the set into a vector, so we can sort it into a nice order
+    // for the user's pleasure.
+    vector<PPDirectory *> dirs;
+    copy(dep.begin(), dep.end(),
+	 back_insert_iterator<vector<PPDirectory *> >(dirs));
+    
+    sort(dirs.begin(), dirs.end(), SortDirectoriesByDependencyAndName());
+    
+    cerr << _dirname << " depends on the following directories:";
+    static const int max_col = 72;
+    int col = max_col;
+    vector<PPDirectory *>::const_iterator di;
+    for (di = dirs.begin(); di != dirs.end(); ++di) {
+      const string &dirname = (*di)->_dirname;
+      col += dirname.length() + 1;
+      if (col >= max_col) {
+	col = dirname.length() + 2;
+	cerr << "\n  " << dirname;
+      } else {
+	cerr << " " << dirname;
+      }
+    }
+    cerr << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::report_needs
+//       Access: Public
+//  Description: Reports all the directories that depend on (need) the
+//               current directory.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+report_needs() const {
+  if (_depends_on_me.empty()) {
+    cerr << _dirname << " is needed by no other directories.\n";
+
+  } else {
+    // Get the complete set of directories we depend on.
+    Depends dep;
+    get_complete_depends_on_me(dep);
+
+    // Copy the set into a vector, so we can sort it into a nice order
+    // for the user's pleasure.
+    vector<PPDirectory *> dirs;
+    copy(dep.begin(), dep.end(),
+	 back_insert_iterator<vector<PPDirectory *> >(dirs));
+    
+    sort(dirs.begin(), dirs.end(), SortDirectoriesByDependencyAndName());
+    
+    cerr << _dirname << " is needed by the following directories:";
+    static const int max_col = 72;
+    int col = max_col;
+    vector<PPDirectory *>::const_iterator di;
+    for (di = dirs.begin(); di != dirs.end(); ++di) {
+      const string &dirname = (*di)->_dirname;
+      col += dirname.length() + 1;
+      if (col >= max_col) {
+	col = dirname.length() + 2;
+	cerr << "\n  " << dirname;
+      } else {
+	cerr << " " << dirname;
+      }
+    }
+    cerr << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::r_scan
+//       Access: Private
+//  Description: The recursive implementation of scan_source().
+////////////////////////////////////////////////////////////////////
+bool PPDirectory::
+r_scan(const string &prefix) {
+  string root_name = ".";
+  if (!prefix.empty()) {
+    root_name = prefix.substr(0, prefix.length() - 1);
+  }
+  
+  DIR *root = opendir(root_name.c_str());
+  if (root == (DIR *)NULL) {
+    cerr << "Unable to scan directory " << root_name << "\n";
+    return false;
+  }
+
+  struct dirent *d;
+  d = readdir(root);
+  while (d != (struct dirent *)NULL) {
+    string filename = d->d_name;
+
+    if (!filename.empty() && filename[0] != '.') {
+      // Is this possibly a subdirectory with its own Sources.pp
+      // within it?
+      string next_prefix = prefix + filename + "/";
+      string source_filename = next_prefix + SOURCE_FILENAME;
+      if (access(source_filename.c_str(), F_OK) == 0) {
+	PPDirectory *subtree = new PPDirectory(filename, this);
+
+	if (!subtree->r_scan(next_prefix)) {
+	  return false;
+	}
+      }
+    }
+
+    d = readdir(root);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::read_source_file
+//       Access: Private
+//  Description: Recursively reads in the source file at each level,
+//               if defined.
+////////////////////////////////////////////////////////////////////
+bool PPDirectory::
+read_source_file(const string &prefix, PPNamedScopes *named_scopes) {
+  string source_filename = prefix + SOURCE_FILENAME;
+
+  ifstream in(source_filename.c_str());
+  if (in) {
+    //    cerr << "Reading " << source_filename << "\n";
+
+    named_scopes->set_current(_dirname);
+    _scope = named_scopes->make_scope("");
+    
+    _scope->define_variable("SOURCEFILE", SOURCE_FILENAME);
+    _scope->define_variable("DIRNAME", _dirname);
+    _scope->define_variable("DIRPREFIX", prefix);
+    _scope->define_variable("PATH", get_path());
+    _scope->define_variable("SUBDIRS", get_child_dirnames());
+    _scope->define_variable("SUBTREE", get_complete_subtree());
+    _scope->set_directory(this);
+    
+    _source = new PPCommandFile(_scope);
+    
+    if (!_source->read_stream(in)) {
+      cerr << "Error when reading " << source_filename << "\n";
+      return false;
+    }
+  }
+    
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    if (!(*ci)->read_source_file(prefix + (*ci)->get_dirname() + "/",
+				  named_scopes)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::read_depends_file
+//       Access: Private
+//  Description: Recursively reads in the dependency definition file
+//               for each source file.
+////////////////////////////////////////////////////////////////////
+bool PPDirectory::
+read_depends_file(PPNamedScopes *named_scopes) {
+  if (_scope != (PPScope *)NULL) {
+    // Read the depends file, so we can determine the relationship
+    // between this source file and all of the other source files.
+
+    string depends_filename = _scope->expand_variable("DEPENDS_FILE");
+    if (depends_filename.empty()) {
+      cerr << "No definition given for $[DEPENDS_FILE], cannot process.\n";
+      return false;
+    }
+    
+    named_scopes->set_current(_dirname);
+    PPCommandFile depends(_scope);
+    if (!depends.read_file(depends_filename)) {
+      cerr << "Error reading dependency definition file "
+	   << depends_filename << ".\n";
+      return false;
+    }
+    
+    // This should have defined the variable DEPEND_DIRS, which lists
+    // the various dirnames this source file depends on.
+
+    vector<string> dirnames;
+    tokenize_whitespace(_scope->expand_variable("DEPEND_DIRS"), dirnames);
+
+    vector<string>::const_iterator ni;
+    for (ni = dirnames.begin(); ni != dirnames.end(); ++ni) {
+      const string &dirname = (*ni);
+      PPDirectory *dir = _tree->find_dirname(dirname);
+      if (dir == (PPDirectory *)NULL) {
+	cerr << "Could not find dependent dirname " << dirname << "\n";
+      } else {
+	if (dir != this) {
+	  _i_depend_on.insert(dir);
+	  dir->_depends_on_me.insert(this);
+	}
+      }
+    }
+
+    // This may also have defined the variable DEPENDABLE_HEADERS,
+    // which lists the header files in this directory that C/C++
+    // source files in this and other directories might be including
+    // (and will therefore depend on).
+    vector<string> headers;
+    tokenize_whitespace(_scope->expand_variable("DEPENDABLE_HEADERS"), headers);
+    for (ni = headers.begin(); ni != headers.end(); ++ni) {
+      get_dependable_file(*ni, true);
+    }
+  }
+    
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    if (!(*ci)->read_depends_file(named_scopes)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::resolve_dependencies
+//       Access: Private
+//  Description: Visits each directory and assigns a correct
+//               _depends_index to each one, such that if directory A
+//               depends on directory B then A._depends_index >
+//               B._depends_index.
+//
+//               This also detects cycles in the directory dependency
+//               graph.
+////////////////////////////////////////////////////////////////////
+bool PPDirectory::
+resolve_dependencies() {
+  if (!compute_depends_index()) {
+    return false;
+  }
+
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    if (!(*ci)->resolve_dependencies()) {
+      return false;
+    }
+  }
+
+  // Now that we've resolved all of our children's dependencies,
+  // redefine our SUBDIRS and SUBTREE variables to put things in the
+  // right order.
+  if (_scope != (PPScope *)NULL) {
+    _scope->define_variable("SUBDIRS", get_child_dirnames());
+    _scope->define_variable("SUBTREE", get_complete_subtree());
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::compute_depends_index
+//       Access: Private
+//  Description: Computes the dependency score for a particular
+//               directory.  See resolve_dependencies().
+////////////////////////////////////////////////////////////////////
+bool PPDirectory::
+compute_depends_index() {
+  if (_depends_index != 0) {
+    return true;
+  }
+
+  if (_i_depend_on.empty()) {
+    _depends_index = 1;
+    return true;
+  }
+
+  _computing_depends_index = true;
+  int max_index = 0;
+
+  Depends::iterator di;
+  for (di = _i_depend_on.begin(); di != _i_depend_on.end(); ++di) {
+    if ((*di)->_computing_depends_index) {
+      // Oops, we have a cycle!
+      cerr << "Cycle detected in inter-directory dependencies!\n"
+	   << _dirname << " depends on " << (*di)->_dirname << "\n";
+      return false;
+    }
+      
+    if (!(*di)->compute_depends_index()) {
+      // Keep reporting the cycle as we unroll the recursion.
+      cerr << _dirname << " depends on " << (*di)->_dirname << "\n";
+      return false;
+    }
+
+    max_index = max(max_index, (*di)->_depends_index);
+  }
+
+  _computing_depends_index = false;
+  _depends_index = max_index + 1;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::read_file_dependencies
+//       Access: Private
+//  Description: Before processing the source files, makes a pass and
+//               reads in all of the dependency cache files so we'll
+//               have a heads-up on which files depend on the others.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+read_file_dependencies(const string &cache_filename) {
+  // Open up the dependency cache file in the directory.
+  string cache_pathname = get_path() + "/" + cache_filename;
+  ifstream in(cache_pathname.c_str());
+  if (!in) {
+    // Can't read it.  Maybe it's not there.  No problem.
+    return;
+  }
+
+  string line;
+  getline(in, line);
+  while (!in.fail() && !in.eof()) {
+    vector<string> words;
+    tokenize_whitespace(line, words);
+    if (words.size() >= 2) {
+      PPDependableFile *file = get_dependable_file(words[0], false);
+      file->update_from_cache(words);
+    }
+    getline(in, line);
+  }
+
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    (*ci)->read_file_dependencies(cache_filename);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::update_file_dependencies
+//       Access: Private
+//  Description: After all source processing has completed, makes one
+//               more pass through the directory hierarchy and writes
+//               out the inter-file dependency cache.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+update_file_dependencies(const string &cache_filename) {
+  // Open up the dependency cache file in the directory.
+  string cache_pathname = get_path() + "/" + cache_filename;
+  unlink(cache_pathname.c_str());
+
+  // If we have no files, don't bother writing the cache.
+  if (!_dependables.empty()) {
+    bool wrote_anything = false;
+
+    ofstream out(cache_pathname.c_str(), ios::out, 0666);
+    if (!out) {
+      cerr << "Cannot update cache dependency file " << cache_pathname << "\n";
+      return;
+    }
+    
+    // Walk through our list of dependable files, writing them out the
+    // the cache file.
+    Dependables::const_iterator di;
+    for (di = _dependables.begin(); di != _dependables.end(); ++di) {
+      PPDependableFile *file = (*di).second;
+      if (file->was_examined()) {
+	if (file->is_circularity()) {
+	  cerr << "Warning: circular #include directives:\n"
+	       << "  " << file->get_circularity() << "\n";
+	}
+	file->write_cache(out);
+	wrote_anything = true;
+      }
+    }
+
+    out.close();
+
+    if (!wrote_anything) {
+      // Well, if we didn't write anything, remove the cache file
+      // after all.
+      unlink(cache_pathname.c_str());
+    }
+  }
+
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    (*ci)->update_file_dependencies(cache_filename);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_complete_i_depend_on
+//       Access: Private
+//  Description: Gets the transitive closure of i_depend_on.  This
+//               fills the given set (which must have been empty
+//               before this call) with the complete set of all
+//               directories this directory depends on, directly or
+//               indirectly.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+get_complete_i_depend_on(Depends &dep) const {
+  Depends::const_iterator di;
+  for (di = _i_depend_on.begin(); di != _i_depend_on.end(); ++di) {
+    PPDirectory *dir = (*di);
+    bool inserted = dep.insert(dir).second;
+    if (inserted) {
+      dir->get_complete_i_depend_on(dep);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPDirectory::get_complete_depends_on_me
+//       Access: Private
+//  Description: Gets the transitive closure of depends_on_me.  This
+//               fills the given set (which must have been empty
+//               before this call) with the complete set of all
+//               directories this that depend on this directory,
+//               directly or indirectly.
+////////////////////////////////////////////////////////////////////
+void PPDirectory::
+get_complete_depends_on_me(Depends &dep) const {
+  Depends::const_iterator di;
+  for (di = _depends_on_me.begin(); di != _depends_on_me.end(); ++di) {
+    PPDirectory *dir = (*di);
+    bool inserted = dep.insert(dir).second;
+    if (inserted) {
+      dir->get_complete_depends_on_me(dep);
+    }
+  }
+}

+ 94 - 0
ppremake/ppDirectory.h

@@ -0,0 +1,94 @@
+// Filename: ppDirectory.h
+// Created by:  drose (28Sep00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PPDIRECTORY_H
+#define PPDIRECTORY_H
+
+#include "ppremake.h"
+
+#include <vector>
+#include <map>
+#include <set>
+
+class PPCommandFile;
+class PPScope;
+class PPNamedScopes;
+class PPDirectoryTree;
+class PPDependableFile;
+
+///////////////////////////////////////////////////////////////////
+// 	 Class : PPDirectory
+// Description : Represents a single directory in the source
+//               hierarchy.  Each PPDirectory object is one-to-one
+//               associated with a PPCommandFile object, that
+//               corresponds to the source file found within this
+//               directory.
+////////////////////////////////////////////////////////////////////
+class PPDirectory {
+public:
+  PPDirectory(PPDirectoryTree *tree);
+  PPDirectory(const string &dirname, PPDirectory *parent);
+  ~PPDirectory();
+
+  PPDirectoryTree *get_tree() const;
+  int count_source_files() const;
+
+  const string &get_dirname() const;
+  int get_depends_index() const;
+  string get_path() const;
+  string get_rel_to(const PPDirectory *other) const;
+
+  PPCommandFile *get_source() const;
+
+  int get_num_children() const;
+  PPDirectory *get_child(int n) const;
+
+  string get_child_dirnames() const;
+  string get_complete_subtree() const;
+
+  PPDependableFile *get_dependable_file(const string &filename, 
+					bool is_header);
+
+  void report_depends() const;
+  void report_needs() const;
+
+private:
+  typedef set<PPDirectory *> Depends;
+
+  bool r_scan(const string &prefix);
+  bool read_source_file(const string &prefix, PPNamedScopes *named_scopes);
+  bool read_depends_file(PPNamedScopes *named_scopes);
+  bool resolve_dependencies();
+  bool compute_depends_index();
+  void read_file_dependencies(const string &cache_filename);
+  void update_file_dependencies(const string &cache_filename);
+
+  void get_complete_i_depend_on(Depends &dep) const;
+  void get_complete_depends_on_me(Depends &dep) const;
+
+  string _dirname;
+  PPScope *_scope;
+  PPCommandFile *_source;
+  PPDirectory *_parent;
+  PPDirectoryTree *_tree;
+  typedef vector<PPDirectory *> Children;
+  Children _children;
+  int _depth;
+
+  Depends _i_depend_on;
+  Depends _depends_on_me;
+  int _depends_index;
+  bool _computing_depends_index;
+
+  typedef map<string, PPDependableFile *> Dependables;
+  Dependables _dependables;
+
+  friend class PPDirectoryTree;
+};
+
+extern PPDirectory *current_output_directory;
+
+#endif
+

+ 83 - 442
ppremake/ppDirectoryTree.cxx

@@ -4,89 +4,26 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "ppDirectoryTree.h"
 #include "ppDirectoryTree.h"
-#include "ppScope.h"
-#include "ppNamedScopes.h"
-#include "ppCommandFile.h"
-#include "tokenize.h"
-
-#include <sys/types.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <algorithm>
-
-PPDirectoryTree *current_output_directory = (PPDirectoryTree *)NULL;
-
-// An STL object to sort directories in order by dependency and then
-// by name, used in get_child_dirnames().
-class SortDirectoriesByDependencyAndName {
-public:
-  bool operator () (const PPDirectoryTree *a, const PPDirectoryTree *b) const {
-    if (a->get_depends_index() != b->get_depends_index()) {
-      return a->get_depends_index() < b->get_depends_index();
-    }
-    return a->get_dirname() < b->get_dirname();
-  }
-};
+#include "ppDirectory.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPDirectoryTree::Constructor
 //     Function: PPDirectoryTree::Constructor
 //       Access: Public
 //       Access: Public
-//  Description: Creates the root level of the PPDirectoryTree.
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PPDirectoryTree::
 PPDirectoryTree::
 PPDirectoryTree() {
 PPDirectoryTree() {
-  _scope = (PPScope *)NULL;
-  _source = (PPCommandFile *)NULL;
-  _parent = (PPDirectoryTree *)NULL;
-  _depth = 0;
-  _depends_index = 0;
-  _computing_depends_index = false;
-  _dirnames = new Dirnames;
-
-  _dirname = "top";
-  (*_dirnames).insert(Dirnames::value_type(_dirname, this));
+  _root = new PPDirectory(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPDirectoryTree::Destructor
 //     Function: PPDirectoryTree::Destructor
 //       Access: Public
 //       Access: Public
-//  Description: When a tree root destructs, all of its children are
-//               also destroyed.
+//  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PPDirectoryTree::
 PPDirectoryTree::
 ~PPDirectoryTree() {
 ~PPDirectoryTree() {
-  Children::iterator ci;
-  for (ci = _children.begin(); ci != _children.end(); ++ci) {
-    delete (*ci);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::Constructor
-//       Access: Protected
-//  Description: Creates a new directory level that automatically adds
-//               itself to its parent's children list.
-////////////////////////////////////////////////////////////////////
-PPDirectoryTree::
-PPDirectoryTree(const string &dirname, PPDirectoryTree *parent) :
-  _dirname(dirname),
-  _parent(parent)
-{
-  assert(_parent != (PPDirectoryTree *)NULL);
-  _scope = (PPScope *)NULL;
-  _source = (PPCommandFile *)NULL;
-  _parent->_children.push_back(this);
-  _depth = _parent->_depth + 1;
-  _depends_index = 0;
-  _computing_depends_index = false;
-  _dirnames = _parent->_dirnames;
-
-  bool inserted = 
-    (*_dirnames).insert(Dirnames::value_type(_dirname, this)).second;
-  if (!inserted) {
-    cerr << "Warning: multiple directories encountered named "
-	 << _dirname << "\n";
-  }
+  delete _root;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -97,11 +34,11 @@ PPDirectoryTree(const string &dirname, PPDirectoryTree *parent) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPDirectoryTree::
 bool PPDirectoryTree::
 scan_source(PPNamedScopes *named_scopes) {
 scan_source(PPNamedScopes *named_scopes) {
-  if (!r_scan("")) {
+  if (!_root->r_scan("")) {
     return false;
     return false;
   }
   }
 
 
-  if (!read_source_file("", named_scopes)) {
+  if (!_root->read_source_file("", named_scopes)) {
     return false;
     return false;
   }
   }
 
 
@@ -116,11 +53,11 @@ scan_source(PPNamedScopes *named_scopes) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPDirectoryTree::
 bool PPDirectoryTree::
 scan_depends(PPNamedScopes *named_scopes) {
 scan_depends(PPNamedScopes *named_scopes) {
-  if (!read_depends_file(named_scopes)) {
+  if (!_root->read_depends_file(named_scopes)) {
     return false;
     return false;
   }
   }
 
 
-  if (!resolve_dependencies()) {
+  if (!_root->resolve_dependencies()) {
     return false;
     return false;
   }
   }
 
 
@@ -135,426 +72,130 @@ scan_depends(PPNamedScopes *named_scopes) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int PPDirectoryTree::
 int PPDirectoryTree::
 count_source_files() const {
 count_source_files() const {
-  int count = 0;
-  if (_source != (PPCommandFile *)NULL) {
-    count++;
-  }
-
-  Children::const_iterator ci;
-  for (ci = _children.begin(); ci != _children.end(); ++ci) {
-    count += (*ci)->count_source_files();
-  }
-
-  return count;
+  return _root->count_source_files();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_dirname
+//     Function: PPDirectoryTree::get_root
 //       Access: Public
 //       Access: Public
-//  Description: Returns the name of this particular directory level.
+//  Description: Returns the root directory of the tree.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const string &PPDirectoryTree::
-get_dirname() const {
-  return _dirname;
+PPDirectory *PPDirectoryTree::
+get_root() const {
+  return _root;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_depends_index
+//     Function: PPDirectoryTree::get_complete_tree
 //       Access: Public
 //       Access: Public
-//  Description: Returns the dependency index associated with this
-//               directory.  It is generally true that if directory A
-//               depends on B, then A.get_depends_index() >
-//               B.get_depends_index().
-////////////////////////////////////////////////////////////////////
-int PPDirectoryTree::
-get_depends_index() const {
-  return _depends_index;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_path
-//       Access: Public
-//  Description: Returns the relative path from the root to this
-//               particular directory.  This does not include the root
-//               name itself, and does not include a trailing slash.
+//  Description: Returns a single string listing the relative path
+//               from the source root to each source directory in the
+//               tree, delimited by spaces.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPDirectoryTree::
 string PPDirectoryTree::
-get_path() const {
-  if (_parent == (PPDirectoryTree *)NULL) {
-    return ".";
-  }
-  if (_parent->_parent == (PPDirectoryTree *)NULL) {
-    return _dirname;
-  }
-  return _parent->get_path() + "/" + _dirname;
+get_complete_tree() const {
+  return _root->get_complete_subtree();
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_rel_to
-//       Access: Public
-//  Description: Returns the relative path to the other directory from
-//               this one.  This does not include a trailing slash.
-////////////////////////////////////////////////////////////////////
-string PPDirectoryTree::
-get_rel_to(const PPDirectoryTree *other) const {
-  const PPDirectoryTree *a = this;
-  const PPDirectoryTree *b = other;
-
-  if (a == b) {
-    return ".";
-  }
-
-  string prefix, postfix;
-  while (a->_depth > b->_depth) {
-    prefix += "../";
-    a = a->_parent;
-    assert(a != (PPDirectoryTree *)NULL);
-  }
-
-  while (b->_depth > a->_depth) {
-    postfix = b->_dirname + "/" + postfix;
-    b = b->_parent;
-    assert(b != (PPDirectoryTree *)NULL);
-  }
-
-  while (a != b) {
-    prefix += "../";
-    postfix = b->_dirname + "/" + postfix;
-    a = a->_parent;
-    b = b->_parent;
-    assert(a != (PPDirectoryTree *)NULL);
-    assert(b != (PPDirectoryTree *)NULL);
-  }
-
-  string result = prefix + postfix;
-  assert(!result.empty());
-  return result.substr(0, result.length() - 1);
-}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPDirectoryTree::find_dirname
 //     Function: PPDirectoryTree::find_dirname
 //       Access: Public
 //       Access: Public
-//  Description: Searches for the first subdirectory found with the
+//  Description: Searches for the a source directory with the
 //               matching dirname.  This is just the name of the
 //               matching dirname.  This is just the name of the
 //               directory itself, not the relative path to the
 //               directory itself, not the relative path to the
 //               directory.
 //               directory.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PPDirectoryTree *PPDirectoryTree::
-find_dirname(const string &dirname) {
-  assert(_dirnames != (Dirnames *)NULL);
+PPDirectory *PPDirectoryTree::
+find_dirname(const string &dirname) const {
   Dirnames::const_iterator di;
   Dirnames::const_iterator di;
-  di = _dirnames->find(dirname);
-  if (di != _dirnames->end()) {
+  di = _dirnames.find(dirname);
+  if (di != _dirnames.end()) {
     return (*di).second;
     return (*di).second;
   }
   }
 
 
   // No such dirname; too bad.
   // No such dirname; too bad.
-  return (PPDirectoryTree *)NULL;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_source
-//       Access: Public
-//  Description: Returns the source file associated with this level
-//               of the directory hierarchy.  This *might* be NULL.
-////////////////////////////////////////////////////////////////////
-PPCommandFile *PPDirectoryTree::
-get_source() const {
-  return _source;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_num_children
-//       Access: Public
-//  Description: Returns the number of subdirectories below this
-//               level.
-////////////////////////////////////////////////////////////////////
-int PPDirectoryTree::
-get_num_children() const {
-  return _children.size();
+  return (PPDirectory *)NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_child
+//     Function: PPDirectoryTree::find_dependable_file
 //       Access: Public
 //       Access: Public
-//  Description: Returns the nth subdirectory below this level.
-////////////////////////////////////////////////////////////////////
-PPDirectoryTree *PPDirectoryTree::
-get_child(int n) const {
-  assert(n >= 0 && n < (int)_children.size());
-  return _children[n];
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_child_dirnames
-//       Access: Public
-//  Description: Returns a single string listing the names of all the
-//               subdirectories of this level, delimited by spaces.
-//
-//               The list is sorted in dependency order such that a
-//               directory is listed after the other directories it
-//               might depend on.
-////////////////////////////////////////////////////////////////////
-string PPDirectoryTree::
-get_child_dirnames() const {
-  Children copy_children = _children;
-  sort(copy_children.begin(), copy_children.end(),
-       SortDirectoriesByDependencyAndName());
-
-  vector<string> words;
-  Children::const_iterator ci;
-  for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
-    words.push_back((*ci)->get_dirname());
+//  Description: Returns a PPDependableFile object corresponding to
+//               the named filename, searching all of the known source
+//               subdirectories.  This can only find files marked by a
+//               previous call to get_dependable_file() with is_header
+//               set to true.  Unlike
+//               get_dependable_file_by_pathname() or
+//               PPDirectory::get_dependable_file(), this does not
+//               create an entry if it does not exist; instead, it
+//               returns NULL if no matching file can be found.
+////////////////////////////////////////////////////////////////////
+PPDependableFile *PPDirectoryTree::
+find_dependable_file(const string &filename) const {
+  Dependables::const_iterator di;
+  di = _dependables.find(filename);
+  if (di != _dependables.end()) {
+    return (*di).second;
   }
   }
 
 
-  string result = repaste(words, " ");
-  return result;
+  return (PPDependableFile *)NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::get_complete_subtree
+//     Function: PPDirectoryTree::get_dependable_file_by_pathname
 //       Access: Public
 //       Access: Public
-//  Description: Returns a single string listing the relative path
-//               from the source root to each source directory at this
-//               level and below, delimited by spaces.
+//  Description: Given a dirname/filename for a particular dependable
+//               filename, return (or create and return) the
+//               corresponding PPDirectoryTree.  This is different
+//               from find_dependable_file() in that an explicit
+//               dirname is given, and the entry will be created if
+//               it does not already exist.  However, if the directory
+//               name does not exist, nothing is created, and NULL is
+//               returned.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-string PPDirectoryTree::
-get_complete_subtree() const {
-  Children copy_children = _children;
-  sort(copy_children.begin(), copy_children.end(),
-       SortDirectoriesByDependencyAndName());
-
-  vector<string> words;
-  words.push_back(get_path());
-
-  Children::const_iterator ci;
-  for (ci = copy_children.begin(); ci != copy_children.end(); ++ci) {
-    words.push_back((*ci)->get_complete_subtree());
+PPDependableFile *PPDirectoryTree::
+get_dependable_file_by_dirpath(const string &dirpath, bool is_header) {
+  size_t slash = dirpath.rfind('/');
+  if (slash == string::npos) {
+    // No valid directory name.
+    return (PPDependableFile *)NULL;
   }
   }
 
 
-  string result = repaste(words, " ");
-  return result;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::r_scan
-//       Access: Private
-//  Description: The recursive implementation of scan().
-////////////////////////////////////////////////////////////////////
-bool PPDirectoryTree::
-r_scan(const string &prefix) {
-  string root_name = ".";
-  if (!prefix.empty()) {
-    root_name = prefix.substr(0, prefix.length() - 1);
-  }
-  
-  DIR *root = opendir(root_name.c_str());
-  if (root == (DIR *)NULL) {
-    cerr << "Unable to scan directory " << root_name << "\n";
-    return false;
-  }
-
-  struct dirent *d;
-  d = readdir(root);
-  while (d != (struct dirent *)NULL) {
-    string dirname = d->d_name;
-
-    if (!dirname.empty() && dirname[0] != '.') {
-      // Do we have a source file in this subdirectory (if it is a
-      // subdirectory)?
-      string next_prefix = prefix + dirname + "/";
-      string source_filename = next_prefix + SOURCE_FILENAME;
-      if (access(source_filename.c_str(), F_OK) == 0) {
-	PPDirectoryTree *subtree = new PPDirectoryTree(dirname, this);
+  string dirname = dirpath.substr(0, slash);
+  string filename = dirpath.substr(slash + 1);
 
 
-	if (!subtree->r_scan(next_prefix)) {
-	  return false;
-	}
-      }
-    }
-
-    d = readdir(root);
+  PPDirectory *dir = find_dirname(dirname);
+  if (dir == (PPDirectory *)NULL) {
+    // No valid directory name.
+    return (PPDependableFile *)NULL;
   }
   }
 
 
-  return true;
+  return dir->get_dependable_file(filename, is_header);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::read_source_file
-//       Access: Private
-//  Description: Recursively reads in the source file at each level,
-//               if defined.
+//     Function: PPDirectoryTree::read_file_dependencies
+//       Access: Public
+//  Description: Before processing the source files, makes a pass and
+//               reads in all of the dependency cache files so we'll
+//               have a heads-up on which files depend on the others.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool PPDirectoryTree::
-read_source_file(const string &prefix, PPNamedScopes *named_scopes) {
-  string source_filename = prefix + SOURCE_FILENAME;
-
-  ifstream in(source_filename.c_str());
-  if (in) {
-    //    cerr << "Reading " << source_filename << "\n";
-
-    named_scopes->set_current(_dirname);
-    _scope = named_scopes->make_scope("");
-    
-    _scope->define_variable("SOURCEFILE", SOURCE_FILENAME);
-    _scope->define_variable("DIRNAME", _dirname);
-    _scope->define_variable("DIRPREFIX", prefix);
-    _scope->define_variable("PATH", get_path());
-    _scope->define_variable("SUBDIRS", get_child_dirnames());
-    _scope->define_variable("SUBTREE", get_complete_subtree());
-    _scope->set_directory(this);
-    
-    _source = new PPCommandFile(_scope);
-    
-    if (!_source->read_stream(in)) {
-      cerr << "Error when reading " << source_filename << "\n";
-      return false;
-    }
-  }
-    
-  Children::iterator ci;
-  for (ci = _children.begin(); ci != _children.end(); ++ci) {
-    if (!(*ci)->read_source_file(prefix + (*ci)->get_dirname() + "/",
-				  named_scopes)) {
-      return false;
-    }
-  }
-
-  return true;
+void PPDirectoryTree::
+read_file_dependencies(const string &cache_filename) {
+  _root->read_file_dependencies(cache_filename);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::read_depends_file
-//       Access: Private
-//  Description: Recursively reads in the dependency definition file
-//               for each source file.
-////////////////////////////////////////////////////////////////////
-bool PPDirectoryTree::
-read_depends_file(PPNamedScopes *named_scopes) {
-  if (_scope != (PPScope *)NULL) {
-    // Read the depends file, so we can determine the relationship
-    // between this source file and all of the other source files.
-
-    string depends_filename = _scope->expand_variable("DEPENDS_FILE");
-    if (depends_filename.empty()) {
-      cerr << "No definition given for $[DEPENDS_FILE], cannot process.\n";
-      return false;
-    }
-    
-    named_scopes->set_current(_dirname);
-    PPCommandFile depends(_scope);
-    if (!depends.read_file(depends_filename)) {
-      cerr << "Error reading dependency definition file "
-	   << depends_filename << ".\n";
-      return false;
-    }
-    
-    // This should have defined the variable DEPENDS, which lists the
-    // various dirnames this source file depends on.
-
-    vector<string> dirnames;
-    tokenize_whitespace(_scope->expand_variable("DEPENDS"), dirnames);
-
-    vector<string>::const_iterator ni;
-    for (ni = dirnames.begin(); ni != dirnames.end(); ++ni) {
-      const string &dirname = (*ni);
-      PPDirectoryTree *dir = find_dirname(dirname);
-      if (dir == (PPDirectoryTree *)NULL) {
-	cerr << "Could not find dependent dirname " << dirname << "\n";
-      } else {
-	if (dir != this) {
-	  _i_depend_on.insert(dir);
-	  dir->_depends_on_me.insert(this);
-	}
-      }
-    }
-  }
-    
-  Children::iterator ci;
-  for (ci = _children.begin(); ci != _children.end(); ++ci) {
-    if (!(*ci)->read_depends_file(named_scopes)) {
-      return false;
-    }
-  }
-
-  return true;
-}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::resolve_dependencies
-//       Access: Private
-//  Description: Visits each directory and assigns a correct
-//               _depends_index to each one, such that if directory A
-//               depends on directory B then A._depends_index >
-//               B._depends_index.
-//
-//               This also detects cycles in the directory dependency
-//               graph.
+//     Function: PPDirectoryTree::update_file_dependencies
+//       Access: Public
+//  Description: After all source processing has completed, makes one
+//               more pass through the directory hierarchy and writes
+//               out the inter-file dependency cache.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool PPDirectoryTree::
-resolve_dependencies() {
-  if (!compute_depends_index()) {
-    return false;
-  }
-
-  Children::iterator ci;
-  for (ci = _children.begin(); ci != _children.end(); ++ci) {
-    if (!(*ci)->resolve_dependencies()) {
-      return false;
-    }
-  }
-
-  // Now that we've resolved all of our children's dependencies,
-  // redefine our SUBDIRS and SUBTREE variables to put things in the
-  // right order.
-  if (_scope != (PPScope *)NULL) {
-    _scope->define_variable("SUBDIRS", get_child_dirnames());
-    _scope->define_variable("SUBTREE", get_complete_subtree());
-  }
-
-  return true;
+void PPDirectoryTree::
+update_file_dependencies(const string &cache_filename) {
+  _root->update_file_dependencies(cache_filename);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PPDirectoryTree::compute_depends_index
-//       Access: Private
-//  Description: Computes the dependency score for a particular
-//               directory.  See resolve_dependencies().
-////////////////////////////////////////////////////////////////////
-bool PPDirectoryTree::
-compute_depends_index() {
-  if (_depends_index != 0) {
-    return true;
-  }
-
-  if (_i_depend_on.empty()) {
-    _depends_index = 1;
-    return true;
-  }
-
-  _computing_depends_index = true;
-  int max_index = 0;
-
-  Depends::iterator di;
-  for (di = _i_depend_on.begin(); di != _i_depend_on.end(); ++di) {
-    if ((*di)->_computing_depends_index) {
-      // Oops, we have a cycle!
-      cerr << "Cycle detected in inter-directory dependencies!\n"
-	   << _dirname << " depends on " << (*di)->_dirname << "\n";
-      return false;
-    }
-      
-    if (!(*di)->compute_depends_index()) {
-      // Keep reporting the cycle as we unroll the recursion.
-      cerr << _dirname << " depends on " << (*di)->_dirname << "\n";
-      return false;
-    }
-
-    max_index = max(max_index, (*di)->_depends_index);
-  }
-
-  _computing_depends_index = false;
-  _depends_index = max_index + 1;
-
-  return true;
-}

+ 24 - 50
ppremake/ppDirectoryTree.h

@@ -1,5 +1,5 @@
 // Filename: ppDirectoryTree.h
 // Filename: ppDirectoryTree.h
-// Created by:  drose (28Sep00)
+// Created by:  drose (15Oct00)
 // 
 // 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
@@ -8,77 +8,51 @@
 
 
 #include "ppremake.h"
 #include "ppremake.h"
 
 
-#include <vector>
 #include <map>
 #include <map>
-#include <set>
 
 
-class PPCommandFile;
-class PPScope;
 class PPNamedScopes;
 class PPNamedScopes;
+class PPDirectory;
+class PPDependableFile;
 
 
 ///////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////
 // 	 Class : PPDirectoryTree
 // 	 Class : PPDirectoryTree
-// Description : Stores the directory hierarchy relationship of the
-//               source tree.  Each PPDirectoryTree object is
-//               one-to-one associated with a PPCommandFile object,
-//               that corresponds to the source file found within this
-//               directory.
+// Description : Stores the entire directory hierarchy relationship of the
+//               source tree.  This is the root of a tree of
+//               PPDirectory objects, each of which corresponds to a
+//               particular directory.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class PPDirectoryTree {
 class PPDirectoryTree {
 public:
 public:
   PPDirectoryTree();
   PPDirectoryTree();
   ~PPDirectoryTree();
   ~PPDirectoryTree();
 
 
-protected:
-  PPDirectoryTree(const string &dirname, PPDirectoryTree *parent);
-
-public:
   bool scan_source(PPNamedScopes *named_scopes);
   bool scan_source(PPNamedScopes *named_scopes);
   bool scan_depends(PPNamedScopes *named_scopes);
   bool scan_depends(PPNamedScopes *named_scopes);
 
 
   int count_source_files() const;
   int count_source_files() const;
+  PPDirectory *get_root() const;
 
 
-  const string &get_dirname() const;
-  int get_depends_index() const;
-  string get_path() const;
-  string get_rel_to(const PPDirectoryTree *other) const;
+  string get_complete_tree() const;
 
 
-  PPDirectoryTree *find_dirname(const string &dirname);
-  PPCommandFile *get_source() const;
+  PPDirectory *find_dirname(const string &dirname) const;
 
 
-  int get_num_children() const;
-  PPDirectoryTree *get_child(int n) const;
+  PPDependableFile *find_dependable_file(const string &filename) const;
+  PPDependableFile *get_dependable_file_by_dirpath(const string &dirpath,
+						   bool is_header);
 
 
-  string get_child_dirnames() const;
-  string get_complete_subtree() const;
+  void read_file_dependencies(const string &cache_filename);
+  void update_file_dependencies(const string &cache_filename);
 
 
 private:
 private:
-  bool r_scan(const string &prefix);
-  bool read_source_file(const string &prefix, PPNamedScopes *named_scopes);
-  bool read_depends_file(PPNamedScopes *named_scopes);
-  bool resolve_dependencies();
-  bool compute_depends_index();
-
-  string _dirname;
-  PPScope *_scope;
-  PPCommandFile *_source;
-  PPDirectoryTree *_parent;
-  typedef vector<PPDirectoryTree *> Children;
-  Children _children;
-  int _depth;
-
-  typedef set<PPDirectoryTree *> Depends;
-  Depends _i_depend_on;
-  Depends _depends_on_me;
-  int _depends_index;
-  bool _computing_depends_index;
-
-
-  typedef map<string, PPDirectoryTree *> Dirnames;
-  Dirnames *_dirnames;
-};
+  PPDirectory *_root;
 
 
-extern PPDirectoryTree *current_output_directory;
+  typedef map<string, PPDirectory *> Dirnames;
+  Dirnames _dirnames;
 
 
-#endif
+  typedef map<string, PPDependableFile *> Dependables;
+  Dependables _dependables;
 
 
+  friend class PPDirectory;
+};
+
+#endif

+ 56 - 6
ppremake/ppMain.cxx

@@ -6,6 +6,7 @@
 #include "ppMain.h"
 #include "ppMain.h"
 #include "ppScope.h"
 #include "ppScope.h"
 #include "ppCommandFile.h"
 #include "ppCommandFile.h"
+#include "ppDirectory.h"
 
 
 #include <unistd.h>
 #include <unistd.h>
 #include <assert.h>
 #include <assert.h>
@@ -97,7 +98,7 @@ read_source(const string &root) {
     return false;
     return false;
   }
   }
 
 
-  _def_scope->define_variable("TREE", _tree.get_complete_subtree());
+  _def_scope->define_variable("TREE", _tree.get_complete_tree());
 
 
   if (_tree.count_source_files() == 0) {
   if (_tree.count_source_files() == 0) {
     cerr << "Could not find any source definition files named " << SOURCE_FILENAME
     cerr << "Could not find any source definition files named " << SOURCE_FILENAME
@@ -131,7 +132,22 @@ read_source(const string &root) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPMain::
 bool PPMain::
 process_all() {
 process_all() {
-  return r_process_all(&_tree);
+  string cache_filename = _def_scope->expand_variable("DEPENDENCY_CACHE_FILENAME");
+
+  if (cache_filename.empty()) {
+    cerr << "Warning: no definition given for $[DEPENDENCY_CACHE_FILENAME].\n";
+  } else {
+    _tree.read_file_dependencies(cache_filename);
+  }
+
+  if (!r_process_all(_tree.get_root())) {
+    return false;
+  }
+
+  if (!cache_filename.empty()) {
+    _tree.update_file_dependencies(cache_filename);
+  }
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -144,8 +160,8 @@ process_all() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPMain::
 bool PPMain::
 process(const string &dirname) {
 process(const string &dirname) {
-  PPDirectoryTree *dir = _tree.find_dirname(dirname);
-  if (dir == (PPDirectoryTree *)NULL) {
+  PPDirectory *dir = _tree.find_dirname(dirname);
+  if (dir == (PPDirectory *)NULL) {
     cerr << "Unknown directory: " << dirname << "\n";
     cerr << "Unknown directory: " << dirname << "\n";
     return false;
     return false;
   }
   }
@@ -158,13 +174,47 @@ process(const string &dirname) {
   return p_process(dir);
   return p_process(dir);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPMain::report_depends
+//       Access: Public
+//  Description: Reports all the directories that the named directory
+//               depends on.
+////////////////////////////////////////////////////////////////////
+void PPMain::
+report_depends(const string &dirname) const {
+  PPDirectory *dir = _tree.find_dirname(dirname);
+  if (dir == (PPDirectory *)NULL) {
+    cerr << "Unknown directory: " << dirname << "\n";
+    return;
+  }
+
+  dir->report_depends();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPMain::report_needs
+//       Access: Public
+//  Description: Reports all the directories that depend on (need) the
+//               named directory.
+////////////////////////////////////////////////////////////////////
+void PPMain::
+report_needs(const string &dirname) const {
+  PPDirectory *dir = _tree.find_dirname(dirname);
+  if (dir == (PPDirectory *)NULL) {
+    cerr << "Unknown directory: " << dirname << "\n";
+    return;
+  }
+
+  dir->report_needs();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPMain::r_process_all
 //     Function: PPMain::r_process_all
 //       Access: Private
 //       Access: Private
 //  Description: The recursive implementation of process_all().
 //  Description: The recursive implementation of process_all().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPMain::
 bool PPMain::
-r_process_all(PPDirectoryTree *dir) {
+r_process_all(PPDirectory *dir) {
   if (dir->get_source() != (PPCommandFile *)NULL) {
   if (dir->get_source() != (PPCommandFile *)NULL) {
     if (!p_process(dir)) {
     if (!p_process(dir)) {
       return false;
       return false;
@@ -187,7 +237,7 @@ r_process_all(PPDirectoryTree *dir) {
 //  Description: The private implementation of process().
 //  Description: The private implementation of process().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PPMain::
 bool PPMain::
-p_process(PPDirectoryTree *dir) {
+p_process(PPDirectory *dir) {
   current_output_directory = dir;
   current_output_directory = dir;
   _named_scopes.set_current(dir->get_dirname());
   _named_scopes.set_current(dir->get_dirname());
   PPCommandFile *source = dir->get_source();
   PPCommandFile *source = dir->get_source();

+ 5 - 2
ppremake/ppMain.h

@@ -29,9 +29,12 @@ public:
   bool process_all();
   bool process_all();
   bool process(const string &dirname);
   bool process(const string &dirname);
 
 
+  void report_depends(const string &dirname) const;
+  void report_needs(const string &dirname) const;
+
 private:
 private:
-  bool r_process_all(PPDirectoryTree *dir);
-  bool p_process(PPDirectoryTree *dir);
+  bool r_process_all(PPDirectory *dir);
+  bool p_process(PPDirectory *dir);
   bool read_global_file();
   bool read_global_file();
   static string get_cwd();
   static string get_cwd();
 
 

+ 7 - 7
ppremake/ppNamedScopes.cxx

@@ -5,7 +5,7 @@
 
 
 #include "ppNamedScopes.h"
 #include "ppNamedScopes.h"
 #include "ppScope.h"
 #include "ppScope.h"
-#include "ppDirectoryTree.h"
+#include "ppDirectory.h"
 
 
 #include <assert.h>
 #include <assert.h>
 #include <algorithm>
 #include <algorithm>
@@ -15,12 +15,12 @@
 class SortScopesByDependencyAndName {
 class SortScopesByDependencyAndName {
 public:
 public:
   bool operator () (const PPScope *a, const PPScope *b) const {
   bool operator () (const PPScope *a, const PPScope *b) const {
-    PPDirectoryTree *da = a->get_directory();
-    PPDirectoryTree *db = b->get_directory();
+    PPDirectory *da = a->get_directory();
+    PPDirectory *db = b->get_directory();
 
 
     // Scopes without associated directories appear first in the list.
     // Scopes without associated directories appear first in the list.
-    bool da_is_null = (da == (PPDirectoryTree *)NULL);
-    bool db_is_null = (db == (PPDirectoryTree *)NULL);
+    bool da_is_null = (da == (PPDirectory *)NULL);
+    bool db_is_null = (db == (PPDirectory *)NULL);
 
 
     if (da_is_null != db_is_null) {
     if (da_is_null != db_is_null) {
       return da_is_null > db_is_null;
       return da_is_null > db_is_null;
@@ -33,8 +33,8 @@ public:
     } else {
     } else {
       // Otherwise, both scopes have associated directories, and we
       // Otherwise, both scopes have associated directories, and we
       // can properly put them in order by dependencies.
       // can properly put them in order by dependencies.
-      assert(da != (PPDirectoryTree *)NULL);
-      assert(db != (PPDirectoryTree *)NULL);
+      assert(da != (PPDirectory *)NULL);
+      assert(db != (PPDirectory *)NULL);
       if (da->get_depends_index() != db->get_depends_index()) {
       if (da->get_depends_index() != db->get_depends_index()) {
 	return da->get_depends_index() < db->get_depends_index();
 	return da->get_depends_index() < db->get_depends_index();
       }
       }

+ 231 - 17
ppremake/ppScope.cxx

@@ -6,9 +6,10 @@
 #include "ppScope.h"
 #include "ppScope.h"
 #include "ppNamedScopes.h"
 #include "ppNamedScopes.h"
 #include "ppFilenamePattern.h"
 #include "ppFilenamePattern.h"
-#include "ppDirectoryTree.h"
+#include "ppDirectory.h"
 #include "ppSubroutine.h"
 #include "ppSubroutine.h"
 #include "ppCommandFile.h"
 #include "ppCommandFile.h"
+#include "ppDependableFile.h"
 #include "tokenize.h"
 #include "tokenize.h"
 #include "find_searchpath.h"
 #include "find_searchpath.h"
 
 
@@ -23,6 +24,7 @@
 #include <signal.h>
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
+#include <assert.h>
 
 
 static const string variable_patsubst(VARIABLE_PATSUBST);
 static const string variable_patsubst(VARIABLE_PATSUBST);
 
 
@@ -39,7 +41,7 @@ PPScope::
 PPScope(PPNamedScopes *named_scopes) : 
 PPScope(PPNamedScopes *named_scopes) : 
   _named_scopes(named_scopes)
   _named_scopes(named_scopes)
 {
 {
-  _directory = (PPDirectoryTree *)NULL;
+  _directory = (PPDirectory *)NULL;
   _parent_scope = (PPScope *)NULL;
   _parent_scope = (PPScope *)NULL;
 }
 }
 
 
@@ -368,21 +370,21 @@ find_map_variable(const string &varname) const {
 //               scope, if any, or with the nearest parent to this
 //               scope, if any, or with the nearest parent to this
 //               scope.
 //               scope.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PPDirectoryTree *PPScope::
+PPDirectory *PPScope::
 get_directory() const {
 get_directory() const {
-  if (_directory != (PPDirectoryTree *)NULL) {
+  if (_directory != (PPDirectory *)NULL) {
     return _directory;
     return _directory;
   }
   }
 
 
   // Check the stack.
   // Check the stack.
   ScopeStack::reverse_iterator si;
   ScopeStack::reverse_iterator si;
   for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
   for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
-    if ((*si)->_directory != (PPDirectoryTree *)NULL) {
+    if ((*si)->_directory != (PPDirectory *)NULL) {
       return (*si)->_directory;
       return (*si)->_directory;
     }
     }
   }
   }
 
 
-  return (PPDirectoryTree *)NULL;
+  return (PPDirectory *)NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -394,7 +396,7 @@ get_directory() const {
 //               known directory level.
 //               known directory level.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPScope::
 void PPScope::
-set_directory(PPDirectoryTree *directory) {
+set_directory(PPDirectory *directory) {
   _directory = directory;
   _directory = directory;
 }
 }
 
 
@@ -545,6 +547,44 @@ tokenize_params(const string &str, vector<string> &tokens,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::tokenize_numeric_pair
+//       Access: Public
+//  Description: This function is used by all the numeric comparision
+//               functions, e.g. nne, nlt, etc.  It splits the string
+//               up into two parameters based on commas, and evaluates
+//               each parameter as a number, into a and b.  It returns
+//               true if successful, or false if there was some user
+//               error.
+////////////////////////////////////////////////////////////////////
+bool PPScope::
+tokenize_numeric_pair(const string &str, double &a, double &b) const {
+  vector<string> words;
+  tokenize_params(str, words, true);
+  if (words.size() != 2) {
+    cerr << words.size() << " parameters supplied when two were expected:\n"
+	 << str << "\n";
+    return false;
+  }
+
+  double results[2];
+
+  for (int i = 0; i < 2; i++) {
+    const char *param = words[i].c_str();
+    char *n;
+    results[i] = strtod(param, &n);
+    if (n == param) {
+      // strtod failed--not a numeric representation.
+      cerr << "Warning: " << words[i] << " is not a number.\n";
+      results[i] = 0.0;
+    }
+  }
+
+  a = results[0];
+  b = results[1];
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::p_set_variable
 //     Function: PPScope::p_set_variable
 //       Access: Private
 //       Access: Private
@@ -586,8 +626,8 @@ p_get_variable(const string &varname, string &result) const {
   }
   }
 
 
   if (varname == "RELDIR" && 
   if (varname == "RELDIR" && 
-      _directory != (PPDirectoryTree *)NULL &&
-      current_output_directory != (PPDirectoryTree *)NULL) {
+      _directory != (PPDirectory *)NULL &&
+      current_output_directory != (PPDirectory *)NULL) {
     // $[RELDIR] is a special variable name that evaluates to the
     // $[RELDIR] is a special variable name that evaluates to the
     // relative directory of the current scope to the current output
     // relative directory of the current scope to the current output
     // directory.
     // directory.
@@ -596,7 +636,7 @@ p_get_variable(const string &varname, string &result) const {
   }
   }
 
 
   if (varname == "DEPENDS_INDEX" && 
   if (varname == "DEPENDS_INDEX" && 
-      _directory != (PPDirectoryTree *)NULL) {
+      _directory != (PPDirectory *)NULL) {
     // $[DEPENDS_INDEX] is another special variable name that
     // $[DEPENDS_INDEX] is another special variable name that
     // evaluates to the numeric sorting index assigned to this
     // evaluates to the numeric sorting index assigned to this
     // directory based on its dependency relationship with other
     // directory based on its dependency relationship with other
@@ -797,6 +837,18 @@ r_expand_variable(const string &str, size_t &vp,
       return expand_eq(params);
       return expand_eq(params);
     } else if (funcname == "ne") {
     } else if (funcname == "ne") {
       return expand_ne(params);
       return expand_ne(params);
+    } else if (funcname == "=" || funcname == "==") {
+      return expand_eqn(params);
+    } else if (funcname == "!=") {
+      return expand_nen(params);
+    } else if (funcname == "<") {
+      return expand_ltn(params);
+    } else if (funcname == "<=") {
+      return expand_len(params);
+    } else if (funcname == ">") {
+      return expand_gtn(params);
+    } else if (funcname == ">=") {
+      return expand_gen(params);
     } else if (funcname == "not") {
     } else if (funcname == "not") {
       return expand_not(params);
       return expand_not(params);
     } else if (funcname == "or") {
     } else if (funcname == "or") {
@@ -813,6 +865,8 @@ r_expand_variable(const string &str, size_t &vp,
       return expand_closure(params);
       return expand_closure(params);
     } else if (funcname == "unmapped") {
     } else if (funcname == "unmapped") {
       return expand_unmapped(params);
       return expand_unmapped(params);
+    } else if (funcname == "dependencies") {
+      return expand_dependencies(params);
     }
     }
 
 
     // It must be a map variable.
     // It must be a map variable.
@@ -1665,7 +1719,8 @@ expand_if(const string &params) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_eq
 //     Function: PPScope::expand_eq
 //       Access: Private
 //       Access: Private
-//  Description: Expands the "eq" function variable.
+//  Description: Expands the "eq" function variable.  This tests
+//               string equivalence.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_eq(const string &params) const {
 expand_eq(const string &params) const {
@@ -1689,7 +1744,8 @@ expand_eq(const string &params) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_ne
 //     Function: PPScope::expand_ne
 //       Access: Private
 //       Access: Private
-//  Description: Expands the "ne" function variable.
+//  Description: Expands the "ne" function variable.  This tests
+//               string equivalence.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_ne(const string &params) const {
 expand_ne(const string &params) const {
@@ -1710,6 +1766,126 @@ expand_ne(const string &params) const {
   return result;
   return result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_eqn
+//       Access: Private
+//  Description: Expands the "=" function variable.  This tests
+//               numeric equivalence.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_eqn(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a == b) {
+    result = "1";
+  }
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_nen
+//       Access: Private
+//  Description: Expands the "!=" function variable.  This tests
+//               numeric equivalence.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_nen(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a != b) {
+    result = "1";
+  }
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_ltn
+//       Access: Private
+//  Description: Expands the "<" function variable.  This tests
+//               numeric relationships.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_ltn(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a < b) {
+    result = "1";
+  }
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_len
+//       Access: Private
+//  Description: Expands the "<=" function variable.  This tests
+//               numeric relationships.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_len(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a <= b) {
+    result = "1";
+  }
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_gtn
+//       Access: Private
+//  Description: Expands the ">" function variable.  This tests
+//               numeric relationships.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_gtn(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a > b) {
+    result = "1";
+  }
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_gen
+//       Access: Private
+//  Description: Expands the ">=" function variable.  This tests
+//               numeric relationships.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_gen(const string &params) const {
+  double a, b;
+  if (!tokenize_numeric_pair(params, a, b)) {
+    return string();
+  }
+
+  string result;
+  if (a >= b) {
+    result = "1";
+  }
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_not
 //     Function: PPScope::expand_not
 //       Access: Private
 //       Access: Private
@@ -1724,7 +1900,7 @@ expand_not(const string &params) const {
   tokenize_params(params, tokens, true);
   tokenize_params(params, tokens, true);
 
 
   if (tokens.size() != 1) {
   if (tokens.size() != 1) {
-    cerr << "not requires two parameters.\n";
+    cerr << "not requires one parameter.\n";
     return string();
     return string();
   }
   }
 
 
@@ -1778,11 +1954,12 @@ expand_and(const string &params) const {
     }
     }
   }
   }
 
 
-  if (tokens.empty()) {
-    return "1";
-  } else {
-    return tokens.back();
+  string result = "1";
+  if (!tokens.empty()) {
+    result = tokens.back();
   }
   }
+
+  return result;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1974,6 +2151,43 @@ expand_unmapped(const string &params) const {
   return result;
   return result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPScope::expand_dependencies
+//       Access: Private
+//  Description: Expands the "dependencies" function variable.  This
+//               function returns all of the inter-file dependencies
+//               that the named file(s) depend on, as defined by the
+//               #include directives appearing within the files.
+////////////////////////////////////////////////////////////////////
+string PPScope::
+expand_dependencies(const string &params) const {
+  // Split the string up into filenames based on whitespace.
+  vector<string> filenames;
+  tokenize_whitespace(expand_string(params), filenames);
+
+  vector<string> results;
+  vector<string>::const_iterator fi;
+  for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
+    PPDependableFile *file = _directory->get_dependable_file(*fi, false);
+    assert(file != (PPDependableFile *)NULL);
+
+    set<PPDependableFile *> files;
+    file->get_complete_dependencies(files);
+
+    set<PPDependableFile *>::const_iterator dfi;
+    for (dfi = files.begin(); dfi != files.end(); ++dfi) {
+      PPDependableFile *df = (*dfi);
+      string rel_filename =
+	current_output_directory->get_rel_to(df->get_directory()) + "/" +
+	df->get_filename();
+      results.push_back(rel_filename);
+    }
+  }
+
+  string result = repaste(results, " ");
+  return result;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_function
 //     Function: PPScope::expand_function
 //       Access: Private
 //       Access: Private

+ 12 - 4
ppremake/ppScope.h

@@ -12,7 +12,7 @@
 #include <vector>
 #include <vector>
 
 
 class PPNamedScopes;
 class PPNamedScopes;
-class PPDirectoryTree;
+class PPDirectory;
 class PPSubroutine;
 class PPSubroutine;
 
 
 ///////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////
@@ -47,8 +47,8 @@ public:
   string expand_variable(const string &varname) const;
   string expand_variable(const string &varname) const;
   MapVariableDefinition &find_map_variable(const string &varname) const;
   MapVariableDefinition &find_map_variable(const string &varname) const;
 
 
-  PPDirectoryTree *get_directory() const;
-  void set_directory(PPDirectoryTree *directory);
+  PPDirectory *get_directory() const;
+  void set_directory(PPDirectory *directory);
 
 
   string expand_string(const string &str) const;
   string expand_string(const string &str) const;
   string expand_self_reference(const string &str, const string &varname) const;
   string expand_self_reference(const string &str, const string &varname) const;
@@ -59,6 +59,7 @@ public:
 
 
   void tokenize_params(const string &str, vector<string> &tokens,
   void tokenize_params(const string &str, vector<string> &tokens,
 		       bool expand) const;
 		       bool expand) const;
+  bool tokenize_numeric_pair(const string &str, double &a, double &b) const;
 
 
   static MapVariableDefinition _null_map_def;
   static MapVariableDefinition _null_map_def;
 
 
@@ -96,6 +97,12 @@ private:
   string expand_if(const string &params) const;
   string expand_if(const string &params) const;
   string expand_eq(const string &params) const;
   string expand_eq(const string &params) const;
   string expand_ne(const string &params) const;
   string expand_ne(const string &params) const;
+  string expand_eqn(const string &params) const;
+  string expand_nen(const string &params) const;
+  string expand_ltn(const string &params) const;
+  string expand_len(const string &params) const;
+  string expand_gtn(const string &params) const;
+  string expand_gen(const string &params) const;
   string expand_not(const string &params) const;
   string expand_not(const string &params) const;
   string expand_or(const string &params) const;
   string expand_or(const string &params) const;
   string expand_and(const string &params) const;
   string expand_and(const string &params) const;
@@ -104,6 +111,7 @@ private:
   string expand_cdefine(const string &params) const;
   string expand_cdefine(const string &params) const;
   string expand_closure(const string &params) const;
   string expand_closure(const string &params) const;
   string expand_unmapped(const string &params) const;
   string expand_unmapped(const string &params) const;
+  string expand_dependencies(const string &params) const;
   string expand_function(const string &funcname, const PPSubroutine *sub,
   string expand_function(const string &funcname, const PPSubroutine *sub,
 			 const string &params) const;
 			 const string &params) const;
   string expand_map_variable(const string &varname, const string &params) const;
   string expand_map_variable(const string &varname, const string &params) const;
@@ -117,7 +125,7 @@ private:
 
 
   PPNamedScopes *_named_scopes;
   PPNamedScopes *_named_scopes;
 
 
-  PPDirectoryTree *_directory;
+  PPDirectory *_directory;
 
 
   typedef map<string, string> Variables;
   typedef map<string, string> Variables;
   Variables _variables;
   Variables _variables;

+ 188 - 13
ppremake/ppremake.cxx

@@ -6,6 +6,8 @@
 #include "ppremake.h"
 #include "ppremake.h"
 #include "ppMain.h"
 #include "ppMain.h"
 #include "ppScope.h"
 #include "ppScope.h"
+#include "check_include.h"
+#include "tokenize.h"
 
 
 #ifdef HAVE_GETOPT
 #ifdef HAVE_GETOPT
 #include <getopt.h>
 #include <getopt.h>
@@ -13,6 +15,12 @@
 #include <gnu_getopt.h>
 #include <gnu_getopt.h>
 #endif
 #endif
 
 
+#include <set>
+#include <vector>
+#include <algorithm>
+#include <sys/stat.h>
+#include <unistd.h>
+
 static void
 static void
 usage() {
 usage() {
   cerr <<
   cerr <<
@@ -20,16 +28,16 @@ usage() {
     "ppremake [opts] subdir-name [subdir-name..]\n"
     "ppremake [opts] subdir-name [subdir-name..]\n"
     "ppremake\n"
     "ppremake\n"
     "\n"
     "\n"
-    "This is Panda pre-make: a script preprocessor that scans the directory\n"
-    "hierarchy beginning at root-directory, looking for directories that\n"
-    "contain a file called " SOURCE_FILENAME ".  At the top of the directory\n"
-    "tree must be a file called " PACKAGE_FILENAME ", which should define\n"
+    "This is Panda pre-make: a script preprocessor that scans the source\n"
+    "directory hierarchy containing the current directory, looking for\n"
+    "directories that contain a file called " SOURCE_FILENAME ".  At the top of the\n"
+    "directory tree must be a file called " PACKAGE_FILENAME ", which should define\n"
     "key variable definitions for processing, as well as pointing out the\n"
     "key variable definitions for processing, as well as pointing out the\n"
     "locations of further config files.\n\n"
     "locations of further config files.\n\n"
 
 
     "The package file is read and interpreted, followed by each source file\n"
     "The package file is read and interpreted, followed by each source file\n"
     "in turn; after each source file is read, the template file (specified in\n"
     "in turn; after each source file is read, the template file (specified in\n"
-    "the config file) is read.  The template file  contains the actual statements\n"
+    "the config file) is read.  The template file contains the actual statements\n"
     "to be output and will typically be set up to generate Makefiles or whatever\n"
     "to be output and will typically be set up to generate Makefiles or whatever\n"
     "is equivalent and appropriate to the particular build environment in use.\n\n"
     "is equivalent and appropriate to the particular build environment in use.\n\n"
 
 
@@ -45,6 +53,16 @@ usage() {
     "  -h           Display this page.\n"
     "  -h           Display this page.\n"
     "  -V           Report the version of ppremake, and exit.\n"
     "  -V           Report the version of ppremake, and exit.\n"
     "  -P           Report the current platform name, and exit.\n\n"
     "  -P           Report the current platform name, and exit.\n\n"
+    "  -D pp.dep    Examine the given dependency file, and re-run ppremake\n"
+    "               only if the dependency file is stale.\n\n"
+    "  -d           Instead of generating makefiles, report the set of\n"
+    "               subdirectories that the named subdirectory depends on.\n"
+    "               Directories are named by their local name, not by the\n"
+    "               path to them; e.g. util instead of src/util.\n"
+    "  -n           As above, but report the set of subdirectories that\n"
+    "               depend on (need) the named subdirectory.  Options -d and\n"
+    "               -n may be combined, and you may also name multiple\n"
+    "               subdirectories to scan at once.\n\n"
     "  -p platform  Build as if for the indicated platform name.\n\n";
     "  -p platform  Build as if for the indicated platform name.\n\n";
 }
 }
 
 
@@ -58,13 +76,122 @@ report_platform() {
   cerr << "ppremake built for platform " << PLATFORM << ".\n";
   cerr << "ppremake built for platform " << PLATFORM << ".\n";
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: check_one_file
+//  Description: Checks a single file listed in the dependency cache
+//               to see if it matches the cache.  Returns true if it
+//               does, false if it does not.
+////////////////////////////////////////////////////////////////////
+static bool
+check_one_file(const string &dir_prefix, const vector<string> &words) {
+  assert(words.size() >= 2);
+
+  string pathname = dir_prefix + words[0];
+  time_t mtime = strtol(words[1].c_str(), (char **)NULL, 10);
+
+  struct stat st;
+  if (stat(pathname.c_str(), &st) < 0) {
+    // The file doesn't even exist!
+    return false;
+  }
+
+  if (st.st_mtime == mtime) {
+    // The modification time matches; don't bother to read the file.
+    return true;
+  }
+
+  // The modification time doesn't match, so we'll need to read the
+  // file and look for #include directives.  First, get the complete
+  // set of files we're expecting to find.
+
+  set<string> expected_files;
+  for (int i = 2; i < (int)words.size(); i++) {
+    const string &word = words[i];
+    size_t slash = word.rfind('/');
+    if (slash == string::npos) {
+      // Every file is expected to include a slash.
+      return false;
+    }
+    expected_files.insert(word.substr(slash + 1));
+  }
+
+  // Now open the source file and read it for #include directives.
+  set<string> found_files;
+  ifstream in(pathname.c_str());
+  if (!in) {
+    // Can't read the file for some reason.
+    return false;
+  }
+
+  string line;
+  getline(in, line);
+  while (!in.fail() && !in.eof()) {
+    string filename = check_include(line);
+    if (!filename.empty() && filename.find('/') == string::npos) {
+      found_files.insert(filename);
+    }
+    getline(in, line);
+  }
+  
+  // Now check that the two sets are equivalent.
+  return (expected_files == found_files);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: check_dependencies
+//  Description: Read in the indicated dependency cache file,
+//               verifying that it is still current.  If it is stale,
+//               return false; otherwise, return true.
+////////////////////////////////////////////////////////////////////
+static bool
+check_dependencies(const string &dep_filename) {
+  // Extract the directory prefix from the dependency filename.  This
+  // is everything up until (and including) the last slash.
+  string dir_prefix;
+  size_t slash = dep_filename.rfind('/');
+  if (slash != string::npos) {
+    dir_prefix = dep_filename.substr(0, slash + 1);
+  }
+
+  ifstream in(dep_filename.c_str());
+  if (!in) {
+    // The dependency filename doesn't even exist: it's definitely
+    // stale.
+    return false;
+  }
+
+  string line;
+  getline(in, line);
+  while (!in.fail() && !in.eof()) {
+    vector<string> words;
+    tokenize_whitespace(line, words);
+    if (words.size() < 2) {
+      // Invalid file; return false.
+      return false;
+    }
+    if (!check_one_file(dir_prefix, words)) {
+      // This file is stale; return false.
+      return false;
+    }
+    getline(in, line);
+  }
+
+  // All files are ok; return true.
+  return true;
+}
+
 int
 int
 main(int argc, char *argv[]) {
 main(int argc, char *argv[]) {
+  string progname = argv[0];
   extern char *optarg;
   extern char *optarg;
   extern int optind;
   extern int optind;
-  const char *optstr = "hVPp:";
+  const char *optstr = "hVPD:p:dn";
 
 
+  bool any_d = false;
+  bool dependencies_stale = false;
   string platform = PLATFORM;
   string platform = PLATFORM;
+  bool report_depends = false;
+  bool report_needs = false;
   int flag = getopt(argc, argv, optstr);
   int flag = getopt(argc, argv, optstr);
 
 
   while (flag != EOF) {
   while (flag != EOF) {
@@ -83,10 +210,25 @@ main(int argc, char *argv[]) {
       exit(0);
       exit(0);
       break;
       break;
 
 
+    case 'D':
+      if (!check_dependencies(optarg)) {
+	dependencies_stale = true;
+      }
+      any_d = true;
+      break;
+
     case 'p':
     case 'p':
       platform = optarg;
       platform = optarg;
       break;
       break;
 
 
+    case 'd':
+      report_depends = true;
+      break;
+
+    case 'n':
+      report_needs = true;
+      break;
+
     default:
     default:
       exit(1);
       exit(1);
     }
     }
@@ -96,26 +238,59 @@ main(int argc, char *argv[]) {
   argc -= (optind-1);
   argc -= (optind-1);
   argv += (optind-1);
   argv += (optind-1);
 
 
+  // If the user supplied one or more -d parameters, then we should
+  // not continue unless some of the dependencies were stale.
+  if (any_d) {
+    if (!dependencies_stale) {
+      exit(0);
+    }
+    cout << progname << "\n";
+  }
+
   PPScope global_scope((PPNamedScopes *)NULL);
   PPScope global_scope((PPNamedScopes *)NULL);
-  global_scope.define_variable("PROGRAM", PACKAGE);
-  global_scope.define_variable("PROGVER", VERSION);
+  global_scope.define_variable("PPREMAKE", PACKAGE);
+  global_scope.define_variable("PPREMAKE_VERSION", VERSION);
   global_scope.define_variable("PLATFORM", platform);
   global_scope.define_variable("PLATFORM", platform);
+  global_scope.define_variable("PACKAGE_FILENAME", PACKAGE_FILENAME);
+  global_scope.define_variable("SOURCE_FILENAME", SOURCE_FILENAME);
 
 
   PPMain ppmain(&global_scope);
   PPMain ppmain(&global_scope);
   if (!ppmain.read_source(".")) {
   if (!ppmain.read_source(".")) {
     exit(1);
     exit(1);
   }
   }
 
 
-  if (argc < 2) {
-    if (!ppmain.process_all()) {
+  if (report_depends || report_needs) {
+    // With -d or -n, just report inter-directory dependency
+    // relationships.
+    if (argc < 2) {
+      cerr << "No named directories.\n";
       exit(1);
       exit(1);
     }
     }
-  } else {
+
     for (int i = 1; i < argc; i++) {
     for (int i = 1; i < argc; i++) {
-      if (!ppmain.process(argv[i])) {
-	cerr << "Unable to process " << argv[i] << ".\n";
+      if (report_depends) {
+	ppmain.report_depends(argv[i]);
+      }
+      if (report_needs) {
+	ppmain.report_needs(argv[i]);
+      }
+      cerr << "\n";
+    }
+
+  } else {
+    // Without -d or -n, do normal processing.
+
+    if (argc < 2) {
+      if (!ppmain.process_all()) {
 	exit(1);
 	exit(1);
       }
       }
+    } else {
+      for (int i = 1; i < argc; i++) {
+	if (!ppmain.process(argv[i])) {
+	  cerr << "Unable to process " << argv[i] << ".\n";
+	  exit(1);
+	}
+      }
     }
     }
   }
   }
 
 

+ 0 - 1
ppremake/ppremake.h

@@ -31,7 +31,6 @@ using namespace std;
 #define PACKAGE_FILENAME "Package.pp"
 #define PACKAGE_FILENAME "Package.pp"
 #define SOURCE_FILENAME "Sources.pp"
 #define SOURCE_FILENAME "Sources.pp"
 
 
-#define DIRECTORY_SEPARATOR '/'
 #define COMMAND_PREFIX '#'
 #define COMMAND_PREFIX '#'
 #define VARIABLE_PREFIX '$'
 #define VARIABLE_PREFIX '$'
 #define VARIABLE_OPEN_BRACE '['
 #define VARIABLE_OPEN_BRACE '['