From 08c90f11b20dc8413a3fb27a9334ff3f0466b116 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Mon, 17 Nov 2014 13:55:58 +0100
Subject: Add FixIncludes.py utility.

This tool reads in implementation or header files and groups and sorts include
statements at the top according to our guidelines.
It does not support files using #if statements in head of the file.

Change-Id: I34b02546ecaf1653372f6edd319126b2ebb22ab5

diff --git a/BuildTools/FixIncludes.py b/BuildTools/FixIncludes.py
new file mode 100644
index 0000000..def2dd5
--- /dev/null
+++ b/BuildTools/FixIncludes.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+
+import sys;
+import os;
+import re;
+from sets import Set
+
+filename = sys.argv[1]
+
+filename_base = os.path.basename(filename)
+(filename_name, filename_ext) = os.path.splitext(filename_base)
+
+c_stdlib_headers = Set(["assert.h",  "limits.h",  "signal.h",  "stdlib.h", "ctype.h", "locale.h",  "stdarg.h", "string.h", "errno.h", "math.h", "stddef.h", "time.h", "float.h",  "setjmp.h", "stdio.h", "iso646.h", "wchar.h", "wctype.h", "complex.h", "inttypes.h", "stdint.h", "tgmath.h", "fenv.h", "stdbool.h"])
+
+cpp_stdlib_headers = Set(["algorithm", "fstream", "list", "regex", "typeindex", "array", "functional", "locale", "set", "typeinfo", "atomic", "future", "map", "sstream", "type_traits", "bitset", "initializer_list", "memory", "stack", "unordered_map", "chrono", "iomanip", "mutex", "stdexcept", "unordered_set", "codecvt", "ios", "new", "streambuf", "utility", "complex", "iosfwd", "numeric", "string", "valarray", "condition_variable", "iostream", "ostream", "strstream", "vector", "deque", "istream", "queue", "system_error", "exception", "iterator", "random", "thread", "forward_list", "limits", "ratio", "tuple", "cassert", "ciso646", "csetjmp", "cstdio", "ctime", "cctype", "climits", "csignal", "cstdlib", "cwchar", "cerrno", "clocale", "cstdarg", "cstring", "cwctype", "cfloat", "cmath", "cstddef"])
+
+class HeaderType:
+    PRAGMA_ONCE, CORRESPONDING_HEADER, C_STDLIB, CPP_STDLIB, BOOST, QT, OTHER, SWIFTEN, SWIFT_CONTROLLERS, SWIFTOOLS, SWIFT = range(11)
+
+def findHeaderBlock(lines):
+  start = False
+  end = False
+
+  for idx, line in enumerate(lines):
+    if not start and line.startswith("#"):
+      start = idx
+    elif start and (not end) and (not line.startswith("#")) and line.strip():
+      end = idx-1
+      break
+  return (start, end)
+
+def lineToFileName(line):
+  match = re.match( r'#include "(.*)"', line)
+  if match:
+    return match.group(1)
+  match = re.match( r'#include <(.*)>', line)
+  if match:
+    return match.group(1)
+  return False
+
+def fileNameToHeaderType(name):
+  if name.endswith(filename_name + ".h"):
+    return HeaderType.CORRESPONDING_HEADER
+
+  if name in c_stdlib_headers:
+    return HeaderType.C_STDLIB
+  
+  if name in cpp_stdlib_headers:
+    return HeaderType.CPP_STDLIB
+
+  if name.startswith("boost"):
+    return HeaderType.BOOST
+
+  if name.startswith("Q"):
+    return HeaderType.QT
+
+  if name.startswith("Swiften"):
+    return HeaderType.SWIFTEN
+
+  if name.startswith("Swift/Controllers"):
+    return HeaderType.SWIFT_CONTROLLERS
+
+  if name.startswith("SwifTools"):
+    return HeaderType.SWIFTOOLS
+
+  if name.startswith("Swift"):
+    return HeaderType.SWIFT
+
+  return HeaderType.OTHER
+
+def serializeHeaderGroups(groups):
+  headerList = []
+  for group in range(0, HeaderType.SWIFT + 1):
+    if group in groups:
+      headers = sorted(groups[group])
+      headerList.extend(headers)
+      headerList.extend(["\n"])
+  headerList.pop()
+  return headerList
+
+def cleanHeaderFile(content, headerStart, headerEnd, headerGroups):
+  del content[headerStart:headerEnd]
+  newHeaders = serializeHeaderGroups(headerGroups)
+  content[headerStart:1] = newHeaders
+
+  for line in content:
+    print line,
+
+def cleanImplementationFile(content, headerStart, headerEnd, headerGroups):
+  del content[headerStart:headerEnd]
+  newHeaders = serializeHeaderGroups(headerGroups)
+  content[headerStart:1] = newHeaders
+
+  for line in content:
+    print line,
+
+
+containsIf = False
+
+with open(filename) as f:
+  content = f.readlines()
+
+(headerStart, headerEnd) = findHeaderBlock(content)
+
+headerGroups = {}
+
+for line in content[headerStart:headerEnd]:
+  if line.strip():
+    if line.strip().startswith("#if "):
+      containsIf = True
+      break
+
+    if line.strip().startswith("#pragma once"):
+      headerType = HeaderType.PRAGMA_ONCE
+    else:
+      headerType = fileNameToHeaderType(lineToFileName(line))
+    
+    filename = lineToFileName(line)
+    if headerType in headerGroups:
+      headerGroups[headerType].append(line)
+    else:
+      headerGroups[headerType] = [line]
+
+if containsIf:
+  print "Cannot format headers containing preprocessor if statements"
+  exit(1)
+
+if filename_base.endswith(".h"):
+  if not HeaderType.PRAGMA_ONCE in headerGroups:
+    print "Missing #pragma once!"
+    exit(2)
+  cleanHeaderFile(content, headerStart, headerEnd, headerGroups)
+elif filename_base.endswith(".cpp"):
+  cleanImplementationFile(content, headerStart, headerEnd, headerGroups)
-- 
cgit v0.10.2-6-g49f6