Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
1302 lines (1173 loc) · 51.4 KB
/
Copy pathMakefile
File metadata and controls
1302 lines (1173 loc) · 51.4 KB
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
# -*- Makefile -*-
#
# Makefile for DynEarthSol
#
# Author: Eh Tan <tan2@earth.sinica.edu.tw>
#
## Build notes
## - Run simply `make` to build the optimized production executable.
## - For a debugging build, run for example: `make opt=0 openmp=0`.
## - A flag rebuilds only what it reaches (hdf5: 4 of 23 units; LDFLAGS: relink
## only). `make feature-map` prints the reach; `make config` summarises it.
## Common configuration variables (set on the make command line or edit below):
## - ndims = 3 : build 3D code; set to 2 for the 2D code.
## - opt = 0..3 : optimization level. 0 = debugging (no optimizations),
## 1 = low optimization, 2 = default optimized build, 3 = aggressive
## optimizations (-march=native, -O3, etc.).
## - openacc = 1 : enable OpenACC compilation (NVHPC).
## - GPU_CC = 80 : GPU compute capability to target, for openacc=1. Default: the
## capability of the GPU in this host, else 80. `make config` prints it.
## - openmp = 1 : enable OpenMP parallelization.
## - nprof = 1 : enable NVHPC nprof profiling build (uses nvc++ when set),
## 1 = main dynearthsol loop profiling, 2 = detailed profiling.
## - gprof = 1 : enable GNU gprof instrumentation (-pg).
## - usemmg = 1 : enable MMG mesh optimization support (requires MMG headers/libs).
## - hdf5 = 1 : enable HDF5-based vtkhdf output support (requires hdf5).
## - useexo = 1 : enable ExodusII import support (3D only; requires seacas/exodus libs).
## - snapshot_diff = 1 : embed uncommitted code changes in the executable.
## Boost, HDF5 and, on macOS, the OpenMP runtime are found automatically -- see
## the "Optional paths" block below for the Boost, HDF5 and NVHPC overrides; every
## other dependency's path is declared beside the code that uses it.
ndims = 3
opt = 2
openacc = 0
openmp = 1
nprof = 0
gprof = 0
usemmg = 0
useexo = 0
use_gospl = 0
hdf5 = 0
snapshot_diff = 0
nofma = 0 # disable FMA instructions when using nvc++, may help if using mixed precision
## Every knob above but ndims and opt; the build.snapshot records those not at 0, in this
## order. The check below accepts only listed knobs, so a new one cannot be missed here.
KNOB_VARS = openmp openacc hdf5 usemmg useexo use_gospl nprof gprof nofma snapshot_diff
ifeq ($(ndims), 2)
useexo = 0 # for now, can import only 3d exo mesh
endif
## Reject a variable this Makefile does not know: make accepts any NAME=VALUE
## silently, so `ndim=2` builds 3D and reports success. Command-line names only.
KNOWN_VARS = ndims opt $(KNOB_VARS) GPU_CC CXX \
BOOST_ROOT_DIR HDF5_INCLUDE_DIR HDF5_LIB_DIR NVHPC_DIR \
OPENMP_ROOT_DIR OPENMP_INCLUDE_DIR OPENMP_LIB_DIR \
BREW_PREFIX MACPORTS_PREFIX CONDA_PREFIX \
PYTHON_VERSION PYTHON_INCLUDE_DIR PYTHON_LIB_DIR \
EXO_INCLUDE EXO_LIB_DIR MMG_INCLUDE MMG_LIB_DIR \
GOSPL_EXT_DIR CONDA_ENV_PATH
CMDLINE_VARS = $(foreach v,$(.VARIABLES),$(if $(filter command line,$(origin $(v))),$(v)))
UNKNOWN_VARS = $(filter-out $(KNOWN_VARS),$(CMDLINE_VARS))
ifneq ($(UNKNOWN_VARS),)
$(error unknown make variable(s): $(UNKNOWN_VARS). Known: $(KNOWN_VARS))
endif
OSNAME := $(shell uname -s)
########################################################################
## Optional paths -- Boost, HDF5 and NVHPC. All of these may stay blank.
##
## The build locates them by itself; `make config` prints what it found. Set a
## variable here, or on the make command line, only to force one specific install
## -- an HPC module, a conda env, a hand-built library -- which skips detection
## for that dependency entirely.
##
## Every other dependency's path is declared beside the code that consumes it, so
## it is read together with the flags it feeds: OPENMP_* in the clang++ branch, and
## EXO_*, MMG_* and the GoSPL and PYTHON_* group inside their useexo=1, usemmg=1
## and use_gospl=1 blocks.
##
## Everything here uses `?=`, so an EXPORTED value is honoured -- that is how a
## module file or a conda activate hook can name a prefix once instead of every
## make invocation repeating it. A value on the make command line still outranks
## both. The same holds for OPENMP_ROOT_DIR, declared with the OpenMP search.
##
## The catch, since naming a path skips detection for that dependency: a stale
## export is preferred over a perfectly good installed copy. It is not silent --
## `make check-deps` names the prefix it was given and what it expected under it,
## and `make config` prints every resolved path with each library's architecture --
## but it is the one way these knobs surprise people, so unset what you no longer
## use rather than leaving it in a shell profile.
########################################################################
## Boost. Either an install prefix, holding include/ and lib/, or a b2 build
## directory, recognised by its stage/ subdirectory.
BOOST_ROOT_DIR ?= # /path/to/boost
## HDF5, for hdf5=1. Set either or both; whichever is left blank is still
## detected. Layouts the search knows about, if you would rather name them:
## Debian/Ubuntu /usr/include/hdf5/serial /usr/lib/<arch>-linux-gnu/hdf5/serial
## Fedora/RHEL /usr/include /usr/lib64
## Homebrew $(brew --prefix hdf5)/include $(brew --prefix hdf5)/lib
HDF5_INCLUDE_DIR ?= # /usr/include/hdf5/serial
HDF5_LIB_DIR ?= # /usr/lib/x86_64-linux-gnu/hdf5/serial
## NVHPC. Blank takes nvc++ from PATH; naming an SDK uses its compilers/bin/nvc++
## for openacc=1 and nprof, and its CUDA toolkit for nprof's NVTX headers.
NVHPC_DIR ?= # /cluster/nvidia/hpc_sdk/Linux_x86_64/21.2
## Package-manager prefixes the macOS searches start from. Chosen by host
## architecture, never from PATH: a Mac that has run both Homebrews keeps the
## other one's pkg-config and headers on PATH, and building against those links
## the wrong architecture. `brew --prefix <formula>` is not used either -- it
## prints the would-be path and exits 0 for a formula that is not installed.
ifeq ($(OSNAME), Darwin)
ifeq ($(shell uname -m), arm64)
BREW_PREFIX ?= /opt/homebrew
else
BREW_PREFIX ?= /usr/local
endif
MACPORTS_PREFIX ?= /opt/local
endif
## $(call firstdir,<file>,<candidate dirs>) -- first directory in the list that
## really holds <file>, else empty. Globs allowed in both arguments
## (`lib/clang/*/include`, `libhdf5.*`); the candidate list is a priority list.
##
## $(wildcard) expands the globs, `test -f` decides. Not `ls` and not $(wildcard)
## alone: both accept a DANGLING SYMLINK, and $(BREW_PREFIX)/opt/<formula> is a
## symlink into the Cellar, so a half-removed keg would stop the search on a dud
## instead of falling through to a provider that works.
firstdir = $(patsubst %/,%,$(dir $(call firstfile,$(1),$(2))))
firstfile = $(firstword $(shell for f in $(wildcard $(addsuffix /$(1),$(2))); do \
test -f "$$f" && { echo $$f; break; }; done))
## Drop a candidate whose prefix is empty, so an unset CONDA_PREFIX contributes
## nothing rather than a bare, plausible-looking /include or /lib.
under = $(if $(strip $(1)),$(addprefix $(strip $(1)),$(2)))
## Select C++ compiler and set paths to necessary libraries
## A named NVHPC_DIR picks the compiler out of that SDK, so a host with several
## installed builds against the one whose CUDA toolkit nprof is also given.
NVHPC_CXX = $(if $(strip $(NVHPC_DIR)),$(strip $(NVHPC_DIR))/compilers/bin/nvc++,nvc++)
ifeq ($(openacc), 1)
CXX = $(NVHPC_CXX)
suffix = .gpu
else
ifneq ($(nprof), 0)
CXX = $(NVHPC_CXX)
else
# Select compiler based on platform
ifeq ($(OSNAME), Darwin)
# clang++ is the default optimized compiler for macOS
CXX = clang++
else
CXX = g++
endif
endif
endif
CXX_BACKEND = ${CXX}
ifeq ($(hdf5), 1)
## Every stage below fills only what is still blank, so a path set by hand is
## never overwritten, and setting just one of the two still leaves the other
## detected instead of empty (a blank -L would eat the -lhdf5 that follows it).
HDF5_DETECT :=
ifeq ($(strip $(HDF5_INCLUDE_DIR)),)
HDF5_DETECT := yes
endif
ifeq ($(strip $(HDF5_LIB_DIR)),)
HDF5_DETECT := yes
endif
ifeq ($(HDF5_DETECT), yes)
ifeq ($(OSNAME), Darwin)
## Use the architecture-matched Homebrew prefix (see BREW_PREFIX above)
## and that prefix's own pkg-config, not whichever one PATH offers.
HDF5_BREW = $(BREW_PREFIX)
PKGCONFIG := $(BREW_PREFIX)/bin/pkg-config
else
HDF5_BREW =
PKGCONFIG := pkg-config
endif
## Ask pkg-config for directories rather than parsing --cflags/--libs:
## h5cc -show and --libs mix in inline .a paths and -lsz/-lz/-ldl, which
## are painful to filter and differ per build. Debian and Ubuntu name the
## serial module hdf5-serial; everyone else ships plain hdf5.
HDF5_PC := $(shell for m in hdf5 hdf5-serial; do \
$(PKGCONFIG) --exists $$m 2>/dev/null && { echo $$m; break; }; done)
ifneq ($(HDF5_PC),)
ifeq ($(strip $(HDF5_INCLUDE_DIR)),)
HDF5_INCLUDE_DIR := $(shell $(PKGCONFIG) --variable=includedir $(HDF5_PC))
endif
ifeq ($(strip $(HDF5_LIB_DIR)),)
HDF5_LIB_DIR := $(shell $(PKGCONFIG) --variable=libdir $(HDF5_PC))
endif
endif
## Without a .pc file, probe the layouts real installs use -- Fedora and
## RHEL need this branch unconditionally, as their hdf5-devel ships no
## pkg-config file at all. Debian splits its serial build across two trees
## (/usr/include/hdf5/serial and .../hdf5/serial under the multiarch libdir),
## so the header and the library are searched separately, not under one
## prefix. /usr/local comes before the system directories so a hand-built
## HDF5 installed there wins over the distro package.
HDF5_INC_DIRS = $(call under,$(HDF5_BREW),/opt/hdf5/include) \
/usr/include/hdf5/serial \
/usr/local/include /usr/include
HDF5_LIB_DIRS = $(call under,$(HDF5_BREW),/opt/hdf5/lib) \
/usr/lib/*-linux-gnu/hdf5/serial \
/usr/local/lib /usr/local/lib64 \
/usr/lib/*-linux-gnu \
/usr/lib64 /usr/lib
ifeq ($(strip $(HDF5_INCLUDE_DIR)),)
HDF5_INCLUDE_DIR := $(call firstdir,hdf5.h,$(HDF5_INC_DIRS))
endif
ifeq ($(strip $(HDF5_LIB_DIR)),)
HDF5_LIB_DIR := $(call firstdir,libhdf5.*,$(HDF5_LIB_DIRS))
endif
## Checked separately: finding one without the other is still unbuildable,
## and failing here beats a confusing link error hundreds of lines later.
## Indented with SPACES, not a tab: a tab-indented $(error) this early
## reads as a recipe line and make reports "commands commence before first
## target" instead of the message below.
ifeq ($(strip $(HDF5_INCLUDE_DIR)),)
$(error hdf5=1 but no HDF5 headers found. Install HDF5 (macOS: brew install hdf5; \
Debian/Ubuntu: apt install libhdf5-dev; Fedora/RHEL: dnf install hdf5-devel), \
or set the paths by hand: \
make hdf5=1 HDF5_INCLUDE_DIR=/prefix/include HDF5_LIB_DIR=/prefix/lib)
endif
ifeq ($(strip $(HDF5_LIB_DIR)),)
$(error hdf5=1: found HDF5 headers in $(HDF5_INCLUDE_DIR) but no libhdf5. \
Set HDF5_LIB_DIR to the directory holding libhdf5, \
e.g. make hdf5=1 HDF5_LIB_DIR=/prefix/lib)
endif
endif
endif
## Candidate prefixes, in priority order. macOS has no system Boost, so /usr is
## never the answer there and these tiers are what remove the old requirement to
## pass BOOST_ROOT_DIR=$(brew --prefix boost) by hand.
##
## Package managers come BEFORE conda: a shell that auto-activates `base` has
## conda on for everything, not just the work that wants it, so it is the weaker
## signal of intent. A conda Boost is still used when it is the only one.
## The `opt/boost` keg beats the bare $(BREW_PREFIX) farm -- it pins the version.
## Linux keeps the tiers it had: conda, else the /usr default below.
ifeq ($(OSNAME), Darwin)
BOOST_PREFIXES = $(BREW_PREFIX)/opt/boost $(MACPORTS_PREFIX) $(BREW_PREFIX) \
$(CONDA_PREFIX)
else
BOOST_PREFIXES = $(CONDA_PREFIX)
endif
## Both halves required: a headers-only tree (extracted tarball, half-removed
## keg, conda's libboost-headers without libboost) would otherwise be selected
## and then fail at the link. The `libboost_program_options.*` glob matches
## exactly what -lboost_program_options can link, and not MacPorts' `-mt` variant.
## `override` so a command-line `BOOST_ROOT_DIR=` still falls through to
## detection -- a command-line value otherwise outranks even the /usr fallback.
ifndef BOOST_ROOT_DIR
override BOOST_ROOT_DIR := $(shell for d in $(BOOST_PREFIXES); do \
test -f $$d/include/boost/program_options.hpp || continue; \
set -- $$d/lib/libboost_program_options.*; \
test -f "$$1" || continue; \
echo $$d; break; done)
endif
## Nothing found: keep the historical /usr default so that `make clean`, `make
## config` and a hand-set BOOST_LIB_DIR all still work, and record that this is
## a failed search rather than a deliberate choice. On macOS the difference is
## decisive -- there is no system Boost under /usr (this macOS has no
## /usr/include at all), so the fallback there always means "not found", and
## check-deps says so instead of letting the link produce a wall of undefined
## symbols.
ifndef BOOST_ROOT_DIR
override BOOST_ROOT_DIR = /usr
## Only on macOS does the fallback mean "the search failed" -- on Linux /usr
## is the normal, correct answer and the compiler finds Boost there on its
## own. Keeping that distinction is what lets check-deps be decisive on
## macOS without misreporting a healthy Linux build.
ifeq ($(OSNAME), Darwin)
BOOST_NOT_FOUND := yes
endif
endif
########################################################################
## Select compiler and linker flags
## (Usually you won't need to modify anything below)
########################################################################
BOOST_LDFLAGS = -lboost_program_options
# Detect multiarch tuple (e.g. x86_64-linux-gnu on Debian/Ubuntu); empty on macOS/other.
# Skipped on macOS, where there is no multiarch layout and `gcc` is Apple clang,
# which has no -print-multiarch: two shell-outs per make invocation for nothing.
ifneq ($(OSNAME), Darwin)
MULTIARCH := $(shell gcc -print-multiarch 2>/dev/null)
ifeq ($(MULTIARCH),)
MULTIARCH := $(shell dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)
endif
endif
ifdef BOOST_ROOT_DIR
# check existence of stage/ directory
has_stage_dir = $(wildcard $(BOOST_ROOT_DIR)/stage)
ifeq (, $(has_stage_dir))
# no stage dir, BOOST_ROOT_DIR is the installation directory
ifeq ($(BOOST_ROOT_DIR), /usr)
# Conda environments can disrupt gcc's default search order, so explicitly
# add the multiarch path before /usr/include (keeps bits/stdint-least.h
# resolvable) and use the multiarch lib dir where boost is installed.
ifneq ($(MULTIARCH),)
BOOST_CXXFLAGS = -I/usr/include/$(MULTIARCH) -I/usr/include
BOOST_LIB_DIR = /usr/lib/$(MULTIARCH)
else
BOOST_LIB_DIR = /usr/lib
endif
else
BOOST_CXXFLAGS = -I$(BOOST_ROOT_DIR)/include
BOOST_LIB_DIR = $(BOOST_ROOT_DIR)/lib
endif
else
# with stage dir, BOOST_ROOT_DIR is the build directory
BOOST_CXXFLAGS = -I$(BOOST_ROOT_DIR)
BOOST_LIB_DIR = $(BOOST_ROOT_DIR)/stage/lib
endif
## Full path on macOS, for the same reason as libomp above: a -L dir also
## answers the implicit -lc++, and a conda prefix ships its own libc++.dylib
## (left on -L, the executable loads two libc++ images and says nothing).
## Only when the .dylib exists -- a static-only Boost still needs -l -- and
## via firstfile so a dangling Cellar symlink falls back to -l too.
## Linux keeps -L/-l, right for .so versioning and the stage layout.
BOOST_DYLIB = $(call firstfile,libboost_program_options.dylib,$(BOOST_LIB_DIR))
ifeq ($(OSNAME)$(if $(BOOST_DYLIB),yes,), Darwinyes)
BOOST_LDFLAGS = $(BOOST_DYLIB)
else
BOOST_LDFLAGS += -L$(BOOST_LIB_DIR)
endif
ifneq ($(OSNAME), Darwin)
# Apple's ld supports -rpath only in the comma form, so Darwin gets its
# rpath from the install_name_tool step after the link instead.
BOOST_LDFLAGS += -Wl,-rpath=$(BOOST_LIB_DIR)
endif
endif
ifeq ($(useexo), 1)
# path to exodus header files
EXO_INCLUDE = ./seacas/include
# path of exodus library files, if not in standard system location
EXO_LIB_DIR = ./seacas/lib
EXO_CXXFLAGS = -I$(EXO_INCLUDE) -DUSEEXODUS
EXO_LDFLAGS = -L$(EXO_LIB_DIR) -lexodus
ifneq ($(OSNAME), Darwin) # Apple's ld doesn't support -rpath
EXO_LDFLAGS += -Wl,-rpath=$(EXO_LIB_DIR)
endif
endif
ifeq ($(usemmg), 1)
# path to MMG3D header files
MMG_INCLUDE = ./mmg/build/include
# path of MMG3D library files, if not in standard system location
MMG_LIB_DIR = ./mmg/build/lib
MMG_CXXFLAGS = -I$(MMG_INCLUDE) -DUSEMMG
ifeq ($(ndims), 3)
MMG_LDFLAGS = -L$(MMG_LIB_DIR) -lmmg3d
else
MMG_LDFLAGS = -L$(MMG_LIB_DIR) -lmmg2d
endif
ifneq ($(OSNAME), Darwin) # Apple's ld doesn't support -rpath
MMG_LDFLAGS += -Wl,-rpath=$(MMG_LIB_DIR)
endif
MMG_LIB = $(MMG_LIB_DIR)/libmmg$(ndims)d.a
endif
ifeq ($(use_gospl), 1)
# GoSPL extensions library configuration
# Path to locally built gospl_extensions (clone from GitHub)
GOSPL_EXT_DIR = $(HOME)/opt/gospl_extensions
GOSPL_INCLUDE = $(GOSPL_EXT_DIR)/include
GOSPL_LIB_DIR = $(GOSPL_EXT_DIR)/lib
GOSPL_PYTHONPATH = $(GOSPL_EXT_DIR)/cpp_interface
# Export PYTHONPATH for GoSPL extensions
export PYTHONPATH := $(GOSPL_PYTHONPATH):$(PYTHONPATH)
GOSPL_CXXFLAGS = -I$(GOSPL_INCLUDE) -DHAS_GOSPL_CPP_INTERFACE -Igospl_driver
GOSPL_LDFLAGS = -L$(GOSPL_LIB_DIR) -lgospl_extensions
ifneq ($(OSNAME), Darwin) # Apple's ld doesn't support -rpath
GOSPL_LDFLAGS += -Wl,-rpath=$(GOSPL_LIB_DIR)
endif
# Python include/lib dirs — override on the command line for non-conda builds:
# make use_gospl=1 PYTHON_VERSION=3.12 PYTHON_INCLUDE_DIR=/usr/include/python3.12 PYTHON_LIB_DIR=/usr/lib/x86_64-linux-gnu
CONDA_ENV_PATH = $(HOME)/miniconda3/envs/gospl
PYTHON_VERSION ?= 3.11
PYTHON_INCLUDE_DIR ?= $(CONDA_ENV_PATH)/include/python$(PYTHON_VERSION)
PYTHON_LIB_DIR ?= $(CONDA_ENV_PATH)/lib
GOSPL_CXXFLAGS += -I$(PYTHON_INCLUDE_DIR)
GOSPL_LDFLAGS += -lpython$(PYTHON_VERSION) -L$(PYTHON_LIB_DIR)
ifneq ($(OSNAME), Darwin)
GOSPL_LDFLAGS += -Wl,-rpath=$(PYTHON_LIB_DIR)
endif
endif
## Warnings belong in every build, not only the debug one. gcc's uninitialized and
## array-bounds analyses run inside the optimizer, so -O0 -- the only level that used to
## ask for warnings -- is where they see the least: -O2 -Wall finds real defects that
## -O0 -Wall cannot. The two suppressions cover what this codebase does deliberately,
## namely file-static helpers in headers that not every translation unit calls.
## Two flags are added per compiler below instead of here: -Wno-unknown-pragmas, because
## the clang debug build wants to hear about acc pragmas while every other build would
## drown in them, and -Wno-unused-private-field, which only clang has.
WARNFLAGS = -Wall -Wno-unused-variable -Wno-unused-function
ifneq (, $(findstring clang++, $(CXX)))
CXX_SUPPORTED = yes
## -Wno-unused-private-field is the member-field half of the WARNFLAGS suppressions
## and has no g++ spelling. It fires on members a sibling configuration uses -- the
## THREED-only axis, the ACC-only knn state, the hdf5-only compression level -- which
## is the same deliberate pattern, not a finding.
CXXFLAGS = -g -std=c++0x -DGPP1X $(WARNFLAGS) -Wno-unused-private-field
## Everything but opt=0 and opt=-1; those two are the builds that report acc pragmas.
ifeq (,$(filter 0 -1,$(opt)))
CXXFLAGS += -Wno-unknown-pragmas
endif
LDFLAGS = -lm
TETGENFLAG =
# macOS needs extra headerpad for install_name_tool
ifeq ($(OSNAME), Darwin)
LDFLAGS += -Wl,-headerpad_max_install_names
endif
ifeq ($(opt), 1)
CXXFLAGS += -O1
else ifeq ($(opt), 2)
CXXFLAGS += -O2
else ifeq ($(opt), 3)
CXXFLAGS += -march=native -O3 -ffast-math -funroll-loops
else # debugging
CXXFLAGS += -O0
ifeq ($(opt), -1)
CXXFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
endif
endif
ifeq ($(openmp), 1)
ifeq ($(OSNAME), Darwin)
## Apple clang ships no OpenMP runtime, so one has to be found. Set
## OPENMP_ROOT_DIR to force a prefix (its include/ and lib/), or
## OPENMP_INCLUDE_DIR / OPENMP_LIB_DIR when they are not siblings.
## Plain `=`: an exported value is ignored, as for BOOST_ROOT_DIR.
OPENMP_ROOT_DIR ?= # /path/to/openmp
ifneq ($(strip $(OPENMP_ROOT_DIR)),)
ifeq ($(strip $(OPENMP_INCLUDE_DIR)),)
OPENMP_INCLUDE_DIR := $(OPENMP_ROOT_DIR)/include
endif
ifeq ($(strip $(OPENMP_LIB_DIR)),)
OPENMP_LIB_DIR := $(OPENMP_ROOT_DIR)/lib
endif
endif
## Candidate lists in priority order, named once and reused by the
## search, the check-deps `Searched:` line and `make config`, so the
## three cannot disagree. external/openmp-install is first: it is where
## this project's build-from-source instructions put LLVM OpenMP.
## Header and library are searched separately because Homebrew's llvm
## keeps omp.h under lib/clang/<ver>/include, not beside the library.
OPENMP_INC_DIRS = external/openmp-install/include \
$(BREW_PREFIX)/opt/libomp/include \
$(BREW_PREFIX)/opt/llvm/lib/clang/*/include \
$(MACPORTS_PREFIX)/include/libomp \
$(call under,$(CONDA_PREFIX),/include)
OPENMP_LIB_DIRS = external/openmp-install/lib \
$(BREW_PREFIX)/opt/libomp/lib \
$(BREW_PREFIX)/opt/llvm/lib \
$(MACPORTS_PREFIX)/lib/libomp \
$(call under,$(CONDA_PREFIX),/lib)
ifeq ($(strip $(OPENMP_INCLUDE_DIR)),)
OPENMP_INCLUDE_DIR := $(call firstdir,omp.h,$(OPENMP_INC_DIRS))
endif
ifeq ($(strip $(OPENMP_LIB_DIR)),)
OPENMP_LIB_DIR := $(call firstdir,libomp.dylib,$(OPENMP_LIB_DIRS))
endif
## Flags only when both halves were found, so a half-detected runtime
## cannot produce a truncated command line; check-deps does the
## reporting, which keeps `make clean` and `make config` working.
ifneq ($(strip $(OPENMP_INCLUDE_DIR)),)
ifneq ($(strip $(OPENMP_LIB_DIR)),)
## rpath must be absolute, so a relative OPENMP_LIB_DIR
## (external/... above) is anchored at the build directory. It is
## needed when the dylib's install name is @rpath-relative --
## conda's libomp is, Homebrew's is not.
ifeq ($(filter /%,$(OPENMP_LIB_DIR)),$(OPENMP_LIB_DIR))
OPENMP_RPATH := $(OPENMP_LIB_DIR)
else
OPENMP_RPATH := $(CURDIR)/$(OPENMP_LIB_DIR)
endif
## -idirafter, not -I: placed after the system directories it can
## only supply a header the toolchain lacks, i.e. omp.h. With -I,
## Homebrew llvm's omp.h dir -- a compiler *resource* dir holding
## its own stdint.h -- breaks the SDK ("unknown type name
## 'uint8_t'" from sys/resource.h). Conda shadows the same way.
##
## Full path, not -L/-lomp: every -L dir also answers the implicit
## -lc++/-lz/-lm, and a conda prefix ships its own libc++.dylib.
CXXFLAGS += -Xpreprocessor -fopenmp -idirafter $(OPENMP_INCLUDE_DIR)
LDFLAGS += $(OPENMP_LIB_DIR)/libomp.dylib
endif
endif
else
## Non-Apple clang drives OpenMP itself: -fopenmp both enables the
## pragmas and links the runtime, with no paths to find.
CXXFLAGS += -fopenmp
LDFLAGS += -fopenmp
endif
endif
else ifneq (, $(findstring g++, $(CXX_BACKEND))) # if using any version of g++
CXX_SUPPORTED = yes
CXXFLAGS = -g -std=c++0x $(WARNFLAGS) -Wno-unknown-pragmas
LDFLAGS = -lm
TETGENFLAG = -Wno-unused-but-set-variable -Wno-int-to-pointer-cast
ifeq ($(opt), 1)
CXXFLAGS += -O1
else ifeq ($(opt), 2)
CXXFLAGS += -O2
else ifeq ($(opt), 3) # experimental, use at your own risk :)
CXXFLAGS += -march=native -O3 -ffast-math -funroll-loops
else # debugging flags
CXXFLAGS += -O0 -fbounds-check -ftrapv
ifeq ($(opt), -1)
CXXFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
endif
endif
ifeq ($(openmp), 1)
CXXFLAGS += -fopenmp
LDFLAGS += -fopenmp
endif
ifeq ($(gprof), 1)
CXXFLAGS += -pg
LDFLAGS += -pg
endif
GCCVERSION = $(shell $(CXX) --version | grep g++ | sed 's/^.* //g' | cut -d. -f1)
ifeq ($(shell expr $(GCCVERSION) \> 8), 1)
CXXFLAGS += -DGPP1X
endif
else ifneq (, $(findstring icpc, $(CXX_BACKEND))) # if using intel compiler, tested with v14
CXX_SUPPORTED = yes
CXXFLAGS = -g -std=c++0x
LDFLAGS = -lm
ifeq ($(opt), 1)
CXXFLAGS += -O1
else ifeq ($(opt), 2)
CXXFLAGS += -O2
else ifeq ($(opt), 3) # experimental, use at your own risk :)
CXXFLAGS += -fast -fast-transcendentals -fp-model fast=2
else # debugging flags
CXXFLAGS += -O0 -check=uninit -check-pointers=rw -check-pointers-dangling=all -fp-trap-all=all
endif
ifeq ($(openmp), 1)
CXXFLAGS += -fopenmp
LDFLAGS += -fopenmp
endif
else ifneq (, $(findstring nvc++, $(CXX)))
CXX_SUPPORTED = yes
## No WARNFLAGS here: nvc++ rejects -Wno-unknown-pragmas outright ("Unknown switch"),
## and bare -Wall only adds a macro redefinition inside boost's own pgi.hpp.
## It does report unreferenced file statics by default, which is the class g++'s
## WARNFLAGS suppresses on purpose -- headers here carry helpers not every translation
## unit calls, and each dimension compiles the other's. --diag_suppress is nvc++'s
## spelling of that -Wno-unused-{variable,function}. Its set_but_not_used stays on, so
## that a value written and never read is still reported, as it is under g++.
CXXFLAGS = -g -Minfo=mp,accel --diag_suppress=declared_but_not_referenced
LDFLAGS =
TETGENFLAGS =
ifeq ($(opt), 1)
CXXFLAGS += -O1
else ifeq ($(opt), 2)
CXXFLAGS += -O2
endif
ifeq ($(nofma), 1)
CXXFLAGS += -mno-fma
endif
ifeq ($(openacc), 1)
## Name the GPU target; origin read before the := below rewrites it to "file".
GPU_CC_NOTE := $(if $(filter command line environment,$(origin GPU_CC)),overridden,detected)
GPU_CC := $(strip $(GPU_CC))
ifeq ($(GPU_CC),)
GPU_CC := $(shell nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null \
| head -1 | tr -d '.')
endif
## $(warning) and $(error) indented with SPACES, not a tab -- see the note
## at the hdf5 $(error) above.
ifeq ($(GPU_CC),)
## 80 = A100, what the CI matrix targets. Nothing is known about
## the local host here, so this is a guess that at least links.
GPU_CC := 80
GPU_CC_NOTE := no GPU found, default
$(warning No GPU detected via nvidia-smi -- targeting cc$(GPU_CC). Pass GPU_CC=<cc> to override.)
endif
CXXFLAGS += -acc=gpu -cuda -DACC
LDFLAGS += -acc=gpu -gpu=cc$(GPU_CC),mem:managed -cuda
# CXXFLAGS += -acc=gpu -Mcuda -DACC
# LDFLAGS += -acc=gpu -gpu=managed -Mcuda
## Keep our symbols out of .dynsym
LDFLAGS += -Wl,--version-script=local-symbols.map
ifeq ($(nofma), 1)
CXXFLAGS += -gpu=cc$(GPU_CC),mem:managed,nofma
# CXXFLAGS += -gpu=managed,nofma
else
CXXFLAGS += -gpu=cc$(GPU_CC),mem:managed
# CXXFLAGS += -gpu=managed
endif
endif
ifeq ($(openmp), 1)
CXXFLAGS += -fopenmp
LDFLAGS += -fopenmp
endif
ifneq ($(nprof), 0)
CXXFLAGS += -I$(NVHPC_DIR)/cuda/include -DNPROF
ifeq ($(nprof), 2)
CXXFLAGS += -DNPROF_DETAIL
endif
LDFLAGS += -g
endif
else ifneq (, $(findstring pgc++, $(CXX)))
CXX_SUPPORTED = yes
CXXFLAGS = -march=core2
LDFLAGS =
TETGENFLAGS =
ifeq ($(opt), 1)
CXXFLAGS += -O1
else ifeq ($(opt), 2)
CXXFLAGS += -O2 -silent
else ifeq ($(opt), 3)
CXXFLAGS += -O3 -fast -silent
endif
ifeq ($(openmp), 1)
CXXFLAGS += -mp
LDFLAGS += -mp
endif
ifneq ($(nprof), 0)
CXXFLAGS += -Minfo=mp -I$(NVHPC_DIR)/cuda/include -DNPROF
ifeq ($(nprof), 2)
CXXFLAGS += -DNPROF_DETAIL
endif
LDFLAGS += -L$(NVHPC_DIR)/cuda/lib64 -Wl,-rpath,$(NVHPC_DIR)/cuda/lib64 -lnvToolsExt
endif
else
## Unsupported compiler: leave CXX_SUPPORTED unset. `make config` says so, and
## check-deps refuses the build -- which used to be done with a second `all:`
## target here, whose recipe was silently overridden by the real `all:` further
## down (make warned "overriding commands for target 'all'" and used the other).
endif
ifeq ($(hdf5), 1)
## Named, like MMG_CXXFLAGS, so the stamp split can subtract it. Folded into
## CXXFLAGS it would perturb the global stamp and rebuild everything.
HDF5_CXXFLAGS = -DHDF5 -I$(HDF5_INCLUDE_DIR)
CXXFLAGS += $(HDF5_CXXFLAGS)
LDFLAGS += -L$(HDF5_LIB_DIR) -lhdf5
ifneq ($(OSNAME), Darwin)
LDFLAGS += -Wl,-rpath,$(HDF5_LIB_DIR)
endif
endif
## Is git in the path?
HAS_GIT := $(shell git --version 2>/dev/null)
ifneq ($(HAS_GIT),)
## Is this a git repository?
IS_REPO := $(shell git rev-parse --is-inside-work-tree 2>/dev/null)
endif
SRCS = \
barycentric-fn.cxx \
ats_output_scheduler.cxx \
brc-interpolation.cxx \
bc.cxx \
binaryio.cxx \
dynearthsol.cxx \
earthquake_state.cxx \
fields.cxx \
geometry.cxx \
ic.cxx \
ic-read-temp.cxx \
input.cxx \
matprops.cxx \
mesh.cxx \
monitor.cxx \
nn-interpolation.cxx \
output.cxx \
phasechanges.cxx \
remeshing.cxx \
rheology.cxx \
runtime_info.cxx \
utils.cxx \
markerset.cxx \
knn.cxx
ifeq ($(use_gospl), 1)
SRCS += gospl_driver/gospl-driver.cxx
endif
INCS = \
array2d.hpp \
ats_output_scheduler.hpp \
barycentric-fn.hpp \
bc.hpp \
binaryio.hpp \
brc-interpolation.hpp \
constants.hpp \
earthquake_state.hpp \
fields.hpp \
geometry.hpp \
ic.hpp \
ic-read-temp.hpp \
input.hpp \
knn.hpp \
markerset.hpp \
matprops.hpp \
mesh.hpp \
monitor.hpp \
nn-interpolation.hpp \
output.hpp \
parameters.hpp \
phasechanges.hpp \
remeshing.hpp \
rheology.hpp \
runtime_info.hpp \
sortindex.hpp \
utils.hpp
ifeq ($(use_gospl), 1)
INCS += gospl_driver/gospl-driver.hpp
endif
OBJS = $(SRCS:.cxx=.$(ndims)d$(suffix).o)
# INVARIANT: a flag either renames the output or lands in the build stamp.
# BUILD_STAMP uses this same $(ndims)d$(suffix) key, so the two cannot drift --
# else configs writing disjoint files share a stamp and invalidate each other.
EXE = dynearthsol$(ndims)d$(suffix)
## Libraries
## Same $(ndims)d$(suffix) key as everything else. On $(suffix) alone, one
## triangle.o served both 2d and 3d despite -DTHREED: last build won.
TET_SRCS = tetgen/predicates.cxx tetgen/tetgen.cxx
TET_INCS = tetgen/tetgen.h
TET_OBJS = $(TET_SRCS:.cxx=.$(ndims)d$(suffix).o)
TRI_SRCS = triangle/triangle.c
TRI_INCS = triangle/triangle.h
TRI_OBJS = $(TRI_SRCS:.c=.$(ndims)d$(suffix).o)
M_SRCS = $(TRI_SRCS)
M_INCS = $(TRI_INCS)
M_OBJS = $(TRI_OBJS)
ifeq ($(ndims), 3)
M_SRCS += $(TET_SRCS)
M_INCS += $(TET_INCS)
M_OBJS += $(TET_OBJS)
CXXFLAGS += -DTHREED
endif
ifeq ($(useexo), 1)
CXXFLAGS += $(EXO_CXXFLAGS)
LDFLAGS += $(EXO_LDFLAGS)
endif
ifeq ($(usemmg), 1)
CXXFLAGS += $(MMG_CXXFLAGS)
LDFLAGS += $(MMG_LDFLAGS)
endif
ifeq ($(use_gospl), 1)
CXXFLAGS += $(GOSPL_CXXFLAGS)
LDFLAGS += $(GOSPL_LDFLAGS)
endif
C3X3_DIR = 3x3-C
C3X3_LIBNAME = 3x3$(suffix)
## GPU_CC goes to the sub-make as itself: it folds the capability into the one -gpu= it
## assembles, and its flag stamp covers CFLAGS, so a changed capability deletes the
## objects it invalidates.
ifeq ($(openacc), 1)
C3X3_SUBMAKE = CC=$(NVHPC_CXX) GPU_CC=$(GPU_CC)
endif
ANN_DIR = nanoflann
CXXFLAGS += -I$(ANN_DIR)/include
GOSPL_DIR = gospl_driver
CXXFLAGS += -I$(GOSPL_DIR)
KNN_BVH_DIR = knn-bvh
ifeq ($(openacc), 1)
CXXFLAGS += -I$(KNN_BVH_DIR)/include
KNN_BVH_LIB = $(KNN_BVH_DIR)/lib/libknn_bvh.$(ndims)d.sm$(GPU_CC).a
LDFLAGS += $(KNN_BVH_LIB)
endif
# Enable Array2D structure of Array
CXXFLAGS += -DSOA
## nanoflann uses std::thread, which needs pthread on glibc. openmp=1 gets it for
## free from -fopenmp; openmp=0 linked nothing and failed with an undefined
## pthread_create. Harmless where libc already provides it.
ifneq ($(strip $(openmp)), 1)
CXXFLAGS += -pthread
LDFLAGS += -pthread
endif
## Action
.PHONY: all clean take-snapshot prepare build check-deps config FORCE
all: prepare
@$(MAKE) build
## Rebuild when the FLAGS change, not just when a source file does.
##
## Objects are named only for ndims and suffix, so every other switch -- openmp,
## opt, hdf5, usemmg, the compiler under nprof=1 -- reuses whatever was built
## last. `make openmp=0` then `make` reports success, relinks nothing, and leaves
## a single-threaded executable behind. The stamp holds the last build's flags;
## its recipe runs every time (FORCE) but only rewrites the file when they
## differ, so a changed flag rebuilds and an unchanged one costs one `cmp`.
##
## One stamp per configuration (key $(ndims)d$(suffix)), split three ways:
## BUILD_STAMP flags every object sees -- rebuild all 23.
## FEATURE_STAMPS one per FEATURES macro -- rebuild only what it reaches.
## LINK_STAMP LDFLAGS -- relink only, never recompile.
BUILD_STAMP = .build-flags.$(ndims)d$(suffix)
LINK_STAMP = .link-flags.$(ndims)d$(suffix)
## Feature table: macros whose flags are separable and that reach only part of
## the tree. Each needs FEATURE_<macro>_FLAGS naming the chunk to subtract.
## Omitted: THREED (ndims already renames outputs), HAS_GOSPL_CPP_INTERFACE
## (in parameters.hpp, so it reaches everything).
FEATURES = HDF5 USEMMG USEEXODUS
FEATURE_HDF5_FLAGS = $(HDF5_CXXFLAGS)
FEATURE_USEMMG_FLAGS = $(MMG_CXXFLAGS)
FEATURE_USEEXODUS_FLAGS = $(EXO_CXXFLAGS)
FEATURE_FLAGS = $(foreach f,$(FEATURES),$(FEATURE_$(f)_FLAGS))
FEATURE_STAMP = .build-flags-$(1).$(ndims)d$(suffix)
FEATURE_STAMPS = $(foreach f,$(FEATURES),$(call FEATURE_STAMP,$(f)))
## What is left once every feature chunk is removed (filter-out is exact-token).
GLOBAL_CXXFLAGS = $(filter-out $(FEATURE_FLAGS),$(CXXFLAGS) $(BOOST_CXXFLAGS))
## Stamps DELETE what they invalidate; mtime is not trustworthy here. make 3.81
## compares to the whole second and treats equal as up to date, and a 4-object
## feature rebuild finishes in the same second the stamp was written -- two
## back-to-back builds then linked an object still carrying -DHDF5 with no
## -lhdf5. A deleted object has no mtime to be wrong about.
##
## The delete must run in an earlier PROCESS: make 3.81 stats a target before its
## prerequisites' recipes and reuses that mtime, so deleting from the stamp
## recipe dropped 4 objects but rebuilt 3. Hence FLAG_STAMPS hangs off `prepare`,
## which the outer make finishes before `all` runs `$(MAKE) build`. The map's
## dependency edges stay too: they cover a stamp that does not exist yet.
FLAG_STAMPS = $(BUILD_STAMP) $(LINK_STAMP) $(FEATURE_STAMPS)
CXX_ID = command -v $(firstword $(CXX)); v=`$(CXX) --version 2>/dev/null | head -1`; \
test -n "$$v" || v=`$(CXX) -V 2>&1 | grep -m1 .`; echo "$$v"
$(BUILD_STAMP): FORCE
@{ echo '$(CXX)|$(GLOBAL_CXXFLAGS)'; $(CXX_ID); } > $@.tmp
@if cmp -s $@.tmp $@; then rm -f $@.tmp; else \
test -f $@ && echo " compiler or compile flags changed since the last build -- rebuilding"; \
mv $@.tmp $@; rm -f $(OBJS) $(M_OBJS); \
fi
$(LINK_STAMP): FORCE
@echo '$(CXX)|$(LDFLAGS)|$(BOOST_LDFLAGS)' > $@.tmp
@if cmp -s $@.tmp $@; then rm -f $@.tmp; else \
test -f $@ && echo " link flags changed since the last build -- relinking"; \
mv $@.tmp $@; rm -f $(EXE); \
fi
## One rule per feature via eval, so FEATURES stays the only place one is named.
## FEATURE_<macro>_OBJS comes from the map; empty makes the rm a harmless no-op.
define FEATURE_STAMP_RULE
$(call FEATURE_STAMP,$(1)): FORCE
@echo '$$(FEATURE_$(1)_FLAGS)' > $$@.tmp
@if cmp -s $$@.tmp $$@; then rm -f $$@.tmp; else \
test -f $$@ && echo " $(1) changed since the last build -- rebuilding what uses it"; \
mv $$@.tmp $$@; rm -f $$(FEATURE_$(1)_OBJS); \
fi
endef
$(foreach f,$(FEATURES),$(eval $(call FEATURE_STAMP_RULE,$(f))))
## Which objects each feature reaches. DERIVED every build, never hand-written;
## regenerated on any source or header change, so a new #ifdef widens the set
## first. If the generator cannot run, the fallback depends every object on every
## feature -- slow and correct. Under-reporting would mean a stale object in a
## plausible binary, so nothing here may risk it.
FEATURE_DEPS = .feature-deps.$(ndims)d$(suffix).mk
$(FEATURE_DEPS): $(SRCS) $(INCS) utils/gen_feature_deps.py
@python3 utils/gen_feature_deps.py \
--obj-suffix .$(ndims)d$(suffix).o \
--stamp-prefix .build-flags- --stamp-suffix .$(ndims)d$(suffix) \
--sources $(SRCS) --headers $(INCS) -- $(FEATURES) > $@.tmp \
&& mv $@.tmp $@ \
|| { rm -f $@.tmp; \
echo " feature map unavailable -- every object depends on every feature" >&2; \
{ echo '$$(OBJS): $$(FEATURE_STAMPS)'; \
$(foreach f,$(FEATURES),echo 'FEATURE_$(f)_OBJS = $$(OBJS)';) } > $@; }
## Build goals only: under `make clean` this would generate the map just to
## delete it, and warn about a missing python3 during a clean.
ifeq (,$(filter clean cleanall deepclean config check-deps show-exe,$(MAKECMDGOALS)))
-include $(FEATURE_DEPS)
endif
## The map for the current flags -- first thing to check on a surprise rebuild.
.PHONY: feature-map
feature-map: $(FEATURE_DEPS)
@cat $<
FORCE:
## Name the missing dependency and the command that installs it, rather than
## letting it surface as a linker error hundreds of lines down. A recipe, not
## $(error), so `make clean` and `make config` still work on a machine that
## cannot build yet.
##
## Fatal on macOS only: there this detection is the sole source of -I/-L and
## there is no system Boost nor Apple omp.h, so a miss is certain. Elsewhere the
## toolchain has paths no filesystem probe can see (CPATH, LIBRARY_PATH, a
## module sysroot), so a failed guess must not block a working build.
ifeq ($(OSNAME), Darwin)
DEP_FATAL = exit 1
BOOST_INSTALL_HINT = brew install boost
else
DEP_FATAL = echo " (continuing: your compiler may find it via CPATH or a module)"
BOOST_INSTALL_HINT = apt install libboost-program-options-dev / dnf install boost-devel
endif
check-deps:
@test -n "$(CXX_SUPPORTED)" || { \
echo "Unsupported compiler '$(CXX)': no flags would be set. Use clang++,"; \
echo "g++, icpc, nvc++ or pgc++, e.g. make CXX=g++."; exit 1; }
@test -f "$(BOOST_ROOT_DIR)/include/boost/program_options.hpp" \
|| test -f "$(BOOST_ROOT_DIR)/boost/program_options.hpp" \
|| test -f /usr/include/boost/program_options.hpp || { \
echo "Missing: Boost.ProgramOptions. Install: $(BOOST_INSTALL_HINT)"; \
echo " Searched: $(strip $(BOOST_PREFIXES)) /usr"; \
echo " Or name one: make BOOST_ROOT_DIR=/prefix (now $(BOOST_ROOT_DIR))"; \
$(DEP_FATAL); }
ifeq ($(OSNAME), Darwin)
ifeq ($(openmp), 1)
ifneq (, $(findstring clang++, $(CXX)))
@test -f "$(OPENMP_INCLUDE_DIR)/omp.h" \
&& test -f "$(OPENMP_LIB_DIR)/libomp.dylib" || { \
echo "Missing: an OpenMP runtime -- Apple clang has the pragmas, ships none."; \
echo " Install: brew install libomp (or https://brew.sh)"; \
echo " Searched: $(strip $(OPENMP_LIB_DIRS))"; \
echo " Or: make openmp=0 make OPENMP_ROOT_DIR=/prefix"; \
echo " make BREW_PREFIX=/your/prefix (now $(BREW_PREFIX))"; \
exit 1; }
endif
endif
endif