boost_check_library.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #!/usr/bin/env python
  2. # Copyright Rene Rivera 2016
  3. #
  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. import os
  8. import inspect
  9. import optparse
  10. import sys
  11. import glob
  12. import fnmatch
  13. import json
  14. class check_library():
  15. '''
  16. This is a collection of checks for a library to test if a library
  17. follows the Boost C++ Libraries requirements and guidelines. It also
  18. checks for possible and likely errors in the library.
  19. '''
  20. def __init__(self):
  21. self.main()
  22. def check_organization(self):
  23. self.run_batch('check_organization_')
  24. def check_organization_build(self):
  25. if os.path.isdir(os.path.join(self.library_dir, 'build')):
  26. self.assert_file_exists(os.path.join(self.library_dir, 'build'), self.jamfile,
  27. '''
  28. Did not find a Boost Build file in the [project-root]/build directory.
  29. The library needs to provide a Boost Build project that the user,
  30. and the top level Boost project, can use to build the library if it
  31. has sources to build.
  32. ''',
  33. 'org-build-ok')
  34. if os.path.isdir(os.path.join(self.library_dir, 'src')):
  35. self.assert_dir_exists(os.path.join(self.library_dir,'build'),
  36. '''
  37. Missing [project-root]/build directory. The [project-root]/build directory
  38. is required for libraries that have a [project-root]/src directory.
  39. ''',
  40. 'org-build-src')
  41. def check_organization_doc(self):
  42. self.assert_file_exists(self.library_dir, ['index.html'],
  43. '''
  44. Did not find [project-root]/index.html file.
  45. The file is required for all libraries. Redirection to HTML documentation.
  46. ''',
  47. 'org-doc-redir')
  48. self.assert_dir_exists(os.path.join(self.library_dir,'doc'),
  49. '''
  50. Missing [project-root]/doc directory. The [project-root]/doc directory
  51. is required for all libraries.
  52. Sources to build with and built documentation for the library. If the
  53. library needs to build documentation from non-HTML files this location
  54. must be buildable with Boost Build.
  55. ''',
  56. 'org-doc-dir')
  57. def check_organization_include(self):
  58. if os.path.isdir(os.path.join(self.library_dir,'include','boost',self.library_name)):
  59. self.warn_file_exists(os.path.join(self.library_dir,'include','boost'), ['*.h*'],
  60. '''
  61. Found extra files in [project-root]/include/boost directory.
  62. ''',
  63. 'org-inc-extra',
  64. negate = True,
  65. globs_to_exclude = ['%s.h*'%(self.library_name)])
  66. else:
  67. self.warn_file_exists(os.path.join(self.library_dir,'include','boost'), ['%s.h*'%(self.library_name)],
  68. '''
  69. Did not find [project-root]/include/boost/[library].h* file.
  70. A single header for the library is suggested at [project-root]/include/boost/[library].h*
  71. if the library does not have a header directory at [project-root]/include/boost/[library].
  72. ''',
  73. 'org-inc-one')
  74. def check_organization_meta(self):
  75. parent_dir = os.path.dirname(self.library_dir)
  76. # If this is a sublibrary it's possible that the library information is the
  77. # parent library's meta/libraries.json. Otherwise it's a regular library
  78. # and structure.
  79. if not self.test_dir_exists(os.path.join(self.library_dir,'meta')) \
  80. and self.test_file_exists(os.path.join(parent_dir,'meta'),['libraries.json']):
  81. if self.get_library_meta():
  82. return
  83. self.assert_file_exists(os.path.join(self.library_dir, 'meta'), ['libraries.json'],
  84. '''
  85. Did not find [project-root]/meta/libraries.json file, nor did
  86. [super-project]/meta/libraries.json contain an entry for the sublibrary.
  87. The file is required for all libraries. And contains information about
  88. the library used to generate website and documentation for the
  89. Boost C++ Libraries collection.
  90. ''',
  91. 'org-meta-libs')
  92. elif self.assert_dir_exists(os.path.join(self.library_dir,'meta'),
  93. '''
  94. Missing [project-root]/meta directory. The [project-root]/meta directory
  95. is required for all libraries.
  96. ''',
  97. 'org-meta-dir'):
  98. self.assert_file_exists(os.path.join(self.library_dir, 'meta'), ['libraries.json'],
  99. '''
  100. Did not find [project-root]/meta/libraries.json file.
  101. The file is required for all libraries. And contains information about
  102. the library used to generate website and documentation for the
  103. Boost C++ Libraries collection.
  104. ''',
  105. 'org-meta-libs')
  106. def check_organization_test(self):
  107. if self.assert_dir_exists(os.path.join(self.library_dir,'test'),
  108. '''
  109. Missing [project-root]/test directory. The [project-root]/test directory
  110. is required for all libraries.
  111. Regression or other test programs or scripts. This is the only location
  112. considered for automated testing. If you have additional locations that
  113. need to be part of automated testing it is required that this location
  114. refer to the additional test locations.
  115. ''',
  116. 'org-test-dir'):
  117. self.assert_file_exists(os.path.join(self.library_dir, 'test'), self.jamfile,
  118. '''
  119. Did not find a Boost Build file in the [project-root]/test directory.
  120. ''',
  121. 'org-test-ok')
  122. def main(self):
  123. commands = [];
  124. for method in inspect.getmembers(self, predicate=inspect.ismethod):
  125. if method[0].startswith('check_'):
  126. commands.append(method[0][6:].replace('_','-'))
  127. commands = "commands: %s" % ', '.join(commands)
  128. opt = optparse.OptionParser(
  129. usage="%prog [options] [commands]",
  130. description=commands)
  131. opt.add_option('--boost-root')
  132. opt.add_option('--library')
  133. opt.add_option('--jamfile')
  134. opt.add_option('--debug', action='store_true')
  135. self.boost_root = None
  136. self.library = None
  137. self.jamfile = None
  138. self.debug = False
  139. ( _opt_, self.actions ) = opt.parse_args(None,self)
  140. self.library_dir = os.path.join(self.boost_root, self.library)
  141. self.error_count = 0;
  142. self.jamfile = self.jamfile.split(';')
  143. self.library_name = self.library.split('/',1)[1] #os.path.basename(self.library)
  144. self.library_key = self.library.split('/',1)[1]
  145. if self.debug:
  146. print(">>> cwd: %s"%(os.getcwd()))
  147. print(">>> actions: %s"%(self.actions))
  148. print(">>> boost_root: %s"%(self.boost_root))
  149. print(">>> library: %s"%(self.library))
  150. print(">>> jamfile: %s"%(self.jamfile))
  151. for action in self.actions:
  152. action_m = "check_"+action.replace('-','_')
  153. if hasattr(self,action_m):
  154. getattr(self,action_m)()
  155. def run_batch(self, action_base, *args, **kargs):
  156. for method in inspect.getmembers(self, predicate=inspect.ismethod):
  157. if method[0].startswith(action_base):
  158. getattr(self,method[0])(*args, **kargs)
  159. def get_library_meta(self):
  160. '''
  161. Fetches the meta data for the current library. The data could be in
  162. the superlib meta data file. If we can't find the data None is returned.
  163. '''
  164. parent_dir = os.path.dirname(self.library_dir)
  165. if self.test_file_exists(os.path.join(self.library_dir,'meta'),['libraries.json']):
  166. with open(os.path.join(self.library_dir,'meta','libraries.json'),'r') as f:
  167. meta_data = json.load(f)
  168. if isinstance(meta_data,list):
  169. for lib in meta_data:
  170. if lib['key'] == self.library_key:
  171. return lib
  172. elif 'key' in meta_data and meta_data['key'] == self.library_key:
  173. return meta_data
  174. if not self.test_dir_exists(os.path.join(self.library_dir,'meta')) \
  175. and self.test_file_exists(os.path.join(parent_dir,'meta'),['libraries.json']):
  176. with open(os.path.join(parent_dir,'meta','libraries.json'),'r') as f:
  177. libraries_json = json.load(f)
  178. if isinstance(libraries_json,list):
  179. for lib in libraries_json:
  180. if lib['key'] == self.library_key:
  181. return lib
  182. return None
  183. def error(self, reason, message, key):
  184. self.error_count += 1
  185. print("%s: error: %s; %s <<%s>>"%(
  186. self.library,
  187. self.clean_message(reason),
  188. self.clean_message(message),
  189. key,
  190. ))
  191. def warn(self, reason, message, key):
  192. print("%s: warning: %s; %s <<%s>>"%(
  193. self.library,
  194. self.clean_message(reason),
  195. self.clean_message(message),
  196. key,
  197. ))
  198. def info(self, message):
  199. if self.debug:
  200. print("%s: info: %s"%(self.library, self.clean_message(message)))
  201. def clean_message(self, message):
  202. return " ".join(message.strip().split())
  203. def assert_dir_exists(self, dir, message, key, negate = False):
  204. self.info("check directory '%s', negate = %s"%(dir,negate))
  205. if os.path.isdir(dir):
  206. if negate:
  207. self.error("directory found", message, key)
  208. return False
  209. else:
  210. if not negate:
  211. self.error("directory not found", message, key)
  212. return False
  213. return True
  214. def warn_dir_exists(self, dir, message, key, negate = False):
  215. self.info("check directory '%s', negate = %s"%(dir,negate))
  216. if os.path.isdir(dir):
  217. if negate:
  218. self.warn("directory found", message, key)
  219. return False
  220. else:
  221. if not negate:
  222. self.warn("directory not found", message, key)
  223. return False
  224. return True
  225. def assert_file_exists(self, dir, globs_to_include, message, key, negate = False, globs_to_exclude = []):
  226. found = self.test_file_exists(dir, globs_to_include = globs_to_include, globs_to_exclude = globs_to_exclude)
  227. if negate:
  228. if found:
  229. self.error("file found", message, key)
  230. return False
  231. else:
  232. if not found:
  233. self.error("file not found", message, key)
  234. return False
  235. return True
  236. def warn_file_exists(self, dir, globs_to_include, message, key, negate = False, globs_to_exclude = []):
  237. found = self.test_file_exists(dir, globs_to_include = globs_to_include, globs_to_exclude = globs_to_exclude)
  238. if negate:
  239. if found:
  240. self.warn("file found", message, key)
  241. return False
  242. else:
  243. if not found:
  244. self.warn("file not found", message, key)
  245. return False
  246. return True
  247. def test_dir_exists(self, dir):
  248. return os.path.isdir(dir)
  249. def test_file_exists(self, dir, globs_to_include, globs_to_exclude = []):
  250. self.info("test file(s) in dir '%s', include = '%s', exclude = %s"%(dir,globs_to_include,globs_to_exclude))
  251. found = False
  252. if os.path.isdir(dir):
  253. for g in globs_to_include:
  254. for f in glob.iglob(os.path.join(dir,g)):
  255. exclude = False
  256. for ge in globs_to_exclude:
  257. if fnmatch.fnmatch(os.path.basename(f),ge):
  258. exclude = True
  259. found = not exclude
  260. if found:
  261. break
  262. return found
  263. if check_library().error_count > 0:
  264. sys.exit(1)
粤ICP备19079148号