MockToolset.py 8.5 KB

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