-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsetup.py
1174 lines (1012 loc) · 43 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
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python
##############################################################################
##
# This file is part of Taurus
##
# http://taurus-scada.org
##
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
##
# Taurus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
##
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus. If not, see <http://www.gnu.org/licenses/>.
##
##############################################################################
from __future__ import print_function
import os
import sys
import copy
import shutil
import imp
import StringIO
from distutils import log
from distutils.core import setup, Command
from distutils.command.build import build as dftbuild
from distutils.command.clean import clean as dftclean
from distutils.command.install import install as dftinstall
from distutils.command.install_lib import install_lib as dftinstall_lib
from distutils.command.install_scripts import install_scripts as dftinstall_scripts
try:
import sphinx
import sphinx.util.console
sphinx.util.console.color_terminal = lambda: False
except:
sphinx = None
def abspath(*path):
"""A method to determine absolute path for a given relative path to the
directory where this setup.py script is located"""
setup_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(setup_dir, *path)
def get_release_info():
name = "release"
release_dir = abspath('lib', 'taurus', 'core')
data = imp.find_module(name, [release_dir])
release = imp.load_module(name, *data)
return release
Release = get_release_info()
package_dir = {'taurus': abspath('lib', 'taurus')}
packages = [
'taurus',
'taurus.test',
'taurus.external',
'taurus.external.argparse',
'taurus.external.enum',
#'taurus.external.enum.enum', #todo: do we need this one?
'taurus.external.ordereddict',
'taurus.external.pint',
'taurus.external.qt',
'taurus.external.unittest',
'taurus.external.test',
'taurus.core',
'taurus.core.test',
'taurus.core.util',
'taurus.core.util.argparse',
'taurus.core.util.decorator',
'taurus.core.util.report',
'taurus.core.util.test',
'taurus.core.epics',
'taurus.core.epics.test',
'taurus.core.epics.test.res',
'taurus.core.resource',
'taurus.core.resource.test',
'taurus.core.resource.test.res',
# 'taurus.core.spec',
# 'taurus.core.spec',
'taurus.core.evaluation',
'taurus.core.evaluation.test',
'taurus.core.tango',
'taurus.core.tango.img',
'taurus.core.tango.util',
'taurus.core.tango.test',
'taurus.core.tango.test.res',
'taurus.console',
'taurus.console.util',
'taurus.qt',
'taurus.qt.qtcore',
'taurus.qt.qtcore.communication',
'taurus.qt.qtcore.configuration',
'taurus.qt.qtcore.mimetypes',
'taurus.qt.qtcore.model',
'taurus.qt.qtcore.tango',
'taurus.qt.qtcore.util',
'taurus.qt.qtdesigner',
'taurus.qt.qtdesigner.taurusplugin',
'taurus.qt.qtgui',
'taurus.qt.qtgui.test',
'taurus.qt.qtgui.application',
'taurus.qt.qtgui.base',
'taurus.qt.qtgui.base.test',
'taurus.qt.qtgui.button',
'taurus.qt.qtgui.button.test',
'taurus.qt.qtgui.button.test.res',
'taurus.qt.qtgui.compact',
# 'taurus.qt.qtgui.console',
'taurus.qt.qtgui.container',
'taurus.qt.qtgui.dialog',
'taurus.qt.qtgui.display',
'taurus.qt.qtgui.display.test',
'taurus.qt.qtgui.display.demo',
'taurus.qt.qtgui.editor',
'taurus.qt.qtgui.gauge',
'taurus.qt.qtgui.gauge.demo',
'taurus.qt.qtgui.graphic',
'taurus.qt.qtgui.graphic.jdraw',
'taurus.qt.qtgui.graphic.jdraw.test.res',
'taurus.qt.qtgui.help',
'taurus.qt.qtgui.image',
'taurus.qt.qtgui.input',
'taurus.qt.qtgui.model',
'taurus.qt.qtgui.panel',
'taurus.qt.qtgui.panel.test',
'taurus.qt.qtgui.panel.report',
'taurus.qt.qtgui.plot',
'taurus.qt.qtgui.resource',
# 'taurus.qt.qtgui.shell',
'taurus.qt.qtgui.style',
'taurus.qt.qtgui.table',
'taurus.qt.qtgui.taurusgui',
'taurus.qt.qtgui.taurusgui.conf',
'taurus.qt.qtgui.tree',
'taurus.qt.qtgui.ui',
'taurus.qt.qtgui.util',
'taurus.qt.qtgui.util.test',
'taurus.qt.qtgui.util.test.test_ui',
'taurus.qt.qtgui.util.test.test_ui.mywidget3',
'taurus.qt.uic',
]
extra_packages = [
'taurus.qt.qtgui.extra_nexus',
'taurus.qt.qtgui.extra_xterm',
'taurus.qt.qtgui.extra_guiqwt',
'taurus.qt.qtgui.taurusgui.conf.tgconf_example01',
'taurus.qt.qtgui.taurusgui.conf.tgconf_macrogui',
# For backwards compat. They may be removed later on:
'taurus.qt.qtgui.extra_macroexecutor',
'taurus.qt.qtgui.extra_sardana',
'taurus.qt.qtgui.extra_pool',
]
provides = [
'taurus',
'taurus.core',
'taurus.qt',
]
requires = [
'numpy (>=1.1)',
#########################################################################
# TODO: if using setuptools, the following can be moved to extra_requires
'PyTango (>=7.1)', # [Taurus-Tango]
'PyQt4 (>=4.8)', # [Taurus-Qt]
'PyQt4.Qwt5 (>=5.2.0)', # [Taurus-Qt-Plot]
'ply (>=2.3)', # [Taurus-Qt-Synoptic]
'lxml (>=2.1)', # [Taurus-TaurusGUI]
'spyder (>=2.2)', # [Taurus-Editor] --> or maybe move it to sardana
# Consider also guiqwt, guidata, PyMca,...
#
#########################################################################
]
package_data = {
'taurus.core.epics': ['__taurus_plugin__'],
'taurus.core.evaluation': ['__taurus_plugin__'],
'taurus.core.resource': ['__taurus_plugin__'],
# 'taurus.core.spec' : ['__taurus_plugin__'],
'taurus.core.tango': ['__taurus_plugin__'],
'taurus.core.tango.test': ['res/*'],
'taurus.core.epics.test': ['res/*'],
'taurus.core.resource.test': ['res/*'],
'taurus.qt.qtgui.resource': ['*.rcc'],
'taurus.qt.qtgui.util': ['tauruswidget_template',
'tauruswidget_qtdesignerplugin_template'],
'taurus.qt.uic': ['pyuic4/*'],
'taurus.qt.qtgui.taurusgui.conf.tgconf_example01': ['images/*'],
'taurus.qt.qtgui.button.test': ['res/*'],
'taurus.qt.qtgui.graphic.jdraw.test': ['res/*'],
'taurus.qt.qtgui.help': ['ui/*.ui'],
'taurus.qt.qtgui.panel.report': ['ui/*.ui'],
'taurus.qt.qtgui.panel': ['ui/*.ui'],
'taurus.qt.qtgui.plot': ['ui/*.ui'],
'taurus.qt.qtgui.taurusgui': ['ui/*.ui'],
'taurus.qt.qtgui.extra_guiqwt': ['ui/*.ui'],
'taurus.qt.qtgui.util.test.test_ui': ['ui/*.ui', 'ui/mywidget2/*.ui'],
'taurus.qt.qtgui.util.test.test_ui.mywidget3': ['ui/*.ui'],
}
# The files listed here will be made executable when installed.
# The file names are relative to the dir containing setup.py
# Note: They must also be listed in packages or package_data
executable_data = [
'taurus/core/tango/test/res/TangoSchemeTest',
'taurus/qt/qtgui/button/test/res/Timeout',
]
# check if local implementations of enum and pint are here (debian removes them
# before running setup to avoid license issues)
if os.path.isdir(abspath('lib', 'taurus', 'external', 'enum', 'enum')):
packages.append('taurus.external.enum.enum')
if os.path.isdir(abspath('lib', 'taurus', 'external', 'pint', 'pint_local')):
packages.append('taurus.external.pint.pint_local')
packages.append('taurus.external.pint.pint_local.compat')
package_data['taurus.external.pint.pint_local'] = ['*.txt']
def get_script_files():
scripts_dir = abspath('scripts')
scripts = []
for item in os.listdir(scripts_dir):
# avoid hidden files
if item.startswith("."):
continue
abs_item = os.path.join(scripts_dir, item)
# avoid non files
if not os.path.isfile(abs_item):
continue
# avoid files that have any extension
if len(os.path.splitext(abs_item)[1]) > 0:
continue
scripts.append(os.path.join(scripts_dir, item))
return scripts
scripts = get_script_files()
data_files = [
]
classifiers = [
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Environment :: X11 Applications :: Qt',
'Environment :: Win32 (MS Windows)',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'Operating System :: Unix',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Topic :: Scientific/Engineering',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: User Interfaces',
'Topic :: Software Development :: Widget Sets',
]
class build_resources(Command):
description = "\"build\" Qt resource files"
user_options = [
('logo=', None, "alternative logo file (default is taurus.png)")]
AllowedExt = ('svg', 'png', 'jpg', 'jpeg', 'gif')
def initialize_options(self):
self.resource_dir = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource')
self.taurus = os.path.join(self.resource_dir, 'taurus.png')
self.logo = None # os.path.join(self.resource_dir,'taurus.png')
if self.distribution.verbose:
self.out = sys.stdout
else:
self.out = StringIO.StringIO()
def finalize_options(self):
if self.logo is None:
build = self.get_finalized_command('build')
if build:
self.logo = build.logo
if self.logo is None:
self.logo = self.taurus
if not os.path.isabs(self.logo):
self.logo = os.path.abspath(self.logo)
self.logo = os.path.realpath(self.logo)
if os.name == 'nt':
try:
self.QTDIR = os.environ["QTDIR"]
self.rcc_exec = self.rcc_exec = os.path.join(
self.QTDIR, 'bin', 'rcc')
except KeyError:
msg = "Cannot find QT installation. " \
"You should set the env. variable QTDIR " \
"pointing to the Qt C++ installation directory"
if build.with_tango_icons:
msg += ". Skipping creation of rcc files"
print (msg, file=self.out, end='')
self.rcc_exec = None
else:
msg += " or allow skipping creation of the rcc files by " \
"passing --with-tango-icons parameter to the build command"
raise Exception(msg)
else:
self.rcc_exec = 'rcc'
def run(self):
orig_dir = os.path.abspath(os.curdir)
os.chdir(self.resource_dir)
try:
cur_dir = os.path.abspath(os.curdir)
result = self._build_general_res()
result2 = self._build_res(cur_dir)
result[0].extend(result2[0])
result[1].extend(result2[1])
finally:
os.chdir(orig_dir)
def _build_general_res(self):
qrc_filename = 'general.qrc'
rcc_filename = 'qrc_general.rcc'
out = self.out
print("Generating %s... " % qrc_filename, file=out, end='')
out.flush()
f = file(qrc_filename, 'w')
try:
logo_relpath = os.path.relpath(self.logo)
taurus_relpath = os.path.relpath(self.taurus)
f.write('<RCC>\n <qresource>\n')
f.write(' <file alias="logo.png">%s</file>\n' %
logo_relpath)
f.write(' <file alias="taurus.png">%s</file>\n' %
taurus_relpath)
f.write(' </qresource>\n')
f.write('</RCC>\n')
except Exception, e:
print("[FAILED]\nDescription:\n%s" % str(e), file=out)
raise e
finally:
f.close()
print("[DONE]", file=out)
# Generate binary rcc file
if self.rcc_exec:
print("Generating %s... " % rcc_filename, file=out, end='')
out.flush()
cmd = '%s -binary %s -o %s' % (self.rcc_exec,
qrc_filename, rcc_filename)
if os.system(cmd):
print("[FAILED]", file=out)
else:
print("[DONE]", file=out)
return [[qrc_filename], [rcc_filename]]
def _build_res(self, abs_dir, bases=list()):
"""Builds the resources in the abs_dir recursively.
The result is a list of 2 items:
- a list of generated qrc files
- a list of generated rcc files
"""
result = [[], []]
res_name = os.path.basename(abs_dir)
local_elems, local_bases = [], copy.copy(bases)
local_bases.append(res_name)
out = self.out
for elem in os.listdir(abs_dir):
if elem.startswith('.'):
continue
abs_elem = os.path.abspath(os.path.join(abs_dir, elem))
if os.path.isdir(abs_elem):
ret = self._build_res(abs_elem, local_bases)
result[0].extend(ret[0])
result[1].extend(ret[1])
elif os.path.splitext(abs_elem)[1][1:].lower() in build_resources.AllowedExt:
local_elems.append(elem)
if local_elems and local_bases[1:]:
base_dir = os.path.join(*local_bases[1:])
base_filename = "_".join(local_bases[1:])
base_filename = base_filename.replace('-', '_')
qrc_filename = base_filename + ".qrc"
rcc_filename = 'qrc_' + base_filename + ".rcc"
# Generate qrc file
print("Generating %s... " % qrc_filename, file=out, end='')
out.flush()
f = file(qrc_filename, 'w')
try:
qres_prefix = ""
if len(local_bases) > 2:
qres_prefix = "/" + "/".join(local_bases[2:])
f.write('<RCC>\n <qresource prefix="%s">\n' %
qres_prefix)
else:
f.write('<RCC>\n <qresource>\n')
qres_prefix = ":" + qres_prefix
qres_prefix += "/"
for elem in local_elems:
rel_elem = os.path.join(base_dir, elem)
f.write(' <file alias="%s">%s</file>\n' %
(elem, rel_elem))
f.write(' </qresource>\n</RCC>')
except Exception, e:
print("[FAILED]\nDescription:\n%s" % str(e), file=out)
raise e
finally:
f.close()
result[0].append(qrc_filename)
print("[DONE]", file=out)
# Generate binary rcc file
if self.rcc_exec:
print("Generating %s... " % rcc_filename, file=out, end='')
out.flush()
cmd = '%s -binary %s -o %s' % (self.rcc_exec,
qrc_filename, rcc_filename)
if os.system(cmd):
print("[FAILED]", file=out)
else:
result[1].append(rcc_filename)
print("[DONE]", file=out)
return result
class build(dftbuild):
user_options = dftbuild.user_options + \
[('logo=', None, "alternative logo file (default is taurus.png)"),
('with-extra-widgets', None, "distribute extra widgets"),
('no-doc', None, "do not build documentation"),
('with-tango-icons', None, "add Tango icons too (not just *.rcc files)")]
boolean_options = dftbuild.boolean_options + \
['with-extra-widgets', 'no-doc']
def initialize_options(self):
dftbuild.initialize_options(self)
self.logo = None
self.doc_fmt = None
self.no_doc = None
self.with_tango_icons = None
self.with_extra_widgets = True
def finalize_options(self):
dftbuild.finalize_options(self)
if self.logo is None:
self.logo = abspath('lib', 'taurus', 'qt',
'qtgui', 'resource', 'taurus.png')
def run(self):
self.build_package_data()
dftbuild.run(self)
def build_package_data(self):
packages = self.distribution.packages
package_data = self.distribution.package_data
if self.with_extra_widgets:
packages.extend(extra_packages)
resource_package_data = self.get_extra_resource_package_data()
package_data['taurus.qt.qtgui.resource'].extend(resource_package_data)
def has_doc(self):
if self.no_doc:
return False
if not sphinx:
print("Sphinx not available: Documentation will not be build!")
return False
return os.path.isdir(abspath('doc'))
def has_resources(self):
return os.path.isdir(abspath('lib', 'taurus', 'qt', 'qtgui', 'resource'))
def get_extra_resource_package_data(self):
data = []
import PyQt4.Qt
if self.with_tango_icons or not hasattr(PyQt4.Qt.QIcon, "fromTheme"):
tango_icons_dir = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource',
'tango-icons')
for tango_icon_item in os.listdir(tango_icons_dir):
if tango_icon_item.startswith("."):
continue
abs_item = os.path.join(tango_icons_dir, tango_icon_item)
if not os.path.isdir(abs_item):
continue
data.append('tango-icons/%s/*' % tango_icon_item)
return data
sub_commands = [('build_resources', has_resources)] + \
dftbuild.sub_commands + \
[('build_doc', has_doc)]
class install_man(Command):
user_options = [
('install-dir=', 'd', 'base directory for installing man page files')]
def initialize_options(self):
self.install_dir = None
def finalize_options(self):
self.set_undefined_options('install',
('install_man', 'install_dir'))
def run(self):
src_man_dir = abspath('doc', 'man')
man_elems = os.listdir(src_man_dir)
man_pages = []
for f in man_elems:
f = os.path.join(src_man_dir, f)
if not os.path.isfile(f):
continue
if not f.endswith(".1"):
continue
man_pages.append(f)
install_dir = os.path.join(self.install_dir, 'man1')
if not os.path.isdir(install_dir):
os.makedirs(install_dir)
for man_page in man_pages:
self.copy_file(man_page, install_dir)
class install_html(Command):
user_options = [
('install-dir=', 'd', 'base directory for installing HTML documentation files')]
def initialize_options(self):
self.install_dir = None
def finalize_options(self):
self.set_undefined_options('install',
('install_html', 'install_dir'))
def run(self):
build_doc = self.get_finalized_command('build_doc')
src_html_dir = abspath(build_doc.build_dir, 'html')
self.copy_tree(src_html_dir, self.install_dir)
class install_scripts(dftinstall_scripts):
'''Customization to create .bat wrappers for the scripts
when installing on windows.
Adapted from a recipe by Matthew Brett (who licensed it under CC0):
https://github.com/matthew-brett/myscripter/blob/master/setup.py
See rationale in:
http://matthew-brett.github.io/pydagogue/installing_scripts.html
'''
user_options = list(dftinstall_scripts.user_options)
user_options.extend(
[
('wrappers', None,
'Install .bat wrappers for windows (enabled by default on windows)'),
('ignore-shebang', None, 'Use "python" as the interpreter in .bat wrappers (instead of using the interpreter found in the shebang line of the scripts). Note: this only affects to windows .bat wrappers!'),
])
BAT_TEMPLATE_SHEBANG = \
r"""@echo off
REM wrapper to use shebang first line of {FNAME}
set mypath=%~dp0
set pyscript="%mypath%{FNAME}"
set /p line1=<%pyscript%
if "%line1:~0,2%" == "#!" (goto :goodstart)
echo First line of %pyscript% does not start with "#!"
exit /b 1
:goodstart
set py_exe=%line1:~2%
call %py_exe% %pyscript% %*
"""
BAT_TEMPLATE_PATH = \
r"""@echo off
REM wrapper to launch {FNAME}
set mypath=%~dp0
set pyscript="%mypath%{FNAME}"
set py_exe="python"
call %py_exe% %pyscript% %*
"""
def initialize_options(self):
self.ignore_shebang = None
self.wrappers = (os.name == "nt")
dftinstall_scripts.initialize_options(self)
def run(self):
dftinstall_scripts.run(self)
if self.wrappers:
for filepath in self.get_outputs():
# If we can find an executable name in the #! top line of the script
# file, make .bat wrapper for script.
with open(filepath, 'rt') as fobj:
first_line = fobj.readline()
if not (first_line.startswith('#!') and
'python' in first_line.lower()):
print("No #!python executable found, skipping .bat wrapper")
continue
pth, fname = os.path.split(filepath)
froot, ext = os.path.splitext(fname)
bat_file = os.path.join(pth, froot + '.bat')
if self.ignore_shebang:
template = self.BAT_TEMPLATE_PATH
else:
template = self.BAT_TEMPLATE_SHEBANG
bat_contents = template.replace('{FNAME}', fname)
print("Making %s wrapper for %s" % (bat_file, filepath))
if self.dry_run:
continue
with open(bat_file, 'wt') as fobj:
fobj.write(bat_contents)
class install_lib(dftinstall_lib):
def run(self):
dftinstall_lib.run(self)
# Set the executable bits (owner, group, and world) on
# all executable_data
exe_ouput = [os.path.join(self.install_dir, f)
for f in executable_data]
if os.name == 'posix':
for fn in self.get_outputs():
if fn in exe_ouput:
if self.dry_run:
log.info("changing mode of %s", fn)
else:
mode = ((os.stat(fn).st_mode) | 0555) & 07777
log.info("changing mode of %s to %o", fn, mode)
os.chmod(fn, mode)
class install(dftinstall):
user_options = list(dftinstall.user_options)
user_options.extend([
('install-man=', None, 'installation directory for Unix man pages'),
('install-html=', None, "installation directory for HTML documentation"),
('no-doc', None, "do not install HTML documentation")])
def initialize_options(self):
self.install_man = None
self.install_html = None
self.no_doc = None
dftinstall.initialize_options(self)
def finalize_options(self):
# We do a hack here. We cannot trust the 'install_base' value because it
# is not always the final target. For example, in unix, the install_base
# is '/usr' and all other install_* are directly relative to it. However,
# in unix-local (like ubuntu) install_base is still '/usr' but, for
# example, install_data, is '$install_base/local' which breaks everything.
#
# The hack consists in using install_data instead of install_base since
# install_data seems to be, in practice, the proper install_base on all
# different systems.
dftinstall.finalize_options(self)
if os.name != "posix":
if self.install_man is not None:
self.warn("install-man option ignored on this platform")
self.install_man = None
else:
if self.install_man is None:
self.install_man = os.path.join(
self.install_data, 'share', 'man')
if self.install_html is None:
self.install_html = os.path.join(
self.install_data, 'share', 'doc', 'taurus', 'html')
if self.no_doc is None:
self.no_doc = False
self.dump_dirs("Installation directories")
def expand_dirs(self):
dftinstall.expand_dirs(self)
self._expand_attrs(['install_man'])
def has_man(self):
return os.name == "posix"
def has_html(self):
if self.no_doc:
return False
return sphinx is not None
sub_commands = list(dftinstall.sub_commands)
sub_commands.append(('install_man', has_man))
sub_commands.append(('install_html', has_html))
class clean(dftclean):
def run(self):
dftclean.run(self)
# This is a very crude approach to clean the garbage created by taurus
# outside of the build dir when running the build command
# see: https://sourceforge.net/p/sardana/tickets/324/
import glob
from distutils.dir_util import remove_tree
# collect the garbage *files* to be deleted
garbage = []
resource = abspath('lib', 'taurus', 'qt', 'qtgui', 'resource')
garbage.extend(glob.glob(os.path.join(resource, '*.rcc')))
garbage.extend(glob.glob(os.path.join(resource, '*.qrc')))
garbage.append(os.path.join(resource, 'catalog.html'))
doc_devel = abspath('doc', 'source', 'devel')
garbage.append(os.path.join(doc_devel, 'catalog.html'))
doc = abspath('doc')
garbage.append(os.path.join(doc, '~thumbnails.zip'))
# delete the garbage files
for fn in garbage:
if os.path.exists(fn):
log.info("removing '%s'", fn)
if self.dry_run:
continue
os.remove(fn)
else:
log.debug("'%s' does not exist -- can't clean it", fn)
# now delete the api dir
api_dir = os.path.join(doc_devel, 'api')
if os.path.exists(api_dir):
remove_tree(api_dir, dry_run=self.dry_run)
else:
log.debug("'%s' does not exist -- can't clean it", api_dir)
cmdclass = {'build': build,
'build_resources': build_resources,
'install': install,
'install_lib': install_lib,
'install_man': install_man,
'install_html': install_html,
'install_scripts': install_scripts,
'clean': clean}
if sphinx:
from sphinx.setup_command import BuildDoc
class build_catalog(object):
'''builds an html catalog of icons. It links to png thumbnails that are
created in the _static dir
'''
AllowedExt = build_resources.AllowedExt
HTML_IL = '<tr height="30">' \
'<td width="30" align="center">' \
'<img width="24" src="{thumbnail}"' \
' alt="{res_relpath}"/></td>' \
'<td width="400">{qres_prefix}{icon_name}</td>' \
'<td width="400">{res_relpath}</td>' \
'<td width="200">{theme}</td></tr>\n'
HTML_T = '<table border="1" cellspacing="0" cellpadding="2">\n' \
'<th colspan="4">Resource: "%s" Directory: "%s"</th>\n' \
'<tr><th>Preview</th><th>Resouce</th><th>File name</th>' \
'<th>Theme</th></tr>\n'
def run(self):
self.resource_dir = abspath('lib', 'taurus', 'qt', 'qtgui',
'resource')
pngs_dir = os.path.abspath(os.path.join(self.builder_target_dir,
'_static',
'icon_thumbnails'))
devel_dir = os.path.abspath(os.path.join(self.builder_target_dir,
'devel'))
self.thumbnails_relpath = os.path.relpath(pngs_dir, devel_dir)
orig_dir = os.path.abspath(os.curdir)
os.chdir(self.resource_dir)
# create temporary catalog file (to be removed at end of build_doc)
catalog = file(self.fname, 'w')
catalog.write("<html><head>\n<title>taurus Icon Catalog</title>\n"
"<style>table { border-collapse: collapse; }</style>\n</head>\n<body>\n")
try:
cur_dir = os.path.abspath(os.curdir)
result = self._build_general_res()
result2 = self._build_res(cur_dir)
result[0].extend(result2[0])
result[1].extend(result2[1])
catalog.write("<h1>Index</h1>\n<ul>")
for anchor in result[1]:
catalog.write("<li>%s</li>\n" % anchor)
catalog.write("</ul>\n")
catalog.writelines(result[0])
finally:
catalog.write("""</body></html>""")
catalog.close()
os.chdir(orig_dir)
# make thumbnails
try:
if self.thumbnails_source == 'wand':
from wand.image import Image
def transform(ifname, ofname):
with Image(filename=ifname) as img:
img.transform(resize='24x')
img.save(filename=ofname)
return True
return False
elif self.thumbnails_source == 'qt':
import PyQt4.Qt
if PyQt4.Qt.qApp.instance() is None:
self.app = PyQt4.Qt.QApplication([])
from PyQt4 import Qt
def transform(ifname, ofname):
pixmap = Qt.QPixmap(ifname)
p = pixmap.scaledToWidth(
24, Qt.Qt.SmoothTransformation)
return p.save(ofname)
else:
if not os.path.isabs(self.thumbnails_source):
m = 'Absolute path required for Thumbnails dir or zip'
raise ValueError(m)
shutil.rmtree(pngs_dir, ignore_errors=True)
if self.thumbnails_source.lower().endswith('.zip'):
from zipfile import ZipFile
zfile = ZipFile(self.thumbnails_source)
zfile.extractall(pngs_dir)
else:
shutil.copytree(self.thumbnails_source, pngs_dir)
def transform(ifname, ofname):
# just check if the required thumbnail exists
return os.path.isfile(ofname)
print("\tCreating PNG thumbnails for icon catalog")
os.path.walk(self.resource_dir, self._make_thumbnails,
(self.resource_dir, pngs_dir, transform))
# create a zipped file for the thumbnails
fname = abspath('doc', '~thumbnails.zip')
if os.path.isfile(fname):
os.remove(fname)
self._zipdir(pngs_dir, fname)
except ImportError, e:
print("\tCannot create PNG thumbnails for icon catalog: %s" %
repr(e))
@staticmethod
def _make_thumbnails(arg, dirname, fnames):
'''create thumbnails. To be called by a walker'''
resource, target, transform = arg
relpath = os.path.relpath(dirname, start=resource)
path = os.path.join(target, relpath)
if not os.path.isdir(path):
os.makedirs(path)
for fname in fnames:
fbase, f_ext = os.path.splitext(fname)
if f_ext[1:] in build_catalog.AllowedExt:
full_source_fname = os.path.join(dirname, fname)
target_fname = fbase + ".png"
full_target_fname = os.path.join(path, target_fname)
if not os.path.isfile(full_target_fname):
ok = transform(full_source_fname, full_target_fname)
print(ok and "[OK]" or "[FAIL]", full_source_fname,
'->', full_target_fname)
@staticmethod
def _zipdir(basedir, archivename):
'''function to zip the contents of basedir into archivename.
Adapted from: http://stackoverflow.com/questions/296499
'''
from zipfile import ZipFile, ZIP_DEFLATED
from contextlib import closing
assert os.path.isdir(basedir)
with closing(ZipFile(archivename, "w", ZIP_DEFLATED)) as z:
for root, dirs, files in os.walk(basedir):
# NOTE: ignore empty directories
for fn in files:
absfn = os.path.join(root, fn)
zfn = absfn[len(basedir) + len(os.sep):]
z.write(absfn, zfn)
def getThemeIcon(self, resource):
try:
import PyQt4.Qt
if not hasattr(PyQt4.Qt.QIcon, "hasThemeIcon"):
return "Unknown"
i = resource.rfind("/")
if i >= 0:
resource = resource[i + 1:]
i = resource.rfind(".")
if i >= 0:
resource = resource[:i]
if PyQt4.Qt.QIcon.hasThemeIcon(resource):
return resource
return "No"
except:
return "Unknown"
def _build_general_res(self):
out = self.out
html = '<h2><a name="_base">Base icons</a></h2>\n'
html += self.HTML_T % (':/', '')
anchor = '<a href="#_base">Base icons</a>'
try:
taurus_relpath = 'taurus.png'
png_relpath = os.path.join(self.thumbnails_relpath,
taurus_relpath)
html += self.HTML_IL.format(thumbnail=png_relpath,
res_relpath=taurus_relpath,
qres_prefix=":/",
icon_name=taurus_relpath,
theme=self.getThemeIcon(
"taurus.png")
)
except Exception, e:
print("[FAILED]\nDescription:\n%s" % str(e), file=out)
import traceback
traceback.print_exc()
raise e
finally:
html += '</table>\n'
return [[html], [anchor]]
def _build_res(self, abs_dir, bases=list()):
"""Builds the resources in the abs_dir recursively.
The result is a list of 5 items:
- a list of HTML strings
- a list of HTML anchors
"""
result = [[], []]
res_name = os.path.basename(abs_dir)
local_elems, local_bases = [], copy.copy(bases)
local_bases.append(res_name)
out = self.out
for elem in os.listdir(abs_dir):
if elem.startswith('.'):
continue
abs_elem = os.path.abspath(os.path.join(abs_dir, elem))
if os.path.isdir(abs_elem):
ret = self._build_res(abs_elem, local_bases)
result[0].extend(ret[0])
result[1].extend(ret[1])
elif os.path.splitext(abs_elem)[1][1:].lower() in build_resources.AllowedExt:
local_elems.append(elem)
if local_elems and local_bases[1:]: