1#===----------------------------------------------------------------------===##
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7#===----------------------------------------------------------------------===##
8
9import os
10import pickle
11import pipes
12import platform
13import re
14import shutil
15import tempfile
16
17import libcxx.test.format
18import lit
19import lit.LitConfig
20import lit.Test
21import lit.TestRunner
22import lit.util
23
24class ConfigurationError(Exception):
25  pass
26
27class ConfigurationCompilationError(ConfigurationError):
28  pass
29
30class ConfigurationRuntimeError(ConfigurationError):
31  pass
32
33def _memoizeExpensiveOperation(extractCacheKey):
34  """
35  Allows memoizing a very expensive operation.
36
37  We pickle the cache key to make sure we store an immutable representation
38  of it. If we stored an object and the object was referenced elsewhere, it
39  could be changed from under our feet, which would break the cache.
40
41  We also store the cache for a given function persistently across invocations
42  of Lit. This dramatically speeds up the configuration of the test suite when
43  invoking Lit repeatedly, which is important for developer workflow. However,
44  with the current implementation that does not synchronize updates to the
45  persistent cache, this also means that one should not call a memoized
46  operation from multiple threads. This should normally not be a problem
47  since Lit configuration is single-threaded.
48  """
49  def decorator(function):
50    def f(config, *args, **kwargs):
51      cacheRoot = os.path.join(config.test_exec_root, '__config_cache__')
52      persistentCache = os.path.join(cacheRoot, function.__name__)
53      if not os.path.exists(cacheRoot):
54        os.makedirs(cacheRoot)
55
56      cache = {}
57      # Load a cache from a previous Lit invocation if there is one.
58      if os.path.exists(persistentCache):
59        with open(persistentCache, 'rb') as cacheFile:
60          cache = pickle.load(cacheFile)
61
62      cacheKey = pickle.dumps(extractCacheKey(config, *args, **kwargs))
63      if cacheKey not in cache:
64        cache[cacheKey] = function(config, *args, **kwargs)
65        # Update the persistent cache so it knows about the new key
66        with open(persistentCache, 'wb') as cacheFile:
67          pickle.dump(cache, cacheFile)
68      return cache[cacheKey]
69    return f
70  return decorator
71
72def _executeScriptInternal(test, commands):
73  """
74  Returns (stdout, stderr, exitCode, timeoutInfo)
75
76  TODO: This really should be easier to access from Lit itself
77  """
78  parsedCommands = libcxx.test.format.parseScript(test, preamble=commands)
79
80  litConfig = lit.LitConfig.LitConfig(
81    progname='lit',
82    path=[],
83    quiet=False,
84    useValgrind=False,
85    valgrindLeakCheck=False,
86    valgrindArgs=[],
87    noExecute=False,
88    debug=False,
89    isWindows=platform.system() == 'Windows',
90    order='smart',
91    params={})
92  _, tmpBase = libcxx.test.format._getTempPaths(test)
93  execDir = os.path.dirname(test.getExecPath())
94  res = lit.TestRunner.executeScriptInternal(test, litConfig, tmpBase, parsedCommands, execDir)
95  if isinstance(res, lit.Test.Result): # Handle failure to parse the Lit test
96    res = ('', res.output, 127, None)
97  (out, err, exitCode, timeoutInfo) = res
98
99  # TODO: As a temporary workaround until https://reviews.llvm.org/D81892 lands, manually
100  #       split any stderr output that is included in stdout. It shouldn't be there, but
101  #       the Lit internal shell conflates stderr and stdout.
102  conflatedErrorOutput = re.search("(# command stderr:.+$)", out, flags=re.DOTALL)
103  if conflatedErrorOutput:
104    conflatedErrorOutput = conflatedErrorOutput.group(0)
105    out = out[:-len(conflatedErrorOutput)]
106    err += conflatedErrorOutput
107
108  return (out, err, exitCode, timeoutInfo)
109
110def _makeConfigTest(config):
111  # Make sure the support directories exist, which is needed to create
112  # the temporary file %t below.
113  sourceRoot = os.path.join(config.test_exec_root, '__config_src__')
114  execRoot = os.path.join(config.test_exec_root, '__config_exec__')
115  for supportDir in (sourceRoot, execRoot):
116    if not os.path.exists(supportDir):
117      os.makedirs(supportDir)
118
119  # Create a dummy test suite and single dummy test inside it. As part of
120  # the Lit configuration, automatically do the equivalent of 'mkdir %T'
121  # and 'rm -r %T' to avoid cluttering the build directory.
122  suite = lit.Test.TestSuite('__config__', sourceRoot, execRoot, config)
123  tmp = tempfile.NamedTemporaryFile(dir=sourceRoot, delete=False, suffix='.cpp')
124  tmp.close()
125  pathInSuite = [os.path.relpath(tmp.name, sourceRoot)]
126  class TestWrapper(lit.Test.Test):
127    def __enter__(self):
128      testDir, _ = libcxx.test.format._getTempPaths(self)
129      os.makedirs(testDir)
130      return self
131    def __exit__(self, *args):
132      testDir, _ = libcxx.test.format._getTempPaths(self)
133      shutil.rmtree(testDir)
134      os.remove(tmp.name)
135  return TestWrapper(suite, pathInSuite, config)
136
137@_memoizeExpensiveOperation(lambda c, s, f=[]: (c.substitutions, c.environment, s, f))
138def sourceBuilds(config, source, additionalFlags=[]):
139  """
140  Return whether the program in the given string builds successfully.
141
142  This is done by compiling and linking a program that consists of the given
143  source with the %{cxx} substitution, and seeing whether that succeeds. If
144  any additional flags are passed, they are appended to the compiler invocation.
145  """
146  with _makeConfigTest(config) as test:
147    with open(test.getSourcePath(), 'w') as sourceFile:
148      sourceFile.write(source)
149    _, _, exitCode, _ = _executeScriptInternal(test, ['%{{build}} {}'.format(' '.join(additionalFlags))])
150    return exitCode == 0
151
152@_memoizeExpensiveOperation(lambda c, p, args=None: (c.substitutions, c.environment, p, args))
153def programOutput(config, program, args=None):
154  """
155  Compiles a program for the test target, run it on the test target and return
156  the output.
157
158  Note that execution of the program is done through the %{exec} substitution,
159  which means that the program may be run on a remote host depending on what
160  %{exec} does.
161  """
162  if args is None:
163    args = []
164  with _makeConfigTest(config) as test:
165    with open(test.getSourcePath(), 'w') as source:
166      source.write(program)
167    _, err, exitCode, _ = _executeScriptInternal(test, ['%{build}'])
168    if exitCode != 0:
169      raise ConfigurationCompilationError("Failed to build program, stderr is:\n{}".format(err))
170
171    out, err, exitCode, _ = _executeScriptInternal(test, ["%{{run}} {}".format(' '.join(args))])
172    if exitCode != 0:
173      raise ConfigurationRuntimeError("Failed to run program, stderr is:\n{}".format(err))
174
175    actualOut = re.search("# command output:\n(.+)\n$", out, flags=re.DOTALL)
176    actualOut = actualOut.group(1) if actualOut else ""
177    return actualOut
178
179@_memoizeExpensiveOperation(lambda c, p, args=None: (c.substitutions, c.environment, p, args))
180def programSucceeds(config, program, args=None):
181  """
182  Compiles a program for the test target, run it on the test target and return
183  whether it completed successfully.
184
185  Note that execution of the program is done through the %{exec} substitution,
186  which means that the program may be run on a remote host depending on what
187  %{exec} does.
188  """
189  try:
190    programOutput(config, program, args)
191  except ConfigurationRuntimeError:
192    return False
193  return True
194
195@_memoizeExpensiveOperation(lambda c, f: (c.substitutions, c.environment, f))
196def hasCompileFlag(config, flag):
197  """
198  Return whether the compiler in the configuration supports a given compiler flag.
199
200  This is done by executing the %{cxx} substitution with the given flag and
201  checking whether that succeeds.
202  """
203  with _makeConfigTest(config) as test:
204    out, err, exitCode, timeoutInfo = _executeScriptInternal(test, [
205      "%{{cxx}} -xc++ {} -Werror -fsyntax-only %{{flags}} %{{compile_flags}} {}".format(os.devnull, flag)
206    ])
207    return exitCode == 0
208
209@_memoizeExpensiveOperation(lambda c, s: (c.substitutions, c.environment, s))
210def runScriptExitCode(config, script):
211  """
212  Runs the given script as a Lit test, and returns the exit code of the execution.
213
214  The script must be a list of commands, each of which being something that
215  could appear on the right-hand-side of a `RUN:` keyword.
216  """
217  with _makeConfigTest(config) as test:
218    _, _, exitCode, _ = _executeScriptInternal(test, script)
219    return exitCode
220
221@_memoizeExpensiveOperation(lambda c, s: (c.substitutions, c.environment, s))
222def commandOutput(config, command):
223  """
224  Runs the given script as a Lit test, and returns the output.
225  If the exit code isn't 0 an exception is raised.
226
227  The script must be a list of commands, each of which being something that
228  could appear on the right-hand-side of a `RUN:` keyword.
229  """
230  with _makeConfigTest(config) as test:
231    out, _, exitCode, _ = _executeScriptInternal(test, command)
232    if exitCode != 0:
233     raise ConfigurationRuntimeError()
234    return out
235
236@_memoizeExpensiveOperation(lambda c, l: (c.substitutions, c.environment, l))
237def hasAnyLocale(config, locales):
238  """
239  Return whether the runtime execution environment supports a given locale.
240  Different systems may use different names for a locale, so this function checks
241  whether any of the passed locale names is supported by setlocale() and returns
242  true if one of them works.
243
244  This is done by executing a program that tries to set the given locale using
245  %{exec} -- this means that the command may be executed on a remote host
246  depending on the %{exec} substitution.
247  """
248  program = """
249    #include <stddef.h>
250    #if defined(_LIBCPP_HAS_NO_LOCALIZATION)
251      int main(int, char**) { return 1; }
252    #else
253      #include <locale.h>
254      int main(int argc, char** argv) {
255        for (int i = 1; i < argc; i++) {
256          if (::setlocale(LC_ALL, argv[i]) != NULL) {
257            return 0;
258          }
259        }
260        return 1;
261      }
262    #endif
263  """
264  return programSucceeds(config, program, args=[pipes.quote(l) for l in locales])
265
266@_memoizeExpensiveOperation(lambda c, flags='': (c.substitutions, c.environment, flags))
267def compilerMacros(config, flags=''):
268  """
269  Return a dictionary of predefined compiler macros.
270
271  The keys are strings representing macros, and the values are strings
272  representing what each macro is defined to.
273
274  If the optional `flags` argument (a string) is provided, these flags will
275  be added to the compiler invocation when generating the macros.
276  """
277  with _makeConfigTest(config) as test:
278    with open(test.getSourcePath(), 'w') as sourceFile:
279      sourceFile.write("""
280      #if __has_include(<__config_site>)
281      #  include <__config_site>
282      #endif
283      """)
284    unparsedOutput, err, exitCode, _ = _executeScriptInternal(test, [
285      "%{{cxx}} %s -dM -E %{{flags}} %{{compile_flags}} {}".format(flags)
286    ])
287    if exitCode != 0:
288      raise ConfigurationCompilationError("Failed to retrieve compiler macros, stderr is:\n{}".format(err))
289    parsedMacros = dict()
290    defines = (l.strip() for l in unparsedOutput.split('\n') if l.startswith('#define '))
291    for line in defines:
292      line = line[len('#define '):]
293      macro, _, value = line.partition(' ')
294      parsedMacros[macro] = value
295    return parsedMacros
296
297def featureTestMacros(config, flags=''):
298  """
299  Return a dictionary of feature test macros.
300
301  The keys are strings representing feature test macros, and the values are
302  integers representing the value of the macro.
303  """
304  allMacros = compilerMacros(config, flags)
305  return {m: int(v.rstrip('LlUu')) for (m, v) in allMacros.items() if m.startswith('__cpp_')}
306
307def _appendToSubstitution(substitutions, key, value):
308  return [(k, v + ' ' + value) if k == key else (k, v) for (k, v) in substitutions]
309
310def _prependToSubstitution(substitutions, key, value):
311  return [(k, value + ' ' + v) if k == key else (k, v) for (k, v) in substitutions]
312
313
314class ConfigAction(object):
315  """
316  This class represents an action that can be performed on a Lit TestingConfig
317  object.
318
319  Examples of such actions are adding or modifying substitutions, Lit features,
320  etc. This class only provides the interface of such actions, and it is meant
321  to be subclassed appropriately to create new actions.
322  """
323  def applyTo(self, config):
324    """
325    Applies the action to the given configuration.
326
327    This should modify the configuration object in place, and return nothing.
328
329    If applying the action to the configuration would yield an invalid
330    configuration, and it is possible to diagnose it here, this method
331    should produce an error. For example, it should be an error to modify
332    a substitution in a way that we know for sure is invalid (e.g. adding
333    a compiler flag when we know the compiler doesn't support it). Failure
334    to do so early may lead to difficult-to-diagnose issues down the road.
335    """
336    pass
337
338  def pretty(self, config, litParams):
339    """
340    Returns a short and human-readable string describing what this action does.
341
342    This is used for logging purposes when running the test suite, so it should
343    be kept concise.
344    """
345    pass
346
347
348class AddFeature(ConfigAction):
349  """
350  This action defines the given Lit feature when running the test suite.
351
352  The name of the feature can be a string or a callable, in which case it is
353  called with the configuration to produce the feature name (as a string).
354  """
355  def __init__(self, name):
356    self._name = name
357
358  def _getName(self, config):
359    name = self._name(config) if callable(self._name) else self._name
360    if not isinstance(name, str):
361      raise ValueError("Lit feature did not resolve to a string (got {})".format(name))
362    return name
363
364  def applyTo(self, config):
365    config.available_features.add(self._getName(config))
366
367  def pretty(self, config, litParams):
368    return 'add Lit feature {}'.format(self._getName(config))
369
370
371class AddFlag(ConfigAction):
372  """
373  This action adds the given flag to the %{flags} substitution.
374
375  The flag can be a string or a callable, in which case it is called with the
376  configuration to produce the actual flag (as a string).
377  """
378  def __init__(self, flag):
379    self._getFlag = lambda config: flag(config) if callable(flag) else flag
380
381  def applyTo(self, config):
382    flag = self._getFlag(config)
383    assert hasCompileFlag(config, flag), "Trying to enable flag {}, which is not supported".format(flag)
384    config.substitutions = _appendToSubstitution(config.substitutions, '%{flags}', flag)
385
386  def pretty(self, config, litParams):
387    return 'add {} to %{{flags}}'.format(self._getFlag(config))
388
389
390class AddFlagIfSupported(ConfigAction):
391  """
392  This action adds the given flag to the %{flags} substitution, only if
393  the compiler supports the flag.
394
395  The flag can be a string or a callable, in which case it is called with the
396  configuration to produce the actual flag (as a string).
397  """
398  def __init__(self, flag):
399    self._getFlag = lambda config: flag(config) if callable(flag) else flag
400
401  def applyTo(self, config):
402    flag = self._getFlag(config)
403    if hasCompileFlag(config, flag):
404      config.substitutions = _appendToSubstitution(config.substitutions, '%{flags}', flag)
405
406  def pretty(self, config, litParams):
407    return 'add {} to %{{flags}}'.format(self._getFlag(config))
408
409
410class AddCompileFlag(ConfigAction):
411  """
412  This action adds the given flag to the %{compile_flags} substitution.
413
414  The flag can be a string or a callable, in which case it is called with the
415  configuration to produce the actual flag (as a string).
416  """
417  def __init__(self, flag):
418    self._getFlag = lambda config: flag(config) if callable(flag) else flag
419
420  def applyTo(self, config):
421    flag = self._getFlag(config)
422    assert hasCompileFlag(config, flag), "Trying to enable compile flag {}, which is not supported".format(flag)
423    config.substitutions = _appendToSubstitution(config.substitutions, '%{compile_flags}', flag)
424
425  def pretty(self, config, litParams):
426    return 'add {} to %{{compile_flags}}'.format(self._getFlag(config))
427
428
429class AddLinkFlag(ConfigAction):
430  """
431  This action appends the given flag to the %{link_flags} substitution.
432
433  The flag can be a string or a callable, in which case it is called with the
434  configuration to produce the actual flag (as a string).
435  """
436  def __init__(self, flag):
437    self._getFlag = lambda config: flag(config) if callable(flag) else flag
438
439  def applyTo(self, config):
440    flag = self._getFlag(config)
441    assert hasCompileFlag(config, flag), "Trying to enable link flag {}, which is not supported".format(flag)
442    config.substitutions = _appendToSubstitution(config.substitutions, '%{link_flags}', flag)
443
444  def pretty(self, config, litParams):
445    return 'append {} to %{{link_flags}}'.format(self._getFlag(config))
446
447
448class PrependLinkFlag(ConfigAction):
449  """
450  This action prepends the given flag to the %{link_flags} substitution.
451
452  The flag can be a string or a callable, in which case it is called with the
453  configuration to produce the actual flag (as a string).
454  """
455  def __init__(self, flag):
456    self._getFlag = lambda config: flag(config) if callable(flag) else flag
457
458  def applyTo(self, config):
459    flag = self._getFlag(config)
460    assert hasCompileFlag(config, flag), "Trying to enable link flag {}, which is not supported".format(flag)
461    config.substitutions = _prependToSubstitution(config.substitutions, '%{link_flags}', flag)
462
463  def pretty(self, config, litParams):
464    return 'prepend {} to %{{link_flags}}'.format(self._getFlag(config))
465
466
467class AddOptionalWarningFlag(ConfigAction):
468  """
469  This action adds the given warning flag to the %{compile_flags} substitution,
470  if it is supported by the compiler.
471
472  The flag can be a string or a callable, in which case it is called with the
473  configuration to produce the actual flag (as a string).
474  """
475  def __init__(self, flag):
476    self._getFlag = lambda config: flag(config) if callable(flag) else flag
477
478  def applyTo(self, config):
479    flag = self._getFlag(config)
480    # Use -Werror to make sure we see an error about the flag being unsupported.
481    if hasCompileFlag(config, '-Werror ' + flag):
482      config.substitutions = _appendToSubstitution(config.substitutions, '%{compile_flags}', flag)
483
484  def pretty(self, config, litParams):
485    return 'add {} to %{{compile_flags}}'.format(self._getFlag(config))
486
487
488class AddSubstitution(ConfigAction):
489  """
490  This action adds the given substitution to the Lit configuration.
491
492  The substitution can be a string or a callable, in which case it is called
493  with the configuration to produce the actual substitution (as a string).
494  """
495  def __init__(self, key, substitution):
496    self._key = key
497    self._getSub = lambda config: substitution(config) if callable(substitution) else substitution
498
499  def applyTo(self, config):
500    key = self._key
501    sub = self._getSub(config)
502    config.substitutions.append((key, sub))
503
504  def pretty(self, config, litParams):
505    return 'add substitution {} = {}'.format(self._key, self._getSub(config))
506
507
508class Feature(object):
509  """
510  Represents a Lit available feature that is enabled whenever it is supported.
511
512  A feature like this informs the test suite about a capability of the compiler,
513  platform, etc. Unlike Parameters, it does not make sense to explicitly
514  control whether a Feature is enabled -- it should be enabled whenever it
515  is supported.
516  """
517  def __init__(self, name, actions=None, when=lambda _: True):
518    """
519    Create a Lit feature for consumption by a test suite.
520
521    - name
522        The name of the feature. This is what will end up in Lit's available
523        features if the feature is enabled. This can be either a string or a
524        callable, in which case it is passed the TestingConfig and should
525        generate a string representing the name of the feature.
526
527    - actions
528        An optional list of ConfigActions to apply when the feature is supported.
529        An AddFeature action is always created regardless of any actions supplied
530        here -- these actions are meant to perform more than setting a corresponding
531        Lit feature (e.g. adding compiler flags). If 'actions' is a callable, it
532        is called with the current configuration object to generate the actual
533        list of actions.
534
535    - when
536        A callable that gets passed a TestingConfig and should return a
537        boolean representing whether the feature is supported in that
538        configuration. For example, this can use `hasCompileFlag` to
539        check whether the compiler supports the flag that the feature
540        represents. If omitted, the feature will always be considered
541        supported.
542    """
543    self._name = name
544    self._actions = [] if actions is None else actions
545    self._isSupported = when
546
547  def _getName(self, config):
548    name = self._name(config) if callable(self._name) else self._name
549    if not isinstance(name, str):
550      raise ValueError("Feature did not resolve to a name that's a string, got {}".format(name))
551    return name
552
553  def getActions(self, config):
554    """
555    Return the list of actions associated to this feature.
556
557    If the feature is not supported, an empty list is returned.
558    If the feature is supported, an `AddFeature` action is automatically added
559    to the returned list of actions, in addition to any actions provided on
560    construction.
561    """
562    if not self._isSupported(config):
563      return []
564    else:
565      actions = self._actions(config) if callable(self._actions) else self._actions
566      return [AddFeature(self._getName(config))] + actions
567
568  def pretty(self, config):
569    """
570    Returns the Feature's name.
571    """
572    return self._getName(config)
573
574
575def _str_to_bool(s):
576  """
577  Convert a string value to a boolean.
578
579  True values are "y", "yes", "t", "true", "on" and "1", regardless of capitalization.
580  False values are "n", "no", "f", "false", "off" and "0", regardless of capitalization.
581  """
582  trueVals = ["y", "yes", "t", "true", "on", "1"]
583  falseVals = ["n", "no", "f", "false", "off", "0"]
584  lower = s.lower()
585  if lower in trueVals:
586    return True
587  elif lower in falseVals:
588    return False
589  else:
590    raise ValueError("Got string '{}', which isn't a valid boolean".format(s))
591
592def _parse_parameter(s, type):
593  if type is bool and isinstance(s, str):
594    return _str_to_bool(s)
595  elif type is list and isinstance(s, str):
596    return [x.strip() for x in s.split(',') if x.strip()]
597  return type(s)
598
599
600class Parameter(object):
601  """
602  Represents a parameter of a Lit test suite.
603
604  Parameters are used to customize the behavior of test suites in a user
605  controllable way. There are two ways of setting the value of a Parameter.
606  The first one is to pass `--param <KEY>=<VALUE>` when running Lit (or
607  equivalently to set `litConfig.params[KEY] = VALUE` somewhere in the
608  Lit configuration files. This method will set the parameter globally for
609  all test suites being run.
610
611  The second method is to set `config.KEY = VALUE` somewhere in the Lit
612  configuration files, which sets the parameter only for the test suite(s)
613  that use that `config` object.
614
615  Parameters can have multiple possible values, and they can have a default
616  value when left unspecified. They can also have any number of ConfigActions
617  associated to them, in which case the actions will be performed on the
618  TestingConfig if the parameter is enabled. Depending on the actions
619  associated to a Parameter, it may be an error to enable the Parameter
620  if some actions are not supported in the given configuration. For example,
621  trying to set the compilation standard to C++23 when `-std=c++23` is not
622  supported by the compiler would be an error.
623  """
624  def __init__(self, name, type, help, actions, choices=None, default=None):
625    """
626    Create a Lit parameter to customize the behavior of a test suite.
627
628    - name
629        The name of the parameter that can be used to set it on the command-line.
630        On the command-line, the parameter can be set using `--param <name>=<value>`
631        when running Lit. This must be non-empty.
632
633    - choices
634        An optional non-empty set of possible values for this parameter. If provided,
635        this must be anything that can be iterated. It is an error if the parameter
636        is given a value that is not in that set, whether explicitly or through a
637        default value.
638
639    - type
640        A callable that can be used to parse the value of the parameter given
641        on the command-line. As a special case, using the type `bool` also
642        allows parsing strings with boolean-like contents, and the type `list`
643        will parse a string delimited by commas into a list of the substrings.
644
645    - help
646        A string explaining the parameter, for documentation purposes.
647        TODO: We should be able to surface those from the Lit command-line.
648
649    - actions
650        A callable that gets passed the parsed value of the parameter (either
651        the one passed on the command-line or the default one), and that returns
652        a list of ConfigAction to perform given the value of the parameter.
653        All the ConfigAction must be supported in the given configuration.
654
655    - default
656        An optional default value to use for the parameter when no value is
657        provided on the command-line. If the default value is a callable, it
658        is called with the TestingConfig and should return the default value
659        for the parameter. Whether the default value is computed or specified
660        directly, it must be in the 'choices' provided for that Parameter.
661    """
662    self._name = name
663    if len(self._name) == 0:
664      raise ValueError("Parameter name must not be the empty string")
665
666    if choices is not None:
667      self._choices = list(choices) # should be finite
668      if len(self._choices) == 0:
669        raise ValueError("Parameter '{}' must be given at least one possible value".format(self._name))
670    else:
671      self._choices = None
672
673    self._parse = lambda x: _parse_parameter(x, type)
674    self._help = help
675    self._actions = actions
676    self._default = default
677
678  def _getValue(self, config, litParams):
679    """
680    Return the value of the parameter given the configuration objects.
681    """
682    param = getattr(config, self.name, None)
683    param = litParams.get(self.name, param)
684    if param is None and self._default is None:
685      raise ValueError("Parameter {} doesn't have a default value, but it was not specified in the Lit parameters or in the Lit config".format(self.name))
686    getDefault = lambda: self._default(config) if callable(self._default) else self._default
687
688    if param is not None:
689      (pretty, value) = (param, self._parse(param))
690    else:
691      value = getDefault()
692      pretty = '{} (default)'.format(value)
693
694    if self._choices and value not in self._choices:
695      raise ValueError("Got value '{}' for parameter '{}', which is not in the provided set of possible choices: {}".format(value, self.name, self._choices))
696    return (pretty, value)
697
698  @property
699  def name(self):
700    """
701    Return the name of the parameter.
702
703    This is the name that can be used to set the parameter on the command-line
704    when running Lit.
705    """
706    return self._name
707
708  def getActions(self, config, litParams):
709    """
710    Return the list of actions associated to this value of the parameter.
711    """
712    (_, parameterValue) = self._getValue(config, litParams)
713    return self._actions(parameterValue)
714
715  def pretty(self, config, litParams):
716    """
717    Return a pretty representation of the parameter's name and value.
718    """
719    (prettyParameterValue, _) = self._getValue(config, litParams)
720    return "{}={}".format(self.name, prettyParameterValue)
721