瀏覽代碼

Add two cmake commands -E echo for echoing strings and -E comspec for workaround of bug of windows 9x; add another implementation of run command on windows which should work...

Andy Cedilnik 23 年之前
父節點
當前提交
780a9bbda7
共有 3 個文件被更改,包括 313 次插入6 次删除
  1. 270 4
      Source/cmSystemTools.cxx
  2. 10 0
      Source/cmSystemTools.h
  3. 33 2
      Source/cmake.cxx

+ 270 - 4
Source/cmSystemTools.cxx

@@ -74,6 +74,19 @@ bool cmSystemTools::s_DisableRunCommandOutput = false;
 bool cmSystemTools::s_ErrorOccured = false;
 bool cmSystemTools::s_DisableMessages = false;
 
+std::string cmSystemTools::s_Windows9xComspecSubstitute = "command.com";
+void cmSystemTools::SetWindows9xComspecSubstitute(const char* str)
+{
+  if ( str )
+    {
+    cmSystemTools::s_Windows9xComspecSubstitute = str;
+    }
+}
+const char* cmSystemTools::GetWindows9xComspecSubstitute()
+{
+  return cmSystemTools::s_Windows9xComspecSubstitute.c_str();
+}
+
 void (*cmSystemTools::s_ErrorCallback)(const char*, const char*, bool&, void*);
 void* cmSystemTools::s_ErrorCallbackClientData = 0;
 
@@ -1004,7 +1017,7 @@ void cmSystemTools::Message(const char* m1, const char *title)
     }
   else
     {
-    std::cerr << m1 << std::endl;
+    std::cerr << m1 << std::endl << std::flush;
     }
   
 }
@@ -1249,6 +1262,250 @@ bool cmSystemTools::RunCommand(const char* command,
 }
 
 #if defined(WIN32) && !defined(__CYGWIN__)
