-
Notifications
You must be signed in to change notification settings - Fork 7
/
SConstruct
994 lines (824 loc) · 42.1 KB
/
SConstruct
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
# Scons "makefile" for imfit and related programs
#
# To use this, type the following on the command line:
# $ scons <target-name>
# where <target-name> is, e.g., "imft", "makeimage", etc.
# "scons" by itself will build *all* targets
#
# To run unit tests (if you have the full Github-based distribution)
# $ scons unit
#
# To clean up files associated with a given target:
# $ scons -c <target-name>
# To clean up all targets (including programs):
# $ scons -c
# *** SPECIAL STUFF ***
# To build a version with extra, experimental functions
# $ scons --extra-funcs <target-name>
#
# To build a version *without* OpenMP enabled
# $ scons --no-openmp <target-name>
#
#
# To build a version using non-default compiler:
# $ scons --cc=<C_COMPILER> --cpp=<C++_COMPILE> <target-name>
# e.g.
# $ scons --cc=gcc-4.9 --cpp=g++-4.9 <target-name>
# shorthand for using GCC 9
# $ scons --use-gcc <target-name>
#
#
# To build a version with full debugging printouts:
# $ scons define=DEBUG <target-name>
#
# To build export version (all non-system libraries statically linked):
# $ scons --static <target-name>
#
# *** EXPORT CONFIGURATIONS ***
# macOS static binaries (including static linking of libgcc and libstdc++)
# $ scons --allstatic --mac-distribution
# To add one or more directories to the header or library search paths:
# $ scons --header-path=/path/to/header/dir
# OR $ scons --header-path=/path/to/header/dir:/alt/path:/another/path
# $ scons --lib-path=/path/to/lib/dir
# etc.
# Copyright 2010--2023 by Peter Erwin.
#
# This file is part of Imfit.
#
# Imfit is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# Imfit 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 General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Imfit. If not, see <http://www.gnu.org/licenses/>.
import os, subprocess, platform, getpass, pwd, copy
def GetLinuxType():
txt = subprocess.check_output("hostnamectl")
txt = txt.decode('utf-8')
lines = txt.splitlines()
linux_name = None
for line in lines:
pp = line.split(":")
if pp[0].strip() == "Operating System":
linux_name = pp[1].split()[0]
return linux_name
# possible values: "Darwin", "Linux"
os_type = platform.system()
linux_type = None
# possible values: "Ubuntu", "CentOS", ...
if os_type == "Linux":
linux_type = GetLinuxType()
usingAppleSilicon = False
else: # assuming Darwin/macos
proctype = platform.processor()
if proctype == "arm":
usingAppleSilicon = True
else:
usingAppleSilicon = False
def GetXCodeVersion():
"""Attempts to determine XCode version (e.g., 15.0) by parsing output of
"clang --version".
"""
result = subprocess.run("clang --version", capture_output=True, shell=True)
pp = result.stdout.split()
pp_str = [pp[i].decode() for i in range(4)]
if pp_str[0:3] == ["Apple", "clang", "version"]:
version_str = pp_str[3]
ppp = version_str.split(".")
version_num = float(ppp[0] + "." + ppp[1])
return version_num
else:
print("WARNING: Unable to determine clang version!")
return None
# the following is our default for Linux *and* macOS-Intel
include_path_base = [".", "/usr/local/include"]
if usingAppleSilicon:
useVectorExtensions = False
# assuming arm64 binaries and libraries from Homebrew
compiler_path = ['/bin', '/usr/bin', '/opt/homebrew/bin']
lib_path = ["/opt/homebrew/lib"]
MAC_STATIC_LIBS_PATH = "/opt/homebrew/lib/"
LOCAL_STATIC_LIBS_PATH = "/Users/erwin/coding/imfit/static_libs/arm64/"
# kludgey solution for compiling on Apple Silicon (Homebrew Apple Silicon
# distribution of nlopt does *not* include static libraries)
STATIC_NLOPT_LIBRARY_FILE = File("/Users/erwin/coding/imfit/static_libs/arm64/libnlopt.a")
include_path_base = [".", "/opt/homebrew/include/"]
else: # Intel
useVectorExtensions = True
# assuming x86-64 binaries and libraries from Homebrew
compiler_path = ['/bin', '/usr/bin', '/usr/local/bin']
lib_path = ["/usr/local/lib"]
MAC_STATIC_LIBS_PATH = "/usr/local/lib/"
LOCAL_STATIC_LIBS_PATH = "/Users/erwin/coding/imfit/static_libs/x86-64/"
STATIC_NLOPT_LIBRARY_FILE = File("/Users/erwin/coding/imfit/static_libs/x86-64/libnlopt.a")
# LIBRARIES:
# m
# pthread [Linux]
# dl [Linux, if using loguru for g]
# cfitsio (v4.0 and later)
# -- if static, then on macOS we must link with curl AND zlib [part of system]
# -- for Linux, we use our compiled static-library version of cfitsio
# (not Ubuntu's), so we don't need any extra libraries
# fftw3, fftw3_threads
# [gsl, gslcblas]
# [nlopt]
# -- note that static-library version of this (libnlopt.a) should be the
# *non*-C++ version (build with cmake ... -DNLOPT_CXX=OFF ...),
# otherwise there can be problems when compiler for these programs is
# different from compiler used to build libnlopt.a.
# *** Hard-coded paths to static libraries (to ensure static linking when that's what
# we want to do; compilers sometimes insist on linking to shared library even when you
# don't specify that)
# NOTE TO USERS: For normal compilation, don't use static linking, since it's
# harder to set up and ensure the right library paths. The static-library stuff
# here is primarily for use in preparing the binary distributions of Imfit.
# We assume that FFTW library is static-only (since that's the default installation).
# Debian/Ubuntu standard x86-64 package installation path
LINUX_UBUNTU_STATIC_LIBS_PATH = "/usr/local/lib/"
libDirs = {"Darwin": MAC_STATIC_LIBS_PATH, "Linux": LINUX_UBUNTU_STATIC_LIBS_PATH}
extraSharedLibs_static_cfitsio = {"Darwin": ["curl", "z"], "Linux": []}
BASE_SHARED_LIBS = ["m"]
if os_type == "Linux":
BASE_SHARED_LIBS.append("pthread")
# The following definitions are for when we want to force static linking to
# the cfitsio, FFTW3, GSL, and NLopt libraries.
# (Change these if the locations are different on your system)
# cfitsio weirdness: if we link to the static-library version, then we
# must also link to the (shared library) libcurl on Mac;
# for Linux, we link to our pre-compiled static library in /usr/local/lib
# Also, static-library path is different than for other static libraries on Linux
# VM installation.
STATIC_CFITSIO_LIBRARY_FILE = File(libDirs[os_type] + "libcfitsio.a")
BASE_SHARED_LIBS += extraSharedLibs_static_cfitsio[os_type]
STATIC_FFTW_LIBRARY_FILE = File(libDirs[os_type] + "libfftw3.a")
STATIC_FFTW_THREADED_LIBRARY_FILE = File(libDirs[os_type] + "libfftw3_threads.a")
# The following is for when we want to force static linking to the GSL library
# (Change these if the locations are different on your system)
STATIC_GSL_LIBRARY_FILE1 = File(libDirs[os_type] + "libgsl.a")
STATIC_GSL_LIBRARY_FILE2 = File(libDirs[os_type] + "libgslcblas.a")
# the following is for when we want to force static linking to the NLopt library
# (Change these if the locations are different on your system)
if os_type == "Linux":
STATIC_NLOPT_LIBRARY_FILE = File(libDirs[os_type] + "libnlopt.a")
# locations of source-code files (including our header files)
CORE_SUBDIR = "core/"
FUNCTION_SUBDIR = "function_objects/"
FUNCTION_1D_SUBDIR = "function_objects_1d/"
SOLVER_SUBDIR = "solvers/"
CDREAM_SUBDIR = "cdream/"
CDREAM_INCLUDE_SUBDIR = CDREAM_SUBDIR + "include/"
PROFILEFIT_SUBDIR = "profile_fitting/"
# *** Set up compiler flags, library lists, include paths
# Note on sse2, etc.:
# SSE2 is supported on Intel processors from xxx onward
# AVX is supported on Intel "Core i3/i5/i7" processors from 2011 onward
# AVX2 is supported on Intel Haswell and later processors (mostly 2014 onward)
# AVX-512 is supported only on "Knights Landing" Xeon Phi processors (2016 onward)
cflags_opt = ["-O3", "-g0", "-fPIC", "-Wall"]
if os_type == "Darwin":
cflags_opt.append("-mmacosx-version-min=10.13")
if useVectorExtensions:
cflags_opt.append("-msse2")
cflags_db = ["-Wall", "-g3", "-O0", "-fPIC", "-Wshadow", "-Wredundant-decls", "-Wpointer-arith"]
# the following will probably be appended to multiple times
link_flags = []
base_defines = ["ANSI", "USING_SCONS"]
# libraries needed for imfit, makeimage, psfconvolve, & other 2D programs
lib_list = BASE_SHARED_LIBS
# libraries needed for profilefit and psfconvolve1d compilation
lib_list_1d = ["fftw3", "fftw3_threads", "m"]
include_path = include_path_base + [CORE_SUBDIR, SOLVER_SUBDIR, CDREAM_SUBDIR,
CDREAM_INCLUDE_SUBDIR, FUNCTION_SUBDIR, FUNCTION_1D_SUBDIR, PROFILEFIT_SUBDIR]
# the following flag ("-ld_classic") is a workaround on macos to use the old linker
# instead of the new (in XCode 15) linker, which fails for us (and other people)
# FIXME: If an updated version of XCode fixes the problem, then we should modify this
# so it just checks for the specific bad version(s).
if os_type == "Darwin" and GetXCodeVersion() >= 15:
link_flags.append("-ld_classic")
# find out what the default compilers (according to SCons) are
default_environ = DefaultEnvironment(ENV = {'PATH' : os.environ['PATH']})
cc_default = default_environ["CC"]
cpp_default = default_environ["CXX"]
CC_COMPILER = cc_default
CPP_COMPILER = cpp_default
c_compiler_changed = False
cpp_compiler_changed = False
usingGCC = True
# ** Special setup for compilation by P.E. on Mac (assumes GCC v14 is installed and
# callable via gcc-14 and g++-14)
# Comment this out otherwise!
# Note that the following way of determining the username seems to be a bit more
# portable than "getpass.getuser()", which fails for "Ubuntu on Windows" (acc. to
# Lee Kelvin, who contributed the new version)
userName = pwd.getpwuid(os.getuid())[0]
if (os_type == "Darwin") and (userName == "erwin"):
CC_COMPILER = "gcc-14"
CPP_COMPILER = "g++-14"
c_compiler_changed = True
cpp_compiler_changed = True
usingGCC = True
extra_defines = []
# *** System-specific setup
# if (os_type == "Darwin"): # OK, we're compiling on macOS (a.k.a. Mac OS X)
# # Note: if for some reason you need to compile to 32-bit -- e.g., because
# # your machine is 32-bit only, or because the fftw3 and cfitsio libraries
# # are 32-bit, use the following
# cflags_db = ["-Wall", "-Wshadow", "-Wredundant-decls", "-Wpointer-arith", "-g3"]
if (os_type == "Linux"):
extra_defines.append("LINUX")
# change the following path definitions as needed
include_path.append("/usr/include")
if userName == "erwin":
include_path.append("/home/erwin/include")
lib_path.append("/home/erwin/lib")
else:
extra_defines.append("MACOS")
defines_opt = base_defines
defines_db = base_defines
# *** Set up default settings, check for user-requested changes
# Default settings for compilation
#useGSL = True
useNLopt = True
useOpenMP = True
usingClangOpenMP = False
useExtraFuncs = False
useStaticLibs = False
totalStaticLinking = False
useModernCpp = False
buildFatBinary = False
build32bit = False
buildForOldMacOS = False
scanBuild = False
addressSanitize = False
allSanitize = False
setOptToDebug = False
useLogging = False
# Define some user options
AddOption("--fftw-path", dest="fftwLibraryPath", type="string", action="store", default=None,
help="path to directory containing FFTW libraries")
AddOption("--fftw-openmp", dest="fftwOpenMP", action="store_true",
default=False, help="compile with OpenMP-threaded FFTW library")
AddOption("--lib-path", dest="libraryPath", type="string", action="store", default=None,
help="colon-separated list of additional paths to search for libraries")
AddOption("--header-path", dest="headerPath", type="string", action="store", default=None,
help="colon-separated list of additional paths to search for header files")
AddOption("--no-nlopt", dest="useNLopt", action="store_false",
default=True, help="do *not* use NLopt library")
AddOption("--no-openmp", dest="noOpenMP", action="store_true",
default=False, help="compile *without* OpenMP support")
AddOption("--clang-openmp", dest="useClangOpenMP", action="store_true",
default=False, help="compile with clang++ on macOS")
AddOption("--extra-funcs", dest="useExtraFuncs", action="store_true",
default=False, help="compile additional FunctionObject classes for testing")
AddOption("--modern-cpp", dest="useModernCpp", action="store_true",
default=False, help="tell compiler to assume C++-17")
AddOption("--extra-checks", dest="doExtraChecks", action="store_true",
default=False, help="turn on additional error-checking and warning flags during compilation")
# options to specify use of non-default compilers, extra checks, logging
AddOption("--cc", dest="cc_compiler", type="string", action="store", default=None,
help="C compiler to use instead of system default")
AddOption("--cpp", dest="cpp_compiler", type="string", action="store", default=None,
help="C++ compiler to use instead of system default")
AddOption("--use-gcc", dest="useGCC", action="store_true",
default=False, help="use gcc and g++ v9 compilers")
AddOption("--scan-build", dest="doingScanBuild", action="store_true",
default=False, help="set this when using scan-build (only for imfit_db and makeimage_db)")
AddOption("--sanitize", dest="useAllSanitize", action="store_true",
default=False, help="set this to generate binaries with -fsanitize-address, -fsanitize-undefined, and -fsanitize=leak")
AddOption("--address-sanitize", dest="useAddressSanitize", action="store_true",
default=False, help="set this to generate binaries with -fsanitize-address")
AddOption("--logging", dest="useLogging", action="store_true",
default=False, help="compile with support for logging via loguru")
# Define some more arcane options (e.g., for making binaries for distribution)
AddOption("--static", dest="useStaticLibs", action="store_true",
default=False, help="force static library linking")
AddOption("--allstatic", dest="useTotalStaticLinking", action="store_true",
default=False, help="force static library linking, *including* system libraries if possible")
AddOption("--mac-distribution", dest="compileForMacDistribution", action="store_true",
default=False, help="use this to make macOS binaries for public distribution")
# * Check to see if user actually specified something, and implement it
# special stuff for compiling Mac binary distribution
if GetOption("compileForMacDistribution"):
print("DOING MAC DISTRIBUTION COMPILE!")
usingClangOpenMP = True
usingGCC = False
CC_COMPILER = "clang"
CPP_COMPILER = "clang++"
useStaticLibs = True
# totalStaticLinking = True
# reset lib_path so linker only looks where we want it to -- i.e., in the
# directory with static library files
lib_path = ["/Users/erwin/coding/imfit/static_libs"]
if GetOption("headerPath") is not None:
extraPaths = GetOption("headerPath").split(":")
print("extra header search paths: ", extraPaths)
include_path += extraPaths
if GetOption("libraryPath") is not None:
extraPaths = GetOption("libraryPath").split(":")
print("extra library search paths: ", extraPaths)
lib_path += extraPaths
# if GetOption("useGSL") is False:
# useGSL = False
if GetOption("useNLopt") is False:
useNLopt = False
if GetOption("noOpenMP"):
useOpenMP = False
if GetOption("useClangOpenMP"):
usingClangOpenMP = True
usingGCC = False
CC_COMPILER = "clang"
CPP_COMPILER = "clang++"
if GetOption("useExtraFuncs"):
useExtraFuncs = True
doExtraChecks = False
if GetOption("useModernCpp"):
useModernCpp = True
if GetOption("doExtraChecks"):
doExtraChecks = True
# change the compilers if user requests it
if GetOption("cc_compiler") is not None:
CC_COMPILER = GetOption("cc_compiler")
print("using %s for C compiler" % CC_COMPILER)
c_compiler_changed = True
if GetOption("cpp_compiler") is not None:
CPP_COMPILER = GetOption("cpp_compiler")
print("using %s for C++ compiler" % CPP_COMPILER)
cpp_compiler_changed = True
if GetOption("useGCC"):
if usingClangOpenMP:
print("ERROR: You cannot specify both Clang and GCC as the compiler!")
Exit(2)
usingGCC = True
CC_COMPILER = "gcc-14"
CPP_COMPILER = "g++-14"
print("using %s for C compiler" % CC_COMPILER)
print("using %s for C++ compiler" % CPP_COMPILER)
c_compiler_changed = True
cpp_compiler_changed = True
if GetOption("doingScanBuild"):
scanBuild = True
useOpenMP = False # scan-build uses clang, which doesn't have OpenMP
if GetOption("useAddressSanitize"):
addressSanitize = True
useOpenMP = False
setOptToDebug = True
if GetOption("useAllSanitize"):
allSanitize = True
useOpenMP = False
setOptToDebug = True
if GetOption("useLogging"):
useLogging = True
if GetOption("useStaticLibs"):
useStaticLibs = True
if GetOption("useTotalStaticLinking"):
useStaticLibs = True
if usingGCC:
totalStaticLinking = True
# TESTING FFTW
if GetOption("fftwLibraryPath"):
fftwPath = GetOption("fftwLibraryPath") + "/lib/"
STATIC_FFTW_LIBRARY_FILE = File(fftwPath + "libfftw3.a")
if GetOption("fftwOpenMP"):
STATIC_FFTW_THREADED_LIBRARY_FILE = File(fftwPath + "libfftw3_omp.a")
else:
STATIC_FFTW_THREADED_LIBRARY_FILE = File(fftwPath + "libfftw3_threads.a")
# *** Setup for various options (either default, or user-altered)
if setOptToDebug:
print("** Turning off optimizations!")
cflags_opt = cflags_db
if useStaticLibs:
lib_list.append(STATIC_CFITSIO_LIBRARY_FILE)
lib_list.append(STATIC_FFTW_LIBRARY_FILE)
lib_list.append(STATIC_FFTW_THREADED_LIBRARY_FILE)
else:
lib_list += ["cfitsio", "fftw3", "fftw3_threads"]
extra_defines.append("FFTW_THREADING")
#if useGSL: # true by default
# GNU Scientific Library
if useStaticLibs:
lib_list.append(STATIC_GSL_LIBRARY_FILE1)
lib_list.append(STATIC_GSL_LIBRARY_FILE2)
lib_list_1d.append(STATIC_GSL_LIBRARY_FILE1)
lib_list_1d.append(STATIC_GSL_LIBRARY_FILE2)
else:
lib_list.append("gsl")
lib_list.append("gslcblas")
lib_list_1d.append("gsl")
lib_list_1d.append("gslcblas")
if useNLopt: # default is to do this
if useStaticLibs:
lib_list.append(STATIC_NLOPT_LIBRARY_FILE)
lib_list_1d.append(STATIC_NLOPT_LIBRARY_FILE)
else:
if (os_type == "Linux") and (linux_type == "CentOS"):
# CentOS package versions of NLopt tend to call the library file
# "libnlopt_cxx" instead of just "libnlopt"
lib_list.append("nlopt_cxx")
lib_list_1d.append("nlopt_cxx")
else:
lib_list.append("nlopt")
lib_list_1d.append("nlopt")
else:
extra_defines.append("NO_NLOPT")
# Special case where we try to link libstdc++, libgomp, and libgcc_s statically
# (probably *won't* work with clang/clang++, only useful with GCC)
# Note that "-static-libstdc++" doesn't seem to accomplish anything; only
# "-static-libgcc" does (and that statically links in libstdc++ and libgomp
# in addition to libgcc_s). But we'll leave it in because it seems to be
# the standard.
if totalStaticLinking:
link_flags.append("-static-libgcc")
link_flags.append("-static-libstdc++")
if useOpenMP: # default is to do this (turn this off with "--no-openmp")
if usingClangOpenMP:
# special flags for Apple clang++ (assumes libomp is installed)
cflags_opt.append("-Xpreprocessor")
cflags_opt.append("-fopenmp")
cflags_db.append("-Xpreprocessor")
cflags_db.append("-fopenmp")
link_flags.append("-Xpreprocessor")
link_flags.append("-fopenmp")
if useStaticLibs:
link_flags.append(MAC_STATIC_LIBS_PATH + "/libomp.a")
else:
# dynamic linking to libomp
link_flags.append("-lomp")
else:
# flags for (real) g++
cflags_opt.append("-fopenmp")
cflags_db.append("-fopenmp")
link_flags.append("-fopenmp")
extra_defines.append("USE_OPENMP")
if useExtraFuncs: # default is to NOT do this; user must specify with "--extra-funcs"
extra_defines.append("USE_EXTRA_FUNCS")
if doExtraChecks: # default is to NOT do this; user must specify with "--extra-checks"
cflags_opt.append(["-Wall", "-Wshadow", "-Wredundant-decls", "-Wpointer-arith",
"-Wextra", "-pedantic"])
# which dialect/version of C++ are we using?
if useModernCpp: # command-line option "--modern-cpp"
cflags_opt.append("-std=c++17")
cflags_db.append("-std=c++17")
else:
cflags_opt.append("-std=c++11")
cflags_db.append("-std=c++11")
if useLogging:
extra_defines.append(["-DUSE_LOGGING"])
if os_type == "Linux":
lib_list.append("dl")
# special definitions for compiling libimfit.a
# (currently useful for turning off signal-handling that catches Ctrl-C, since
# including that code that causes problems for pyimfit)
if 'libimfit.a' in COMMAND_LINE_TARGETS:
extra_defines.append("NO_SIGNALS")
# Add any additional, user-specified preprocessor definitions (e.g., "define=DEBUG")
for key, value in ARGLIST:
if key == 'define':
extra_defines.append(value)
if addressSanitize:
cflags_opt.append("-fsanitize=address")
cflags_opt.append("-fno-omit-frame-pointer")
cflags_db.append("-fsanitize=address")
cflags_db.append("-fno-omit-frame-pointer")
link_flags.append("-fsanitize=address")
link_flags.append("-fno-omit-frame-pointer")
if allSanitize:
cflags_opt += ["-fsanitize=address", "-fsanitize=undefined", "-fsanitize=leak"]
cflags_opt.append("-fno-omit-frame-pointer")
cflags_db += ["-fsanitize=address", "-fsanitize=undefined", "-fsanitize=leak"]
cflags_db.append("-fno-omit-frame-pointer")
link_flags += ["-fsanitize=address", "-fsanitize=undefined", "-fsanitize=leak"]
link_flags.append("-fno-omit-frame-pointer")
# * Collect together all the updated preprocessor definitions
defines_db = defines_db + extra_defines
defines_opt = defines_opt + extra_defines
# *** Create Environments for compilation:
# "env_debug" is environment with debugging options turned on
# "env" is an environment for optimized compiling (our default)
env = Environment( CC=CC_COMPILER, CXX=CPP_COMPILER, CPPPATH=include_path, LIBS=lib_list,
LIBPATH=lib_path, CCFLAGS=cflags_opt, LINKFLAGS=link_flags,
CPPDEFINES=defines_opt, ENV = {'PATH' : os.environ['PATH']} )
env_debug = Environment( CC=CC_COMPILER, CXX=CPP_COMPILER, CPPPATH=include_path, LIBS=lib_list,
LIBPATH=lib_path, CCFLAGS=cflags_db, LINKFLAGS=link_flags,
CPPDEFINES=defines_db, ENV = {'PATH' : os.environ['PATH']} )
# env_multi = Environment( CC=CC_COMPILER, CXX=CPP_COMPILER, CPPPATH=include_path, LIBS=lib_list,
# LIBPATH=lib_path, CCFLAGS=cflags_db, LINKFLAGS=link_flags,
# CPPDEFINES=defines_opt )
lib_list_nofits = copy.copy(lib_list)
if "nlopt" in lib_list_nofits:
lib_list_nofits.remove("nlopt")
env_debug_nofits = Environment( CC=CC_COMPILER, CXX=CPP_COMPILER, CPPPATH=include_path,
LIBS=lib_list_nofits,
LIBPATH=lib_path, CCFLAGS=cflags_db, LINKFLAGS=link_flags,
CPPDEFINES=defines_db, ENV = {'PATH' : os.environ['PATH']} )
# Checks for libraries and headers -- if we're not doing scons -c:
# WARNING: This is NOT a good idea for us at the moment, because
# 1. It fails to work on our Linux VM installation
# 2. It automatically inserts "-l<libname>" if it finds the libraries, which ends
# up forcing the linking of dynamic-library versions even if we're trying to
# do static compilation
# if not env.GetOption('clean'):
# conf_opt = Configure(env)
# cfitsioFound = conf_opt.CheckLibWithHeader('cfitsio', 'fitsio.h', 'c')
# fftwFound = conf_opt.CheckLibWithHeader('fftw3', 'fftw3.h', 'c')
# fftwThreadsFound = conf_opt.CheckLib('fftw3_threads')
# nloptFound = conf_opt.CheckLibWithHeader('nlopt', 'nlopt.h', 'c')
# gslFound = conf_opt.CheckLib('gsl')
# libsOK = False
# if cfitsioFound and fftwFound:
# libsOK = True
# else:
# print("ERROR: Failed to find one or more required libraries and/or header files (cfitsio and/or fftw3)!")
# print("\tMake sure they are installed; if necessary, include correct path to library with --lib-path option")
# print("\tand correct path to header with --header-path option")
# exit(1)
# if useFFTWThreading and not fftwThreadsFound:
# print("ERROR: Failed to find fftw3_threading library!")
# print("\tSuggestion: include correct path to library with --lib-path option")
# print("\tOR run SCons with --no-threading option")
# exit(1)
# if useGSL and not gslFound:
# print("ERROR: Failed to find gsl library!")
# print("\tSuggestion: include correct path to library with --lib-path option")
# print("\tOR run SCons with --no-gsl option")
# exit(1)
# if useNLopt and not nloptFound:
# print("ERROR: Failed to find nlopt library!")
# print("\tSuggestion: include correct path to library with --lib-path option")
# print("\tOR run SCons with --no-nlopt option")
# exit(1)
# env = conf_opt.Finish()
# We have separate lists of object names (what we want the .o files to be called) and
# source names (.cpp) so that we can specify separate debugging and optimized compilations.
# ModelObject and related classes/files:
modelobject_obj_string = """model_object convolver oversampled_region downsample
psf_oversampling_info setup_model_object"""
modelobject_objs = [ CORE_SUBDIR + name for name in modelobject_obj_string.split() ]
modelobject_sources = [name + ".cpp" for name in modelobject_objs]
# Function objects:
functionobject_obj_string = """function_object func_gaussian func_exp func_gen-exp
func_sersic func_gen-sersic func_core-sersic func_broken-exp
func_broken-exp2d func_moffat func_flatsky func_tilted-sky-plane
func_flatbar func_gaussian-ring func_gaussian-ring2side func_gaussian-ring-az
func_edge-on-ring func_edge-on-ring2side
func_king func_king2 func_ferrersbar2d func_peanut_dattathri
helper_funcs helper_funcs_3d psf_interpolators"""
#if useGSL:
# NOTE: the following modules require GSL be present
functionobject_obj_string += " func_edge-on-disk"
functionobject_obj_string += " integrator"
functionobject_obj_string += " func_expdisk3d" # requires integrator
functionobject_obj_string += " func_brokenexpdisk3d" # requires integrator
functionobject_obj_string += " func_gaussianring3d" # requires integrator
functionobject_obj_string += " func_ferrersbar3d" # requires integrator
functionobject_obj_string += " func_pointsource"
functionobject_obj_string += " func_pointsource-rot"
if useExtraFuncs:
# experimental extra functions for personal testing
functionobject_obj_string += " func_broken-exp-bar"
functionobject_obj_string += " func_double-broken-exp"
functionobject_obj_string += " func_gen-exp2"
functionobject_obj_string += " func_flatbar_trunc"
functionobject_obj_string += " func_gauss_extraparams"
functionobject_obj_string += " func_gen-flatbar"
functionobject_obj_string += " func_bp-cross-section"
functionobject_obj_string += " func_lorentzian-ring"
functionobject_obj_string += " func_n4608disk"
functionobject_obj_string += " func_brokenexpbar3d"
functionobject_obj_string += " func_boxytest3d"
functionobject_obj_string += " func_boxytest3d2"
functionobject_obj_string += " func_flatbar3d"
functionobject_obj_string += " func_double-brokenexpdisk3d"
functionobject_obj_string += " func_expdisk3d_trunc"
functionobject_obj_string += " func_logspiral_exp"
functionobject_obj_string += " func_logspiral_brokenexp"
functionobject_obj_string += " func_logspiral"
functionobject_obj_string += " func_logspiral2"
functionobject_obj_string += " func_logspiral3"
functionobject_obj_string += " func_logspiral_gauss"
functionobject_obj_string += " func_logspiral_arc"
# func_edge-on-disk_n4762
# func_edge-on-disk_n4762v2
functionobject_obj_string += " func_polynomial_d1"
functionobject_obj_string += " func_triaxbar3d"
functionobject_obj_string += " func_triaxbar3d_sq"
functionobject_obj_string += " func_triaxbar3d_gengauss_sq"
functionobject_obj_string += " func_exp-higher-mom"
functionobject_obj_string += " func_nan"
functionobject_obj_string += " func_simple-checkerboard"
functionobject_obj_string += " func_nuker"
functionobject_obj_string += " func_sersic_var-ell"
functionobject_obj_string += " func_bpbar3d"
functionobject_obj_string += " func_double-gaussian"
# ADD CODE FOR NEW FUNCTIONS HERE
# (NOTE: be sure to include one or more spaces before the file name!)
# e.g.,
# functionobject_obj_string += " func_mynewfunction"
functionobject_objs = [ FUNCTION_SUBDIR + name for name in functionobject_obj_string.split() ]
functionobject_sources = [name + ".cpp" for name in functionobject_objs]
# Solvers and associated code
solver_obj_string = """levmar_fit mpfit diff_evoln_fit DESolver dispatch_solver solver_results"""
if useNLopt:
solver_obj_string += " nmsimplex_fit nlopt_fit"
solver_objs = [ SOLVER_SUBDIR + name for name in solver_obj_string.split() ]
solver_sources = [name + ".cpp" for name in solver_objs]
# CDREAM and associated code for MCMC
cdream_obj_string = """check_outliers dream dream_initialize dream_pars gelman_rubin gen_CR
restore_state"""
cdream_objs = [ CDREAM_SUBDIR + name for name in cdream_obj_string.split() ]
cdream_sources = [name + ".cpp" for name in cdream_objs]
# Base files for imfit, makeimage, imfit-mcmc, and libimfit:
base_obj_string = """mp_enorm statistics mersenne_twister commandline_parser utilities
config_file_parser add_functions count_cpu_cores"""
base_objs = [ CORE_SUBDIR + name for name in base_obj_string.split() ]
# FITS image-file I/O
image_io_obj_string = "image_io getimages"
image_io_objs = [ CORE_SUBDIR + name for name in image_io_obj_string.split() ]
# Main set of files for imfit
imfit_obj_string = """print_results bootstrap_errors estimate_memory
imfit_main"""
imfit_base_objs = [ CORE_SUBDIR + name for name in imfit_obj_string.split() ]
if useLogging:
imfit_base_objs.append("loguru/loguru")
imfit_base_objs = base_objs + image_io_objs + imfit_base_objs
imfit_base_sources = [name + ".cpp" for name in imfit_base_objs]
# Main set of files for makeimage
makeimage_base_objs = base_objs + image_io_objs + [CORE_SUBDIR + "makeimage_main"]
if useLogging:
makeimage_base_objs.append("loguru/loguru")
makeimage_base_sources = [name + ".cpp" for name in makeimage_base_objs]
# Main set of files for imfit-mcmc
mcmc_obj_string = """estimate_memory mcmc_main"""
mcmc_base_objs = [ CORE_SUBDIR + name for name in mcmc_obj_string.split() ]
if useLogging:
mcmc_base_objs.append("loguru/loguru")
mcmc_base_objs = mcmc_base_objs + base_objs + image_io_objs + cdream_objs
mcmc_base_sources = [name + ".cpp" for name in mcmc_base_objs]
# Main set of files for multimfit
multimfit_obj_string = """print_results print_results_multi bootstrap_errors
estimate_memory multimfit_main model_object_multimage read_simple_params
paramvector_processing param_holder imageparams_file_parser store_psf_oversampling
utilities_multimfit"""
multimfit_base_objs = [ CORE_SUBDIR + name for name in multimfit_obj_string.split() ]
multimfit_base_objs = base_objs + image_io_objs + multimfit_base_objs
multimfit_base_sources = [name + ".cpp" for name in multimfit_base_objs]
# Main set of files for makemultimages
makemultimages_obj_string = """makemultimages_main model_object_multimage
read_simple_params paramvector_processing param_holder imageparams_file_parser
store_psf_oversampling"""
makemultimages_base_objs = [ CORE_SUBDIR + name for name in makemultimages_obj_string.split() ]
if useLogging:
makemultimages_base_objs.append("loguru/loguru")
makemultimages_base_objs = base_objs + image_io_objs + makemultimages_base_objs
makemultimages_base_sources = [name + ".cpp" for name in makemultimages_base_objs]
# imfit: put all the object and source-code lists together
imfit_objs = imfit_base_objs + modelobject_objs + functionobject_objs + solver_objs
imfit_sources = imfit_base_sources + modelobject_sources + functionobject_sources + solver_sources
# makeimage: put all the object and source-code lists together
makeimage_objs = makeimage_base_objs + modelobject_objs + functionobject_objs
makeimage_sources = makeimage_base_sources + modelobject_sources + functionobject_sources
# imfit-mcmc: put all the object and source-code lists together
mcmc_objs = mcmc_base_objs + modelobject_objs + functionobject_objs
mcmc_sources = mcmc_base_sources + modelobject_sources + functionobject_sources
# makemultimages: put all the object and source-code lists together
# makemultimages_objs = makemultimages_base_objs + modelobject_objs + functionobject_objs
# makemultimages_sources = makemultimages_base_sources + modelobject_sources + functionobject_sources
makemultimages_objs = makemultimages_base_objs + modelobject_objs + functionobject_objs
makemultimages_sources = makemultimages_base_sources + modelobject_sources + functionobject_sources
# multimfit: put all the object and source-code lists together
multimfit_objs = multimfit_base_objs + modelobject_objs + functionobject_objs + solver_objs
multimfit_sources = multimfit_base_sources + modelobject_sources + functionobject_sources + solver_sources
# import environment variables if we're doing scan-build static analysis
if scanBuild:
env_debug["CC"] = os.getenv("CC")
env_debug["CXX"] = os.getenv("CXX")
env_debug["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_"))
# *** Finally, define the actual targets for building
# specify ".do" as the suffix for "full-debug" object code
imfit_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(imfit_objs, imfit_sources) ]
env_debug.Program("imfit_db", imfit_dbg_objlist)
imfit_opt_objlist = [ env.Object(obj, src) for (obj,src) in zip(imfit_objs, imfit_sources) ]
env.Program("imfit", imfit_opt_objlist)
makeimage_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(makeimage_objs, makeimage_sources) ]
env_debug.Program("makeimage_db", makeimage_dbg_objlist)
env.Program("makeimage", makeimage_sources)
mcmc_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(mcmc_objs, mcmc_sources) ]
env_debug.Program("imfit-mcmc_db", mcmc_dbg_objlist)
env.Program("imfit-mcmc", mcmc_sources)
multi_objlist = [ env.Object(obj, src) for (obj,src) in zip(makemultimages_objs, makemultimages_sources) ]
env.Program("makemultimages", multi_objlist)
multimfit_objlist = [ env.Object(obj, src) for (obj,src) in zip(multimfit_objs, multimfit_sources) ]
env.Program("multimfit", multimfit_objlist)
# Run tests
# Unit tests:
env.Command("unit", None, "./run_unit_tests.sh")
# All tests:
env.Command("alltests", None,
"./run_unit_tests.sh ; ./do_makeimage_tests ; ./do_imfit_tests ; ./do_mcmc_tests")
# Build Imfit library
base_for_lib_objstring = """mp_enorm statistics mersenne_twister utilities
config_file_parser add_functions bootstrap_errors count_cpu_cores"""
base_for_lib_objs = [ CORE_SUBDIR + name for name in base_for_lib_objstring.split() ]
libimfit_objs = modelobject_objs + functionobject_objs + solver_objs
libimfit_objs += base_for_lib_objs
libimfit_sourcelist = [name + ".cpp" for name in libimfit_objs]
#print(imfit_lib_sourcelist)
# Note that for some reason we have to give the library name with its
# "lib" prefix: "libimfit"
# invoke the following as "scons libimfit.a" for the static-library version
# and "scons libimfit.dylib" (macOS) or "scons libimfit.so" (Linux) for the
# dynamic-library version
libimfit_objlist = [ env.Object(obj, src) for (obj,src) in zip(libimfit_objs, libimfit_sourcelist) ]
staticlib = env.StaticLibrary(target="libimfit", source=libimfit_objlist)
# THE FOLLOWING CURRENTLY DOES NOT WORK
# ("Source file: core/model_object.o is static and is not compatible with shared target: libimfit.dylib")
# sharedlib = env.SharedLibrary(target="libimfit", source=libimfit_objlist,
# LIBS=lib_list_libimfit)
# *** Other programs (profilefit, psfconvolve, older stuff)
# From here to the end of the file: removed from exported distribution version of SConstruct
env_1d = Environment( CC=CC_COMPILER, CXX=CPP_COMPILER, CPPPATH=include_path, LIBS=lib_list_1d, LIBPATH=lib_path,
CCFLAGS=cflags_db, LINKFLAGS=link_flags, CPPDEFINES=defines_db,
ENV = {'PATH' : os.environ['PATH']} )
# ModelObject1d and related classes:
# (Note that model_object includes references to oversampled_region and downsample,
# so we need to include those in the compilation and link, even though they aren't
# actually used in model_object1d. Similarly, code in image_io is referenced from
# downsample.)
modelobject1d_obj_string = """model_object oversampled_region downsample psf_oversampling_info"""
modelobject1d_objs = [CORE_SUBDIR + name for name in modelobject1d_obj_string.split()]
modelobject1d_sources = [name + ".cpp" for name in modelobject1d_objs]
# 1D FunctionObject classes (note that we have to add a separate entry for function_object.cpp,
# which is in a different subdirectory):
functionobject1d_obj_string = """func1d_gaussian func1d_gaussian_linear func1d_exp func1d_sersic
func1d_core-sersic func1d_broken-exp func1d_moffat func1d_delta func1d_sech
func1d_sech2 func1d_vdksech func1d_gaussian2side func1d_nuker func1d_spline
func1d_n1543majmin_circbulge func1d_n1543majmin func1d_n1543majmin2
func1d_double-gauss-hermite func1d_gauss-hermite"""
functionobject1d_objs = [ FUNCTION_1D_SUBDIR + name for name in functionobject1d_obj_string.split() ]
functionobject1d_objs.append(FUNCTION_SUBDIR + "function_object")
functionobject1d_sources = [name + ".cpp" for name in functionobject1d_objs]
# Base files for profilefit:
profilefit_base_obj_string = """core/commandline_parser core/utilities profile_fitting/read_profile
core/config_file_parser core/print_results profile_fitting/add_functions_1d core/convolver
core/mp_enorm core/statistics core/mersenne_twister core/count_cpu_cores
function_objects/psf_interpolators
profile_fitting/convolver1d profile_fitting/model_object_1d
profile_fitting/bootstrap_errors_1d profile_fitting/profilefit_main"""
profilefit_base_objs = profilefit_base_obj_string.split()
profilefit_base_sources = [name + ".cpp" for name in profilefit_base_objs]
# profilefit: put all the object and source-code lists together
profilefit_objs = profilefit_base_objs + modelobject1d_objs + functionobject1d_objs + solver_objs
profilefit_sources = profilefit_base_sources + modelobject1d_sources + functionobject1d_sources + solver_sources
# psfconvolve1d: put all the object and source-code lists together
psfconvolve1d_objs = ["profile_fitting/psfconvolve1d_main", "core/commandline_parser", "core/utilities",
"profile_fitting/read_profile", "profile_fitting/convolver1d"]
psfconvolve1d_sources = [name + ".cpp" for name in psfconvolve1d_objs]
# source+obj lists for older or less-used programs:
# readimage: put all the object and source-code lists together
readimage_sources = ["readimage_main.cpp", "image_io.cpp"]
# psfconvolve: put all the object and source-code lists together
psfconvolve_objs = ["extra/psfconvolve_main", "core/commandline_parser", "core/utilities",
"core/image_io", "core/convolver"]
psfconvolve_sources = [name + ".cpp" for name in psfconvolve_objs]
# test_parser: put all the object and source-code lists together
testparser_objs = ["test_parser", "core/config_file_parser", "core/utilities"]
testparser_sources = [name + ".cpp" for name in testparser_objs]
# test_2dspline: put all the object and source-code lists together
spline2dtest_objs = ["spline2dtest_main", "function_objects/psf_interpolators",
"core/commandline_parser", "core/utilities", "core/image_io",
"function_objects/function_object", "function_objects/func_pointsource"]
spline2dtest_sources = [name + ".cpp" for name in spline2dtest_objs]
# profile fit is fast, so we don't really need an "optimized" version
profilefit_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(profilefit_objs, profilefit_sources) ]
env_1d.Program("profilefit", profilefit_dbg_objlist)
psfconvolve_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(psfconvolve_objs, psfconvolve_sources) ]
env_debug_nofits.Program("psfconvolve", psfconvolve_dbg_objlist)
psfconvolve1d_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(psfconvolve1d_objs, psfconvolve1d_sources) ]
env_1d.Program("psfconvolve1d", psfconvolve1d_dbg_objlist)
spline2dtest_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(spline2dtest_objs, spline2dtest_sources) ]
env_debug.Program("spline2dtest", spline2dtest_objlist)
# timing: variation on makeimage designed to time image-generation and convolution
# Base files for timing:
timing_base_obj_string = """core/commandline_parser core/utilities core/image_io
core/config_file_parser core/add_functions core/mp_enorm core/mersenne_twister
extra/timing_main"""
timing_base_objs = timing_base_obj_string.split()
timing_base_sources = [name + ".cpp" for name in timing_base_objs]
timing_sources = timing_base_sources + modelobject_sources + functionobject_sources
env.Program("timing", timing_sources)
# test harnesses, etc.:
# test_commandline_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(test_commandline_objs, test_commandline_sources) ]
# env_debug.Program("test_commandline", test_commandline_objlist)
# older programs
testparser_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(testparser_objs, testparser_sources) ]
env_debug.Program("testparser", testparser_objlist)
readimage_test_objs = ["readimage_test", "core/image_io"]
readimage_test_sources = [name + ".cpp" for name in readimage_test_objs]
readimage_test_dbg_objlist = [ env_debug.Object(obj + ".do", src) for (obj,src) in zip(readimage_test_objs, readimage_test_sources) ]
env.Program("readimage_test", readimage_test_dbg_objlist)