From 0ab0efc3270f44da3d8b3a9db2845eeddde44dc6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 14 Jul 2011 14:00:12 -0400 Subject: Work around broken Windows command-line processing. It's wonderful that the Python interpreter will accept a whole multi-line script as a composite -c argument... but because Windows command-line processing is fundamentally flawed, we simply can't count on it for Windows. Instead, accept script text, write a temporary script file in a system- dependent temp directory, ask Python to run that script and delete the file. Also, on Windows, use _spawnl(), much simpler than adding bizarre Windows wait logic to LLProcessLauncher. Use LLProcessLauncher only on Mac & Linux, with waitpid() to capture rc. --- indra/llcommon/tests/llsdserialize_test.cpp | 93 ++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 15 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index ff0d8d5f46..4e09a4fe3c 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -31,18 +31,28 @@ #if LL_WINDOWS #include typedef U32 uint32_t; +#include #else #include #include #include +#include "llprocesslauncher.h" #endif +// As we're not trying to preserve compatibility with old Boost.Filesystem +// code, but rather writing brand-new code, use the newest available +// Filesystem API. +#define BOOST_FILESYSTEM_VERSION 3 +#include "boost/filesystem.hpp" +#include "boost/filesystem/v3/fstream.hpp" +#include "boost/range.hpp" +#include "boost/foreach.hpp" + #include "../llsd.h" #include "../llsdserialize.h" #include "../llformat.h" #include "../test/lltut.h" -#include "llprocesslauncher.h" #include "stringize.h" std::vector string_to_vector(const std::string& str) @@ -50,6 +60,51 @@ std::vector string_to_vector(const std::string& str) return std::vector(str.begin(), str.end()); } +// boost::filesystem::temp_directory_path() isn't yet in Boost 1.45! :-( +// Switch to that one as soon as we update to a Boost that contains it. +boost::filesystem::path temp_directory_path() +{ +#if LL_WINDOWS + char buffer[PATH_MAX]; + GetTempPath(sizeof(buffer), buffer); + return boost::filesystem::path(buffer); + +#else // LL_DARWIN, LL_LINUX + static const char* vars[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }; + BOOST_FOREACH(const char* var, vars) + { + const char* found = getenv(var); + if (found) + return boost::filesystem::path(found); + } + return boost::filesystem::path("/tmp"); +#endif // LL_DARWIN, LL_LINUX +} + +// Create a Python script file with specified content "somewhere in the +// filesystem," cleaning up when it goes out of scope. +class NamedTempScript +{ +public: + NamedTempScript(const std::string& content): + mPath(/*boost::filesystem*/::temp_directory_path() / + boost::filesystem::unique_path("%%%%-%%%%-%%%%-%%%%.py")) + { + boost::filesystem::ofstream file(mPath); + file << content << '\n'; + } + + ~NamedTempScript() + { + boost::filesystem::remove(mPath); + } + + std::string getName() const { return mPath.native(); } + +private: + boost::filesystem::path mPath; +}; + namespace tut { struct sd_xml_data @@ -1496,26 +1551,34 @@ namespace tut TestPythonCompatible() {} ~TestPythonCompatible() {} - void python(const std::string& desc, const std::string& script, F32 timeout=5) + void python(const std::string& desc, const std::string& script /*, F32 timeout=5 */) { const char* PYTHON(getenv("PYTHON")); ensure("Set $PYTHON to the Python interpreter", PYTHON); + + NamedTempScript scriptfile(script); + +#if LL_WINDOWS + std::string q("\""); + std::string qPYTHON(q + PYTHON + q); + std::string qscript(q + scriptfile.getName() + q); + int rc(_spawnl(_P_WAIT, PYTHON, qPYTHON.c_str(), qscript.c_str(), NULL)); + if (rc == -1) + { + char buffer[256]; + strerror_s(buffer, errno); // C++ can infer the buffer size! :-O + ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); + } + else + { + ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, 0); + } + +#else // LL_DARWIN, LL_LINUX LLProcessLauncher py; py.setExecutable(PYTHON); - py.addArgument("-c"); - py.addArgument(script); + py.addArgument(scriptfile.getName()); ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0); - -#if LL_WINDOWS - ensure_equals(STRINGIZE(desc << " script ran beyond " - << std::fixed << std::setprecision(1) - << timeout << " seconds"), - WaitForSingleObject(py.getProcessHandle(), DWORD(timeout * 1000)), - WAIT_OBJECT_0); - DWORD rc(0); - GetExitCodeProcess(py.getProcessHandle(), &rc); - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, 0); -#else // Implementing timeout would mean messing with alarm() and // catching SIGALRM... later maybe... int status(0); -- cgit v1.2.3