+// Code from a Borland web site with the following explaination : 
+/* In this article, I will explain how to spawn a console application
+ * and redirect its standard input/output using anonymous pipes. An
+ * anonymous pipe is a pipe that goes only in one direction (read
+ * pipe, write pipe, etc.). Maybe you are asking, "why would I ever
+ * need to do this sort of thing?" One example would be a Windows
+ * telnet server, where you spawn a shell and listen on a port and
+ * send and receive data between the shell and the socket
+ * client. (Windows does not really have a built-in remote
+ * shell). First, we should talk about pipes. A pipe in Windows is
+ * simply a method of communication, often between process. The SDK
+ * defines a pipe as "a communication conduit with two ends;
+ a process
+ * with a handle to one end can communicate with a process having a
+ * handle to the other end." In our case, we are using "anonymous"
+ * pipes, one-way pipes that "transfer data between a parent process
+ * and a child process or between two child processes of the same
+ * parent process." It's easiest to imagine a pipe as its namesake. An
+ * actual pipe running between processes that can carry data. We are
+ * using anonymous pipes because the console app we are spawning is a
+ * child process. We use the CreatePipe function which will create an
+ * anonymous pipe and return a read handle and a write handle. We will
+ * create two pipes, on for stdin and one for stdout. We will then
+ * monitor the read end of the stdout pipe to check for display on our
+ * child process. Every time there is something availabe for reading,
+ * we will display it in our app. Consequently, we check for input in
+ * our app and send it off to the write end of the stdin pipe. */ 
+
+inline bool IsWinNT() 
+//check if we're running NT 
+{
+  OSVERSIONINFO osv;
+  osv.dwOSVersionInfoSize = sizeof(osv);
+  GetVersionEx(&osv);
+  return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT); 
+}
+
+void DisplayErrorMessage()
+{
+  LPVOID lpMsgBuf;
+  FormatMessage( 
+    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+    FORMAT_MESSAGE_FROM_SYSTEM | 
+    FORMAT_MESSAGE_IGNORE_INSERTS,
+    NULL,
+    GetLastError(),
+    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+    (LPTSTR) &lpMsgBuf,
+    0,
+    NULL 
+    );
+  // Process any inserts in lpMsgBuf.
+  // ... 
+  // Display the string.
+  MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
+  // Free the buffer.
+  LocalFree( lpMsgBuf );
+}
+ 
+//--------------------------------------------------------------------------- 
+bool WindowsRunCommand(const char* command, const char* dir, 
+                       std::string& output, int& retVal, bool verbose) 
+{
+  //verbose = true;
+  std::cerr << std::endl 
+	    << "WindowsRunCommand(" << command << ")" << std::endl 
+	    << std::flush;
+  const int BUFFER_SIZE = 4096;
+  char buf[BUFFER_SIZE];
+ 
+//i/o buffer 
+  STARTUPINFO si;
+  SECURITY_ATTRIBUTES sa;
+  SECURITY_DESCRIPTOR sd;
+ 
+//security information for pipes 
+  PROCESS_INFORMATION pi;
+  HANDLE newstdin,newstdout,read_stdout,write_stdin;
+ 
+//pipe handles 
+  if (IsWinNT()) 
+//initialize security descriptor (Windows NT) 
+    {
+    InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
+    SetSecurityDescriptorDacl(&sd, true, NULL, false);
+    sa.lpSecurityDescriptor = &sd;
+ 
+    }
+  else sa.lpSecurityDescriptor = NULL;
+
+  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+  sa.bInheritHandle = true;
+ 
+//allow inheritable handles 
+  if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) 
+//create stdin pipe 
+    {
+    std::cerr << "CreatePipe" << std::endl;
+    return false;
+ 
+    }
+  if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) 
+//create stdout pipe 
+    {
+    std::cerr << "CreatePipe" << std::endl;
+    CloseHandle(newstdin);
+    CloseHandle(write_stdin);
+    return false;
+ 
+    }
+  GetStartupInfo(&si);
+ 
+//set startupinfo for the spawned process 
+  /* The dwFlags member tells CreateProcess how to make the
+   * process. STARTF_USESTDHANDLES validates the hStd*
+   * members. STARTF_USESHOWWINDOW validates the wShowWindow
+   * member. */ 
+  
+  si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_HIDE;
+  si.hStdOutput = newstdout;
+  si.hStdError = newstdout;
+ 
+//set the new handles for the child process si.hStdInput = newstdin;
+  char* commandAndArgs = strcpy(new char[strlen(command)+1], command);
+  if (!CreateProcess(NULL,commandAndArgs,NULL,NULL,TRUE,CREATE_NEW_CONSOLE, 
+                     NULL,dir,&si,&pi)) 
+    {
+    std::cerr << "CreateProcess failed " << commandAndArgs << std::endl;
+    CloseHandle(newstdin);
+    CloseHandle(newstdout);
+    CloseHandle(read_stdout);
+    CloseHandle(write_stdin);
+    delete [] commandAndArgs;
+    return false;
+ 
+    }
+  delete [] commandAndArgs;
+  unsigned long exit=0;
+ 
+//process exit code unsigned 
+  unsigned long bread;
+ 
+//bytes read unsigned 
+  unsigned long avail;
+ 
+//bytes available 
+  memset(buf, 0, sizeof(buf));
+  for(;;) 
+//main program loop 
+    {
+    Sleep(10);
+    //std::cout << "Check for process..." << std::endl;
+    GetExitCodeProcess(pi.hProcess,&exit);
+ 
+//while the process is running 
+    if (exit != STILL_ACTIVE) break;
+ 
+//check to see if there is any data to read from stdout 
+    //std::cout << "Peek for data..." << std::endl;
+    PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL);
+    if (bread != 0) 
+      {
+      memset(buf, 0, sizeof(buf));
+      if (avail > 1023) 
+        {
+        while (bread >= 1023) 
+          {
+	  //std::cout << "Read data..." << std::endl;
+          ReadFile(read_stdout,buf,1023,&bread,NULL);
+ 
+//read the stdout pipe 
+          printf("%s",buf);
+          memset(buf, 0, sizeof(buf));
+ 
+          }
+ 
+        }
+      else 
+        {
+        ReadFile(read_stdout,buf,1023,&bread,NULL);
+        output += buf;
+        output += "\n";
+        if(verbose) 
+          {
+          std::cerr << verbose << " [{" << buf << "}]" 
+		    << std::endl << std::flush;
+ 
+          }
+ 
+        }
+ 
+      }
+ 
+    }
+  CloseHandle(pi.hThread);
+  CloseHandle(pi.hProcess);
+  CloseHandle(newstdin);
+ 
+//clean stuff up 
+  CloseHandle(newstdout);
+  CloseHandle(read_stdout);
+  CloseHandle(write_stdin);
+  retVal = exit;
+  std::cerr << std::endl << "End of WindowsRunCommand(" << command << ")" << std::endl << std::flush;
+  return true;
+ 
+}
+
+#include "cmWin32ProcessExecution.h"
+// use this for shell commands like echo and dir
+bool RunCommandViaWin32(const char* command,
+			const char* dir,
+			std::string& output,
+			int& retVal,
+			bool verbose)
+{
+  if ( ! command )
+    {
+    cmSystemTools::Error("No command specified");
+    return false;
+    }
+  //std::cout << "Command: " << command << std::endl;
+  if ( dir )
+    {
+    //std::cout << "Dir: " << dir << std::endl;
+    }
+
+  cmWin32ProcessExecution resProc;
+  if ( cmSystemTools::GetWindows9xComspecSubstitute() )
+    {
+    resProc.SetConsoleSpawn(cmSystemTools::GetWindows9xComspecSubstitute() );
+    }
+  if ( !resProc.StartProcess(command, dir, verbose) )
+    {
+    std::cout << "Problem starting command" << std::endl;
+    return false;
+    }
+  resProc.Wait(INFINITE);
+  output = resProc.GetOutput();
+  retVal = resProc.GetExitValue();
+  return true;
+}
+
 // use this for shell commands like echo and dir
 bool RunCommandViaSystem(const char* command,
                          const char* dir,
@@ -1256,6 +1513,8 @@ bool RunCommandViaSystem(const char* command,
                          int& retVal,
                          bool verbose)
 {  
+  std::cout << "@@ " << command << std::endl;
+
   const int BUFFER_SIZE = 4096;
   char buffer[BUFFER_SIZE];
   std::string commandInDir;
@@ -1366,8 +1625,13 @@ bool cmSystemTools::RunCommand(const char* command,
           }
         shortCmd += " ";
         shortCmd += args;
-        return RunCommandViaSystem(shortCmd.c_str(), dir, 
-                                   output, retVal, verbose);
+
+        //return RunCommandViaSystem(shortCmd.c_str(), dir, 
+        //                           output, retVal, verbose);
+        //return WindowsRunCommand(shortCmd.c_str(), dir, 
+	//output, retVal, verbose);
+        return RunCommandViaWin32(shortCmd.c_str(), dir, 
+				  output, retVal, verbose);
         }
       else
         {
@@ -1377,7 +1641,9 @@ bool cmSystemTools::RunCommand(const char* command,
       }
     }
   // if there is only one set of quotes or no quotes then just run the command
-  return RunCommandViaSystem(command, dir, output, retVal, verbose);
+  //return RunCommandViaSystem(command, dir, output, retVal, verbose);
+  //return WindowsRunCommand(command, dir, output, retVal, verbose);
+  return RunCommandViaWin32(command, dir, output, retVal, verbose);
 #else
   // if only popen worked on windows.....
   std::string commandInDir;

+ 10 - 0
Source/cmSystemTools.h

@@ -311,6 +311,14 @@ public:
    */
   static e_FileFormat GetFileFormat(const char* ext);
 
+  /**
+   * On Windows 9x we need a comspec (command.com) substitute to run
+   * programs correctly. This string has to be constant available
+   * through the running of program. This method does not create a copy.
+   */
+  static void SetWindows9xComspecSubstitute(const char*);
+  static const char* GetWindows9xComspecSubstitute();
+
 protected:
   // these two functions can be called from ConvertToOutputPath
   /**
@@ -333,6 +341,8 @@ private:
   static bool s_DisableRunCommandOutput;
   static ErrorCallback s_ErrorCallback;
   static void* s_ErrorCallbackClientData;
+
+  static std::string s_Windows9xComspecSubstitute;
 };
 
 

+ 33 - 2
Source/cmake.cxx

@@ -28,6 +28,7 @@
 #include "cmGlobalVisualStudio7Generator.h"
 #include "cmGlobalBorlandMakefileGenerator.h"
 #include "cmGlobalNMakeMakefileGenerator.h"
+#include "cmWin32ProcessExecution.h"
 #else
 #include "cmGlobalUnixMakefileGenerator.h"
 #endif
@@ -430,6 +431,11 @@ int cmake::AddCMakePaths(const char *arg0)
   this->m_CacheManager->AddCacheEntry
     ("CMAKE_ROOT", cMakeRoot.c_str(),
      "Path to CMake installation.", cmCacheManager::INTERNAL);
+
+#ifdef _WIN32
+  cmSystemTools::SetWindows9xComspecSubstitute(
+    (cMakeSelf + " -E comspec").c_str());
+#endif
   return 1;
 }
 
@@ -448,12 +454,14 @@ void CMakeCommandUsage(const char* program)
     << "Available commands: \n"
     << "  chdir dir cmd [args]... - run command in a given directory\n"
     << "  copy file destination   - copy file to destination (either file or directory)\n"
+    << "  echo [string]...        - displays arguments as text\n"
     << "  remove file1 file2 ...  - remove the file(s)\n"
     << "  time command [args] ... - run command and return elapsed time\n";
 #if defined(_WIN32) && !defined(__CYGWIN__)
   errorStream
     << "  write_regv key value    - write registry value\n"
-    << "  delete_regv key         - delete registry value\n";
+    << "  delete_regv key         - delete registry value\n"
+    << "  comspec                 - on windows 9x use this for RunCommand\n";
 #endif
 
   cmSystemTools::Error(errorStream.str().c_str());
@@ -470,6 +478,18 @@ int cmake::CMakeCommand(std::vector<std::string>& args)
       return cmSystemTools::GetErrorOccuredFlag();
       }
 
+    // Echo string
+    else if (args[1] == "echo" )
+      {
+      int cc;
+      for ( cc = 2; cc < args.size(); cc ++ )
+	{
+	std::cout << args[cc] << " ";
+	}
+      std::cout << std::endl;
+      return 0;
+      }
+
     // Remove file
     else if (args[1] == "remove" && args.size() > 2)
       {
@@ -522,7 +542,7 @@ int cmake::CMakeCommand(std::vector<std::string>& args)
     }
 
     // Clock command
-    else if (args[1] == "chdir" && args.size() > 2)
+    else if (args[1] == "chdir" && args.size() == 4)
       {
       std::string directory = args[2];
       std::string command = args[3];
@@ -557,6 +577,17 @@ int cmake::CMakeCommand(std::vector<std::string>& args)
       {
       return cmSystemTools::DeleteRegistryValue(args[2].c_str()) ? 0 : 1;
       }
+    // Remove file
+    else if (args[1] == "comspec" && args.size() > 2)
+      {
+      int cc;
+      std::string command = args[2];
+      for ( cc = 3; cc < args.size(); cc ++ )
+	{
+	command += " " + args[cc];
+	}
+      return cmWin32ProcessExecution::Windows9xHack(command.c_str());
+      }
 #endif
     }