#include <config.h>
-#include "debug.h"
-#include "filetools.h"
-#include "qstring_helpers.h"
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0600
+#endif
+
+#include "support/convert.h"
+#include "support/debug.h"
+#include "support/filetools.h"
+#include "support/qstring_helpers.h"
+#include "support/regex.h"
#include <QDir>
// e.g., the author hash is always 32-bit.
template<bool Condition> struct static_assert_helper;
template <> struct static_assert_helper<true> {};
-enum {
+enum {
dummy = sizeof(static_assert_helper<sizeof(int) == 4>)
};
namespace support {
namespace os {
-static string const python2(string const & binary, bool verbose = false)
+int timeout_min()
+{
+ return 3;
+}
+
+
+static string const python23_call(string const & binary, bool verbose = false)
{
+ const string version_info = " -c \"from __future__ import print_function;import sys; print(sys.version_info[:2], end=\\\"\\\")\"";
+ // Default to "python" if no binary is given.
+ if (binary.empty())
+ return "python -tt";
+
if (verbose)
lyxerr << "Examining " << binary << "\n";
+ // Check whether this is a python 2 or 3 binary.
+ cmd_ret const out = runCommand(binary + version_info);
+
+ smatch sm;
+ try {
+ static regex const python_reg("\\((\\d*), (\\d*)\\)");
+ if (out.first < 0 || !regex_match(out.second, sm, python_reg))
+ return string();
+ } catch(regex_error const & /*e*/) {
+ LYXERR0("Regex error! This should not happen.");
+ return string();
+ }
- // Check whether this is a python 2 binary.
- cmd_ret const out = runCommand(binary + " -V 2>&1");
- if (out.first < 0 || !prefixIs(out.second, "Python 2"))
+ int major = convert<int>(sm.str(1));
+ int minor = convert<int>(sm.str(2));
+ if((major == 2 && minor < 7) || (major == 3 && minor < 5))
return string();
if (verbose)
- lyxerr << "Found " << out.second << "\n";
- return binary;
+ lyxerr << "Found Python " << out.second << "\n";
+ // Add the -tt switch so that mixed tab/whitespace
+ // indentation is an error
+ return binary + " -tt";
}
-int timeout_min()
+static string const find_python_binary()
{
- return 3;
+ // This function takes inspiration from PEP 394 and PEP 397
+ // PEP 394 -- The "python" Command on Unix-Like Systems
+ // https://www.python.org/dev/peps/pep-0394/
+ // PEP 397 -- Python launcher for Windows
+ // https://www.python.org/dev/peps/pep-0397/
+
+#ifdef _WIN32
+ // Check through python launcher whether python 3 is
+ // installed on computer.
+ string command = python23_call("py -3");
+#else
+ // Check whether python3 in PATH is the right one.
+ string command = python23_call("python3");
+#endif // _WIN32
+ if (!command.empty())
+ return command;
+
+#ifndef _WIN32
+ // python3 does not exists, let us try to find python3.x in PATH
+ // the search is probably broader than required
+ // but we are trying hard to find a valid python binary
+ vector<string> const path = getEnvPath("PATH");
+ lyxerr << "Looking for python 3.x ...\n";
+ for (auto bin : path) {
+ QString const dir = toqstr(bin);
+ string const localdir = dir.toLocal8Bit().constData();
+ QDir qdir(dir);
+ qdir.setFilter(QDir::Files | QDir::Executable);
+ QStringList list = qdir.entryList(QStringList("python3*"));
+ for (auto bin2 : list) {
+ string const binary = "\"" + addName(localdir,
+ bin2.toLocal8Bit().constData()) + "\"";
+ command = python23_call(binary, true);
+ if (!command.empty())
+ return command;
+ }
+ }
+#endif // !_WIN32
+
+ // python 3 was not found let us look for python 2
+#ifdef _WIN32
+ command = python23_call("py -2");
+#else
+ command = python23_call("python2");
+#endif // _WIN32
+ if (!command.empty())
+ return command;
+
+#ifdef _WIN32
+ // python launcher is not installed, let cmd auto check
+ // PATH for a python.exe
+ command = python23_call("python");
+ if (!command.empty())
+ return command;
+
+ //failed, prepare to search PATH manually
+ vector<string> const path = getEnvPath("PATH");
+ lyxerr << "Manually looking for python in PATH ...\n";
+ QString const exeName = "python*";
+#else
+ // python2 does not exists, let us try to find python2.x in PATH
+ // the search is probably broader than required
+ // but we are trying hard to find a valid python binary
+ lyxerr << "Looking for python 2.x ...\n";
+ QString const exeName = "python2*";
+#endif // _WIN32
+
+ for (auto bin : path) {
+ QString const dir = toqstr(bin);
+ string const localdir = dir.toLocal8Bit().constData();
+ QDir qdir(dir);
+ qdir.setFilter(QDir::Files | QDir::Executable);
+ QStringList list = qdir.entryList(QStringList(exeName));
+ for (auto bin2 : list) {
+ string const binary = "\"" + addName(localdir,
+ bin2.toLocal8Bit().constData()) + "\"";
+ command = python23_call(binary, true);
+ if (!command.empty())
+ return command;
+ }
+ }
+
+ // If this happens all hope is lost that this is a sane system
+ lyxerr << "Warning: No python v2.x or 3.x binary found.\n";
+ return python23_call("");
}
string const python(bool reset)
{
- // Check whether the first python in PATH is the right one.
- static string command = python2("python -tt");
+ static string command = find_python_binary();
+ // FIXME THREAD
if (reset) {
- command = python2("python -tt");
- }
-
- if (command.empty()) {
- // It was not, so check whether we can find it elsewhere in
- // PATH, maybe with some suffix appended.
- vector<string> const path = getEnvPath("PATH");
- vector<string>::const_iterator it = path.begin();
- vector<string>::const_iterator const end = path.end();
- lyxerr << "Looking for python v2.x ...\n";
- for (; it != end; ++it) {
- QString const dir = toqstr(*it);
- string const localdir = dir.toLocal8Bit().constData();
- QDir qdir(dir);
- qdir.setFilter(QDir::Files | QDir::Executable);
- QStringList list = qdir.entryList(QStringList("python*"));
- for (int i = 0; i < list.size() && command.empty(); ++i) {
- string const binary = addName(localdir,
- list.at(i).toLocal8Bit().constData());
- command = python2(binary, true);
- }
- }
-
- // Default to "python" if no usable binary was found.
- if (command.empty()) {
- lyxerr << "Warning: No python v2.x binary found.\n";
- command = "python";
- }
-
- // Add the -tt switch so that mixed tab/whitespace
- // indentation is an error
- command += " -tt";
+ command = find_python_binary();
}
return command;
}
-}
-}
-}
+} // namespace os
+} // namespace support
+} // namespace lyx