Salome HOME
'sat source' : add the testing of files after getting the sources
[tools/sat.git] / commands / source.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  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 import shutil
21
22 import src
23 import prepare
24
25 # Define all possible option for patch command :  sat patch <options>
26 parser = src.options.Options()
27 parser.add_option('p', 'product', 'list2', 'products',
28     _('products from which to get the sources. This option can be'
29     ' passed several time to get the sources of several products.'))
30
31 def get_source_for_dev(config, product_info, source_dir, logger, pad):
32     '''The method called if the product is in development mode
33     
34     :param config Config: The global configuration
35     :param product_info Config: The configuration specific to 
36                                the product to be prepared
37     :param source_dir Path: The Path instance corresponding to the 
38                             directory where to put the sources
39     :param logger Logger: The logger instance to use for the display and logging
40     :param pad int: The gap to apply for the terminal display
41     :return: True if it succeed, else False
42     :rtype: boolean
43     '''
44        
45     # Call the function corresponding to get the sources with True checkout
46     retcode = get_product_sources(config, 
47                                  product_info, 
48                                  True, 
49                                  source_dir,
50                                  logger, 
51                                  pad, 
52                                  checkout=True)
53     logger.write("\n", 3, False)
54     # +2 because product name is followed by ': '
55     logger.write(" " * (pad+2), 3, False) 
56     
57     logger.write('dev: %s ... ' % 
58                  src.printcolors.printcInfo(product_info.source_dir), 3, False)
59     logger.flush()
60     
61     return retcode
62
63 def get_source_from_git(product_info, source_dir, logger, pad, is_dev=False):
64     '''The method called if the product is to be get in git mode
65     
66     :param product_info Config: The configuration specific to 
67                                the product to be prepared
68     :param source_dir Path: The Path instance corresponding to the 
69                             directory where to put the sources
70     :param logger Logger: The logger instance to use for the display and logging
71     :param pad int: The gap to apply for the terminal display
72     :param is_dev boolean: True if the product is in development mode
73     :return: True if it succeed, else False
74     :rtype: boolean
75     '''
76     # The str to display
77     coflag = 'git'
78
79     # Get the repository address. (from repo_dev key if the product is 
80     # in dev mode.
81     if is_dev and 'repo_dev' in product_info.git_info:
82         coflag = src.printcolors.printcHighlight(coflag.upper())
83         repo_git = product_info.git_info.repo_dev    
84     else:
85         repo_git = product_info.git_info.repo    
86         
87     # Display informations
88     logger.write('%s:%s' % (coflag, src.printcolors.printcInfo(repo_git)), 3, 
89                  False)
90     logger.write(' ' * (pad + 50 - len(repo_git)), 3, False)
91     logger.write(' tag:%s' % src.printcolors.printcInfo(
92                                                     product_info.git_info.tag), 
93                  3,
94                  False)
95     logger.write(' %s. ' % ('.' * (10 - len(product_info.git_info.tag))), 3, 
96                  False)
97     logger.flush()
98     logger.write('\n', 5, False)
99     # Call the system function that do the extraction in git mode
100     retcode = src.system.git_extract(repo_git,
101                                  product_info.git_info.tag,
102                                  source_dir, logger)
103     return retcode
104
105 def get_source_from_archive(product_info, source_dir, logger):
106     '''The method called if the product is to be get in archive mode
107     
108     :param product_info Config: The configuration specific to 
109                                the product to be prepared
110     :param source_dir Path: The Path instance corresponding to the 
111                             directory where to put the sources
112     :param logger Logger: The logger instance to use for the display and logging
113     :return: True if it succeed, else False
114     :rtype: boolean
115     '''
116     # check archive exists
117     if not os.path.exists(product_info.archive_info.archive_name):
118         raise src.SatException(_("Archive not found: '%s'") % 
119                                product_info.archive_info.archive_name)
120
121     logger.write('arc:%s ... ' % 
122                  src.printcolors.printcInfo(product_info.archive_info.archive_name),
123                  3, 
124                  False)
125     logger.flush()
126     # Call the system function that do the extraction in archive mode
127     retcode, NameExtractedDirectory = src.system.archive_extract(
128                                     product_info.archive_info.archive_name,
129                                     source_dir.dir(), logger)
130     
131     # Rename the source directory if 
132     # it does not match with product_info.source_dir
133     if (NameExtractedDirectory.replace('/', '') != 
134             os.path.basename(product_info.source_dir)):
135         shutil.move(os.path.join(os.path.dirname(product_info.source_dir), 
136                                  NameExtractedDirectory), 
137                     product_info.source_dir)
138     
139     return retcode
140
141 def get_source_from_cvs(user, product_info, source_dir, checkout, logger, pad):
142     '''The method called if the product is to be get in cvs mode
143     
144     :param user str: The user to use in for the cvs command
145     :param product_info Config: The configuration specific to 
146                                the product to be prepared
147     :param source_dir Path: The Path instance corresponding to the 
148                             directory where to put the sources
149     :param checkout boolean: If True, get the source in checkout mode
150     :param logger Logger: The logger instance to use for the display and logging
151     :param pad int: The gap to apply for the terminal display
152     :return: True if it succeed, else False
153     :rtype: boolean
154     '''
155     # Get the protocol to use in the command
156     if "protocol" in product_info.cvs_info:
157         protocol = product_info.cvs_info.protocol
158     else:
159         protocol = "pserver"
160     
161     # Construct the line to display
162     if "protocol" in product_info.cvs_info:
163         cvs_line = "%s:%s@%s:%s" % \
164             (protocol, user, product_info.cvs_info.server, 
165              product_info.cvs_info.product_base)
166     else:
167         cvs_line = "%s / %s" % (product_info.cvs_info.server, 
168                                 product_info.cvs_info.product_base)
169
170     coflag = 'cvs'
171     if checkout: coflag = src.printcolors.printcHighlight(coflag.upper())
172
173     logger.write('%s:%s' % (coflag, src.printcolors.printcInfo(cvs_line)), 
174                  3, 
175                  False)
176     logger.write(' ' * (pad + 50 - len(cvs_line)), 3, False)
177     logger.write(' src:%s' % 
178                  src.printcolors.printcInfo(product_info.cvs_info.source), 
179                  3, 
180                  False)
181     logger.write(' ' * (pad + 1 - len(product_info.cvs_info.source)), 3, False)
182     logger.write(' tag:%s' % 
183                     src.printcolors.printcInfo(product_info.cvs_info.tag), 
184                  3, 
185                  False)
186     # at least one '.' is visible
187     logger.write(' %s. ' % ('.' * (10 - len(product_info.cvs_info.tag))), 
188                  3, 
189                  False) 
190     logger.flush()
191     logger.write('\n', 5, False)
192
193     # Call the system function that do the extraction in cvs mode
194     retcode = src.system.cvs_extract(protocol, user,
195                                  product_info.cvs_info.server,
196                                  product_info.cvs_info.product_base,
197                                  product_info.cvs_info.tag,
198                                  product_info.cvs_info.source,
199                                  source_dir, logger, checkout)
200     return retcode
201
202 def get_source_from_svn(user, product_info, source_dir, checkout, logger):
203     '''The method called if the product is to be get in svn mode
204     
205     :param user str: The user to use in for the svn command
206     :param product_info Config: The configuration specific to 
207                                the product to be prepared
208     :param source_dir Path: The Path instance corresponding to the 
209                             directory where to put the sources
210     :param checkout boolean: If True, get the source in checkout mode
211     :param logger Logger: The logger instance to use for the display and logging
212     :return: True if it succeed, else False
213     :rtype: boolean
214     '''
215     coflag = 'svn'
216     if checkout: coflag = src.printcolors.printcHighlight(coflag.upper())
217
218     logger.write('%s:%s ... ' % (coflag, 
219                                  src.printcolors.printcInfo(
220                                             product_info.svn_info.repo)), 
221                  3, 
222                  False)
223     logger.flush()
224     logger.write('\n', 5, False)
225     # Call the system function that do the extraction in svn mode
226     retcode = src.system.svn_extract(user, 
227                                      product_info.svn_info.repo, 
228                                      product_info.svn_info.tag,
229                                      source_dir, 
230                                      logger, 
231                                      checkout)
232     return retcode
233
234 def get_product_sources(config, 
235                        product_info, 
236                        is_dev, 
237                        source_dir,
238                        logger, 
239                        pad, 
240                        checkout=False):
241     '''Get the product sources.
242     
243     :param config Config: The global configuration
244     :param product_info Config: The configuration specific to 
245                                the product to be prepared
246     :param is_dev boolean: True if the product is in development mode
247     :param source_dir Path: The Path instance corresponding to the 
248                             directory where to put the sources
249     :param logger Logger: The logger instance to use for the display and logging
250     :param pad int: The gap to apply for the terminal display
251     :param checkout boolean: If True, get the source in checkout mode
252     :return: True if it succeed, else False
253     :rtype: boolean
254     '''
255     if not checkout and is_dev:
256         return get_source_for_dev(config, 
257                                    product_info, 
258                                    source_dir, 
259                                    logger, 
260                                    pad)
261
262     if product_info.get_source == "git":
263         return get_source_from_git(product_info, source_dir, logger, pad, 
264                                     is_dev)
265
266     if product_info.get_source == "archive":
267         return get_source_from_archive(product_info, source_dir, logger)
268     
269     if product_info.get_source == "cvs":
270         cvs_user = config.USER.cvs_user
271         return get_source_from_cvs(cvs_user, 
272                                     product_info, 
273                                     source_dir, 
274                                     checkout, 
275                                     logger,
276                                     pad)
277
278     if product_info.get_source == "svn":
279         svn_user = config.USER.svn_user
280         return get_source_from_svn(svn_user, product_info, source_dir, 
281                                     checkout,
282                                     logger)
283
284     if product_info.get_source == "native":
285         # skip
286         logger.write('%s  ' % src.printcolors.printc(src.OK_STATUS),
287                      3,
288                      False)
289         msg = _('INFORMATION : Not doing anything because the product'
290                 ' is of type "native".\n')
291         logger.write(msg, 3)
292         return True        
293
294     if product_info.get_source == "fixed":
295         # skip
296         logger.write('%s  ' % src.printcolors.printc(src.OK_STATUS),
297                      3,
298                      False)
299         msg = _('INFORMATION : Not doing anything because the product'
300                 ' is of type "fixed".\n')
301         logger.write(msg, 3)
302         return True  
303
304     # if the get_source is not in [git, archive, cvs, svn, fixed, native]
305     logger.write(_("Unknown get source method \"%(get)s\" for product %(product)s") % \
306         { 'get': product_info.get_source, 'product': product_info.name }, 3, False)
307     logger.write(" ... ", 3, False)
308     logger.flush()
309     return False
310
311 def get_all_product_sources(config, products, logger):
312     '''Get all the product sources.
313     
314     :param config Config: The global configuration
315     :param products List: The list of tuples (product name, product informations)
316     :param logger Logger: The logger instance to be used for the logging
317     :return: the tuple (number of success, dictionary product_name/success_fail)
318     :rtype: (int,dict)
319     '''
320
321     # Initialize the variables that will count the fails and success
322     results = dict()
323     good_result = 0
324
325     # Get the maximum name length in order to format the terminal display
326     max_product_name_len = 1
327     if len(products) > 0:
328         max_product_name_len = max(map(lambda l: len(l), products[0])) + 4
329     
330     # The loop on all the products from which to get the sources
331     for product_name, product_info in products:
332         # get product name, product informations and the directory where to put
333         # the sources
334         if (not (src.product.product_is_fixed(product_info) or 
335                  src.product.product_is_native(product_info))):
336             source_dir = src.Path(product_info.source_dir)
337         else:
338             source_dir = src.Path('')
339
340         # display and log
341         logger.write('%s: ' % src.printcolors.printcLabel(product_name), 3)
342         logger.write(' ' * (max_product_name_len - len(product_name)), 3, False)
343         logger.write("\n", 4, False)
344         
345         # Remove the existing source directory if 
346         # the product is not in development mode
347         is_dev = src.product.product_is_dev(product_info)
348         if source_dir.exists():
349             logger.write('%s  ' % src.printcolors.printc(src.OK_STATUS),
350                          3,
351                          False)
352             msg = _("INFORMATION : Not doing anything because the source"
353                     " directory already exists.\n")
354             logger.write(msg, 3)
355             good_result = good_result + 1
356             # Do not get the sources and go to next product
357             continue
358
359         # Call to the function that get the sources for one product
360         retcode = get_product_sources(config, 
361                                      product_info, 
362                                      is_dev, 
363                                      source_dir,
364                                      logger, 
365                                      max_product_name_len, 
366                                      checkout=False)
367         
368         '''
369         if 'no_rpath' in product_info.keys():
370             if product_info.no_rpath:
371                 hack_no_rpath(config, product_info, logger)
372         '''
373         
374         # Check that the sources are correctly get using the files to be tested
375         # in product information
376         if retcode:
377             check_OK, wrong_path = check_sources(product_info, logger)
378             if not check_OK:
379                 # Print the missing file path
380                 msg = _("The required file %s does not exists. " % wrong_path)
381                 logger.write(src.printcolors.printcError("\nERROR: ") + msg, 3)
382                 retcode = False
383
384         # show results
385         results[product_name] = retcode
386         if retcode:
387             # The case where it succeed
388             
389             
390             res = src.OK_STATUS
391             good_result = good_result + 1
392         else:
393             # The case where it failed
394             res = src.KO_STATUS
395         
396         # print the result
397         if not(src.product.product_is_fixed(product_info) or 
398                src.product.product_is_native(product_info)):
399             logger.write('%s\n' % src.printcolors.printc(res), 3, False)
400
401     return good_result, results
402
403 def check_sources(product_info, logger):
404     '''Check that the sources are correctly get, using the files to be tested
405        in product information
406     
407     :param product_info Config: The configuration specific to 
408                                 the product to be prepared
409     :return: True if the files exists (or no files to test is provided).
410     :rtype: boolean
411     '''
412     # Get the files to test if there is any
413     if ("present_files" in product_info and 
414         "source" in product_info.present_files):
415         l_files_to_be_tested = product_info.present_files.source
416         for file_path in l_files_to_be_tested:
417             # The path to test is the source directory 
418             # of the product joined the file path provided
419             path_to_test = os.path.join(product_info.source_dir, file_path)
420             logger.write(_("\nTesting existence of file: \n"), 5)
421             logger.write(path_to_test, 5)
422             if not os.path.exists(path_to_test):
423                 return False, path_to_test
424             logger.write(src.printcolors.printcSuccess(" OK\n"), 5)
425     return True, ""
426
427 def description():
428     '''method that is called when salomeTools is called with --help option.
429     
430     :return: The text to display for the source command description.
431     :rtype: str
432     '''
433     return _("The source command gets the sources of the application products "
434              "from cvs, git, an archive or a directory..")
435   
436 def run(args, runner, logger):
437     '''method that is called when salomeTools is called with source parameter.
438     '''
439     # Parse the options
440     (options, args) = parser.parse_args(args)
441     
442     # check that the command has been called with an application
443     src.check_config_has_application( runner.cfg )
444
445     # Print some informations
446     logger.write(_('Getting sources of the application %s\n') % 
447                 src.printcolors.printcLabel(runner.cfg.VARS.application), 1)
448     src.printcolors.print_value(logger, 'workdir', 
449                                 runner.cfg.APPLICATION.workdir, 2)
450     logger.write("\n", 2, False)
451        
452     # Get the products list with products informations regarding the options
453     products_infos = prepare.get_products_list(options, runner.cfg, logger)
454     
455     # Call to the function that gets all the sources
456     good_result, results = get_all_product_sources(runner.cfg, 
457                                                   products_infos,
458                                                   logger)
459
460     # Display the results (how much passed, how much failed, etc...)
461     status = src.OK_STATUS
462     details = []
463
464     logger.write("\n", 2, False)
465     if good_result == len(products_infos):
466         res_count = "%d / %d" % (good_result, good_result)
467     else:
468         status = src.KO_STATUS
469         res_count = "%d / %d" % (good_result, len(products_infos))
470
471         for product in results:
472             if results[product] == 0 or results[product] is None:
473                 details.append(product)
474
475     result = len(products_infos) - good_result
476
477     # write results
478     logger.write(_("Getting sources of the application:"), 1)
479     logger.write(" " + src.printcolors.printc(status), 1, False)
480     logger.write(" (%s)\n" % res_count, 1, False)
481
482     if len(details) > 0:
483         logger.write(_("Following sources haven't been get:\n"), 2)
484         logger.write(" ".join(details), 2)
485         logger.write("\n", 2, False)
486
487     return result