Skip to content
Snippets Groups Projects
Commit 0ab0efc3 authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

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.
parent ec780a73
No related branches found
No related tags found
No related merge requests found
...@@ -31,18 +31,28 @@ ...@@ -31,18 +31,28 @@
#if LL_WINDOWS #if LL_WINDOWS
#include <winsock2.h> #include <winsock2.h>
typedef U32 uint32_t; typedef U32 uint32_t;
#include <process.h>
#else #else
#include <netinet/in.h> #include <netinet/in.h>
#include <errno.h> #include <errno.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "llprocesslauncher.h"
#endif #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 "../llsd.h"
#include "../llsdserialize.h" #include "../llsdserialize.h"
#include "../llformat.h" #include "../llformat.h"
#include "../test/lltut.h" #include "../test/lltut.h"
#include "llprocesslauncher.h"
#include "stringize.h" #include "stringize.h"
std::vector<U8> string_to_vector(const std::string& str) std::vector<U8> string_to_vector(const std::string& str)
...@@ -50,6 +60,51 @@ std::vector<U8> string_to_vector(const std::string& str) ...@@ -50,6 +60,51 @@ std::vector<U8> string_to_vector(const std::string& str)
return std::vector<U8>(str.begin(), str.end()); return std::vector<U8>(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 namespace tut
{ {
struct sd_xml_data struct sd_xml_data
...@@ -1496,26 +1551,34 @@ namespace tut ...@@ -1496,26 +1551,34 @@ namespace tut
TestPythonCompatible() {} TestPythonCompatible() {}
~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")); const char* PYTHON(getenv("PYTHON"));
ensure("Set $PYTHON to the Python interpreter", 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; LLProcessLauncher py;
py.setExecutable(PYTHON); py.setExecutable(PYTHON);
py.addArgument("-c"); py.addArgument(scriptfile.getName());
py.addArgument(script);
ensure_equals(STRINGIZE("Couldn't launch " << desc << " script"), py.launch(), 0); 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 // Implementing timeout would mean messing with alarm() and
// catching SIGALRM... later maybe... // catching SIGALRM... later maybe...
int status(0); int status(0);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment