Browse Source

more VC++ support

David Rose 23 years ago
parent
commit
8579e7c138

+ 4 - 1
ppremake/Makefile.am

@@ -5,7 +5,6 @@ ppremake_SOURCES =							\
     dSearchPath.I dSearchPath.cxx dSearchPath.h				\
     executionEnvironment.cxx executionEnvironment.h			\
     filename.I filename.cxx filename.h					\
-    find_searchpath.cxx find_searchpath.h				\
     gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h			\
     ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx		\
     ppDependableFile.h ppDirectory.cxx					\
@@ -18,3 +17,7 @@ ppremake_SOURCES =							\
     sedCommand.h sedContext.cxx sedContext.h sedProcess.cxx		\
     sedProcess.h sedScript.cxx sedScript.h tokenize.cxx			\
     tokenize.h vector_string.h
+
+# Extra files for VC++ project description
+EXTRA_DIST =							\
+    ppremake.sln ppremake.vcproj ppremake.vsdir ppremake.vsz

+ 0 - 25
ppremake/find_searchpath.cxx

@@ -1,25 +0,0 @@
-// Filename: find_searchpath.cxx
-// Created by:  drose (09Oct00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "find_searchpath.h"
-#include "include_access.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-string
-find_searchpath(const vector<string> &directories, const string &filename) {
-  vector<string>::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();
-}

+ 0 - 20
ppremake/find_searchpath.h

@@ -1,20 +0,0 @@
-// Filename: find_searchpath.h
-// Created by:  drose (09Oct00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef FIND_SEARCHPATH_H
-#define FIND_SEARCHPATH_H
-
-#include "ppremake.h"
-
-#include <vector>
-
-// 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<string> &directories,
-                       const string &filename);
-
-#endif
-

+ 0 - 25
ppremake/include_access.h

@@ -1,25 +0,0 @@
-// Filename: include_access.cxx
-// Created by:  drose (21May02)
-// 
-////////////////////////////////////////////////////////////////////
-
-#ifndef INCLUDE_ACCESS_H
-#define INCLUDE_ACCESS_H
-
-// This file includes whatever is necessary to define the access()
-// function.
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef WIN32_VC
-#include <io.h>      // Windows requires this for access()
-#define access _access
-#define F_OK 00
-#define W_OK 02
-#define R_OK 04
-#endif  // WIN32_VC
-
-#endif  // INCLUDE_ACCESS_H
-

+ 78 - 39
ppremake/ppCommandFile.cxx

@@ -8,7 +8,6 @@
 #include "ppNamedScopes.h"
 #include "ppSubroutine.h"
 #include "tokenize.h"
-#include "include_access.h"
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -1197,7 +1196,9 @@ handle_sinclude_command() {
     filename = filename.substr(1, filename.length() - 2);
   }
 
