forked from easybuilders/easybuild-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.py
More file actions
2147 lines (1858 loc) · 118 KB
/
Copy pathoptions.py
File metadata and controls
2147 lines (1858 loc) · 118 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-2025 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
Authors:
* Stijn De Weirdt (Ghent University)
* Dries Verdegem (Ghent University)
* Kenneth Hoste (Ghent University)
* Pieter De Baets (Ghent University)
* Jens Timmerman (Ghent University)
* Toon Willems (Ghent University)
* Ward Poelmans (Ghent University)
* Damian Alvarez (Forschungszentrum Juelich GmbH)
* Maxime Boissonneault (Compute Canada)
"""
import copy
import glob
import os
import re
import shutil
import sys
import tempfile
import pwd
from collections import OrderedDict
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, EXTRACT_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.tools import alt_easyconfig_paths, get_paths_for
from easybuild.toolchains.compiler.systemcompiler import TC_CONSTANT_SYSTEM
from easybuild.tools import LooseVersion, build_log, run # build_log should always stay there, to ensure EasyBuildLog
from easybuild.tools.build_log import DEVEL_LOG_LEVEL, EasyBuildError, EasyBuildExit
from easybuild.tools.build_log import init_logging, log_start, print_msg, print_warning, raise_easybuilderror
from easybuild.tools.config import CHECKSUM_PRIORITY_CHOICES, DEFAULT_CHECKSUM_PRIORITY
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_DOWNLOAD_TIMEOUT
from easybuild.tools.config import DEFAULT_ENV_FOR_SHEBANG, DEFAULT_ENVVAR_USERS_MODULES
from easybuild.tools.config import DEFAULT_FORCE_DOWNLOAD, DEFAULT_INDEX_MAX_AGE, DEFAULT_JOB_BACKEND
from easybuild.tools.config import DEFAULT_JOB_EB_CMD, 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_EXTRA_SOURCE_URLS
from easybuild.tools.config import DEFAULT_REPOSITORY, DEFAULT_WAIT_ON_LOCK_INTERVAL, DEFAULT_WAIT_ON_LOCK_LIMIT
from easybuild.tools.config import DEFAULT_PR_TARGET_ACCOUNT, DEFAULT_FILTER_RPATH_SANITY_LIBS
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
from easybuild.tools.config import OUTPUT_STYLE_AUTO, OUTPUT_STYLES, WARN
from easybuild.tools.config import get_pretend_installpath, init, init_build_options, mk_full_default_path
from easybuild.tools.config import BuildOptions, ConfigurationVariables
from easybuild.tools.config import PYTHON_SEARCH_PATH_TYPES, PYTHONPATH
from easybuild.tools.configobj import ConfigObj, ConfigObjError
from easybuild.tools.docs import FORMAT_JSON, FORMAT_MD, FORMAT_RST, FORMAT_TXT
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, get_cwd
from easybuild.tools.filetools import install_fake_vsc, move_file, which, is_parent_path
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_commit, 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.robot import det_robot_path
from easybuild.tools.run import run_shell_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 DEFAULT_SEARCH_PATH_CPP_HEADERS, DEFAULT_SEARCH_PATH_LINKER, SEARCH_PATH
from easybuild.tools.toolchain.toolchain import SYSTEM_TOOLCHAIN_NAME
from easybuild.tools.repository.repository import avail_repositories
from easybuild.tools.systemtools import DARWIN, UNKNOWN, check_python_version, get_cpu_architecture, get_cpu_family
from easybuild.tools.systemtools import get_cpu_features, get_gpu_info, get_os_type, get_system_info
from easybuild.tools.utilities import flatten
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/xdg').split(os.pathsep)
DEFAULT_SYS_CFGFILES = [[f for f in sorted(glob.glob(os.path.join(d, 'easybuild.d', '*.cfg')))]
for d in XDG_CONFIG_DIRS]
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
RPATH_DEFAULT = False if get_os_type() == DARWIN else True
_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'
# https://specifications.freedesktop.org/basedir-spec/latest/
# says precedence should be
# XDG_CONFIG_HOME > 1st entry of XDG_CONFIG_DIRS > 2nd entry ...
# EasyBuild parses this list backwards, gives priority to last entry
DEFAULT_CONFIGFILES = flatten(DEFAULT_SYS_CFGFILES[::-1])
if 'XDG_CONFIG_DIRS' not in os.environ:
old_etc_location = os.path.join('/etc', 'easybuild.d')
if os.path.isdir(old_etc_location) and glob.glob(os.path.join(old_etc_location, '*.cfg')):
_log.deprecated(f"Using {old_etc_location} is deprecated. Please use "
"/etc/xdg/easybuild.d instead or add /etc to XDG_CONFIG_DIRS", '6.0')
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 = {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, optionally consider additional paths to search for easyconfigs",
'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', EXTRACT_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.")
all_deprecations = ('python2', 'Lmod6', 'easyconfig', 'toolchain')
opts = OrderedDict({
'accept-eula-for': ("Accept EULA for specified software", 'strlist', 'store', []),
'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-unresolved-templates': ("Don't error out when templates such as %(name)s in EasyConfigs "
"could not be resolved", None, 'store_true', False),
'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. "
"Auto-enabled when using --module-only or --skip",
None, 'store_true', None), # default None to allow auto-enabling if not disabled
'backup-patched-files': ("Create a backup (*.orig) file when applying a patch",
None, 'store_true', False),
'banned-linked-shared-libs': ("Comma-separated list of shared libraries (names, file names, or paths) "
"which are not allowed to be linked in any installed binary/library",
'strlist', 'extend', None),
'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),
'checksum-priority': ("When checksums are found in both the EasyConfig and the checksums.json file"
"Define which one to use. ",
'choice', 'store_or_None', DEFAULT_CHECKSUM_PRIORITY, CHECKSUM_PRIORITY_CHOICES),
'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-cache-dir': ("Path to CUDA cache dir to use if enabled. Defaults to a path inside the build dir.",
str, 'store', None, {'metavar': "PATH"}),
'cuda-cache-maxsize': ("Maximum size of the CUDA cache (in MiB) used for JIT compilation of PTX code. "
"Leave value empty to let EasyBuild choose a value or '0' to disable the cache",
int, 'store_or_None', None),
'cuda-compute-capabilities': ("List of CUDA compute capabilities to use when building GPU software; "
"values should be specified as digits separated by a dot, "
"for example: 3.5,5.0,7.2", '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_OPTIONS),
'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',
DEFAULT_DOWNLOAD_TIMEOUT),
'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),
'env-for-shebang': ("Define the env command to use when fixing shebangs", None, 'store',
DEFAULT_ENV_FOR_SHEBANG),
'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),
"extra-source-urls": ("Specify URLs to fetch sources from in addition to those in the easyconfig",
"urltuple", "add_flex", DEFAULT_EXTRA_SOURCE_URLS, {'metavar': 'URL[|URL]'}),
'fail-on-mod-files-gcccore': ("Fail if .mod files are detected in a GCCcore install", None, 'store_true',
False),
'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-ecs': ("List of easyconfigs (given as glob patterns) to *ignore* when given on command line "
"or auto-selected when building with --from-pr. (e.g. --filter-ecs=*intel*)",
'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),
'filter-rpath-sanity-libs': ("List of libraries that should be ignored by the RPATH sanity check. "
"I.e. if these libraries are not RPATH-ed, that will be accepted "
"by the RPATH sanity check. Note that you'll need to provide the exact "
"library name, as it is returned by 'ldd', including any version ",
'strlist', 'store', DEFAULT_FILTER_RPATH_SANITY_LIBS),
'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),
'generate-devel-module': ("Generate a develop module file, implies --force if disabled",
None, 'store_true', True),
'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),
'http-header-fields-urlpat': ("Set extra HTTP header FIELD when downloading files from URL PATterns. "
"Use FILE (to hide sensitive values) and newline separated FIELDs in the "
"same format. e.g. \"^https://www.example.com::path/to/headers.txt\" or "
"\"client[A-z0-9]*.example.com:: Authorization: Basic token\".",
None, 'append', None, {'metavar': '[URLPAT::][HEADER:]FIELDVALUE|FILE'}),
'ignore-checksums': ("Ignore failing checksum verification", None, 'store_true', False),
'ignore-test-failure': ("Ignore a failing test step", None, 'store_true', False),
'ignore-osdeps': ("Ignore any listed OS dependencies", None, 'store_true', False),
'insecure-download': ("Don't check the server certificate against the available certificate authorities.",
None, 'store_true', False),
'install-latest-eb-release': ("Install latest known version of easybuild", None, 'store_true', False),
'keep-debug-symbols': ("Sets default value of debug toolchain option", None, 'store_true', True),
'lib-lib64-symlink': ("Automatically create symlinks for lib/ pointing to lib64/ if the former is missing",
None, 'store_true', True),
'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-cache-suffix': ("Suffix to add to the cache file name (before the extension) "
"when updating the modules tool cache",
None, 'store', None),
'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_JSON, FORMAT_MD, FORMAT_RST, FORMAT_TXT]),
'output-style': ("Control output style; auto implies using Rich if available to produce rich output, "
"with fallback to basic colored output",
'choice', 'store', OUTPUT_STYLE_AUTO, OUTPUT_STYLES),
'parallel': ("Specify (maximum) level of parallelism used during build procedure",
'int', 'store', None),
'parallel-extensions-install': ("Install list of extensions in parallel (if supported)",
None, 'store_true', False),
'pre-create-installdir': ("Create installation directory before submitting build jobs",
None, 'store_true', True),
'prefer-python-search-path': (("Prefer using specified environment variable when possible to specify where"
" Python packages were installed; see also "
"https://docs.easybuild.io/python-search-path"),
'choice', 'store_or_None', PYTHONPATH, PYTHON_SEARCH_PATH_TYPES),
'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),
'required-linked-shared-libs': ("Comma-separated list of shared libraries (names, file names, or paths) "
"which must be linked in all installed binaries/libraries",
'strlist', 'extend', None),
'rpath': ("Enable use of RPATH for linking with libraries", None, 'store_true', RPATH_DEFAULT),
'rpath-filter': ("List of regex patterns to use for filtering out RPATH paths", 'strlist', 'store', None),
'rpath-override-dirs': ("Path(s) to be prepended when linking with RPATH (string, colon-separated)",
None, 'store', None),
'sanity-check-only': ("Only run sanity check (module is expected to be installed already",
None, 'store_true', False),
'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),
'show-progress-bar': ("Show progress bar in terminal output", None, 'store_true', True),
'silence-deprecation-warnings': (
"Silence specified deprecation warnings out of (%s)" % ', '.join(all_deprecations),
'strlist', 'extend', []),
'silence-hook-trigger': ("Suppress printing of debug message every time a hook is triggered",
None, 'store_true', False),
'skip-extensions': ("Skip installation of extensions", None, 'store_true', False),
'skip-sanity-check': ("Skip running the sanity check step "
"(e.g. testing for installed files or running basic commands)",
None, 'store_true', False),
'skip-test-cases': ("Skip running test cases", None, 'store_true', False, 't'),
'skip-test-step': ("Skip running the test step (e.g. unit tests)", None, 'store_true', False),
'software-commit': (
"Git commit to use for the target software build (robot capabilities are automatically disabled)",
None, 'store', None),
'sticky-bit': ("Set sticky bit on newly created directories", None, 'store_true', False),
'strict-rpath-sanity-check': ("Perform strict RPATH sanity check, which involces unsetting "
"$LD_LIBRARY_PATH before checking whether all required libraries are found",
None, 'store_true', False),
'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', True, '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),
'unit-testing-mode': ("Run in unit test mode", 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-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')),
'envvars-user-modules': ("List of environment variables that hold the base paths for which user-specific "
"modules will be installed relative to", 'strlist', 'store',
[DEFAULT_ENVVAR_USERS_MODULES]),
'external-modules-metadata': ("List of (glob patterns for) paths to files specifying metadata "
"for external modules (INI format)", 'strlist', 'store', None),
'failed-installs-build-dirs-path': ("Location where build directories are copied if installation fails; "
"an empty value disables copying of build directories",
None, 'store', None, {'metavar': "PATH"}),
'failed-installs-logs-path': ("Location where log files are copied if installation fails; "
"an empty value disables copying of log files",
None, 'store', None, {'metavar': "PATH"}),
'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', True),
'module-extensions': ("Include 'extensions' statement in generated module file (Lua syntax only)",
None, 'store_true', True),
'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),
'search-path-cpp-headers': ("Search path used at build time for include directories", 'choice',
'store', DEFAULT_SEARCH_PATH_CPP_HEADERS, [*SEARCH_PATH["cpp_headers"]]),
'search-path-linker': ("Search path used at build time by the linker for libraries", 'choice',
'store', DEFAULT_SEARCH_PATH_LINKER, [*SEARCH_PATH["linker"]]),
'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 --envvars-user-modules",
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),
'check-eb-deps': ("Check presence and version of (required and optional) EasyBuild dependencies",
None, 'store_true', False),
'dep-graph': ("Create dependency graph. Output format depends on <ext>, e.g. 'dot', 'png', 'pdf', 'gv'.",
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),
'easystack': ("Path to easystack file in YAML format, specifying details of a software stack",
None, 'store', None),
})
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({
'add-pr-labels': ("Try to add labels to PR based on files changed", int, 'store', None, {'metavar': 'PR#'}),
'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-commit': ("Obtain easyconfigs from specified commit", 'str', 'store',
None, {'metavar': 'commit_SHA'}),
'from-pr': ("Obtain easyconfigs from specified PR", 'strlist', 'store', [], {'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-commit': ("Include easyblocks from specified commit", 'str', 'store', None,
{'metavar': 'commit_SHA'}),
'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#'}),
'review-pr-filter': ("Regex used to filter out easyconfigs to diff against in --review-pr",
None, 'regex', None),
'review-pr-max': ("Maximum number of easyconfigs to diff against in --review-pr", int, 'store', None),
'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),
'inject-checksums-to-json': ("Inject checksums of specified type for sources/patches into checksums.json",
'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]),
'eb-cmd': ("EasyBuild command to use in jobs", 'str', 'store', DEFAULT_JOB_EB_CMD),
'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', get_cwd()),
'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),
exit_code=EasyBuildExit.OPTION_ERROR
)
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: