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