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