-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathpackage.py
executable file
·336 lines (284 loc) · 12.1 KB
/
package.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
from spack.pkg.builtin.icon import Icon as SpackIcon
import os
import re
import glob
from collections import defaultdict
from llnl.util import tty
from spack.util.environment import is_system_path
import spack.error as error
def check_variant_fcgroup(fcgroup):
pattern = re.compile(r"^[A-Z]+\..+\..")
# fcgroup is False as default
if pattern.match(fcgroup) or fcgroup == 'none':
return True
else:
tty.warn('Variant fcgroup needs format GROUP.files.flag')
return False
def check_variant_extra_config_args(extra_config_arg):
pattern = re.compile(r'--(enable|disable)-\S+')
if pattern.match(extra_config_arg) or extra_config_arg == 'none':
return True
else:
tty.warn(
f'The value "{extra_config_arg}" for the extra_config_args variant must follow the format "--enable-arg" or "--disable-arg"'
)
return False
class Icon(SpackIcon):
git = '[email protected]:icon/icon-nwp.git'
maintainers('jonasjucker', 'huppd')
version('develop', submodules=True)
version("2024.01-1",
tag="icon-2024.01-1",
git='[email protected]:icon/icon.git',
submodules=True)
version('2.6.6-mch2b', tag='icon-nwp/icon-2.6.6-mch2b', submodules=True)
version('2.6.6-mch2a', tag='icon-nwp/icon-2.6.6-mch2a', submodules=True)
version('nwp-master', submodules=True)
# The variants' default follow those of ICON
# as described here
# https://gitlab.dkrz.de/icon/icon/-/blob/icon-2024.01/configure?ref_type=tags#L1492-1638
# Model Features:
variant('dace',
default=False,
description='Enable the DACE modules for data assimilation')
requires("+mpi", when="+dace")
variant('emvorado',
default=False,
description='Enable the radar forward operator EMVORADO')
requires("+mpi", when="+emvorado")
variant('art-gpl',
default=False,
description='Enable GPL-licensed code parts of the ART component')
variant(
'acm-license',
default=False,
description=
'Enable code parts that require accepting the ACM Software License')
# Infrastructural Features:
variant(
'active-target-sync',
default=False,
description=
'Enable MPI active target mode (otherwise, passive target mode is used)'
)
variant('async-io-rma',
default=True,
description='Enable remote memory access (RMA) for async I/O')
variant('realloc-buf',
default=False,
description='Enable reallocatable communication buffer')
variant('sct', default=False, description='Enable the SCT timer')
variant(
'extra-config-args',
default='none',
multi=True,
values=check_variant_extra_config_args,
description=
'Inject any configure argument not yet available as variant\nUse this feature cautiously, as injecting non-variant configure arguments may potentially disrupt the build process'
)
# Optimization Features:
variant('loop-exchange', default=False, description='Enable loop exchange')
variant('vectorized-lrtm',
default=False,
description='Enable the parallelization-invariant version of LRTM')
variant(
'pgi-inlib',
default=False,
description=
'Enable PGI/NVIDIA cross-file function inlining via an inline library')
variant('nccl', default=False, description='Enable NCCL for communication')
variant('cuda-graphs', default=False, description='Enable CUDA graphs.')
requires('%[email protected]:', when='+cuda-graphs')
variant(
'fcgroup',
default='none',
multi=True,
values=check_variant_fcgroup,
description=
'Create a Fortran compile group: GROUP;files;flag \nNote: flag can only be one single value, i.e. -O1'
)
# verbosity
variant('silent-rules',
default=True,
description='Enable silent-rules for build-process')
variant(
'eccodes-definitions',
default=False,
description=
'Enable extension of eccodes with center specific definition files')
depends_on('cosmo-eccodes-definitions',
type='run',
when='+eccodes-definitions')
with when('+emvorado'):
depends_on('eccodes +fortran')
depends_on('hdf5 +szip +hl +fortran')
depends_on('zlib-ng')
# WORKAROUND: A build and link dependency should imply that the same compiler is used. This enforces it.
depends_on('eccodes %nvhpc', when='%nvhpc')
depends_on('eccodes %gcc', when='%gcc')
# WORKAROUND: A build and link dependency should imply that the same compiler is used. This enforces it.
for __x in SpackIcon.serialization_values:
depends_on('serialbox+fortran %nvhpc',
when='serialization={0} %nvhpc'.format(__x))
depends_on('serialbox+fortran %gcc',
when='serialization={0} %gcc'.format(__x))
# WORKAROUND: A build and link dependency should imply that the same compiler is used. This enforces it.
depends_on('netcdf-fortran %nvhpc', when='%nvhpc')
depends_on('netcdf-fortran %gcc', when='%gcc')
depends_on('hdf5 +szip', when='+sct')
# patch_libtool is a function from Autotoolspackage.
# For BB we cannot use it because it finds all files
# named "libtool". spack-c2sm is cloned into icon-repo,
# therefore this function detects not only "libtool" files, but
# also the folder where libtool package itself is installed.
patch_libtool = False
def configure_args(self):
args = super().configure_args()
super_libs = args.pop()
libs = LibraryList([])
flags = defaultdict(list)
for x in [
'dace',
'emvorado',
'art-gpl',
'acm-license',
'active-target-sync',
'async-io-rma',
'realloc-buf',
'parallel-netcdf',
'sct',
'loop-exchange',
'vectorized-lrtm',
'pgi-inlib',
'nccl',
'cuda-graphs',
'silent-rules',
]:
args += self.enable_or_disable(x)
if '+emvorado' in self.spec:
libs += self.spec['eccodes:fortran'].libs
libs += self.spec['hdf5:fortran,hl'].libs
libs += self.spec['zlib-ng'].libs
if '+sct' in self.spec:
libs += self.spec['hdf5'].libs
fcgroup = self.spec.variants['fcgroup'].value
# ('none',) is the values spack assign if fcgroup is not set
if fcgroup != ('none', ):
args.extend(self.fcgroup_to_config_arg())
flags.update(self.fcgroup_to_config_var())
# add configure arguments not yet available as variant
extra_config_args = self.spec.variants['extra-config-args'].value
if extra_config_args != ('none', ):
for x in extra_config_args:
# prevent configure-args already available as variant
# to be set through variant extra_config_args
self.validate_extra_config_args(x)
args.append(x)
tty.warn(
'You use variant extra-config-args. Injecting non-variant configure arguments may potentially disrupt the build process!'
)
# Finalize the LIBS variable (we always put the real collected
# libraries to the front):
flags['LIBS'].insert(0, libs.link_flags)
# Help the libtool scripts of the bundled libraries find the correct
# paths to the external libraries. Specify the library search (-L) flags
# in the reversed order
# (see https://gitlab.dkrz.de/icon/icon#icon-dependencies):
# and for non-system directories only:
flags['LDFLAGS'].extend([
'-L{0}'.format(d) for d in reversed(libs.directories)
if not is_system_path(d)
])
args.extend([
"{0}={1}".format(name, " ".join(value))
for name, value in flags.items()
])
args.append(f"{super_libs} {libs.link_flags}")
return args
def fcgroup_to_config_arg(self):
arg = []
for group in self.spec.variants['fcgroup'].value:
name = group.split('.')[0]
files = group.split('.')[1]
arg.append(f'--enable-fcgroup-{name}={files}')
return arg
def fcgroup_to_config_var(self):
var = {}
for group in self.spec.variants['fcgroup'].value:
name = group.split('.')[0]
flag = group.split('.')[2]
# Note: flag needs to be a list
var[f'ICON_{name}_FCFLAGS'] = [flag]
return var
def strip_variant_prefix(self, variant_string):
prefixes = ["--enable-", "--disable-"]
for prefix in prefixes:
if variant_string.startswith(prefix):
return variant_string[len(prefix):]
raise ValueError
def validate_extra_config_args(self, arg):
variant_from_arg = self.strip_variant_prefix(arg)
if variant_from_arg in self.spec.variants:
raise error.SpecError(
f'The value "{arg}" for the extra_config_args variant conflicts '
f'with the existing variant {variant_from_arg}. Set this variant instead.'
)
def configure(self, spec, prefix):
if os.path.exists(
os.path.join(self.build_directory,
'icon.mk')) and self.build_uses_same_spec():
tty.warn(
'icon.mk already present -> skip configure stage',
'\t delete "icon.mk" or run "make distclean" to not skip configure'
)
return
# Call configure of Autotools
super().configure(spec, prefix)
def build_uses_same_spec(self):
"""
Ensure that configure is rerun in case spec has changed,
otherwise for the case below
$ spack dev-build icon @develop ~dace
$ spack dev-build icon @develop +dace
configure is skipped for the latter.
"""
is_same_spec = False
previous_spec = os.path.join(self.build_directory,
'.previous_spec.yaml')
# not the first build in self.build_directory
if os.path.exists(previous_spec):
with open(previous_spec, mode='r') as f:
if self.spec == Spec.from_yaml(f):
is_same_spec = True
else:
is_same_spec = False
tty.warn(
'Cannot skip configure phase because spec changed')
# first build in self.build_directory, no worries
else:
is_same_spec = True
# dump spec of new build
with open(previous_spec, mode='w') as f:
f.write(self.spec.to_yaml())
return is_same_spec
@run_after('configure')
def copy_runscript_related_input_files(self):
with working_dir(self.build_directory):
icon_dir = self.configure_directory
# only synchronize if out-of-source build
if os.path.abspath(icon_dir) != os.path.abspath(
self.build_directory):
Rsync = which('rsync', required=True)
Rsync("-uavz", f"{icon_dir}/run", ".", "--exclude=*.in",
"--exclude=.*", "--exclude=standard_*")
Rsync("-uavz", f"{icon_dir}/externals", ".", "--exclude=.git",
"--exclude=*.f90", "--exclude=*.F90", "--exclude=*.c",
"--exclude=*.h", "--exclude=*.Po", "--exclude=tests",
"--exclude=*.mod", "--exclude=*.o")
Rsync("-uavz", f"{icon_dir}/make_runscripts", ".")
Ln = which('ln', required=True)
dirs = glob.glob(f"{icon_dir}/run/standard_*")
for dir in dirs:
Ln("-sf", "-t", "run/", f"{dir}")
Ln("-sf", f"{icon_dir}/data")
Ln("-sf", f"{icon_dir}/vertical_coord_tables")