25
25
import time
26
26
27
27
import distutils
28
- from distutils .core import Extension
28
+ from distutils .core import Extension , Distribution
29
+ from distutils .command .build_clib import build_clib
29
30
30
31
import Cython
31
32
from Cython .Build .Inline import _get_build_extension
39
40
40
41
logger = logging .getLogger ('pystan' )
41
42
43
+ def _build_libraries (self , libraries ):
44
+ """Fork distutils.build_clib.build_libraries to enable compiler flags."""
45
+ for (lib_name , build_info ) in libraries :
46
+ sources = build_info .get ('sources' )
47
+ if sources is None or not isinstance (sources , (list , tuple )):
48
+ raise DistutilsSetupError (
49
+ "in 'libraries' option (library '%s'), "
50
+ "'sources' must be present and must be "
51
+ "a list of source filenames" % lib_name )
52
+ sources = list (sources )
53
+
54
+ distutils .command .build_clib .log .info ("building '%s' library" , lib_name )
55
+
56
+ macros = build_info .get ('macros' )
57
+ include_dirs = build_info .get ('include_dirs' )
58
+ extra_postargs = build_info .get ('extra_postargs' )
59
+ objects = self .compiler .compile (sources ,
60
+ output_dir = self .build_temp ,
61
+ macros = macros ,
62
+ include_dirs = include_dirs ,
63
+ extra_postargs = extra_postargs ,
64
+ debug = self .debug )
65
+
66
+ self .compiler .create_static_lib (objects , lib_name ,
67
+ output_dir = self .build_clib ,
68
+ debug = self .debug )
69
+
70
+ # overwrite the default class method
71
+ build_clib .build_libraries = _build_libraries
72
+
73
+ def _get_build_clib ():
74
+ """Convenience function to create build_clib class instance."""
75
+ dist = Distribution ()
76
+ config_files = dist .find_config_files ()
77
+ dist .parse_config_files (config_files )
78
+ build_clibrary = build_clib (dist )
79
+ build_clibrary .finalize_options ()
80
+ return build_clibrary
81
+
82
+ def _build_clib (sources , include_dirs_c , lib_dir , extra_args ):
83
+ """Build C-library."""
84
+ libraries = [("libsundials" , {
85
+ "sources" : sources ,
86
+ "include_dirs" : include_dirs_c ,
87
+ "extra_postargs" : extra_args ,
88
+ })]
89
+
90
+ build_clibrary = _get_build_clib ()
91
+ build_clibrary .libraries = libraries
92
+ build_clibrary .include_dirs = include_dirs_c
93
+ build_clibrary .build_temp = lib_dir
94
+ build_clibrary .build_clib = lib_dir
95
+ build_clibrary .run ()
96
+
97
+ objects = []
98
+ for _ , build_info in libraries :
99
+ obj = build_clibrary .compiler .object_filenames (build_info ["sources" ], output_dir = lib_dir )
100
+ objects .extend (obj )
101
+
102
+ return objects
42
103
43
104
def load_module (module_name , module_path ):
44
105
"""Load the module named `module_name` from `module_path`
@@ -309,58 +370,95 @@ def __init__(self, file=None, charset='utf-8', model_name="anon_model",
309
370
s = template .safe_substitute (model_cppname = self .model_cppname )
310
371
outfile .write (s )
311
372
312
- ## cvodes sources
373
+ if extra_compile_args is None :
374
+ extra_compile_args = []
375
+
376
+ distutils .log .set_verbosity (verbose )
377
+ build_extension = _get_build_extension ()
378
+ ## sundials sources
313
379
314
- # cvodes sources are complied and linked together with the Stan model
380
+ # sundials sources are compiled and linked together with the Stan model
315
381
# extension module. This is not ideal. In theory, build_clib could be
316
382
# used to build a library once and models would be complied and then
317
383
# linked with this library. This would save 7 or more seconds from every build.
318
384
# But such a strategy is frustrated by the
319
385
# lack of ``install_clib`` functionality in Python's distutils.
320
386
#
321
387
# TODO: numpy provides install_clib functionality, use that.
322
- cvodes_src_path = os .path .join (pystan_dir , 'stan' , 'lib' , 'stan_math' , 'lib' , 'cvodes_2.9.0' , 'src' )
323
- cvodes_sources = [
324
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodea.c' ),
325
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodea_io.c' ),
326
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes.c' ),
327
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_band.c' ),
328
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_bandpre.c' ),
329
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_bbdpre.c' ),
330
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_dense.c' ),
331
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_diag.c' ),
332
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_direct.c' ),
333
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_io.c' ),
334
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_sparse.c' ),
335
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_spbcgs.c' ),
336
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_spgmr.c' ),
337
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_spils.c' ),
338
- os .path .join (cvodes_src_path , 'cvodes' , 'cvodes_sptfqmr.c' ),
339
- os .path .join (cvodes_src_path , 'nvec_ser' , 'nvector_serial.c' ),
340
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_band.c' ),
341
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_dense.c' ),
342
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_direct.c' ),
343
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_iterative.c' ),
344
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_math.c' ),
345
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_nvector.c' ),
346
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_spbcgs.c' ),
347
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_spgmr.c' ),
348
- os .path .join (cvodes_src_path , 'sundials' , 'sundials_sptfqmr.c' ),
388
+
389
+ sundials_excluded = {
390
+ "nvector/openmp" ,
391
+ "nvector/openmpdev" ,
392
+ "nvector/parallel" ,
393
+ "nvector/parhyp" ,
394
+ "nvector/petsc" ,
395
+ "nvector/pthreads" ,
396
+ "sundials_mpi" ,
397
+ "sunlinsol/klu" ,
398
+ "sunlinsol/lapack" ,
399
+ "sunlinsol/super" ,
400
+ }
401
+
402
+ sundials_src_path = os .path .join (pystan_dir , 'stan' , 'lib' , 'stan_math' , 'lib' , 'sundials_4.1.0' , 'src' )
403
+ sundials_sources = []
404
+ for sun_root , _ , sunfiles in os .walk (sundials_src_path ):
405
+ for sunfile in sunfiles :
406
+ if os .path .splitext (sunfile )[1 ] == ".c" :
407
+ path = os .path .join (sun_root , sunfile ).replace ("\\ " , "/" )
408
+ if not any (item in path for item in sundials_excluded ):
409
+ sundials_sources .append (path )
410
+
411
+ include_dirs_c = [
412
+ lib_dir ,
413
+ os .path .join (pystan_dir , "stan" , "lib" , "stan_math" , "lib" , "sundials_4.1.0" , "include" ),
349
414
]
350
415
416
+ extra_compile_args_c = []
417
+ if platform .system () == 'Windows' :
418
+ if build_extension .compiler in (None , 'msvc' ):
419
+ logger .warning ("MSVC compiler is not supported" )
420
+ extra_compile_args_c .extend ([
421
+ '/EHsc' ,
422
+ '-DBOOST_DATE_TIME_NO_LIB'
423
+ ])
424
+ else :
425
+ # Windows, but not msvc, likely mingw
426
+ # fix bug in MingW-W64
427
+ # use posix threads
428
+ extra_compile_args_c .extend ([
429
+ '-O2' ,
430
+ '-Wno-unused-function' ,
431
+ '-Wno-uninitialized' ,
432
+ '-std=c11' ,
433
+ "-D_hypot=hypot" ,
434
+ "-pthread" ,
435
+ "-fexceptions" ,
436
+ "-include" ,
437
+ "stan_sundials_printf_override.hpp" ,
438
+ ] + [item for item in extra_compile_args if "std=c++" not in extra_compile_args ])
439
+ else :
440
+ # linux or macOS
441
+ extra_compile_args_c .extend ([
442
+ '-O2' ,
443
+ '-Wno-unused-function' ,
444
+ '-Wno-uninitialized' ,
445
+ '-std=c11' ,
446
+ "-include" ,
447
+ "stan_sundials_printf_override.hpp" ,
448
+ ] + [item for item in extra_compile_args if "std=c++" not in extra_compile_args ])
449
+
450
+ sundials_objects = _build_clib (sundials_sources , include_dirs_c , lib_dir , extra_compile_args_c )
451
+
351
452
stan_macros = [
352
453
('BOOST_RESULT_OF_USE_TR1' , None ),
353
454
('BOOST_NO_DECLTYPE' , None ),
354
455
('BOOST_DISABLE_ASSERTS' , None ),
355
456
]
356
457
357
- build_extension = _get_build_extension ()
358
458
# compile stan models with optimization (-O2)
359
459
# (stanc is compiled without optimization (-O0) currently, see #33)
360
- if extra_compile_args is None :
361
- extra_compile_args = []
362
460
363
- if platform .platform (). startswith ( 'Win' ) :
461
+ if platform .system () == 'Windows' :
364
462
if build_extension .compiler in (None , 'msvc' ):
365
463
logger .warning ("MSVC compiler is not supported" )
366
464
extra_compile_args = [
@@ -381,6 +479,8 @@ def __init__(self, file=None, charset='utf-8', model_name="anon_model",
381
479
"-D_hypot=hypot" ,
382
480
"-pthread" ,
383
481
"-fexceptions" ,
482
+ "-include" ,
483
+ "stan_sundials_printf_override.hpp" ,
384
484
] + extra_compile_args
385
485
else :
386
486
# linux or macOS
@@ -390,15 +490,18 @@ def __init__(self, file=None, charset='utf-8', model_name="anon_model",
390
490
'-Wno-unused-function' ,
391
491
'-Wno-uninitialized' ,
392
492
'-std=c++1y' ,
493
+ '-include' ,
494
+ 'stan_sundials_printf_override.hpp' ,
393
495
] + extra_compile_args
394
496
395
- distutils .log .set_verbosity (verbose )
396
497
extension = Extension (name = self .module_name ,
397
498
language = "c++" ,
398
- sources = [pyx_file ] + cvodes_sources ,
499
+ sources = [pyx_file ],
399
500
define_macros = stan_macros ,
400
501
include_dirs = include_dirs ,
401
- extra_compile_args = extra_compile_args )
502
+ extra_objects = sundials_objects ,
503
+ extra_compile_args = extra_compile_args
504
+ )
402
505
403
506
cython_include_dirs = ['.' , pystan_dir ]
404
507
0 commit comments