1#!/usr/local/bin/python3.8 2 3# Copyright (C) 2013 Steven Watanabe 4# Distributed under the Boost Software License, Version 1.0. 5# (See accompanying file LICENSE_1_0.txt or copy at 6# http://www.boost.org/LICENSE_1_0.txt) 7 8import sys 9 10def create(t): 11 t.write('''mockinfo.py''', ''' 12from __future__ import print_function 13import re 14import optparse 15import os 16 17parser = optparse.OptionParser() 18parser.add_option('-o', dest="output_file") 19parser.add_option('-x', dest="language") 20parser.add_option('-c', dest="compile", action="store_true") 21parser.add_option('-I', dest="includes", action="append") 22parser.add_option('-D', dest="defines", action="append") 23parser.add_option('-L', dest="library_path", action="append") 24parser.add_option('--dll', dest="dll", action="store_true") 25parser.add_option('--archive', dest="archive", action="store_true") 26parser.add_option('--static-lib', dest="static_libraries", action="append") 27parser.add_option('--shared-lib', dest="shared_libraries", action="append") 28 29cwd = os.environ["JAM_CWD"] 30 31class MockInfo(object): 32 def __init__(self, verbose=False): 33 self.files = dict() 34 self.commands = list() 35 self.verbose = verbose 36 def source_file(self, name, pattern): 37 self.files[name] = pattern 38 def action(self, command, status=0): 39 if isinstance(command, str): 40 command = command.split() 41 self.commands.append((command, status)) 42 def check(self, command): 43 print("Testing command", command) 44 for (raw, status) in self.commands: 45 if self.matches(raw, command): 46 return status 47 def matches(self, raw, command): 48 (expected_options, expected_args) = parser.parse_args(raw) 49 options = command[0] 50 input_files = list(command[1]) 51 if self.verbose: 52 print(" - matching against", (expected_options, expected_args)) 53 if len(expected_args) != len(input_files): 54 if self.verbose: 55 print(" argument list sizes differ") 56 return False 57 for arg in expected_args: 58 if arg.startswith('$'): 59 fileid = arg[1:] 60 pattern = self.files[fileid] if fileid in self.files else fileid 61 matching_file = None 62 for input_file in input_files: 63 with open(input_file, 'r') as f: 64 contents = f.read() 65 if pattern == contents: 66 matching_file = input_file 67 break 68 if matching_file is not None: 69 input_files.remove(matching_file) 70 else: 71 if self.verbose: 72 print(" Failed to match input file contents: %s" % arg) 73 return False 74 else: 75 if arg in input_files: 76 input_files.remove(arg) 77 else: 78 if self.verbose: 79 print(" Failed to match input file: %s" % arg) 80 return False 81 82 if options.language != expected_options.language: 83 if self.verbose: 84 print(" Failed to match -c") 85 return False 86 87 if options.compile != expected_options.compile: 88 if self.verbose: 89 print(" Failed to match -x") 90 return False 91 92 # Normalize a path for comparison purposes 93 def adjust_path(p): 94 return os.path.normcase(os.path.normpath(os.path.join(cwd, p))) 95 96 # order matters 97 if options.includes is None: 98 options.includes = [] 99 if expected_options.includes is None: 100 expected_options.includes = [] 101 if list(map(adjust_path, options.includes)) != \ 102 list(map(adjust_path, expected_options.includes)): 103 if self.verbose: 104 print(" Failed to match -I ", list(map(adjust_path, options.includes)), \ 105 " != ", list(map(adjust_path, expected_options.includes))) 106 return False 107 108 if options.defines is None: 109 options.defines = [] 110 if expected_options.defines is None: 111 expected_options.defines = [] 112 if options.defines != expected_options.defines: 113 if self.verbose: 114 print(" Failed to match -I ", options.defines, \ 115 " != ", expected_options.defines) 116 return False 117 118 if options.library_path is None: 119 options.library_path = [] 120 if expected_options.library_path is None: 121 expected_options.library_path = [] 122 if list(map(adjust_path, options.library_path)) != \ 123 list(map(adjust_path, expected_options.library_path)): 124 if self.verbose: 125 print(" Failed to match -L ", list(map(adjust_path, options.library_path)), \ 126 " != ", list(map(adjust_path, expected_options.library_path))) 127 return False 128 129 if options.static_libraries != expected_options.static_libraries: 130 if self.verbose: 131 print(" Failed to match --static-lib") 132 return False 133 134 if options.shared_libraries != expected_options.shared_libraries: 135 if self.verbose: 136 print(" Failed to match --shared-lib") 137 return False 138 139 if options.dll != expected_options.dll: 140 if self.verbose: 141 print(" Failed to match --dll") 142 return False 143 144 if options.archive != expected_options.archive: 145 if self.verbose: 146 print(" Failed to match --archive") 147 return False 148 149 # The output must be handled after everything else 150 # is validated 151 if expected_options.output_file is not None: 152 if options.output_file is not None: 153 if expected_options.output_file.startswith('$'): 154 fileid = expected_options.output_file[1:] 155 if fileid not in self.files: 156 self.files[fileid] = fileid 157 else: 158 assert(self.files[fileid] == fileid) 159 with open(options.output_file, 'w') as output: 160 output.write(fileid) 161 else: 162 if self.verbose: 163 print("Failed to match -o") 164 return False 165 elif options.output_file is not None: 166 if self.verbose: 167 print("Failed to match -o") 168 return False 169 170 # if we've gotten here, then everything matched 171 if self.verbose: 172 print(" Matched") 173 return True 174''') 175 176 t.write('mock.py', ''' 177from __future__ import print_function 178import mockinfo 179import markup 180import sys 181 182status = markup.info.check(mockinfo.parser.parse_args()) 183if status is not None: 184 exit(status) 185else: 186 print("Unrecognized command: " + ' '.join(sys.argv)) 187 exit(1) 188''') 189 190 t.write('mock.jam', ''' 191import feature ; 192import toolset ; 193import path ; 194import modules ; 195import common ; 196import type ; 197 198.python-cmd = "\"%s\"" ; 199 200# Behave the same as gcc on Windows, because that's what 201# the test system expects 202type.set-generated-target-prefix SHARED_LIB : <toolset>mock <target-os>windows : lib ; 203type.set-generated-target-suffix STATIC_LIB : <toolset>mock <target-os>windows : a ; 204 205rule init ( ) 206{ 207 local here = [ path.make [ modules.binding $(__name__) ] ] ; 208 here = [ path.native [ path.root [ path.parent $(here) ] [ path.pwd ] ] ] ; 209 .config-cmd = [ common.variable-setting-command JAM_CWD : $(here) ] $(.python-cmd) -B ; 210} 211 212feature.extend toolset : mock ; 213 214generators.register-c-compiler mock.compile.c++ : CPP : OBJ : <toolset>mock ; 215generators.register-c-compiler mock.compile.c : C : OBJ : <toolset>mock ; 216 217generators.register-linker mock.link : LIB OBJ : EXE : <toolset>mock ; 218generators.register-linker mock.link.dll : LIB OBJ : SHARED_LIB : <toolset>mock ; 219generators.register-archiver mock.archive : OBJ : STATIC_LIB : <toolset>mock ; 220 221toolset.flags mock.compile OPTIONS <link>shared : -fPIC ; 222toolset.flags mock.compile INCLUDES : <include> ; 223toolset.flags mock.compile DEFINES : <define> ; 224 225actions compile.c 226{ 227 $(.config-cmd) mock.py -c -x c -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" 228} 229 230actions compile.c++ 231{ 232 $(.config-cmd) mock.py -c -x c++ -I"$(INCLUDES)" -D"$(DEFINES)" "$(>)" -o "$(<)" 233} 234 235toolset.flags mock.link USER_OPTIONS <linkflags> ; 236toolset.flags mock.link FINDLIBS-STATIC <find-static-library> ; 237toolset.flags mock.link FINDLIBS-SHARED <find-shared-library> ; 238toolset.flags mock.link LINK_PATH <library-path> ; 239toolset.flags mock.link LIBRARIES <library-file> ; 240 241actions link 242{ 243 $(.config-cmd) mock.py "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED) 244} 245 246actions archive 247{ 248 $(.config-cmd) mock.py --archive "$(>)" -o "$(<)" $(USER_OPTIONS) 249} 250 251actions link.dll 252{ 253 $(.config-cmd) mock.py --dll "$(>)" -o "$(<)" $(USER_OPTIONS) -L"$(LINK_PATH)" --static-lib=$(FINDLIBS-STATIC) --shared-lib=$(FINDLIBS-SHARED) 254} 255 256''' % sys.executable.replace('\\', '\\\\')) 257 258def set_expected(t, markup): 259 verbose = "True" if t.verbose else "False" 260 t.write('markup.py', ''' 261import mockinfo 262info = mockinfo.MockInfo(%s) 263def source_file(name, contents): 264 info.source_file(name, contents) 265def action(command, status=0): 266 info.action(command, status) 267''' % (verbose) + markup) 268