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