Salome HOME
add tests
[modules/yacs.git] / bin / salomeContextUtils.py.in
1 #! /usr/bin/env python
2
3 # Copyright (C) 2013-2015  CEA/DEN, EDF R&D, OPEN CASCADE
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, or (at your option) any later version.
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 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21
22 import os
23 import sys
24 import glob
25 import subprocess
26 import re
27 import socket
28 import json
29
30 """
31 Define a specific exception class to manage exceptions related to SalomeContext
32 """
33 class SalomeContextException(Exception):
34   """Report error messages to the user interface of SalomeContext."""
35 #
36
37 def __listDirectory(path):
38   allFiles = []
39   for root, dirs, files in os.walk(path):
40     cfgFiles = glob.glob(os.path.join(root,'*.cfg'))
41     allFiles += cfgFiles
42
43     shFiles = glob.glob(os.path.join(root,'*.sh'))
44     for f in shFiles:
45       no_ext = os.path.splitext(f)[0]
46       if not os.path.isfile(no_ext+".cfg"):
47         allFiles.append(f)
48
49   return allFiles
50 #
51
52 def __getConfigFileNamesDefault():
53   absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
54   if not absoluteAppliPath:
55     return []
56
57   envdDir = absoluteAppliPath + '/env.d'
58   if not os.path.isdir(envdDir):
59     return []
60
61   return __listDirectory(envdDir)
62 #
63
64 def getConfigFileNames(args, checkExistence=False):
65   # special case: configuration files are provided by user
66   # Search for command-line argument(s) --config=file1,file2,..., filen
67   # Search for command-line argument(s) --config=dir1,dir2,..., dirn
68   configOptionPrefix = "--config="
69   configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ]
70
71   if len(configArgs) == 0:
72     return __getConfigFileNamesDefault(), args, []
73
74   args = [ x for x in args if not x.startswith(configOptionPrefix) ]
75   allLists = [ x.replace(configOptionPrefix, '') for x in configArgs ]
76
77   configFileNames = []
78   unexisting = []
79   for currentList in allLists:
80     elements = currentList.split(',')
81     for elt in elements:
82       elt = os.path.realpath(os.path.expanduser(elt))
83       if os.path.isdir(elt):
84         configFileNames += __listDirectory(elt)
85       else:
86         if checkExistence and not os.path.isfile(elt):
87           unexisting += [elt]
88         else:
89           configFileNames += [elt]
90
91   return configFileNames, args, unexisting
92 #
93
94 def __getScriptPath(scriptName, searchPathList):
95   scriptName = os.path.expanduser(scriptName)
96   if os.path.isabs(scriptName):
97     return scriptName
98
99   if searchPathList is None or len(searchPathList) == 0:
100     return None
101
102   for path in searchPathList:
103     fullName = os.path.join(path, scriptName)
104     if os.path.isfile(fullName) or os.path.isfile(fullName+".py"):
105       return fullName
106
107   return None
108 #
109
110 class ScriptAndArgs:
111   # script: the command to be run, e.g. python <script.py>
112   # args: its input parameters
113   # out: its output parameters
114   def __init__(self, script=None, args=None, out=None):
115     self.script = script
116     self.args = args
117     self.out = out
118   #
119   def __repr__(self):
120     msg = "\n# Script: %s\n"%self.script
121     msg += "     * Input: %s\n"%self.args
122     msg += "     * Output: %s\n"%self.out
123     return msg
124   #
125 #
126 class ScriptAndArgsObjectEncoder(json.JSONEncoder):
127   def default(self, obj):
128     if isinstance(obj, ScriptAndArgs):
129       # to be easily parsed in GUI module (SalomeApp_Application)
130       # Do not export output arguments
131       return {obj.script:obj.args or []}
132     else:
133       return json.JSONEncoder.default(self, obj)
134 #
135
136 def getShortAndExtraArgs(args=None):
137   if args is None:
138     args = []
139   try:
140     pos = args.index("--") # raise a ValueError if not found
141     short_args = args[:pos]
142     extra_args = args[pos:] # include "--"
143   except ValueError:
144     short_args = args
145     extra_args = []
146     pass
147
148   return short_args, extra_args
149 #
150
151 # Return an array of ScriptAndArgs objects
152 def getScriptsAndArgs(args=None, searchPathList=None):
153   if args is None:
154     args = []
155   short_args, extra_args = getShortAndExtraArgs(args)
156   args = short_args
157
158   if searchPathList is None:
159     searchPathList = sys.path
160
161   # Syntax of args: script.py [args:a1,a2=val,an] ... script.py [args:a1,a2=val,an]
162   scriptArgs = []
163   currentKey = None
164   argsPrefix = "args:"
165   outPrefix = "out:"
166   callPython = False
167   afterArgs = False
168   currentScript = None
169
170   for i in range(len(args)):
171     elt = os.path.expanduser(args[i])
172     isDriver = (elt == "driver") # special case for YACS scheme execution
173
174     if elt.startswith(argsPrefix):
175       if not currentKey or callPython:
176         raise SalomeContextException("args list must follow corresponding script file in command line.")
177       elt = elt.replace(argsPrefix, '')
178       scriptArgs[len(scriptArgs)-1].args = [os.path.expanduser(x) for x in elt.split(",")]
179       currentKey = None
180       callPython = False
181       afterArgs = True
182     elif elt.startswith(outPrefix):
183       if (not currentKey and not afterArgs) or callPython:
184         raise SalomeContextException("out list must follow both corresponding script file and its args in command line.")
185       elt = elt.replace(outPrefix, '')
186       scriptArgs[len(scriptArgs)-1].out = [os.path.expanduser(x) for x in elt.split(",")]
187       currentKey = None
188       callPython = False
189       afterArgs = False
190     elif elt.startswith("python"):
191       callPython = True
192       afterArgs = False
193     else:
194       if not os.path.isfile(elt) and not os.path.isfile(elt+".py"):
195         eltInSearchPath = __getScriptPath(elt, searchPathList)
196         if eltInSearchPath is None or (not os.path.isfile(eltInSearchPath) and not os.path.isfile(eltInSearchPath+".py")):
197           if elt[-3:] == ".py":
198             raise SalomeContextException("Script not found: %s"%elt)
199           scriptArgs.append(ScriptAndArgs(script=elt))
200           continue
201         elt = eltInSearchPath
202
203       if elt[-4:] != ".hdf":
204         if elt[-3:] == ".py" or isDriver:
205           currentScript = os.path.abspath(elt)
206         elif os.path.isfile(elt+".py"):
207           currentScript = os.path.abspath(elt+".py")
208         else:
209           currentScript = os.path.abspath(elt) # python script not necessary has .py extension
210         pass
211
212       if currentScript and callPython:
213         currentKey = "@PYTHONBIN@ "+currentScript
214         scriptArgs.append(ScriptAndArgs(script=currentKey))
215         callPython = False
216       elif currentScript:
217         if isDriver:
218           currentKey = currentScript
219           scriptArgs.append(ScriptAndArgs(script=currentKey))
220           callPython = False
221         elif not os.access(currentScript, os.X_OK):
222           currentKey = "@PYTHONBIN@ "+currentScript
223           scriptArgs.append(ScriptAndArgs(script=currentKey))
224         else:
225           ispython = False
226           try:
227             fn = open(currentScript)
228             for i in xrange(10): # read only 10 first lines
229               ln = fn.readline()
230               if re.search("#!.*python"):
231                 ispython = True
232                 break
233               pass
234             fn.close()
235           except:
236             pass
237           if not ispython and currentScript[-3:] == ".py":
238             currentKey = "@PYTHONBIN@ "+currentScript
239           else:
240             currentKey = currentScript
241             pass
242           scriptArgs.append(ScriptAndArgs(script=currentKey))
243       # CLOSE elif currentScript
244       afterArgs = False
245   # end for loop
246
247   if len(extra_args) > 1: # syntax: -- program [options] [arguments]
248     command = extra_args[1]
249     command_args = extra_args[2:]
250     scriptArgs.append(ScriptAndArgs(script=command, args=command_args))
251     pass
252
253   return scriptArgs
254 #
255
256 # Formatting scripts and args as a Bash-like command-line:
257 # script1.py [args] ; script2.py [args] ; ...
258 # scriptArgs is a list of ScriptAndArgs objects; their output parameters are omitted
259 def formatScriptsAndArgs(scriptArgs=None):
260     if scriptArgs is None:
261       return ""
262     commands = []
263     for sa_obj in scriptArgs:
264       cmd = sa_obj.script
265       if sa_obj.args:
266         cmd = " ".join([cmd]+sa_obj.args)
267       commands.append(cmd)
268
269     sep = " ; "
270     if sys.platform == "win32":
271       sep = " & "
272     command = sep.join(["%s"%x for x in commands])
273     return command
274 #
275
276 # Ensure OMNIORB_USER_PATH is defined. This variable refers to a folder in which
277 # SALOME will write omniOrb configuration files.
278 # If OMNIORB_USER_PATH is already set, only checks write access to associated directory ;
279 # an exception is raised if check fails. It allows users for choosing a specific folder.
280 # Else the function sets OMNIORB_USER_PATH this way:
281 # - If APPLI environment variable is set, OMNIORB_USER_PATH is set to ${APPLI}/USERS.
282 #   The function does not check USERS folder existence or write access. This folder
283 #   must exist ; this is the case if SALOME virtual application has been created using
284 #   appli_gen.py script.
285 # - Else OMNIORB_USER_PATH is set to user home directory.
286 def setOmniOrbUserPath():
287   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
288   if omniorbUserPath:
289     if not os.access(omniorbUserPath, os.W_OK):
290       raise Exception("Unable to get write access to directory: %s"%omniorbUserPath)
291     pass
292   else:
293     homePath = os.path.realpath(os.path.expanduser('~'))
294     #defaultOmniorbUserPath = os.path.join(homePath, ".salomeConfig/USERS")
295     defaultOmniorbUserPath = homePath
296     if os.getenv("APPLI"):
297       defaultOmniorbUserPath = os.path.join(homePath, os.getenv("APPLI"), "USERS")
298       pass
299     os.environ["OMNIORB_USER_PATH"] = defaultOmniorbUserPath
300 #
301
302 def getHostname():
303   return socket.gethostname().split('.')[0]
304 #