From 780a9bbda7142773dfa479a5fe3c231a173943fc Mon Sep 17 00:00:00 2001 From: Andy Cedilnik Date: Fri, 27 Sep 2002 17:28:15 -0400 Subject: 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... --- Source/cmSystemTools.cxx | 274 ++++++++++++++++++++++++++++++++++++++++++++++- Source/cmSystemTools.h | 10 ++ Source/cmake.cxx | 35 +++++- 3 files changed, 313 insertions(+), 6 deletions(-) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index f4fd0da..3717a90 100644 --- a/Source/cmSystemTools.cxx +++ b/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; diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 716c2cf..7ef4a4d 100644 --- a/Source/cmSystemTools.h +++ b/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; }; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 684116a..887091e 100644 --- a/Source/cmake.cxx +++ b/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& 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& 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& 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 } -- cgit v0.12