diff --git a/check_source/check_style_c.py b/check_source/check_style_c.py
index e8507211d137d85d5ced0e4afc87c2b827ea8e0d..d3d40314f1fd1da92e16a6af86be43b87c23ecaf 100755
--- a/check_source/check_style_c.py
+++ b/check_source/check_style_c.py
@@ -48,6 +48,12 @@ def is_ignore(f):
     return False
 
 
+def hash_of_file(fp):
+    import hashlib
+    with open(fp, 'rb') as fh:
+        return hashlib.sha512(fh.read()).digest()
+
+
 # TODO
 #
 # Add checks for:
@@ -238,7 +244,7 @@ def extract_ws_indent(index):
     return text[:len(text) - len(text.lstrip("\t"))]
 
 
-def extract_statement_if(index_kw):
+def extract_statement_if(fn, index_kw):
     # assert(tokens[index_kw].text == "if")
 
     # ignore preprocessor
@@ -257,7 +263,7 @@ def extract_statement_if(index_kw):
     # print(tokens[i_next])
 
     if tokens[i_next].type != Token.Punctuation or tokens[i_next].text != "(":
-        warning("E105", "no '(' after '%s'" % tokens[index_kw].text, i_start, i_next)
+        warning(fn, "E105", "no '(' after '%s'" % tokens[index_kw].text, i_start, i_next)
         return None
 
     i_end = tk_match_backet(i_next)
@@ -372,20 +378,20 @@ def tk_range_find_by_type(index_start, index_end, type_, filter_tk=None):
     return -1
 
 
-def warning(id_, message, index_kw_start, index_kw_end):
+def warning(fn, id_, message, index_kw_start, index_kw_end):
     if PRINT_QTC_TASKFORMAT:
-        print("%s\t%d\t%s\t%s %s" % (filepath, tokens[index_kw_start].line, "comment", id_, message))
+        fn("%s\t%d\t%s\t%s %s" % (filepath, tokens[index_kw_start].line, "comment", id_, message))
     else:
-        print("%s:%d: %s: %s" % (filepath, tokens[index_kw_start].line, id_, message))
+        fn("%s:%d: %s: %s" % (filepath, tokens[index_kw_start].line, id_, message))
         if WARN_TEXT:
-            print(tk_range_to_str(index_kw_start, index_kw_end, expand_tabs=True))
+            fn(tk_range_to_str(index_kw_start, index_kw_end, expand_tabs=True))
 
 
-def warning_lineonly(id_, message, line):
+def warning_lineonly(fn, id_, message, line):
     if PRINT_QTC_TASKFORMAT:
-        print("%s\t%d\t%s\t%s %s" % (filepath, line, "comment", id_, message))
+        fn("%s\t%d\t%s\t%s %s" % (filepath, line, "comment", id_, message))
     else:
-        print("%s:%d: %s: %s" % (filepath, line, id_, message))
+        fn("%s:%d: %s: %s" % (filepath, line, id_, message))
 
     # print(tk_range_to_str(index_kw_start, index_kw_end))
 
@@ -393,17 +399,17 @@ def warning_lineonly(id_, message, line):
 # ------------------------------------------------------------------
 # Own Blender rules here!
 
-def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
+def blender_check_kw_if(fn, index_kw_start, index_kw, index_kw_end):
 
     # check if we have: 'if('
     if not tk_item_is_ws(tokens[index_kw + 1]):
-        warning("E106", "no white space between '%s('" % tokens[index_kw].text, index_kw_start, index_kw_end)
+        warning(fn, "E106", "no white space between '%s('" % tokens[index_kw].text, index_kw_start, index_kw_end)
 
     # check for: ){
     index_next = tk_advance_ws_newline(index_kw_end, 1)
     if tokens[index_next].type == Token.Punctuation and tokens[index_next].text == "{":
         if not tk_item_is_ws_newline(tokens[index_next - 1]):
