Salome HOME
f2ad0ecdcf9ac5a023ac259c96e94149aae42baa
[tools/sat.git] / src / __init__.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 """\
21 initial imports and utilities methods for salomeTools
22 """
23
24 import os
25 import shutil
26 import errno
27 import stat
28 import fnmatch
29
30 from . import pyconf
31 from . import architecture
32 from . import printcolors
33 from . import options
34 from . import system
35 from . import ElementTree
36 from . import logger
37 from . import product
38 from . import environment
39 from . import fileEnviron
40 from . import compilation
41 from . import test_module
42 from . import template
43
44 import platform
45 if platform.system() == "Windows" :
46     import colorama
47     colorama.init()
48
49 OK_STATUS = "OK"
50 KO_STATUS = "KO"
51 NA_STATUS = "NA"
52 KNOWNFAILURE_STATUS = "KF"
53 TIMEOUT_STATUS = "TIMEOUT"
54
55 CONFIG_FILENAME = "sat-config.pyconf"
56
57 class SatException(Exception):
58     """rename Exception Class"""
59     pass
60
61 def ensure_path_exists(p):
62     """Create a path if not existing
63     
64     :param p str: The path.
65     """
66     if not os.path.exists(p):
67         os.makedirs(p)
68         
69 def check_config_has_application( config, details = None ):
70     """check that the config has the key APPLICATION. Else raise an exception.
71     
72     :param config class 'common.pyconf.Config': The config.
73     """
74     if 'APPLICATION' not in config:
75         message = _("An APPLICATION is required. Use 'config --list' to get the list of available applications.\n")
76         if details :
77             details.append(message)
78         raise SatException( message )
79
80 def check_config_has_profile( config, details = None ):
81     """\
82     check that the config has the key APPLICATION.profile.
83     else, raise an exception.
84     
85     :param config class 'common.pyconf.Config': The config.
86     """
87     check_config_has_application(config)
88     if 'profile' not in config.APPLICATION:
89         message = _("A profile section is required in your application.\n")
90         if details :
91             details.append(message)
92         raise SatException( message )
93
94 def config_has_application( config ):
95     return 'APPLICATION' in config
96
97 def get_cfg_param(config, param_name, default):
98     """\
99     eearch for param_name value in config.
100     if param_name is not in config 
101     then return default,
102     else return the found value
103        
104     :param config class 'common.pyconf.Config': The config.
105     :param param_name str: the name of the parameter to get the value
106     :param default str: The value to return if param_name is not in config
107     :return: see initial description of the function
108     :rtype: str
109     """
110     if param_name in config:
111         return config[param_name]
112     return default
113
114
115 def getProductNames(cfg, wildcards, logger):
116     """get products names using * or ? as wildcards like shell Linux"""
117     res = []
118     if type(wildcards) is list:
119       wilds = wildcards
120     else:
121       wilds = [wildcards]
122     products = cfg.APPLICATION.products.keys()
123     for prod in products:
124       for wild in wildcards:
125         filtered = fnmatch.filter([prod], wild)
126         # print("filtered", prod, wild, filtered)
127         if len(filtered) > 0:
128           res.append(prod)
129           break
130     if len(res) == 0:
131       logger.warning("Empty list of products, from %s" % wilds)
132     return res
133
134
135 def print_info(logger, info):
136     """\
137     Prints the tuples that are in info variable in a formatted way.
138     
139     :param logger Logger: The logging instance to use for the prints.
140     :param info list: The list of tuples to display
141     """
142     # find the maximum length of the first value of the tuples in info
143     smax = max(map(lambda l: len(l[0]), info))
144     # Print each item of info with good indentation
145     for i in info:
146         sp = " " * (smax - len(i[0]))
147         printcolors.print_value(logger, sp + i[0], i[1], 2)
148     logger.write("\n", 2)
149
150 def get_base_path(config):
151     """\
152     Returns the path of the products base.
153     
154     :param config Config: The global Config instance.
155     :return: The path of the products base.
156     :rtype: str
157     """
158     if "base" not in config.LOCAL:
159         local_file_path = os.path.join(config.VARS.salometoolsway,
160                                       "data",
161                                       "local.pyconf")
162         msg = _("Please define a base path in the file %s" % local_file_path)
163         raise SatException(msg)
164         
165     base_path = os.path.abspath(config.LOCAL.base)
166     
167     return base_path
168
169 def get_launcher_name(config):
170     """\
171     Returns the name of salome launcher.
172     
173     :param config Config: The global Config instance.
174     :return: The name of salome launcher.
175     :rtype: str
176     """
177     check_config_has_application(config)
178     if 'profile' in config.APPLICATION and 'launcher_name' in config.APPLICATION.profile:
179         launcher_name = config.APPLICATION.profile.launcher_name
180     else:
181         launcher_name = 'salome'
182
183     return launcher_name
184
185 def get_log_path(config):
186     """\
187     Returns the path of the logs.
188     
189     :param config Config: The global Config instance.
190     :return: The path of the logs.
191     :rtype: str
192     """
193     if "log_dir" not in config.LOCAL:
194         local_file_path = os.path.join(config.VARS.salometoolsway,
195                                       "data",
196                                       "local.pyconf")
197         msg = _("Please define a log_dir in the file %s" % local_file_path)
198         raise SatException(msg)
199       
200     log_dir_path = os.path.abspath(config.LOCAL.log_dir)
201     
202     return log_dir_path
203
204 def get_salome_version(config):
205     import versionMinorMajorPatch as VMMP
206
207     if hasattr(config.APPLICATION, 'version_salome'):
208         version = VMMP.MinorMajorPatch(config.APPLICATION.version_salome)
209     else:
210         kernel_info = product.get_product_config(config, "KERNEL")
211         aFile = os.path.join(
212                             kernel_info.install_dir,
213                             "bin",
214                             "salome",
215                             "VERSION")
216         if not os.path.isfile(aFile):
217             return None
218         with open(aFile) as f:
219           line = f.readline()  # example: '[SALOME KERNEL] : 8.4.0'
220         version = VMMP.MinorMajorPatch(line.split(":")[1])
221
222     res = version.strCompact()
223     # print("get_salome_version %s -> %s" % (version, res))
224     return res
225
226 def read_config_from_a_file(filePath):
227         try:
228             cfg_file = pyconf.Config(filePath)
229         except pyconf.ConfigError as e:
230             raise SatException(_("Error in configuration file: %(file)s\n  %(error)s") % \
231                 { 'file': filePath, 'error': str(e) })
232         return cfg_file
233
234 def get_tmp_filename(cfg, name):
235     if not os.path.exists(cfg.VARS.tmp_root):
236         os.makedirs(cfg.VARS.tmp_root)
237
238     return os.path.join(cfg.VARS.tmp_root, name)
239
240 ##
241 # Utils class to simplify path manipulations.
242 class Path:
243     def __init__(self, path):
244         self.path = str(path)
245
246     def __add__(self, other):
247         return Path(os.path.join(self.path, str(other)))
248
249     def __abs__(self):
250         return Path(os.path.abspath(self.path))
251
252     def __str__(self):
253         return self.path
254
255     def __eq__(self, other):
256         return self.path == other.path
257
258     def exists(self):
259         return self.islink() or os.path.exists(self.path)
260
261     def islink(self):
262         return os.path.islink(self.path)
263
264     def isdir(self):
265         return os.path.isdir(self.path)
266
267     def isfile(self):
268         return os.path.isfile(self.path)
269
270     def list(self):
271         return [Path(p) for p in os.listdir(self.path)]
272
273     def dir(self):
274         return Path(os.path.dirname(self.path))
275
276     def base(self):
277         return Path(os.path.basename(self.path))
278
279     def make(self, mode=None):
280         os.makedirs(self.path)        
281         if mode:
282             os.chmod(self.path, mode)
283         
284     def chmod(self, mode):
285         os.chmod(self.path, mode)
286
287     def rm(self):    
288         if self.islink():
289             os.remove(self.path)
290         else:
291             shutil.rmtree( self.path, onerror = handleRemoveReadonly )
292
293     def copy(self, path, smart=False):
294         if not isinstance(path, Path):
295             path = Path(path)
296
297         if os.path.islink(self.path):
298             return self.copylink(path)
299         elif os.path.isdir(self.path):
300             return self.copydir(path, smart)
301         else:
302             return self.copyfile(path)
303
304     def smartcopy(self, path):
305         return self.copy(path, True)
306
307     def readlink(self):
308         if self.islink():
309             return os.readlink(self.path)
310         else:
311             return False
312
313     def symlink(self, path):
314         try:
315             os.symlink(str(path), self.path)
316             return True
317         except:
318             return False
319
320     def copylink(self, path):
321         try:
322             os.symlink(os.readlink(self.path), str(path))
323             return True
324         except:
325             return False
326
327     def copydir(self, dst, smart=False):
328         try:
329             names = self.list()
330
331             if not dst.exists():
332                 dst.make()
333
334             for name in names:
335                 if name == dst:
336                     continue
337                 if smart and (str(name) in [".git", "CVS", ".svn"]):
338                     continue
339                 srcname = self + name
340                 dstname = dst + name
341                 srcname.copy(dstname, smart)
342             return True
343         except:
344             return False
345
346     def copyfile(self, path):
347         try:
348             shutil.copy2(self.path, str(path))
349             return True
350         except:
351             return False
352
353 def find_file_in_lpath(file_name, lpath, additional_dir = ""):
354     """\
355     Find in all the directories in lpath list the file that has the same name
356     as file_name. 
357     If it is found 
358     then return the full path of the file
359     else return False.
360  
361     The additional_dir (optional) is the name of the directory to add to all 
362     paths in lpath.
363     
364     :param file_name str: The file name to search
365     :param lpath List: The list of directories where to search
366     :param additional_dir str: The name of the additional directory
367     :return: the full path of the file or False if not found
368     :rtype: str
369     """
370     for directory in lpath:
371         dir_complete = os.path.join(directory, additional_dir)
372         if not os.path.isdir(directory) or not os.path.isdir(dir_complete):
373             continue
374         l_files = os.listdir(dir_complete)
375         for file_n in l_files:
376             if file_n == file_name:
377                 return os.path.join(dir_complete, file_name)
378     return False
379
380 def handleRemoveReadonly(func, path, exc):
381     excvalue = exc[1]
382     if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
383         os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
384         func(path)
385     else:
386         raise
387
388 def deepcopy_list(input_list):
389     """\
390     Do a deep copy of a list
391     
392     :param input_list List: The list to copy
393     :return: The copy of the list
394     :rtype: List
395     """
396     res = []
397     for elem in input_list:
398         res.append(elem)
399     return res
400
401 def remove_item_from_list(input_list, item):
402     """\
403     Remove all occurences of item from input_list
404     
405     :param input_list List: The list to modify
406     :return: The without any item
407     :rtype: List
408     """
409     res = []
410     for elem in input_list:
411         if elem == item:
412             continue
413         res.append(elem)
414     return res
415
416 def parse_date(date):
417     """\
418     Transform YYYYMMDD_hhmmss into YYYY-MM-DD hh:mm:ss.
419     
420     :param date str: The date to transform
421     :return: The date in the new format
422     :rtype: str
423     """
424     if len(date) != 15:
425         return date
426     res = "%s-%s-%s %s:%s:%s" % (date[0:4],
427                                  date[4:6],
428                                  date[6:8],
429                                  date[9:11],
430                                  date[11:13],
431                                  date[13:])
432     return res
433
434 def merge_dicts(*dict_args):
435     """\
436     Given any number of dicts, shallow copy and merge into a new dict,
437     precedence goes to key value pairs in latter dicts.
438     """
439     result = {}
440     for dictionary in dict_args:
441         result.update(dictionary)
442     return result
443
444 def replace_in_file(filein, strin, strout):
445     """Replace <strin> by <strout> in file <filein>"""
446     shutil.move(filein, filein + "_old")
447     fileout= filein
448     filein = filein + "_old"
449     fin = open(filein, "r")
450     fout = open(fileout, "w")
451     for line in fin:
452         fout.write(line.replace(strin, strout))
453
454 def get_property_in_product_cfg(product_cfg, pprty):
455     if not "properties" in product_cfg:
456         return None
457     if not pprty in product_cfg.properties:
458         return None
459     return product_cfg.properties[pprty]
460
461 def activate_mesa_property(config):
462     """Add mesa property into application properties
463     
464     :param config Config: The global configuration. It must have an application!
465     """
466     # Verify the existence of the file
467     if not 'properties' in config.APPLICATION:
468         config.APPLICATION.addMapping( 'properties', pyconf.Mapping(), None )
469     config.APPLICATION.properties.use_mesa="yes"
470