3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Ruurd A. Reitsma
7 * \author Enrico Forestieri
9 * Full author contact details are available in file CREDITS.
15 # define _WIN32_WINNT 0x0600
18 #include "support/convert.h"
19 #include "support/debug.h"
20 #include "support/filetools.h"
21 #include "support/qstring_helpers.h"
27 #if defined(__CYGWIN__)
28 #include "support/os_cygwin.cpp"
30 #include "support/os_win32.cpp"
32 #include "support/os_unix.cpp"
35 // Static assert to break compilation on platforms where
36 // int/unsigned int is not 4 bytes. Added to make sure that
37 // e.g., the author hash is always 32-bit.
38 template<bool Condition> struct static_assert_helper;
39 template <> struct static_assert_helper<true> {};
41 dummy = sizeof(static_assert_helper<sizeof(int) == 4>)
50 // Starting in 2.4.0, we allow the user to cancel the background
51 // process at any time with LFUN_EXPORT_CANCEL, so the timeout dialog
52 // is no longer useful.
53 // "-1" effectively disables the timeout (it is a special case in
54 // SystemcallPrivate::waitWhile()).
59 static string const python23_call(string const & binary, bool verbose = false)
61 const string version_info = " -c \"from __future__ import print_function; import sys; print(sys.version_info[:2], end='')\"";
62 // Default to "python" if no binary is given.
67 lyxerr << "Examining " << binary << "\n";
68 // Check whether this is a python 2 or 3 binary.
69 cmd_ret const out = runCommand(binary + version_info);
73 static regex const python_reg("\\((\\d*), (\\d*)\\)");
74 if (!out.valid || !regex_match(out.result, sm, python_reg))
76 } catch(regex_error const & /*e*/) {
77 LYXERR0("Regex error! This should not happen.");
81 int major = convert<int>(sm.str(1));
82 int minor = convert<int>(sm.str(2));
83 if((major == 2 && minor < 7) || (major == 3 && minor < 5))
87 lyxerr << "Found Python " << out.result << "\n";
88 // Add the -tt switch so that mixed tab/whitespace
89 // indentation is an error
90 return binary + " -tt";
94 static string const find_python_binary()
96 // This function takes inspiration from PEP 394 and PEP 397
97 // PEP 394 -- The "python" Command on Unix-Like Systems
98 // https://www.python.org/dev/peps/pep-0394/
99 // PEP 397 -- Python launcher for Windows
100 // https://www.python.org/dev/peps/pep-0397/
103 // Check through python launcher whether python 3 is
104 // installed on computer.
105 string command = python23_call("py -3");
107 // Check whether python3 in PATH is the right one.
108 string command = python23_call("python3");
110 if (!command.empty())
114 // python3 does not exists, let us try to find python3.x in PATH
115 // the search is probably broader than required
116 // but we are trying hard to find a valid python binary
117 vector<string> const path = getEnvPath("PATH");
118 lyxerr << "Looking for python 3.x ...\n";
119 for (auto const & bin : path) {
120 QString const dir = toqstr(bin);
121 string const localdir = dir.toLocal8Bit().constData();
123 qdir.setFilter(QDir::Files | QDir::Executable);
124 QStringList list = qdir.entryList(QStringList("python3*"));
125 for (auto const & bin2 : list) {
126 string const binary = "\"" + addName(localdir,
127 bin2.toLocal8Bit().constData()) + "\"";
128 command = python23_call(binary, true);
129 if (!command.empty())
135 // python 3 was not found let us look for python 2
137 command = python23_call("py -2");
139 command = python23_call("python2");
141 if (!command.empty())
145 // python launcher is not installed, let cmd auto check
146 // PATH for a python.exe
147 command = python23_call("python");
148 if (!command.empty())
151 //failed, prepare to search PATH manually
152 vector<string> const path = getEnvPath("PATH");
153 lyxerr << "Manually looking for python in PATH ...\n";
154 QString const exeName = "python*";
156 // python2 does not exists, let us try to find python2.x in PATH
157 // the search is probably broader than required
158 // but we are trying hard to find a valid python binary
159 lyxerr << "Looking for python 2.x ...\n";
160 QString const exeName = "python2*";
163 for (auto const & bin : path) {
164 QString const dir = toqstr(bin);
165 string const localdir = dir.toLocal8Bit().constData();
167 qdir.setFilter(QDir::Files | QDir::Executable);
168 QStringList list = qdir.entryList(QStringList(exeName));
169 for (auto const & bin2 : list) {
170 string const binary = "\"" + addName(localdir,
171 bin2.toLocal8Bit().constData()) + "\"";
172 command = python23_call(binary, true);
173 if (!command.empty())
178 // If this happens all hope is lost that this is a sane system
179 lyxerr << "Warning: No python v2.x or 3.x binary found.\n";
180 return python23_call("");
184 string const python(bool reset)
186 static string command = find_python_binary();
189 command = find_python_binary();
197 return !(python23_call(python()).empty());
200 string const python_info()
202 const string info_version = " -c \"from __future__ import print_function; import sys; print('{} ({})'.format(sys.version.split()[0],sys.executable), end='')\"";
205 return (runCommand(python() + info_version).result);
209 } // namespace support