Salome HOME
sat #18867 : pour les url des bases git : substitution des references par leur valeur...
[tools/sat.git] / commands / find_duplicates.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20
21 import src
22
23 # create a parser for command line options
24 parser = src.options.Options()
25 parser.add_option("s",
26                   "sources",
27                   "boolean",
28                   "sources",
29                   _("Search the duplicate files in the SOURCES directory."))
30 parser.add_option("p",
31                   "path",
32                   "list2",
33                   "path",
34                   _("Optional: Search the duplicate files in the given "
35                     "directory paths."))
36 parser.add_option("",
37                   "exclude-file",
38                   "list2",
39                   "exclude_file",
40                   _("Optional: Override the default list of filtered files."))
41 parser.add_option("",
42                   "exclude-extension",
43                   "list2",
44                   "exclude_extension",
45                   _("Optional: Override the default list of filtered "
46                     "extensions."))
47 parser.add_option("",
48                   "exclude-path",
49                   "list2",
50                   "exclude_path",
51                   _("Optional: Override the default list of filtered paths."))
52
53 default_extension_ignored = ['html', 'png', 'txt', 'js', 'xml', 'cmake', 'gif', 
54                      'm4', 'in', 'pyo', 'pyc', 'doctree', 'css']
55 default_files_ignored = ['__init__.py', 'Makefile.am', 'VERSION',
56                          'build_configure', 
57                          'README', 'AUTHORS', 'NEWS', 'COPYING', 'ChangeLog']
58 default_directories_ignored = []
59
60 def list_directory(lpath, extension_ignored, files_ignored, directories_ignored):
61     '''Make the list of all files and paths that are not filtered 
62     
63     :param lpath List: The list of path to of the directories where to 
64                        search for duplicates
65     :param extension_ignored List: The list of extensions to ignore
66     :param files_ignored List: The list of files to ignore
67     :param directories_ignored List: The list of directory paths to ignore
68     :return: files_arb_out is the list of [file, path] 
69              and files_out is is the list of files
70     :rtype: List, List
71     '''
72     files_out = []
73     files_arb_out=[]
74     for path in lpath:
75         for root, __, files in os.walk(path):  
76             for fic in files:
77                 extension = fic.split('.')[-1]   
78                 if (extension not in extension_ignored and 
79                                                       fic not in files_ignored):
80                     in_ignored_dir = False
81                     for rep in directories_ignored:
82                         if rep in root:
83                             in_ignored_dir = True                
84                     if not in_ignored_dir:
85                         files_out.append([fic])              
86                         files_arb_out.append([fic, root])
87     return files_arb_out, files_out
88
89 def format_list_of_str(l_str):
90     '''Make a list from a string
91     
92     :param l_str List or Str: The variable to format
93     :return: the formatted variable
94     :rtype: List
95     '''
96     if not isinstance(l_str, list):
97         return l_str
98     return ",".join(l_str)
99
100 def print_info(logger, info, level=2):
101     '''Format a display
102     
103     :param logger Logger: The logger instance
104     :param info List: the list of tuple to display
105     :param valMax float: the maximum value of the variable
106     :param level int: the verbose level that will be used
107     '''
108     smax = max(map(lambda l: len(l[0]), info))
109     for i in info:
110         sp = " " * (smax - len(i[0]))
111         src.printcolors.print_value(logger,
112                                     sp + i[0],
113                                     format_list_of_str(i[1]),
114                                     2)
115     logger.write("\n", level)
116
117 class Progress_bar:
118     "Create a progress bar in the terminal"
119     
120     def __init__(self, name, valMin, valMax, logger, length = 50):
121         '''Initialization of the progress bar.
122         
123         :param name str: The name of the progress bar
124         :param valMin float: the minimum value of the variable
125         :param valMax float: the maximum value of the variable
126         :param logger Logger: the logger instance
127         :param length int: the lenght of the progress bar
128         '''
129         self.name = name
130         self.valMin = valMin
131         self.valMax = valMax
132         self.length = length
133         self.logger = logger
134         if (self.valMax - self.valMin) <= 0 or length <= 0:
135             out_err = _('ERROR: Wrong init values for the progress bar\n')
136             raise src.SatException(out_err)
137         
138     def display_value_progression(self,val):
139         '''Display the progress bar.
140         
141         :param val float: val must be between valMin and valMax.
142         '''
143         if val < self.valMin or val > self.valMax:
144             self.logger.write(src.printcolors.printcWarning(_(
145                            'WARNING : wrong value for the progress bar.\n')), 3)
146         else:
147             perc = (float(val-self.valMin) / (self.valMax - self.valMin)) * 100.
148             nb_equals = int(perc * self.length / 100)
149             out = '\r %s : %3d %% [%s%s]' % (self.name, perc, nb_equals*'=',
150                                              (self.length - nb_equals)*' ' )
151             self.logger.write(out, 3)
152             self.logger.flush()
153
154 def description():
155     '''method that is called when salomeTools is called with --help option.
156     
157     :return: The text to display for the find_duplicates command description.
158     :rtype: str
159     '''
160     return _("The find_duplicates command search recursively for all duplicates"
161              " files in a the INSTALL directory (or the optionally given "
162              "directory) and prints the found files to the terminal.\n\n"
163              "example:\nsat find_duplicates --path /tmp")
164
165 def run(args, runner, logger):
166     '''method that is called when salomeTools is called with find_duplicates 
167        parameter.
168     '''
169     # parse the arguments
170     (options, args) = parser.parse_args(args)
171     
172     # Determine the directory path where to search 
173     # for duplicates files regarding the options
174     if options.path:
175         l_dir_path = options.path
176     else:
177         src.check_config_has_application(runner.cfg)
178         if options.sources:
179             l_dir_path = [os.path.join(runner.cfg.APPLICATION.workdir,
180                                        "SOURCES")]
181         else:
182             # find all installation paths
183             all_products = runner.cfg.APPLICATION.products.keys()
184             l_product_cfg = src.product.get_products_infos(all_products,
185                                                            runner.cfg)
186             l_dir_path = [pi.install_dir for __, pi in l_product_cfg]
187     
188     # Get the files to ignore during the searching
189     files_ignored = default_files_ignored
190     if options.exclude_file:
191         files_ignored = options.exclude_file
192
193     # Get the extension to ignore during the searching
194     extension_ignored = default_extension_ignored
195     if options.exclude_extension:
196         extension_ignored = options.exclude_extension
197
198     # Get the directory paths to ignore during the searching
199     directories_ignored = default_directories_ignored
200     if options.exclude_path:
201         directories_ignored = options.exclude_path
202     
203     # Check the directories
204     l_path = src.deepcopy_list(l_dir_path)
205     l_dir_path = []
206     for dir_path in l_path:
207         if not(os.path.isdir(dir_path)):
208             msg = _("%s does not exists or is not a directory path: "
209                     "it will be ignored" % dir_path)
210             logger.write("%s\n" % src.printcolors.printcWarning(msg), 3)
211             continue
212         l_dir_path.append(dir_path)
213             
214     
215     # Display some information
216     info = [(_("Directories"), "\n".join(l_dir_path)),
217             (_("Ignored files"), files_ignored),
218             (_("Ignored extensions"), extension_ignored),
219             (_("Ignored directories"), directories_ignored)
220            ]
221     print_info(logger, info)
222     
223     # Get all the files and paths
224     logger.write(_("Store all file paths ... "), 3)
225     logger.flush()
226     dic, fic = list_directory(l_dir_path,
227                               extension_ignored,
228                               files_ignored,
229                               directories_ignored)  
230     logger.write(src.printcolors.printcSuccess('OK\n'), 3)
231     
232     # Eliminate all the singletons
233     len_fic = len(fic)
234     range_fic = range(0,len_fic)
235     range_fic.reverse()
236     my_bar = Progress_bar(_('Eliminate the files that are not duplicated'),
237                           0,
238                           len_fic,
239                           logger,
240                           length = 50)
241     for i in range_fic:
242         my_bar.display_value_progression(len_fic - i)
243         if fic.count(fic[i])==1:
244             fic.remove(fic[i])
245             dic.remove(dic[i])
246
247     # Format the resulting variable to get a dictionary
248     logger.write(_("\n\nCompute the dict {files : [list of pathes]} ... "), 3)
249     fic.sort()
250     len_fic = len(fic)
251     rg_fic = range(0,len_fic)
252     rg_fic.reverse()
253     for i in rg_fic:
254         if fic[i-1] != fic[i]:
255             fic.remove(fic[i])
256
257     dic_fic_paths = {}
258     for fichier in fic:
259         the_file = fichier[0]
260         l_path = []
261         for fic_path in dic:
262             if fic_path[0] == the_file:
263                 l_path.append(fic_path[1])
264         dic_fic_paths[the_file] = l_path
265     
266     logger.write(src.printcolors.printcSuccess('OK\n'), 3)
267
268     # End the execution if no duplicates were found
269     if len(dic_fic_paths) == 0:
270         logger.write(_("No duplicate files found.\n"), 3)
271         return 0
272
273     # Check that there are no singletons in the result (it would be a bug)
274     for elem in dic_fic_paths:
275         if len(dic_fic_paths[elem])<2:
276             logger.write(_("Warning : element %s has not more than"
277                          " two paths.\n") % elem, 3)
278
279
280     # Display the results
281     logger.write(src.printcolors.printcInfo(_('\nResults:\n\n')), 3)
282     max_file_name_lenght = max(map(lambda l: len(l), dic_fic_paths.keys()))
283     for fich in dic_fic_paths:
284         logger.write(src.printcolors.printcLabel(fich), 1)
285         sp = " " * (max_file_name_lenght - len(fich))
286         logger.write(sp, 1)
287         for rep in dic_fic_paths[fich]:
288             logger.write(rep, 1)
289             logger.write(" ", 1)
290         logger.write("\n", 1)
291
292     return 0