|
Packit |
58578d |
#!/usr/bin/env python
|
|
Packit |
58578d |
|
|
Packit |
58578d |
# Copyright Rene Rivera 2016
|
|
Packit |
58578d |
#
|
|
Packit |
58578d |
# Distributed under the Boost Software License, Version 1.0.
|
|
Packit |
58578d |
# (See accompanying file LICENSE_1_0.txt or copy at
|
|
Packit |
58578d |
# http://www.boost.org/LICENSE_1_0.txt)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
import os
|
|
Packit |
58578d |
import inspect
|
|
Packit |
58578d |
import optparse
|
|
Packit |
58578d |
import sys
|
|
Packit |
58578d |
import glob
|
|
Packit |
58578d |
import fnmatch
|
|
Packit |
58578d |
import json
|
|
Packit |
58578d |
|
|
Packit |
58578d |
class check_library():
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
This is a collection of checks for a library to test if a library
|
|
Packit |
58578d |
follows the Boost C++ Libraries requirements and guidelines. It also
|
|
Packit |
58578d |
checks for possible and likely errors in the library.
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def __init__(self):
|
|
Packit |
58578d |
self.main()
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization(self):
|
|
Packit |
58578d |
self.run_batch('check_organization_')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization_build(self):
|
|
Packit |
58578d |
if os.path.isdir(os.path.join(self.library_dir, 'build')):
|
|
Packit |
58578d |
self.assert_file_exists(os.path.join(self.library_dir, 'build'), self.jamfile,
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find a Boost Build file in the [project-root]/build directory.
|
|
Packit |
58578d |
The library needs to provide a Boost Build project that the user,
|
|
Packit |
58578d |
and the top level Boost project, can use to build the library if it
|
|
Packit |
58578d |
has sources to build.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-build-ok')
|
|
Packit |
58578d |
if os.path.isdir(os.path.join(self.library_dir, 'src')):
|
|
Packit |
58578d |
self.assert_dir_exists(os.path.join(self.library_dir,'build'),
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Missing [project-root]/build directory. The [project-root]/build directory
|
|
Packit |
58578d |
is required for libraries that have a [project-root]/src directory.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-build-src')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization_doc(self):
|
|
Packit |
58578d |
self.assert_file_exists(self.library_dir, ['index.html'],
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find [project-root]/index.html file.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
The file is required for all libraries. Redirection to HTML documentation.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-doc-redir')
|
|
Packit |
58578d |
self.assert_dir_exists(os.path.join(self.library_dir,'doc'),
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Missing [project-root]/doc directory. The [project-root]/doc directory
|
|
Packit |
58578d |
is required for all libraries.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
Sources to build with and built documentation for the library. If the
|
|
Packit |
58578d |
library needs to build documentation from non-HTML files this location
|
|
Packit |
58578d |
must be buildable with Boost Build.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-doc-dir')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization_include(self):
|
|
Packit |
58578d |
if os.path.isdir(os.path.join(self.library_dir,'include','boost',self.library_name)):
|
|
Packit |
58578d |
self.warn_file_exists(os.path.join(self.library_dir,'include','boost'), ['*.h*'],
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Found extra files in [project-root]/include/boost directory.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-inc-extra',
|
|
Packit |
58578d |
negate = True,
|
|
Packit |
58578d |
globs_to_exclude = ['%s.h*'%(self.library_name)])
|
|
Packit |
58578d |
else:
|
|
Packit |
58578d |
self.warn_file_exists(os.path.join(self.library_dir,'include','boost'), ['%s.h*'%(self.library_name)],
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find [project-root]/include/boost/[library].h* file.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
A single header for the library is suggested at [project-root]/include/boost/[library].h*
|
|
Packit |
58578d |
if the library does not have a header directory at [project-root]/include/boost/[library].
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-inc-one')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization_meta(self):
|
|
Packit |
58578d |
parent_dir = os.path.dirname(self.library_dir)
|
|
Packit |
58578d |
# If this is a sublibrary it's possible that the library information is the
|
|
Packit |
58578d |
# parent library's meta/libraries.json. Otherwise it's a regular library
|
|
Packit |
58578d |
# and structure.
|
|
Packit |
58578d |
if not self.test_dir_exists(os.path.join(self.library_dir,'meta')) \
|
|
Packit |
58578d |
and self.test_file_exists(os.path.join(parent_dir,'meta'),['libraries.json']):
|
|
Packit |
58578d |
if self.get_library_meta():
|
|
Packit |
58578d |
return
|
|
Packit |
58578d |
self.assert_file_exists(os.path.join(self.library_dir, 'meta'), ['libraries.json'],
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find [project-root]/meta/libraries.json file, nor did
|
|
Packit |
58578d |
[super-project]/meta/libraries.json contain an entry for the sublibrary.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
The file is required for all libraries. And contains information about
|
|
Packit |
58578d |
the library used to generate website and documentation for the
|
|
Packit |
58578d |
Boost C++ Libraries collection.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-meta-libs')
|
|
Packit |
58578d |
elif self.assert_dir_exists(os.path.join(self.library_dir,'meta'),
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Missing [project-root]/meta directory. The [project-root]/meta directory
|
|
Packit |
58578d |
is required for all libraries.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-meta-dir'):
|
|
Packit |
58578d |
self.assert_file_exists(os.path.join(self.library_dir, 'meta'), ['libraries.json'],
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find [project-root]/meta/libraries.json file.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
The file is required for all libraries. And contains information about
|
|
Packit |
58578d |
the library used to generate website and documentation for the
|
|
Packit |
58578d |
Boost C++ Libraries collection.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-meta-libs')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def check_organization_test(self):
|
|
Packit |
58578d |
if self.assert_dir_exists(os.path.join(self.library_dir,'test'),
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Missing [project-root]/test directory. The [project-root]/test directory
|
|
Packit |
58578d |
is required for all libraries.
|
|
Packit |
58578d |
|
|
Packit |
58578d |
Regression or other test programs or scripts. This is the only location
|
|
Packit |
58578d |
considered for automated testing. If you have additional locations that
|
|
Packit |
58578d |
need to be part of automated testing it is required that this location
|
|
Packit |
58578d |
refer to the additional test locations.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-test-dir'):
|
|
Packit |
58578d |
self.assert_file_exists(os.path.join(self.library_dir, 'test'), self.jamfile,
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Did not find a Boost Build file in the [project-root]/test directory.
|
|
Packit |
58578d |
''',
|
|
Packit |
58578d |
'org-test-ok')
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def main(self):
|
|
Packit |
58578d |
commands = [];
|
|
Packit |
58578d |
for method in inspect.getmembers(self, predicate=inspect.ismethod):
|
|
Packit |
58578d |
if method[0].startswith('check_'):
|
|
Packit |
58578d |
commands.append(method[0][6:].replace('_','-'))
|
|
Packit |
58578d |
commands = "commands: %s" % ', '.join(commands)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
opt = optparse.OptionParser(
|
|
Packit |
58578d |
usage="%prog [options] [commands]",
|
|
Packit |
58578d |
description=commands)
|
|
Packit |
58578d |
opt.add_option('--boost-root')
|
|
Packit |
58578d |
opt.add_option('--library')
|
|
Packit |
58578d |
opt.add_option('--jamfile')
|
|
Packit |
58578d |
opt.add_option('--debug', action='store_true')
|
|
Packit |
58578d |
self.boost_root = None
|
|
Packit |
58578d |
self.library = None
|
|
Packit |
58578d |
self.jamfile = None
|
|
Packit |
58578d |
self.debug = False
|
|
Packit |
58578d |
( _opt_, self.actions ) = opt.parse_args(None,self)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
self.library_dir = os.path.join(self.boost_root, self.library)
|
|
Packit |
58578d |
self.error_count = 0;
|
|
Packit |
58578d |
self.jamfile = self.jamfile.split(';')
|
|
Packit |
58578d |
self.library_name = self.library.split('/',1)[1] #os.path.basename(self.library)
|
|
Packit |
58578d |
self.library_key = self.library.split('/',1)[1]
|
|
Packit |
58578d |
|
|
Packit |
58578d |
if self.debug:
|
|
Packit |
58578d |
print ">>> cwd: %s"%(os.getcwd())
|
|
Packit |
58578d |
print ">>> actions: %s"%(self.actions)
|
|
Packit |
58578d |
print ">>> boost_root: %s"%(self.boost_root)
|
|
Packit |
58578d |
print ">>> library: %s"%(self.library)
|
|
Packit |
58578d |
print ">>> jamfile: %s"%(self.jamfile)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
for action in self.actions:
|
|
Packit |
58578d |
action_m = "check_"+action.replace('-','_')
|
|
Packit |
58578d |
if hasattr(self,action_m):
|
|
Packit |
58578d |
getattr(self,action_m)()
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def run_batch(self, action_base, *args, **kargs):
|
|
Packit |
58578d |
for method in inspect.getmembers(self, predicate=inspect.ismethod):
|
|
Packit |
58578d |
if method[0].startswith(action_base):
|
|
Packit |
58578d |
getattr(self,method[0])(*args, **kargs)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def get_library_meta(self):
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
Fetches the meta data for the current library. The data could be in
|
|
Packit |
58578d |
the superlib meta data file. If we can't find the data None is returned.
|
|
Packit |
58578d |
'''
|
|
Packit |
58578d |
parent_dir = os.path.dirname(self.library_dir)
|
|
Packit |
58578d |
if self.test_file_exists(os.path.join(self.library_dir,'meta'),['libraries.json']):
|
|
Packit |
58578d |
with open(os.path.join(self.library_dir,'meta','libraries.json'),'r') as f:
|
|
Packit |
58578d |
meta_data = json.load(f)
|
|
Packit |
58578d |
if isinstance(meta_data,list):
|
|
Packit |
58578d |
for lib in meta_data:
|
|
Packit |
58578d |
if lib['key'] == self.library_key:
|
|
Packit |
58578d |
return lib
|
|
Packit |
58578d |
elif 'key' in meta_data and meta_data['key'] == self.library_key:
|
|
Packit |
58578d |
return meta_data
|
|
Packit |
58578d |
if not self.test_dir_exists(os.path.join(self.library_dir,'meta')) \
|
|
Packit |
58578d |
and self.test_file_exists(os.path.join(parent_dir,'meta'),['libraries.json']):
|
|
Packit |
58578d |
with open(os.path.join(parent_dir,'meta','libraries.json'),'r') as f:
|
|
Packit |
58578d |
libraries_json = json.load(f)
|
|
Packit |
58578d |
if isinstance(libraries_json,list):
|
|
Packit |
58578d |
for lib in libraries_json:
|
|
Packit |
58578d |
if lib['key'] == self.library_key:
|
|
Packit |
58578d |
return lib
|
|
Packit |
58578d |
return None
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def error(self, reason, message, key):
|
|
Packit |
58578d |
self.error_count += 1
|
|
Packit |
58578d |
print("%s: error: %s; %s <<%s>>"%(
|
|
Packit |
58578d |
self.library,
|
|
Packit |
58578d |
self.clean_message(reason),
|
|
Packit |
58578d |
self.clean_message(message),
|
|
Packit |
58578d |
key,
|
|
Packit |
58578d |
))
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def warn(self, reason, message, key):
|
|
Packit |
58578d |
print("%s: warning: %s; %s <<%s>>"%(
|
|
Packit |
58578d |
self.library,
|
|
Packit |
58578d |
self.clean_message(reason),
|
|
Packit |
58578d |
self.clean_message(message),
|
|
Packit |
58578d |
key,
|
|
Packit |
58578d |
))
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def info(self, message):
|
|
Packit |
58578d |
if self.debug:
|
|
Packit |
58578d |
print("%s: info: %s"%(self.library, self.clean_message(message)))
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def clean_message(self, message):
|
|
Packit |
58578d |
return " ".join(message.strip().split())
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def assert_dir_exists(self, dir, message, key, negate = False):
|
|
Packit |
58578d |
self.info("check directory '%s', negate = %s"%(dir,negate))
|
|
Packit |
58578d |
if os.path.isdir(dir):
|
|
Packit |
58578d |
if negate:
|
|
Packit |
58578d |
self.error("directory found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
else:
|
|
Packit |
58578d |
if not negate:
|
|
Packit |
58578d |
self.error("directory not found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
return True
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def warn_dir_exists(self, dir, message, key, negate = False):
|
|
Packit |
58578d |
self.info("check directory '%s', negate = %s"%(dir,negate))
|
|
Packit |
58578d |
if os.path.isdir(dir):
|
|
Packit |
58578d |
if negate:
|
|
Packit |
58578d |
self.warn("directory found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
else:
|
|
Packit |
58578d |
if not negate:
|
|
Packit |
58578d |
self.warn("directory not found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
return True
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def assert_file_exists(self, dir, globs_to_include, message, key, negate = False, globs_to_exclude = []):
|
|
Packit |
58578d |
found = self.test_file_exists(dir, globs_to_include = globs_to_include, globs_to_exclude = globs_to_exclude)
|
|
Packit |
58578d |
if negate:
|
|
Packit |
58578d |
if found:
|
|
Packit |
58578d |
self.error("file found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
else:
|
|
Packit |
58578d |
if not found:
|
|
Packit |
58578d |
self.error("file not found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
return True
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def warn_file_exists(self, dir, globs_to_include, message, key, negate = False, globs_to_exclude = []):
|
|
Packit |
58578d |
found = self.test_file_exists(dir, globs_to_include = globs_to_include, globs_to_exclude = globs_to_exclude)
|
|
Packit |
58578d |
if negate:
|
|
Packit |
58578d |
if found:
|
|
Packit |
58578d |
self.warn("file found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
else:
|
|
Packit |
58578d |
if not found:
|
|
Packit |
58578d |
self.warn("file not found", message, key)
|
|
Packit |
58578d |
return False
|
|
Packit |
58578d |
return True
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def test_dir_exists(self, dir):
|
|
Packit |
58578d |
return os.path.isdir(dir)
|
|
Packit |
58578d |
|
|
Packit |
58578d |
def test_file_exists(self, dir, globs_to_include, globs_to_exclude = []):
|
|
Packit |
58578d |
self.info("test file(s) in dir '%s', include = '%s', exclude = %s"%(dir,globs_to_include,globs_to_exclude))
|
|
Packit |
58578d |
found = False
|
|
Packit |
58578d |
if os.path.isdir(dir):
|
|
Packit |
58578d |
for g in globs_to_include:
|
|
Packit |
58578d |
for f in glob.iglob(os.path.join(dir,g)):
|
|
Packit |
58578d |
exclude = False
|
|
Packit |
58578d |
for ge in globs_to_exclude:
|
|
Packit |
58578d |
if fnmatch.fnmatch(os.path.basename(f),ge):
|
|
Packit |
58578d |
exclude = True
|
|
Packit |
58578d |
found = not exclude
|
|
Packit |
58578d |
if found:
|
|
Packit |
58578d |
break
|
|
Packit |
58578d |
return found
|
|
Packit |
58578d |
|
|
Packit |
58578d |
if check_library().error_count > 0:
|
|
Packit |
58578d |
sys.exit(1)
|
|
Packit |
58578d |
|