-
Notifications
You must be signed in to change notification settings - Fork 1
/
conanfile.py
253 lines (228 loc) · 10.6 KB
/
conanfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
from conans import ConanFile
from conan.errors import ConanException
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import os
# pylint: disable=W0201
class PythonDevConfigConan(ConanFile):
name = "python_dev_config"
version = "0.6"
license = "MIT"
export = ["LICENSE.md"]
description = "Configuration of Python interpreter for use as a development dependency."
url = "https://github.com/bincrafters/conan-python_dev_config"
options = {"python": "ANY"}
default_options = {'python': 'python'}
settings = "os", "arch", "compiler"
build_policy = "missing"
def package_id(self):
self.info.header_only()
self.info.options.python_version = self._python_version
def package_info(self):
if self.have_python_dev:
self.cpp_info.includedirs = [self._python_include]
self.cpp_info.libdirs = [os.path.dirname(self._python_lib)]
self.cpp_info.libs = [self._python_lib_ldname]
self.cpp_info.bindirs = [os.path.dirname(self._python_lib), os.path.dirname(self._python_exec)]
self.user_info.python_version = self._python_version
self.user_info.python_exec = self._python_exec
self.user_info.python_include_dir = self._python_include
self.user_info.python_lib = self._python_lib
self.user_info.python_lib_dir = os.path.dirname(self._python_lib)
self.env_info.path.append(os.path.dirname(self._python_lib))
self.env_info.path.append(os.path.dirname(self._python_exec))
@property
def have_python_dev(self):
if not self._python_exec:
return False
if not self._python_include:
return False
if not self._python_lib:
return False
if not os.path.exists(os.path.join(self._python_include, 'Python.h')):
return False
return True
@property
def _is_msvc(self):
return self.settings.compiler == "Visual Studio"
@property
def _python_exec(self):
"""
obtain full path to the python interpreter executable
:return: path to the python interpreter executable, either set by option, or system default
"""
if not hasattr(self, '_py_exec'):
self._py_exec = self._run_python_script("from __future__ import print_function; "
"import sys; "
"print(sys.executable)",
str(self.options.python))
return self._py_exec
@property
def _python_version(self):
"""
obtain version of python interpreter
:return: python interpreter version, in format major.minor
"""
if not hasattr(self, '_py_version'):
self._py_version = self._run_python_script("from __future__ import print_function; "
"import sys; "
"print('%s.%s' % (sys.version_info[0], sys.version_info[1]))")
return self._py_version
@property
def _python_lib(self):
"""
attempt to find python development library
:return: the full path to the python library to be linked with
"""
if not hasattr(self, '_py_lib'):
self._py_lib = None
library = self._get_python_var("LIBRARY")
ldlibrary = self._get_python_var("LDLIBRARY")
libdir = self._get_python_var("LIBDIR")
multiarch = self._get_python_var("MULTIARCH")
masd = self._get_python_var("multiarchsubdir")
with_dyld = self._get_python_var("WITH_DYLD")
if libdir and multiarch and masd:
if masd.startswith(os.sep):
masd = masd[len(os.sep):]
libdir = os.path.join(libdir, masd)
if not libdir:
libdest = self._get_python_var("LIBDEST")
libdir = os.path.join(os.path.dirname(libdest), "libs")
candidates = [ldlibrary, library]
library_prefixes = [""] if self._is_msvc else ["", "lib"]
library_suffixes = [".lib"] if self._is_msvc else [".so", ".dll.a", ".a"]
if with_dyld:
library_suffixes.insert(0, ".dylib")
python_version = self._python_version
python_version_no_dot = python_version.replace(".", "")
versions = ["", python_version, python_version_no_dot]
abiflags = self._python_abiflags
for prefix in library_prefixes:
for suffix in library_suffixes:
for version in versions:
candidates.append("%spython%s%s%s" % (prefix, version, abiflags, suffix))
for candidate in candidates:
if candidate:
python_lib = os.path.join(libdir, candidate)
self.output.info('checking %s' % python_lib)
if os.path.isfile(python_lib):
self.output.info('found python library: %s' % python_lib)
self._py_lib = python_lib.replace('\\', '/')
return self._py_lib
return self._py_lib
@property
def _python_lib_ldname(self):
if not hasattr(self, '_py_lib_ldname'):
libname = os.path.splitext(os.path.basename(self._python_lib))[0]
if not self._is_msvc:
libname = libname[3:] if libname.startswith("lib") else libname
self._py_lib_ldname = libname
return self._py_lib_ldname
@property
def _python_bindir(self):
if not hasattr(self, '_py_bindir'):
self._py_bindir = self._get_python_var('BINDIR')
return self._py_bindir
@property
def _python_include(self):
"""
attempt to find directory containing Python.h header file
:return: the directory with python includes
"""
if not hasattr(self, '_py_include'):
self._py_include = None
include = self._get_python_path('include')
plat_include = self._get_python_path('platinclude')
include_py = self._get_python_var('INCLUDEPY')
include_dir = self._get_python_var('INCLUDEDIR')
python_inc = self._python_inc
candidates = [include,
plat_include,
include_py,
include_dir,
python_inc]
for candidate in candidates:
if candidate:
python_h = os.path.join(candidate, 'Python.h')
self.output.info('checking %s' % python_h)
if os.path.isfile(python_h):
self.output.info('found Python.h: %s' % python_h)
self._py_include = candidate.replace('\\', '/')
return self._py_include
return self._py_include
@property
def _python_inc(self):
"""
obtain the result of the "sysconfig.get_python_inc()" call
:return: result of the "sysconfig.get_python_inc()" execution
"""
return self._run_python_script("from __future__ import print_function; "
"import sysconfig; "
"print(sysconfig.get_python_inc())")
def _get_python_sc_var(self, name):
"""
obtain value of python sysconfig variable
:param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
:return: value of python sysconfig variable
"""
return self._run_python_script("from __future__ import print_function; "
"import sysconfig; "
"print(sysconfig.get_config_var('%s'))" % name)
def _get_python_du_var(self, name):
"""
obtain value of python distutils sysconfig variable
(sometimes sysconfig returns empty values, while python.sysconfig provides correct values)
:param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
:return: value of python sysconfig variable
"""
return self._run_python_script("from __future__ import print_function; "
"import distutils.sysconfig as du_sysconfig; "
"print(du_sysconfig.get_config_var('%s'))" % name)
def _get_python_var(self, name):
"""
obtain value of python variable, either by sysconfig, or by distutils.sysconfig
:param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
:return: value of python sysconfig variable
"""
return self._get_python_sc_var(name) or self._get_python_du_var(name)
def _get_python_path(self, name):
"""
obtain path entry for the python installation
:param name: name of the python config entry for path to be queried (such as "include", "platinclude", etc.)
:return: path entry from the sysconfig
"""
# https://docs.python.org/3/library/sysconfig.html
# https://docs.python.org/2.7/library/sysconfig.html
return self._run_python_script("from __future__ import print_function; "
"import sysconfig; "
"print(sysconfig.get_path('%s'))" % name)
@property
def _python_abiflags(self):
"""
obtain python ABI flags, see https://www.python.org/dev/peps/pep-3149/ for the details
:return: the value of python ABI flags
"""
return self._run_python_script("from __future__ import print_function; "
"import sys; "
"print(getattr(sys, 'abiflags', ''))")
def _run_python_script(self, script, python_exec=None):
"""
execute python one-liner script and return its output
:param script: string containing python script to be executed
:return: output of the python script execution, or None, if script has failed
"""
python_exec = python_exec or self._python_exec
output = StringIO()
command = '"%s" -c "%s"' % (python_exec, script)
self.output.info('running %s' % command)
try:
self.run(command=command, output=output)
except ConanException:
self.output.info("(failed)")
return None
output = output.getvalue().strip()
self.output.info(output)
return output if output != "None" else None