Salome HOME
changing some keys name, factorizing dome code
[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('m', 'module', 'list2', 'modules',
28     _('modules from which to get the sources. This option can be'
29     ' passed several time to get the sources of several modules.'))
30 parser.add_option('', 'no_sample', 'boolean', 'no_sample', 
31     _("do not get sources from sample modules."))
32 parser.add_option('f', 'force', 'boolean', 'force', 
33     _("force to get the sources of the modules in development mode."))
34
35 def get_sources_for_dev(config, module_info, source_dir, force, logger, pad):
36     '''The method called if the module is in development mode
37     
38     :param config Config: The global configuration
39     :param module_info Config: The configuration specific to 
40                                the module 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 module 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(module_info.source_dir) or force:
54         # Call the function corresponding to get the sources with True checkout
55         retcode = get_module_sources(config, 
56                                      module_info, 
57                                      True, 
58                                      source_dir,
59                                      force, 
60                                      logger, 
61                                      pad, 
62                                      checkout=True)
63         logger.write("\n", 3, False)
64         # +2 because module name is followed by ': '
65         logger.write(" " * (pad+2), 3, False) 
66     
67     logger.write('dev: %s ... ' % 
68                  src.printcolors.printcInfo(module_info.source_dir), 3, False)
69     logger.flush()
70     
71     return retcode
72
73 def get_sources_from_git(module_info, source_dir, logger, pad, is_dev=False):
74     '''The method called if the module is to be get in git mode
75     
76     :param module_info Config: The configuration specific to 
77                                the module to be prepared
78     :param source_dir Path: The Path instance corresponding to the 
79                             directory where to put the sources
80     :param logger Logger: The logger instance to use for the display and logging
81     :param pad int: The gap to apply for the terminal display
82     :param is_dev boolean: True if the module is in development mode
83     :return: True if it succeed, else False
84     :rtype: boolean
85     '''
86     # The str to display
87     coflag = 'git'
88
89     # Get the repository address. (from repo_dev key if the module is 
90     # in dev mode.
91     if is_dev and 'repo_dev' in module_info.git_info:
92         coflag = src.printcolors.printcHighlight(coflag.upper())
93         repo_git = module_info.git_info.repo_dev    
94     else:
95         repo_git = module_info.git_info.repo    
96         
97     # Display informations
98     logger.write('%s:%s' % (coflag, src.printcolors.printcInfo(repo_git)), 3, 
99                  False)
100     logger.write(' ' * (pad + 50 - len(repo_git)), 3, False)
101     logger.write(' tag:%s' % src.printcolors.printcInfo(
102                                                     module_info.git_info.tag), 
103                  3,
104                  False)
105     logger.write(' %s. ' % ('.' * (10 - len(module_info.git_info.tag))), 3, 
106                  False)
107     logger.flush()
108     logger.write('\n', 5, False)
109     # Call the system function that do the extraction in git mode
110     retcode = src.system.git_extract(repo_git,
111                                  module_info.git_info.tag,
112                                  source_dir, logger)
113     return retcode
114
115 def get_sources_from_archive(module_info, source_dir, logger):
116     '''The method called if the module is to be get in archive mode
117     
118     :param module_info Config: The configuration specific to 
119                                the module to be prepared
120     :param source_dir Path: The Path instance corresponding to the 
121                             directory where to put the sources
122     :param logger Logger: The logger instance to use for the display and logging
123     :return: True if it succeed, else False
124     :rtype: boolean
125     '''
126     # check archive exists
127     if not os.path.exists(module_info.archive_info.archive_name):
128         raise src.SatException(_("Archive not found: '%s'") % 
129                                module_info.archive_info.archive_name)
130
131     logger.write('arc:%s ... ' % 
132                  src.printcolors.printcInfo(module_info.archive_info.archive_name),
133                  3, 
134                  False)
135     logger.flush()
136     # Call the system function that do the extraction in archive mode
137     retcode, NameExtractedDirectory = src.system.archive_extract(
138                                     module_info.archive_info.archive_name,
139                                     source_dir.dir(), logger)
140     
141     # Rename the source directory if 
142     # it does not match with module_info.source_dir
143     if (NameExtractedDirectory.replace('/', '') != 
144             os.path.basename(module_info.source_dir)):
145         shutil.move(os.path.join(os.path.dirname(module_info.source_dir), 
146                                  NameExtractedDirectory), 
147                     module_info.source_dir)
148     
149     return retcode
150
151 def get_sources_from_cvs(user, module_info, source_dir, checkout, logger, pad):
152     '''The method called if the module is to be get in cvs mode
153     
154     :param user str: The user to use in for the cvs command
155     :param module_info Config: The configuration specific to 
156                                the module to be prepared
157     :param source_dir Path: The Path instance corresponding to the 
158                             directory where to put the sources
159     :param checkout boolean: If True, get the source in checkout mode
160     :param logger Logger: The logger instance to use for the display and logging
161     :param pad int: The gap to apply for the terminal display
162     :return: True if it succeed, else False
163     :rtype: boolean
164     '''
165     # Get the protocol to use in the command
166     if "protocol" in module_info.cvs_info:
167         protocol = module_info.cvs_info.protocol
168     else:
169         protocol = "pserver"
170     
171     # Construct the line to display
172     if "protocol" in module_info.cvs_info:
173         cvs_line = "%s:%s@%s:%s" % \
174             (protocol, user, module_info.cvs_info.server, 
175              module_info.cvs_info.module_base)
176     else:
177         cvs_line = "%s / %s" % (module_info.cvs_info.server, 
178                                 module_info.cvs_info.module_base)
179
180     coflag = 'cvs'
181     if checkout: coflag = src.printcolors.printcHighlight(coflag.upper())
182
183     logger.write('%s:%s' % (coflag, src.printcolors.printcInfo(cvs_line)), 
184                  3, 
185                  False)
186     logger.write(' ' * (pad + 50 - len(cvs_line)), 3, False)
187     logger.write(' src:%s' % 
188                  src.printcolors.printcInfo(module_info.cvs_info.source), 
189                  3, 
190                  False)
191     logger.write(' ' * (pad + 1 - len(module_info.cvs_info.source)), 3, False)
192     logger.write(' tag:%s' % 
193                     src.printcolors.printcInfo(module_info.cvs_info.tag), 
194                  3, 
195                  False)
196     # at least one '.' is visible
197     logger.write(' %s. ' % ('.' * (10 - len(module_info.cvs_info.tag))), 
198                  3, 
199                  False) 
200     logger.flush()
201     logger.write('\n', 5, False)
202
203     # Call the system function that do the extraction in cvs mode
204     retcode = src.system.cvs_extract(protocol, user,
205                                  module_info.cvs_info.server,
206                                  module_info.cvs_info.module_base,
207                                  module_info.cvs_info.tag,
208                                  module_info.cvs_info.source,
209                                  source_dir, logger, checkout)
210     return retcode
211
212 def get_sources_from_svn(user, module_info, source_dir, checkout, logger):
213     '''The method called if the module is to be get in svn mode
214     
215     :param user str: The user to use in for the svn command
216     :param module_info Config: The configuration specific to 
217                                the module to be prepared
218     :param source_dir Path: The Path instance corresponding to the 
219                             directory where to put the sources
220     :param checkout boolean: If True, get the source in checkout mode
221     :param logger Logger: The logger instance to use for the display and logging
222     :return: True if it succeed, else False
223     :rtype: boolean
224     '''
225     coflag = 'svn'
226     if checkout: coflag = src.printcolors.printcHighlight(coflag.upper())
227
228     logger.write('%s:%s ... ' % (coflag, 
229                                  src.printcolors.printcInfo(
230                                             module_info.svn_info.repo)), 
231                  3, 
232                  False)
233     logger.flush()
234     logger.write('\n', 5, False)
235     # Call the system function that do the extraction in svn mode
236     retcode = src.system.svn_extract(user, 
237                                      module_info.svn_info.repo, 
238                                      module_info.svn_info.tag,
239                                      source_dir, 
240                                      logger, 
241                                      checkout)
242     return retcode
243
244 def get_module_sources(config, 
245                        module_info, 
246                        is_dev, 
247                        source_dir,
248                        force,
249                        logger, 
250                        pad, 
251                        checkout=False):
252     '''Get the module sources.
253     
254     :param config Config: The global configuration
255     :param module_info Config: The configuration specific to 
256                                the module to be prepared
257     :param is_dev boolean: True if the module is in development mode
258     :param source_dir Path: The Path instance corresponding to the 
259                             directory where to put the sources
260     :param force boolean: True if the --force option was invoked
261     :param logger Logger: The logger instance to use for the display and logging
262     :param pad int: The gap to apply for the terminal display
263     :param checkout boolean: If True, get the source in checkout mode
264     :return: True if it succeed, else False
265     :rtype: boolean
266     '''
267     if not checkout and is_dev:
268         return get_sources_for_dev(config, 
269                                    module_info, 
270                                    source_dir, 
271                                    force, 
272                                    logger, 
273                                    pad)
274
275     if module_info.get_sources == "git":
276         return get_sources_from_git(module_info, source_dir, logger, pad, 
277                                     is_dev)
278
279     if module_info.get_sources == "archive":
280         return get_sources_from_archive(module_info, source_dir, logger)
281     
282     if module_info.get_sources == "cvs":
283         cvs_user = config.USER.cvs_user
284         return get_sources_from_cvs(cvs_user, 
285                                     module_info, 
286                                     source_dir, 
287                                     checkout, 
288                                     logger,
289                                     pad)
290
291     if module_info.get_sources == "svn":
292         svn_user = config.USER.svn_user
293         return get_sources_from_svn(svn_user, module_info, source_dir, 
294                                     checkout,
295                                     logger)
296
297     if module_info.get_sources == "native":
298         # skip
299         logger.write('%s ...' % _("native (ignored)"), 3, False)
300         return True        
301
302     if module_info.get_sources == "fixed":
303         # skip
304         logger.write('%s ...' % _("fixed (ignored)"), 3, False)
305         return True  
306     
307     if len(module_info.get_sources) == 0:
308         # skip
309         logger.write('%s ...' % _("ignored"), 3, False)
310         return True
311
312     # if the get_sources is not in [git, archive, cvs, svn, dir]
313     logger.write(_("Unknown get_mehtod %(get)s for module %(module)s") % \
314         { 'get': module_info.get_sources, 'module': module_info.name }, 3, False)
315     logger.write(" ... ", 3, False)
316     logger.flush()
317     return False
318
319 def get_all_module_sources(config, modules, force, logger):
320     '''Get all the module sources.
321     
322     :param config Config: The global configuration
323     :param modules List: The list of tuples (module name, module 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 module_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_module_name_len = 1
336     if len(modules) > 0:
337         max_module_name_len = max(map(lambda l: len(l), modules[0])) + 4
338     
339     # The loop on all the modules from which to get the sources
340     for module_name, module_info in modules:
341         # get module name, module informations and the directory where to put
342         # the sources
343         if not src.module.module_is_fixed(module_info):
344             source_dir = src.Path(module_info.source_dir)
345         else:
346             source_dir = src.Path('')
347
348         # display and log
349         logger.write('%s: ' % src.printcolors.printcLabel(module_name), 3)
350         logger.write(' ' * (max_module_name_len - len(module_name)), 3, False)
351         logger.write("\n", 4, False)
352         
353         # Remove the existing source directory if 
354         # the module is not in development mode 
355         is_dev = ("dev_modules" in config.APPLICATION and 
356                   module_name in config.APPLICATION.dev_modules)
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 module
363         retcode = get_module_sources(config, 
364                                      module_info, 
365                                      is_dev, 
366                                      source_dir,
367                                      force, 
368                                      logger, 
369                                      max_module_name_len, 
370                                      checkout=False)
371         
372         '''
373         if 'no_rpath' in module_info.keys():
374             if module_info.no_rpath:
375                 hack_no_rpath(config, module_info, logger)
376         '''
377
378         # show results
379         results[module_name] = retcode
380         if retcode == 'N\A':
381             # The case where the module 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 modules "
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 modules in development mode\n\n")
430         logger.write(src.printcolors.printcWarning(msg))
431     
432     # Get the modules list with modules informations reagrding the options
433     modules_infos = prepare.get_modules_list(options, runner.cfg, logger)
434     
435     # Call to the function that gets all the sources
436     good_result, results = get_all_module_sources(runner.cfg, 
437                                                   modules_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(modules_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(modules))
451
452         for module in results:
453             if results[module] == 0 or results[module] is None:
454                 details.append(module)
455
456     result = len(modules_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