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