forked from usnistgov/OOF2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup.py
1503 lines (1330 loc) · 61.1 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
# -*- python -*-
# This software was produced by NIST, an agency of the U.S. government,
# and by statute is not subject to copyright in the United States.
# Recipients of this software assume all responsibilities associated
# with its operation, modification and maintenance. However, to
# facilitate maintenance we ask that before distributing modified
# versions of this software, you first contact the authors at
## TODO: swig needs to be run with -DDEBUG when setup.py is run with --debug.
## Usage:
# In the top oof2 directory (the one containing this file) type
# something like this:
# python setup.py install # installs in the default location
# python setup.py install --prefix=/some/other/place
# python setup.py [build [--debug]] install --prefix ...
# The flags --3D, --enable-mpi, --enable-petsc, and --enable-devel
# can occur anywhere after 'setup.py' in the command line.
## TODO: Copy the TEST directory into share/oof2/TEST when installing.
## This will make it easier to run tests on nanoHUB and maybe
## elsewhere.
# Required version numbers of required external libraries. These
# aren't used explicitly in this file, but they are used in the DIR.py
# files that are execfile'd here.
GTK_VERSION = "2.6.0"
PYGTK_VERSION = "2.6"
GNOMECANVAS_VERSION = "2.6"
MAGICK_VERSION = "6.0"
# If on a 64-bit system with Python 2.5 or later, make sure that
# pygobject is at least version 2.12.
try:
import ctypes # only in 2.5 and later
if ctypes.sizeof(ctypes.c_long) == 8:
PYGOBJECT_VERSION = "2.12"
else:
PYGOBJECT_VERSION = "2.6"
except ImportError:
PYGOBJECT_VERSION = "2.6"
# The make_dist script edits the following line when a distribution is
# built. Don't change it by hand. On the git master branch,
# "(unreleased)" is replaced by the version number.
version_from_make_dist = "(unreleased)"
# will need to add vtk
###############################
import distutils.core
from distutils.command import build
from distutils.command import build_ext
from distutils.command import build_py
from distutils.command import clean
from distutils.command import build_scripts
from distutils import errors
from distutils import log
from distutils.dir_util import remove_tree
from distutils.sysconfig import get_config_var
import oof2installlib
import shlib # adds build_shlib and install_shlib to the distutils command set
from shlib import build_shlib
from oof2setuputils import run_swig, find_file, extend_path, get_third_party_libs
import os
import shlex
import stat
import string
import sys
import subprocess
import tempfile
import time
from types import *
# Tell distutils that .C is a C++ file suffix.
from distutils.ccompiler import CCompiler
CCompiler.language_map['.C'] = 'c++'
# py2app stuff is commented out because, in python 2.3 at least,
# py2app installs itself in a way that's inconsistent with
# build_shlib, and build_shlib is more important for us.
##try:
## import py2app # for creating a frozen Mac app.
##except ImportError:
## have_py2app = False
##else:
## have_py2app = True
DIRFILE = "DIR.py" # oof subdirectory manifest files
SWIGCFILEEXT = 'cmodule.C' # suffix for swig output files
SWIGINSTALLDIR = "SWIG"
##############
# readDIRs() walks through the oof2 source directory looking for
# DIR.py files, reads the files, and fills in the CLibInfo objects.
# CLibInfo categorize all of the source files except for the python
# files for pure python modules.
# DIR.py files contain the following python variables. All are optional.
# dirname: The name of the directory. It's actually not used.
# clib: the name of the library to be built from the C and C++ files
# in the directory. The library will be called lib<name>.<suffix>,
# where suffix is system dependent. More than one DIR.py file can use
# the same name.
# cfiles: a list of the names of all of the C and C++ files in the
# directory that need to be compiled to form lib<name>.
# hfiles: a list of the names of the header files that go with the C
# and C++ files.
# swigfiles: a list of the names of the swig input files in the
# directory. Swig-generated C++ code will be compiled but *not*
# included in lib<name>. Each swig input file will create a separate
# python-loadable module which will *link* to lib<name>.
# swigpyfiles: a list of the names of python files that are included
# in swig output files.
# clib_order: an integer specifying the order in which the libraries
# must be built. Later libraries may link to earlier ones. This
# linking is done by setting clib.externalLibs in the set_clib_flags
# function in a DIR.py file.
# set_clib_flags: a function which may be called to set compilation
# and linker flags for building the library. Its argument is a
# CLibInfo object. The includeDirs, externalLibDirs, and externalLibs
# members of the object may be modified by set_clib_flags.
# subdirs: a list of subdirectories that should be processed. Each
# subdirectory must have its own DIR.py file.
# TODO: ensure that typemaps.swg files are included in the source
# distribution!
allCLibs = {}
purepyfiles = []
def getCLibInfo(name):
try:
return allCLibs[name]
except KeyError:
clib = allCLibs[name] = CLibInfo(name)
return clib
class CLibInfo:
def __init__(self, name):
allCLibs[name] = self
self.libname = name
self.dirdata = {'cfiles': [], # *.[Cc] -- c and c++ source code
'hfiles': [], # *.h -- c and c++ header files
'swigfiles': [], # *.swg -- swig source code
'swigpyfiles': [], # *.spy -- python included in swig
}
self.externalLibs = []
self.externalLibDirs = []
self.includeDirs = []
self.extra_link_args = []
self.extra_compile_args = []
self.extensionObjs = None
self.ordering = None
# Parse the file lists in a DIR.py file. The file has been read
# already, and its key,list pairs are in dirdict. Only the data
# relevant to CLibInfo is dealt with here. The rest is handled
# by readDIRs().
def extractData(self, srcdir, dirdict):
for key in self.dirdata.keys():
try:
value = dirdict[key]
del dirdict[key]
except KeyError:
pass
else:
for filename in value:
self.dirdata[key].append(os.path.join(srcdir, filename))
try:
flagFunc = dirdict['set_clib_flags']
del dirdict['set_clib_flags']
except KeyError:
pass
else:
flagFunc(self)
try:
self.ordering = dirdict['clib_order']
del dirdict['clib_order']
except KeyError:
pass
def get_extensions(self):
if self.extensionObjs is None:
self.extensionObjs = []
for swigfile in self.dirdata['swigfiles']:
# The [6:] in the following strips the "./SRC/" from
# the beginning of the file names. splitext(file)[0]
# is the path to a file with the extension stripped.
basename = os.path.splitext(swigfile)[0][6:]
# swig 1.1 version
modulename = os.path.splitext(basename + SWIGCFILEEXT)[0]
sourcename = os.path.join(swigroot, basename+SWIGCFILEEXT)
# swig 1.3 version
## modulename = '_' + basename
## sourcename = os.path.join(swigroot, basename+'_wrap.cxx')
extension = distutils.core.Extension(
name = os.path.join(OOFNAME,"ooflib", SWIGINSTALLDIR,
modulename),
language = 'c++',
sources = [sourcename],
define_macros = platform['macros'],
extra_compile_args = self.extra_compile_args + \
platform['extra_compile_args'],
include_dirs = self.includeDirs + platform['incdirs'],
library_dirs = self.externalLibDirs + platform['libdirs'],
libraries = [self.libname] + self.externalLibs,
# + platform['libs'],
extra_link_args = self.extra_link_args + \
platform['extra_link_args']
)
self.extensionObjs.append(extension)
return self.extensionObjs
def get_shlib(self):
if self.dirdata['cfiles']:
return build_shlib.SharedLibrary(
self.libname,
sources=self.dirdata['cfiles'],
extra_compile_args=platform['extra_compile_args']+self.extra_compile_args,
include_dirs=self.includeDirs + platform['incdirs'],
libraries=self.externalLibs,# + platform['libs'],
library_dirs=self.externalLibDirs +
platform['libdirs'],
extra_link_args=platform['extra_link_args'])
# Find all directories containing at least one swig input file. These
# are used to create the swigged python packages. This is done by
# traversing the DIR.py files, so that random leftover .swg files in
# strange places don't create packages, and so that modules can be
# included conditionally by HAVE_XXX tests in DIR.py files.
def find_swig_pkgs(self):
pkgs = set()
for swigfile in self.dirdata['swigfiles']:
pkgs.add(os.path.split(swigfile)[0])
# pkgs is a set of dirs containing swig files, relative to
# the main OOF2 dir, eg, "./SRC/common".
# Convert it to a list of dirs relative to the swigroot
swigpkgs = []
for pkg in pkgs:
pkgdir = os.path.join(swigroot, pkg[6:]) # eg, SRC/SWIG/common
pkgname = OOFNAME + '.' + pkgdir[4:].replace('/', '.') # oof2.ooflib.SWIG.common
swigpkgs.append(pkgname)
return swigpkgs
# end class CLibInfo
def moduleSort(moduleA, moduleB):
if moduleA.ordering is not None:
if moduleB.ordering is not None:
return cmp(moduleA.ordering, moduleB.ordering)
return -1
else: # moduleA.ordering is None
if moduleB.ordering is not None:
return 1
return cmp(moduleA.name, moduleB.name)
def allFiles(key):
hierlist = [lib.dirdata[key] for lib in allCLibs.values()]
flatlist = []
for sublist in hierlist:
flatlist.extend(sublist)
return flatlist
def readDIRs(srcdir):
dirfile = os.path.join(srcdir, DIRFILE)
if os.path.exists(dirfile):
# print >> sys.stderr, "LOADING", dirfile
# dirfile defines variables whose names are the same as the
# ModuleInfo.dirdata keys. The variables contain lists of
# file names.
localdict = {}
execfile(dirfile, globals(), localdict)
# Now the variables and functions defined in dirfile are in localdict.
try:
dirname = localdict['dirname']
del localdict['dirname']
except KeyError:
pass
try:
clib = localdict['clib']
del localdict['clib']
except KeyError:
pass
else:
clibinfo = getCLibInfo(clib)
clibinfo.extractData(srcdir, localdict)
try:
pyfiles = localdict['pyfiles']
del localdict['pyfiles']
except KeyError:
pass
else:
for filename in pyfiles:
purepyfiles.append(os.path.join(srcdir, filename))
# dirfile also contains a list of subdirectories to process.
try:
subdirs = localdict['subdirs']
del localdict['subdirs']
except KeyError:
pass
else:
# At this point, all args in localdict should have been processed.
if len(localdict) > 0:
print "WARNING: unrecognized values", localdict.keys(), \
"in", dirfile
for subdir in subdirs:
readDIRs(os.path.join(srcdir, subdir))
##########
# Find all python packages and subpackages in a directory by looking
# for __init__.py files.
def find_pkgs():
pkglist = []
os.path.walk('SRC', _find_pkgs, pkglist)
return pkglist
def _find_pkgs(pkglist, dirname, subdirs):
if os.path.exists(os.path.join(dirname, '__init__.py')):
pkglist.append(dirname)
##########
def swig_clibs(dry_run, force, debug, build_temp, with_swig=None):
# First make sure that swig has been built.
if with_swig is None:
## TODO 3.1: swig is installed inside the distutils
## build/temp* directory to avoid conflicts if oof is being
## built for multiple architectures on a shared file system.
## However, swig's .o file and other intermediate files
## (parser.cxx, parser.h, config.log, Makefiles, etc) are
## still in OOFSWIG/SWIG. They'll have to be removed manually
## before building on a different architecture. It would be
## better if they were in build/temp* too, but that might
## require modifying the Makefile.
swigsrcdir = os.path.abspath('OOFSWIG')
swigbuilddir = os.path.join(os.path.abspath(build_temp), 'swig-build')
if not os.path.exists(swigbuilddir):
os.mkdir(swigbuilddir)
swigexec = os.path.join(swigbuilddir, 'bin', 'swig')
if not os.path.exists(swigexec):
print "Building swig"
status = os.system(
'cd %s && ./configure --prefix=%s && make && make install'
% (swigsrcdir, swigbuilddir))
if status:
sys.exit(status)
else:
swigexec = with_swig
srcdir = os.path.abspath('SRC')
extra_args = platform['extra_swig_args']
if debug:
extra_args.append('-DDEBUG')
for clib in allCLibs.values():
for swigfile in clib.dirdata['swigfiles']:
# run_swig requires a src dir and an input file path
# relative to it. The '+1' in the following line strips
# off a '/', so that sfile doesn't look like an absolute
# path.
sfile = os.path.abspath(swigfile)[len(srcdir)+1:]
run_swig(srcdir='SRC', swigfile=sfile, destdir=swigroot,
cext=SWIGCFILEEXT,
include_dirs = ['SRC'],
dry_run=dry_run,
extra_args=extra_args,
force=force,
with_swig=swigexec,
DIM_3=DIM_3
)
##########
# Get a file's modification time. The time is returned as an integer.
# All we care about is that the integers are properly ordered.
def modification_time(phile):
return os.stat(phile)[stat.ST_MTIME]
#########
# Define subclasses of the distutils build_ext and build_shlib class.
# We need subclasses so that oofconfig.h can be created before the
# files are compiled, and so that makedepend can be run.
# oof_build_xxxx contains the routines that are being added to both
# build_ext and build_shlib.
_dependencies_checked = 0
class oof_build_xxxx:
def make_oofconfig(self):
cfgfilename = os.path.normpath(os.path.join(self.build_temp,
'SRC/oofconfig.h'))
includedir = os.path.join('include', OOFNAME)
self.distribution.data_files.append((includedir, [cfgfilename]))
# If oofconfig.h already exists, don't recreate it unless
# forced to. It would require everything that depends on it
# to be recompiled unnecessarily.
if self.force or not os.path.exists(cfgfilename):
print "creating", cfgfilename
if not self.dry_run:
os.system('mkdir -p %s' % os.path.join(self.build_temp, 'SRC'))
cfgfile = open(cfgfilename, "w")
print >> cfgfile, """\
// This file was created automatically by the oof2 setup script.
// Do not edit it.
// Re-run setup.py to change the options.
#ifndef OOFCONFIG_H
#define OOFCONFIG_H
"""
if HAVE_PETSC:
print >> cfgfile, '#define HAVE_PETSC 1'
if HAVE_MPI:
print >> cfgfile, '#define HAVE_MPI 1'
if HAVE_OPENMP: # TODO: is this necessary? _OPENMP is predefined
print >> cfgfile, '#define HAVE_OPENMP'
if DEVEL:
print >> cfgfile, '#define DEVEL ', DEVEL
if NO_GUI:
print >> cfgfile, '#define NO_GUI 1'
if ENABLE_SEGMENTATION:
print >> cfgfile, '#define ENABLE_SEGMENTATION'
if NANOHUB:
print >> cfgfile, '#define NANOHUB'
if DIM_3:
print >> cfgfile, '#define DIM 3'
print >> cfgfile, '#define DIM_3'
else: # for good measure
print >> cfgfile, '#define DIM 2'
if self.check_header('<sstream>'):
print >> cfgfile, '#define HAVE_SSTREAM'
else:
print >> cfgfile, '// #define HAVE_SSTREAM'
# Python pre-2.5 compatibility
print >> cfgfile, """\
#include <Python.h>
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
#define PY_SSIZE_T_MAX INT_MAX
#define PY_SSIZE_T_MIN INT_MIN
#endif /* PY_VERSION_HEX check */
"""
print >> cfgfile, "#endif"
cfgfile.close()
def check_header(self, headername):
# Check that a c++ header file exists on the system.
print "Testing for", headername
tmpfiled, tmpfilename = tempfile.mkstemp(suffix='.C')
tmpfile = os.fdopen(tmpfiled, 'w')
print >> tmpfile, """\
#include %s
int main(int, char**) { return 1; }
""" % headername
tmpfile.flush()
try:
try:
ofiles = self.compiler.compile(
[tmpfilename],
extra_postargs=platform['extra_compile_args'] +
platform['prelink_suppression_arg']
)
except errors.CompileError:
return 0
ofile = ofiles[0]
dir = os.path.split(ofile)[0]
os.remove(ofiles[0])
if dir:
try:
os.removedirs(dir)
except:
pass
return 1
finally:
os.remove(tmpfilename)
def find_dependencies(self):
# distutils doesn't provide a makedepend-like facility, so we
# have to do it ourselves. makedepend is deprecated, so we
# use "gcc -MM" and hope that gcc is available. This routine
# runs "gcc -MM" and constructs a dictionary listing the
# dependencies of each .o file and each swig-generated .C
# file.
# TODO: Check for the existence of gcc and makedepend and use
# the one that's available.
# depdict[file] is a list of sources that the file depends
# upon.
depdict = {}
# Run "gcc -MM" on the C++ files to detect dependencies. "gcc
# -MM" only prints the file name of the target, not its path
# relative to the build directory, so we have to use the -MT
# flag to specify the target. That means that we can't
# process more than one C++ file at a time.
print "Finding dependencies for C++ files."
for phile in allFiles('cfiles'):
## Hack Alert. We don't know the full paths to some of
## the system header files at this point. The -MM flag is
## supposed to tell gcc to ignore the system headers, but
## apparently some versions still want to be able to find
## them, and fail when they don't. So we use -MG, which
## tells gcc to add missing headers to the dependency
## list, and then we weed them out later. At least this
## way, the "missing" headers don't cause errors.
cmd = 'g++ -std=c++11 -MM -MG -MT %(target)s -ISRC -I%(builddir)s -I%(buildsrc)s %(file)s' \
% {'file' : phile,
'target': os.path.splitext(phile)[0] + ".o",
'builddir' : self.build_temp,
'buildsrc' : os.path.join(self.build_temp, 'SRC')
}
proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE,
bufsize=4096)
stdoutdata, stderrdata = proc.communicate()
if stderrdata:
print >> sys.stderr, "Command failed:", cmd
print >> sys.stderr, stderrdata
sys.exit(1)
if not stdoutdata:
print >> sys.stderr, "Command failed, no data received:", cmd
sys.exit(1)
# stdoutdata is a multiline string. The first substring
# is the name of the target file, followed by a colon.
# The remaining substrings are the source files that the
# target depends on, but there can also be line
# continuation characters (backslashes) which must be
# ignored. It's even possible that the *first* line is
# blank except for a backslash.
files = [f for f in stdoutdata.split() if f != "\\"]
target = files[0][:-1] # omit the colon
realtarget = os.path.normpath(os.path.join(self.build_temp, target))
for source in files[1:]:
## See Hack Alert, above. Missing header files will
## all be outside of our directory hierarchy, so we
## just ignore any dependency that doesn't begin with
## "SRC/".
if (source.startswith('SRC/') or
source.startswith(self.build_temp)):
depdict.setdefault(realtarget, []).append(source)
# .C and.py files in the SWIG directory depend on those in the
# SRC directory. Run gcc -MM on the swig source files.
print "Finding dependencies for .swg files."
for phile in allFiles('swigfiles'):
cmd = 'g++ -std=c++11 -MM -MG -MT %(target)s -x c++ -I. -ISRC -I%(builddir)s %(file)s'\
% {'file' : phile,
'target': os.path.splitext(phile)[0] + '.o',
'builddir' : self.build_temp
}
proc = subprocess.Popen(shlex.split(cmd),
stdout=subprocess.PIPE, bufsize=4096)
stdoutdata, stderrdata = proc.communicate()
if stderrdata:
print >> sys.stderr, "Command failed:", cmd
print >> sys.stderr, stderrdata
sys.exit(1)
files = [f for f in stdoutdata.split() if f != "\\"]
# print "---\nstdoutdata=", stdoutdata
# print "files=", files
target = files[0][:-1]
targetbase = os.path.splitext(target)[0]
# On some systems, target begins with "SRC/". On
# others, it begins with "./SRC/". Arrgh. This
# strips off either one.
targetbase = targetbase.split("SRC/", 1)[1]
targetc = os.path.normpath(
os.path.join(swigroot, targetbase + SWIGCFILEEXT))
targetpy = os.path.normpath(
os.path.join(swigroot, targetbase + '.py'))
for source in files[1:]:
if (source.startswith('SRC/') or
source.startswith(self.build_temp)):
depdict.setdefault(targetc, []).append(source)
depdict.setdefault(targetpy,[]).append(source)
## Debugging:
# def dumpdepdict(filename, depdict):
# print >> sys.stderr, "Dumping dependency information to", filename
# f = file(filename, "w")
# keys = depdict.keys()
# keys.sort()
# for target in keys:
# print >> f, target
# sources = depdict[target]
# sources.sort()
# for source in sources:
# print >> f, " ", source
# f.close()
# dumpdepdict("depdict", depdict)
# Add in the implicit dependencies on the .swg files.
for phile in allFiles('swigfiles'):
# file is ./SRC/dir/whatver.swg
base = os.path.splitext(phile)[0][4:] # dir/whatever
cfile = os.path.normpath(os.path.join(swigroot,
base+SWIGCFILEEXT))
pyfile = os.path.normpath(os.path.join(swigroot, base+'.py'))
depdict.setdefault(cfile, []).append(phile)
depdict.setdefault(pyfile, []).append(phile)
# Add in the implicit dependencies on the .spy files.
for underpyfile in allFiles('swigpyfiles'):
base = os.path.splitext(underpyfile)[0] # drop .spy
pyfile = os.path.normpath(os.path.join(swigroot,base[6:]+'.py'))
depdict.setdefault(pyfile, []).append(underpyfile)
return depdict
# Remove out-of-date target files. We have to do this because
# distutils for Python 2.5 and earlier checks the dates of the .C
# and .o files, but doesn't check for any included .h files, so it
# doesn't rebuild enough. For 2.6 and later, it doesn't check
# anything, and it rebuilds far too much. To fix that, we
# monkeypatch _setup_compile from Python 2.5 as well as remove the
# out of date target files.
def clean_targets(self, depdict):
outofdate = False
if not self.dry_run:
for target, sources in depdict.items():
if os.path.exists(target):
targettime = modification_time(target)
sourcetime = max([modification_time(x) for x in sources])
if sourcetime > targettime:
os.remove(target)
print "clean_targets: Removed out-of-date target", target
outofdate = True
else:
outofdate = True
if outofdate:
## TODO: Remove the .so file. I can't figure out how to
## find its name at this point, though.
pass
def clean_dependencies(self):
global _dependencies_checked
if not _dependencies_checked:
# Read dependencies from a file unless MAKEDEPEND has been
# defined by providing the --makedepend command line
# option, or if the file doesn't exist.
depfilename = os.path.join(self.build_temp, 'depend')
if not MAKEDEPEND and os.path.exists(depfilename):
locals = {}
print "Loading dependencies from", depfilename
execfile(depfilename, globals(), locals)
depdict = locals['depdict']
else:
depdict = self.find_dependencies()
print "Saving dependencies in", depfilename
depfile = open(depfilename, "w")
print >> depfile, "depdict=", depdict
depfile.close()
self.clean_targets(depdict)
_dependencies_checked = True
# This does the swigging.
class oof_build_ext(build_ext.build_ext, oof_build_xxxx):
description = "build the python extension modules for OOF2"
user_options = build_ext.build_ext.user_options + [
('with-swig=', None, "specify the swig executable")]
def initialize_options(self):
self.with_swig = None
build_ext.build_ext.initialize_options(self)
def finalize_options(self):
self.set_undefined_options('build',
('with_swig', 'with_swig'))
## TODO: Add extra libraries (python2.x) for cygwin?
build_ext.build_ext.finalize_options(self)
# build_extensions is called by build_ext.run().
def build_extensions(self):
self.compiler.add_include_dir(os.path.join(self.build_temp, 'SRC'))
self.compiler.add_include_dir('SRC')
self.compiler.add_library_dir(self.build_lib)
if self.debug:
self.compiler.define_macro('DEBUG')
# self.compiler.define_macro('Py_DEBUG')
self.compiler.undefine_macro('NDEBUG')
# Make the automatically generated .h files.
self.make_oofconfig()
# Run makedepend
self.clean_dependencies()
# Generate swigged .C and .py files
swig_clibs(self.dry_run, self.force, self.debug, self.build_temp,
self.with_swig)
# Build the swig extensions by calling the distutils base
# class function
build_ext.build_ext.build_extensions(self)
class oof_build_shlib(build_shlib.build_shlib, oof_build_xxxx):
user_options = build_shlib.build_shlib.user_options + [
('with-swig=', None, "non-standard swig executable"),
('blas-libraries=', None, "libraries for blas and lapack"),
('blas-link-args=', None, "link arguments required for blas and lapack")
]
def initialize_options(self):
self.with_swig = None
self.blas_libraries = None
self.blas_link_args = None
build_shlib.build_shlib.initialize_options(self)
def finalize_options(self):
self.set_undefined_options('build',
('with_swig', 'with_swig'),
('blas_libraries', 'blas_libraries'),
('blas_link_args', 'blas_link_args'),
('libraries', 'libraries'),
('library_dirs', 'library_dirs'))
build_shlib.build_shlib.finalize_options(self)
def build_libraries(self, libraries):
self.make_oofconfig()
self.clean_dependencies()
self.compiler.add_include_dir(os.path.join(self.build_temp, 'SRC'))
self.compiler.add_include_dir('SRC')
if self.debug:
self.compiler.define_macro('DEBUG')
# The blas libs and arguments weren't actually put into the
# SharedLibrary objects when they were created, because we
# didn't know until now whether or not the user had provided
# alternates. It's time to either use the predefined values
# from "platform" or to use the command line arguments.
if self.blas_libraries is not None:
blaslibs = string.split(self.blas_libraries)
else:
blaslibs = platform['blas_libs']
if self.blas_link_args is not None:
blasargs = string.split(self.blas_link_args)
else:
blasargs = platform['blas_link_args']
extrablaslibs = self.check_extra_blaslibs(blaslibs, blasargs)
blaslibs.extend(extrablaslibs)
# tcmallocdirs, tcmalloclibs = self.check_tcmalloc()
for library in libraries:
library.libraries.extend(blaslibs)
library.extra_link_args.extend(blasargs)
# library.libraries.extend(tcmalloclibs)
# library.library_dirs.extend(tcmallocdirs)
## TODO: Add extra libraries (python2.x) for cygwin?
build_shlib.build_shlib.build_libraries(self, libraries)
def check_extra_blaslibs(self, blaslibs, linkargs):
# Check to see if blas requires extra libraries to link
# properly. If it does, return a list of extra libraries. If
# it links without extra args, return []. If it doesn't
# link at all, raise an exception. (This test is required
# because different Linux distributions seem to build their
# blas libraries differently, and we can't tell which
# distribution we're using.)
print "Testing if blas links correctly"
# First create a temp directory to play in.
tmpdir = tempfile.mkdtemp(dir=os.getcwd())
tmpdirname = os.path.split(tmpdir)[1]
# Create a file with dummy blas code in it.
tmpfilename = os.path.join(tmpdirname, "blastest.C")
tmpfile = open(tmpfilename, "w")
print >> tmpfile, """\
extern "C" {void dgemv_(char*, int*, int*, double*, double*, int*,
double*, double*, double*, double*, int*);}
int main(int argc, char **argv) {
char c;
int i;
double x;
dgemv_(&c, &i, &i, &x, &x, &i, &x, &x, &x, &x, &i);
return 0;
}
"""
tmpfile.close()
try:
# Compile the dummy code.
try:
ofiles=self.compiler.compile(
[tmpfilename],
extra_postargs=(platform['extra_compile_args'] +
platform['prelink_suppression_arg']),
)
except errors.CompileError:
raise errors.DistutilsExecError("can't compile blas test")
# Try linking without extra args
try:
self.compiler.link(
target_desc=self.compiler.EXECUTABLE,
objects=ofiles,
output_filename=tmpfilename[:-2],
library_dirs=platform['libdirs'],
libraries=blaslibs,
extra_preargs=linkargs,
target_lang='c++')
except errors.LinkError:
pass
else:
return [] # Extra args not needed
# Try linking with -lg2c and -lgfortran
for libname in ('g2c', 'gfortran'):
try:
self.compiler.link(
target_desc=self.compiler.EXECUTABLE,
objects=ofiles,
output_filename=tmpfilename[:-2],
library_dirs=platform['libdirs'],
libraries=blaslibs+[libname],
extra_preargs=linkargs,
target_lang='c++')
except errors.LinkError:
pass
else:
return [libname]
finally:
# Clean out the temp directory
remove_tree(tmpdirname)
raise errors.DistutilsExecError("can't link blas!")
# def check_tcmalloc(self):
# # TODO: We should be modifying the compiler args as well as
# # the link args when using tcmalloc with gcc. From tcmalloc's
# # github page:
# ## NOTE: When compiling with programs with gcc, that you plan
# ## to link with libtcmalloc, it's safest to pass in the flags
# ## -fno-builtin-malloc -fno-builtin-calloc
# ## -fno-builtin-realloc -fno-builtin-free when compiling. gcc
# ## makes some optimizations assuming it is using its own,
# ## built-in malloc; that assumption obviously isn't true with
# ## tcmalloc. In practice, we haven't seen any problems with
# ## this, but the expected risk is highest for users who
# ## register their own malloc hooks with tcmalloc (using
# ## gperftools/malloc_hook.h). The risk is lowest for folks who
# ## use tcmalloc_minimal (or, of course, who pass in the above
# ## flags :-) ).
# if NO_TCMALLOC:
# return ([], [])
# # Check to see if tcmalloc is available.
# print "Looking for tcmalloc..."
# # First, try pkg-config. Not all distros include a .pc file
# # for tcmalloc, so a negative result isn't reliable.
# libdirs, libs = get_third_party_libs('pkg-config --libs libtcmalloc')
# if libs:
# print "Found tcmalloc via pkg-config."
# return libdirs, libs
# # Do it the hard way, by seeing if we can link to it. This
# # will work if it's in a standard location.
# libs = ['tcmalloc']
# libdirs = []
# tmpdir = tempfile.mkdtemp(dir=os.getcwd())
# tmpdirname = os.path.split(tmpdir)[1]
# tmpfilename = os.path.join(tmpdirname, "tcmalloctest.C")
# tmpfile = open(tmpfilename, "w")
# print >> tmpfile, """\
# int main(int, char**) {
# double *x = new double[100];
# for(int i=0; i<100; i++) x[i]=0.0;
# return 0;
# }
# """
# tmpfile.close()
# try:
# try:
# ofiles = self.compiler.compile(
# [tmpfilename],
# extra_postargs=(platform['extra_compile_args'] +
# platform['prelink_suppression_arg']),
# )
# except errors.CompileError:
# raise errors.DistutilsExecError("can't compile tcmalloc test")
# try:
# self.compiler.link(
# target_desc=self.compiler.EXECUTABLE,
# objects=ofiles,
# output_filename=tmpfilename[:-2],
# library_dirs=platform['libdirs'] + libdirs,
# libraries=libs,
# target_lang='c++')
# except errors.LinkError:
# print "Can't find tcmalloc! OOF may run slowly."
# return ([], [])
# else:
# print "Found tcmalloc."
# return (libdirs, libs)
# finally:
# remove_tree(tmpdirname)
class oof_build(build.build):
sep_by = " (separated by '%s')" % os.pathsep
user_options = build.build.user_options + [
('with-swig=', None, "non-standard swig executable"),
('libraries=', None, 'external libraries to link with'),
('library-dirs=', None,
"directories to search for external libraries" + sep_by),
('blas-libraries=', None, "libraries for blas and lapack"),
('blas-link-args=', None, "link args for blas and lapack")]
def initialize_options(self):
self.libraries = None
self.library_dirs = None
self.blas_libraries = None
self.blas_link_args = None
self.with_swig = None
build.build.initialize_options(self)
# override finalize_options in build.py in order to include the
# dimension in the build directory.
def finalize_options(self):
if not DIM_3:
dim = "2d"
else:
dim = "3d"
plat_specifier = ".%s-%s-%s" % (build.get_platform(), sys.version[0:3],dim)
if self.build_purelib is None:
self.build_purelib = os.path.join(self.build_base, 'lib')
if self.build_platlib is None:
self.build_platlib = os.path.join(self.build_base,
'lib' + plat_specifier)
if self.build_lib is None:
if self.distribution.ext_modules:
self.build_lib = self.build_platlib
else:
self.build_lib = self.build_purelib
if self.build_temp is None:
self.build_temp = os.path.join(self.build_base,
'temp' + plat_specifier)
if self.build_scripts is None:
self.build_scripts = os.path.join(
self.build_base, 'scripts-' + sys.version[0:3] + "-" + dim)
try: #only in newer version of distutils
if self.executable is None:
self.executable = os.path.normpath(sys.executable)
except AttributeError:
pass
###################################################
## Modify the build_py command so that it creates oof2config.py. The
## file is created in the build_lib directory so that it gets
## installed at the top level beside the oof2 or oof3d package. We
## also need an init script in the oof2 or oof3d directory.
class oof_build_py(build_py.build_py):
def run(self):
self.make_oof2config()
self.make_toplevel_init()
build_py.build_py.run(self) #this is where swigroot is copied
def make_toplevel_init(self):
initname = os.path.join(self.build_lib,OOFNAME,'__init__.py')
initfile = open(initname,'w')
initfile.close()
def make_oof2config(self):
cfgscriptname = os.path.join(self.build_lib, OOFNAME+'config.py')
cfgscript = open(cfgscriptname, 'w')
install = self.get_finalized_command('install')
build_shlib = self.get_finalized_command('build_shlib')
install_shlib = self.get_finalized_command('install_shlib')
print >> cfgscript, 'root = "%s"' % os.path.abspath('.')
print >> cfgscript, 'version = "%s"' % self.distribution.get_version()
print >> cfgscript, 'prefix = "%s"' % install.prefix
idirs = build_shlib.include_dirs + [