Files
OpenSpace/support/coding/check_style_guide.py
Alexander Bock 2ebc803cb7 Reduce dependency of onscreengui module in libOpenspace
Add check to `check_style_guide` that reports wrong dependencies
2017-02-16 16:54:03 -05:00

346 lines
10 KiB
Python

"""
OpenSpace
Copyright (c) 2014-2017
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This script traverses the file tree of OpenSpace and will check all files' include
guards for correctness. At the moment this includes:
* Correctness (file has a #ifndef. #define, and #endif lines)
* Equality (using the same name for the #ifdef and #define)
* Styling
* no empty line between #ifndef and #define lines
* Empty lines before and after #ifndef #define block
* Files end with an empty line
* Copyright header is correctly indented
* Include guard correctly uses the filename
* Include guard is all upper case
* Correct usage of the name in the final comment of the file
* Correct year of copyright notice
* Naming convention
* OpenSpace include guards start with OPENSPACE, Ghoul with GHOUL,
module includes have the module name in it
* The correct submodule is used
* Checking for duplicates between all files
* Checking that no file includes glm header directly
If this script is executed from the base directory of OpenSpace, no arguments need to
be passed, otherwise the first and only argument has to point to the base directory.
Thus, the default value of the first argument is '.'
"""
import fnmatch
import glob
import os
import re
import sys
current_year = '2017'
def get_ifndef_symbol(lines):
index = [i for i,s in enumerate(lines) if '#ifndef ' in s]
if len(index) == 0:
return '', -1
result = re.search('#ifndef (.*)\n', lines[index[0]])
return result.group(1), index[0]
def get_define_symbol(lines):
index = [i for i,s in enumerate(lines) if '#define ' in s]
if len(index) == 0:
return '', -1
result = re.search('#define (.*)\n', lines[index[0]])
return result.group(1), index[0]
def check_correctness(lines):
ifndef_symbol, line_number = get_ifndef_symbol(lines)
if line_number == -1:
return 'No #ifndef in file'
define_symbol, line_number = get_define_symbol(lines)
if (line_number == -1):
return 'No #define in file'
index = [i for i,s in enumerate(lines) if '#endif' in s]
if len(index) == 0:
return 'No #endif in file'
return ''
def check_equality(lines):
ifndef, _ = get_ifndef_symbol(lines)
define, _ = get_define_symbol(lines)
if ifndef == define:
return ''
else:
return ifndef + ' ' + define
def check_styling(lines):
ifndef_symbol, ifndef_line = get_ifndef_symbol(lines)
_, define_line = get_define_symbol(lines)
if abs(ifndef_line - define_line) != 1:
return '#ifndef and #define lines are not subsequent'
if lines[ifndef_line - 1].strip() != '':
return 'Preceding line is not empty'
if lines[define_line + 1].strip() != '':
return 'Following line is not empty'
if not lines[-1][-1] in ['\n', '\r']:
return 'Last line must end with a newline'
for l in lines[2:23]:
if l[0] != ' ':
return 'Copyright header must be indented'
if ifndef_symbol != ifndef_symbol.upper():
return 'Include guard is not all upper case'
return ''
def check_styling_filename(lines, filename):
ifndef_symbol, _ = get_ifndef_symbol(lines)
file = os.path.splitext(os.path.basename(filename))[0].upper()
if not (file in ifndef_symbol or file in ifndef_symbol.replace('_', '')):
return 'Malformed include guard: ' + ifndef_symbol + ' || ' + file
def check_comment(lines):
ifndef_symbol, _ = get_ifndef_symbol(lines)
index = [i for i,s in enumerate(lines) if '#endif' in s]
endif_line = lines[index[-1]].strip()
if endif_line != '#endif // ' + ifndef_symbol:
return '#endif line is not correctly formatted'
else:
return ''
def check_copyright(lines):
index = [i for i,s in enumerate(lines[0:23]) if 'Copyright' in s]
if len(index) == 0:
return 'No copyright header found'
beginning_string = ' * Copyright (c) 2012-'
# * Copyright (c) 2014-
year = lines[index[0]][len(beginning_string) : len(beginning_string) + 4]
if year != current_year:
return 'Out of date copyright notice ' + year + ' || ' + current_year
else:
return ''
def check_naming_convention_component(lines, component):
ifndef_symbol, _ = get_ifndef_symbol(lines)
component_part = ifndef_symbol[2:2 + len(component)]
if component_part != component.upper():
return '#ifndef naming convention broken: ' + ifndef_symbol + ' || ' + component.upper()
else:
return ''
def check_naming_convention_subcomponent(lines, component, file):
ifndef_symbol, _ = get_ifndef_symbol(lines)
if component == "ghoul" or component == "openspace_core":
return
subcomponent_part = ifndef_symbol[2 + len(component) + 1 :]
subcomponent_part = subcomponent_part[: subcomponent_part.find('_')]
path_part = file.split(os.sep)[2]
if path_part.upper() != subcomponent_part:
return 'Subcomponent naming convention broken: ' + ifndef_symbol
else:
return ''
def check_duplicates(lines, previousSymbols):
ifndef_symbol, _ = get_ifndef_symbol(lines)
if ifndef_symbol in previousSymbols:
return False, ifndef_symbol
else:
return True, ifndef_symbol
def check_glm_header(lines, file):
Allowed_Files = [
'ghoul/glm.h'
]
for f in Allowed_Files:
if f in file:
return ''
index = [i for i,s in enumerate(lines)
if '#include <glm/glm.hpp>' in s or
'#include "glm/glm.hpp>"' in s]
if len(index) > 0:
return 'File used wrong glm include. Use "#include <ghoul/glm.h>" instead'
else:
return ''
def check_core_dependency(lines, component):
if component != "openspace_core":
return ''
index = [i for i,s in enumerate(lines) if 'OPENSPACE_MODULE_' in s]
if len(index) > 0:
return lines[index[0]][:-1]
else:
return ''
previousSymbols = {}
def check_header_file(file, component):
with open(file, 'r+') as f:
lines = f.readlines()
correctness = check_correctness(lines)
if correctness:
print(file, '\t', 'Correctness check failed', '\t', correctness)
return
equality = check_equality(lines)
if equality:
print(file, '\t', 'Equality check failed', '\t', equality)
return
styling = check_styling(lines)
if styling:
print(file, '\t', 'Styling check failed', '\t', styling)
return
styling_filename = check_styling_filename(lines, file)
if styling_filename:
print(file, '\t', 'Filename styling check failed', '\t', styling_filename)
return
comment = check_comment(lines)
if comment:
print(file, '\t', 'Comment check failed', '\t', comment)
return
copyright = check_copyright(lines)
if copyright:
print(file, '\t', 'Copyright check failed', '\t', copyright)
return
naming_component = check_naming_convention_component(lines, component)
if naming_component:
print(file, '\t', 'Naming convention broken', '\t', naming_component)
return
naming_subcomponent = check_naming_convention_subcomponent(lines, component, file)
if naming_subcomponent:
print(file, '\t', 'Naming convention broken', '\t', naming_subcomponent)
return
duplicates, symbol = check_duplicates(lines, previousSymbols)
if not duplicates:
print(file, '\t', 'Duplicate include guard', symbol, 'first in', previousSymbols[symbol])
return
else:
previousSymbols[symbol] = file
header = check_glm_header(lines, file)
if header:
print(file, '\t', 'Illegal glm header include', header)
return
core_dependency = check_core_dependency(lines, component)
if core_dependency:
print(file, '\t' 'Wrong core dependency', core_dependency)
def check_source_file(file, component):
with open(file, 'r+') as f:
lines = f.readlines()
header = check_glm_header(lines, file)
if header:
print(file, '\t', 'Illegal glm header include', header)
return
core_dependency = check_core_dependency(lines, component)
if core_dependency:
print(file, '\t' 'Wrong core dependency', core_dependency)
def check_files(positiveList, negativeList, component, check_function):
files = glob.glob(positiveList, recursive=True)
negativeFiles = glob.glob(negativeList, recursive=True)
files = [f for f in files if f not in negativeFiles]
for file in files:
check_function(file, component)
basePath = './'
if len(sys.argv) > 1:
basePath = sys.argv[1] + '/'
check_files(basePath + 'include/**/*.h', '', 'openspace_core', check_header_file)
check_files(basePath + 'apps/**/*.h', basePath + 'apps/**/ext/**/*.h', 'openspace_app', check_header_file)
check_files(basePath + 'modules/**/*.h', basePath + 'modules/**/ext/**/*.h', 'openspace_module', check_header_file)
check_files(basePath + 'ext/ghoul/include/**/*.h', '', 'ghoul', check_header_file)
check_files(basePath + 'src/**/*.cpp', '', 'openspace_core', check_source_file)
check_files(basePath + 'apps/**/*.cpp', basePath + 'apps/**/ext/**/*.cpp', 'openspace_app', check_source_file)
check_files(basePath + 'modules/**/*.cpp', basePath + 'modules/**/ext/**/*.cpp', 'openspace_module', check_source_file)
check_files(basePath + 'ext/ghoul/include/**/*.cpp', '', 'ghoul', check_source_file)