diff options
Diffstat (limited to '')
-rwxr-xr-x | CMakeScripts/cmake_consistency_check.py | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/CMakeScripts/cmake_consistency_check.py b/CMakeScripts/cmake_consistency_check.py new file mode 100755 index 0000000..d79c480 --- /dev/null +++ b/CMakeScripts/cmake_consistency_check.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 + +# $Id: cmake_consistency_check.py 38869 2011-07-31 03:15:37Z campbellbarton $ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Campbell Barton +# +# ***** END GPL LICENSE BLOCK ***** + +# <pep8 compliant> + +import sys +if not sys.version.startswith("3"): + print("\nPython3.x needed, found %s.\nAborting!\n" % + sys.version.partition(" ")[0]) + sys.exit(1) + +from cmake_consistency_check_config import ( + IGNORE, + UTF8_CHECK, + SOURCE_DIR, +) + + +import os +from os.path import join, dirname, normpath, splitext + +global_h = set() +global_c = set() +global_refs = {} + + +def replace_line(f, i, text, keep_indent=True): + file_handle = open(f, 'r') + data = file_handle.readlines() + file_handle.close() + + l = data[i] + ws = l[:len(l) - len(l.lstrip())] + + data[i] = "%s%s\n" % (ws, text) + + file_handle = open(f, 'w') + file_handle.writelines(data) + file_handle.close() + + +def source_list(path, filename_check=None): + for dirpath, dirnames, filenames in os.walk(path): + + # skip '.git' + if dirpath.startswith("."): + continue + + for filename in filenames: + if filename_check is None or filename_check(filename): + yield os.path.join(dirpath, filename) + + +# extension checking +def is_cmake(filename): + ext = splitext(filename)[1] + return (ext == ".cmake") or (filename == "CMakeLists.txt") + + +def is_c_header(filename): + ext = splitext(filename)[1] + return (ext in {".h", ".hpp", ".hxx", ".hh"}) + + +def is_c(filename): + ext = splitext(filename)[1] + return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl"}) + + +def is_c_any(filename): + return is_c(filename) or is_c_header(filename) + + +def cmake_get_src(f): + + sources_h = [] + sources_c = [] + + filen = open(f, "r", encoding="utf8") + it = iter(filen) + found = False + i = 0 + # print(f) + + def is_definition(l, f, i, name): + if l.startswith("unset("): + return False + + if ('set(%s' % name) in l or ('set(' in l and l.endswith(name)): + if len(l.split()) > 1: + raise Exception("strict formatting not kept 'set(%s*' %s:%d" % (name, f, i)) + return True + + if ("list(APPEND %s" % name) in l or ('list(APPEND ' in l and l.endswith(name)): + if l.endswith(")"): + raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i)) + return True + + while it is not None: + context_name = "" + while it is not None: + i += 1 + try: + l = next(it) + except StopIteration: + it = None + break + l = l.strip() + if not l.startswith("#"): + found = is_definition(l, f, i, "SRC") + if found: + context_name = "SRC" + break + found = is_definition(l, f, i, "INC") + if found: + context_name = "INC" + break + + if found: + cmake_base = dirname(f) + + while it is not None: + i += 1 + try: + l = next(it) + except StopIteration: + it = None + break + + l = l.strip() + + if not l.startswith("#"): + + if ")" in l: + if l.strip() != ")": + raise Exception("strict formatting not kept '*)' %s:%d" % (f, i)) + break + + # replace dirs + l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base) + l = l.strip('"') + + if not l: + pass + elif l.startswith("$"): + if context_name == "SRC": + # assume if it ends with context_name we know about it + if not l.split("}")[0].endswith(context_name): + print("Can't use var '%s' %s:%d" % (l, f, i)) + elif len(l.split()) > 1: + raise Exception("Multi-line define '%s' %s:%d" % (l, f, i)) + else: + new_file = normpath(join(cmake_base, l)) + + if context_name == "SRC": + if is_c_header(new_file): + sources_h.append(new_file) + global_refs.setdefault(new_file, []).append((f, i)) + elif is_c(new_file): + sources_c.append(new_file) + global_refs.setdefault(new_file, []).append((f, i)) + elif l in {"PARENT_SCOPE", }: + # cmake var, ignore + pass + elif new_file.endswith(".list"): + pass + elif new_file.endswith(".def"): + pass + elif new_file.endswith(".cl"): # opencl + pass + elif new_file.endswith(".cu"): # cuda + pass + elif new_file.endswith(".osl"): # open shading language + pass + elif new_file.endswith(".glsl"): + pass + else: + raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file)) + + elif context_name == "INC": + if os.path.isdir(new_file): + new_path_rel = os.path.relpath(new_file, cmake_base) + + if new_path_rel != l: + print("overly relative path:\n %s:%d\n %s\n %s" % (f, i, l, new_path_rel)) + + # # Save time. just replace the line + # replace_line(f, i - 1, new_path_rel) + + else: + raise Exception("non existent include %s:%d -> %s" % (f, i, new_file)) + + # print(new_file) + + global_h.update(set(sources_h)) + global_c.update(set(sources_c)) + ''' + if not sources_h and not sources_c: + raise Exception("No sources %s" % f) + + sources_h_fs = list(source_list(cmake_base, is_c_header)) + sources_c_fs = list(source_list(cmake_base, is_c)) + ''' + # find missing C files: + ''' + for ff in sources_c_fs: + if ff not in sources_c: + print(" missing: " + ff) + ''' + + # reset + del sources_h[:] + del sources_c[:] + + filen.close() + + +def is_ignore(f, ignore_used): + for index, ig in enumerate(IGNORE): + if ig in f: + ignore_used[index] = True + return True + return False + + +def main(): + + print("Scanning:", SOURCE_DIR) + + for cmake in source_list(SOURCE_DIR, is_cmake): + cmake_get_src(cmake) + + # First do stupid check, do these files exist? + print("\nChecking for missing references:") + is_err = False + errs = [] + for f in (global_h | global_c): + + if not os.path.exists(f): + refs = global_refs[f] + if refs: + for cf, i in refs: + errs.append((cf, i)) + else: + raise Exception("CMake referenecs missing, internal error, aborting!") + is_err = True + + errs.sort() + errs.reverse() + for cf, i in errs: + print("%s:%d" % (cf, i)) + # Write a 'sed' script, useful if we get a lot of these + # print("sed '%dd' '%s' > '%s.tmp' ; mv '%s.tmp' '%s'" % (i, cf, cf, cf, cf)) + + if is_err: + raise Exception("CMake referenecs missing files, aborting!") + del is_err + del errs + + ignore_used = [False] * len(IGNORE) + + # now check on files not accounted for. + print("\nC/C++ Files CMake does not know about...") + for cf in sorted(source_list(SOURCE_DIR, is_c)): + if not is_ignore(cf, ignore_used): + if cf not in global_c: + print("missing_c: ", cf) + + # check if automake builds a corrasponding .o file. + ''' + if cf in global_c: + out1 = os.path.splitext(cf)[0] + ".o" + out2 = os.path.splitext(cf)[0] + ".Po" + out2_dir, out2_file = out2 = os.path.split(out2) + out2 = os.path.join(out2_dir, ".deps", out2_file) + if not os.path.exists(out1) and not os.path.exists(out2): + print("bad_c: ", cf) + ''' + + print("\nC/C++ Headers CMake does not know about...") + for hf in sorted(source_list(SOURCE_DIR, is_c_header)): + if not is_ignore(hf, ignore_used): + if hf not in global_h: + print("missing_h: ", hf) + + if UTF8_CHECK: + # test encoding + import traceback + for files in (global_c, global_h): + for f in sorted(files): + if os.path.exists(f): + # ignore outside of our source tree + if "extern" not in f: + i = 1 + try: + for l in open(f, "r", encoding="utf8"): + i += 1 + except UnicodeDecodeError: + print("Non utf8: %s:%d" % (f, i)) + if i > 1: + traceback.print_exc() + + # Check ignores aren't stale + print("\nCheck for unused 'IGNORE' paths...") + for index, ig in enumerate(IGNORE): + if not ignore_used[index]: + print("unused ignore: %r" % ig) + + +if __name__ == "__main__": + main() |