]> SALOME platform Git repositories - tools/sat.git/blob - src/system.py
Salome HOME
51bb81158f05c2a0a9d9d5942833fd6643e064a1
[tools/sat.git] / src / system.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 '''
20 In this file : all functions that do a system call, 
21 like open a browser or an editor, or call a git command
22 '''
23
24 import os
25 import subprocess
26 import time
27 import tarfile
28
29 import debug as DBG
30 import utilsSat as UTS
31 import src
32
33 from . import printcolors
34
35 def show_in_editor(editor, filePath, logger):
36     '''open filePath using editor.
37     
38     :param editor str: The editor to use.
39     :param filePath str: The path to the file to open.
40     '''
41     # default editor is vi
42     if editor is None or len(editor) == 0:
43         editor = 'vi'
44     
45     if '%s' not in editor:
46         editor += ' %s'
47
48     try:
49         # launch cmd using subprocess.Popen
50         cmd = editor % filePath
51         logger.write('Launched command:\n' + cmd + '\n', 5)
52         p = subprocess.Popen(cmd, shell=True)
53         p.communicate()
54     except:
55         logger.write(printcolors.printcError(_("Unable to edit file %s\n") 
56                                              % filePath), 1)
57
58 def git_describe(repo_path):
59     '''Use git describe --tags command to return tag description of the git repository"
60     :param repo_path str: The git repository to describe
61     '''
62     git_cmd="cd %s;git describe --tags" % repo_path
63     p = subprocess.Popen(git_cmd, shell=True,
64                     stdin=subprocess.PIPE,
65                     stdout=subprocess.PIPE,
66                     stderr=subprocess.PIPE)
67     p.wait()
68     if p.returncode != 0:
69         return False
70     else:
71         tag_description=p.stdout.readlines()[0].strip()
72         # with python3 this utf8 bytes should be decoded
73         if isinstance(tag_description, bytes):
74             tag_description=tag_description.decode("utf-8", "ignore")
75         return tag_description
76
77
78 def git_extract(from_what, tag, git_options, where, logger, environment=None):
79   '''Extracts sources from a git repository.
80
81   :param from_what str: The remote git repository.
82   :param tag str: The tag.
83   :param git_options str: git options
84   :param where str: The path where to extract.
85   :param logger Logger: The logger instance to use.
86   :param environment src.environment.Environ: The environment to source when extracting.
87   :return: True if the extraction is successful
88   :rtype: boolean
89   '''
90   DBG.write("git_extract", [from_what, tag, str(where)])
91   if not where.exists():
92     where.make()
93   where_git = os.path.join(str(where), ".git")
94   if tag == "master" or tag == "HEAD":
95     if src.architecture.is_windows():
96       cmd = "git clone %(git_options)s %(remote)s %(where)s"
97     else:
98       cmd = r"""
99 set -x
100 git clone %(git_options)s %(remote)s %(where)s
101 touch -d "$(git --git-dir=%(where_git)s  log -1 --format=date_format)" %(where)s
102 """
103     cmd = cmd % {'git_options': git_options, 'remote': from_what, 'tag': tag, 'where': str(where), 'where_git': where_git}
104   else:
105     # NOTICE: this command only works with recent version of git
106     #         because --work-tree does not work with an absolute path
107     if src.architecture.is_windows():
108       cmd = "rmdir %(where)s && git clone %(git_options)s %(remote)s %(where)s && git --git-dir=%(where_git)s --work-tree=%(where)s checkout %(tag)s"
109     else:
110       cmd = r"""
111 set -x
112 rmdir %(where)s
113 git clone %(git_options)s %(remote)s %(where)s && \
114 git --git-dir=%(where_git)s --work-tree=%(where)s checkout %(tag)s && \
115 touch -d "$(git --git-dir=%(where_git)s  log -1 --format=date_format)" %(where)s
116 """
117     cmd = cmd % {'git_options': git_options,
118                  'remote': from_what,
119                  'tag': tag,
120                  'where': str(where),
121                  'where_git': where_git}
122
123
124   cmd=cmd.replace('date_format','"%ai"')
125   logger.logTxtFile.write("\n" + cmd + "\n")
126   logger.logTxtFile.flush()
127
128   DBG.write("cmd", cmd)
129   # git commands may fail sometimes for various raisons 
130   # (big module, network troubles, tuleap maintenance)
131   # therefore we give several tries
132   i_try = 0
133   max_number_of_tries = 3
134   sleep_delay = 30  # seconds
135   while (True):
136     i_try += 1
137     rc = UTS.Popen(cmd, cwd=str(where.dir()), env=environment.environ.environ, logger=logger)
138     if rc.isOk() or (i_try>=max_number_of_tries):
139       break
140     logger.write('\ngit command failed! Wait %d seconds and give an other try (%d/%d)\n' % \
141                  (sleep_delay, i_try + 1, max_number_of_tries), 3)
142     time.sleep(sleep_delay) # wait a little
143
144   return rc.isOk()
145
146
147 def git_extract_sub_dir(from_what, tag, git_options, where, sub_dir, logger, environment=None):
148   '''Extracts sources from a subtree sub_dir of a git repository.
149
150   :param from_what str: The remote git repository.
151   :param tag str: The tag.
152   :param git_options str: git options
153   :param where str: The path where to extract.
154   :param sub_dir str: The relative path of subtree to extract.
155   :param logger Logger: The logger instance to use.
156   :param environment src.environment.Environ: The environment to source when extracting.
157   :return: True if the extraction is successful
158   :rtype: boolean
159   '''
160   strWhere = str(where)
161   tmpWhere = strWhere + '_tmp'
162   parentWhere = os.path.dirname(strWhere)
163   if not os.path.exists(parentWhere):
164     logger.error("not existing directory: %s" % parentWhere)
165     return False
166   if os.path.isdir(strWhere):
167     logger.error("do not override existing directory: %s" % strWhere)
168     return False
169   aDict = {'git_options': git_options,
170            'remote': from_what,
171            'tag': tag,
172            'sub_dir': sub_dir,
173            'where': strWhere,
174            'parentWhere': parentWhere,
175            'tmpWhere': tmpWhere,
176            }
177   DBG.write("git_extract_sub_dir", aDict)
178   if not src.architecture.is_windows():
179     cmd = r"""
180 set -x
181 export tmpDir=%(tmpWhere)s && \
182 rm -rf $tmpDir
183 git clone %(git_options)s %(remote)s $tmpDir && \
184 cd $tmpDir && \
185 git checkout %(tag)s && \
186 mv %(sub_dir)s %(where)s && \
187 git log -1 > %(where)s/README_git_log.txt && \
188 rm -rf $tmpDir
189 """ % aDict
190   else:
191     cmd = r"""
192
193 set tmpDir=%(tmpWhere)s && \
194 rm -rf $tmpDir
195 git clone %(git_options)s %(remote)s $tmpDir && \
196 cd $tmpDir && \
197 git checkout %(tag)s && \
198 mv %(sub_dir)s %(where)s && \
199 git log -1 > %(where)s/README_git_log.txt && \
200 rm -rf $tmpDir
201 """ % aDict
202
203   DBG.write("cmd", cmd)
204
205   for nbtry in range(0,3): # retries case of network problem
206     rc = UTS.Popen(cmd, cwd=parentWhere, env=environment.environ.environ, logger=logger)
207     if rc.isOk(): break
208     time.sleep(30) # wait a little
209
210   return rc.isOk()
211
212 def archive_extract(from_what, where, logger):
213     '''Extracts sources from an archive.
214     
215     :param from_what str: The path to the archive.
216     :param where str: The path where to extract.
217     :param logger Logger: The logger instance to use.
218     :return: True if the extraction is successful
219     :rtype: boolean
220     '''
221     try:
222         archive = tarfile.open(from_what)
223         for i in archive.getmembers():
224             archive.extract(i, path=str(where))
225         return True, os.path.commonprefix(archive.getnames())
226     except Exception as exc:
227         logger.write("archive_extract: %s\n" % exc)
228         return False, None
229
230 def cvs_extract(protocol, user, server, base, tag, product, where,
231                 logger, checkout=False, environment=None):
232     '''Extracts sources from a cvs repository.
233     
234     :param protocol str: The cvs protocol.
235     :param user str: The user to be used.
236     :param server str: The remote cvs server.
237     :param base str: .
238     :param tag str: The tag.
239     :param product str: The product.
240     :param where str: The path where to extract.
241     :param logger Logger: The logger instance to use.
242     :param checkout boolean: If true use checkout cvs.
243     :param environment src.environment.Environ: The environment to source when
244                                                 extracting.
245     :return: True if the extraction is successful
246     :rtype: boolean
247     '''
248
249     opttag = ''
250     if tag is not None and len(tag) > 0:
251         opttag = '-r ' + tag
252
253     cmd = 'export'
254     if checkout:
255         cmd = 'checkout'
256     elif len(opttag) == 0:
257         opttag = '-DNOW'
258     
259     if len(protocol) > 0:
260         root = "%s@%s:%s" % (user, server, base)
261         command = "cvs -d :%(protocol)s:%(root)s %(command)s -d %(where)s %(tag)s %(product)s" % \
262             { 'protocol': protocol, 'root': root, 'where': str(where.base()),
263               'tag': opttag, 'product': product, 'command': cmd }
264     else:
265         command = "cvs -d %(root)s %(command)s -d %(where)s %(tag)s %(base)s/%(product)s" % \
266             { 'root': server, 'base': base, 'where': str(where.base()),
267               'tag': opttag, 'product': product, 'command': cmd }
268
269     logger.write(command + "\n", 5)
270
271     if not where.dir().exists():
272         where.dir().make()
273
274     logger.logTxtFile.write("\n" + command + "\n")
275     logger.logTxtFile.flush()        
276     res = subprocess.call(command,
277                           cwd=str(where.dir()),
278                           env=environment.environ.environ,
279                           shell=True,
280                           stdout=logger.logTxtFile,
281                           stderr=subprocess.STDOUT)
282     return (res == 0)
283
284 def svn_extract(user,
285                 from_what,
286                 tag,
287                 where,
288                 logger,
289                 checkout=False,
290                 environment=None):
291     '''Extracts sources from a svn repository.
292     
293     :param user str: The user to be used.
294     :param from_what str: The remote git repository.
295     :param tag str: The tag.
296     :param where str: The path where to extract.
297     :param logger Logger: The logger instance to use.
298     :param checkout boolean: If true use checkout svn.
299     :param environment src.environment.Environ: The environment to source when
300                                                 extracting.
301     :return: True if the extraction is successful
302     :rtype: boolean
303     '''
304     if not where.exists():
305         where.make()
306
307     if checkout:
308         command = "svn checkout --username %(user)s %(remote)s %(where)s" % \
309             { 'remote': from_what, 'user' : user, 'where': str(where) }
310     else:
311         command = ""
312         if os.path.exists(str(where)):
313             command = "/bin/rm -rf %(where)s && " % \
314                 { 'remote': from_what, 'where': str(where) }
315         
316         if tag == "master":
317             command += "svn export --username %(user)s %(remote)s %(where)s" % \
318                 { 'remote': from_what, 'user' : user, 'where': str(where) }       
319         else:
320             command += "svn export -r %(tag)s --username %(user)s %(remote)s %(where)s" % \
321                 { 'tag' : tag, 'remote': from_what, 'user' : user, 'where': str(where) }
322     
323     logger.logTxtFile.write(command + "\n")
324     
325     logger.write(command + "\n", 5)
326     logger.logTxtFile.write("\n" + command + "\n")
327     logger.logTxtFile.flush()
328     res = subprocess.call(command,
329                           cwd=str(where.dir()),
330                           env=environment.environ.environ,
331                           shell=True,
332                           stdout=logger.logTxtFile,
333                           stderr=subprocess.STDOUT)
334     return (res == 0)
335
336 def get_pkg_check_cmd(dist_name):
337     '''Build the command to use for checking if a linux package is installed or not.'''
338
339     if dist_name in ["CO","FD","MG","MD","CO","OS"]: # linux using rpm
340         linux="RH"  
341         manager_msg_err="Error : command failed because sat was not able to find apt command"
342     else:
343         linux="DB"
344         manager_msg_err="Error : command failed because sat was not able to find rpm command"
345
346     # 1- search for an installed package manager (rpm on rh, apt on db)
347     cmd_which_rpm=["which", "rpm"]
348     cmd_which_apt=["which", "apt"]
349     with open(os.devnull, 'w') as devnull:
350         # 1) we search for apt (debian based systems)
351         completed=subprocess.call(cmd_which_apt,stdout=devnull, stderr=subprocess.STDOUT)
352         if completed==0 and linux=="DB":
353             cmd_is_package_installed=["apt", "list", "--installed"]
354         else:
355             # 2) if apt not found search for rpm (redhat)
356             completed=subprocess.call(cmd_which_rpm,stdout=devnull, stderr=subprocess.STDOUT) # only 3.8! ,capture_output=True)
357             if completed==0 and linux=="RH":
358                 cmd_is_package_installed=["rpm", "-q"]
359             else:
360                 # no package manager was found corresponding to dist_name
361                 raise src.SatException(manager_msg_err)
362     return cmd_is_package_installed
363
364 def check_system_pkg(check_cmd,pkg):
365     '''Check if a package is installed
366     :param check_cmd list: the list of command to use system package manager
367     :param user str: the pkg name to check
368     :rtype: str
369     :return: a string with package name with status un message
370     '''
371     # build command
372     FNULL = open(os.devnull, 'w')
373     cmd_is_package_installed=[]
374     for cmd in check_cmd:
375         cmd_is_package_installed.append(cmd)
376     cmd_is_package_installed.append(pkg)
377
378
379     if check_cmd[0]=="apt":
380         # special treatment for apt
381         # apt output is too messy for being used
382         # some debian packages have version numbers in their name, we need to add a *
383         # also apt do not return status, we need to use grep
384         # and apt output is too messy for being used 
385         cmd_is_package_installed[-1]+="*" # we don't specify in pyconf the exact name because of version numbers
386         p=subprocess.Popen(cmd_is_package_installed,
387                            stdout=subprocess.PIPE,
388                            stderr=FNULL)
389         try:
390             output = subprocess.check_output(['grep', pkg], stdin=p.stdout)
391             msg_status=src.printcolors.printcSuccess("OK")
392         except:
393             msg_status=src.printcolors.printcError("KO")
394             msg_status+=" (package is not installed!)\n"
395     else:
396         p=subprocess.Popen(cmd_is_package_installed,
397                            stdout=subprocess.PIPE,
398                            stderr=FNULL)
399         output, err = p.communicate()
400         rc = p.returncode
401         if rc==0:
402             msg_status=src.printcolors.printcSuccess("OK")
403             # in python3 output is a byte and should be decoded
404             if isinstance(output, bytes):
405                 output = output.decode("utf-8", "ignore")
406             msg_status+=" (" + output.replace('\n',' ') + ")\n" # remove output trailing \n
407         else:
408             msg_status=src.printcolors.printcError("KO")
409             msg_status+=" (package is not installed!)\n"
410
411     return msg_status