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