From 3ca8c86cf0f975f1af3c1cd2ee1160dda563e06d Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 11 Oct 2000 23:38:03 +0000 Subject: [PATCH] Initial revision --- ppremake/Depends.pp | 26 + ppremake/Global.stopgap.pp | 334 ++++++ ppremake/Makefile.am | 20 + ppremake/System.pp | 34 + ppremake/Template.autoconf.pp | 922 ++++++++++++++++ ppremake/Template.stopgap.pp | 721 +++++++++++++ ppremake/acconfig.h | 39 + ppremake/acinclude.m4 | 405 +++++++ ppremake/configure.in | 76 ++ ppremake/find_searchpath.cxx | 22 + ppremake/find_searchpath.h | 20 + ppremake/gnu_getopt.c | 755 +++++++++++++ ppremake/gnu_getopt.h | 125 +++ ppremake/ppCommandFile.cxx | 1365 ++++++++++++++++++++++++ ppremake/ppCommandFile.h | 153 +++ ppremake/ppDirectoryTree.cxx | 550 ++++++++++ ppremake/ppDirectoryTree.h | 83 ++ ppremake/ppFilenamePattern.cxx | 176 ++++ ppremake/ppFilenamePattern.h | 45 + ppremake/ppMain.cxx | 193 ++++ ppremake/ppMain.h | 47 + ppremake/ppNamedScopes.cxx | 112 ++ ppremake/ppNamedScopes.h | 47 + ppremake/ppScope.cxx | 1805 ++++++++++++++++++++++++++++++++ ppremake/ppScope.h | 123 +++ ppremake/ppSubroutine.cxx | 48 + ppremake/ppSubroutine.h | 34 + ppremake/ppremake.cxx | 124 +++ ppremake/ppremake.h | 51 + ppremake/tokenize.cxx | 134 +++ ppremake/tokenize.h | 30 + 31 files changed, 8619 insertions(+) create mode 100644 ppremake/Depends.pp create mode 100644 ppremake/Global.stopgap.pp create mode 100644 ppremake/Makefile.am create mode 100644 ppremake/System.pp create mode 100644 ppremake/Template.autoconf.pp create mode 100644 ppremake/Template.stopgap.pp create mode 100644 ppremake/acconfig.h create mode 100644 ppremake/acinclude.m4 create mode 100644 ppremake/configure.in create mode 100644 ppremake/find_searchpath.cxx create mode 100644 ppremake/find_searchpath.h create mode 100644 ppremake/gnu_getopt.c create mode 100644 ppremake/gnu_getopt.h create mode 100644 ppremake/ppCommandFile.cxx create mode 100644 ppremake/ppCommandFile.h create mode 100644 ppremake/ppDirectoryTree.cxx create mode 100644 ppremake/ppDirectoryTree.h create mode 100644 ppremake/ppFilenamePattern.cxx create mode 100644 ppremake/ppFilenamePattern.h create mode 100644 ppremake/ppMain.cxx create mode 100644 ppremake/ppMain.h create mode 100644 ppremake/ppNamedScopes.cxx create mode 100644 ppremake/ppNamedScopes.h create mode 100644 ppremake/ppScope.cxx create mode 100644 ppremake/ppScope.h create mode 100644 ppremake/ppSubroutine.cxx create mode 100644 ppremake/ppSubroutine.h create mode 100644 ppremake/ppremake.cxx create mode 100644 ppremake/ppremake.h create mode 100644 ppremake/tokenize.cxx create mode 100644 ppremake/tokenize.h diff --git a/ppremake/Depends.pp b/ppremake/Depends.pp new file mode 100644 index 0000000000..83db3d5396 --- /dev/null +++ b/ppremake/Depends.pp @@ -0,0 +1,26 @@ +// +// 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 $[eq $[DIR_TYPE], src] + +#if $[eq $[DEPENDS],] + #map local_libs TARGET(*/lib_target */noinst_lib_target) + + // Allow the user to define additional DEPENDS targets in each + // Sources.pp. + #define DEPENDS + #set DEPENDS $[EXTRA_DEPENDS] + + #forscopes lib_target bin_target noinst_bin_target + #set DEPENDS $[DEPENDS] $[local_libs $[DIRNAME],$[LOCAL_LIBS]] $[LOCAL_INCS] + #end lib_target bin_target noinst_bin_target + + #set DEPENDS $[sort $[DEPENDS]] +#endif + +#endif // DIR_TYPE diff --git a/ppremake/Global.stopgap.pp b/ppremake/Global.stopgap.pp new file mode 100644 index 0000000000..a0f9f03aee --- /dev/null +++ b/ppremake/Global.stopgap.pp @@ -0,0 +1,334 @@ +// +// 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. +// + +// Define some various compile flags, derived from the variables set +// in Config.pp. +#if $[HAVE_PYTHON] + // We want to let the PYTHON_INCLUDE directory include wildcard characters. + #define python_ipath $[patsubst %,-I%,$[isdir $[PYTHON_IPATH]]] + #define python_lpath $[patsubst %,-L%,$[isdir $[PYTHON_LPATH]]] +#endif + +#if $[HAVE_NSPR] + // We want to let the NSPR directories include wildcard characters. + #define nspr_ipath $[patsubst %,-I%,$[isdir $[NSPR_IPATH]]] + #define nspr_lpath $[patsubst %,-L%,$[isdir $[NSPR_LPATH]]] + #define nspr_libs $[NSPR_LIBS] +#endif + +#if $[HAVE_ZLIB] + #define zlib_ipath $[ZLIB_IPATH:%=-I%] + #define zlib_lpath $[ZLIB_LPATH:%=-L%] + #define zlib_libs $[ZLIB_LIBS] +#endif + +#if $[HAVE_SOXST] + #define soxst_ipath $[SOXST_IPATH:%=-I%] + #define soxst_lpath $[SOXST_LPATH:%=-L%] + #define soxst_libs $[SOXST_LIBS] +#endif + +#if $[HAVE_GL] + #define gl_ipath $[GL_IPATH:%=-I%] + #define gl_lpath $[GL_LPATH:%=-L%] + #define gl_libs $[GL_LIBS] +#endif + +#if $[HAVE_DX] + #define dx_ipath $[DX_IPATH:%=-I%] + #define dx_lpath $[DX_LPATH:%=-L%] + #define dx_libs $[DX_LIBS] +#endif + +#if $[HAVE_MIKMOD] + #define mikmod_ipath $[MIKMOD_IPATH:%=-I%] + #define mikmod_cflags $[MIKMOD_CFLAGS] + #define mikmod_lpath $[MIKMOD_LPATH:%=-L%] + #define mikmod_libs $[MIKMOD_LIBS] +#endif + +#if $[HAVE_GTKMM] + #define gtkmm_ipath $[GTKMM_IPATH:%=-I%] + #define gtkmm_cflags $[GTKMM_CFLAGS] + #define gtkmm_lpath $[GTKMM_LPATH:%=-L%] + #define gtkmm_libs $[GTKMM_LIBS] +#endif + +#if $[and $[HAVE_MAYA],$[MAYA_LOCATION]] + #define maya_ipath -I$[MAYA_LOCATION]/include + #define maya_lpath -L$[MAYA_LOCATION]/lib + #define maya_ld $[MAYA_LOCATION]/bin/mayald +#endif + +#if $[HAVE_NET] + #define net_ipath $[NET_IPATH:%=-I%] + #define net_lpath $[NET_LPATH:%=-L%] + #define net_libs $[NET_LIBS] +#endif + +#if $[HAVE_AUDIO] + #define audio_ipath $[AUDIO_IPATH:%=-I%] + #define audio_lpath $[AUDIO_LPATH:%=-L%] + #define audio_libs $[AUDIO_LIBS] +#endif + + +// This variable, when evaluated in the scope of a particular directory, +// will indicate true (i.e. nonempty) when the directory is truly built, +// or false (empty) when the directory is not to be built. +#defer build_directory \ + $[and \ + $[or $[not $[DIRECTORY_IF_GL]],$[HAVE_GL]], \ + $[or $[not $[DIRECTORY_IF_DX]],$[HAVE_DX]], \ + $[or $[not $[DIRECTORY_IF_GLX]],$[HAVE_GLX]], \ + $[or $[not $[DIRECTORY_IF_GLUT]],$[HAVE_GLUT]], \ + $[or $[not $[DIRECTORY_IF_WGL]],$[HAVE_WGL]], \ + $[or $[not $[DIRECTORY_IF_RIB]],$[HAVE_RIB]], \ + $[or $[not $[DIRECTORY_IF_PS2]],$[HAVE_PS2]], \ + $[or $[not $[DIRECTORY_IF_SGIGL]],$[HAVE_SGIGL]], \ + $[or $[not $[DIRECTORY_IF_VRPN]],$[HAVE_VRPN]], \ + $[or $[not $[DIRECTORY_IF_NET]],$[HAVE_NET]], \ + $[or $[not $[DIRECTORY_IF_AUDIO]],$[HAVE_AUDIO]], \ + $[or $[not $[DIRECTORY_IF_GTKMM]],$[HAVE_GTKMM]], \ + $[or $[not $[DIRECTORY_IF_MAYA]],$[HAVE_MAYA]], \ + 1 ] + +// This variable is true if we are building on some flavor of Unix. +#define unix_platform $[ne $[PLATFORM],Win32] + +// This variable is true if we are building on some flavor of Windows. +#define windows_platform $[eq $[PLATFORM],Win32] + + +// This subroutine will set up the sources variable to reflect the +// complete set of sources for this target, and also set the +// alt_cflags, alt_libs, etc. as appropriate according to how the +// various USE_* flags are set for the current target. +#defsub get_sources + #define sources $[SOURCES] + #if $[ne $[HAVE_ZLIB],] + #set sources $[sources] $[IF_ZLIB_SOURCES] + #endif + #if $[ne $[HAVE_PYTHON],] + #set sources $[sources] $[IF_PYTHON_SOURCES] + #endif + + #define alt_cflags $[nspr_cflags] $[mikmod_cflags] $[python_cflags] + #define alt_ipath $[nspr_ipath] $[mikmod_ipath] $[python_ipath] + #define alt_lpath $[nspr_lpath] $[mikmod_lpath] $[python_lpath] + #define alt_libs $[nspr_libs] $[mikmod_libs] + #define alt_ld + + // If any of a metalib's constituent libraries require interrogate, + // then so does the metalib itself. To look this up, we need this map + // variable. + #map components TARGET(*/lib_target */noinst_lib_target) + + #if $[ne $[USE_ZLIB] $[components $[USE_ZLIB],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[zlib_cflags] + #set alt_ipath $[alt_ipath] $[zlib_ipath] + #set alt_lpath $[alt_lpath] $[zlib_lpath] + #set alt_libs $[alt_libs] $[zlib_libs] + #endif + #if $[ne $[USE_GL] $[components $[USE_GL],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[gl_cflags] + #set alt_ipath $[alt_ipath] $[gl_ipath] + #set alt_lpath $[alt_lpath] $[gl_lpath] + #set alt_libs $[alt_libs] $[gl_libs] + #endif + #if $[ne $[USE_DX] $[components $[USE_DX],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[dx_cflags] + #set alt_ipath $[alt_ipath] $[dx_ipath] + #set alt_lpath $[alt_lpath] $[dx_lpath] + #set alt_libs $[alt_libs] $[dx_libs] + #endif + #if $[ne $[USE_SOXST] $[components $[USE_SOXST],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[soxst_cflags] + #set alt_ipath $[alt_ipath] $[soxst_ipath] + #set alt_lpath $[alt_lpath] $[soxst_lpath] + #set alt_libs $[alt_libs] $[soxst_libs] + #endif + #if $[ne $[USE_NET] $[components $[USE_NET],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[net_cflags] + #set alt_ipath $[alt_ipath] $[net_ipath] + #set alt_lpath $[alt_lpath] $[net_lpath] + #set alt_libs $[alt_libs] $[net_libs] + #endif + #if $[ne $[USE_AUDIO] $[components $[USE_AUDIO],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[audio_cflags] + #set alt_ipath $[alt_ipath] $[audio_ipath] + #set alt_lpath $[alt_lpath] $[audio_lpath] + #set alt_libs $[alt_libs] $[audio_libs] + #endif + #if $[ne $[USE_GTKMM] $[components $[USE_GTKMM],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[gtkmm_cflags] + #set alt_ipath $[alt_ipath] $[gtkmm_ipath] + #set alt_lpath $[alt_lpath] $[gtkmm_lpath] + #set alt_libs $[alt_libs] $[gtkmm_libs] + #endif + #if $[ne $[USE_MAYA] $[components $[USE_MAYA],$[COMPONENT_LIBS]],] + #set alt_cflags $[alt_cflags] $[maya_cflags] + #set alt_ipath $[alt_ipath] $[maya_ipath] + #set alt_lpath $[alt_lpath] $[maya_lpath] + #set alt_libs $[alt_libs] $[maya_libs] + #set alt_ld $[maya_ld] + #endif + #if $[unix_platform] + #set alt_libs $[alt_libs] $[UNIX_SYS_LIBS] $[components $[UNIX_SYS_LIBS],$[COMPONENT_LIBS]] + #endif +#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 current +// target. +#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. + + // These map variables are handy to determine that. + #map module COMPONENT_LIBS(*/metalib_target) + #map all_libs TARGET(*/static_lib_target */lib_target */noinst_lib_target */metalib_target) + #define 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. + #define 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. + #define 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 + #map static_libs TARGET(*/static_lib_target) + #map dynamic_libs TARGET(*/lib_target */metalib_target) + #map all_libs TARGET(*/static_lib_target */lib_target */metalib_target) + #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. +#defsub get_depend_libs + #map module COMPONENT_LIBS(*/metalib_target) + + #define 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. + #map static_libs TARGET(*/static_lib_target) + #map dynamic_libs TARGET(*/lib_target */metalib_target) + #map all_libs TARGET(*/static_lib_target */lib_target */metalib_target) + #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 diff --git a/ppremake/Makefile.am b/ppremake/Makefile.am new file mode 100644 index 0000000000..435f36d3a6 --- /dev/null +++ b/ppremake/Makefile.am @@ -0,0 +1,20 @@ +bin_PROGRAMS = ppremake + +ppremake_SOURCES = \ + find_searchpath.cxx find_searchpath.h \ + gnu_getopt.c gnu_getopt.h \ + ppCommandFile.cxx ppCommandFile.h ppDirectoryTree.cxx \ + ppDirectoryTree.h ppMain.cxx ppMain.h \ + ppFilenamePattern.cxx \ + ppFilenamePattern.h ppNamedScopes.cxx ppNamedScopes.h \ + ppScope.cxx ppScope.h ppSubroutine.cxx ppSubroutine.h \ + ppremake.cxx ppremake.h tokenize.cxx \ + tokenize.h + +data_DATA = \ + System.pp Depends.pp Template.autoconf.pp \ + Global.stopgap.pp Template.stopgap.pp + +EXTRA_DIST =\ + System.pp Depends.pp Template.autoconf.pp \ + Global.stopgap.pp Template.stopgap.pp diff --git a/ppremake/System.pp b/ppremake/System.pp new file mode 100644 index 0000000000..edc33fa049 --- /dev/null +++ b/ppremake/System.pp @@ -0,0 +1,34 @@ +// +// 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.$[BUILD_TYPE].pp +#endif + +#if $[eq $[TEMPLATE_FILE],] + #define TEMPLATE_FILE $[PPREMAKE_DIR]/Template.$[BUILD_TYPE].pp +#endif + +// Include the global definitions for the template type, if the file +// is there. +#sinclude $[GLOBAL_FILE] + diff --git a/ppremake/Template.autoconf.pp b/ppremake/Template.autoconf.pp new file mode 100644 index 0000000000..dd95b45ac9 --- /dev/null +++ b/ppremake/Template.autoconf.pp @@ -0,0 +1,922 @@ +// +// 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 diff --git a/ppremake/Template.stopgap.pp b/ppremake/Template.stopgap.pp new file mode 100644 index 0000000000..2cf41ba727 --- /dev/null +++ b/ppremake/Template.stopgap.pp @@ -0,0 +1,721 @@ +// +// 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)] + + +// This map variable lets us identify which metalib, if any, is +// including each library built here. +#map module COMPONENT_LIBS(*/metalib_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]] + +// 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)/inc/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 inc/parser-inc/,$(PARSER_INC)) +OTHER = $(INST_PARSER_INC) +#else +# OTHER = +#endif + +#### Where the action happens. +include $(DTOOL)/inc/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) : inc/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] +# IGATEFILE = # Specify only if you want a specific name + +#### Additional search directories for C/C++ header files: +IPATH = $[alt_ipath] + +#### 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] + +#### Other linker flags. +#if $[ne $[alt_ld],] +LD = $[alt_ld] +#endif +# LDFLAGS = + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### The .so action is here. +include $(DTOOL)/inc/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] + +#### 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] + +#### Archiver flags +# ARFLAGS = + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### The .a action is here. +include $(DTOOL)/inc/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] + +#### 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] + +#### Other linker flags. +#if $[ne $[alt_ld],] +LD = $[alt_ld] +#endif +# LDFLAGS = + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### The bin action is here. +include $(DTOOL)/inc/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] + + +// This map variable lets us identify which metalib, if any, is +// including each library built here. +#map module COMPONENT_LIBS(*/metalib_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 $[TARGET(metalib_target):%=lib%.so] +#define install_headers $[INSTALL_HEADERS(metalib_target)] +#define install_data $[INSTALL_DATA(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)/inc/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)/inc/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] + #map components TARGET(*/lib_target */noinst_lib_target) + #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] + +#### 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] + +#### Other linker flags. +#if $[ne $[alt_ld],] +LD = $[alt_ld] +#endif +# LDFLAGS = + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### The .so action is here. +include $(DTOOL)/inc/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)/inc/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 diff --git a/ppremake/acconfig.h b/ppremake/acconfig.h new file mode 100644 index 0000000000..08287ef1e3 --- /dev/null +++ b/ppremake/acconfig.h @@ -0,0 +1,39 @@ +/* acconfig.h + This file is in the public domain. + + Descriptive text for the C preprocessor macros that + the distributed Autoconf macros can define. + No software package will use all of them; autoheader copies the ones + your configure.in uses into your configuration header file templates. + + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). Although this order + can split up related entries, it makes it easier to check whether + a given entry is in the file. + + Leave the following blank line there!! Autoheader needs it. */ + + +/* Define if the C++ compiler uses namespaces. */ +#undef HAVE_NAMESPACE + +/* Define if the C++ iostream library supports ios::binary. */ +#undef HAVE_IOS_BINARY + +/* Define if we're compiling for a Windows platform. */ +#undef PLATFORM_WIN32 + +/* The current version number. */ +#define VERSION 0.0 + +/* The platform ppremake is compiled for. This primarily controls the + initial setting of the $[PLATFORM] variable. */ +#define PLATFORM "" + + +/* Leave that blank line there!! Autoheader needs it. + If you're adding to this file, keep in mind: + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). */ + + diff --git a/ppremake/acinclude.m4 b/ppremake/acinclude.m4 new file mode 100644 index 0000000000..8d0ddeb10d --- /dev/null +++ b/ppremake/acinclude.m4 @@ -0,0 +1,405 @@ +AC_DEFUN(AC_HEADER_IOSTREAM, +[AC_CHECK_HEADERS(iostream,[have_iostream=yes],[have_iostream=no])]) + +AC_DEFUN(AC_IOS_BINARY, +[AC_CACHE_CHECK([for ios::binary], + ac_cv_ios_binary, +[ +if test $have_iostream = yes; then + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +else + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +fi + +]) +if test $ac_cv_ios_binary = yes; then + AC_DEFINE(HAVE_IOS_BINARY) +fi +]) + + +AC_DEFUN(AC_NAMESPACE, +[AC_CACHE_CHECK([for compiler namespace support], + ac_cv_namespace, +[AC_TRY_COMPILE( +[namespace std { }; +using namespace std;], +[], + ac_cv_namespace=yes, ac_cv_namespace=no)]) +if test $ac_cv_namespace = yes; then + AC_DEFINE(HAVE_NAMESPACE) +fi +]) + + +dnl A handy function to see if a library is in a particular directory. +dnl AC_CHECK_LIB_LOC(directory, library, function, action-if-found, action-if-not-found, other-libraries) +dnl +AC_DEFUN(AC_CHECK_LIB_LOC, +[AC_MSG_CHECKING([for lib$2 in $1]) +ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` +AC_CACHE_VAL(ac_cv_lib_loc_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-L$1 -l$2 $6 $LIBS" +AC_TRY_LINK(dnl +ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus +extern "C" +#endif +])dnl +[/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $3(); +], + [$3()], + eval "ac_cv_lib_loc_$ac_lib_var=yes", + eval "ac_cv_lib_loc_$ac_lib_var=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lib_loc_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$4], , +[LIBS="-L$1 -l$2 $LIBS" +], [$4]) +else + AC_MSG_RESULT(no) +ifelse([$5], , , [$5 +])dnl +fi +]) + +dnl A handy function to search a number of possible locations for a library. +dnl AC_SEARCH_LIB(search-dirs, library, function, package, other-libraries) +dnl +dnl Sets $package_LIB to the directory containing the library, or to the +dnl empty string if the library cannot be found. +AC_DEFUN(AC_SEARCH_LIB, [ +ac_found_lib="" +for ac_check_dir in $1; do + if test "$ac_found_lib" = ""; then + AC_CHECK_LIB_LOC($ac_check_dir, $2, $3, [ ac_found_lib="$ac_check_dir"; ],, $5) + fi +done +$4_LIB="$ac_found_lib" +]) + +dnl A handy function to see if a header file is in a particular directory. +dnl AC_CHECK_HEADER_LOC(directory, header, action-if-found, action-if-not-found) +dnl +AC_DEFUN(AC_CHECK_HEADER_LOC, [ + AC_MSG_CHECKING([for $2 in $1]) + ac_include_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` + AC_CACHE_VAL(ac_cv_include_loc_$ac_include_var, [ + ac_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-I$1 $CPPFLAGS" + AC_TRY_CPP([#include <$2>], ac_ch_found_it="yes", ac_ch_found_it="no") + if test "$ac_ch_found_it" = "yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + ifelse([$4], , , [$4]) + fi + CPPFLAGS="$ac_save_CPPFLAGS" + ]) +]) + + +dnl A handy function to search a number of possible locations for a header +dnl file. +dnl +dnl AC_SEARCH_HEADER(search-dirs, header, package) +dnl +dnl Sets $package_INCLUDE to the directory containing the header, or to +dnl the empty string if the header cannot be found. +AC_DEFUN(AC_SEARCH_HEADER, [ +ac_found_header="" +for ac_check_dir in $1; do + if test "$ac_found_header" = ""; then + AC_CHECK_HEADER_LOC($ac_check_dir, $2, [ ac_found_header="$ac_check_dir";]) + fi +done +$3_INCLUDE="$ac_found_header" +]) + + +dnl A handy function to scan for a third-party package, consisting of at +dnl least a library and an include file. A few assumptions are made about +dnl the relationships between lib and include directories. +dnl +dnl AC_SEARCH_PACKAGE(search-dirs, package-names, header, library, function, package, other-libraries) +dnl +dnl search-dirs is the whitespace-separated list of directory prefixes to +dnl check. +dnl package-names is a whitespace-separated list of possible names the +dnl package may have been installed under. +dnl +dnl For each combination of ${search-dir} and ${package-name}, the following +dnl directories are searched: +dnl +dnl ${search-dir} +dnl ${search-dir}/lib +dnl ${search-dir}/${package-name} +dnl ${search-dir}/${package-name}/lib +dnl ${search-dir}/lib/${package-name} +dnl +dnl And similarly for include. +dnl +dnl Sets the variables $package_INCLUDE and $package_LIB to the directories +dnl containing the header file and library, respectively. If both pieces +dnl are located, also sets the variable $package_PKG to "yes"; otherwise, +dnl sets the variable $package_PKG to "no". +dnl +AC_DEFUN(AC_SEARCH_PACKAGE, [ +$6_LIB="" +$6_INCLUDE="" +$6_PKG="no" + +dnl Look for the library. +for ac_sp_dir in $1; do + if test "[$]$6_LIB" = ""; then + AC_SEARCH_LIB("$ac_sp_dir" "$ac_sp_dir/lib", $4, $5, $6, $7) + for ac_sp_pkg in $2; do + if test "[$]$6_LIB" = ""; then + AC_SEARCH_LIB("$ac_sp_dir/$ac_sp_pkg" "$ac_sp_dir/$ac_sp_pkg/lib" \ + "$ac_sp_dir/lib/$ac_sp_pkg", $4, $5, $6, $7) + fi + done + fi +done + +dnl Now look for the header file. Don't bother looking if the library +dnl wasn't found. +if test "[$]$6_LIB" != ""; then + dnl First look in the obvious directory corresponding to the lib dir. + ac_sp_testinc=`echo [$]$6_LIB | sed 's:/lib:/include:'` + AC_SEARCH_HEADER("$ac_sp_testinc", $3, $6) + + dnl If it wasn't found there, cast about. + if test "[$]$6_INCLUDE" = ""; then + for ac_sp_dir in $1; do + if test "[$]$6_INCLUDE" = ""; then + AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $6) + for ac_sp_pkg in $2; do + if test "[$]$6_INCLUDE" = ""; then + AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \ + "$ac_sp_dir/$ac_sp_pkg/include" \ + "$ac_sp_dir/include/$ac_sp_pkg", $3, $6) + fi + done + fi + done + fi + + dnl If we got both a header and a library, set the PKG variable. + if test "[$]$6_INCLUDE" != ""; then + $6_PKG="yes" + fi +fi +]) + + + + +# Configure paths for GTK-- +# Erik Andersen 30 May 1998 +# Modified by Tero Pulkkinen (added the compiler checks... I hope they work..) + +dnl Test for GTKMM, and define GTKMM_CFLAGS and GTKMM_LIBS +dnl to be used as follows: +dnl AM_PATH_GTKMM([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl +AC_DEFUN(AM_PATH_GTKMM, +[dnl +dnl Get the cflags and libraries from the gtkmm-config script +dnl +AC_ARG_WITH(gtkmm-prefix,[ --with-gtkmm-prefix=PREFIX + Prefix where GTK-- is installed (optional)], + gtkmm_config_prefix="$withval", gtkmm_config_prefix="") +AC_ARG_WITH(gtkmm-exec-prefix,[ --with-gtkmm-exec-prefix=PREFIX + Exec prefix where GTK-- is installed (optional)], + gtkmm_config_exec_prefix="$withval", gtkmm_config_exec_prefix="") +AC_ARG_ENABLE(gtkmmtest, [ --disable-gtkmmtest Do not try to compile and run a test GTK-- program], + , enable_gtkmmtest=yes) + + if test x$gtkmm_config_exec_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --exec-prefix=$gtkmm_config_exec_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_exec_prefix/bin/gtkmm-config + fi + fi + if test x$gtkmm_config_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --prefix=$gtkmm_config_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_prefix/bin/gtkmm-config + fi + fi + + AC_PATH_PROG(GTKMM_CONFIG, gtkmm-config, no) + min_gtkmm_version=ifelse([$1], ,0.10.0,$1) + + AC_MSG_CHECKING(for GTK-- - version >= $min_gtkmm_version) + no_gtkmm="" + if test "$GTKMM_CONFIG" = "no" ; then + no_gtkmm=yes + else + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + GTKMM_CFLAGS=`$GTKMM_CONFIG $gtkmm_config_args --cflags` + GTKMM_LIBS=`$GTKMM_CONFIG $gtkmm_config_args --libs` + gtkmm_config_major_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + gtkmm_config_minor_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + gtkmm_config_micro_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_gtkmmtest" = "xyes" ; then + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CXXFLAGS="$CXXFLAGS $GTKMM_CFLAGS" + LIBS="$LIBS $GTKMM_LIBS" +dnl +dnl Now check if the installed GTK-- is sufficiently new. (Also sanity +dnl checks the results of gtkmm-config to some extent +dnl + rm -f conf.gtkmmtest + AC_TRY_RUN([ +#include +#include +#include + +int +main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.gtkmmtest"); + + /* HP/UX 0 (%@#!) writes to sscanf strings */ + tmp_version = g_strdup("$min_gtkmm_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtkmm_version"); + exit(1); + } + + if ((gtkmm_major_version != $gtkmm_config_major_version) || + (gtkmm_minor_version != $gtkmm_config_minor_version) || + (gtkmm_micro_version != $gtkmm_config_micro_version)) + { + printf("\n*** 'gtkmm-config --version' returned %d.%d.%d, but GTK-- (%d.%d.%d)\n", + $gtkmm_config_major_version, $gtkmm_config_minor_version, $gtkmm_config_micro_version, + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf ("*** was found! If gtkmm-config was correct, then it is best\n"); + printf ("*** to remove the old version of GTK--. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If gtkmm-config was wrong, set the environment variable GTKMM_CONFIG\n"); + printf("*** to point to the correct copy of gtkmm-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } +/* GTK-- does not have the GTKMM_*_VERSION constants */ +/* + else if ((gtkmm_major_version != GTKMM_MAJOR_VERSION) || + (gtkmm_minor_version != GTKMM_MINOR_VERSION) || + (gtkmm_micro_version != GTKMM_MICRO_VERSION)) + { + printf("*** GTK-- header files (version %d.%d.%d) do not match\n", + GTKMM_MAJOR_VERSION, GTKMM_MINOR_VERSION, GTKMM_MICRO_VERSION); + printf("*** library (version %d.%d.%d)\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + } +*/ + else + { + if ((gtkmm_major_version > major) || + ((gtkmm_major_version == major) && (gtkmm_minor_version > minor)) || + ((gtkmm_major_version == major) && (gtkmm_minor_version == minor) && (gtkmm_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of GTK-- (%d.%d.%d) was found.\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf("*** You need a version of GTK-- newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** GTK-- is always available from ftp://ftp.gtk.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the gtkmm-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of GTK--, but you can also set the GTKMM_CONFIG environment to point to the\n"); + printf("*** correct copy of gtkmm-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_gtkmm=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_gtkmm" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$GTKMM_CONFIG" = "no" ; then + echo "*** The gtkmm-config script installed by GTK-- could not be found" + echo "*** If GTK-- was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the GTKMM_CONFIG environment variable to the" + echo "*** full path to gtkmm-config." + echo "*** The gtkmm-config script was not available in GTK-- versions" + echo "*** prior to 0.9.12. Perhaps you need to update your installed" + echo "*** version to 0.9.12 or later" + else + if test -f conf.gtkmmtest ; then + : + else + echo "*** Could not run GTK-- test program, checking why..." + CXXFLAGS="$CFLAGS $GTKMM_CXXFLAGS" + LIBS="$LIBS $GTKMM_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return ((gtkmm_major_version) || (gtkmm_minor_version) || (gtkmm_micro_version)); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding GTK-- or finding the wrong" + echo "*** version of GTK--. If it is not finding GTK--, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means GTK-- was incorrectly installed" + echo "*** or that you have moved GTK-- since it was installed. In the latter case, you" + echo "*** may want to edit the gtkmm-config script: $GTKMM_CONFIG" ]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + GTKMM_CFLAGS="" + GTKMM_LIBS="" + ifelse([$3], , :, [$3]) + AC_LANG_RESTORE + fi + AC_SUBST(GTKMM_CFLAGS) + AC_SUBST(GTKMM_LIBS) + rm -f conf.gtkmmtest +]) + + diff --git a/ppremake/configure.in b/ppremake/configure.in new file mode 100644 index 0000000000..ea6345f917 --- /dev/null +++ b/ppremake/configure.in @@ -0,0 +1,76 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(ppremake.cxx) +AM_INIT_AUTOMAKE(ppremake, 0.25a) +AM_CONFIG_HEADER(config.h) + +AC_PREFIX_DEFAULT(/usr/local/panda) +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 Checks for programs. +AC_PROG_CC +AC_PROG_CXX + +dnl First, we'll test for C-specific features. +AC_LANG_C + +dnl Checks for libraries. +libdl= +libm= +AC_CHECK_LIB(m, sin, libm=-lm) +AC_SUBST(libm) + +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. + +dnl Checks for library functions. +AC_CHECK_FUNCS(getopt) + + +dnl Now we can test some C++-specific features. +AC_LANG_CPLUSPLUS +AC_HEADER_IOSTREAM +AC_NAMESPACE + + +AC_LANG_C + +AC_ARG_WITH(platform, +[ --with-platform=platform Set the $[PLATFORM] predefined variable.]) + +if test "${with_platform-no}" != "no"; then + PLATFORM=$with_platform +else + case "$host_os" in + irix*) PLATFORM=Irix;; + linux*) PLATFORM=Linux;; + cygwin*) PLATFORM=Win32;; + *) echo "Cannot determine platform; use --with-platform=name." + exit 1;; + esac +fi + +if test "$PLATFORM" = "Win32"; then + AC_DEFINE(PLATFORM_WIN32) +fi + +AC_DEFINE_UNQUOTED(PLATFORM, "$PLATFORM") + + +AC_OUTPUT(Makefile) diff --git a/ppremake/find_searchpath.cxx b/ppremake/find_searchpath.cxx new file mode 100644 index 0000000000..56f05b95a4 --- /dev/null +++ b/ppremake/find_searchpath.cxx @@ -0,0 +1,22 @@ +// Filename: find_searchpath.cxx +// Created by: drose (09Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "find_searchpath.h" + +#include + +string +find_searchpath(const vector &directories, const string &filename) { + vector::const_iterator di; + + for (di = directories.begin(); di != directories.end(); ++di) { + string path = (*di) + "/" + filename; + if (access(path.c_str(), F_OK) == 0) { + return path; + } + } + + return string(); +} diff --git a/ppremake/find_searchpath.h b/ppremake/find_searchpath.h new file mode 100644 index 0000000000..bd4dd0cc8a --- /dev/null +++ b/ppremake/find_searchpath.h @@ -0,0 +1,20 @@ +// Filename: find_searchpath.h +// Created by: drose (09Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FIND_SEARCHPATH_H +#define FIND_SEARCHPATH_H + +#include "ppremake.h" + +#include + +// Searchs for the given filename along the indicated set of +// directories, and returns the first place in which it is found, or +// empty string if it is not found. +string find_searchpath(const vector &directories, + const string &filename); + +#endif + diff --git a/ppremake/gnu_getopt.c b/ppremake/gnu_getopt.c new file mode 100644 index 0000000000..a3ef62a133 --- /dev/null +++ b/ppremake/gnu_getopt.c @@ -0,0 +1,755 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + + +#include + +#if !defined(HAVE_GETOPT) + +#ifdef WIN32_VC +/* This file seems particularly egregious with this particular warning, + but it's not clear why. Disable. */ +/* C4028: formal parameter N different from declaration */ +#pragma warning (disable : 4028) +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifndef __STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "gnu_getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#ifndef __STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (optstring) + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if (nameend - nextchar == (int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT */ diff --git a/ppremake/gnu_getopt.h b/ppremake/gnu_getopt.h new file mode 100644 index 0000000000..7b7b6803ba --- /dev/null +++ b/ppremake/gnu_getopt.h @@ -0,0 +1,125 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. */ + +#ifndef _GNU_GETOPT_H +#define _GNU_GETOPT_H 1 + +/* We don't want to collide with a system getopt() it if it exists. + Redefine our symbols accordingly. */ + +#define getopt gnu_getopt +#define optind gnu_optind +#define opterr gnu_opterr +#define optopt gnu_optopt +#define optarg gnu_optarg +#define getopt_long gnu_getopt_long +#define getopt_long_only gnu_getopt_long_only + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char EXPCL_DTOOL *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int EXPCL_DTOOL optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern EXPCL_DTOOL int +getopt (int argc, char *const *argv, const char *shortopts); +extern EXPCL_DTOOL int +getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *long_options, int *opt_index); +extern EXPCL_DTOOL int +getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *long_options, + int *opt_index); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/ppremake/ppCommandFile.cxx b/ppremake/ppCommandFile.cxx new file mode 100644 index 0000000000..8baa6e3201 --- /dev/null +++ b/ppremake/ppCommandFile.cxx @@ -0,0 +1,1365 @@ +// Filename: ppCommandFile.cxx +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppCommandFile.h" +#include "ppScope.h" +#include "ppNamedScopes.h" +#include "ppSubroutine.h" +#include "tokenize.h" + +#include +#include // for tempnam() +#include + +static const string begin_comment(BEGIN_COMMENT); + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::WriteState::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPCommandFile::WriteState:: +WriteState() { + _out = &cout; + _format = WF_collapse; + _last_blank = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::WriteState::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPCommandFile::WriteState:: +WriteState(const WriteState ©) : + _out(copy._out), + _format(copy._format), + _last_blank(copy._last_blank) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::WriteState::write_line +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool PPCommandFile::WriteState:: +write_line(const string &line) { + switch (_format) { + case WF_straight: + (*_out) << line << "\n"; + return true; + + case WF_collapse: + return write_collapse_line(line); + + case WF_makefile: + return write_makefile_line(line); + } + + cerr << "Unsupported write format: " << (int)_format << "\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::WriteState::write_collapse_line +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool PPCommandFile::WriteState:: +write_collapse_line(const string &line) { + if (line.empty()) { + if (!_last_blank) { + (*_out) << "\n"; + _last_blank = true; + } + + } else { + _last_blank = false; + (*_out) << line << "\n"; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::WriteState::write_makefile_line +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool PPCommandFile::WriteState:: +write_makefile_line(const string &line) { + if (line.length() <= 72) { + return write_collapse_line(line); + } + _last_blank = false; + + // In makefile mode, long variable assignment lines are folded after + // the assignment. + vector words; + tokenize_whitespace(line, words); + + if (words.size() > 2 && (words[1] == "=" || words[1] == ":")) { + // This appears to be a variable assignment or a dependency rule; + // fold it. + (*_out) << words[0] << " " << words[1]; + vector::const_iterator wi; + int col = 80; + wi = words.begin() + 2; + while (wi != words.end()) { + col += (*wi).length() + 1; + if (col > 72) { + (*_out) << " \\\n "; + col = 4 + (*wi).length(); + } + (*_out) << " " << (*wi); + ++wi; + } + (*_out) << "\n"; + + } else { + // This is not a variable assignment, so just write it out. + (*_out) << line << "\n"; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPCommandFile:: +PPCommandFile(PPScope *scope) { + _native_scope = scope; + _scope = scope; + _got_command = false; + _in_for = false; + _if_nesting = (IfNesting *)NULL; + _block_nesting = (BlockNesting *)NULL; + _write_state = new WriteState; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PPCommandFile:: +~PPCommandFile() { + delete _write_state; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::set_scope +// Access: Public +// Description: Changes the command file to use the indicated scope. +// This scope will *not* be deleted when the command +// file destructs. +//////////////////////////////////////////////////////////////////// +void PPCommandFile:: +set_scope(PPScope *scope) { + _scope = scope; + _native_scope = scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::get_scope +// Access: Public +// Description: Returns the current scope associated with the command +// file. This may change as the command file is +// processed (e.g. between #begin .. #end sequences), +// and it may or may not be tied to the life of the +// PPCommandFile itself. +//////////////////////////////////////////////////////////////////// +PPScope *PPCommandFile:: +get_scope() const { + return _scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::read_file +// Access: Public +// Description: Reads input from the given filename. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +read_file(const string &filename) { + ifstream in(filename.c_str()); + + if (!in) { + cerr << "Unable to open " << filename << ".\n"; + return false; + } + + PushFilename pushed(_scope, filename); + + if (!read_stream(in)) { + if (!in.eof()) { + cerr << "Error reading " << filename << ".\n"; + } + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::read_stream +// Access: Public +// Description: Reads input from the given stream. Each line is +// read, commands are processed, variables are expanded, +// and the resulting output is sent to write_line() +// one line at a time. The return value is true if the +// entire file is read with no errors, false if there is +// some problem. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +read_stream(istream &in) { + string line; + getline(in, line); + begin_read(); + while (!in.fail() && !in.eof()) { + if (!read_line(line)) { + return false; + } + getline(in, line); + } + + if (!end_read()) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::begin_read +// Access: Public +// Description: Resets to the beginning-of-the-stream state, in +// preparation for a sequence of read_line() calls. +//////////////////////////////////////////////////////////////////// +void PPCommandFile:: +begin_read() { + assert(_if_nesting == (IfNesting *)NULL); + assert(_block_nesting == (BlockNesting *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::read_line +// Access: Public +// Description: Reads one line at a time, as if from the input +// stream. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +read_line(string line) { + // First things first: strip off any comment in the line. + + // We only recognize comments that are proceeded by whitespace, or + // that start at the beginning of the line. + size_t comment = line.find(begin_comment); + while (comment != string::npos && + !(comment == 0 || isspace(line[comment - 1]))) { + comment = line.find(begin_comment, comment + begin_comment.length()); + } + + if (comment != string::npos) { + // Also strip any whitespace leading up to the comment. + while (comment > 0 && isspace(line[comment - 1])) { + comment--; + } + line = line.substr(0, comment); + } + + // If the comment was at the beginning of the line, ignore the whole + // line, including its whitespace. + if (comment != 0) { + if (_in_for) { + // Save up the lines for later execution if we're within a #forscopes. + _saved_lines.push_back(line); + } + + if (_got_command) { + return handle_command(line); + + } else { + // Find the beginning of the line--skip initial whitespace. + size_t p = 0; + while (p < line.length() && isspace(line[p])) { + p++; + } + + if (p == line.length()) { + // The line is empty. Make it truly empty. + line = ""; + + } else { + if (p + 1 < line.length() && line[p] == COMMAND_PREFIX && + isalpha(line[p + 1])) { + // This is a special command. + return handle_command(line.substr(p + 1)); + } + } + + if (!_in_for && !failed_if()) { + return _write_state->write_line(_scope->expand_string(line)); + } + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::end_read +// Access: Public +// Description: Finishes up the input stream, after a sequence of +// read_line() calls. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +end_read() { + bool okflag = true; + + if (_if_nesting != (IfNesting *)NULL) { + cerr << "Unclosed if\n"; + _if_nesting = (IfNesting *)NULL; + okflag = false; + } + + if (_block_nesting != (BlockNesting *)NULL) { + switch (_block_nesting->_state) { + case BS_begin: + cerr << "Unclosed begin " << _block_nesting->_name << "\n"; + break; + + case BS_forscopes: + case BS_nested_forscopes: + cerr << "Unclosed forscopes " << _block_nesting->_name << "\n"; + break; + + case BS_foreach: + case BS_nested_foreach: + cerr << "Unclosed foreach " << _block_nesting->_name << "\n"; + break; + + case BS_defsub: + cerr << "Unclosed defsub " << _block_nesting->_name << "\n"; + break; + + case BS_output: + cerr << "Unclosed output " << _block_nesting->_name << "\n"; + break; + } + _block_nesting = (BlockNesting *)NULL; + okflag = false; + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_command +// Access: Protected +// Description: Handles a macro command. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_command(const string &line) { + if (_got_command) { + // If we were still processing a command from last time, keep + // going; this line is just a continuation. + _params += line; + + } else { + // This is the first line of a new command. + + // Pull off the first word and the rest of the line. + size_t p = 0; + while (p < line.length() && !isspace(line[p])) { + p++; + } + _command = line.substr(0, p); + + // Skip whitespace between the command and its arguments. + while (p < line.length() && isspace(line[p])) { + p++; + } + _params = line.substr(p); + } + + if (_params[_params.length() - 1] == '\\') { + // If the line ends with a backslash, there's more to come before + // we can process the command. + _got_command = true; + _params[_params.length() - 1] = ' '; + return true; + } + + // We're completely done scanning the command now. + _got_command = false; + + if (_command == "if") { + return handle_if_command(); + + } else if (_command == "elif") { + return handle_elif_command(); + + } else if (_command == "else") { + return handle_else_command(); + + } else if (_command == "endif") { + return handle_endif_command(); + + } else if (failed_if()) { + // If we're in the middle of a failed #if, we ignore all commands + // except for the if-related commands, above. + return true; + + } else if (_command == "begin") { + return handle_begin_command(); + + } else if (_command == "forscopes") { + return handle_forscopes_command(); + + } else if (_command == "foreach") { + return handle_foreach_command(); + + } else if (_command == "format") { + return handle_format_command(); + + } else if (_command == "output") { + return handle_output_command(); + + } else if (_command == "defsub") { + return handle_defsub_command(); + + } else if (_command == "end") { + return handle_end_command(); + + } else if (_in_for) { + // If we're saving up #forscopes commands, we ignore any following + // commands for now. + return true; + + } else if (_command == "include") { + return handle_include_command(); + + } else if (_command == "sinclude") { + return handle_sinclude_command(); + + } else if (_command == "call") { + return handle_call_command(); + + } else if (_command == "error") { + return handle_error_command(); + + } else if (_command == "defer") { + return handle_defer_command(); + + } else if (_command == "define") { + return handle_define_command(); + + } else if (_command == "set") { + return handle_set_command(); + + } else if (_command == "map") { + return handle_map_command(); + } + + cerr << "Invalid command: " << COMMAND_PREFIX << _command << "\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_if_command +// Access: Protected +// Description: Handles the #if command: conditionally evaluate the +// following code. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_if_command() { + if (failed_if()) { + // If we're *already* inside a failed if, we don't have to + // evaluate this one, but we do need to record the nesting level. + + IfNesting *nest = new IfNesting; + nest->_state = IS_done; + nest->_next = _if_nesting; + _if_nesting = nest; + + } else { + + // If the parameter string evaluates to empty, the case is false. + // Otherwise the case is true. However, if we're currently + // scanning #forscopes or something, we don't evaluate this at + // all, because it doesn't matter. + if (!_in_for) { + _params = _scope->expand_string(_params); + } + + bool is_empty = true; + string::const_iterator si; + for (si = _params.begin(); si != _params.end() && is_empty; ++si) { + is_empty = isspace(*si); + } + + IfNesting *nest = new IfNesting; + nest->_state = is_empty ? IS_off : IS_on; + nest->_next = _if_nesting; + _if_nesting = nest; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_elif_command +// Access: Protected +// Description: Handles the #elif command: conditionally evaluate +// the following code, following a failed #if command. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_elif_command() { + if (_if_nesting == (IfNesting *)NULL) { + cerr << "elif encountered without if.\n"; + return false; + } + if (_if_nesting->_state == IS_else) { + cerr << "elif encountered after else.\n"; + return false; + } + if (_if_nesting->_state == IS_on || _if_nesting->_state == IS_done) { + // If we passed the #if above, we don't need to evaluate the #elif. + _if_nesting->_state = IS_done; + return true; + } + + // If the parameter string evaluates to empty, the case is false. + // Otherwise the case is true. + if (!_in_for) { + _params = _scope->expand_string(_params); + } + + bool is_empty = true; + string::const_iterator si; + for (si = _params.begin(); si != _params.end() && is_empty; ++si) { + is_empty = isspace(*si); + } + + _if_nesting->_state = is_empty ? IS_off : IS_on; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_else_command +// Access: Protected +// Description: Handles the #else command: evaluate the following +// code following a failed #if command. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_else_command() { + if (_if_nesting == (IfNesting *)NULL) { + cerr << "else encountered without if.\n"; + return false; + } + if (_if_nesting->_state == IS_else) { + cerr << "else encountered after else.\n"; + return false; + } + if (_if_nesting->_state == IS_on || _if_nesting->_state == IS_done) { + _if_nesting->_state = IS_done; + return true; + } + + _if_nesting->_state = IS_else; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_endif_command +// Access: Protected +// Description: Handles the #endif command: close a preceeding #if +// command. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_endif_command() { + if (_if_nesting == (IfNesting *)NULL) { + cerr << "endif encountered without if.\n"; + return false; + } + + IfNesting *nest = _if_nesting; + _if_nesting = _if_nesting->_next; + delete nest; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_begin_command +// Access: Protected +// Description: Handles the #begin command: begin a named scope +// block. The variables defined between this command +// and the corresponding #end command will be local to +// this named scope. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_begin_command() { + BlockNesting *nest = new BlockNesting; + nest->_state = BS_begin; + nest->_name = trim_blanks(_scope->expand_string(_params)); + nest->_write_state = _write_state; + nest->_scope = _scope; + nest->_next = _block_nesting; + + if (contains_whitespace(nest->_name)) { + cerr << "Attempt to define scope named \"" << nest->_name + << "\".\nScope names may not contain whitespace.\n"; + return false; + } + + if (nest->_name.find(SCOPE_DIRNAME_SEPARATOR) != string::npos) { + cerr << "Attempt to define scope named \"" << nest->_name + << "\".\nScope names may not contain the '" + << SCOPE_DIRNAME_SEPARATOR << "' character.\n"; + return false; + } + + _block_nesting = nest; + + if (nest->_name == "global") { + // There's a special case for the named scope "global": this + // refers to the global scope, allowing us to define macros + // etc. that all scopes can see. + _scope = PPScope::get_bottom_scope(); + + } else { + PPScope *named_scope = _scope->get_named_scopes()->make_scope(nest->_name); + named_scope->set_parent(_scope); + _scope = named_scope; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_forscopes_command +// Access: Protected +// Description: Handles the #forscopes command: interpret all the lines +// between this command and the corresponding #end +// command once for each occurrence of a named scope +// with the given name. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_forscopes_command() { + BlockNesting *nest = new BlockNesting; + nest->_state = _in_for ? BS_nested_forscopes : BS_forscopes; + nest->_name = trim_blanks(_scope->expand_string(_params)); + nest->_write_state = _write_state; + nest->_scope = _scope; + nest->_next = _block_nesting; + + _block_nesting = nest; + + if (!_in_for) { + _in_for = true; + _saved_lines.clear(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_foreach_command +// Access: Protected +// Description: Handles the #foreach command: interpret all the lines +// between this command and the corresponding #end +// command once for each word in the argument. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_foreach_command() { + // Get the parameters of the foreach command. The first word is the + // name of the variable to substitute in (and which should appear on + // the matching #end command), and the remaining words are the + // values to substitute in. + vector words; + tokenize_whitespace(_scope->expand_string(_params), words); + + if (words.empty()) { + cerr << "#foreach requires at least one parameter.\n"; + return false; + } + + string variable_name = words.front(); + + BlockNesting *nest = new BlockNesting; + nest->_state = _in_for ? BS_nested_foreach : BS_foreach; + nest->_name = variable_name; + nest->_write_state = _write_state; + nest->_scope = _scope; + nest->_next = _block_nesting; + + nest->_words.insert(nest->_words.end(), words.begin() + 1, words.end()); + + _block_nesting = nest; + + if (!_in_for) { + _in_for = true; + _saved_lines.clear(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_format_command +// Access: Protected +// Description: Handles the #format command: change the formatting +// mode of lines as they are output. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_format_command() { + _params = trim_blanks(_scope->expand_string(_params)); + if (_params == "straight") { + _write_state->_format = WF_straight; + + } else if (_params == "collapse") { + _write_state->_format = WF_collapse; + + } else if (_params == "makefile") { + _write_state->_format = WF_makefile; + + } else { + cerr << "Ignoring invalid write format: " << _params << "\n"; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_output_command +// Access: Protected +// Description: Handles the #output command: all text between this +// command and the corresponding #end command will be +// sent to the indicated output file. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_output_command() { + BlockNesting *nest = new BlockNesting; + nest->_state = BS_output; + nest->_name = trim_blanks(_scope->expand_string(_params)); + nest->_write_state = _write_state; + nest->_scope = _scope; + nest->_next = _block_nesting; + + _block_nesting = nest; + + if (!_in_for) { + string filename = nest->_name; + if (filename.empty()) { + cerr << "Attempt to output to empty filename\n"; + return false; + } + + string prefix = _scope->expand_variable("DIRPREFIX"); + if (filename[0] != '/') { + filename = prefix + filename; + } + + nest->_true_name = filename; + nest->_tempnam = (char *)NULL; + + if (access(filename.c_str(), F_OK) == 0) { + // If the file already exists, create a temporary file first. + + nest->_tempnam = tempnam((prefix + ".").c_str(), "pptmp"); + assert(nest->_tempnam != (char *)NULL); + + nest->_output.open(nest->_tempnam); + if (nest->_output.fail()) { + cerr << "Unable to open output file " << nest->_tempnam << "\n"; + return false; + } + + } else { + // If the file does not already exist, create it directly instead + // of monkeying around with temporary files. + cerr << "Generating " << filename << "\n"; + + nest->_output.open(filename.c_str(), ios::out, 0666); + if (nest->_output.fail()) { + cerr << "Unable to open output file " << filename << "\n"; + return false; + } + } + + _write_state = new WriteState(*_write_state); + _write_state->_out = &nest->_output; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_defsub_command +// Access: Protected +// Description: Handles the #defsub command: save all the lines +// between this command and the matching #end as a +// callable subroutine to be invoked by a later #call +// command. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_defsub_command() { + // The only parameter of #defsub is the name of the subroutine. + string subroutine_name = trim_blanks(_scope->expand_string(_params)); + + if (subroutine_name.empty()) { + cerr << "#defsub requires at least one parameter.\n"; + return false; + } + + if (_in_for) { + cerr << "#defsub may not appear within a #forscopes or #foreach block,\n" + << "or within another #defsub block.\n"; + return false; + } + + BlockNesting *nest = new BlockNesting; + nest->_state = BS_defsub; + nest->_name = subroutine_name; + nest->_write_state = _write_state; + nest->_scope = _scope; + nest->_next = _block_nesting; + + _block_nesting = nest; + + _in_for = true; + _saved_lines.clear(); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_end_command +// Access: Protected +// Description: Handles the #end command. This closes a number of +// different kinds of blocks, like #begin and #forscopes. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_end_command() { + if (_block_nesting == (BlockNesting *)NULL) { + cerr << "Unmatched end " << _params << ".\n"; + return false; + } + + string name = trim_blanks(_scope->expand_string(_params)); + if (name != _block_nesting->_name) { + cerr << "end " << name << " encountered where end " + << _block_nesting->_name << " expected.\n"; + return false; + } + + BlockNesting *nest = _block_nesting; + + _scope = nest->_scope; + if (_write_state != nest->_write_state) { + delete _write_state; + _write_state = nest->_write_state; + } + + _block_nesting = nest->_next; + + if (nest->_state == BS_forscopes) { + // Now replay all of the saved lines. + _in_for = false; + if (!replay_forscopes(nest->_name)) { + return false; + } + + } else if (nest->_state == BS_foreach) { + // Now replay all of the saved lines. + _in_for = false; + if (!replay_foreach(nest->_name, nest->_words)) { + return false; + } + + } else if (nest->_state == BS_defsub) { + // Save all of the saved lines as a named subroutine. + _in_for = false; + PPSubroutine *sub = new PPSubroutine; + sub->_lines.swap(_saved_lines); + + // Remove the #end command. This will fail if someone makes an + // #end command that spans multiple lines. Don't do that. + assert(!sub->_lines.empty()); + sub->_lines.pop_back(); + + PPSubroutine::define_sub(nest->_name, sub); + + } else if (nest->_state == BS_output) { + if (!_in_for) { + if (!nest->_output) { + cerr << "Error while writing " << nest->_true_name << "\n"; + return false; + } + nest->_output.close(); + + // Verify the output file. + if (nest->_tempnam != (char *)NULL) { + if (!compare_output(nest->_tempnam, nest->_true_name)) { + return false; + } + free(nest->_tempnam); + } + } + } + + + delete nest; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_include_command +// Access: Protected +// Description: Handles the #include command: the indicated file is +// read and processed at this point. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_include_command() { + string filename = trim_blanks(_scope->expand_string(_params)); + + // We allow optional quotation marks around the filename. + if (filename.length() >= 2 && + filename[0] == '"' && + filename[filename.length() - 1] == '"') { + filename = filename.substr(1, filename.length() - 2); + } + + return include_file(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_sinclude_command +// Access: Protected +// Description: Handles the #sinclude command: the indicated file is +// read and processed at this point. This is different +// from #include only in that if the file does not +// exist, there is no error; instead, nothing happens. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_sinclude_command() { + string filename = trim_blanks(_scope->expand_string(_params)); + + // We allow optional quotation marks around the filename. + if (filename.length() >= 2 && + filename[0] == '"' && + filename[filename.length() - 1] == '"') { + filename = filename.substr(1, filename.length() - 2); + } + + if (access(filename.c_str(), F_OK) != 0) { + // No such file; no error. + return true; + } + + return include_file(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_call_command +// Access: Protected +// Description: Handles the #call command: the indicated named +// subroutine is read and processed at this point. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_call_command() { + string subroutine_name = trim_blanks(_scope->expand_string(_params)); + + if (subroutine_name.empty()) { + cerr << "#call with no parameter.\n"; + } + + const PPSubroutine *sub = PPSubroutine::get_sub(subroutine_name); + if (sub == (const PPSubroutine *)NULL) { + cerr << "Attempt to call undefined subroutine " << subroutine_name << "\n"; + } + + vector::const_iterator li; + for (li = sub->_lines.begin(); li != sub->_lines.end(); ++li) { + if (!read_line(*li)) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_error_command +// Access: Protected +// Description: Handles the #error command: terminate immediately +// with the given error message. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_error_command() { + if (!_params.empty()) { + cerr << _params << "\n"; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_defer_command +// Access: Protected +// Description: Handles the #defer command: define a new variable or +// change the definition of an existing variable. This +// is different from #define in that the variable +// definition is not immediately expanded; it will be +// expanded when the variable is later used. This +// allows the definition of variables that depend on +// other variables whose values have not yet been +// defined. This is akin to GNU make's = assignment. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_defer_command() { + // Pull off the first word and the rest of the params. + size_t p = 0; + while (p < _params.length() && !isspace(_params[p])) { + p++; + } + string varname = _params.substr(0, p); + + // Skip whitespace between the variable name and its definition. + while (p < _params.length() && isspace(_params[p])) { + p++; + } + string def = _params.substr(p); + + // We don't expand the variable's definition immediately; it will be + // expanded when the variable is referenced later. However, we + // should expand any simple self-reference immediately, to allow for + // recursive definitions. + def = _scope->expand_self_reference(def, varname); + + _scope->define_variable(varname, def); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_define_command +// Access: Protected +// Description: Handles the #define command: define a new variable or +// change the definition of an existing variable. The +// variable definition is immediately expanded. This is +// akin to GNU make's := assignment. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_define_command() { + // Pull off the first word and the rest of the params. + size_t p = 0; + while (p < _params.length() && !isspace(_params[p])) { + p++; + } + string varname = _params.substr(0, p); + + // Skip whitespace between the variable name and its definition. + while (p < _params.length() && isspace(_params[p])) { + p++; + } + string def = _scope->expand_string(_params.substr(p)); + _scope->define_variable(varname, def); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_set_command +// Access: Protected +// Description: Handles the #set command: change the definition of an +// existing variable. +// +// This is different from #defer in two ways: (1) the +// variable in question must already have been #defined +// elsewhere, (2) if the variable was #defined in some +// parent scope, this will actually change the variable +// in the parent scope, rather than shadowing it in the +// local scope. Like #define and unlike #defer, the +// variable definition is expanded immediately, similar +// to GNU make's := operator. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_set_command() { + // Pull off the first word and the rest of the params. + size_t p = 0; + while (p < _params.length() && !isspace(_params[p])) { + p++; + } + string varname = _params.substr(0, p); + + // Skip whitespace between the variable name and its definition. + while (p < _params.length() && isspace(_params[p])) { + p++; + } + string def = _scope->expand_string(_params.substr(p)); + + if (!_scope->set_variable(varname, def)) { + cerr << "Attempt to set undefined variable " << varname << "\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_map_command +// Access: Protected +// Description: Handles the #map command: define a new map variable. +// This is a special kind of variable declaration that +// creates a variable that can be used as a function to +// look up variable expansions within a number of +// different named scopes, accessed by keyword. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_map_command() { + // Pull off the first word and the rest of the params. + size_t p = 0; + while (p < _params.length() && !isspace(_params[p])) { + p++; + } + string varname = _params.substr(0, p); + + // Skip whitespace between the variable name and its definition. + while (p < _params.length() && isspace(_params[p])) { + p++; + } + string def = _params.substr(p); + + // We don't expand the variable's definition immediately; it will be + // expanded when the variable is referenced later. However, we + // should expand any simple self-reference immediately, to allow for + // recursive definitions. + def = _scope->expand_string(def); + + _scope->define_map_variable(varname, def); + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::include_file +// Access: Protected +// Description: The internal implementation of #include: includes a +// particular named file at this point. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +include_file(const string &filename) { + ifstream in(filename.c_str()); + + if (!in) { + cerr << "Unable to open include file " << filename << ".\n"; + return false; + } + + PushFilename pushed(_scope, filename); + + string line; + getline(in, line); + while (!in.fail() && !in.eof()) { + if (!read_line(line)) { + return false; + } + getline(in, line); + } + + if (!in.eof()) { + cerr << "Error reading " << filename << ".\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::replay_forscopes +// Access: Protected +// Description: Replays all the lines that were saved during a +// previous #forscopes..#end block. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +replay_forscopes(const string &name) { + bool okflag = true; + + vector lines; + lines.swap(_saved_lines); + + // Remove the #end command. This will fail if someone makes an #end + // command that spans multiple lines. Don't do that. + assert(!lines.empty()); + lines.pop_back(); + + PPNamedScopes *named_scopes = _scope->get_named_scopes(); + + // Extract out the scope names from the #forscopes .. #end name. This + // is a space-delimited list of scope names. + vector words; + tokenize_whitespace(name, words); + + // Now traverse the named scopes with these names. + vector::const_iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + PPNamedScopes::Scopes scopes; + named_scopes->get_scopes(*wi, scopes); + + PPNamedScopes::Scopes::const_iterator si; + for (si = scopes.begin(); si != scopes.end(); ++si) { + PPScope::push_scope(_scope); + _scope = (*si); + + vector::const_iterator li; + for (li = lines.begin(); li != lines.end() && okflag; ++li) { + okflag = read_line(*li); + } + _scope = PPScope::pop_scope(); + } + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::replay_foreach +// Access: Protected +// Description: Replays all the lines that were saved during a +// previous #foreach..#end block. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +replay_foreach(const string &varname, const vector &words) { + bool okflag = true; + + vector lines; + lines.swap(_saved_lines); + + // Remove the #end command. This will fail if someone makes an #end + // command that spans multiple lines. Don't do that. + assert(!lines.empty()); + lines.pop_back(); + + // Now traverse through the saved words. + vector::const_iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + _scope->define_variable(varname, (*wi)); + vector::const_iterator li; + for (li = lines.begin(); li != lines.end() && okflag; ++li) { + okflag = read_line(*li); + } + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::compare_output +// Access: Protected +// Description: After a temporary file has been written due to an +// #output command, compare the results to the original +// file. If they are different, remove the original +// file and rename the temporary file; if they are the +// same, remove the temporary file. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +compare_output(const string &temp_name, const string &true_name) { + ifstream in_a(temp_name.c_str()); + ifstream in_b(true_name.c_str()); + + int a = in_a.get(); + int b = in_b.get(); + bool differ = (a != b); + while (!in_a.eof() && !in_b.eof() && !differ) { + a = in_a.get(); + b = in_b.get(); + differ = (a != b); + } + + in_a.close(); + in_b.close(); + + if (differ) { + cerr << "Generating " << true_name << "\n"; + + if (unlink(true_name.c_str()) < 0) { + cerr << "Unable to remove old " << true_name << "\n"; + return false; + } + + if (rename(temp_name.c_str(), true_name.c_str()) < 0) { + cerr << "Unable to rename temporary file " << temp_name + << " to " << true_name << "\n"; + return false; + } + + } else { + // cerr << "File " << true_name << " is unchanged.\n"; + if (unlink(temp_name.c_str()) < 0) { + cerr << "Warning: unable to remove temporary file " << temp_name << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::failed_if +// Access: Protected +// Description: Returns true if we are currently within a failed #if +// block (or in an #else block for a passed #if block), +// or false otherwise. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +failed_if() const { + return (_if_nesting != (IfNesting *)NULL && + (_if_nesting->_state == IS_off || _if_nesting->_state == IS_done)); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::PushFilename::Constructor +// Access: Public +// Description: This special class changes the current filename of +// the PPCommandFile. The idea is to create one of +// these when the filename is changed (for instance, to +// read in a new file via an #include directive); when +// the variable then goes out of scope, it will restore +// the previous filename. +// +// This updates the scope with the appropriate +// variables. +//////////////////////////////////////////////////////////////////// +PPCommandFile::PushFilename:: +PushFilename(PPScope *scope, const string &filename) { + _scope = scope; + _old_thisdirprefix = _scope->get_variable("THISDIRPREFIX"); + _old_thisfilename = _scope->get_variable("THISFILENAME"); + + _scope->define_variable("THISFILENAME", filename); + size_t slash = filename.rfind('/'); + if (slash == string::npos) { + _scope->define_variable("THISDIRPREFIX", string()); + } else { + _scope->define_variable("THISDIRPREFIX", filename.substr(0, slash + 1)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::PushFilename::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPCommandFile::PushFilename:: +~PushFilename() { + _scope->define_variable("THISDIRPREFIX", _old_thisdirprefix); + _scope->define_variable("THISFILENAME", _old_thisfilename); +} diff --git a/ppremake/ppCommandFile.h b/ppremake/ppCommandFile.h new file mode 100644 index 0000000000..ee286bd7a7 --- /dev/null +++ b/ppremake/ppCommandFile.h @@ -0,0 +1,153 @@ +// Filename: ppCommandFile.h +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPCOMMANDFILE_H +#define PPCOMMANDFILE_H + +#include "ppremake.h" + +#include +#include + +class PPScope; + +/////////////////////////////////////////////////////////////////// +// Class : PPCommandFile +// Description : This encapsulates a file that contains #commands to +// execute (like #define, #if, #begin .. #end), +// $[variables] to expand, and plain text to output. +//////////////////////////////////////////////////////////////////// +class PPCommandFile { +public: + PPCommandFile(PPScope *scope); + ~PPCommandFile(); + + void set_scope(PPScope *scope); + PPScope *get_scope() const; + + bool read_file(const string &filename); + bool read_stream(istream &in); + void begin_read(); + bool read_line(string line); + bool end_read(); + +protected: + bool handle_command(const string &line); + bool handle_if_command(); + bool handle_elif_command(); + bool handle_else_command(); + bool handle_endif_command(); + + bool handle_begin_command(); + bool handle_forscopes_command(); + bool handle_foreach_command(); + bool handle_format_command(); + bool handle_output_command(); + bool handle_defsub_command(); + bool handle_end_command(); + + bool handle_include_command(); + bool handle_sinclude_command(); + bool handle_call_command(); + bool handle_error_command(); + + bool handle_defer_command(); + bool handle_define_command(); + bool handle_set_command(); + bool handle_map_command(); + + bool include_file(const string &filename); + bool replay_forscopes(const string &name); + bool replay_foreach(const string &varname, const vector &words); + bool compare_output(const string &temp_name, const string &true_name); + bool failed_if() const; + +private: + class PushFilename { + public: + PushFilename(PPScope *scope, const string &filename); + ~PushFilename(); + + PPScope *_scope; + string _old_thisdirprefix; + string _old_thisfilename; + }; + +private: + PPScope *_native_scope; + PPScope *_scope; + + enum IfState { + IS_on, // e.g. a passed #if + IS_else, // after matching an #else + IS_off, // e.g. a failed #if + IS_done // e.g. after reaching an #else or #elif for a passed #if. + }; + + class IfNesting { + public: + IfState _state; + IfNesting *_next; + }; + + enum BlockState { + BS_begin, + BS_forscopes, + BS_nested_forscopes, + BS_foreach, + BS_nested_foreach, + BS_defsub, + BS_output + }; + + enum WriteFormat { + WF_straight, // write lines directly as they come in + WF_collapse, // collapse out consecutive blank lines + WF_makefile // fancy makefile formatting + }; + + class WriteState { + public: + WriteState(); + WriteState(const WriteState ©); + bool write_line(const string &line); + bool write_collapse_line(const string &line); + bool write_makefile_line(const string &line); + + ostream *_out; + WriteFormat _format; + bool _last_blank; + }; + + class BlockNesting { + public: + BlockState _state; + string _name; + WriteState *_write_state; + PPScope *_scope; + string _true_name; + char *_tempnam; + ofstream _output; + vector _words; + BlockNesting *_next; + }; + + bool _got_command; + bool _in_for; + IfNesting *_if_nesting; + BlockNesting *_block_nesting; + string _command; + string _params; + WriteState *_write_state; + + vector _saved_lines; + + friend PPCommandFile::IfNesting; + friend PPCommandFile::WriteState; + friend PPCommandFile::BlockNesting; +}; + + +#endif diff --git a/ppremake/ppDirectoryTree.cxx b/ppremake/ppDirectoryTree.cxx new file mode 100644 index 0000000000..0d772b3f12 --- /dev/null +++ b/ppremake/ppDirectoryTree.cxx @@ -0,0 +1,550 @@ +// Filename: ppDirectoryTree.cxx +// Created by: drose (28Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppDirectoryTree.h" +#include "ppScope.h" +#include "ppNamedScopes.h" +#include "ppCommandFile.h" +#include "tokenize.h" + +#include +#include +#include +#include + +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(); + } +}; + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::Constructor +// Access: Public +// Description: Creates the root level of the 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)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::Destructor +// Access: Public +// Description: When a tree root destructs, all of its children are +// also destroyed. +//////////////////////////////////////////////////////////////////// +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"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::scan +// Access: Public +// Description: Reads in the complete hierarchy of source files. +// prefix is the pathname to the directory on disk, +// ending in slash. +//////////////////////////////////////////////////////////////////// +bool PPDirectoryTree:: +scan(const string &prefix, PPNamedScopes *named_scopes) { + if (!r_scan(prefix)) { + return false; + } + + if (!read_source_file(prefix, named_scopes)) { + return false; + } + + if (!read_depends_file(named_scopes)) { + return false; + } + + if (!resolve_dependencies()) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::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 PPDirectoryTree:: +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: PPDirectoryTree::get_dirname +// Access: Public +// Description: Returns the name of this particular directory level. +//////////////////////////////////////////////////////////////////// +const string &PPDirectoryTree:: +get_dirname() const { + return _dirname; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::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 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. +//////////////////////////////////////////////////////////////////// +string PPDirectoryTree:: +get_path() const { + if (_parent == (PPDirectoryTree *)NULL) { + return "."; + } + if (_parent->_parent == (PPDirectoryTree *)NULL) { + return _dirname; + } + return _parent->get_path() + "/" + _dirname; +} + +//////////////////////////////////////////////////////////////////// +// 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 +// Access: Public +// Description: Searches for the first subdirectory found with the +// matching dirname. This is just the name of the +// directory itself, not the relative path to the +// directory. +//////////////////////////////////////////////////////////////////// +PPDirectoryTree *PPDirectoryTree:: +find_dirname(const string &dirname) { + assert(_dirnames != (Dirnames *)NULL); + Dirnames::const_iterator di; + di = _dirnames->find(dirname); + if (di != _dirnames->end()) { + return (*di).second; + } + + // 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(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::get_child +// 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 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: PPDirectoryTree::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 PPDirectoryTree:: +get_complete_subtree() const { + Children copy_children = _children; + sort(copy_children.begin(), copy_children.end(), + SortDirectoriesByDependencyAndName()); + + vector 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: 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); + + if (!subtree->r_scan(next_prefix)) { + return false; + } + } + } + + d = readdir(root); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPDirectoryTree::read_source_file +// Access: Private +// Description: Recursively reads in the source file at each level, +// if defined. +//////////////////////////////////////////////////////////////////// +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; +} + +//////////////////////////////////////////////////////////////////// +// 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 dirnames; + tokenize_whitespace(_scope->expand_variable("DEPENDS"), dirnames); + + vector::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. +//////////////////////////////////////////////////////////////////// +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; +} + +//////////////////////////////////////////////////////////////////// +// 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; +} diff --git a/ppremake/ppDirectoryTree.h b/ppremake/ppDirectoryTree.h new file mode 100644 index 0000000000..f9a6e3981e --- /dev/null +++ b/ppremake/ppDirectoryTree.h @@ -0,0 +1,83 @@ +// Filename: ppDirectoryTree.h +// Created by: drose (28Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPDIRECTORYTREE_H +#define PPDIRECTORYTREE_H + +#include "ppremake.h" + +#include +#include +#include + +class PPCommandFile; +class PPScope; +class PPNamedScopes; + +/////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////// +class PPDirectoryTree { +public: + PPDirectoryTree(); + ~PPDirectoryTree(); + +protected: + PPDirectoryTree(const string &dirname, PPDirectoryTree *parent); + +public: + bool scan(const string &prefix, PPNamedScopes *named_scopes); + + int count_source_files() const; + + const string &get_dirname() const; + int get_depends_index() const; + string get_path() const; + string get_rel_to(const PPDirectoryTree *other) const; + + PPDirectoryTree *find_dirname(const string &dirname); + PPCommandFile *get_source() const; + + int get_num_children() const; + PPDirectoryTree *get_child(int n) const; + + string get_child_dirnames() const; + string get_complete_subtree() const; + +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 Children; + Children _children; + int _depth; + + typedef set Depends; + Depends _i_depend_on; + Depends _depends_on_me; + int _depends_index; + bool _computing_depends_index; + + + typedef map Dirnames; + Dirnames *_dirnames; +}; + +extern PPDirectoryTree *current_output_directory; + +#endif + diff --git a/ppremake/ppFilenamePattern.cxx b/ppremake/ppFilenamePattern.cxx new file mode 100644 index 0000000000..d1d41e743a --- /dev/null +++ b/ppremake/ppFilenamePattern.cxx @@ -0,0 +1,176 @@ +// Filename: ppFilenamePattern.cxx +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppFilenamePattern.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPFilenamePattern:: +PPFilenamePattern(const string &pattern) { + size_t pct = pattern.find(PATTERN_WILDCARD); + _has_wildcard = (pct != string::npos); + + if (_has_wildcard) { + _prefix = pattern.substr(0, pct); + _suffix = pattern.substr(pct + 1); + } else { + _prefix = pattern; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPFilenamePattern:: +PPFilenamePattern(const PPFilenamePattern ©) : + _has_wildcard(copy._has_wildcard), + _prefix(copy._prefix), + _suffix(copy._suffix) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PPFilenamePattern:: +operator = (const PPFilenamePattern ©) { + _has_wildcard = copy._has_wildcard; + _prefix = copy._prefix; + _suffix = copy._suffix; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::has_wildcard +// Access: Public +// Description: Returns true if the filename pattern contained a +// wildcard (and hence represents a pattern and not +// just a single particular filename), or false if it +// did not. +//////////////////////////////////////////////////////////////////// +bool PPFilenamePattern:: +has_wildcard() const { + return _has_wildcard; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::get_pattern +// Access: Public +// Description: Returns the filename pattern. +//////////////////////////////////////////////////////////////////// +string PPFilenamePattern:: +get_pattern() const { + if (_has_wildcard) { + return _prefix + PATTERN_WILDCARD + _suffix; + } else { + return _prefix; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::get_prefix +// Access: Public +// Description: Returns the part of the filename pattern before the +// wildcard. If the filename did not contain a +// wildcard, this returns the entire filename. +//////////////////////////////////////////////////////////////////// +const string &PPFilenamePattern:: +get_prefix() const { + return _prefix; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::get_suffix +// Access: Public +// Description: Returns the part of the filename pattern after the +// wildcard. If the filename did not contain a +// wildcard, this returns the empty string. +//////////////////////////////////////////////////////////////////// +const string &PPFilenamePattern:: +get_suffix() const { + return _suffix; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::matches_filename +// Access: Public +// Description: Returns true if the given filename matches the +// pattern, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PPFilenamePattern:: +matches(const string &filename) const { + if (_has_wildcard) { + return + (filename.length() >= _prefix.length() + _suffix.length()) && + (filename.substr(0, _prefix.length()) == _prefix) && + (filename.substr(filename.length() - _suffix.length()) == _suffix); + + } else { + return (filename == _prefix); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::extract_body +// Access: Public +// Description: If the filename matches the pattern +// (e.g. matches_filename() returns true), return the +// portion of the filename that corresponds to the +// wildcard in the pattern: the part of the filename +// between the prefix and the suffix. If the filename +// does not match the pattern, or the pattern does not +// contain a wildcard, returns empty string. +//////////////////////////////////////////////////////////////////// +string PPFilenamePattern:: +extract_body(const string &filename) const { + if (_has_wildcard) { + size_t outside_length = _prefix.length() + _suffix.length(); + if ((filename.length() >= outside_length) && + (filename.substr(0, _prefix.length()) == _prefix) && + (filename.substr(filename.length() - _suffix.length()) == _suffix)) { + return filename.substr(_prefix.length(), filename.length() - outside_length); + } + } + + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPFilenamePattern::transform_filename +// Access: Public +// Description: Transforms a filename by replacing the parts of the +// filename described in the pattern transform_from with +// the corresponding parts of the filename described in +// this pattern. If the filename does not match +// transform_from, returns the untransformed filename. +// +// It is an error to call this unless both this pattern +// and transform_from include a wildcard. +//////////////////////////////////////////////////////////////////// +string PPFilenamePattern:: +transform(const string &filename, + const PPFilenamePattern &transform_from) const { + assert(transform_from._has_wildcard); + + if (transform_from.matches(filename)) { + if (!_has_wildcard) { + return _prefix; + } else { + string body = transform_from.extract_body(filename); + return _prefix + body + _suffix; + } + } + + return filename; +} + diff --git a/ppremake/ppFilenamePattern.h b/ppremake/ppFilenamePattern.h new file mode 100644 index 0000000000..4e28f69172 --- /dev/null +++ b/ppremake/ppFilenamePattern.h @@ -0,0 +1,45 @@ +// Filename: ppFilenamePattern.h +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPFILENAMEPATTERN_H +#define PPFILENAMEPATTERN_H + +#include "ppremake.h" + +/////////////////////////////////////////////////////////////////// +// Class : PPFilenamePattern +// Description : This is a string that represents a filename, or a +// family of filenames, using the make convention that a +// wildcard sign (PATTERN_WILDCARD, typically '%') in +// the filename represents any sequence of characters. +//////////////////////////////////////////////////////////////////// +class PPFilenamePattern { +public: + PPFilenamePattern(const string &pattern); + PPFilenamePattern(const PPFilenamePattern ©); + void operator = (const PPFilenamePattern ©); + + bool has_wildcard() const; + string get_pattern() const; + const string &get_prefix() const; + const string &get_suffix() const; + + bool matches(const string &filename) const; + string extract_body(const string &filename) const; + string transform(const string &filename, + const PPFilenamePattern &transform_from) const; + +private: + bool _has_wildcard; + string _prefix; + string _suffix; +}; + +inline ostream & +operator << (ostream &out, const PPFilenamePattern &pattern) { + return out << pattern.get_pattern(); +} + +#endif diff --git a/ppremake/ppMain.cxx b/ppremake/ppMain.cxx new file mode 100644 index 0000000000..122decb117 --- /dev/null +++ b/ppremake/ppMain.cxx @@ -0,0 +1,193 @@ +// Filename: ppMain.cxx +// Created by: drose (28Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppMain.h" +#include "ppScope.h" +#include "ppCommandFile.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPMain:: +PPMain(PPScope *global_scope) { + _global_scope = global_scope; + PPScope::push_scope(_global_scope); + + _def_scope = (PPScope *)NULL; + _defs = (PPCommandFile *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPMain:: +~PPMain() { + if (_def_scope != (PPScope *)NULL) { + delete _def_scope; + } + if (_defs != (PPCommandFile *)NULL) { + delete _defs; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::read_source +// Access: Public +// Description: Reads the directory hierarchy of Sources.pp files, at +// the indicated directory and below. +//////////////////////////////////////////////////////////////////// +bool PPMain:: +read_source(const string &root) { + // First, read the package file. We find this either in this + // directory, or in some directory above us. + string search = root + "/"; + if (root == ".") { + search = ""; + } + + string package_file = search + PACKAGE_FILENAME; + + while (access(package_file.c_str(), F_OK) != 0) { + // We continue to walk up directories as long as we see a source + // file in each directory. When we stop seeing source files, we + // stop walking upstairs. + string source_file = search + SOURCE_FILENAME; + if (access(source_file.c_str(), F_OK) != 0) { + cerr << "Could not find ppremake package file " << PACKAGE_FILENAME + << ".\n\n" + << "This file should be present in the top of the source directory tree;\n" + << "it defines implementation-specific variables to control the output\n" + << "of ppremake, as well as pointing out the installed location of\n" + << "important ppremake config files.\n\n"; + return false; + } + search += "../"; + package_file = search + PACKAGE_FILENAME; + } + + _def_scope = new PPScope(&_named_scopes); + _def_scope->define_variable("PACKAGEFILE", package_file); + _def_scope->define_variable("TOPDIRPREFIX", search); + _defs = new PPCommandFile(_def_scope); + + // cerr << "Reading " << package_file << "\n"; + if (!_defs->read_file(package_file)) { + cerr << "Error reading package file " << package_file << ".\n"; + return false; + } + + PPScope::push_scope(_def_scope); + + if (!_tree.scan(search, &_named_scopes)) { + return false; + } + + _def_scope->define_variable("TREE", _tree.get_complete_subtree()); + + if (_tree.count_source_files() == 0) { + cerr << "Could not find any source definition files named " << SOURCE_FILENAME + << ".\n\n" + << "A file by this name should be present in each directory of the source\n" + << "hierarchy; it defines the source files and targets that should be\n" + << "built in each directory, as well as the relationships between the\n" + << "directories.\n\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::process_all +// Access: Public +// Description: Does all the processing on all known directories. +// See process(). +//////////////////////////////////////////////////////////////////// +bool PPMain:: +process_all() { + return r_process_all(&_tree); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::process +// Access: Public +// Description: Does the processing associated with the source file +// in the indicated subdirectory name. This involves +// reading in the template file and generating whatever +// output the template file indicates. +//////////////////////////////////////////////////////////////////// +bool PPMain:: +process(const string &dirname) { + PPDirectoryTree *dir = _tree.find_dirname(dirname); + if (dir == (PPDirectoryTree *)NULL) { + cerr << "Unknown directory: " << dirname << "\n"; + return false; + } + + if (dir->get_source() == (PPCommandFile *)NULL) { + cerr << "No source file in " << dirname << "\n"; + return false; + } + + return p_process(dir); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::r_process_all +// Access: Private +// Description: The recursive implementation of process_all(). +//////////////////////////////////////////////////////////////////// +bool PPMain:: +r_process_all(PPDirectoryTree *dir) { + if (dir->get_source() != (PPCommandFile *)NULL) { + if (!p_process(dir)) { + return false; + } + } + + int num_children = dir->get_num_children(); + for (int i = 0; i < num_children; i++) { + if (!r_process_all(dir->get_child(i))) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPMain::p_process +// Access: Private +// Description: The private implementation of process(). +//////////////////////////////////////////////////////////////////// +bool PPMain:: +p_process(PPDirectoryTree *dir) { + current_output_directory = dir; + _named_scopes.set_current(dir->get_dirname()); + PPCommandFile *source = dir->get_source(); + assert(source != (PPCommandFile *)NULL); + + PPScope *scope = source->get_scope(); + + string template_filename = scope->expand_variable("TEMPLATE_FILE"); + if (template_filename.empty()) { + cerr << "No definition given for $[TEMPLATE_FILE], cannot process.\n"; + return false; + } + + PPCommandFile template_file(scope); + if (!template_file.read_file(template_filename)) { + cerr << "Error reading template file " << template_filename << ".\n"; + return false; + } + + return true; +} diff --git a/ppremake/ppMain.h b/ppremake/ppMain.h new file mode 100644 index 0000000000..cf226c816a --- /dev/null +++ b/ppremake/ppMain.h @@ -0,0 +1,47 @@ +// Filename: ppMain.h +// Created by: drose (28Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPMAIN_H +#define PPMAIN_H + +#include "ppremake.h" +#include "ppDirectoryTree.h" +#include "ppNamedScopes.h" + +class PPScope; +class PPCommandFile; + +/////////////////////////////////////////////////////////////////// +// Class : PPMain +// Description : Handles the toplevel processing in this program: +// holds the tree of source files, and all the scopes, +// etc. Generally get the ball rolling. +//////////////////////////////////////////////////////////////////// +class PPMain { +public: + PPMain(PPScope *global_scope); + ~PPMain(); + + bool read_source(const string &root); + + bool process_all(); + bool process(const string &dirname); + +private: + bool r_process_all(PPDirectoryTree *dir); + bool p_process(PPDirectoryTree *dir); + + + PPScope *_global_scope; + PPScope *_def_scope; + PPCommandFile *_defs; + + PPDirectoryTree _tree; + PPNamedScopes _named_scopes; + PPScope *_parent_scope; +}; + +#endif + diff --git a/ppremake/ppNamedScopes.cxx b/ppremake/ppNamedScopes.cxx new file mode 100644 index 0000000000..a42c9fad16 --- /dev/null +++ b/ppremake/ppNamedScopes.cxx @@ -0,0 +1,112 @@ +// Filename: ppNamedScopes.cxx +// Created by: drose (27Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppNamedScopes.h" +#include "ppScope.h" + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPNamedScopes:: +PPNamedScopes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPNamedScopes:: +~PPNamedScopes() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::make_scope +// Access: Public +// Description: Creates a new scope in the current directory name +// with the indicated scope name. +//////////////////////////////////////////////////////////////////// +PPScope *PPNamedScopes:: +make_scope(const string &name) { + PPScope *scope = new PPScope(this); + _directories[_current][name].push_back(scope); + return scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::get_scopes +// Access: Public +// Description: Returns a list of all the named scopes matching the +// given scope name. The scope name may be of the form +// "dirname/scopename", in which case the dirname may be +// another directory name, or "." or "*". If the +// dirname is ".", it is the same as the current +// directory name; if it is "*", it represents all +// directory names. If omitted, the current directory +// name is implied. +// +// It is the responsibility of the user to ensure that +// scopes is empty before calling this function; this +// will append to the existing vector without first +// clearing it. +//////////////////////////////////////////////////////////////////// +void PPNamedScopes:: +get_scopes(const string &name, Scopes &scopes) const { + string dirname = _current; + string scopename = name; + + size_t slash = name.find(SCOPE_DIRNAME_SEPARATOR); + if (slash != string::npos) { + dirname = name.substr(0, slash); + scopename = name.substr(slash + 1); + if (dirname == SCOPE_DIRNAME_CURRENT) { + dirname = _current; + } + } + + Directories::const_iterator di; + + if (dirname == SCOPE_DIRNAME_WILDCARD) { + for (di = _directories.begin(); di != _directories.end(); ++di) { + p_get_scopes((*di).second, scopename, scopes); + } + + } else { + di = _directories.find(dirname); + if (di != _directories.end()) { + p_get_scopes((*di).second, scopename, scopes); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::set_current +// Access: Public +// Description: Changes the currently-active directory, i.e. the +// dirname represented by ".". +//////////////////////////////////////////////////////////////////// +void PPNamedScopes:: +set_current(const string &dirname) { + _current = dirname; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPNamedScopes::p_get_scopes +// Access: Private +// Description: Adds the scopes from the given directory with the +// matching name into the returned vector. +//////////////////////////////////////////////////////////////////// +void PPNamedScopes:: +p_get_scopes(const PPNamedScopes::Named &named, const string &name, + Scopes &scopes) const { + Named::const_iterator ni; + ni = named.find(name); + if (ni != named.end()) { + const Scopes &s = (*ni).second; + scopes.insert(scopes.end(), s.begin(), s.end()); + } +} diff --git a/ppremake/ppNamedScopes.h b/ppremake/ppNamedScopes.h new file mode 100644 index 0000000000..ec082b8738 --- /dev/null +++ b/ppremake/ppNamedScopes.h @@ -0,0 +1,47 @@ +// Filename: ppNamedScopes.h +// Created by: drose (27Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPNAMEDSCOPES_H +#define PPNAMEDSCOPES_H + +#include "ppremake.h" + +#include +#include + +class PPScope; + +/////////////////////////////////////////////////////////////////// +// Class : PPNamedScopes +// Description : A collection of named scopes, as defined by #begin +// .. #end sequences within a series of command files, +// each associated with the directory name of the +// command file in which it was read. +//////////////////////////////////////////////////////////////////// +class PPNamedScopes { +public: + PPNamedScopes(); + ~PPNamedScopes(); + + typedef vector Scopes; + + PPScope *make_scope(const string &name); + void get_scopes(const string &name, Scopes &scopes) const; + + void set_current(const string &dirname); + +private: + typedef map Named; + + void p_get_scopes(const Named &named, const string &name, + Scopes &scopes) const; + + typedef map Directories; + Directories _directories; + string _current; +}; + +#endif + diff --git a/ppremake/ppScope.cxx b/ppremake/ppScope.cxx new file mode 100644 index 0000000000..857f1ee3ce --- /dev/null +++ b/ppremake/ppScope.cxx @@ -0,0 +1,1805 @@ +// Filename: ppScope.cxx +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppScope.h" +#include "ppNamedScopes.h" +#include "ppFilenamePattern.h" +#include "ppDirectoryTree.h" +#include "tokenize.h" +#include "find_searchpath.h" + +#include +#include +#include +#include +#include +#include +#include // for perror(). +#include +#include +#include +#include + +static const string variable_patsubst(VARIABLE_PATSUBST); + +PPScope::MapVariableDefinition PPScope::_null_map_def; + +PPScope::ScopeStack PPScope::_scope_stack; + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PPScope:: +PPScope(PPNamedScopes *named_scopes) : + _named_scopes(named_scopes) +{ + _directory = (PPDirectoryTree *)NULL; + _parent_scope = (PPScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::get_named_scopes +// Access: Public +// Description: Returns a pointer to the PPNamedScopes collection +// associated with this scope. This pointer could be +// NULL. +//////////////////////////////////////////////////////////////////// +PPNamedScopes *PPScope:: +get_named_scopes() const { + return _named_scopes; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::set_parent +// Access: Public +// Description: Sets a static parent scope to this scope. When a +// variable reference is undefined in this scope, it +// will search first up the static parent chain before +// it searches the dynamic scope stack. +//////////////////////////////////////////////////////////////////// +void PPScope:: +set_parent(PPScope *parent) { + _parent_scope = parent; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::get_parent +// Access: Public +// Description: Returns the static parent scope to this scope, if +// any, or NULL if the static parent has not been set. +// See set_parent(). +//////////////////////////////////////////////////////////////////// +PPScope *PPScope:: +get_parent() const { + return _parent_scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::define_variable +// Access: Public +// Description: Makes a new variable definition. If the variable +// does not already exist in this scope, a new variable +// is created, possibly shadowing a variable declaration +// in some parent scope. +//////////////////////////////////////////////////////////////////// +void PPScope:: +define_variable(const string &varname, const string &definition) { + _variables[varname] = definition; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::set_variable +// Access: Public +// Description: Changes the definition of an already-existing +// variable. The variable is changed in whichever scope +// it is defined. Returns false if the variable has not +// been defined. +//////////////////////////////////////////////////////////////////// +bool PPScope:: +set_variable(const string &varname, const string &definition) { + if (p_set_variable(varname, definition)) { + return true; + } + + // Check the scopes on the stack for the variable definition. + ScopeStack::reverse_iterator si; + for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) { + if ((*si)->p_set_variable(varname, definition)) { + return true; + } + } + + // If the variable isn't defined, we check the environment. + const char *env = getenv(varname.c_str()); + if (env != (const char *)NULL) { + // It is defined in the environment; thus, it is implicitly + // defined here at the global scope: the bottom of the stack. + PPScope *bottom = this; + if (!_scope_stack.empty()) { + bottom = _scope_stack.front(); + } + bottom->define_variable(varname, definition); + return true; + } + + // The variable isn't defined anywhere. Too bad. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::define_map_variable +// Access: Public +// Description: Makes a new map variable definition. This defines a +// new variable that can be used as a function to +// retrieve variables from within a named scope, based +// on a particular key variable. +// +// In this variant of define_map_variable(), the +// definition is a string of the form +// key_varname(scope_names). +//////////////////////////////////////////////////////////////////// +void PPScope:: +define_map_variable(const string &varname, const string &definition) { + size_t p = definition.find(VARIABLE_OPEN_NESTED); + if (p != string::npos && definition[definition.length() - 1] == VARIABLE_CLOSE_NESTED) { + size_t q = definition.length() - 1; + string scope_names = definition.substr(p + 1, q - (p + 1)); + string key_varname = definition.substr(0, p); + define_map_variable(varname, key_varname, scope_names); + } else { + // No scoping; not really a map variable. + define_map_variable(varname, definition, ""); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::define_map_variable +// Access: Public +// Description: Makes a new map variable definition. This defines a +// new variable that can be used as a function to +// retrieve variables from within a named scope, based +// on a particular key variable. +//////////////////////////////////////////////////////////////////// +void PPScope:: +define_map_variable(const string &varname, const string &key_varname, + const string &scope_names) { + MapVariableDefinition &def = _map_variables[varname]; + def.clear(); + define_variable(varname, ""); + + if (_named_scopes == (PPNamedScopes *)NULL) { + return; + } + + vector names; + tokenize_whitespace(scope_names, names); + + // Get all of the named scopes. + PPNamedScopes::Scopes scopes; + + vector::const_iterator ni; + for (ni = names.begin(); ni != names.end(); ++ni) { + const string &name = (*ni); + _named_scopes->get_scopes(name, scopes); + } + + if (scopes.empty()) { + return; + } + + // Now go through the scopes and build up the results. + vector results; + + PPNamedScopes::Scopes::const_iterator si; + for (si = scopes.begin(); si != scopes.end(); ++si) { + PPScope *scope = (*si); + string key_string = scope->expand_variable(key_varname); + vector keys; + tokenize_whitespace(key_string, keys); + + if (!keys.empty()) { + vector::const_iterator ki; + results.insert(results.end(), keys.begin(), keys.end()); + for (ki = keys.begin(); ki != keys.end(); ++ki) { + def[*ki] = scope; + } + } + } + + // Also define a traditional variable along with the map variable. + define_variable(varname, repaste(results, " ")); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::get_variable +// Access: Public +// Description: Returns the variable definition associated with the +// indicated variable name. +//////////////////////////////////////////////////////////////////// +string PPScope:: +get_variable(const string &varname) const { + string result; + if (p_get_variable(varname, result)) { + return result; + } + + // Check the scopes on the stack for the variable definition. + ScopeStack::reverse_iterator si; + for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) { + if ((*si)->p_get_variable(varname, result)) { + return result; + } + } + + // If the variable isn't defined, we check the environment. + const char *env = getenv(varname.c_str()); + if (env != (const char *)NULL) { + return env; + } + + // It's not defined anywhere, so it's implicitly empty. + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_variable +// Access: Public +// Description: Similar to get_variable(), except the variable +// definition is in turn expanded. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_variable(const string &varname) const { + return expand_string(get_variable(varname)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::get_directory +// Access: Public +// Description: Returns the directory level associated with this +// scope, if any, or with the nearest parent to this +// scope. +//////////////////////////////////////////////////////////////////// +PPDirectoryTree *PPScope:: +get_directory() const { + if (_directory != (PPDirectoryTree *)NULL) { + return _directory; + } + + // Check the stack. + ScopeStack::reverse_iterator si; + for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) { + if ((*si)->_directory != (PPDirectoryTree *)NULL) { + return (*si)->_directory; + } + } + + return (PPDirectoryTree *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::set_directory +// Access: Public +// Description: Associates this scope with the indicated directory +// level. Typically this is done when definition a +// scope for a particular source file which exists at a +// known directory level. +//////////////////////////////////////////////////////////////////// +void PPScope:: +set_directory(PPDirectoryTree *directory) { + _directory = directory; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_string +// Access: Public +// Description: Expands out all the variable references in the given +// string. Variables are expanded recursively; that is, +// if a variable expansion includes a reference to +// another variable name, the second variable name is +// expanded. However, cyclical references are not +// expanded. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_string(const string &str) const { + return r_expand_string(str, (ExpandedVariable *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_self_reference +// Access: Public +// Description: Similar to expand_string(), except that only simple +// references to the named variable are expanded--other +// variable references are left unchanged. This allows +// us to define a variable in terms of its previous +// definition. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_self_reference(const string &str, const string &varname) const { + // Look for a simple reference to the named variable. A more + // complex reference, like a computed variable name or something + // equally loopy, won't work with this simple test. Too bad. + string reference; + reference += VARIABLE_PREFIX; + reference += VARIABLE_OPEN_BRACE; + reference += varname; + reference += VARIABLE_CLOSE_BRACE; + + string result; + + size_t p = 0; + size_t q = str.find(reference, p); + while (q != string::npos) { + result += str.substr(p, q - p); + p = q; + result += r_expand_variable(str, p, (ExpandedVariable *)NULL); + q = str.find(reference, p); + } + + result += str.substr(p); + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::push_scope +// Access: Public, Static +// Description: Pushes the indicated scope onto the top of the stack. +// When a variable reference is unresolved in the +// current scope, the scope stack is searched, in LIFO +// order. +//////////////////////////////////////////////////////////////////// +void PPScope:: +push_scope(PPScope *scope) { + _scope_stack.push_back(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::pop_scope +// Access: Public, Static +// Description: Pops another level off the top of the stack. See +// push_scope(). +//////////////////////////////////////////////////////////////////// +PPScope *PPScope:: +pop_scope() { + assert(!_scope_stack.empty()); + PPScope *back = _scope_stack.back(); + _scope_stack.pop_back(); + return back; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::get_bottom_scope +// Access: Public, Static +// Description: Returns the scope on the bottom of the stack. This +// was the very first scope ever pushed, e.g. the global +// scope. +//////////////////////////////////////////////////////////////////// +PPScope *PPScope:: +get_bottom_scope() { + assert(!_scope_stack.empty()); + return _scope_stack.front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::p_set_variable +// Access: Private +// Description: The private implementation of p_set_variable. +// Returns true if the variable's definition is found +// and set, false otherwise. +//////////////////////////////////////////////////////////////////// +bool PPScope:: +p_set_variable(const string &varname, const string &definition) { + Variables::iterator vi; + vi = _variables.find(varname); + if (vi != _variables.end()) { + (*vi).second = definition; + return true; + } + + if (_parent_scope != (PPScope *)NULL) { + return _parent_scope->p_set_variable(varname, definition); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::p_get_variable +// Access: Private +// Description: The private implementation of get_variable(). This +// checks the local scope only; it does not check the +// stack. It returns true if the variable is defined, +// false otherwise.. +//////////////////////////////////////////////////////////////////// +bool PPScope:: +p_get_variable(const string &varname, string &result) const { + Variables::const_iterator vi; + vi = _variables.find(varname); + if (vi != _variables.end()) { + result = (*vi).second; + return true; + } + + if (varname == "RELDIR" && + _directory != (PPDirectoryTree *)NULL && + current_output_directory != (PPDirectoryTree *)NULL) { + // $[RELDIR] is a special variable name that evaluates to the + // relative directory of the current scope to the current output + // directory. + result = current_output_directory->get_rel_to(_directory); + return true; + } + + if (_parent_scope != (PPScope *)NULL) { + return _parent_scope->p_get_variable(varname, result); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::tokenize_params +// Access: Private +// Description: Separates a string into tokens based on comma +// delimiters, e.g. for parameters to a function. +// Nested variable references are skipped correctly, +// even if they include commas. Leading and trailing +// whitespace in each token is automatically stripped. +// +// If expand is true, the nested variables are +// automatically expanded as the string is tokenized; +// otherwise, they are left unexpanded. +//////////////////////////////////////////////////////////////////// +void PPScope:: +tokenize_params(const string &str, vector &tokens, + bool expand) const { + size_t p = 0; + while (p < str.length()) { + // Skip initial whitespace. + while (p < str.length() && isspace(str[p])) { + p++; + } + + string token; + while (p < str.length() && str[p] != FUNCTION_PARAMETER_SEPARATOR) { + if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX && + str[p + 1] == VARIABLE_OPEN_BRACE) { + // Skip a nested variable reference. + if (expand) { + token += r_expand_variable(str, p, (ExpandedVariable *)NULL); + } else { + token += r_scan_variable(str, p); + } + } else { + token += str[p]; + p++; + } + } + + // Back up past trailing whitespace. + size_t q = token.length(); + while (q > 0 && isspace(token[q - 1])) { + q--; + } + + tokens.push_back(token.substr(0, q)); + p++; + + if (p == str.length()) { + // In this case, we have just read past a trailing comma symbol + // at the end of the string, so we have one more empty token. + tokens.push_back(string()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::r_expand_string +// Access: Private +// Description: The recursive implementation of expand_string(). +// This function detects cycles in the variable +// expansion by storing the set of variable names that +// have thus far been expanded in the linked list. +//////////////////////////////////////////////////////////////////// +string PPScope:: +r_expand_string(const string &str, PPScope::ExpandedVariable *expanded) const { + string result; + + // Search for a variable reference. + size_t p = 0; + while (p < str.length()) { + if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX && + str[p + 1] == VARIABLE_OPEN_BRACE) { + // Here's a nested variable! Expand it fully. + result += r_expand_variable(str, p, expanded); + + } else { + result += str[p]; + p++; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::r_scan_variable +// Access: Private +// Description: Scans past a single variable reference without +// expanding it. On input, str is a string containing a +// variable reference (among other stuff), and vp is the +// position within the string of the prefix character at +// the beginning of the variable reference. +// +// On output, vp is set to the position within the +// string of the first character after the variable +// reference's closing bracket. The variable reference +// itself is returned. +//////////////////////////////////////////////////////////////////// +string PPScope:: +r_scan_variable(const string &str, size_t &vp) const { + + // Search for the end of the variable name: an unmatched square + // bracket. + size_t start = vp; + size_t p = vp + 2; + while (p < str.length() && str[p] != VARIABLE_CLOSE_BRACE) { + if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX && + str[p + 1] == VARIABLE_OPEN_BRACE) { + // Here's a nested variable! Scan past it, matching braces + // properly. + r_scan_variable(str, p); + } else { + p++; + } + } + + if (p < str.length()) { + assert(str[p] == VARIABLE_CLOSE_BRACE); + p++; + } else { + cerr << "Warning! Unclosed variable reference:\n" + << str.substr(vp) << "\n"; + } + + vp = p; + return str.substr(start, vp - start); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::r_expand_variable +// Access: Private +// Description: Expands a single variable reference. On input, str +// is a string containing a variable reference (among +// other stuff), and vp is the position within the +// string of the prefix character at the beginning of +// the variable reference. +// +// On output, vp is set to the position within the +// string of the first character after the variable +// reference's closing bracket, and the string expansion +// of the variable reference is returned. +//////////////////////////////////////////////////////////////////// +string PPScope:: +r_expand_variable(const string &str, size_t &vp, + PPScope::ExpandedVariable *expanded) const { + string varname; + + size_t whitespace_at = 0; + size_t open_nested_at = 0; + + // Search for the end of the variable name: an unmatched square + // bracket. + size_t p = vp + 2; + while (p < str.length() && str[p] != VARIABLE_CLOSE_BRACE) { + if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX && + str[p + 1] == VARIABLE_OPEN_BRACE) { + if (whitespace_at != 0) { + // Once we have encountered whitespace, we don't expand + // variables inline anymore. These are now function + // parameters, and might need to be expanded in some other + // scope. + varname += r_scan_variable(str, p); + } else { + varname += r_expand_variable(str, p, expanded); + } + + } else { + if (open_nested_at == 0 && str[p] == VARIABLE_OPEN_NESTED) { + open_nested_at = p - (vp + 2); + } + if (open_nested_at == 0 && whitespace_at == 0 && isspace(str[p])) { + whitespace_at = p - (vp + 2); + } + varname += str[p]; + p++; + } + } + + if (p < str.length()) { + assert(str[p] == VARIABLE_CLOSE_BRACE); + p++; + } else { + cerr << "Warning! Unclosed variable reference:\n" + << str.substr(vp) << "\n"; + } + + vp = p; + + // Check for a function expansion. + if (whitespace_at != 0) { + string funcname = varname.substr(0, whitespace_at); + p = whitespace_at; + while (p < varname.length() && isspace(varname[p])) { + p++; + } + string params = varname.substr(p); + + // Is it a built-in function? + + if (funcname == "wildcard") { + return expand_wildcard(params); + } else if (funcname == "isdir") { + return expand_isdir(params); + } else if (funcname == "libtest") { + return expand_libtest(params); + } else if (funcname == "bintest") { + return expand_bintest(params); + } else if (funcname == "shell") { + return expand_shell(params); + } else if (funcname == "firstword") { + return expand_firstword(params); + } else if (funcname == "patsubst") { + return expand_patsubst(params); + } else if (funcname == "subst") { + return expand_subst(params); + } else if (funcname == "filter") { + return expand_filter(params); + } else if (funcname == "filter_out" || funcname == "filter-out") { + return expand_filter_out(params); + } else if (funcname == "sort") { + return expand_sort(params); + } else if (funcname == "unique") { + return expand_unique(params); + } else if (funcname == "if") { + return expand_if(params); + } else if (funcname == "eq") { + return expand_eq(params); + } else if (funcname == "ne") { + return expand_ne(params); + } else if (funcname == "not") { + return expand_not(params); + } else if (funcname == "or") { + return expand_or(params); + } else if (funcname == "and") { + return expand_and(params); + } else if (funcname == "upcase") { + return expand_upcase(params); + } else if (funcname == "downcase") { + return expand_downcase(params); + } else if (funcname == "closure") { + return expand_closure(params); + } + + // It must be a map variable. + return expand_map_variable(funcname, params); + } + + // Now we have the variable name; was it previously expanded? + ExpandedVariable *ev; + for (ev = expanded; ev != (ExpandedVariable *)NULL; ev = ev->_next) { + if (ev->_varname == varname) { + // Yes, this is a cyclical expansion. + cerr << "Ignoring cyclical expansion of " << varname << "\n"; + return string(); + } + } + + // And now expand the variable. + string expansion; + + // Check for a special inline patsubst operation, like GNU make: + // $[varname:%.c=%.o] + string patsubst; + bool got_patsubst = false; + p = varname.find(variable_patsubst); + if (p != string::npos) { + got_patsubst = true; + patsubst = varname.substr(p + variable_patsubst.length()); + varname = varname.substr(0, p); + } + + // Check for special scoping operators in the variable name. + p = varname.find(VARIABLE_OPEN_NESTED); + if (p != string::npos && varname[varname.length() - 1] == VARIABLE_CLOSE_NESTED) { + size_t q = varname.length() - 1; + string scope_names = varname.substr(p + 1, q - (p + 1)); + varname = varname.substr(0, p); + expansion = expand_variable_nested(varname, scope_names); + + } else { + // No special scoping; just expand the variable name. + expansion = get_variable(varname); + } + + // Finally, recursively expand any variable references in the + // variable's expansion. + ExpandedVariable new_var; + new_var._varname = varname; + new_var._next = expanded; + string result = r_expand_string(expansion, &new_var); + + // And *then* apply any inline patsubst. + if (got_patsubst) { + vector tokens; + tokenize(patsubst, tokens, VARIABLE_PATSUBST_DELIM); + + if (tokens.size() != 2) { + cerr << "inline patsubst should be of the form " + << VARIABLE_PREFIX << VARIABLE_OPEN_BRACE << "varname" + << VARIABLE_PATSUBST << PATTERN_WILDCARD << ".c" + << VARIABLE_PATSUBST_DELIM << PATTERN_WILDCARD << ".o" + << VARIABLE_CLOSE_BRACE << ".\n"; + } else { + PPFilenamePattern from(tokens[0]); + PPFilenamePattern to(tokens[1]); + + if (!from.has_wildcard() || !to.has_wildcard()) { + cerr << "The two parameters of inline patsubst must both include " + << PATTERN_WILDCARD << ".\n"; + return string(); + } + + // Split the expansion into tokens based on the spaces. + vector words; + tokenize_whitespace(result, words); + + vector::iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + (*wi) = to.transform(*wi, from); + } + + result = repaste(words, " "); + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_variable_nested +// Access: Private +// Description: Expands a variable reference of the form +// $[varname(scope scope scope)]. This means to +// concatenate the expansions of the variable in all of +// the named scopes. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_variable_nested(const string &varname, + const string &scope_names) const { + if (_named_scopes == (PPNamedScopes *)NULL) { + return string(); + } + + vector names; + tokenize_whitespace(scope_names, names); + + // Get all of the named scopes. + PPNamedScopes::Scopes scopes; + + vector::const_iterator ni; + for (ni = names.begin(); ni != names.end(); ++ni) { + const string &name = (*ni); + _named_scopes->get_scopes(name, scopes); + } + + if (scopes.empty()) { + return string(); + } + + // Now go through the scopes and build up the results. + vector results; + + PPNamedScopes::Scopes::const_iterator si; + for (si = scopes.begin(); si != scopes.end(); ++si) { + PPScope *scope = (*si); + string nested = scope->expand_variable(varname); + if (!nested.empty()) { + results.push_back(nested); + } + } + + string result = repaste(results, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_wildcard +// Access: Private +// Description: Expands the "wildcard" function variable. This +// returns the set of files matched by the parameters +// with shell matching characters. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_wildcard(const string ¶ms) const { + vector results; + glob_string(expand_string(params), results); + + string result = repaste(results, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_isdir +// Access: Private +// Description: Expands the "isdir" function variable. This +// returns true if the parameter exists and is a +// directory, or false otherwise. This actually expands +// the parameter(s) with shell globbing characters, +// similar to the "wildcard" function, and looks only at +// the first expansion. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_isdir(const string ¶ms) const { + vector results; + glob_string(expand_string(params), results); + + if (results.empty()) { + // No matching file, too bad. + return string(); + } + + const string &filename = results[0]; + struct stat stbuf; + + string result; + if (stat(filename.c_str(), &stbuf) == 0) { + if (S_ISDIR(stbuf.st_mode)) { + result = filename; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_libtest +// Access: Private +// Description: Expands the "libtest" function variable. This +// serves as a poor man's autoconf feature to check to +// see if a library by the given name exists on the +// indicated search path, or on the system search path. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_libtest(const string ¶ms) const { + // Get the parameters out based on commas. The first parameter is a + // space-separated set of directories to search, the second + // parameter is a space-separated set of library names. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 2) { + cerr << "libtest requires two parameters.\n"; + return string(); + } + + vector directories; + tokenize_whitespace(tokens[0], directories); + + // Also add the system directories to the list, whatever we think + // those should be. Here we have to make a few assumptions. +#ifdef PLATFORM_WIN32 + const char *windir = getenv("WINDIR"); + if (windir != (const char *)NULL) { + directories.push_back(string(windir) + "\\System"); + directories.push_back(string(windir) + "\\System32"); + } + + const char *lib = getenv("LIB"); + if (lib != (const char *)NULL) { + tokenize(lib, directories, ";"); + } +#endif + + // We'll also check the Unix standard places, even if we're building + // Windows, since we might be using Cygwin. + + // Check LD_LIBRARY_PATH. + const char *ld_library_path = getenv("LD_LIBRARY_PATH"); + if (ld_library_path != (const char *)NULL) { + tokenize(ld_library_path, directories, ":"); + } + + directories.push_back("/lib"); + directories.push_back("/usr/lib"); + + vector libnames; + tokenize_whitespace(tokens[1], libnames); + + if (libnames.empty()) { + // No libraries is a default "false". + return string(); + } + + // We only bother to search for the first library name in the list. + string libname = libnames[0]; + + string found; + +#ifdef PLATFORM_WIN32 + if (libname.length() > 4 && libname.substr(libname.length() - 4) == ".lib") { + found = find_searchpath(directories, libname); + if (found.empty()) { + found = find_searchpath(directories, libname.substr(0, libname.length() - 4) + ".dll"); + } + } else { + found = find_searchpath(directories, "lib" + libname + ".lib"); + if (found.empty()) { + found = find_searchpath(directories, "lib" + libname + ".dll"); + } + } + +#else + found = find_searchpath(directories, "lib" + libname + ".a"); + if (found.empty()) { + found = find_searchpath(directories, "lib" + libname + ".so"); + } +#endif + + + return found; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_bintest +// Access: Private +// Description: Expands the "bintest" function variable. This +// serves as a poor man's autoconf feature to check to +// see if an executable program by the given name exists +// on the indicated search path, or on the system search +// path. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_bintest(const string ¶ms) const { + // We only have one parameter: the filename of the executable. We + // always search for it on the path. + string binname = expand_string(params); + + if (binname.empty()) { + // No binary, no exist. + return string(); + } + + // An explicit path from the root does not require a search. +#ifdef PLATFORM_WIN32 + if ((binname.length() > 2 && binname[1] == ':') || binname[0] == '/') +#else + if (binname[0] == '/') +#endif + { + if (access(binname.c_str(), F_OK) == 0) { + return binname; + } + return string(); + } + + const char *path = getenv("PATH"); + if (path == (const char *)NULL) { + // If the path is undefined, too bad. + return string(); + } + + string pathvar(path); + + vector directories; + +#ifdef PLATFORM_WIN32 + if (pathvar.find(';') != string::npos) { + // If the path contains semicolons, it's a native Windows-style + // path: split it up based on semicolons. + tokenize(pathvar, directories, ";"); + + } else { + // Otherwise, assume it's a Cygwin-style path: split it up based + // on colons. + tokenize(pathvar, directories, ":"); + } +#else + tokenize(pathvar, directories, ":"); +#endif + + string found; + +#ifdef PLATFORM_WIN32 + found = find_searchpath(directories, binname + ".exe"); + if (found.empty()) { + found = find_searchpath(directories, binname); + } + +#else + found = find_searchpath(directories, binname); +#endif + + return found; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_shell +// Access: Private +// Description: Expands the "shell" function variable. This executes +// the given command in a subprocess and returns the +// standard output. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_shell(const string ¶ms) const { + string command = expand_string(params); + int pid, status; + + int pd[2]; + if (pipe(pd) < 0) { + // pipe() failed. + perror("pipe"); + return string(); + } + + pid = fork(); + if (pid < 0) { + // fork() failed. + perror("fork"); + return string(); + } + + if (pid == 0) { + // Child. + dup2(pd[1], STDOUT_FILENO); + char *argv[4]; + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = (char *)command.c_str(); + argv[3] = (char *)NULL; + execv("/bin/sh", argv); + exit(127); + } + + // Parent. Wait for the child to terminate, and read from its + // output while we're waiting. + close(pd[1]); + bool child_done = false; + bool pipe_closed = false; + string output; + + while (!child_done && !pipe_closed) { + static const int buffer_size = 1024; + char buffer[buffer_size]; + int read_bytes = (int)read(pd[0], buffer, buffer_size); + if (read_bytes < 0) { + perror("read"); + } else if (read_bytes == 0) { + pipe_closed = true; + } else { + output += string(buffer, read_bytes); + } + + if (!child_done) { + int waitresult = waitpid(pid, &status, WNOHANG); + if (waitresult < 0) { + if (errno != EINTR) { + perror("waitpid"); + return string(); + } + } else if (waitresult > 0) { + child_done = true; + } + } + } + close(pd[0]); + + // Now get the output. We split it into words and then reconnect + // it, to simulate the shell's backpop operator. + vector results; + tokenize_whitespace(output, results); + + string result = repaste(results, " "); + + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_firstword +// Access: Private +// Description: Expands the "firstword" function variable. This +// returns the first of several words separated by +// whitespace. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_firstword(const string ¶ms) const { + // Split the parameter into tokens based on the spaces. + vector words; + tokenize_whitespace(expand_string(params), words); + + if (!words.empty()) { + return words[0]; + } + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_patsubst +// Access: Private +// Description: Expands the "patsubst" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_patsubst(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() < 3) { + cerr << "patsubst requires at least three parameters.\n"; + return string(); + } + + if ((tokens.size() % 2) != 1) { + cerr << "subst requires an odd number of parameters.\n"; + return string(); + } + + // Split the last parameter into tokens based on the spaces. + vector words; + tokenize_whitespace(tokens.back(), words); + + // Build up a vector of from/to patterns. + typedef vector Patterns; + typedef vector FromPatterns; + FromPatterns from; + Patterns to; + + size_t i; + for (i = 0; i < tokens.size() - 1; i += 2) { + // Each "from" pattern might be a collection of patterns separated + // by spaces. + from.push_back(Patterns()); + vector froms; + tokenize_whitespace(tokens[i], froms); + vector::const_iterator fi; + for (fi = froms.begin(); fi != froms.end(); ++fi) { + PPFilenamePattern pattern(*fi); + if (!pattern.has_wildcard()) { + cerr << "All the \"from\" parameters of patsubst must include " + << PATTERN_WILDCARD << ".\n"; + return string(); + } + from.back().push_back(pattern); + } + + // However, the corresponding "to" pattern is just one pattern. + to.push_back(PPFilenamePattern(tokens[i + 1])); + } + size_t num_patterns = from.size(); + assert(num_patterns == to.size()); + + vector::iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + bool matched = false; + for (i = 0; i < num_patterns && !matched; i++) { + Patterns::const_iterator pi; + for (pi = from[i].begin(); pi != from[i].end() && !matched; ++pi) { + if ((*pi).matches(*wi)) { + matched = true; + (*wi) = to[i].transform(*wi, (*pi)); + } + } + } + } + + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_filter +// Access: Private +// Description: Expands the "filter" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_filter(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 2) { + cerr << "filter requires two parameters.\n"; + return string(); + } + + // Split up the first parameter--the list of patterns to filter + // by--into tokens based on the spaces. + vector pattern_strings; + tokenize_whitespace(tokens[0], pattern_strings); + + vector patterns; + vector::const_iterator psi; + for (psi = pattern_strings.begin(); psi != pattern_strings.end(); ++psi) { + patterns.push_back(PPFilenamePattern(*psi)); + } + + // Split up the second parameter--the list of words to filter--into + // tokens based on the spaces. + vector words; + tokenize_whitespace(tokens[1], words); + + vector::iterator wi, wnext; + wnext = words.begin(); + for (wi = words.begin(); wi != words.end(); ++wi) { + const string &word = (*wi); + + bool matches_pattern = false; + vector::const_iterator pi; + for (pi = patterns.begin(); + pi != patterns.end() && !matches_pattern; + ++pi) { + matches_pattern = (*pi).matches(word); + } + + if (matches_pattern) { + *wnext++ = word; + } + } + + words.erase(wnext, words.end()); + + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_filter_out +// Access: Private +// Description: Expands the "filter_out" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_filter_out(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 2) { + cerr << "filter requires two parameters.\n"; + return string(); + } + + // Split up the first parameter--the list of patterns to filter + // by--into tokens based on the spaces. + vector pattern_strings; + tokenize_whitespace(tokens[0], pattern_strings); + + vector patterns; + vector::const_iterator psi; + for (psi = pattern_strings.begin(); psi != pattern_strings.end(); ++psi) { + patterns.push_back(PPFilenamePattern(*psi)); + } + + // Split up the second parameter--the list of words to filter--into + // tokens based on the spaces. + vector words; + tokenize_whitespace(tokens[1], words); + + vector::iterator wi, wnext; + wnext = words.begin(); + for (wi = words.begin(); wi != words.end(); ++wi) { + const string &word = (*wi); + + bool matches_pattern = false; + vector::const_iterator pi; + for (pi = patterns.begin(); + pi != patterns.end() && !matches_pattern; + ++pi) { + matches_pattern = (*pi).matches(word); + } + + if (!matches_pattern) { + *wnext++ = word; + } + } + + words.erase(wnext, words.end()); + + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_subst +// Access: Private +// Description: Expands the "subst" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_subst(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() < 3) { + cerr << "subst requires at least three parameters.\n"; + return string(); + } + + if ((tokens.size() % 2) != 1) { + cerr << "subst requires an odd number of parameters.\n"; + return string(); + } + + // Split the last parameter into tokens based on the spaces. + vector words; + tokenize_whitespace(tokens.back(), words); + + vector::iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + string &word = (*wi); + + // Check for the given word in the subst/replace strings. + bool found = false; + for (size_t i = 0; i < tokens.size() - 1 && !found; i += 2) { + if (tokens[i] == word) { + found = true; + word = tokens[i + 1]; + } + } + } + + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_sort +// Access: Private +// Description: Expands the "sort" function variable: sort the words +// into alphabetical order, and also remove duplicates. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_sort(const string ¶ms) const { + // Split the string up into tokens based on the spaces. + vector words; + tokenize_whitespace(expand_string(params), words); + + sort(words.begin(), words.end()); + words.erase(unique(words.begin(), words.end()), words.end()); + + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_unique +// Access: Private +// Description: Expands the "unique" function variable: remove +// duplicates from the list of words without changing +// the order. The first appearance of each word +// remains. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_unique(const string ¶ms) const { + // Split the string up into tokens based on the spaces. + vector words; + tokenize_whitespace(expand_string(params), words); + + vector::iterator win, wout; + set included_words; + + win = words.begin(); + wout = words.begin(); + while (win != words.end()) { + if (included_words.insert(*win).second) { + // This is a unique word so far. + *wout++ = *win; + } + ++win; + } + + words.erase(wout, words.end()); + string result = repaste(words, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_eq +// Access: Private +// Description: Expands the "if" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_if(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() == 2) { + if (!tokens[0].empty()) { + return tokens[1]; + } else { + return ""; + } + } else if (tokens.size() == 2) { + if (!tokens[0].empty()) { + return tokens[1]; + } else { + return tokens[2]; + } + } + + cerr << "if requires two or three parameters.\n"; + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_eq +// Access: Private +// Description: Expands the "eq" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_eq(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 2) { + cerr << "eq requires two parameters.\n"; + return string(); + } + + string result; + if (tokens[0] == tokens[1]) { + result = "1"; + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_ne +// Access: Private +// Description: Expands the "ne" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_ne(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 2) { + cerr << "ne requires two parameters.\n"; + return string(); + } + + string result; + if (!(tokens[0] == tokens[1])) { + result = "1"; + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_not +// Access: Private +// Description: Expands the "not" function variable. This returns +// nonempty if its argument is empty, empty if its +// argument is nonempty. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_not(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + if (tokens.size() != 1) { + cerr << "not requires two parameters.\n"; + return string(); + } + + string result; + if (tokens[0].empty()) { + result = "1"; + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_or +// Access: Private +// Description: Expands the "or" function variable. This returns +// nonempty if any of its arguments are nonempty. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_or(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + vector::const_iterator ti; + for (ti = tokens.begin(); ti != tokens.end(); ++ti) { + if (!(*ti).empty()) { + return "1"; + } + } + return ""; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_and +// Access: Private +// Description: Expands the "and" function variable. This returns +// nonempty if all of its arguments are nonempty. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_and(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, true); + + vector::const_iterator ti; + for (ti = tokens.begin(); ti != tokens.end(); ++ti) { + if ((*ti).empty()) { + return ""; + } + } + return "1"; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_upcase +// Access: Private +// Description: Expands the "upcase" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_upcase(const string ¶ms) const { + string result = expand_string(params); + string::iterator si; + for (si = result.begin(); si != result.end(); ++si) { + (*si) = toupper(*si); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_downcase +// Access: Private +// Description: Expands the "downcase" function variable. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_downcase(const string ¶ms) const { + string result = expand_string(params); + string::iterator si; + for (si = result.begin(); si != result.end(); ++si) { + (*si) = tolower(*si); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_closure +// Access: Private +// Description: Expands the "closure" function variable. This is a +// special function that recursively expands a map +// variable with the given parameter string until all +// definitions have been encountered. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_closure(const string ¶ms) const { + // Split the string up into tokens based on the commas. + vector tokens; + tokenize_params(params, tokens, false); + + if (tokens.size() != 2) { + cerr << "closure requires two parameters.\n"; + return string(); + } + + // The first parameter is the map variable name, the second + // parameter is the expression to close. + string varname = expand_string(tokens[0]); + string expression = tokens[1]; + + const MapVariableDefinition &def = find_map_variable(varname); + if (&def == &_null_map_def) { + cerr << "Warning: undefined map variable: " << varname << "\n"; + return string(); + } + + // Now evaluate the expression within this scope, and then again + // within each scope indicated by the result, and then within each + // scope indicated by *that* result, and so on. We need to keep + // track of the words we have already evaluated (hence the set), and + // we also need to keep track of all the partial results we have yet + // to evaluate (hence the vector of strings). + set closure; + vector partial_results; + + partial_results.push_back(expand_string(expression)); + + while (!partial_results.empty()) { + // Pull off one of the partial results (it doesn't matter which + // one), and chop it up into its constituent words. + vector pass; + tokenize_whitespace(partial_results.back(), pass); + partial_results.pop_back(); + + // And then map each of those words into scopes. + vector::const_iterator wi; + for (wi = pass.begin(); wi != pass.end(); ++wi) { + const string &word = (*wi); + bool inserted = closure.insert(word).second; + if (inserted) { + // This is a new word, which presumably maps to a scope. What + // does the expression evaluate to within that scope? + + MapVariableDefinition::const_iterator mvi; + mvi = def.find(word); + if (mvi != def.end()) { + PPScope *scope = (*mvi).second; + partial_results.push_back(scope->expand_string(expression)); + } + } + } + } + + // Now we have the complete transitive closure of $[mapvar expression]. + vector results; + set::const_iterator ci; + for (ci = closure.begin(); ci != closure.end(); ++ci) { + results.push_back(*ci); + } + + string result = repaste(results, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_map_variable +// Access: Private +// Description: Expands a map variable function reference. This +// looks up the given keys in the map and expands the +// first parameter for each corresponding scope. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_map_variable(const string &varname, const string ¶ms) const { + // Split the string up into tokens based on the commas, but don't + // expand the variables yet. + vector tokens; + tokenize_params(params, tokens, false); + + if (tokens.size() != 2) { + cerr << "map variable expansions require two parameters: $[" + << varname << " " << params << "]\n"; + return string(); + } + + // Split the second parameter into tokens based on the spaces. This + // is the set of keys. + vector keys; + tokenize_whitespace(expand_string(tokens[1]), keys); + + return expand_map_variable(varname, tokens[0], keys); +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::expand_map_variable +// Access: Private +// Description: Expands a map variable function reference. This +// looks up the given keys in the map and expands the +// expression for each corresponding scope. +//////////////////////////////////////////////////////////////////// +string PPScope:: +expand_map_variable(const string &varname, const string &expression, + const vector &keys) const { + const MapVariableDefinition &def = find_map_variable(varname); + if (&def == &_null_map_def) { + cerr << "Warning: undefined map variable: " << varname << "\n"; + return string(); + } + + vector results; + + // Now build up the set of expansions of the expression in the + // various scopes indicated by the keys. + vector::const_iterator wi; + for (wi = keys.begin(); wi != keys.end(); ++wi) { + MapVariableDefinition::const_iterator di; + di = def.find(*wi); + if (di != def.end()) { + PPScope *scope = (*di).second; + string expansion = scope->expand_string(expression); + if (!expansion.empty()) { + results.push_back(expansion); + } + } + } + + string result = repaste(results, " "); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::find_map_variable +// Access: Private +// Description: Looks for the map variable definition in this scope +// or some ancestor scope. +//////////////////////////////////////////////////////////////////// +const PPScope::MapVariableDefinition &PPScope:: +find_map_variable(const string &varname) const { + const MapVariableDefinition &def = p_find_map_variable(varname); + if (&def != &_null_map_def) { + return def; + } + + // No such map variable. Check the stack. + ScopeStack::reverse_iterator si; + for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) { + const MapVariableDefinition &def = (*si)->p_find_map_variable(varname); + if (&def != &_null_map_def) { + return def; + } + } + + // Nada. + return _null_map_def; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::p_find_map_variable +// Access: Private +// Description: The implementation of find_map_variable() for a +// particular static scope, without checking the stack. +//////////////////////////////////////////////////////////////////// +const PPScope::MapVariableDefinition &PPScope:: +p_find_map_variable(const string &varname) const { + MapVariables::const_iterator mvi; + mvi = _map_variables.find(varname); + if (mvi != _map_variables.end()) { + return (*mvi).second; + } + + if (_parent_scope != (PPScope *)NULL) { + return _parent_scope->find_map_variable(varname); + } + + return _null_map_def; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPScope::glob_string +// Access: Private +// Description: Expands the words in the string as if they were a set +// of filenames using the shell globbing characters. +// Fills up the results vector (which the user should +// ensure is empty before calling) with the set of all +// files that actually match the globbing characters. +//////////////////////////////////////////////////////////////////// +void PPScope:: +glob_string(const string &str, vector &results) const { + vector words; + tokenize_whitespace(str, words); + + vector::const_iterator wi; + + glob_t pglob; + memset(&pglob, 0, sizeof(pglob)); + + int flags = 0; + for (wi = words.begin(); wi != words.end(); ++wi) { + glob((*wi).c_str(), flags, NULL, &pglob); + flags |= GLOB_APPEND; + } + + for (int i = 0; i < (int)pglob.gl_pathc; i++) { + results.push_back(string(pglob.gl_pathv[i])); + } + + globfree(&pglob); +} diff --git a/ppremake/ppScope.h b/ppremake/ppScope.h new file mode 100644 index 0000000000..75b925f2c1 --- /dev/null +++ b/ppremake/ppScope.h @@ -0,0 +1,123 @@ +// Filename: ppScope.h +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPSCOPE_H +#define PPSCOPE_H + +#include "ppremake.h" + +#include +#include + +class PPNamedScopes; +class PPDirectoryTree; + +/////////////////////////////////////////////////////////////////// +// Class : PPScope +// Description : Defines a (possibly nested) scope for variable +// definitions. Variables may be defined in a +// system-wide variable file, in a template file, or in +// an individual source file. +//////////////////////////////////////////////////////////////////// +class PPScope { +public: + PPScope(PPNamedScopes *named_scopes); + + PPNamedScopes *get_named_scopes() const; + + void set_parent(PPScope *parent); + PPScope *get_parent() const; + + void define_variable(const string &varname, const string &definition); + bool set_variable(const string &varname, const string &definition); + void define_map_variable(const string &varname, const string &definition); + void define_map_variable(const string &varname, const string &key_varname, + const string &scope_names); + + string get_variable(const string &varname) const; + string expand_variable(const string &varname) const; + + PPDirectoryTree *get_directory() const; + void set_directory(PPDirectoryTree *directory); + + string expand_string(const string &str) const; + string expand_self_reference(const string &str, const string &varname) const; + + static void push_scope(PPScope *scope); + static PPScope *pop_scope(); + static PPScope *get_bottom_scope(); + +private: + class ExpandedVariable { + public: + string _varname; + ExpandedVariable *_next; + }; + + typedef map MapVariableDefinition; + + bool p_set_variable(const string &varname, const string &definition); + bool p_get_variable(const string &varname, string &result) const; + + void tokenize_params(const string &str, vector &tokens, + bool expand) const; + + string r_expand_string(const string &str, ExpandedVariable *expanded) const; + string r_scan_variable(const string &str, size_t &vp) const; + string r_expand_variable(const string &str, size_t &vp, + PPScope::ExpandedVariable *expanded) const; + string expand_variable_nested(const string &varname, + const string &scope_names) const; + + string expand_wildcard(const string ¶ms) const; + string expand_isdir(const string ¶ms) const; + string expand_libtest(const string ¶ms) const; + string expand_bintest(const string ¶ms) const; + string expand_shell(const string ¶ms) const; + string expand_firstword(const string ¶ms) const; + string expand_patsubst(const string ¶ms) const; + string expand_filter(const string ¶ms) const; + string expand_filter_out(const string ¶ms) const; + string expand_subst(const string ¶ms) const; + string expand_sort(const string ¶ms) const; + string expand_unique(const string ¶ms) const; + string expand_if(const string ¶ms) const; + string expand_eq(const string ¶ms) const; + string expand_ne(const string ¶ms) const; + string expand_not(const string ¶ms) const; + string expand_or(const string ¶ms) const; + string expand_and(const string ¶ms) const; + string expand_upcase(const string ¶ms) const; + string expand_downcase(const string ¶ms) const; + string expand_closure(const string ¶ms) const; + string expand_map_variable(const string &varname, const string ¶ms) const; + string expand_map_variable(const string &varname, const string &expression, + const vector &keys) const; + + const MapVariableDefinition & + find_map_variable(const string &varname) const; + const MapVariableDefinition & + p_find_map_variable(const string &varname) const; + + void glob_string(const string &str, vector &results) const; + + PPNamedScopes *_named_scopes; + + PPDirectoryTree *_directory; + + typedef map Variables; + Variables _variables; + + typedef map MapVariables; + MapVariables _map_variables; + static MapVariableDefinition _null_map_def; + + PPScope *_parent_scope; + typedef vector ScopeStack; + static ScopeStack _scope_stack; +}; + + +#endif diff --git a/ppremake/ppSubroutine.cxx b/ppremake/ppSubroutine.cxx new file mode 100644 index 0000000000..e2299271f3 --- /dev/null +++ b/ppremake/ppSubroutine.cxx @@ -0,0 +1,48 @@ +// Filename: ppSubroutine.cxx +// Created by: drose (10Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppSubroutine.h" + +PPSubroutine::Subroutines PPSubroutine::_subroutines; + +//////////////////////////////////////////////////////////////////// +// Function: PPSubroutine::define_sub +// Access: Public, Static +// Description: Adds a subroutine to the global list with the +// indicated name. The subroutine pointer must have +// been recently allocated, and ownership of the pointer +// will be passed to the global list; it may later +// delete it if another subroutine is defined with the +// same name. +//////////////////////////////////////////////////////////////////// +void PPSubroutine:: +define_sub(const string &name, PPSubroutine *sub) { + Subroutines::iterator si; + si = _subroutines.find(name); + if (si == _subroutines.end()) { + _subroutines.insert(Subroutines::value_type(name, sub)); + } else { + delete (*si).second; + (*si).second = sub; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PPSubroutine::get_sub +// Access: Public, Static +// Description: Returns the previously-defined subroutine with the +// given name, or NULL if there is no such subroutine +// with that name. +//////////////////////////////////////////////////////////////////// +const PPSubroutine *PPSubroutine:: +get_sub(const string &name) { + Subroutines::const_iterator si; + si = _subroutines.find(name); + if (si == _subroutines.end()) { + return NULL; + } else { + return (*si).second; + } +} diff --git a/ppremake/ppSubroutine.h b/ppremake/ppSubroutine.h new file mode 100644 index 0000000000..a51dfd97e2 --- /dev/null +++ b/ppremake/ppSubroutine.h @@ -0,0 +1,34 @@ +// Filename: ppSubroutine.h +// Created by: drose (10Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PPSUBROUTINE_H +#define PPSUBROUTINE_H + +#include "ppremake.h" + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : PPSubroutine +// Description : This represents a named subroutine defined via the +// #defsub .. #end sequence that may be invoked at any +// time via #call. All subroutine definitions are +// global. +//////////////////////////////////////////////////////////////////// +class PPSubroutine { +public: + vector _lines; + +public: + static void define_sub(const string &name, PPSubroutine *sub); + static const PPSubroutine *get_sub(const string &name); + + typedef map Subroutines; + static Subroutines _subroutines; +}; + +#endif + diff --git a/ppremake/ppremake.cxx b/ppremake/ppremake.cxx new file mode 100644 index 0000000000..9ca60e96c6 --- /dev/null +++ b/ppremake/ppremake.cxx @@ -0,0 +1,124 @@ +// Filename: ppremake.cxx +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "ppremake.h" +#include "ppMain.h" +#include "ppScope.h" + +#ifdef HAVE_GETOPT +#include +#else +#include +#endif + +static void +usage() { + cerr << + "\n" + "ppremake [opts] subdir-name [subdir-name..]\n" + "ppremake\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" + "key variable definitions for processing, as well as pointing out the\n" + "locations of further config files.\n\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" + "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" + "is equivalent and appropriate to the particular build environment in use.\n\n" + + "The parameters are the names of the subdirectories (their local names, not\n" + "the relative or full paths to them) that are to be processed. All\n" + "subdirectories (that contain a file named " SOURCE_FILENAME ") will be\n" + "scanned, but only the named subdirectories will have output files\n" + "generated. If no parameter is given, then all directories will be\n" + "processed.\n\n" + + "Options:\n\n" + + " -h Display this page.\n" + " -V Report the version of ppremake, and exit.\n" + " -P Report the current platform name, and exit.\n\n" + " -p platform Build as if for the indicated platform name.\n\n"; +} + +static void +report_version() { + cerr << "This is " << PACKAGE << " version " << VERSION << ".\n"; +} + +static void +report_platform() { + cerr << "ppremake built for platform " << PLATFORM << ".\n"; +} + +int +main(int argc, char *argv[]) { + extern char *optarg; + extern int optind; + const char *optstr = "hVPp:"; + + string platform = PLATFORM; + int flag = getopt(argc, argv, optstr); + + while (flag != EOF) { + switch (flag) { + case 'h': + usage(); + exit(0); + + case 'V': + report_version(); + exit(0); + break; + + case 'P': + report_platform(); + exit(0); + break; + + case 'p': + platform = optarg; + break; + + default: + exit(1); + } + flag = getopt(argc, argv, optstr); + } + + argc -= (optind-1); + argv += (optind-1); + + PPScope global_scope((PPNamedScopes *)NULL); + global_scope.define_variable("PROGRAM", PACKAGE); + global_scope.define_variable("PROGVER", VERSION); + global_scope.define_variable("PLATFORM", platform); + + PPMain ppmain(&global_scope); + if (!ppmain.read_source(".")) { + exit(1); + } + + if (argc < 2) { + if (!ppmain.process_all()) { + exit(1); + } + } else { + for (int i = 1; i < argc; i++) { + if (!ppmain.process(argv[i])) { + cerr << "Unable to process " << argv[i] << ".\n"; + exit(1); + } + } + } + + cerr << "No errors.\n"; + return (0); +} diff --git a/ppremake/ppremake.h b/ppremake/ppremake.h new file mode 100644 index 0000000000..09be351a5d --- /dev/null +++ b/ppremake/ppremake.h @@ -0,0 +1,51 @@ +/* +// Filename: ppremake.h +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// +*/ + +#ifndef PPREMAKE_H +#define PPREMAKE_H + +#include "config.h" + +#ifdef __cplusplus +#ifdef HAVE_IOSTREAM +#include +#include +#else +#include +#include +#endif + +#include + +#ifdef HAVE_NAMESPACE +using namespace std; +#endif +#endif /* __cplusplus */ + +#define PACKAGE_FILENAME "Package.pp" +#define SOURCE_FILENAME "Sources.pp" + +#define DIRECTORY_SEPARATOR '/' +#define COMMAND_PREFIX '#' +#define VARIABLE_PREFIX '$' +#define VARIABLE_OPEN_BRACE '[' +#define VARIABLE_CLOSE_BRACE ']' +#define PATTERN_WILDCARD '%' +#define BEGIN_COMMENT "//" + +#define FUNCTION_PARAMETER_SEPARATOR ',' + +#define VARIABLE_OPEN_NESTED '(' +#define VARIABLE_CLOSE_NESTED ')' +#define VARIABLE_PATSUBST ":" +#define VARIABLE_PATSUBST_DELIM "=" + +#define SCOPE_DIRNAME_SEPARATOR '/' +#define SCOPE_DIRNAME_WILDCARD "*" +#define SCOPE_DIRNAME_CURRENT "." + +#endif diff --git a/ppremake/tokenize.cxx b/ppremake/tokenize.cxx new file mode 100644 index 0000000000..5e6ea4c32f --- /dev/null +++ b/ppremake/tokenize.cxx @@ -0,0 +1,134 @@ +// Filename: tokenize.cxx +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "tokenize.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: tokenize +// Description: Chops the source string up into pieces delimited by +// any of the characters specified in delimiters. +// Repeated delimiter characters represent zero-length +// tokens. +// +// It is the user's responsibility to ensure the output +// vector is cleared before calling this function; the +// results will simply be appended to the end of the +// vector. +//////////////////////////////////////////////////////////////////// +void +tokenize(const string &source, vector &tokens, + const string &delimiters) { + size_t p = 0; + while (p < source.length()) { + size_t q = source.find_first_of(delimiters, p); + if (q == string::npos) { + tokens.push_back(source.substr(p)); + return; + } + tokens.push_back(source.substr(p, q - p)); + p = q + 1; + } + tokens.push_back(string()); +} + +//////////////////////////////////////////////////////////////////// +// Function: tokenize_whitespace +// Description: Chops the source string up into pieces delimited by +// whitespace characters. It is different from +// tokenize() in that repeated whitespace characters are +// not significant. +// +// It is the user's responsibility to ensure the output +// vector is cleared before calling this function; the +// results will simply be appended to the end of the +// vector. +//////////////////////////////////////////////////////////////////// +void +tokenize_whitespace(const string &source, vector &tokens) { + // First, start at the first non-whitespace character. + size_t p = 0; + while (p < source.length() && isspace(source[p])) { + p++; + } + + while (p < source.length()) { + // Now scan to the end of the word. + size_t q = p; + while (q < source.length() && !isspace(source[q])) { + q++; + } + tokens.push_back(source.substr(p, q - p)); + p = q; + + while (p < source.length() && isspace(source[p])) { + p++; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: repaste +// Description: Returns a string representing the given sequence of +// tokens concatenated together with the separator +// string between them. +//////////////////////////////////////////////////////////////////// +string +repaste(const vector &tokens, const string &separator) { + string result; + if (!tokens.empty()) { + vector::const_iterator ti; + ti = tokens.begin(); + result += (*ti); + ++ti; + + while (ti != tokens.end()) { + result += separator; + result += (*ti); + ++ti; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: trim_blanks +// Description: Returns a new string, equivalent to the source +// string, but with the leading and trailing whitespace +// removed. +//////////////////////////////////////////////////////////////////// +string +trim_blanks(const string &str) { + size_t p = 0; + while (p < str.length() && isspace(str[p])) { + p++; + } + + size_t q = str.length(); + while (q > p && isspace(str[q - 1])) { + q--; + } + + return str.substr(p, q - p); +} + +//////////////////////////////////////////////////////////////////// +// Function: contains_whitespace +// Description: Returns true if the string contains any whitespace +// characters, false if it does not. +//////////////////////////////////////////////////////////////////// +bool +contains_whitespace(const string &str) { + string::const_iterator si; + for (si = str.begin(); si != str.end(); ++si) { + if (isspace(*si)) { + return true; + } + } + + return false; +} diff --git a/ppremake/tokenize.h b/ppremake/tokenize.h new file mode 100644 index 0000000000..426f036b6b --- /dev/null +++ b/ppremake/tokenize.h @@ -0,0 +1,30 @@ +// Filename: tokenize.h +// Created by: drose (25Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TOKENIZE_H +#define TOKENIZE_H + +#include "ppremake.h" + +#include + +// A couple of handy functions for breaking up a string into tokens, +// and repasting the tokens back into a string. + +void tokenize(const string &source, vector &tokens, + const string &delimiters); + +void tokenize_whitespace(const string &source, vector &tokens); + +string repaste(const vector &tokens, const string &separator); + +// And this is just handy to have. +string trim_blanks(const string &str); + +// So is this. +bool contains_whitespace(const string &str); + +#endif +