#!/usr/bin/python # Copyright (C) 2013 Steven Watanabe # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE.txt or copy at # https://www.bfgroup.xyz/b2/LICENSE.txt) import sys def create(t): t.write('''mockinfo.py''', ''' from __future__ import print_function import re import optparse import os parser = optparse.OptionParser() parser.add_option('-o', dest="output_file") parser.add_option('-x', dest="language") parser.add_option('-c', dest="compile", action="store_true") parser.add_option('-I', dest="includes", action="append") parser.add_option('-D', dest="defines", action="append") parser.add_option('-L', dest="library_path", action="append") parser.add_option('--dll', dest="dll", action="store_true") parser.add_option('--archive', dest="archive", action="store_true") parser.add_option('--static-lib', dest="static_libraries", action="append") parser.add_option('--shared-lib', dest="shared_libraries", action="append") cwd = os.environ["JAM_CWD"] class MockInfo(object): def __init__(self, verbose=False): self.files = dict() self.commands = list() self.verbose = verbose def source_file(self, name, pattern): self.files[name] = pattern def action(self, command, status=0): if isinstance(command, str): command = command.split() self.commands.append((command, status)) def check(self, command): print("Testing command", command) for (raw, status) in self.commands: if self.matches(raw, command): return status def matches(self, raw, command): (expected_options, expected_args) = parser.parse_args(raw) options = command[0] input_files = list(command[1]) if self.verbose: print(" - matching against", (expected_options, expected_args)) if len(expected_args) != len(input_files): if self.verbose: print(" argument list sizes differ") return False for arg in expected_args: if arg.startswith('$'): fileid = arg[1:] pattern = self.files[fileid] if fileid in self.files else fileid matching_file = None for input_file in input_files: with open(input_file, 'r') as f: contents = f.read() if pattern == contents: matching_file = input_file break if matching_file is not None: input_files.remove(matching_file) else: if self.verbose: print(" Failed to match input file contents: %s" % arg) return False else: if arg in input_files: input_files.remove(arg) else: if self.verbose: print(" Failed to match input file: %s" % arg) return False if options.language != expected_options.language: if self.verbose: print(" Failed to match -c") return False if options.compile != expected_options.compile: if self.verbose: print(" Failed to match -x") return False # Normalize a path for comparison purposes def adjust_path(p): return os.path.normcase(os.path.normpath(os.path.join(cwd, p))) # order matters if options.includes is None: options.includes = [] if expected_options.includes is None: expected_options.includes = [] if list(map(adjust_path, options.includes)) != \ list(map(adjust_path, expected_options.includes)): if self.verbose: print(" Failed to match -I ", list(map(adjust_path, options.includes)), \ " != ", list(map(adjust_path, expected_options.includes))) return False if options.defines is None: options.defines = [] if expected_options.defines is None: expected_options.defines = [] if options.defines != expected_options.defines: if self.verbose: print(" Failed to match -I ", options.defines, \ " != ", expected_options.defines) return False if options.library_path is None: options.library_path = [] if expected_options.library_path is None: expected_options.library_path = [] if list(map(adjust_path, options.library_path)) != \ list(map(adjust_path, expected_options.library_path)): if self.verbose: print(" Failed to match -L ", list(map(adjust_path, options.library_path)), \ " != ", list(map(adjust_path, expected_options.library_path))) return False if options.static_libraries != expected_options.static_libraries: if self.verbose: print(" Failed to match --static-lib") return False if options.shared_libraries != expected_options.shared_libraries: if self.verbose: print(" Failed to match --shared-lib") return False if options.dll != expected_options.dll: if self.verbose: print(" Failed to match --dll") return False if options.archive != expected_options.archive: if self.verbose: print(" Failed to match --archive") return False # The output must be handled after everything else # is validated if expected_options.output_file is not None: if options.output_file is not None: if expected_options.output_file.startswith('$'): fileid = expected_options.output_file[1:] if fileid not in self.files: self.files[fileid] = fileid else: assert(self.files[fileid] == fileid) with open(options.output_file, 'w') as output: output.write(fileid) else: if self.verbose: print("Failed to match -o") return False elif options.output_file is not None: if self.verbose: print("Failed to match -o") return False # if we've gotten here, then everything matched if self.verbose: print(" Matched") return True ''') t.write('mock.py', ''' from __future__ import print_function import mockinfo import markup import sys status = markup.info.check(mockinfo.parser.parse_args()) if status is not None: exit(status) else: print("Unrecognized command: " + ' '.join(sys.argv)) exit(1) ''') t.write('mock.jam', ''' import feature ; import toolset ; import path ; import modules ; import common ; import type ; .python-cmd = "\"%s\"" ; # Behave the same as gcc on Windows, because that's what # the test system expects type.set-generated-target-prefix SHARED_LIB : mock windows : lib ; type.set-generated-target-suffix STATIC_LIB : mock windows : a ; rule init ( ) { local here = [ path.make [ modules.binding $(__name__) ] ] ; here = [ path.native [ path.root [ path.parent $(here) ] [ path.pwd ] ] ] ; .config-cmd = [ common.variable-setting-command JAM_CWD : $(here) ] $(.python-cmd) -B ; } feature.extend toolset : mock ; generators.register-c-compiler mock.compile.c++ : CPP : OBJ : mock ; generators.register-c-compiler mock.compile.c : C : OBJ : mock ; generators.register-linker mock.link : LIB OBJ : EXE : mock ; generators.register-linker mock.link.dll : LIB OBJ : SHARED_LIB : mock ; generators.register-archiver mock.archive : OBJ : STATIC_LIB : mock ; toolset.flags mock.compile OPTIONS shared : -fPIC ; toolset.flags mock.compile INCLUDES : ; toolset.flags mock.compile DEFINES : ; actions compile.c { $(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" } actions compile.c++ { $(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" } toolset.flags mock.link USER_OPTIONS ; toolset.flags mock.link FINDLIBS-STATIC ; toolset.flags mock.link FINDLIBS-SHARED ; toolset.flags mock.link LINK_PATH ; toolset.flags mock.link LIBRARIES ; actions link { $(.config-cmd) mock.py "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED) } actions archive { $(.config-cmd) mock.py --archive "$(>)" -o "$(<)" $(USER_OPTIONS) } actions link.dll { $(.config-cmd) mock.py --dll "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED) } ''' % sys.executable.replace('\\', '\\\\')) def set_expected(t, markup): verbose = "True" if t.verbose else "False" t.write('markup.py', ''' import mockinfo info = mockinfo.MockInfo(%s) def source_file(name, contents): info.source_file(name, contents) def action(command, status=0): info.action(command, status) ''' % (verbose) + markup)