-
Notifications
You must be signed in to change notification settings - Fork 5
/
setup.py
432 lines (351 loc) · 16.3 KB
/
setup.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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
#!/usr/bin/env python
from __future__ import print_function
__author__ = "Remi Chateauneu"
__copyright__ = "Copyright 2020-2021, Primhill Computers"
__license__ = "GPL"
import os
import sys
import ast
import importlib
is_py2 = sys.version_info < (3,)
# pip install ..\dist\survol-1.0.dev0.zip --upgrade --install-option="--port 12345"
# TODO: Have a look to setuptools.setup
from distutils import log
from distutils.core import setup
from setuptools import find_packages
from setuptools.command.install import install
from setuptools.command.install_lib import install_lib
from setuptools.command.build_ext import build_ext
from distutils.command.clean import clean
# See https://gist.github.com/ctokheim/6c34dc1d672afca0676a for more details.
try:
from Cython.Build import cythonize
except ImportError:
cythonize = None
# If cython is present, it is used by default.
if cythonize:
if '--without-cython' in sys.argv:
USE_CYTHON = False
sys.argv.remove('--without-cython')
elif '--no-cython' in sys.argv:
USE_CYTHON = False
sys.argv.remove('--no-cython')
else:
USE_CYTHON = True
else:
USE_CYTHON = False
if 'clean' in sys.argv:
# Maybe Cython cannot work for some reason, and it would prevent cleaning up.
USE_CYTHON = False
if is_py2:
from future_builtins import filter
language_level = "2" if is_py2 else "3"
# https://stackoverflow.com/questions/677577/distutils-how-to-pass-a-user-defined-parameter-to-setup-py
#
# pip install -v displays methods calls and their outputs.
#
# The following methods are called, see -v option:
# running install
# Running initialize_options.
class InstallCommand(install):
# The html files can be copied at any place.
# For example at ~/public_html on Unix, i.e. "Users Dir feature of Apache".
# TODO: These options are not used yet.
user_options = install.user_options + [
('port=', 'p', 'CGI server port number'), # Not used at the moment.
('www=', 'w', 'Web UI destination directory'), # Not used at the moment.
]
def initialize_options(self):
print("Running initialize_options")
install.initialize_options(self)
self.port = 24680 # TODO: This is not used yet.
# By default, cgiserver will pick its files from the Python installation directory,
# and this is acceptable because they are part of the same package.
# http://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation
# For the default destination of HTML pages, see also "pkg_resources" and the likes:
# "The distutils normally install general "data files" to a platform-specific location (e.g. /usr/share)"
# So it is possible to install the HTML pages in a normal directory, and also in a location,
# standard for Python, where Python modules can retrieve them. and also the Python server.
self.www = None
def finalize_options(self):
print('The port number for install is:%s' % self.port)
install.finalize_options(self)
def run(self):
# This must be given to a parameter file for cgiserver.
my_port = self.port
# The HTML, css and js files must be copied at this place.
# This avoids to have a HTTP server such as Apache or IIS,
# pick its files from the Python installation dir.
my_www = self.www
# Also, the css and js files must be copied into the Python directory.
print("Custom installation. Port:%s Dest dir=%s" % (my_port, my_www))
if my_www:
print("About to copy %s" % my_www)
install.run(self) # OR: install.do_egg_install(self)
def _script_to_cgi_executable(one_path):
"""
This rewrites a Python script, making it executable by setting the Linux flag,
and also fixes the line terminator to Linxu standard, if necessary.
TODO: Consider the command: build_scripts: "build" scripts (copy and fixup #! line)
"""
with open(one_path, 'rb') as input_stream:
# Binary read so the line terminator is intact.
file_content = input_stream.readlines()
if len(file_content) == 0:
log.info("InstallLibCommand empty file=%s" % one_path)
return
# By convention, all Survol CGI scripts start with this header line.
# There must not be any CR character after this shebang line.
if file_content[0].startswith(b"#!/usr/bin/env python"):
log.info("Script file=%s l=%d" % (one_path, len(file_content)))
try:
# Maybe this file is a script. If so, remove "CRLF" at the end.
lines_number = 0
with open(one_path, 'wb') as output_stream:
for one_line in file_content:
if one_line.endswith((b'\r\n', b'\n\r')):
output_stream.write(b"%s\n" % one_line[:-2])
else:
output_stream.write(one_line)
lines_number += 1
log.info("Script written=%d" % lines_number)
# Set executable flag for Linux CGI scripts.
os.chmod(one_path, 0o744)
except Exception as exc:
log.error("Script err=%s" % exc)
is_linux_or_darwin = sys.platform.startswith("lin") or sys.platform == "darwin"
class InstallLibCommand(install_lib):
"""
On Linux and Darwin, this converts Python files to proper Unix format, and sets the executable flag.
This is needed because setup.py does not keep the executable flag information.
Also, files stored in Github have Windows lines terminators.
So, to be sure, this strips these line terminators which are invalid in Linux shell scripts.
"""
def copy_tree(
self, infile, outfile,
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
):
"""This is called on Linux and Darwin, from the top-level infile='build\\lib' """
if is_linux_or_darwin:
library_top = os.path.join(infile, "survol")
for library_root, library_dirs, library_files in os.walk(library_top):
for one_file in library_files:
if one_file.endswith(".py"):
one_path = os.path.join(library_root, one_file)
self._script_to_cgi_executable(one_path)
return install_lib.copy_tree(self, infile, outfile,
preserve_mode, preserve_times, preserve_symlinks, level)
def _cythonizable_source_files():
"""
This returns the list of absolute paths of files to be cythonized.
Only a subset of files are compiled.
"""
survol_base_dir = os.path.join(os.path.dirname(__file__), "survol")
src_files = []
basenames_list = [
"scripts/linux_api_definitions.py",
"scripts/win32_api_definitions.py",
# "cim_objects_definitions",
# Import problem with Python 3
## "naming_conventions",
# Unexplained bug yet (In summarization of function calls).
# "dockit",
"scripts/dockit_aggregate_clusterize.py",
]
for root, dir, top_level_files in os.walk(survol_base_dir):
break
not_compiled_files = set([
# https://stackoverflow.com/questions/19630634/python-file-is-not-defined
"lib_util.py",
# survol\lib_client.py:875:63: Compiler crash in AnalyseExpressionsTransform
"lib_client.py",
# LINK : error LNK2001: unresolved external symbol init__init__
"__init__.py",
# AttributeError: 'module' object has no attribute 'gUriGen'
"lib_common.py",
])
for one_file in top_level_files:
if one_file.endswith(".py") and one_file not in not_compiled_files:
basenames_list.append(one_file)
for one_filename in basenames_list:
one_path_name = os.path.join(survol_base_dir, one_filename)
src_files.append(one_path_name)
return src_files
class BuildExtCommand(build_ext):
"""
A custom command to build Cython extensions.
This is needed otherwise setup does not build or rebuild C files from Python source files.
FIXME: Not sure why the presence of this custom command which does not do much, fix the build issue.
Usage example: python setup.py build_ext --inplace
It ignores build-lib and put compiled extensions into the source directory alongside the pure Python modules.
These libraries are prioritized before *.py files by Python, and loaded instead.
"""
description = 'Build Cython extensions'
def initialize_options(self):
print("build_ext.initialize_options")
build_ext.initialize_options(self)
def finalize_options(self):
"""Post-process options."""
print("build_ext.finalize_options")
build_ext.finalize_options(self)
def run(self):
"""Run command."""
print("build_ext.run")
build_ext.run(self)
class CleanCommand(clean):
"""
Clean build including iniplace built extensions.
"""
description = 'Clean build including in-place built extensions.'
def _cleanup_libs(self):
def remove_lib_file(lib_path):
try:
os.remove(lib_path)
print("removed cythonized file:", lib_path)
except:
print("Cannot remove:", lib_path)
src_files = _cythonizable_source_files()
for one_file in src_files:
assert one_file.endswith(".py")
file_without_extension = os.path.splitext(one_file)[0]
if sys.platform.startswith("lin") or sys.platform == "darwin":
lib_path = file_without_extension + ".so"
remove_lib_file(lib_path)
else:
# for example: ['.cp36-win_amd64.pyd', '.pyd']
for one_suffix in importlib.machinery.EXTENSION_SUFFIXES:
# The file name might be something like: "collection.cp36-win_amd64.pyd"
lib_path = file_without_extension + one_suffix
remove_lib_file(lib_path)
def run(self):
"""Run command."""
print("Removing in-place built libs")
self._cleanup_libs()
clean.run(self)
# Some explanations: The Python scripts in survol/sources_types (Plus some of them
# in survol/ like survol/entity.py etc...) are CGI scripts.
# Therefore, they can easily be run, debugged and tested in isolation, without a HTTP server.
# These scripts can also be imported: This is how the WSGI server works.
# Some CGI scripts could easily be rewritten in another language for performance.
def package_files(directory):
"""The current directory is where setup.py is."""
paths = []
for path, directories, filenames in os.walk(directory):
for filename in filenames:
paths.append(os.path.join('..', path, filename))
return paths
# HTML and Javascript files for the D3 interface.
extra_files = package_files('survol/www')
# This file is needed for the events generators processes, started by the package supervisor.
extra_files += ['survol/scripts/supervisord.conf']
# The created zip archive contains directories: docs, survol and tests.
# This extracts the version number from the file "survol/__init__.py"
with open(os.path.join('survol', '__init__.py')) as f:
__version__ = ast.parse(next(filter(lambda line: line.startswith('__version__'), f))).body[0].value.s
with open('README.txt') as readme_file:
README = readme_file.read()
# FIXME: Cleanup the doc strings, for example survol.__doc__ = '\nSurvol library\n' ...
# These packages are mandatory.
required_packages = ['psutil', 'rdflib']
if is_py2:
required_packages.append("configparser")
# Other very useful packages: "wmi", "pywbem", "rdflib-sqlalchemy".
setup_options = dict(
name='survol',
version=__version__,
description='Exploring legacy IT systems',
long_description=README,
author='Primhill Computers',
author_email='contact@primhillcomputers.com',
url='http://www.primhillcomputers.com/survol.html',
packages=find_packages(),
package_dir={"survol": "survol"},
# This is apparently not recursive.
package_data={'survol': extra_files},
entry_points={'console_scripts': [
'survolcgi = survol.scripts.cgiserver:cgiserver_entry_point',
'survolwsgi = survol.scripts.wsgiserver:wsgiserver_entry_point',
'dockit = survol.scripts.wsgiserver:dockit_entry_point',
]},
install_requires=required_packages,
cmdclass={
'install': InstallCommand,
'install_lib': InstallLibCommand,
'clean': CleanCommand,
'build_ext': BuildExtCommand,
},
scripts=['survol/scripts/cgiserver.py', 'survol/scripts/wsgiserver.py', 'survol/scripts/dockit.py'],
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'Intended Audience :: System Administrators',
'Intended Audience :: Education',
'Intended Audience :: Information Technology',
'License :: OSI Approved :: Python Software Foundation License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: JavaScript',
'Topic :: Software Development :: Bug Tracking',
'Topic :: Education',
'Topic :: Software Development :: Documentation',
'Topic :: System :: Systems Administration',
'Topic :: Documentation'
]
)
if USE_CYTHON:
src_files = _cythonizable_source_files()
cython_ext_modules = cythonize(
src_files,
build_dir="build_cythonize",
# nthreads = 3,
annotate=True,
compiler_directives={'language_level': language_level})
# With "force", the C code is regenerated each time.
# Otherwise, setup does not detect an input Python file change.
# Maybe this can be forced with: python setup.py build_ext --force
cython_options = dict(
ext_modules=cython_ext_modules,
# script_args=['build'],
options={
#'build': {'build_lib': 'build_build_ext', 'force': 1},
'build': {'build_lib': 'build_build_ext'},
})
setup_options.update(cython_options)
setup(**setup_options)
################################################################################
# APPENDIX: Some tips about the installation of Survol under Apache.
#
# Two installations types are possible:
# (1) With the CGI scripts cgiserver, which just need to be accessible,
# and imports survol Python modules, installed by sdist.
# (2) Or if Apache runs the sources files from the development directory or from the installed packages.
# This is what is demonstrated here.
# Alias /Survol "C:/Users/rchateau/Developpement/ReverseEngineeringApps/PythonStyle"
# <Directory "C:/Users/rchateau/Developpement/ReverseEngineeringApps/PythonStyle" >
# Options Indexes FollowSymLinks Includes ExecCGI
# Allow from all
# AddHandler cgi-script .py
# # http://stackoverflow.com/questions/2036577/how-do-i-ignore-the-perl-shebang-on-windows-with-apache-2
# ScriptInterpreterSource Registry-Strict
# # SetEnv PYTHONPATH C:\Users\rchateau\Developpement\ReverseEngineeringApps\PythonStyle\survol\revlib
# </Directory>
################################################################################
## Appendix'appendix: How to install Yawn, which is an HTML navigator into OpenLmi (Pegasus) objects and classes.
## apache's configuration file for yawn using wsgi
## We could add this content in yawn.conf and incldue the content.
# WSGIScriptAlias /yawn "C:/Users/rchateau/Developpement/ReverseEngineeringApps/pywbem_all/pywbem_sourceforge/yawn2/trunk/mod_wsgi/yawn_wsgi.py"
## For development convenience, no need to install anything because we point to the development files.
# <Directory "C:/Users/rchateau/Developpement/ReverseEngineeringApps/pywbem_all/pywbem_sourceforge/yawn2/trunk/mod_wsgi>
# # Options Indexes FollowSymLinks Includes ExecCGI
# Options ExecCGI
# # Allow from all
# WSGIPassAuthorization On
# # AddHandler cgi-script .py
# # http://stackoverflow.com/questions/2036577/how-do-i-ignore-the-perl-shebang-on-windows-with-apache-2
# # ScriptInterpreterSource Registry-Strict
# SetEnv PYTHONPATH C:\Users\rchateau\Developpement\ReverseEngineeringApps\pywbem_all\pywbem_sourceforge\yawn2\trunk\mod_wsgi\pywbem_yawn
# </Directory>
################################################################################