]> git.lyx.org Git - lyx.git/blob - src/support/os.cpp
Divide the python detection in three functions, with a clear delegation of responsibi...
[lyx.git] / src / support / os.cpp
1 /**
2  * \file os.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Ruurd A. Reitsma
7  * \author Enrico Forestieri
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "support/convert.h"
15 #include "support/debug.h"
16 #include "support/filetools.h"
17 #include "support/qstring_helpers.h"
18 #include "support/regex.h"
19
20 #include <QDir>
21
22 #if defined(__CYGWIN__)
23 #include "support/os_cygwin.cpp"
24 #elif defined(_WIN32)
25 #include "support/os_win32.cpp"
26 #else
27 #include "support/os_unix.cpp"
28 #endif
29
30 // Static assert to break compilation on platforms where
31 // int/unsigned int is not 4 bytes. Added to make sure that
32 // e.g., the author hash is always 32-bit.
33 template<bool Condition> struct static_assert_helper;
34 template <> struct static_assert_helper<true> {};
35 enum {
36         dummy = sizeof(static_assert_helper<sizeof(int) == 4>)
37 };
38
39 namespace lyx {
40 namespace support {
41 namespace os {
42
43 int timeout_min()
44 {
45         return 3;
46 }
47
48
49 static string const python23_call(string const & binary, bool verbose = false)
50 {
51         const string version_info = " -c 'from __future__ import print_function;import sys; print(sys.version_info[:2], end=\"\")'";
52         static regex const python_reg("\\((\\d*), (\\d*)\\)");
53         // Default to "python" if no binary is given.
54         if (binary.empty())
55                 return "python -tt";
56
57         if (verbose)
58                 lyxerr << "Examining " << binary << "\n";
59         // Check whether this is a python 2 or 3 binary.
60         cmd_ret const out = runCommand(binary + version_info);
61
62         smatch sm;
63         if (out.first < 0 || !regex_match(out.second, sm, python_reg))
64                 return string();
65
66         int major = convert<int>(sm.str(1));
67         int minor = convert<int>(sm.str(2));
68         if((major == 2 && minor < 7) || (major == 3 && minor < 4))
69                 return string();
70
71         if (verbose)
72                 lyxerr << "Found Python " << out.second << "\n";
73         // Add the -tt switch so that mixed tab/whitespace
74         // indentation is an error
75         return binary + " -tt";
76 }
77
78
79 static string const find_python_binary()
80 {
81         // This function takes inspiration from PEP 394 and PEP 397
82         // PEP 394 -- The "python" Command on Unix-Like Systems
83         // https://www.python.org/dev/peps/pep-0394/
84         // PEP 397 -- Python launcher for Windows
85         // https://www.python.org/dev/peps/pep-0397/
86
87         // Check whether python3 in PATH is the right one.
88         string command = python23_call("python3");
89         if (!command.empty())
90                 return command;
91
92         // python3 does not exists, let us try to find python3.x in PATH
93         // the search is probably broader than required
94         // but we are trying hard to find a valid python binary
95         vector<string> const path = getEnvPath("PATH");
96         lyxerr << "Looking for python 3.x ...\n";
97         for (auto bin: path) {
98                 QString const dir = toqstr(bin);
99                 string const localdir = dir.toLocal8Bit().constData();
100                 QDir qdir(dir);
101                 qdir.setFilter(QDir::Files | QDir::Executable);
102                 QStringList list = qdir.entryList(QStringList("python3*"));
103                 for (auto bin: list) {
104                         string const binary = addName(localdir,
105                                 bin.toLocal8Bit().constData());
106                         command = python23_call(binary, true);
107                         if (!command.empty())
108                                 return command;
109                 }
110         }
111
112         // python 3 was not found let us look for python 2
113         command = python23_call("python2");
114         if (!command.empty())
115                 return command;
116
117         // python2 does not exists, let us try to find python2.x in PATH
118         // the search is probably broader than required
119         // but we are trying hard to find a valid python binary
120         lyxerr << "Looking for python 2.x ...\n";
121         for (auto bin: path) {
122                 QString const dir = toqstr(bin);
123                 string const localdir = dir.toLocal8Bit().constData();
124                 QDir qdir(dir);
125                 qdir.setFilter(QDir::Files | QDir::Executable);
126                 QStringList list = qdir.entryList(QStringList("python2*"));
127                 for (auto bin: list) {
128                         string const binary = addName(localdir,
129                                 bin.toLocal8Bit().constData());
130                         command = python23_call(binary, true);
131                         if (!command.empty())
132                                 return command;
133                 }
134         }
135
136         // If this happens all hope is lost that this is a sane system
137         lyxerr << "Warning: No python v2.x or 3.x binary found.\n";
138         return python23_call("");
139 }
140
141
142 string const python(bool reset)
143 {
144         static string command = find_python_binary();
145         // FIXME THREAD
146         if (reset) {
147                 command = find_python_binary();
148         }
149         return command;
150 }
151
152 } // namespace os
153 } // namespace support
154 } // namespace lyx