diff options
Diffstat (limited to 'src/boost/tools/build/test/toolset-mock/src/MockProgram.py')
-rw-r--r-- | src/boost/tools/build/test/toolset-mock/src/MockProgram.py | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/boost/tools/build/test/toolset-mock/src/MockProgram.py b/src/boost/tools/build/test/toolset-mock/src/MockProgram.py new file mode 100644 index 000000000..f0cc858d7 --- /dev/null +++ b/src/boost/tools/build/test/toolset-mock/src/MockProgram.py @@ -0,0 +1,287 @@ +# coding: utf-8 +# Copyright 2017 Steven Watanabe +# Copyright 2020 René Ferdinand Rivera Morell +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.bfgroup.xyz/b2/LICENSE.txt) + +from __future__ import print_function + +import sys +import os +import re +import fnmatch + +# Represents a sequence of arguments that must appear +# in a fixed order. +class ordered: + def __init__(self, *args): + self.args = args + def match(self, command_line, pos, outputs): + for p in self.args: + res = try_match(command_line, pos, p, outputs) + if res is None: + return + pos = res + return pos + +# Represents a sequence of arguments that can appear +# in any order. +class unordered: + def __init__(self, *args): + self.args = list(args) + def match(self, command_line, pos, outputs): + unmatched = self.args[:] + while len(unmatched) > 0: + res = try_match_one(command_line, pos, unmatched, outputs) + if res is None: + return + pos = res + return pos + +# Represents a single input file. +# If id is set, then the file must have been created +# by a prior use of output_file. +# If source is set, then the file must be that source file. +class input_file: + def __init__(self, id=None, source=None): + assert((id is None) ^ (source is None)) + self.id = id + self.source = source + def check(self, path): + if path.startswith("-"): + return + if self.id is not None: + try: + with open(path, "r") as f: + data = f.read() + if data == make_file_contents(self.id): + return True + else: + return + except: + return + elif self.source is not None: + if self.source == path: + return True + else: + return + assert(False) + def match(self, command_line, pos, outputs): + if self.check(command_line[pos]): + return pos + 1 + +# Matches an output file. +# If the full pattern is matched, The +# file will be created. +class output_file: + def __init__(self, id): + self.id = id + def match(self, command_line, pos, outputs): + if command_line[pos].startswith("-"): + return + outputs.append((command_line[pos], self.id)) + return pos + 1 + +class arg_file: + def __init__(self, id): + self.id = id + def match(self, command_line, pos, outputs): + if command_line[pos].startswith("-"): + return + if fnmatch.fnmatch(command_line[pos], self.id): + return pos + 1 + else: + return + +# Matches the directory containing an input_file +class target_path(object): + def __init__(self, id): + self.tester = input_file(id=id) + def match(self, command_line, pos, outputs): + arg = command_line[pos] + if arg.startswith("-"): + return + try: + for path in os.listdir(arg): + if self.tester.check(os.path.join(arg, path)): + return pos + 1 + except: + return + +# Matches a single argument, which is composed of a prefix and a path +# for example arguments of the form -ofilename. +class arg(object): + def __init__(self, prefix, a): + # The prefix should be a string, a should be target_path or input_file. + self.prefix = prefix + self.a = a + def match(self, command_line, pos, outputs): + s = command_line[pos] + if s.startswith(self.prefix) and try_match([s[len(self.prefix):]], 0, self.a, outputs) == 1: + return pos + 1 + +# +class opt(object): + def __init__(self, *args): + self.args = args + def match(self, command_line, pos, outputs): + for p in self.args: + res = try_match_one(command_line, pos, p, outputs) + if res is not None: + pos = res + return pos + +# Given a file id, returns a string that will be +# written to the file to allow it to be recognized. +def make_file_contents(id): + return id + +# Matches a single pattern from a list. +# If it succeeds, the matching pattern +# is removed from the list. +# Returns the index after the end of the match +def try_match_one(command_line, pos, patterns, outputs): + for p in patterns: + tmp = outputs[:] + res = try_match(command_line, pos, p, tmp) + if res is not None: + outputs[:] = tmp + patterns.remove(p) + return res + +# returns the end of the match if any +def try_match(command_line, pos, pattern, outputs): + if pos == len(command_line): + return + elif type(pattern) is str: + if pattern == command_line[pos]: + return pos + 1 + else: + return pattern.match(command_line, pos, outputs) + +known_patterns = [] +program_name = None + +# Registers a command +# The arguments should be a sequence of: +# str, ordered, unordered, arg, input_file, output_file, target_path +# kwarg: stdout is text that will be printed on success. +def command(*args, **kwargs): + global known_patterns + global program_name + stdout = kwargs.get("stdout", None) + pattern = ordered(*args) + known_patterns += [(pattern, stdout)] + if program_name is None: + program_name = args[0] + else: + assert(program_name == args[0]) + +# Use this to filter the recognized commands, based on the properties +# passed to b2. +def allow_properties(*args): + try: + return all(a in os.environ["B2_PROPERTIES"].split(" ") for a in args) + except KeyError: + return True + +# Use this in the stdout argument of command to print the command +# for running another script. +def script(name): + return os.path.join(os.path.dirname(__file__), "bin", re.sub('\.py$', '', name)) + +def match(command_line): + for (p, stdout) in known_patterns: + outputs = [] + if try_match(command_line, 0, p, outputs) == len(command_line): + return (stdout, outputs) + +# Every mock program should call this after setting up all the commands. +def main(): + command_line = [program_name] + sys.argv[1:] + result = match(command_line) + if result is not None: + (stdout, outputs) = result + if stdout is not None: + print(stdout) + for (file,id) in outputs: + with open(file, "w") as f: + f.write(make_file_contents(id)) + exit(0) + else: + print("ERROR on command: %s"%(" ".join(command_line))) + exit(1) + +# file should be the name of a file in the same directory +# as this. Must be called after verify_setup +def verify_file(filename): + global known_files + if filename not in known_files: + known_files.add(filename) + srcdir = os.path.dirname(__file__) + execfile(os.path.join(srcdir, filename), {}) + +def verify_setup(): + """Override the behavior of most module components + in order to detect whether they are being used correctly.""" + global main + global allow_properties + global output_file + global input_file + global target_path + global script + global command + global verify_errors + global output_ids + global input_ids + global known_files + def allow_properties(*args): + return True + def main(): + pass + def output_file(id): + global output_ids + global verify_error + if id in output_ids: + verify_error("duplicate output_file: %s" % id) + output_ids.add(id) + def input_file(id=None, source=None): + if id is not None: + input_ids.add(id) + def target_path(id): + input_ids.add(id) + def script(filename): + verify_file(filename) + def command(*args, **kwargs): + pass + verify_errors = [] + output_ids = set() + input_ids = set() + known_files = set() + +def verify_error(message): + global verify_errors + verify_errors += [message] + +def verify_finalize(): + for id in input_ids: + if not id in output_ids: + verify_error("Input file does not exist: %s" % id) + for error in verify_errors: + print("error: %s" % error) + if len(verify_errors) != 0: + return 1 + else: + return 0 + +def verify(): + srcdir = os.path.dirname(__file__) + if srcdir == '': + srcdir = '.' + verify_setup() + for f in os.listdir(srcdir): + if re.match(r"(gcc|clang|darwin|intel)-.*\.py", f): + verify_file(f) + exit(verify_finalize()) |