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