summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/build/test/toolset-mock/src/MockProgram.py
diff options
context:
space:
mode:
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.py287
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())