Salome HOME
remove extra_env feature
[modules/kernel.git] / bin / salomeContextUtils.py.in
1 #! /usr/bin/env python
2
3 # Copyright (C) 2013-2016  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 __getEnvironmentFileNames(args, optionPrefix, checkExistence):
65   # special case: extra configuration/environment files are provided by user
66   # Search for command-line argument(s) <optionPrefix>=file1,file2,..., filen
67   # Search for command-line argument(s) <optionPrefix>=dir1,dir2,..., dirn
68   configArgs = [ str(x) for x in args if str(x).startswith(optionPrefix) ]
69
70   args = [ x for x in args if not x.startswith(optionPrefix) ]
71   allLists = [ x.replace(optionPrefix, '') for x in configArgs ]
72
73   configFileNames = []
74   unexisting = []
75   for currentList in allLists:
76     elements = currentList.split(',')
77     for elt in elements:
78       elt = os.path.realpath(os.path.expanduser(elt))
79       if os.path.isdir(elt):
80         configFileNames += __listDirectory(elt)
81       else:
82         if checkExistence and not os.path.isfile(elt):
83           unexisting += [elt]
84         else:
85           configFileNames += [elt]
86
87   return configFileNames, args, unexisting
88 #
89
90 def getConfigFileNames(args, checkExistence=False):
91   configOptionPrefix = "--config="
92   configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ]
93   if len(configArgs) == 0:
94     configFileNames, unexist = __getConfigFileNamesDefault(), []
95   else:
96     # get configuration filenames
97     configFileNames, args, unexist = __getEnvironmentFileNames(args, configOptionPrefix, checkExistence)
98
99   return configFileNames, args, unexist
100 #
101
102 def __getScriptPath(scriptName, searchPathList):
103   scriptName = os.path.expanduser(scriptName)
104   if os.path.isabs(scriptName):
105     return scriptName
106
107   if searchPathList is None or len(searchPathList) == 0:
108     return None
109
110   for path in searchPathList:
111     fullName = os.path.join(path, scriptName)
112     if os.path.isfile(fullName) or os.path.isfile(fullName+".py"):
113       return fullName
114
115   return None
116 #
117
118 class ScriptAndArgs:
119   # script: the command to be run, e.g. python <script.py>
120   # args: its input parameters
121   # out: its output parameters
122   def __init__(self, script=None, args=None, out=None):
123     self.script = script
124     self.args = args
125     self.out = out
126   #
127   def __repr__(self):
128     msg = "\n# Script: %s\n"%self.script
129     msg += "     * Input: %s\n"%self.args
130     msg += "     * Output: %s\n"%self.out
131     return msg
132   #
133 #
134 class ScriptAndArgsObjectEncoder(json.JSONEncoder):
135   def default(self, obj):
136     if isinstance(obj, ScriptAndArgs):
137       # to be easily parsed in GUI module (SalomeApp_Application)
138       # Do not export output arguments
139       return {obj.script:obj.args or []}
140     else:
141       return json.JSONEncoder.default(self, obj)
142 #
143
144 def getShortAndExtraArgs(args=None):
145   if args is None:
146     args = []
147   try:
148     pos = args.index("--") # raise a ValueError if not found
149     short_args = args[:pos]
150     extra_args = args[pos:] # include "--"
151   except ValueError:
152     short_args = args
153     extra_args = []
154     pass
155
156   return short_args, extra_args
157 #
158
159 # Return an array of ScriptAndArgs objects
160 def getScriptsAndArgs(args=None, searchPathList=None):
161   if args is None:
162     args = []
163   short_args, extra_args = getShortAndExtraArgs(args)
164   args = short_args
165
166   if searchPathList is None:
167     searchPathList = sys.path
168
169   # Syntax of args: script.py [args:a1,a2=val,an] ... script.py [args:a1,a2=val,an]
170   scriptArgs = []
171   currentKey = None
172   argsPrefix = "args:"
173   outPrefix = "out:"
174   callPython = False
175   afterArgs = False
176   currentScript = None
177
178   for i in range(len(args)):
179     elt = os.path.expanduser(args[i])
180     isDriver = (elt == "driver") # special case for YACS scheme execution
181
182     if elt.startswith(argsPrefix):
183       if not currentKey or callPython:
184         raise SalomeContextException("args list must follow corresponding script file in command line.")
185       elt = elt.replace(argsPrefix, '')
186       # Special process if some items of 'args:' list are themselves lists
187       # Note that an item can be a list, but not a list of lists...
188       # So we can have something like this:
189       # myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
190       # With such a call, an elt variable contains the string representing ['file1','file2'],val1,"done",[1,2,3],[True,False],"ok" that is '[file1,file2],val1,done,[1,2,3],[True,False],ok'
191       # We have to split elt to obtain: ['[file1,file2]','val1','done','[1,2,3]','[True,False]','ok']
192       contains_list = re.findall('(\[[^\]]*\])', elt)
193       if contains_list:
194         extracted_args = []
195         x = elt.split(",")
196         # x is ['[file1', 'file2]', 'val1', 'done', '[1', '2', '3]', '[True', 'False]', 'ok']
197         list_begin_indices = [i for i in xrange(len(x)) if x[i].startswith('[')] # [0, 4, 7]
198         list_end_indices = [i for i in xrange(len(x)) if x[i].endswith(']')] # [1, 6, 8]
199         start = 0
200         for lbeg, lend in zip(list_begin_indices,list_end_indices): # [(0, 1), (4, 6), (7, 8)]
201           if lbeg > start:
202             extracted_args += x[start:lbeg]
203             pass
204           extracted_args += [','.join(x[lbeg:lend+1])]
205           start = lend+1
206           pass
207         if start < len(x):
208           extracted_args += x[start:len(x)]
209           pass
210         scriptArgs[len(scriptArgs)-1].args = extracted_args
211         pass
212       else: # a single split is enough
213         scriptArgs[len(scriptArgs)-1].args = [os.path.expanduser(x) for x in elt.split(",")]
214         pass
215       currentKey = None
216       callPython = False
217       afterArgs = True
218     elif elt.startswith(outPrefix):
219       if (not currentKey and not afterArgs) or callPython:
220         raise SalomeContextException("out list must follow both corresponding script file and its args in command line.")
221       elt = elt.replace(outPrefix, '')
222       scriptArgs[len(scriptArgs)-1].out = [os.path.expanduser(x) for x in elt.split(",")]
223       currentKey = None
224       callPython = False
225       afterArgs = False
226     elif elt.startswith("python"):
227       callPython = True
228       afterArgs = False
229     else:
230       if not os.path.isfile(elt) and not os.path.isfile(elt+".py"):
231         eltInSearchPath = __getScriptPath(elt, searchPathList)
232         if eltInSearchPath is None or (not os.path.isfile(eltInSearchPath) and not os.path.isfile(eltInSearchPath+".py")):
233           if elt[-3:] == ".py":
234             raise SalomeContextException("Script not found: %s"%elt)
235           scriptArgs.append(ScriptAndArgs(script=elt))
236           continue
237         elt = eltInSearchPath
238
239       if elt[-4:] != ".hdf":
240         if elt[-3:] == ".py" or isDriver:
241           currentScript = os.path.abspath(elt)
242         elif os.path.isfile(elt+".py"):
243           currentScript = os.path.abspath(elt+".py")
244         else:
245           currentScript = os.path.abspath(elt) # python script not necessary has .py extension
246         pass
247
248       if currentScript and callPython:
249         currentKey = "@PYTHONBIN@ "+currentScript
250         scriptArgs.append(ScriptAndArgs(script=currentKey))
251         callPython = False
252       elif currentScript:
253         if isDriver:
254           currentKey = currentScript
255           scriptArgs.append(ScriptAndArgs(script=currentKey))
256           callPython = False
257         elif not os.access(currentScript, os.X_OK):
258           currentKey = "@PYTHONBIN@ "+currentScript
259           scriptArgs.append(ScriptAndArgs(script=currentKey))
260         else:
261           ispython = False
262           try:
263             fn = open(currentScript)
264             for i in xrange(10): # read only 10 first lines
265               ln = fn.readline()
266               if re.search("#!.*python"):
267                 ispython = True
268                 break
269               pass
270             fn.close()
271           except:
272             pass
273           if not ispython and currentScript[-3:] == ".py":
274             currentKey = "@PYTHONBIN@ "+currentScript
275           else:
276             currentKey = currentScript
277             pass
278           scriptArgs.append(ScriptAndArgs(script=currentKey))
279       # CLOSE elif currentScript
280       afterArgs = False
281   # end for loop
282
283   if len(extra_args) > 1: # syntax: -- program [options] [arguments]
284     command = extra_args[1]
285     command_args = extra_args[2:]
286     scriptArgs.append(ScriptAndArgs(script=command, args=command_args))
287     pass
288
289   return scriptArgs
290 #
291
292 # Formatting scripts and args as a Bash-like command-line:
293 # script1.py [args] ; script2.py [args] ; ...
294 # scriptArgs is a list of ScriptAndArgs objects; their output parameters are omitted
295 def formatScriptsAndArgs(scriptArgs=None):
296     if scriptArgs is None:
297       return ""
298     commands = []
299     for sa_obj in scriptArgs:
300       cmd = sa_obj.script
301       if sa_obj.args:
302         cmd = " ".join([cmd]+sa_obj.args)
303       commands.append(cmd)
304
305     sep = " ; "
306     if sys.platform == "win32":
307       sep = " & "
308     command = sep.join(["%s"%x for x in commands])
309     return command
310 #
311
312 # Ensure OMNIORB_USER_PATH is defined. This variable refers to a folder in which
313 # SALOME will write omniOrb configuration files.
314 # If OMNIORB_USER_PATH is already set, only checks write access to associated directory ;
315 # an exception is raised if check fails. It allows users for choosing a specific folder.
316 # Else the function sets OMNIORB_USER_PATH this way:
317 # - If APPLI environment variable is set, OMNIORB_USER_PATH is set to ${APPLI}/USERS.
318 #   The function does not check USERS folder existence or write access. This folder
319 #   must exist ; this is the case if SALOME virtual application has been created using
320 #   appli_gen.py script.
321 # - Else OMNIORB_USER_PATH is set to user home directory.
322 def setOmniOrbUserPath():
323   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
324   if omniorbUserPath:
325     if not os.access(omniorbUserPath, os.W_OK):
326       raise Exception("Unable to get write access to directory: %s"%omniorbUserPath)
327     pass
328   else:
329     homePath = os.path.realpath(os.path.expanduser('~'))
330     #defaultOmniorbUserPath = os.path.join(homePath, ".salomeConfig/USERS")
331     defaultOmniorbUserPath = homePath
332     if os.getenv("APPLI"):
333       defaultOmniorbUserPath = os.path.join(homePath, os.getenv("APPLI"), "USERS")
334       pass
335     os.environ["OMNIORB_USER_PATH"] = defaultOmniorbUserPath
336 #
337
338 def getHostname():
339   return socket.gethostname().split('.')[0]
340 #