forked from easybuilders/easybuild-framework
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathoptions.py
More file actions
1733 lines (1484 loc) · 94.7 KB
/
options.py
File metadata and controls
1733 lines (1484 loc) · 94.7 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
##
# Copyright 2009-2020 Ghent University
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
# with support of Ghent University (http://ugent.be/hpc),
# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be),
# Flemish Research Foundation (FWO) (http://www.fwo.be/en)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# https://github.com/easybuilders/easybuild
#
# EasyBuild 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 v2.
#
# EasyBuild 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 EasyBuild. If not, see <http://www.gnu.org/licenses/>.
##
"""
Command line options for eb
:author: Stijn De Weirdt (Ghent University)
:author: Dries Verdegem (Ghent University)
:author: Kenneth Hoste (Ghent University)
:author: Pieter De Baets (Ghent University)
:author: Jens Timmerman (Ghent University)
:author: Toon Willems (Ghent University)
:author: Ward Poelmans (Ghent University)
:author: Damian Alvarez (Forschungszentrum Juelich GmbH)
"""
import copy
import glob
import os
import re
import shutil
import sys
import tempfile
import pwd
from distutils.version import LooseVersion
import easybuild.tools.environment as env
from easybuild.base import fancylogger # build_log should always stay there, to ensure EasyBuildLog
from easybuild.base.fancylogger import setLogLevel
from easybuild.base.generaloption import GeneralOption
from easybuild.framework.easyblock import MODULE_ONLY_STEPS, SOURCE_STEP, FETCH_STEP, EasyBlock
from easybuild.framework.easyconfig import EASYCONFIGS_PKG_SUBDIR
from easybuild.framework.easyconfig.easyconfig import HAVE_AUTOPEP8
from easybuild.framework.easyconfig.format.one import EB_FORMAT_EXTENSION
from easybuild.framework.easyconfig.format.pyheaderconfigobj import build_easyconfig_constants_dict
from easybuild.framework.easyconfig.format.yeb import YEB_FORMAT_EXTENSION
from easybuild.framework.easyconfig.tools import alt_easyconfig_paths, get_paths_for
from easybuild.toolchains.compiler.systemcompiler import TC_CONSTANT_SYSTEM
from easybuild.tools import build_log, run # build_log should always stay there, to ensure EasyBuildLog
from easybuild.tools.build_log import DEVEL_LOG_LEVEL, EasyBuildError
from easybuild.tools.build_log import init_logging, log_start, print_msg, print_warning, raise_easybuilderror
from easybuild.tools.config import CONT_IMAGE_FORMATS, CONT_TYPES, DEFAULT_CONT_TYPE, DEFAULT_ALLOW_LOADED_MODULES
from easybuild.tools.config import DEFAULT_BRANCH, DEFAULT_FORCE_DOWNLOAD, DEFAULT_INDEX_MAX_AGE
from easybuild.tools.config import DEFAULT_JOB_BACKEND, DEFAULT_LOGFILE_FORMAT, DEFAULT_MAX_FAIL_RATIO_PERMS
from easybuild.tools.config import DEFAULT_MINIMAL_BUILD_ENV, DEFAULT_MNS, DEFAULT_MODULE_SYNTAX, DEFAULT_MODULES_TOOL
from easybuild.tools.config import DEFAULT_MODULECLASSES, DEFAULT_PATH_SUBDIRS, DEFAULT_PKG_RELEASE, DEFAULT_PKG_TOOL
from easybuild.tools.config import DEFAULT_PKG_TYPE, DEFAULT_PNS, DEFAULT_PREFIX, DEFAULT_PR_TARGET_ACCOUNT
from easybuild.tools.config import DEFAULT_REPOSITORY, DEFAULT_WAIT_ON_LOCK_INTERVAL, DEFAULT_WAIT_ON_LOCK_LIMIT
from easybuild.tools.config import EBROOT_ENV_VAR_ACTIONS, ERROR, FORCE_DOWNLOAD_CHOICES, GENERAL_CLASS, IGNORE
from easybuild.tools.config import JOB_DEPS_TYPE_ABORT_ON_ERROR, JOB_DEPS_TYPE_ALWAYS_RUN, LOADED_MODULES_ACTIONS
from easybuild.tools.config import LOCAL_VAR_NAMING_CHECK_WARN, LOCAL_VAR_NAMING_CHECKS, WARN
from easybuild.tools.config import get_pretend_installpath, init, init_build_options, mk_full_default_path
from easybuild.tools.configobj import ConfigObj, ConfigObjError
from easybuild.tools.docs import FORMAT_TXT, FORMAT_RST
from easybuild.tools.docs import avail_cfgfile_constants, avail_easyconfig_constants, avail_easyconfig_licenses
from easybuild.tools.docs import avail_toolchain_opts, avail_easyconfig_params, avail_easyconfig_templates
from easybuild.tools.docs import list_easyblocks, list_toolchains
from easybuild.tools.environment import restore_env, unset_env_vars
from easybuild.tools.filetools import CHECKSUM_TYPE_SHA256, CHECKSUM_TYPES, expand_glob_paths, install_fake_vsc
from easybuild.tools.filetools import move_file, which
from easybuild.tools.github import GITHUB_PR_DIRECTION_DESC, GITHUB_PR_ORDER_CREATED
from easybuild.tools.github import GITHUB_PR_STATE_OPEN, GITHUB_PR_STATES, GITHUB_PR_ORDERS, GITHUB_PR_DIRECTIONS
from easybuild.tools.github import HAVE_GITHUB_API, HAVE_KEYRING, VALID_CLOSE_PR_REASONS
from easybuild.tools.github import fetch_easyblocks_from_pr, fetch_github_token
from easybuild.tools.hooks import KNOWN_HOOKS
from easybuild.tools.include import include_easyblocks, include_module_naming_schemes, include_toolchains
from easybuild.tools.job.backend import avail_job_backends
from easybuild.tools.modules import avail_modules_tools
from easybuild.tools.module_generator import ModuleGeneratorLua, avail_module_generators
from easybuild.tools.module_naming_scheme.utilities import avail_module_naming_schemes
from easybuild.tools.modules import Lmod
from easybuild.tools.py2vs3 import OrderedDict, string_type
from easybuild.tools.robot import det_robot_path
from easybuild.tools.run import run_cmd
from easybuild.tools.package.utilities import avail_package_naming_schemes
from easybuild.tools.toolchain.compiler import DEFAULT_OPT_LEVEL, OPTARCH_MAP_CHAR, OPTARCH_SEP, Compiler
from easybuild.tools.toolchain.toolchain import SYSTEM_TOOLCHAIN_NAME
from easybuild.tools.repository.repository import avail_repositories
from easybuild.tools.systemtools import UNKNOWN, check_python_version, get_cpu_architecture, get_cpu_family
from easybuild.tools.systemtools import get_cpu_features, get_system_info
from easybuild.tools.version import this_is_easybuild
try:
from humanfriendly.terminal import terminal_supports_colors
except ImportError:
# provide an approximation that should work in most cases
def terminal_supports_colors(stream):
try:
return os.isatty(stream.fileno())
except Exception:
# in case of errors do not bother and just return the safe default
return False
CONFIG_ENV_VAR_PREFIX = 'EASYBUILD'
XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), ".config"))
XDG_CONFIG_DIRS = os.environ.get('XDG_CONFIG_DIRS', '/etc').split(os.pathsep)
DEFAULT_SYS_CFGFILES = [f for d in XDG_CONFIG_DIRS for f in sorted(glob.glob(os.path.join(d, 'easybuild.d', '*.cfg')))]
DEFAULT_USER_CFGFILE = os.path.join(XDG_CONFIG_HOME, 'easybuild', 'config.cfg')
DEFAULT_LIST_PR_STATE = GITHUB_PR_STATE_OPEN
DEFAULT_LIST_PR_ORDER = GITHUB_PR_ORDER_CREATED
DEFAULT_LIST_PR_DIREC = GITHUB_PR_DIRECTION_DESC
_log = fancylogger.getLogger('options', fname=False)
def cleanup_and_exit(tmpdir):
"""
Clean up temporary directory and exit.
:param tmpdir: path to temporary directory to clean up
"""
try:
shutil.rmtree(tmpdir)
except OSError as err:
raise EasyBuildError("Failed to clean up temporary directory %s: %s", tmpdir, err)
sys.exit(0)
def pretty_print_opts(opts_dict):
"""
Pretty print options dict.
:param opts_dict: dictionary with option names as keys, and (value, location) tuples as values
"""
# rewrite option names/values a bit for pretty printing
for opt in sorted(opts_dict):
opt_val, loc = opts_dict[opt]
if opt_val == '':
opt_val = "''"
elif isinstance(opt_val, list):
opt_val = ', '.join(opt_val)
opts_dict[opt] = (opt_val, loc)
# determine max width or option names
nwopt = max([len(opt) for opt in opts_dict])
# header
lines = [
'#',
"# Current EasyBuild configuration",
"# (C: command line argument, D: default value, E: environment variable, F: configuration file)",
'#',
]
# add one line per retained option
for opt in sorted(opts_dict):
opt_val, loc = opts_dict[opt]
lines.append("{0:<{nwopt}} ({1:}) = {2:}".format(opt, loc, opt_val, nwopt=nwopt))
print('\n'.join(lines))
def use_color(colorize, stream=sys.stdout):
"""
Return ``True`` or ``False`` depending on whether ANSI color
escapes are to be used when printing to `stream`.
The `colorize` argument can take the three values
``fancylogger.Colorize.AUTO``/``.ALWAYS``/``.NEVER``,
see the ``--color`` option for their meaning.
"""
# turn color=auto/yes/no into a boolean value
if colorize == fancylogger.Colorize.AUTO:
return terminal_supports_colors(stream)
elif colorize == fancylogger.Colorize.ALWAYS:
return True
else:
assert colorize == fancylogger.Colorize.NEVER, \
"Argument `colorize` must be one of: %s" % ', '.join(fancylogger.Colorize)
return False
class EasyBuildOptions(GeneralOption):
"""Easybuild generaloption class"""
VERSION = this_is_easybuild()
DEFAULT_LOGLEVEL = 'INFO'
DEFAULT_CONFIGFILES = DEFAULT_SYS_CFGFILES[:]
if os.path.exists(DEFAULT_USER_CFGFILE):
DEFAULT_CONFIGFILES.append(DEFAULT_USER_CFGFILE)
ALLOPTSMANDATORY = False # allow more than one argument
CONFIGFILES_RAISE_MISSING = True # don't allow non-existing config files to be specified
def __init__(self, *args, **kwargs):
"""Constructor."""
self.with_include = kwargs.pop('with_include', True)
self.single_cfg_level = kwargs.pop('single_cfg_level', False)
self.default_repositorypath = [mk_full_default_path('repositorypath')]
self.default_robot_paths = get_paths_for(subdir=EASYCONFIGS_PKG_SUBDIR, robot_path=None) or []
# set up constants to seed into config files parser, by section
self.go_cfg_constants = {
self.DEFAULTSECT: {
'DEFAULT_REPOSITORYPATH': (self.default_repositorypath[0],
"Default easyconfigs repository path"),
'DEFAULT_ROBOT_PATHS': (os.pathsep.join(self.default_robot_paths),
"List of default robot paths ('%s'-separated)" % os.pathsep),
'USER': (pwd.getpwuid(os.geteuid()).pw_name,
"Current username, translated uid from password file"),
'HOME': (os.path.expanduser('~'),
"Current user's home directory, expanded '~'")
}
}
# update or define go_configfiles_initenv in named arguments to pass to parent constructor
go_cfg_initenv = kwargs.setdefault('go_configfiles_initenv', {})
for section, constants in self.go_cfg_constants.items():
constants = dict([(name, value) for (name, (value, _)) in constants.items()])
go_cfg_initenv.setdefault(section, {}).update(constants)
super(EasyBuildOptions, self).__init__(*args, **kwargs)
def basic_options(self):
"""basic runtime options"""
all_stops = [x[0] for x in EasyBlock.get_steps()]
strictness_options = [IGNORE, WARN, ERROR]
descr = ("Basic options", "Basic runtime options for EasyBuild.")
opts = OrderedDict({
'dry-run': ("Print build overview incl. dependencies (full paths)", None, 'store_true', False),
'dry-run-short': ("Print build overview incl. dependencies (short paths)", None, 'store_true', False, 'D'),
'extended-dry-run': ("Print build environment and (expected) build procedure that will be performed",
None, 'store_true', False, 'x'),
'extended-dry-run-ignore-errors': ("Ignore errors that occur during dry run", None, 'store_true', True),
'force': ("Force to rebuild software even if it's already installed (i.e. if it can be found as module), "
"and skipping check for OS dependencies", None, 'store_true', False, 'f'),
'ignore-locks': ("Ignore locks that prevent two identical installations running in parallel",
None, 'store_true', False),
'job': ("Submit the build as a job", None, 'store_true', False),
'logtostdout': ("Redirect main log to stdout", None, 'store_true', False, 'l'),
'locks-dir': ("Directory to store lock files (should be on a shared filesystem); "
"None implies .locks subdirectory of software installation directory",
None, 'store_or_None', None),
'missing-modules': ("Print list of missing modules for dependencies of specified easyconfigs",
None, 'store_true', False, 'M'),
'only-blocks': ("Only build listed blocks", 'strlist', 'extend', None, 'b', {'metavar': 'BLOCKS'}),
'rebuild': ("Rebuild software, even if module already exists (don't skip OS dependencies checks)",
None, 'store_true', False),
'robot': ("Enable dependency resolution, using easyconfigs in specified paths",
'pathlist', 'store_or_None', [], 'r', {'metavar': 'PATH[%sPATH]' % os.pathsep}),
'robot-paths': ("Additional paths to consider by robot for easyconfigs (--robot paths get priority)",
'pathlist', 'add_flex', self.default_robot_paths, {'metavar': 'PATH[%sPATH]' % os.pathsep}),
'search-paths': ("Additional locations to consider in --search (next to --robot and --robot-paths paths)",
'pathlist', 'store_or_None', [], {'metavar': 'PATH[%sPATH]' % os.pathsep}),
'skip': ("Skip existing software (useful for installing additional packages)",
None, 'store_true', False, 'k'),
'stop': ("Stop the installation after certain step",
'choice', 'store_or_None', SOURCE_STEP, 's', all_stops),
'strict': ("Set strictness level", 'choice', 'store', WARN, strictness_options),
})
self.log.debug("basic_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def software_options(self):
# software build options
descr = ("Software search and build options",
("Specify software search and build options: EasyBuild will search for a "
"matching easyconfig and build it. When called with the try prefix "
"(i.e. --try-X ), EasyBuild will search for a matching easyconfig "
"and if none are found, try to generate one based on a close matching one "
"(NOTE: --try-X is best effort, it might produce wrong builds!)")
)
opts = OrderedDict({
'amend': (("Specify additional search and build parameters (can be used multiple times); "
"for example: versionprefix=foo or patches=one.patch,two.patch)"),
None, 'append', None, {'metavar': 'VAR=VALUE[,VALUE]'}),
'software': ("Search and build software with given name and version",
'strlist', 'extend', None, {'metavar': 'NAME,VERSION'}),
'software-name': ("Search and build software with given name",
None, 'store', None, {'metavar': 'NAME'}),
'software-version': ("Search and build software with given version",
None, 'store', None, {'metavar': 'VERSION'}),
'toolchain': ("Search and build with given toolchain (name and version)",
'strlist', 'extend', None, {'metavar': 'NAME,VERSION'}),
'toolchain-name': ("Search and build with given toolchain name",
None, 'store', None, {'metavar': 'NAME'}),
'toolchain-version': ("Search and build with given toolchain version",
None, 'store', None, {'metavar': 'VERSION'}),
})
for longopt in list(opts):
hlp = opts[longopt][0]
hlp = "Try to %s (USE WITH CARE!)" % (hlp[0].lower() + hlp[1:])
opts["try-%s" % longopt] = (hlp,) + opts[longopt][1:]
opts['map-toolchains'] = ("Enable mapping of (sub)toolchains when --try-toolchain(-version) is used",
None, 'store_true', True)
opts['try-update-deps'] = ("Try to update versions of the dependencies of an easyconfig based on what is "
"available in the robot path",
None, 'store_true', False)
opts['try-ignore-versionsuffixes'] = ("Ignore versionsuffix differences when --try-update-deps is used",
None, 'store_true', False)
self.log.debug("software_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def override_options(self):
# override options
descr = ("Override options", "Override default EasyBuild behavior.")
opts = OrderedDict({
'add-dummy-to-minimal-toolchains': ("Include dummy toolchain in minimal toolchain searches "
"[DEPRECATED, use --add-system-to-minimal-toolchains instead!)",
None, 'store_true', False),
'add-system-to-minimal-toolchains': ("Include system toolchain in minimal toolchain searches",
None, 'store_true', False),
'allow-loaded-modules': ("List of software names for which to allow loaded modules in initial environment",
'strlist', 'store', DEFAULT_ALLOW_LOADED_MODULES),
'allow-modules-tool-mismatch': ("Allow mismatch of modules tool and definition of 'module' function",
None, 'store_true', False),
'allow-use-as-root-and-accept-consequences': ("Allow using of EasyBuild as root (NOT RECOMMENDED!)",
None, 'store_true', False),
'backup-modules': ("Back up an existing module file, if any. Only works when using --module-only",
None, 'store_true', None), # default None to allow auto-enabling if not disabled
'check-ebroot-env-vars': ("Action to take when defined $EBROOT* environment variables are found "
"for which there is no matching loaded module; "
"supported values: %s" % ', '.join(EBROOT_ENV_VAR_ACTIONS), None, 'store', WARN),
'cleanup-builddir': ("Cleanup build dir after successful installation.", None, 'store_true', True),
'cleanup-tmpdir': ("Cleanup tmp dir after successful run.", None, 'store_true', True),
'color': ("Colorize output", 'choice', 'store', fancylogger.Colorize.AUTO, fancylogger.Colorize,
{'metavar': 'WHEN'}),
'consider-archived-easyconfigs': ("Also consider archived easyconfigs", None, 'store_true', False),
'containerize': ("Generate container recipe/image", None, 'store_true', False, 'C'),
'copy-ec': ("Copy specified easyconfig(s) to specified location", None, 'store_true', False),
'cuda-compute-capabilities': ("List of CUDA compute capabilities to use when building GPU software",
'strlist', 'extend', None),
'debug-lmod': ("Run Lmod modules tool commands in debug module", None, 'store_true', False),
'default-opt-level': ("Specify default optimisation level", 'choice', 'store', DEFAULT_OPT_LEVEL,
Compiler.COMPILER_OPT_FLAGS),
'deprecated': ("Run pretending to be (future) version, to test removal of deprecated code.",
None, 'store', None),
'detect-loaded-modules': ("Detect loaded EasyBuild-generated modules, act accordingly; "
"supported values: %s" % ', '.join(LOADED_MODULES_ACTIONS), None, 'store', WARN),
'devel': ("Enable including of development log messages", None, 'store_true', False),
'download-timeout': ("Timeout for initiating downloads (in seconds)", float, 'store', None),
'dump-autopep8': ("Reformat easyconfigs using autopep8 when dumping them", None, 'store_true', False),
'easyblock': ("easyblock to use for processing the spec file or dumping the options",
None, 'store', None, 'e', {'metavar': 'CLASS'}),
'enforce-checksums': ("Enforce availability of checksums for all sources/patches, so they can be verified",
None, 'store_true', False),
'experimental': ("Allow experimental code (with behaviour that can be changed/removed at any given time).",
None, 'store_true', False),
'extra-modules': ("List of extra modules to load after setting up the build environment",
'strlist', 'extend', None),
'fetch': ("Allow downloading sources ignoring OS and modules tool dependencies, "
"implies --stop=fetch, --ignore-osdeps and ignore modules tool", None, 'store_true', False),
'filter-deps': ("List of dependencies that you do *not* want to install with EasyBuild, "
"because equivalent OS packages are installed. (e.g. --filter-deps=zlib,ncurses)",
'strlist', 'extend', None),
'filter-env-vars': ("List of names of environment variables that should *not* be defined/updated by "
"module files generated by EasyBuild", 'strlist', 'extend', None),
'fixed-installdir-naming-scheme': ("Use fixed naming scheme for installation directories", None,
'store_true', True),
'force-download': ("Force re-downloading of sources and/or patches, "
"even if they are available already in source path",
'choice', 'store_or_None', DEFAULT_FORCE_DOWNLOAD, FORCE_DOWNLOAD_CHOICES),
'group': ("Group to be used for software installations (only verified, not set)", None, 'store', None),
'group-writable-installdir': ("Enable group write permissions on installation directory after installation",
None, 'store_true', False),
'hidden': ("Install 'hidden' module file(s) by prefixing their version with '.'",
None, 'store_true', False),
'hide-deps': ("Comma separated list of dependencies that you want automatically hidden, "
"(e.g. --hide-deps=zlib,ncurses)", 'strlist', 'extend', None),
'hide-toolchains': ("Comma separated list of toolchains that you want automatically hidden, "
"(e.g. --hide-toolchains=GCCcore)", 'strlist', 'extend', None),
'ignore-checksums': ("Ignore failing checksum verification", None, 'store_true', False),
'ignore-osdeps': ("Ignore any listed OS dependencies", None, 'store_true', False),
'install-latest-eb-release': ("Install latest known version of easybuild", None, 'store_true', False),
'lib64-fallback-sanity-check': ("Fallback in sanity check to lib64/ equivalent for missing libraries",
None, 'store_true', True),
'lib64-lib-symlink': ("Automatically create symlinks for lib64/ pointing to lib/ if the former is missing",
None, 'store_true', True),
'max-fail-ratio-adjust-permissions': ("Maximum ratio for failures to allow when adjusting permissions",
'float', 'store', DEFAULT_MAX_FAIL_RATIO_PERMS),
'minimal-build-env': ("Minimal build environment to define when using system toolchain, "
"specified as a comma-separated list that defines a mapping between name of "
"environment variable and its value separated by a colon (':')",
None, 'store', DEFAULT_MINIMAL_BUILD_ENV),
'minimal-toolchains': ("Use minimal toolchain when resolving dependencies", None, 'store_true', False),
'module-only': ("Only generate module file(s); skip all steps except for %s" % ', '.join(MODULE_ONLY_STEPS),
None, 'store_true', False),
'modules-tool-version-check': ("Check version of modules tool being used", None, 'store_true', True),
'mpi-cmd-template': ("Template for MPI commands (template keys: %(nr_ranks)s, %(cmd)s)",
None, 'store', None),
'mpi-tests': ("Run MPI tests (when relevant)", None, 'store_true', True),
'optarch': ("Set architecture optimization, overriding native architecture optimizations",
None, 'store', None),
'output-format': ("Set output format", 'choice', 'store', FORMAT_TXT, [FORMAT_TXT, FORMAT_RST]),
'parallel': ("Specify (maximum) level of parallellism used during build procedure",
'int', 'store', None),
'pre-create-installdir': ("Create installation directory before submitting build jobs",
None, 'store_true', True),
'pretend': (("Does the build/installation in a test directory located in $HOME/easybuildinstall"),
None, 'store_true', False, 'p'),
'read-only-installdir': ("Set read-only permissions on installation directory after installation",
None, 'store_true', False),
'remove-ghost-install-dirs': ("Remove ghost installation directories when --force or --rebuild is used, "
"rather than just warning about them",
None, 'store_true', False),
'rpath': ("Enable use of RPATH for linking with libraries", None, 'store_true', False),
'rpath-filter': ("List of regex patterns to use for filtering out RPATH paths", 'strlist', 'store', None),
'set-default-module': ("Set the generated module as default", None, 'store_true', False),
'set-gid-bit': ("Set group ID bit on newly created directories", None, 'store_true', False),
'silence-deprecation-warnings': ("Silence specified deprecation warnings", 'strlist', 'extend', None),
'sticky-bit': ("Set sticky bit on newly created directories", None, 'store_true', False),
'skip-test-cases': ("Skip running test cases", None, 'store_true', False, 't'),
'generate-devel-module': ("Generate a develop module file, implies --force if disabled",
None, 'store_true', True),
'sysroot': ("Location root directory of system, prefix for standard paths like /usr/lib and /usr/include",
None, 'store', None),
'trace': ("Provide more information in output to stdout on progress", None, 'store_true', False, 'T'),
'umask': ("umask to use (e.g. '022'); non-user write permissions on install directories are removed",
None, 'store', None),
'update-modules-tool-cache': ("Update modules tool cache file(s) after generating module file",
None, 'store_true', False),
'use-ccache': ("Enable use of ccache to speed up compilation, with specified cache dir",
str, 'store', False, {'metavar': "PATH"}),
'use-f90cache': ("Enable use of f90cache to speed up compilation, with specified cache dir",
str, 'store', False, {'metavar': "PATH"}),
'use-existing-modules': ("Use existing modules when resolving dependencies with minimal toolchains",
None, 'store_true', False),
'verify-easyconfig-filenames': ("Verify whether filename of specified easyconfigs matches with contents",
None, 'store_true', False),
'wait-on-lock': ("Wait for lock to be released; 0 implies no waiting (exit with an error if the lock "
"already exists), non-zero value specified waiting interval [DEPRECATED: "
"use --wait-on-lock-interval and --wait-on-lock-limit instead]",
int, 'store_or_None', None),
'wait-on-lock-interval': ("Wait interval (in seconds) to use when waiting for existing lock to be removed",
int, 'store', DEFAULT_WAIT_ON_LOCK_INTERVAL),
'wait-on-lock-limit': ("Maximum amount of time (in seconds) to wait until lock is released (0 means no "
"waiting at all, exit with error; -1 means no waiting limit, keep waiting)",
int, 'store', DEFAULT_WAIT_ON_LOCK_LIMIT),
'zip-logs': ("Zip logs that are copied to install directory, using specified command",
None, 'store_or_None', 'gzip'),
})
self.log.debug("override_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def config_options(self):
# config options
descr = ("Configuration options", "Configure EasyBuild behavior.")
opts = OrderedDict({
'avail-module-naming-schemes': ("Show all supported module naming schemes",
None, 'store_true', False,),
'avail-modules-tools': ("Show all supported module tools",
None, "store_true", False,),
'avail-repositories': ("Show all repository types (incl. non-usable)",
None, "store_true", False,),
'buildpath': ("Temporary build path", None, 'store', mk_full_default_path('buildpath')),
'containerpath': ("Location where container recipe & image will be stored", None, 'store',
mk_full_default_path('containerpath')),
'external-modules-metadata': ("List of (glob patterns for) paths to files specifying metadata "
"for external modules (INI format)", 'strlist', 'store', None),
'hooks': ("Location of Python module with hook implementations", 'str', 'store', None),
'ignore-dirs': ("Directory names to ignore when searching for files/dirs",
'strlist', 'store', ['.git', '.svn']),
'include-easyblocks': ("Location(s) of extra or customized easyblocks", 'strlist', 'store', []),
'include-module-naming-schemes': ("Location(s) of extra or customized module naming schemes",
'strlist', 'store', []),
'include-toolchains': ("Location(s) of extra or customized toolchains or toolchain components",
'strlist', 'store', []),
'installpath': ("Install path for software and modules",
None, 'store', mk_full_default_path('installpath')),
'installpath-modules': ("Install path for modules (if None, combine --installpath and --subdir-modules)",
None, 'store', None),
'installpath-software': ("Install path for software (if None, combine --installpath and --subdir-software)",
None, 'store', None),
'job-backend': ("Backend to use for submitting jobs", 'choice', 'store',
DEFAULT_JOB_BACKEND, sorted(avail_job_backends().keys())),
# purposely take a copy for the default logfile format
'logfile-format': ("Directory name and format of the log file",
'strtuple', 'store', DEFAULT_LOGFILE_FORMAT[:], {'metavar': 'DIR,FORMAT'}),
'module-depends-on': ("Use depends_on (Lmod 7.6.1+) for dependencies in all generated modules "
"(implies recursive unloading of modules).",
None, 'store_true', False),
'module-extensions': ("Include 'extensions' statement in generated module file (Lua syntax only)",
None, 'store_true', False),
'module-naming-scheme': ("Module naming scheme to use", None, 'store', DEFAULT_MNS),
'module-syntax': ("Syntax to be used for module files", 'choice', 'store', DEFAULT_MODULE_SYNTAX,
sorted(avail_module_generators().keys())),
'moduleclasses': (("Extend supported module classes "
"(For more info on the default classes, use --show-default-moduleclasses)"),
'strlist', 'extend', [x[0] for x in DEFAULT_MODULECLASSES]),
'modules-footer': ("Path to file containing footer to be added to all generated module files",
None, 'store_or_None', None, {'metavar': "PATH"}),
'modules-header': ("Path to file containing header to be added to all generated module files",
None, 'store_or_None', None, {'metavar': "PATH"}),
'modules-tool': ("Modules tool to use",
'choice', 'store', DEFAULT_MODULES_TOOL, sorted(avail_modules_tools().keys())),
'packagepath': ("The destination path for the packages built by package-tool",
None, 'store', mk_full_default_path('packagepath')),
'package-naming-scheme': ("Packaging naming scheme choice",
'choice', 'store', DEFAULT_PNS, sorted(avail_package_naming_schemes().keys())),
'prefix': (("Change prefix for buildpath, installpath, sourcepath and repositorypath "
"(used prefix for defaults %s)" % DEFAULT_PREFIX),
None, 'store', None),
'recursive-module-unload': ("Enable generating of modules that unload recursively.",
None, 'store_true', False),
'repository': ("Repository type, using repositorypath",
'choice', 'store', DEFAULT_REPOSITORY, sorted(avail_repositories().keys())),
'repositorypath': (("Repository path, used by repository "
"(is passed as list of arguments to create the repository instance). "
"For more info, use --avail-repositories."),
'strlist', 'store', self.default_repositorypath),
'sourcepath': ("Path(s) to where sources should be downloaded (string, colon-separated)",
None, 'store', mk_full_default_path('sourcepath')),
'subdir-modules': ("Installpath subdir for modules", None, 'store', DEFAULT_PATH_SUBDIRS['subdir_modules']),
'subdir-software': ("Installpath subdir for software",
None, 'store', DEFAULT_PATH_SUBDIRS['subdir_software']),
'subdir-user-modules': ("Base path of user-specific modules relative to their $HOME", None, 'store', None),
'suffix-modules-path': ("Suffix for module files install path", None, 'store', GENERAL_CLASS),
# this one is sort of an exception, it's something jobscripts can set,
# has no real meaning for regular eb usage
'testoutput': ("Path to where a job should place the output (to be set within jobscript)",
None, 'store', None),
'tmp-logdir': ("Log directory where temporary log files are stored", None, 'store', None),
'tmpdir': ('Directory to use for temporary storage', None, 'store', None),
})
self.log.debug("config_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def informative_options(self):
# informative options
descr = ("Informative options", "Obtain information about EasyBuild.")
opts = OrderedDict({
'avail-cfgfile-constants': ("Show all constants that can be used in configuration files",
None, 'store_true', False),
'avail-easyconfig-constants': ("Show all constants that can be used in easyconfigs",
None, 'store_true', False),
'avail-easyconfig-licenses': ("Show all license constants that can be used in easyconfigs",
None, 'store_true', False),
'avail-easyconfig-params': (("Show all easyconfig parameters (include "
"easyblock-specific ones by using -e)"),
None, 'store_true', False, 'a'),
'avail-easyconfig-templates': (("Show all template names and template constants "
"that can be used in easyconfigs."),
None, 'store_true', False),
'avail-hooks': ("Show list of known hooks", None, 'store_true', False),
'avail-toolchain-opts': ("Show options for toolchain", 'str', 'store', None),
'check-conflicts': ("Check for version conflicts in dependency graphs", None, 'store_true', False),
'dep-graph': ("Create dependency graph", None, 'store', None, {'metavar': 'depgraph.<ext>'}),
'dump-env-script': ("Dump source script to set up build environment based on toolchain/dependencies",
None, 'store_true', False),
'last-log': ("Print location to EasyBuild log file of last (failed) session", None, 'store_true', False),
'list-easyblocks': ("Show list of available easyblocks",
'choice', 'store_or_None', 'simple', ['simple', 'detailed']),
'list-installed-software': ("Show list of installed software", 'choice', 'store_or_None', 'simple',
['simple', 'detailed']),
'list-software': ("Show list of supported software", 'choice', 'store_or_None', 'simple',
['simple', 'detailed']),
'list-toolchains': ("Show list of known toolchains",
None, 'store_true', False),
'search': ("Search for easyconfig files in the robot search path, print full paths",
None, 'store', None, {'metavar': 'REGEX'}),
'search-filename': ("Search for easyconfig files in the robot search path, print only filenames",
None, 'store', None, {'metavar': 'REGEX'}),
'search-short': ("Search for easyconfig files in the robot search path, print short paths",
None, 'store', None, 'S', {'metavar': 'REGEX'}),
'show-config': ("Show current EasyBuild configuration (only non-default + selected settings)",
None, 'store_true', False),
'show-default-configfiles': ("Show list of default config files", None, 'store_true', False),
'show-default-moduleclasses': ("Show default module classes with description",
None, 'store_true', False),
'show-ec': ("Show contents of specified easyconfig(s)", None, 'store_true', False),
'show-full-config': ("Show current EasyBuild configuration (all settings)", None, 'store_true', False),
'show-system-info': ("Show system information relevant to EasyBuild", None, 'store_true', False),
'terse': ("Terse output (machine-readable)", None, 'store_true', False),
})
self.log.debug("informative_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def github_options(self):
"""GitHub integration configuration options."""
descr = ("GitHub integration options", "Integration with GitHub")
opts = OrderedDict({
'check-github': ("Check status of GitHub integration, and report back", None, 'store_true', False),
'check-contrib': ("Runs checks to see whether the given easyconfigs are ready to be contributed back",
None, 'store_true', False),
'check-style': ("Run a style check on the given easyconfigs", None, 'store_true', False),
'cleanup-easyconfigs': ("Clean up easyconfig files for pull request", None, 'store_true', True),
'dump-test-report': ("Dump test report to specified path", None, 'store_or_None', 'test_report.md'),
'from-pr': ("Obtain easyconfigs from specified PR", int, 'store', None, {'metavar': 'PR#'}),
'git-working-dirs-path': ("Path to Git working directories for EasyBuild repositories", str, 'store', None),
'github-user': ("GitHub username", str, 'store', None),
'github-org': ("GitHub organization", str, 'store', None),
'include-easyblocks-from-pr': ("Include easyblocks from specified PR", 'strlist', 'store', [],
{'metavar': 'PR#'}),
'install-github-token': ("Install GitHub token (requires --github-user)", None, 'store_true', False),
'close-pr': ("Close pull request", int, 'store', None, {'metavar': 'PR#'}),
'close-pr-msg': ("Custom close message for pull request closed with --close-pr; ", str, 'store', None),
'close-pr-reasons': ("Close reason for pull request closed with --close-pr; "
"supported values: %s" % ", ".join(VALID_CLOSE_PR_REASONS), str, 'store', None),
'list-prs': ("List pull requests", str, 'store_or_None',
",".join([DEFAULT_LIST_PR_STATE, DEFAULT_LIST_PR_ORDER, DEFAULT_LIST_PR_DIREC]),
{'metavar': 'STATE,ORDER,DIRECTION'}),
'merge-pr': ("Merge pull request", int, 'store', None, {'metavar': 'PR#'}),
'new-branch-github': ("Create new branch in GitHub in preparation for a PR", None, 'store_true', False),
'new-pr': ("Open a new pull request", None, 'store_true', False),
'new-pr-from-branch': ("Open a new pull request from branch in GitHub", str, 'store', None),
'pr-branch-name': ("Branch name to use for new PRs; '<timestamp>_new_pr_<name><version>' if unspecified",
str, 'store', None),
'pr-commit-msg': ("Commit message for new/updated pull request created with --new-pr", str, 'store', None),
'pr-descr': ("Description for new pull request created with --new-pr", str, 'store', None),
'pr-target-account': ("Target account for new PRs", str, 'store', DEFAULT_PR_TARGET_ACCOUNT),
'pr-target-branch': ("Target branch for new PRs", str, 'store', DEFAULT_BRANCH),
'pr-target-repo': ("Target repository for new/updating PRs (default: auto-detect based on provided files)",
str, 'store', None),
'pr-title': ("Title for new pull request created with --new-pr", str, 'store', None),
'preview-pr': ("Preview a new pull request", None, 'store_true', False),
'sync-branch-with-develop': ("Sync branch with current 'develop' branch", str, 'store', None),
'sync-pr-with-develop': ("Sync pull request with current 'develop' branch",
int, 'store', None, {'metavar': 'PR#'}),
'review-pr': ("Review specified pull request", int, 'store', None, {'metavar': 'PR#'}),
'test-report-env-filter': ("Regex used to filter out variables in environment dump of test report",
None, 'regex', None),
'update-branch-github': ("Update specified branch in GitHub", str, 'store', None),
'update-pr': ("Update an existing pull request", int, 'store', None, {'metavar': 'PR#'}),
'upload-test-report': ("Upload full test report as a gist on GitHub", None, 'store_true', False, 'u'),
})
self.log.debug("github_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def regtest_options(self):
"""Regression test configuration options."""
descr = ("Regression test options", "Run and control an EasyBuild regression test.")
opts = OrderedDict({
'aggregate-regtest': ("Collect all the xmls inside the given directory and generate a single file",
None, 'store', None, {'metavar': 'DIR'}),
'regtest': ("Enable regression test mode",
None, 'store_true', False),
'regtest-output-dir': ("Set output directory for test-run",
None, 'store', None, {'metavar': 'DIR'}),
'sequential': ("Specify this option if you want to prevent parallel build",
None, 'store_true', False),
})
self.log.debug("regtest_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def package_options(self):
# package-related options
descr = ("Package options", "Control packaging performed by EasyBuild.")
opts = OrderedDict({
'package': ("Enabling packaging", None, 'store_true', False),
'package-tool': ("Packaging tool to use", None, 'store', DEFAULT_PKG_TOOL),
'package-tool-options': ("Extra options for packaging tool", None, 'store', ''),
'package-type': ("Type of package to generate", None, 'store', DEFAULT_PKG_TYPE),
'package-release': ("Package release iteration number", None, 'store', DEFAULT_PKG_RELEASE),
})
self.log.debug("package_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr)
def container_options(self):
# container-related options
descr = ("Container options", "Options related to generating container recipes & images")
opts = OrderedDict({
'build-image': ("Build container image (requires sudo privileges!)", None, 'store_true', False),
'config': ("Configuration for container image", str, 'store', None),
'image-format': ("Container image format", 'choice', 'store', None, CONT_IMAGE_FORMATS),
'image-name': ("Custom name for container image (defaults to name of easyconfig)", None, 'store', None),
'template-recipe': ("Template recipe for container image", str, 'store', None),
'tmpdir': ("Temporary directory where container image is built", None, 'store', None),
'type': ("Type of container recipe/image to create", 'choice', 'store', DEFAULT_CONT_TYPE, CONT_TYPES),
})
self.log.debug("container_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr, prefix='container')
def easyconfig_options(self):
descr = ("Options for Easyconfigs", "Options that affect all specified easyconfig files.")
opts = OrderedDict({
'create-index': ("Create index for files in specified directory", None, 'store', None),
'fix-deprecated-easyconfigs': ("Fix use of deprecated functionality in specified easyconfig files.",
None, 'store_true', False),
'ignore-index': ("Ignore index when searching for files", None, 'store_true', False),
'index-max-age': ("Maximum age for index before it is considered stale (in seconds)",
int, 'store', DEFAULT_INDEX_MAX_AGE),
'inject-checksums': ("Inject checksums of specified type for sources/patches into easyconfig file(s)",
'choice', 'store_or_None', CHECKSUM_TYPE_SHA256, CHECKSUM_TYPES),
'local-var-naming-check': ("Mode to use when checking whether local variables follow the recommended "
"naming scheme ('log': only log warnings (no printed messages); 'warn': print "
"warnings; 'error': fail with an error)", 'choice', 'store',
LOCAL_VAR_NAMING_CHECK_WARN, LOCAL_VAR_NAMING_CHECKS),
})
self.log.debug("easyconfig_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr, prefix='')
def job_options(self):
"""Option related to --job."""
descr = ("Options for job backend", "Options for job backend (only relevant when --job is used)")
opts = OrderedDict({
'backend-config': ("Configuration file for job backend", None, 'store', None),
'cores': ("Number of cores to request per job", 'int', 'store', None),
'deps-type': ("Type of dependency to set between jobs (default depends on job backend)",
'choice', 'store', None, [JOB_DEPS_TYPE_ABORT_ON_ERROR, JOB_DEPS_TYPE_ALWAYS_RUN]),
'max-jobs': ("Maximum number of concurrent jobs (queued and running, 0 = unlimited)", 'int', 'store', 0),
'max-walltime': ("Maximum walltime for jobs (in hours)", 'int', 'store', 24),
'output-dir': ("Output directory for jobs (default: current directory)", None, 'store', os.getcwd()),
'polling-interval': ("Interval between polls for status of jobs (in seconds)", float, 'store', 30.0),
'target-resource': ("Target resource for jobs", None, 'store', None),
})
self.log.debug("job_options: descr %s opts %s", descr, opts)
self.add_group_parser(opts, descr, prefix='job')
def easyblock_options(self):
# easyblock options (to be passed to easyblock instance)
descr = ("Options for Easyblocks", "Options to be passed to all Easyblocks.")
opts = None
self.log.debug("easyblock_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr, prefix='easyblock')
def unittest_options(self):
# unittest options
descr = ("Unittest options", "Options dedicated to unittesting (experts only).")
opts = OrderedDict({
'file': ("Log to this file in unittest mode", None, 'store', None),
})
self.log.debug("unittest_options: descr %s opts %s" % (descr, opts))
self.add_group_parser(opts, descr, prefix='unittest')
def validate(self):
"""Additional validation of options"""
error_msgs = []
for opt in ['software', 'try-software', 'toolchain', 'try-toolchain']:
val = getattr(self.options, opt.replace('-', '_'))
if val and len(val) != 2:
if opt in ['toolchain', 'try-toolchain'] and val == [TC_CONSTANT_SYSTEM]:
setattr(self.options, opt.replace('-', '_'), [SYSTEM_TOOLCHAIN_NAME, SYSTEM_TOOLCHAIN_NAME])
else:
msg = "--%s requires NAME,VERSION (given %s)" % (opt, ','.join(val))
error_msgs.append(msg)
if self.options.umask:
umask_regex = re.compile('^[0-7]{3}$')
if not umask_regex.match(self.options.umask):
msg = "--umask value should be 3 digits (0-7) (regex pattern '%s')" % umask_regex.pattern
error_msgs.append(msg)
# subdir options must be relative
for typ in ['modules', 'software']:
subdir_opt = 'subdir_%s' % typ
val = getattr(self.options, subdir_opt)
if os.path.isabs(getattr(self.options, subdir_opt)):
msg = "Configuration option '%s' must specify a *relative* path (use 'installpath-%s' instead?): '%s'"
msg = msg % (subdir_opt, typ, val)
error_msgs.append(msg)
# specified module naming scheme must be a known one
avail_mnss = avail_module_naming_schemes()
if self.options.module_naming_scheme and self.options.module_naming_scheme not in avail_mnss:
msg = "Selected module naming scheme '%s' is unknown: %s" % (self.options.module_naming_scheme, avail_mnss)
error_msgs.append(msg)
# values passed to --cuda-compute-capabilities must be of form X.Y (with both X and Y integers),
# see https://developer.nvidia.com/cuda-gpus
if self.options.cuda_compute_capabilities:
cuda_cc_regex = re.compile(r'^[0-9]+\.[0-9]+$')
faulty_cuda_ccs = [x for x in self.options.cuda_compute_capabilities if not cuda_cc_regex.match(x)]
if faulty_cuda_ccs:
error_msg = "Incorrect values in --cuda-compute-capabilities (expected pattern: '%s'): %s"
error_msgs.append(error_msg % (cuda_cc_regex.pattern, ', '.join(faulty_cuda_ccs)))
if error_msgs:
raise EasyBuildError("Found problems validating the options: %s", '\n'.join(error_msgs))
def postprocess(self):
"""Do some postprocessing, in particular print stuff"""
build_log.EXPERIMENTAL = self.options.experimental
# enable devel logging
if self.options.devel:
setLogLevel(DEVEL_LOG_LEVEL)
# set strictness of run module
if self.options.strict:
run.strictness = self.options.strict
# override current version of EasyBuild with version specified to --deprecated
if self.options.deprecated:
build_log.CURRENT_VERSION = LooseVersion(self.options.deprecated)
# log to specified value of --unittest-file
if self.options.unittest_file:
fancylogger.logToFile(self.options.unittest_file, max_bytes=0)
# set tmpdir
self.tmpdir = set_tmpdir(self.options.tmpdir)
# take --include options into account (unless instructed otherwise)
if self.with_include:
self._postprocess_include()
# prepare for --list/--avail
if any([self.options.avail_easyconfig_params, self.options.avail_easyconfig_templates,
self.options.list_easyblocks, self.options.list_toolchains, self.options.avail_cfgfile_constants,
self.options.avail_easyconfig_constants, self.options.avail_easyconfig_licenses,
self.options.avail_repositories, self.options.show_default_moduleclasses,
self.options.avail_modules_tools, self.options.avail_module_naming_schemes,
self.options.show_default_configfiles, self.options.avail_toolchain_opts,
self.options.avail_hooks, self.options.show_system_info,
]):
build_easyconfig_constants_dict() # runs the easyconfig constants sanity check
self._postprocess_list_avail()
# run configuration checks, unless only a single configuration level is being processed
# (this should only happen during --show-config)
if not self.single_cfg_level:
self._postprocess_checks()
# imply --terse for --last-log to avoid extra output that gets in the way
if self.options.last_log:
self.options.terse = True
# auto-enable --backup-modules with --skip and --module-only, unless it was hard disabled
if (self.options.module_only or self.options.skip) and self.options.backup_modules is None:
self.log.debug("Auto-enabling --backup-modules because of --module-only or --skip")
self.options.backup_modules = True
# make sure --optarch has a valid format, but do it only if we are not going to submit jobs. Otherwise it gets
# processed twice and fails when trying to parse a dictionary as if it was a string
if self.options.optarch and not self.options.job:
self._postprocess_optarch()
# make sure --close-pr-reasons has a valid format and if so use it to set close-pr-msg
if self.options.close_pr_reasons:
self._postprocess_close_pr_reasons()
# make sure --list-prs has a valid format
if self.options.list_prs:
self._postprocess_list_prs()
# handle configuration options that affect other configuration options
self._postprocess_config()
# show current configuration and exit, if requested
if self.options.show_config or self.options.show_full_config:
self.show_config()
cleanup_and_exit(self.tmpdir)
def _postprocess_optarch(self):
"""Postprocess --optarch option."""
optarch_parts = self.options.optarch.split(OPTARCH_SEP)
# we expect to find a ':' in every entry in optarch, in case optarch is specified on a per-compiler basis
n_parts = len(optarch_parts)
map_char_cnts = [p.count(OPTARCH_MAP_CHAR) for p in optarch_parts]
if (n_parts > 1 and any(c != 1 for c in map_char_cnts)) or (n_parts == 1 and map_char_cnts[0] > 1):
raise EasyBuildError("The optarch option has an incorrect syntax: %s", self.options.optarch)
else:
# if there are options for different compilers, we set up a dict
if OPTARCH_MAP_CHAR in optarch_parts[0]:
optarch_dict = {}
for compiler, compiler_opt in [p.split(OPTARCH_MAP_CHAR) for p in optarch_parts]:
if compiler in optarch_dict:
raise EasyBuildError("The optarch option contains duplicated entries for compiler %s: %s",
compiler, self.options.optarch)
else:
optarch_dict[compiler] = compiler_opt
self.options.optarch = optarch_dict
self.log.info("Transforming optarch into a dict: %s", self.options.optarch)
# if optarch is not in mapping format, we do nothing and just keep the string
else:
self.log.info("Keeping optarch raw: %s", self.options.optarch)
def _postprocess_close_pr_reasons(self):
"""Postprocess --close-pr-reasons options"""
if self.options.close_pr_msg:
raise EasyBuildError("Please either specify predefined reasons with --close-pr-reasons or " +
"a custom message with--close-pr-msg")
reasons = self.options.close_pr_reasons.split(',')
if any([reason not in VALID_CLOSE_PR_REASONS.keys() for reason in reasons]):
raise EasyBuildError("Argument to --close-pr_reasons must be a comma separated list of valid reasons " +
"among %s" % VALID_CLOSE_PR_REASONS.keys())
self.options.close_pr_msg = ", ".join([VALID_CLOSE_PR_REASONS[reason] for reason in reasons])
def _postprocess_list_prs(self):
"""Postprocess --list-prs options"""
list_pr_parts = self.options.list_prs.split(',')
nparts = len(list_pr_parts)
if nparts > 3:
raise EasyBuildError("Argument to --list-prs must be in the format 'state[,order[,direction]]")
list_pr_state = list_pr_parts[0]
list_pr_order = list_pr_parts[1] if nparts > 1 else DEFAULT_LIST_PR_ORDER
list_pr_direc = list_pr_parts[2] if nparts > 2 else DEFAULT_LIST_PR_DIREC
if list_pr_state not in GITHUB_PR_STATES:
raise EasyBuildError("1st item in --list-prs ('%s') must be one of %s", list_pr_state, GITHUB_PR_STATES)
if list_pr_order not in GITHUB_PR_ORDERS:
raise EasyBuildError("2nd item in --list-prs ('%s') must be one of %s", list_pr_order, GITHUB_PR_ORDERS)
if list_pr_direc not in GITHUB_PR_DIRECTIONS:
raise EasyBuildError("3rd item in --list-prs ('%s') must be one of %s", list_pr_direc, GITHUB_PR_DIRECTIONS)
self.options.list_prs = (list_pr_state, list_pr_order, list_pr_direc)
def _postprocess_include(self):
"""Postprocess --include options."""
# set up included easyblocks, module naming schemes and toolchains/toolchain components
if self.options.include_easyblocks:
include_easyblocks(self.tmpdir, self.options.include_easyblocks)
if self.options.include_module_naming_schemes:
include_module_naming_schemes(self.tmpdir, self.options.include_module_naming_schemes)
if self.options.include_toolchains:
include_toolchains(self.tmpdir, self.options.include_toolchains)
def _postprocess_checks(self):
"""Check whether (combination of) configuration options make sense."""
# fail early if required dependencies for functionality requiring using GitHub API are not available:
if self.options.from_pr or self.options.include_easyblocks_from_pr or self.options.upload_test_report:
if not HAVE_GITHUB_API:
raise EasyBuildError("Required support for using GitHub API is not available (see warnings)")
# using Lua module syntax only makes sense when modules tool being used is Lmod
if self.options.module_syntax == ModuleGeneratorLua.SYNTAX and self.options.modules_tool != Lmod.__name__:
error_msg = "Generating Lua module files requires Lmod as modules tool; "
mod_syntaxes = ', '.join(sorted(avail_module_generators().keys()))
error_msg += "use --module-syntax to specify a different module syntax to use (%s)" % mod_syntaxes
raise EasyBuildError(error_msg)
# check whether specified action --detect-loaded-modules is valid
if self.options.detect_loaded_modules not in LOADED_MODULES_ACTIONS:
error_msg = "Unknown action specified to --detect-loaded-modules: %s (known values: %s)"
raise EasyBuildError(error_msg % (self.options.detect_loaded_modules, ', '.join(LOADED_MODULES_ACTIONS)))
# make sure a GitHub token is available when it's required
if self.options.upload_test_report:
if not HAVE_KEYRING:
raise EasyBuildError("Python 'keyring' module required for obtaining GitHub token is not available")
if self.options.github_user is None:
raise EasyBuildError("No GitHub user name provided, required for fetching GitHub token")
token = fetch_github_token(self.options.github_user)
if token is None:
raise EasyBuildError("Failed to obtain required GitHub token for user '%s'" % self.options.github_user)
# make sure autopep8 is available when it needs to be
if self.options.dump_autopep8:
if not HAVE_AUTOPEP8:
raise EasyBuildError("Python 'autopep8' module required to reformat dumped easyconfigs as requested")
# if a path is specified to --sysroot, it must exist
if self.options.sysroot:
if os.path.exists(self.options.sysroot):