-  if (access(filename.c_str(), F_OK) != 0) {
+  Filename fn(filename);
+
+  if (!fn.exists()) {
     // No such file; no error.
     return true;
   }
@@ -1642,9 +1643,10 @@ replay_formap(const string &varname, const string &mapvar) {
 //               contents; otherwise, leave the original alone.
 ////////////////////////////////////////////////////////////////////
 bool PPCommandFile::
-compare_output(const string &new_contents, const string &filename,
+compare_output(const string &new_contents, Filename filename,
                bool notouch) {
-  bool exists = (access(filename.c_str(), F_OK) == 0);
+  filename.set_text();
+  bool exists = filename.exists();
   bool differ = false;
 
   if (exists) {
@@ -1652,55 +1654,92 @@ compare_output(const string &new_contents, const string &filename,
     size_t want_bytes = len + 1;
 
     char *orig_contents = new char[want_bytes];
-    ifstream in(filename.c_str());
-    in.read(orig_contents, want_bytes);
-
-    if (in.gcount() != len) {
-      // The wrong number of bytes.
+    ifstream in;
+    if (!filename.open_read(in)) {
+      cerr << "Cannot read existing " << filename << ", regenerating.\n";
       differ = true;
-
     } else {
-      differ = !(new_contents == string(orig_contents, len));
+      in.read(orig_contents, want_bytes);
+
+      if (in.gcount() != len) {
+        // The wrong number of bytes.
+        differ = true;
+        
+      } else {
+        differ = !(new_contents == string(orig_contents, len));
+      }
     }
   }
 
   if (differ || !exists) {
-    cerr << "Generating " << filename << "\n";
-
-    if (exists) {
-      if (unlink(filename.c_str()) < 0) {
-        cerr << "Unable to remove old " << filename << "\n";
+#ifndef WIN32_VC
+    if (verbose_dry_run) {
+      // Write our new contents to a file so we can run diff on both
+      // of them.
+      Filename temp_filename = filename.get_fullpath() + string(".ppd");
+      temp_filename.set_text();
+      ofstream out_b;
+      if (!temp_filename.open_write(out_b)) {
+        cerr << "Unable to open temporary file " << filename << " for writing.\n";
         return false;
       }
-    }
-
-#ifdef WIN32_VC
-    ofstream out_b(filename.c_str(), ios::out);
-#else  // WIN32_VC
-    ofstream out_b(filename.c_str(), ios::out, 0666);
-#endif  // WIN32_VC
-    if (!out_b) {
-      cerr << "Unable to open file " << filename << " for writing.\n";
-      return false;
-    }
-
-    out_b.write(new_contents.data(), new_contents.length());
+      
+      out_b.write(new_contents.data(), new_contents.length());
 
-    if (!out_b) {
-      cerr << "Unable to write to file " << filename << "\n";
-      return false;
-    }
-    out_b.close();
+      bool diff_ok = true;
+      if (!out_b) {
+        cerr << "Unable to write to temporary file " << filename << "\n";
+        diff_ok = true;
+      }
+      out_b.close();
+      string command = "diff -u '" + filename.get_fullpath() + "' '" + 
+        temp_filename.get_fullpath() + "'";
+      int sys_result = system(command.c_str());
+      if (sys_result < 0) {
+        cerr << "Unable to invoke diff\n";
+        diff_ok = false;
+      }
+      out_b.close();
+      temp_filename.unlink();
 
+      return diff_ok;
+      
+    } else
+#endif
+      if (dry_run) {
+        cerr << "Would generate " << filename << "\n";
+      } else {
+        cerr << "Generating " << filename << "\n";
+        
+        if (exists) {
+          if (!filename.unlink()) {
+            cerr << "Unable to remove old " << filename << "\n";
+            return false;
+          }
+        }
+        
+        ofstream out_b;
+        if (!filename.open_write(out_b)) {
+          cerr << "Unable to open file " << filename << " for writing.\n";
+          return false;
+        }
+        
+        out_b.write(new_contents.data(), new_contents.length());
+        
+        if (!out_b) {
+          cerr << "Unable to write to file " << filename << "\n";
+          return false;
+        }
+        out_b.close();
+      }
+      
   } else {
-    //    cerr << "File " << filename << " is unchanged.\n";
-
     // Even though the file is unchanged, unless the "notouch" flag is
     // set, we want to update the modification time.  This helps the
     // makefiles know we did something.
-    if (!notouch) {
-      if (utime(filename.c_str(), (struct utimbuf *)NULL) < 0) {
-        cerr << "Warning: unable to touch " << filename << "\n";
+    if (!notouch && !dry_run) {
+      if (!filename.touch()) {
+        cerr << "Warning: unable to update timestamp for " << filename << "\n";
       }
     }
   }

+ 2 - 1
ppremake/ppCommandFile.h

@@ -7,6 +7,7 @@
 #define PPCOMMANDFILE_H
 
 #include "ppremake.h"
+#include "filename.h"
 
 #include <map>
 #include <vector>
@@ -68,7 +69,7 @@ protected:
   bool replay_forscopes(const string &name);
   bool replay_foreach(const string &varname, const vector<string> &words);
   bool replay_formap(const string &varname, const string &mapvar);
-  bool compare_output(const string &new_contents, const string &filename,
+  bool compare_output(const string &new_contents, Filename filename,
                       bool notouch);
   bool failed_if() const;
 

+ 4 - 1
ppremake/ppDependableFile.cxx

@@ -6,6 +6,7 @@
 #include "ppDependableFile.h"
 #include "ppDirectory.h"
 #include "ppDirectoryTree.h"
+#include "filename.h"
 #include "check_include.h"
 
 #ifdef HAVE_UNISTD_H
@@ -441,7 +442,9 @@ stat_file() {
 
   _flags |= F_statted;
   struct stat st;
-  if (stat(get_pathname().c_str(), &st) < 0) {
+  Filename pathname(get_pathname());
+  string ospath = pathname.to_os_specific();
+  if (stat(ospath.c_str(), &st) < 0) {
     // The file doesn't exist!
     return;
   }

+ 48 - 82
ppremake/ppDirectory.cxx

@@ -10,7 +10,6 @@
 #include "ppCommandFile.h"
 #include "ppDependableFile.h"
 #include "tokenize.h"
-#include "include_access.h"
 
 #ifdef HAVE_DIRENT_H
 #include <dirent.h>
@@ -372,13 +371,13 @@ report_depends() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PPDirectory::report_needs
+//     Function: PPDirectory::report_reverse_depends
 //       Access: Public
-//  Description: Reports all the directories that depend on (need) the
+//  Description: Reports all the directories that depend on the
 //               current directory.
 ////////////////////////////////////////////////////////////////////
 void PPDirectory::
-report_needs() const {
+report_reverse_depends() const {
   if (_depends_on_me.empty()) {
     cerr << _dirname << " is needed by no other directories.\n";
 
@@ -402,7 +401,7 @@ report_needs() const {
 ////////////////////////////////////////////////////////////////////
 bool PPDirectory::
 r_scan(const string &prefix) {
-  string root_name = ".";
+  Filename root_name = ".";
   if (!prefix.empty()) {
     root_name = prefix.substr(0, prefix.length() - 1);
   }
@@ -410,55 +409,11 @@ r_scan(const string &prefix) {
   // Collect all the filenames in the directory in this vector first,
   // so we can sort them.
   vector<string> filenames;
-
-#ifdef WIN32_VC
-  // Use FindFirstFile()/FindNextFile() to walk through the list of
-  // files in a directory.
-  string match;
-  if (root_name.empty()) {
-    match = "*.*";
-  } else {
-    match = root_name + "\\*.*";
-  }
-  WIN32_FIND_DATA find_data;
-
-  HANDLE handle = FindFirstFile(match.c_str(), &find_data);
-  if (handle == INVALID_HANDLE_VALUE) {
-    if (GetLastError() == ERROR_NO_MORE_FILES) {
-      // No matching files is not an error.
-      return true;
-    }
-    return false;
-  }
-
-  do {
-    string filename = find_data.cFileName;
-    if (filename != "." && filename != "..") {
-      filenames.push_back(filename);
-    }
-  } while (FindNextFile(handle, &find_data));
-
-  bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
-  FindClose(handle);
-
-#else  // WIN32_VC  
-  DIR *root = opendir(root_name.c_str());
-  if (root == (DIR *)NULL) {
+  if (!root_name.scan_directory(filenames)) {
     cerr << "Unable to scan directory " << root_name << "\n";
     return false;
   }
 
-  struct dirent *d;
-  d = readdir(root);
-  while (d != (struct dirent *)NULL) {
-    filenames.push_back(d->d_name);
-    d = readdir(root);
-  }
-  closedir(root);
-#endif  // WIN32_VC
-
-  sort(filenames.begin(), filenames.end());
-
   vector<string>::const_iterator fi;
   for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
     string filename = (*fi);
@@ -467,9 +422,8 @@ r_scan(const string &prefix) {
       // Is this possibly a subdirectory with its own Sources.pp
       // within it?
       string next_prefix = prefix + filename + "/";
-      string source_filename = next_prefix + SOURCE_FILENAME;
-      if (access(source_filename.c_str(), F_OK) == 0) {
-
+      Filename source_filename = next_prefix + SOURCE_FILENAME;
+      if (source_filename.exists()) {
         PPDirectory *subtree = new PPDirectory(filename, this);
 
         if (!subtree->r_scan(next_prefix)) {
@@ -715,26 +669,8 @@ read_file_dependencies(const string &cache_filename) {
 ////////////////////////////////////////////////////////////////////
 void PPDirectory::
 update_file_dependencies(const string &cache_filename) {
-  // Open up the dependency cache file in the directory.
-  string cache_pathname = get_path() + "/" + cache_filename;
-  unlink(cache_pathname.c_str());
-
-  // If we have no files, don't bother writing the cache.
-  if (!_dependables.empty()) {
-    bool wrote_anything = false;
-
-#ifdef WIN32_VC
-    ofstream out(cache_pathname.c_str(), ios::out);
-#else
-    ofstream out(cache_pathname.c_str(), ios::out, 0666);
-#endif
-    if (!out) {
-      cerr << "Cannot update cache dependency file " << cache_pathname << "\n";
-      return;
-    }
-    
-    // Walk through our list of dependable files, writing them out the
-    // the cache file.
+  if (dry_run) {
+    // If this is just a dry run, just report circularities.
     Dependables::const_iterator di;
     for (di = _dependables.begin(); di != _dependables.end(); ++di) {
       PPDependableFile *file = (*di).second;
@@ -743,20 +679,50 @@ update_file_dependencies(const string &cache_filename) {
           cerr << "Warning: circular #include directives:\n"
                << "  " << file->get_circularity() << "\n";
         }
-        file->write_cache(out);
-        wrote_anything = true;
       }
     }
 
-    out.close();
-
-    if (!wrote_anything) {
-      // Well, if we didn't write anything, remove the cache file
-      // after all.
-      unlink(cache_pathname.c_str());
+  } else {
+    // Open up the dependency cache file in the directory.
+    Filename cache_pathname(get_path(), cache_filename);
+    cache_pathname.set_text();
+    cache_pathname.unlink();
+
+    // If we have no files, don't bother writing the cache.
+    if (!_dependables.empty()) {
+      bool wrote_anything = false;
+      
+      ofstream out;
+      if (!cache_pathname.open_write(out)) {
+        cerr << "Cannot update cache dependency file " << cache_pathname << "\n";
+        return;
+      }
+      
+      // Walk through our list of dependable files, writing them out the
+      // the cache file.
+      Dependables::const_iterator di;
+      for (di = _dependables.begin(); di != _dependables.end(); ++di) {
+        PPDependableFile *file = (*di).second;
+        if (file->was_examined()) {
+          if (file->is_circularity()) {
+            cerr << "Warning: circular #include directives:\n"
+                 << "  " << file->get_circularity() << "\n";
+          }
+          file->write_cache(out);
+          wrote_anything = true;
+        }
+      }
+      
+      out.close();
+      
+      if (!wrote_anything) {
+        // Well, if we didn't write anything, remove the cache file
+        // after all.
+        cache_pathname.unlink();
+      }
     }
   }
-
+    
   Children::iterator ci;
   for (ci = _children.begin(); ci != _children.end(); ++ci) {
     (*ci)->update_file_dependencies(cache_filename);

+ 1 - 1
ppremake/ppDirectory.h

@@ -52,7 +52,7 @@ public:
                                         bool is_header);
 
   void report_depends() const;
-  void report_needs() const;
+  void report_reverse_depends() const;
 
 private:
   typedef set<PPDirectory *> Depends;

+ 40 - 34
ppremake/ppMain.cxx

@@ -8,7 +8,6 @@
 #include "ppCommandFile.h"
 #include "ppDirectory.h"
 #include "tokenize.h"
-#include "include_access.h"
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -23,7 +22,7 @@
 #define getcwd _getcwd
 #endif  // WIN32_VC
 
-string PPMain::_root;
+Filename PPMain::_root;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PPMain::Constructor
@@ -38,15 +37,10 @@ PPMain(PPScope *global_scope) {
   _def_scope = (PPScope *)NULL;
   _defs = (PPCommandFile *)NULL;
 
-  // save current working dir
-  char tmp[1024];
-  string dirpath = getcwd(tmp,sizeof(tmp));
-  size_t slash_pos = dirpath.rfind('/');
-  if (slash_pos == string::npos) {
-     _original_working_dir = dirpath;
-   } else {
-     _original_working_dir = dirpath.substr(slash_pos + 1);
-   }
+  // save current working directory name, so that "ppremake ." can map
+  // to the current directory.
+  Filename dirpath = get_cwd();
+  _original_working_dir = dirpath.get_basename();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -74,35 +68,49 @@ bool PPMain::
 read_source(const string &root) {
   // First, find the top of the source tree, as indicated by the
   // presence of a Package.pp file.
-  string trydir = root;
+  Filename trydir = root;
 
-  string package_file = trydir + "/" + PACKAGE_FILENAME;
+  Filename package_file(trydir, PACKAGE_FILENAME);
+  bool any_source_files_found = false;
 
-  while (access(package_file.c_str(), F_OK) != 0) {
+  while (!package_file.exists()) {
     // 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 = trydir + "/" + 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";
+    Filename source_file(trydir, SOURCE_FILENAME);
+    if (!source_file.exists()) {
+      if (!any_source_files_found) {
+        // If we never saw a single Sources.pp file, complain about that.
+        cerr << "Could not find ppremake source file " << SOURCE_FILENAME
+             << ".\n\n"
+             << "This file should be present at each level of the source directory tree;\n"
+             << "it defines how each directory should be processed by ppremake.\n\n";
+      } else {
+        // If we found at least one Sources.pp file, but didn't find
+        // the Package.pp file at the top of the tree, complain about
+        // *that*.
+        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;
     }
-    trydir += "/..";
-    package_file = trydir + "/" + PACKAGE_FILENAME;
+    any_source_files_found = true;
+    trydir = Filename(trydir, "..");
+    package_file = Filename(trydir, PACKAGE_FILENAME);
   }
 
   // Now cd to the source root and get the actual path.
-  if (chdir(trydir.c_str()) < 0) {
+  string osdir = trydir.to_os_specific();
+  if (chdir(osdir.c_str()) < 0) {
     perror("chdir");
     return false;
   }
 
-  _root = get_cwd();
+  _root = Filename::from_os_specific(get_cwd());
   cerr << "Root is " << _root << "\n";
 
   _def_scope = new PPScope(&_named_scopes);
@@ -190,18 +198,16 @@ process_all() {
 //               output the template file indicates.
 ////////////////////////////////////////////////////////////////////
 bool PPMain::
-process(const string &dirnam) {
+process(string dirname) {
   string cache_filename = _def_scope->expand_variable("DEPENDENCY_CACHE_FILENAME");
-  string dirname = dirnam;
-
   if (cache_filename.empty()) {
     cerr << "Warning: no definition given for $[DEPENDENCY_CACHE_FILENAME].\n";
   } else {
     _tree.read_file_dependencies(cache_filename);
   }
 
-  if(dirname == ".") {
-      dirname = _original_working_dir;
+  if (dirname == ".") {
+    dirname = _original_working_dir;
   }
   
   PPDirectory *dir = _tree.find_dirname(dirname);
@@ -244,20 +250,20 @@ report_depends(const string &dirname) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PPMain::report_needs
+//     Function: PPMain::report_reverse_depends
 //       Access: Public
 //  Description: Reports all the directories that depend on (need) the
 //               named directory.
 ////////////////////////////////////////////////////////////////////
 void PPMain::
-report_needs(const string &dirname) const {
+report_reverse_depends(const string &dirname) const {
   PPDirectory *dir = _tree.find_dirname(dirname);
   if (dir == (PPDirectory *)NULL) {
     cerr << "Unknown directory: " << dirname << "\n";
     return;
   }
 
-  dir->report_needs();
+  dir->report_reverse_depends();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 3
ppremake/ppMain.h

@@ -9,6 +9,7 @@
 #include "ppremake.h"
 #include "ppDirectoryTree.h"
 #include "ppNamedScopes.h"
+#include "filename.h"
 
 class PPScope;
 class PPCommandFile;
@@ -27,10 +28,10 @@ public:
   bool read_source(const string &root);
 
   bool process_all();
-  bool process(const string &dirname);
+  bool process(string dirname);
 
   void report_depends(const string &dirname) const;
-  void report_needs(const string &dirname) const;
+  void report_reverse_depends(const string &dirname) const;
 
   static string get_root();
   static void chdir_root();
@@ -50,7 +51,7 @@ private:
   PPNamedScopes _named_scopes;
   PPScope *_parent_scope;
 
-  static string _root;
+  static Filename _root;
   string _original_working_dir;
 };
 

+ 57 - 93
ppremake/ppScope.cxx

@@ -12,9 +12,8 @@
 #include "ppDependableFile.h"
 #include "ppMain.h"
 #include "tokenize.h"
-#include "find_searchpath.h"
 #include "filename.h"
-#include "include_access.h"
+#include "dSearchPath.h"
 
 #ifdef HAVE_GLOB_H
 #include <glob.h>
@@ -1304,27 +1303,12 @@ expand_isdir(const string &params) {
     return string();
   }
 
-  const string &filename = results[0];
-
-  string result;
-#ifdef WIN32_VC
-  DWORD dresults = GetFileAttributes(filename.c_str());
-  if (dresults != -1) {
-    if ((dresults & FILE_ATTRIBUTE_DIRECTORY) != 0) {
-      result = filename;
-    }
-  }
-
-#else  // WIN32_VC
-  struct stat stbuf;
-  if (stat(filename.c_str(), &stbuf) == 0) {
-    if (S_ISDIR(stbuf.st_mode)) {
-      result = filename;
-    }
+  Filename filename = results[0];
+  if (filename.is_directory()) {
+    return filename;
+  } else {
+    return string();
   }
-#endif // WIN32_VC
-
-  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1347,28 +1331,12 @@ expand_isfile(const string &params) {
     return string();
   }
 
-  const string &filename = results[0];
-
-  string result;
-
-#ifdef WIN32_VC
-  DWORD dresults = GetFileAttributes(filename.c_str());
-  if (dresults != -1) {
-    if (dresults == FILE_ATTRIBUTE_NORMAL) {
-      result = filename;
-    }
-  }
-
-#else  // WIN32_VC
-  struct stat stbuf;
-  if (stat(filename.c_str(), &stbuf) == 0) {
-    if (S_ISREG(stbuf.st_mode)) {
-      result = filename;
-    }
+  Filename filename = results[0];
+  if (filename.is_regular_file()) {
+    return filename;
+  } else {
+    return string();
   }
-#endif  // WIN32_VC
-
-  return result;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1392,21 +1360,21 @@ expand_libtest(const string &params) {
     return string();
   }
 
-  vector<string> directories;
-  tokenize_whitespace(tokens[0], directories);
+  DSearchPath directories;
+  directories.append_path(tokens[0], " \n\t");
 
   // Also add the system directories to the list, whatever we think
   // those should be.  Here we have to make a few assumptions.
 #ifdef WIN32
   const char *windir = getenv("WINDIR");
   if (windir != (const char *)NULL) {
-    directories.push_back(string(windir) + "\\System");
-    directories.push_back(string(windir) + "\\System32");
+    directories.append_directory(Filename(windir, "System"));
+    directories.append_directory(Filename(windir, "System32"));
   }
 
   const char *lib = getenv("LIB");
   if (lib != (const char *)NULL) {
-    tokenize(lib, directories, ";");
+    directories.append_path(lib, ";");
   }
 #endif
 
@@ -1416,11 +1384,11 @@ expand_libtest(const string &params) {
   // 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.append_path(ld_library_path, ":");
   }
 
-  directories.push_back("/lib");
-  directories.push_back("/usr/lib");
+  directories.append_directory("/lib");
+  directories.append_directory("/usr/lib");
 
   vector<string> libnames;
   tokenize_whitespace(tokens[1], libnames);
@@ -1431,32 +1399,34 @@ expand_libtest(const string &params) {
   }
 
   // We only bother to search for the first library name in the list.
-  string libname = libnames[0];
+  Filename libname = libnames[0];
 
-  string found;
+  bool found = false;
 
 #ifdef 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");
-    }
+  if (libname.get_extension() != string("lib")) {
+    libname = "lib" + libname.get_basename() + ".lib";
+  }
+  found = libname.resolve_filename(directories);
+  if (!found) {
+    libname.set_extension("dll");
+    found = libname.resolve_filename(directories);
   }
   
 #else
-  found = find_searchpath(directories, "lib" + libname + ".a");
-  if (found.empty()) {
-    found = find_searchpath(directories, "lib" + libname + ".so");
+  libname = "lib" + libname.get_basename() + ".a";
+  found = libname.resolve_filename(directories);
+  if (!found) {
+    libname.set_extension("so");
+    found = libname.resolve_filename(directories);
   }
 #endif
 
-
-  return found;
+  if (found) {
+    return libname;
+  } else {
+    return string();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1472,7 +1442,7 @@ string PPScope::
 expand_bintest(const string &params) {
   // We only have one parameter: the filename of the executable.  We
   // always search for it on the path.
-  string binname = expand_string(params);
+  Filename binname = Filename::from_os_specific(expand_string(params));
 
   if (binname.empty()) {
     // No binary, no exist.
@@ -1480,17 +1450,13 @@ expand_bintest(const string &params) {
   }
 
   // An explicit path from the root does not require a search.
-#ifdef 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();
-      }
+  if (binname.is_fully_qualified()) {
+    if (binname.exists()) {
+      return binname;
+    } else {
+      return string();
+    }
+  }
 
   const char *path = getenv("PATH");
   if (path == (const char *)NULL) {
@@ -1500,36 +1466,34 @@ expand_bintest(const string &params) {
 
   string pathvar(path);
 
-  vector<string> directories;
+  DSearchPath directories;
 
 #ifdef 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, ";");
+    directories.append_path(pathvar, ";");
 
   } else {
     // Otherwise, assume it's a Cygwin-style path: split it up based
     // on colons.
-    tokenize(pathvar, directories, ":");
+    directories.append_path(pathvar, ":");
   }
 #else
-  tokenize(pathvar, directories, ":");
+  directories.append_path(pathvar, ":");
 #endif
 
-  string found;
-
 #ifdef WIN32
-  found = find_searchpath(directories, binname + ".exe");
-  if (found.empty()) {
-    found = find_searchpath(directories, binname);
-  }
-  
+  bool found = binname.resolve_filename(directories, "exe");
 #else
-  found = find_searchpath(directories, binname);
+  bool found = binname.resolve_filename(directories);
 #endif
 
-  return found;
+  if (found) {
+    return binname;
+  } else {
+    return string();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 31 - 10
ppremake/ppremake.cxx

@@ -28,6 +28,8 @@
 
 bool unix_platform = false;
 bool windows_platform = false;
+bool dry_run = false;
+bool verbose_dry_run = false;
 
 static void
 usage() {
@@ -73,10 +75,14 @@ usage() {
     "               subdirectories that the named subdirectory depends on.\n"
     "               Directories are named by their local name, not by the\n"
     "               path to them; e.g. util instead of src/util.\n"
-    "  -n           As above, but report the set of subdirectories that\n"
-    "               depend on (need) the named subdirectory.  Options -d and\n"
-    "               -n may be combined, and you may also name multiple\n"
-    "               subdirectories to scan at once.\n\n"
+    "  -r           Reverse dependency.  As above, but report instead the set\n"
+    "               of directories that depend on the named subdirectory.\n"
+    "               Options -d and -r may be combined, and you may also\n"
+    "               name multiple subdirectories to scan at once.\n\n"
+    "  -n           Dry run: generate no output, but instead report the\n"
+    "               files that would change.\n"
+    "  -N           Verbose dry run: show the output of diff for the files\n"
+    "               that would change (not supported in Win32-only version).\n\n"
     "  -p platform  Build as if for the indicated platform name.\n"
     "  -c config.pp Read the indicated user-level config.pp file after reading\n"
     "               the system config.pp file.  If this is omitted, the value\n"
@@ -203,12 +209,12 @@ main(int argc, char *argv[]) {
   string progname = argv[0];
   extern char *optarg;
   extern int optind;
-  const char *optstr = "hVPD:dnp:c:s:";
+  const char *optstr = "hVPD:drnNp:c:s:";
 
   bool any_d = false;
   bool dependencies_stale = false;
   bool report_depends = false;
-  bool report_needs = false;
+  bool report_reverse_depends = false;
   string platform = PLATFORM;
   string ppremake_config;
   bool got_ppremake_config = false;
@@ -243,8 +249,17 @@ main(int argc, char *argv[]) {
       report_depends = true;
       break;
 
+    case 'r':
+      report_reverse_depends = true;
+      break;
+
     case 'n':
-      report_needs = true;
+      dry_run = true;
+      break;
+
+    case 'N':
+      dry_run = true;
+      verbose_dry_run = true;
       break;
 
     case 'p':
@@ -279,6 +294,12 @@ main(int argc, char *argv[]) {
     exit(0);
   }
 
+#ifdef WIN32_VC
+  if (verbose_dry_run) {
+    cerr << "Option -N treated like -n when ppremake is built without Cygwin.\n";
+  }
+#endif
+
   // If the user supplied one or more -d parameters, then we should
   // not continue unless some of the dependencies were stale.
   if (any_d) {
@@ -312,7 +333,7 @@ main(int argc, char *argv[]) {
     exit(1);
   }
 
-  if (report_depends || report_needs) {
+  if (report_depends || report_reverse_depends) {
     // With -d or -n, just report inter-directory dependency
     // relationships.
     if (argc < 2) {
@@ -326,8 +347,8 @@ main(int argc, char *argv[]) {
         ppmain.report_depends(argv[i]);
       }
       cerr << "\n";
-      if (report_needs) {
-        ppmain.report_needs(argv[i]);
+      if (report_reverse_depends) {
+        ppmain.report_reverse_depends(argv[i]);
       }
     }
 

+ 5 - 1
ppremake/ppremake.h

@@ -65,11 +65,15 @@ using namespace std;
 #define SCOPE_DIRNAME_WILDCARD "*"
 #define SCOPE_DIRNAME_CURRENT "."
 
+#ifdef __cplusplus
 /* These are set from the similarly-named variables defined in
    System.pp. */
-#ifdef __cplusplus
 extern bool unix_platform;
 extern bool windows_platform;
+
+/* This is a command-line global parameter. */
+extern bool dry_run;
+extern bool verbose_dry_run;
 #endif
 
 /* These are defined so that we may build Filename and DSearchPath,