-            warning("E107", "no white space between trailing bracket '%s (){'" %
+            warning(fn, "E107", "no white space between trailing bracket '%s (){'" %
                     tokens[index_kw].text, index_kw_start, index_kw_end)
 
         # check for: if ()
@@ -420,7 +426,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
                 # allow this to go unnoticed
                 pass
             else:
-                warning("E108", "if body brace on a new line '%s ()\\n{'" %
+                warning(fn, "E108", "if body brace on a new line '%s ()\\n{'" %
                         tokens[index_kw].text, index_kw, index_kw_end)
     else:
         # no '{' on a multi-line if
@@ -437,7 +443,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
             #         b);
             #
             if not (tokens[index_next].type == Token.Punctuation and tokens[index_next].text == ";"):
-                warning("E109", "multi-line if should use a brace '%s (\\n\\n) statement;'" %
+                warning(fn, "E109", "multi-line if should use a brace '%s (\\n\\n) statement;'" %
                         tokens[index_kw].text, index_kw, index_kw_end)
 
         # check for correct single line use & indentation
@@ -461,7 +467,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
                                 has_sep = True
                                 break
                     if not has_sep:
-                        warning("E200", "bad single line indent '%s (...) {'" %
+                        warning(fn, "E200", "bad single line indent '%s (...) {'" %
                                 tokens[index_kw].text, index_kw, index_next)
                     del has_sep
                 del ws_kw, ws_end
@@ -479,7 +485,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
                     ws_kw = extract_ws_indent(index_kw)
                     ws_end = extract_ws_indent(index_end)
                     if len(ws_kw) + 1 != len(ws_end):
-                        warning("E201", "bad single line indent '%s (...) {'" %
+                        warning(fn, "E201", "bad single line indent '%s (...) {'" %
                                 tokens[index_kw].text, index_kw, index_end)
                     del ws_kw, ws_end
                 del index_end
@@ -492,7 +498,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
         #
         if tokens[index_kw_end].line == tokens[index_next].line:
             if not (tokens[index_next].type == Token.Punctuation and tokens[index_next].text == ";"):
-                warning("E103", "multi-line should use a on a new line '%s (\\n\\n) {'" %
+                warning(fn, "E103", "multi-line should use a on a new line '%s (\\n\\n) {'" %
                         tokens[index_kw].text, index_kw, index_kw_end)
 
         # Note: this could be split into its own function
@@ -538,7 +544,7 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
                         # needed for some comments
                         pass
                     else:
-                        warning("E110", "if body brace mult-line indent mismatch", i, i)
+                        warning(fn, "E110", "if body brace mult-line indent mismatch", i, i)
         del index_kw_bracket
         del ws_indent
         del l_last
@@ -550,18 +556,18 @@ def blender_check_kw_if(index_kw_start, index_kw, index_kw_end):
         index_final = tk_match_backet(index_next)
         index_final_step = tk_advance_no_ws(index_final, 1)
         if tokens[index_final_step].text == ";":
-            warning("E111", "semi-colon after brace '%s () { ... };'" %
+            warning(fn, "E111", "semi-colon after brace '%s () { ... };'" %
                     tokens[index_kw].text, index_final_step, index_final_step)
 
 
-def blender_check_kw_else(index_kw):
+def blender_check_kw_else(fn, index_kw):
     # for 'else if' use the if check.
     i_next = tk_advance_ws_newline(index_kw, 1)
 
     # check there is at least one space between:
     # else{
     if index_kw + 1 == i_next:
-        warning("E112", "else has no space between following brace 'else{'", index_kw, i_next)
+        warning(fn, "E112", "else has no space between following brace 'else{'", index_kw, i_next)
 
     # check if there are more than 1 spaces after else, but nothing after the following brace
     # else     {
@@ -577,7 +583,7 @@ def blender_check_kw_else(index_kw):
         # check if the next data after { is on a newline
         i_next_next = tk_advance_ws_newline(i_next, 1)
         if tokens[i_next].line != tokens[i_next_next].line:
-            warning("E113", "unneeded whitespace before brace 'else ... {'", index_kw, i_next)
+            warning(fn, "E113", "unneeded whitespace before brace 'else ... {'", index_kw, i_next)
 
     # this check only tests for:
     # else
@@ -596,7 +602,7 @@ def blender_check_kw_else(index_kw):
             if tokens[i_newline].text.startswith("#"):
                 pass
             else:
-                warning("E114", "else body brace on a new line 'else\\n{'", index_kw, i_next)
+                warning(fn, "E114", "else body brace on a new line 'else\\n{'", index_kw, i_next)
 
     # this check only tests for:
     # else
@@ -629,7 +635,7 @@ def blender_check_kw_else(index_kw):
                 # allow this to go unnoticed
                 pass
             else:
-                warning("E115", "else if is split by a new line 'else\\nif'", index_kw, i_next)
+                warning(fn, "E115", "else if is split by a new line 'else\\nif'", index_kw, i_next)
 
     # check
     # } else
@@ -637,10 +643,10 @@ def blender_check_kw_else(index_kw):
     i_prev = tk_advance_no_ws(index_kw, -1)
     if tokens[i_prev].type == Token.Punctuation and tokens[i_prev].text == "}":
         if tokens[index_kw].line == tokens[i_prev].line:
-            warning("E116", "else has no newline before the brace '} else'", i_prev, index_kw)
+            warning(fn, "E116", "else has no newline before the brace '} else'", i_prev, index_kw)
 
 
-def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end):
+def blender_check_kw_switch(fn, index_kw_start, index_kw, index_kw_end):
     # In this function we check the body of the switch
 
     # switch (value) {
@@ -680,7 +686,7 @@ def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end):
                         if ws_other_indent.isspace():
                             ws_test_other = ws_test[tokens[i].text]
                             if not ws_other_indent.startswith(ws_test_other):
-                                warning("E117", "%s is not indented enough" % tokens[i].text, i, i)
+                                warning(fn, "E117", "%s is not indented enough" % tokens[i].text, i, i)
 
                         if tokens[i].text == "case":
                             # while where here, check:
@@ -691,27 +697,27 @@ def blender_check_kw_switch(index_kw_start, index_kw, index_kw_end):
                             # can be None when the identifier isn't an 'int'
                             if i_case is not None:
                                 if tokens[i_case - 1].text.isspace():
-                                    warning("E132", "%s space before colon" % tokens[i].text, i, i_case)
+                                    warning(fn, "E132", "%s space before colon" % tokens[i].text, i, i_case)
                             del i_case
 
         else:
-            warning("E119", "switch isn't the first token in the line", index_kw_start, index_kw_end)
+            warning(fn, "E119", "switch isn't the first token in the line", index_kw_start, index_kw_end)
     else:
-        warning("E120", "switch brace missing", index_kw_start, index_kw_end)
+        warning(fn, "E120", "switch brace missing", index_kw_start, index_kw_end)
 
 
 def blender_check_kw_sizeof(index_kw):
     if tokens[index_kw + 1].text != "(":
-        warning("E121", "expected '%s('" % tokens[index_kw].text, index_kw, index_kw + 1)
+        warning(fn, "E121", "expected '%s('" % tokens[index_kw].text, index_kw, index_kw + 1)
 
 
-def blender_check_cast(index_kw_start, index_kw_end):
+def blender_check_cast(fn, index_kw_start, index_kw_end):
     # detect: '( float...'
     if tokens[index_kw_start + 1].text.isspace():
-        warning("E122", "cast has space after first bracket '( type...'", index_kw_start, index_kw_end)
+        warning(fn, "E122", "cast has space after first bracket '( type...'", index_kw_start, index_kw_end)
     # detect: '...float )'
     if tokens[index_kw_end - 1].text.isspace():
-        warning("E123", "cast has space before last bracket '... )'", index_kw_start, index_kw_end)
+        warning(fn, "E123", "cast has space before last bracket '... )'", index_kw_start, index_kw_end)
     # detect no space before operator: '(float*)'
 
     for i in range(index_kw_start + 1, index_kw_end):
@@ -722,10 +728,10 @@ def blender_check_cast(index_kw_start, index_kw_end):
             elif tokens[i - 1].text.isspace():
                 pass
             else:
-                warning("E124", "cast has no preceding whitespace '(type*)'", index_kw_start, index_kw_end)
+                warning(fn, "E124", "cast has no preceding whitespace '(type*)'", index_kw_start, index_kw_end)
 
 
-def blender_check_comma(index_kw):
+def blender_check_comma(fn, index_kw):
     i_next = tk_advance_ws_newline(index_kw, 1)
 
     # check there is at least one space between:
@@ -733,13 +739,13 @@ def blender_check_comma(index_kw):
     if index_kw + 1 == i_next:
         # allow: (struct FooBar){ .a, .b, .c,}
         if tokens[i_next].text != "}":
-            warning("E125", "comma has no space after it ',sometext'", index_kw, i_next)
+            warning(fn, "E125", "comma has no space after it ',sometext'", index_kw, i_next)
 
     if tokens[index_kw - 1].type == Token.Text and tokens[index_kw - 1].text.isspace():
-        warning("E126", "comma space before it 'sometext ,", index_kw, i_next)
+        warning(fn, "E126", "comma space before it 'sometext ,", index_kw, i_next)
 
 
-def blender_check_period(index_kw):
+def blender_check_period(fn, index_kw):
     # check we're now apart of ...
     if (tokens[index_kw - 1].text == ".") or (tokens[index_kw + 1].text == "."):
         return
@@ -751,9 +757,9 @@ def blender_check_period(index_kw):
         # ... check for this case by allowing comma or brace beforehand.
         i_prev = tk_advance_ws_newline(index_kw - 1, -1)
         if tokens[i_prev].text not in {",", "{"}:
-            warning("E127", "period space before it 'sometext .", index_kw, index_kw)
+            warning(fn, "E127", "period space before it 'sometext .", index_kw, index_kw)
     if tokens[index_kw + 1].type == Token.Text and tokens[index_kw + 1].text.isspace():
-        warning("E128", "period space after it '. sometext", index_kw, index_kw)
+        warning(fn, "E128", "period space after it '. sometext", index_kw, index_kw)
 
 
 def _is_ws_pad(index_start, index_end):
@@ -761,7 +767,7 @@ def _is_ws_pad(index_start, index_end):
             tokens[index_end + 1].text.isspace())
 
 
-def blender_check_operator(index_start, index_end, op_text, is_cpp):
+def blender_check_operator(fn, index_start, index_end, op_text, is_cpp):
     if op_text == "->":
         # allow compiler to handle
         return
@@ -783,17 +789,17 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
                     # Or:    (struct FooBar)&var
                     (tokens[index_prev].flag & IS_CAST)
             ):
-                warning("E130", "no space before operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "no space before operator '%s'" % op_text, index_start, index_end)
             if (not tokens[index_end + 1].text.isspace() and
                     tokens[index_end + 1].text not in {"]", ")", "}"}):
                 # TODO, needs work to be useful
-                # warning("E130", "no space after operator '%s'" % op_text, index_start, index_end)
+                # warning(fn, "E130", "no space after operator '%s'" % op_text, index_start, index_end)
                 pass
 
         elif op_text in {"/", "%", "^", "|", "=", "<", ">", "?"}:
             if not _is_ws_pad(index_start, index_end):
                 if not (is_cpp and ("<" in op_text or ">" in op_text)):
-                    warning("E130", "no space around operator '%s'" % op_text, index_start, index_end)
+                    warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end)
         elif op_text == ":":
             # check we're not
             #     case 1:
@@ -804,7 +810,7 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
             if not (tokens[index_start - 1].text.isspace() or tokens[index_start - 1].text == "default"):
                 i_case = tk_advance_line_to_token(index_start, -1, "case", Token.Keyword)
                 if i_case is None:
-                    warning("E130", "no space around operator '%s'" % op_text, index_start, index_end)
+                    warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end)
         elif op_text == "&":
             pass  # TODO, check if this is a pointer reference or not
         elif op_text == "*":
@@ -826,9 +832,9 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
                 pass
             elif ((tokens[index_start - 1].type in Token.Number) or
                     (tokens[index_start + 1].type in Token.Number)):
-                warning("E130", "no space around operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end)
             elif not (tokens[index_start - 1].text.isspace() or tokens[index_start - 1].text in {"(", "[", "{"}):
-                warning("E130", "no space before operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "no space before operator '%s'" % op_text, index_start, index_end)
     elif len(op_text) == 2:
         # todo, remove operator check from `if`
         if op_text in {"+=", "-=", "*=", "/=", "&=", "|=", "^=",
@@ -841,20 +847,20 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
                        }:
             if not _is_ws_pad(index_start, index_end):
                 if not (is_cpp and ("<" in op_text or ">" in op_text)):
-                    warning("E130", "no space around operator '%s'" % op_text, index_start, index_end)
+                    warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end)
 
         elif op_text in {"++", "--"}:
             pass  # TODO, figure out the side we are adding to!
             '''
             if     (tokens[index_start - 1].text.isspace() or
                     tokens[index_end   + 1].text.isspace()):
-                warning("E130", "spaces surrounding operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "spaces surrounding operator '%s'" % op_text, index_start, index_end)
             '''
         elif op_text in {"!!", "!*"}:
             # operators we _dont_ want whitespace after (pointers mainly)
             # we can assume these are pointers
             if tokens[index_end + 1].text.isspace():
-                warning("E130", "spaces after operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "spaces after operator '%s'" % op_text, index_start, index_end)
 
         elif op_text == "**":
             pass  # handle below
@@ -865,12 +871,12 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
         elif op_text == "*>":
             pass  # ignore for now, C++ <Class *>
         else:
-            warning("E000.0", "unhandled operator 2 '%s'" % op_text, index_start, index_end)
+            warning(fn, "E000.0", "unhandled operator 2 '%s'" % op_text, index_start, index_end)
     elif len(op_text) == 3:
         if op_text in {">>=", "<<="}:
             if not _is_ws_pad(index_start, index_end):
                 if not (is_cpp and ("<" in op_text or ">" in op_text)):
-                    warning("E130", "no space around operator '%s'" % op_text, index_start, index_end)
+                    warning(fn, "E130", "no space around operator '%s'" % op_text, index_start, index_end)
         elif op_text == "***":
             pass
         elif op_text in {"*--", "*++"}:
@@ -882,22 +888,22 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
         elif op_text == "::~":
             pass
         else:
-            warning("E000.1", "unhandled operator 3 '%s'" % op_text, index_start, index_end)
+            warning(fn, "E000.1", "unhandled operator 3 '%s'" % op_text, index_start, index_end)
     elif len(op_text) == 4:
         if op_text == "*>::":
             pass
         else:
-            warning("E000.2", "unhandled operator 4 '%s'" % op_text, index_start, index_end)
+            warning(fn, "E000.2", "unhandled operator 4 '%s'" % op_text, index_start, index_end)
     else:
-        warning("E000.3", "unhandled operator (len > 4) '%s'" % op_text, index_start, index_end)
+        warning(fn, "E000.3", "unhandled operator (len > 4) '%s'" % op_text, index_start, index_end)
 
     if len(op_text) > 1:
         if op_text[0] == "*" and op_text[-1] == "*":
             if ((not tokens[index_start - 1].text.isspace()) and
                     (not tokens[index_start - 1].type == Token.Punctuation)):
-                warning("E130", "no space before pointer operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "no space before pointer operator '%s'" % op_text, index_start, index_end)
             if tokens[index_end + 1].text.isspace():
-                warning("E130", "space before pointer operator '%s'" % op_text, index_start, index_end)
+                warning(fn, "E130", "space before pointer operator '%s'" % op_text, index_start, index_end)
 
     # check if we are first in the line
     if op_text[0] == "!":
@@ -930,18 +936,18 @@ def blender_check_operator(index_start, index_end, op_text, is_cpp):
         pass
     else:
         if tk_index_is_linestart(index_start):
-            warning("E143", "operator starts a new line '%s'" % op_text, index_start, index_end)
+            warning(fn, "E143", "operator starts a new line '%s'" % op_text, index_start, index_end)
 
 
-def blender_check_linelength(index_start, index_end, length):
+def blender_check_linelength(fn, index_start, index_end, length):
     if length > LIN_SIZE:
         text = tk_range_to_str(index_start, index_end, expand_tabs=True)
         for l in text.split("\n"):
             if len(l) > LIN_SIZE:
-                warning("E144", "line length %d > %d" % (len(l), LIN_SIZE), index_start, index_end)
+                warning(fn, "E144", "line length %d > %d" % (len(l), LIN_SIZE), index_start, index_end)
 
 
-def blender_check_function_definition(i):
+def blender_check_function_definition(fn, i):
     # Warning, this is a fairly slow check and guesses
     # based on some fuzzy rules
 
@@ -1006,10 +1012,10 @@ def blender_check_function_definition(i):
                         i_begin += 1
                     # now we are done skipping stuff
 
-                    warning("E101", "function's '{' must be on a newline", i_begin, i)
+                    warning(fn, "E101", "function's '{' must be on a newline", i_begin, i)
 
 
-def blender_check_brace_indent(i):
+def blender_check_brace_indent(fn, i):
     # assert(tokens[index].text == "{")
 
     i_match = tk_match_backet(i)
@@ -1028,10 +1034,10 @@ def blender_check_brace_indent(i):
             ws_i = ws_i[:len(ws_i) - len(ws_i.lstrip())]
             ws_i_match = ws_i_match[:len(ws_i_match) - len(ws_i_match_lstrip)]
             if ws_i != ws_i_match:
-                warning("E104", "indentation '{' does not match brace", i, i_match)
+                warning(fn, "E104", "indentation '{' does not match brace", i, i_match)
 
 
-def quick_check_includes(lines):
+def quick_check_includes(fn, lines):
     # Find overly relative headers (could check other things here too...)
 
     # header dupes
@@ -1051,19 +1057,19 @@ def quick_check_includes(lines):
                 if os.path.exists(l_header_full):
                     l_header_rel = os.path.relpath(l_header_full, base)
                     if l_header.count("/") != l_header_rel.count("/"):
-                        warning_lineonly("E170", "overly relative include %r" % l_header, i + 1)
+                        warning_lineonly(fn, "E170", "overly relative include %r" % l_header, i + 1)
 
             # check if we're in mode than once
             len_inc = len(inc)
             inc.add(l_header)
             if len(inc) == len_inc:
-                warning_lineonly("E171", "duplicate includes %r" % l_header, i + 1)
+                warning_lineonly(fn, "E171", "duplicate includes %r" % l_header, i + 1)
 
 
 quick_check_includes.re_inc_match = re.compile(r"\s*#\s*include\s+\"([a-zA-Z0-9_\-\.\/]+)\"").match
 
 
-def quick_check_indentation(lines):
+def quick_check_indentation(fn, lines):
     """
     Quick check for multiple tab indents.
     """
@@ -1091,8 +1097,9 @@ def quick_check_indentation(lines):
                     # we have indent, check previous line
                     if not ls_prev.rstrip().endswith("\\"):
                         # report indented line
-                        warning_lineonly("E145", "indentation found with preprocessor "
-                                         "(expected none or after '#')", i + 1)
+                        warning_lineonly(
+                            fn, "E145", "indentation found with preprocessor "
+                            "(expected none or after '#')", i + 1)
 
                 skip = True
             if ls[0:2] == "//":
@@ -1126,8 +1133,9 @@ def quick_check_indentation(lines):
             tabs = l[:len(l) - len(ls)]
             t = len(tabs)
             if (t > t_prev + 1) and (t_prev != -1):
-                warning_lineonly("E146", "indentation mismatch (indent of %d) '%s'" %
-                                 (t - t_prev, tabs), i + 1)
+                warning_lineonly(
+                    fn, "E146", "indentation mismatch (indent of %d) '%s'" %
+                    (t - t_prev, tabs), i + 1)
             t_prev = t
 
             # check for same indentation with different space/tab mix
@@ -1135,8 +1143,9 @@ def quick_check_indentation(lines):
             ws_expand = ws.expandtabs(4)
             if ws_expand == ws_prev_expand:
                 if ws != ws_prev:
-                    warning_lineonly("E152", "indentation tab/space mismatch",
-                                     i + 1)
+                    warning_lineonly(
+                        fn, "E152", "indentation tab/space mismatch",
+                        i + 1)
             ws_prev = ws
             ws_prev_expand = ws_expand
 
@@ -1146,7 +1155,7 @@ re_ifndef = re.compile(r"^\s*#\s*ifndef\s+([A-z0-9_]+).*$")
 re_define = re.compile(r"^\s*#\s*define\s+([A-z0-9_]+).*$")
 
 
-def quick_check_include_guard(lines):
+def quick_check_include_guard(fn, lines):
     # found = 0
     def_value = ""
     ok = False
@@ -1182,13 +1191,15 @@ def quick_check_include_guard(lines):
         # print("found:", def_value, "->", filepath)
         if def_value != guard:
             # print("%s: %s -> %s" % (filepath, def_value, guard))
-            warning_lineonly("E147", "non-conforming include guard (found %r, expected %r)" %
-                             (def_value, guard), i + 1)
+            warning_lineonly(
+                fn, "E147", "non-conforming include guard (found %r, expected %r)" %
+                (def_value, guard), i + 1)
     else:
-        warning_lineonly("E148", "missing include guard %r" % guard, 1)
+        warning_lineonly(
+            fn, "E148", "missing include guard %r" % guard, 1)
 
 
-def quick_check_source(fp, code, args):
+def quick_check_source(fp, code, args, fn):
 
     global filepath
 
@@ -1199,14 +1210,14 @@ def quick_check_source(fp, code, args):
     lines = code.split("\n")
 
     if is_header:
-        quick_check_include_guard(lines)
+        quick_check_include_guard(fn, lines)
 
-    quick_check_includes(lines)
+    quick_check_includes(fn, lines)
 
-    quick_check_indentation(lines)
+    quick_check_indentation(fn, lines)
 
 
-def scan_source(fp, code, args):
+def scan_source(fp, code, args, fn):
     # print("scanning: %r" % fp)
 
     global filepath
@@ -1238,20 +1249,20 @@ def scan_source(fp, code, args):
         # print(tok.type, tok.text)
         if tok.type == Token.Keyword:
             if tok.text in {"switch", "while", "if", "for"}:
-                item_range = extract_statement_if(i)
+                item_range = extract_statement_if(fn, i)
                 if item_range is not None:
-                    blender_check_kw_if(item_range[0], i, item_range[1])
+                    blender_check_kw_if(fn, item_range[0], i, item_range[1])
                 if tok.text == "switch":
-                    blender_check_kw_switch(item_range[0], i, item_range[1])
+                    blender_check_kw_switch(fn, item_range[0], i, item_range[1])
             elif tok.text == "else":
-                blender_check_kw_else(i)
+                blender_check_kw_else(fn, i)
             elif tok.text == "sizeof":
                 blender_check_kw_sizeof(i)
         elif tok.type == Token.Punctuation:
             if tok.text == ",":
-                blender_check_comma(i)
+                blender_check_comma(fn, i)
             elif tok.text == ".":
-                blender_check_period(i)
+                blender_check_period(fn, i)
             elif tok.text == "[":
                 # note, we're quite relaxed about this but
                 # disallow 'foo ['
@@ -1260,16 +1271,16 @@ def scan_source(fp, code, args):
                         # c++ can do delete []
                         pass
                     else:
-                        warning("E149", "space before '['", i, i)
+                        warning(fn, "E149", "space before '['", i, i)
             elif tok.text == "(":
                 # check if this is a cast, eg:
                 #  (char), (char **), (float (*)[3])
                 item_range = extract_cast(i)
                 if item_range is not None:
-                    blender_check_cast(item_range[0], item_range[1])
+                    blender_check_cast(fn, item_range[0], item_range[1])
             elif tok.text == "{":
                 # check matching brace is indented correctly (slow!)
-                blender_check_brace_indent(i)
+                blender_check_brace_indent(fn, i)
 
                 # check previous character is either a '{' or whitespace.
                 if ((tokens[i - 1].line == tok.line) and
@@ -1277,15 +1288,15 @@ def scan_source(fp, code, args):
                                  tokens[i - 1].text == "{" or
                                  tokens[i - 1].flag & IS_CAST)
                         ):
-                    warning("E150", "no space before '{'", i, i)
+                    warning(fn, "E150", "no space before '{'", i, i)
 
-                blender_check_function_definition(i)
+                blender_check_function_definition(fn, i)
 
         elif tok.type == Token.Operator:
             # we check these in pairs, only want first
             if tokens[i - 1].type != Token.Operator:
                 op, index_kw_end = extract_operator(i)
-                blender_check_operator(i, index_kw_end, op, is_cpp)
+                blender_check_operator(fn, i, index_kw_end, op, is_cpp)
         elif tok.type in Token.Comment:
             doxyfn = None
             if "\\file" in tok.text:
@@ -1296,12 +1307,12 @@ def scan_source(fp, code, args):
             if doxyfn is not None:
                 doxyfn_base = os.path.basename(doxyfn)
                 if doxyfn_base != filepath_base:
-                    warning("E151", "doxygen filename mismatch %s != %s" % (doxyfn_base, filepath_base), i, i)
+                    warning(fn, "E151", "doxygen filename mismatch %s != %s" % (doxyfn_base, filepath_base), i, i)
                 doxyfn_split = doxyfn.split("/")
                 if len(doxyfn_split) > 1:
                     fp_split = filepath_split[-len(doxyfn_split):]
                     if doxyfn_split != fp_split:
-                        warning("E151", "doxygen filepath mismatch %s != %s" % (doxyfn, "/".join(fp_split)), i, i)
+                        warning(fn, "E151", "doxygen filepath mismatch %s != %s" % (doxyfn, "/".join(fp_split)), i, i)
                     del fp_split
                 del doxyfn_base, doxyfn_split
             del doxyfn
@@ -1309,7 +1320,7 @@ def scan_source(fp, code, args):
         # ensure line length
         if (not args.no_length_check) and tok.type == Token.Text and tok.text == "\n":
             # check line len
-            blender_check_linelength(index_line_start, i - 1, col)
+            blender_check_linelength(fn, index_line_start, i - 1, col)
 
             col = 0
             index_line_start = i + 1
@@ -1326,21 +1337,51 @@ def scan_source(fp, code, args):
     #    #print(value, end="")
 
 
-def scan_source_filepath(filepath, args):
+def scan_source_filepath__cached(filepath, args, fn, cache):
+    cache_files_src, cache_files_dst = cache
+    h_code = hash_of_file(filepath)
+    info_src = cache_files_src.get(filepath)
+    if info_src is not None and info_src[0] == h_code:
+        for a in info_src[1]:
+            fn(*a)
+        cache_files_dst[filepath] = info_src
+        return None
+    else:
+        lines = []
+        info_dst = (h_code, lines)
+        def fn_wrap(*a):
+            fn_wrap.lines.append(a)
+            fn_wrap.fn(*a)
+        fn_wrap.fn = fn
+        fn_wrap.lines = lines
+        cache_files_dst[filepath] = info_dst
+        return cache_files_src, cache_files_dst, fn_wrap
+
+
+def scan_source_filepath(filepath, args, fn, cache=None):
     # for quick tests
     # ~ if not filepath.endswith("creator.c"):
     # ~     return
 
     code = open(filepath, 'r', encoding="utf-8").read()
 
+    if cache:
+        cache_result = scan_source_filepath__cached(filepath, args, fn, cache)
+        if cache_result is None:
+            # No need to ececute
+            return
+        else:
+            cache_files_src, cache_files_dst, fn = cache_result
+        del cache_result
+
     # fast checks which don't require full parsing
-    quick_check_source(filepath, code, args)
+    quick_check_source(filepath, code, args, fn)
 
     # use lexer
-    scan_source(filepath, code, args)
+    scan_source(filepath, code, args, fn)
 
 
-def scan_source_recursive(dirpath, args):
+def scan_source_recursive(dirpath, args, fn, cache=None):
     import os
     from os.path import join, splitext
 
@@ -1367,7 +1408,7 @@ def scan_source_recursive(dirpath, args):
         if is_ignore(filepath):
             continue
 
-        scan_source_filepath(filepath, args)
+        scan_source_filepath(filepath, args, fn, cache)
 
 
 def create_parser():
@@ -1394,6 +1435,9 @@ def main(argv=None):
     import sys
     import os
 
+    # For cache
+    import pickle
+
     if argv is None:
         argv = sys.argv[1:]
 
@@ -1403,6 +1447,7 @@ def main(argv=None):
 
     print("Scanning:", SOURCE_DIR)
 
+
     if 0:
         # SOURCE_DIR = os.path.normpath(
         #     os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))))
@@ -1410,13 +1455,35 @@ def main(argv=None):
         scan_source_recursive(os.path.join(SOURCE_DIR, "source/blender/makesrna/intern"), args)
         sys.exit(0)
 
+    cache_filename = os.environ.get("CHECK_STYLE_C_CACHE", "")
+    if cache_filename:
+        if os.path.exists(cache_filename):
+            with open(cache_filename, 'rb') as fh:
+                (hash_of_script_src, cache_files_src) = pickle.load(fh)
+        else:
+            hash_of_script_src = b''
+
+        cache_files_dst = {}
+        hash_of_script_dst = hash_of_file(__file__)
+        # If we change the Python code, ignore old cache.
+        if hash_of_script_src != hash_of_script_dst:
+            cache_files_src = {}
+        cache = (cache_files_src, cache_files_dst)
+    else:
+        cache = None
+
     for filepath in args.paths:
         if os.path.isdir(filepath):
             # recursive search
-            scan_source_recursive(filepath, args)
+            scan_source_recursive(filepath, args, print, cache)
         else:
             # single file
-            scan_source_filepath(filepath, args)
+            scan_source_filepath(filepath, args, print, cache)
+
+
+    if cache_filename:
+        with open(cache_filename, 'wb') as fh:
+            pickle.dump((hash_of_script_dst, cache_files_dst), fh)
 
 
 if __name__ == "__main__":
diff --git a/tests/check_source/check_style_c_test.py b/tests/check_source/check_style_c_test.py
index 631ad143f9847d825d444729a1f920f84c21d90f..a7573c6372bb59ebe37a83d9844bce5c02725db7 100755
--- a/tests/check_source/check_style_c_test.py
+++ b/tests/check_source/check_style_c_test.py
@@ -14,9 +14,7 @@ sys.path.append(
 
 import unittest
 
-warnings = []
 import check_style_c
-check_style_c.print = warnings.append
 
 # ----
 parser = check_style_c.create_parser()
@@ -36,8 +34,8 @@ FUNC_END = """
 
 
 def test_code(code):
-    warnings.clear()
-    check_style_c.scan_source("test.c", code, args)
+    warnings = []
+    check_style_c.scan_source("test.c", code, args, warnings.append)
     err_found = [w.split(":", 3)[2].strip() for w in warnings]
     # print(warnings)
     return err_found