fw4spl
sortincludes.py
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3 
4 import os
5 import re
6 import string
7 
8 import common
9 from common import FormatReturn
10 
11 g_libs = []
12 g_bundles = []
13 
14 
15 def find_current_library(path):
16  # Just assert first for 'src' or 'include'
17  match = re.search('(?:(?:include\/)|(?:src\/)).*', path)
18  if match is None:
19  raise Exception('lib path not found')
20 
21  # Here we split so we have for instance
22  # /home/fbridault/dev/f4s-sandbox/src/f4s/SrcLib/core/fwDataCamp/include/fwMedDataCamp/DicomSeries.hpp
23  # split into
24  # ['/home/fbridault/dev/f4s-sandbox/src/f4s/SrcLib/core/fwDataCamp/', 'include/fwMedDataCamp/DicomSeries.hpp']
25  split_by_inc = string.split(path, 'include/')
26 
27  # If something has been split, that means we have an include
28  if len(split_by_inc) > 1:
29  lib_path = split_by_inc
30  else:
31  lib_path = string.split(path, 'src/')
32 
33  # Now we take the second last element (we start from the end because 'include' or 'src' may appear multiple times)
34  lib = lib_path[-2]
35 
36  lib_name = string.split(lib, '/')[-2]
37  if lib_name == "tu":
38  # If we have a unit test, for instance '/home/fbridault/dev/f4s-sandbox/src/f4s/SrcLib/io/fwCsvIO/test/tu/CsvReaderTest.hpp'
39  # We will get 'tu' instead of the library name and thus we have to skip '/test/tu'
40  lib_name = string.split(lib, '/')[-4]
41 
42  # We have kept '/home/fbridault/dev/f4s-sandbox/src/f4s/SrcLib/core/fwDataCamp/'
43  # We take the second last element split by '/', so in this case 'fwDataCamp'
44  return lib_name
45 
46 
47 def find_libraries_and_bundles(fw4spl_projects):
48  global g_libs
49  global g_bundles
50 
51  g_libs = []
52  g_bundles = []
53 
54  for project_dir in fw4spl_projects:
55  if not os.path.isdir(project_dir):
56  common.warn("%s isn't a valid directory." % project_dir)
57  continue
58  for root, dirs, files in os.walk(project_dir):
59  rootdir = os.path.split(root)[1]
60  # Do not inspect hidden folders
61  if not rootdir.startswith("."):
62  for file in files:
63  if file == "CMakeLists.txt":
64  if re.match('.*Bundles', root):
65  g_bundles += [rootdir]
66  elif re.match('.*SrcLib', root):
67  g_libs += [rootdir]
68 
69  g_libs.sort()
70  g_bundles.sort()
71 
72 
73 
74 
75 def clean_list(includes):
76  new_include_list = []
77 
78  if len(includes) == 0:
79  return new_include_list
80 
81  includes.sort(key=lambda s: s[1].lower())
82 
83  prev_module = includes[0][0]
84  for module, include in includes:
85  if prev_module != module:
86  new_include_list += ['\n']
87  new_include_list += [include]
88  prev_module = module
89 
90  new_include_list += ['\n']
91  return new_include_list
92 
93 
94 def sort_includes(path, enable_reformat):
95  try:
96  cur_lib = find_current_library(path)
97  except:
98  common.warn('Failed to find current library for file ' + path + ', includes order might be wrong.\n')
99  cur_lib = '!!NOTFOUND!!'
100 
101  pathname = os.path.dirname(__file__) + "/"
102 
103  file = open(pathname + "std_headers.txt", 'r')
104 
105  lib_std = file.read()
106  file.close()
107 
108  file = open(path, 'rb')
109  content = file.readlines()
110  file.close()
111 
112  includes = set()
113  first_line = -1
114  last_line = -1
115 
116  out_of_include = False
117 
118  for i, line in enumerate(content):
119  if re.match("#include", line):
120  if out_of_include:
121  common.warn(
122  'Failed to parse includes in file ' + path + ', includes sort is skipped. Maybe there is a #ifdef ? This may be handled in a future version.\n')
123  return
124 
125  if first_line == -1:
126  first_line = i
127  last_line = i
128 
129  includes.add(line)
130  elif first_line > -1 and line != "\n":
131  out_of_include = True
132 
133  if first_line == -1 and last_line == -1:
134  # No include, skip
135  return
136 
137  include_modules = []
138 
139  # Create associated list of modules
140  for include in includes:
141  include_path_match = re.match('.*<(.*/.*)>', include)
142  module = ""
143  if include_path_match:
144  module = include_path_match.group(1).split('/')[0]
145  else:
146  include_path_match = re.match('.*"(.*/.*)"', include)
147  if include_path_match:
148  module = include_path_match.group(1).split('/')[0]
149  else:
150  module = ""
151 
152  include_modules += [module]
153 
154  own_header_include = []
155  current_module_includes = []
156  lib_includes = []
157  bundles_includes = []
158  other_includes = []
159  std_includes = []
160 
161  orig_path = re.sub(".hg-new", "", path)
162  extension = os.path.splitext(orig_path)[1]
163 
164  cpp = False
165  if extension == ".cpp":
166  filename = os.path.basename(orig_path)
167  matched_header = re.sub(extension, ".hpp", filename)
168  cpp = True
169 
170  for include, module in zip(includes, include_modules):
171 
172  if cpp and re.search('".*' + matched_header + '.*"', include):
173  own_header_include += [(module, include)]
174  elif module == cur_lib or re.search('".*"', include):
175  current_module_includes += [(module, include)]
176  elif module in g_libs:
177  lib_includes += [(module, include)]
178  elif module in g_bundles:
179  bundles_includes += [(module, include)]
180  else:
181  include_path_match = re.match('.*<(.*)>', include)
182  if include_path_match and include_path_match.group(1) in lib_std:
183  std_includes += [(module, include)]
184  else:
185  other_includes += [(module, include)]
186 
187  new_includes = clean_list(own_header_include) + clean_list(current_module_includes) + clean_list(
188  lib_includes) + clean_list(bundles_includes) + clean_list(other_includes) + clean_list(std_includes)
189 
190  new_content = []
191  for i, line in enumerate(content):
192  if i == first_line:
193  new_content += new_includes[:-1]
194  elif i < first_line or i > last_line:
195  new_content += [line]
196 
197  if content != new_content:
198  if enable_reformat:
199  open(path, 'wb').writelines(new_content)
200  return FormatReturn.Modified
201  else:
202  common.error('Include headers are not correctly sorted in file : ' + path + '.')
203  return FormatReturn.Error
204 
205  return FormatReturn.NotModified
def error(msg)
Definition: common.py:52
def warn(msg)
Definition: common.py:56