root/MGET/Trunk/PythonPackage/src/GeoEco/Matlab.py

Revision 294, 10.0 kB (checked in by jjr8, 1 year ago)

Merged [291-293] from Jason branch into Trunk. This will be released as MGET 0.6a5.

Line 
1 # Matlab.py - Provides methods for initializing the Matlab Component
2 # Runtime (MCR). These methods are only intended for use within the
3 # GeoEco Python package and should not be invoked by external callers.
4 #
5 # Copyright (C) 2008 Jason J. Roberts
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License (available in the file LICENSE.TXT)
16 # for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21
22 import os
23 import os.path
24 import string
25 import sys
26
27 from GeoEco.Dependencies import Dependency, SoftwareNotInstalledError, UnsupportedPlatformError
28 from GeoEco.DynamicDocString import DynamicDocString
29 from GeoEco.Internationalization import _
30 from GeoEco.Logging import Logger
31
32
33 class MatlabDependency(Dependency):
34     __doc__ = DynamicDocString()
35
36     def GetResultCacheKey(self):
37         return u'MatlabDependency'
38
39     def Initialize(self):
40
41         # Return successfully if we have already been initalized.
42
43         if MatlabDependency._Initialized:
44             return
45
46         # Fail if we are not running on Windows. Currently we only
47         # support Matlab on Windows.
48
49         if sys.platform.lower() != u'win32':
50             Logger.RaiseException(UnsupportedPlatformError(_(u'This tool can only execute on a computer running the Microsoft Windows operating system. Python reports that this computer is running the \"%s\" operating system.') % platform.system()))
51
52         else:
53
54             # Look in the system path for the libraries MCLMCRRT77.DLL
55             # and MCLMCR.DLL.
56
57             Logger.Debug(_(u'Checking software dependency: MATLAB 2007b or MATLAB Component Runtime (MCR) 7.7.'))
58
59             found = False
60             dir1 = None
61             dir2 = None
62            
63             for i, p in enumerate(map(string.strip, os.environ['PATH'].split(os.path.pathsep))):
64                 if len(p) > 0 and os.path.isfile(os.path.join(p, 'MCLMCRRT77.DLL')):
65                     dir1 = p
66                     if os.path.isfile(os.path.join(dir1, 'MCLMCR.DLL')):       # Seems to be true if MATLAB is installed
67                         dir2 = dir1
68                         found = True
69                         break
70                     elif os.path.isfile(os.path.join(os.path.dirname(os.path.dirname(dir1)), 'bin', 'win32', 'MCLMCR.DLL')):       # Seems to be true if MCR is installed
71                         dir2 = os.path.join(os.path.dirname(os.path.dirname(dir1)), 'bin', 'win32')
72                         found = True
73                         break
74
75             # If we don't find the libraries in the path, try the
76             # default MATLAB 2007b and MCR 7.7 installation
77             # directories.
78
79             if not found:
80                 dir1 = os.path.join(os.environ['ProgramFiles'], 'MATLAB', 'R2007b', 'bin', 'win32')
81                 dir2 = dir1
82                 if os.path.isfile(os.path.join(dir1, 'MCLMCRRT77.DLL')) and os.path.isfile(os.path.join(dir2, 'MCLMCR.DLL')):
83                     found = True
84                 else:
85                     dir1 = os.path.join(os.environ['ProgramFiles'], 'MATLAB', 'MATLAB Component Runtime', 'v77', 'runtime', 'win32')
86                     dir2 = os.path.join(os.environ['ProgramFiles'], 'MATLAB', 'MATLAB Component Runtime', 'v77', 'bin', 'win32')
87                     if os.path.isfile(os.path.join(dir1, 'MCLMCRRT77.DLL')) and os.path.isfile(os.path.join(dir2, 'MCLMCR.DLL')):
88                         found = True
89
90             # If we found the directories containing the libraries,
91             # move them to the front of the system path, so that if
92             # multiple copies of MATLAB or the MCR are installed,
93             # these are the ones that are probed first when DLLs are
94             # loaded.
95
96             if found:
97                 newPaths = map(string.strip, os.environ['PATH'].split(os.path.pathsep))
98                 while i < len(newPaths):
99                     if newPaths[i] == dir1 or newPaths[i] == dir2:
100                         del newPaths[i]
101                     else:
102                         i += 1
103                        
104                 if dir1 == dir2:
105                     Logger.Debug(_(u'Found required MATLAB libraries %(dir1)s\\MCLMCRRT77.DLL and %(dir2)s\\MCLMCR.DLL. MATLAB 2007b appears to be installed.') % {u'dir1': dir1, u'dir2': dir2})
106                     os.environ['PATH'] = dir1 + os.path.pathsep + os.path.pathsep.join(newPaths)
107                 else:
108                     Logger.Debug(_(u'Found required MATLAB libraries %(dir1)s\\MCLMCRRT77.DLL and %(dir2)s\\MCLMCR.DLL. MATLAB Component Runtime (MCR) 7.7 appears to be installed.') % {u'dir1': dir1, u'dir2': dir2})
109                     os.environ['PATH'] = dir1 + os.path.pathsep + dir2 + os.path.pathsep + os.path.pathsep.join(newPaths)
110                    
111                 Logger.Debug(_('Set environment variable PATH=%s') % os.environ['PATH'])
112
113             # Fail if we did not fnd the library.
114
115             else:
116                 Logger.RaiseException(SoftwareNotInstalledError(_(u'This tool requires either MATLAB 2007b or the MATLAB Component Runtime (MCR) 7.7, but neither appear to be installed. You can download a free copy of the MCR 7.7 from http://code.env.duke.edu/projects/mget/wiki/MCR. Please install it and try again. If you believe you already have one of these programs installed, and it is the proper version, verify that the program\'s binary directory is in the system PATH variable. Please see the MATLAB documentation for more information.')))
117
118             # If this is ArcGIS 9.3 or later, explicitly load
119             # xerces-c_2_7.dll from the MATLAB directory.
120             #
121             # xerces-c_2_7.dll is an open-source XML parser that is
122             # used by both ArcGIS 9.3 and MATLAB 2007b. Both products
123             # compile this DLL but they versions they produce are not
124             # compatible with each other (perhaps they used different
125             # compilers or compiler options). Because they used the
126             # same file name, when one product calls Win32 LoadLibrary
127             # on that name, there is a chance it will load the other
128             # product's DLL. Or, if the first product has already
129             # loaded its DLL into the process, the second product will
130             # be given a handle to that DLL instead of its own DLL. As
131             # a result, when it calls GetProcAddress (or whatever) to
132             # get the address of a function it wants to call, that
133             # function will not be there and the Win32 error "The
134             # specified procedure could not be found" will occur.
135             #
136             # The proper way to prevent this name collision problem is
137             # for either ESRI or MathWorks or both to compile the DLL
138             # using an ArcGIS- or MATLAB-specific name, rather than
139             # both using the default name, xerces-c_2_7.dll. Neither
140             # of them seem to be aware of the incompatibility problem
141             # they're creating.
142             #
143             # Fortunately it looks like ArcGIS only loads the DLL when
144             # it is needed for specific scenarios. I am not sure what
145             # those scenarios are, but they seem to be rare. But
146             # MATLAB loads it right away when you initialize the MCR.
147             # Our strategy, therefore, is to ensure that the MATLAB
148             # version of the DLL will be loaded. According to the
149             # rules of Win32 LoadLibrary, we should have already done
150             # this by moving the MATLAB paths to the front of the
151             # system PATH variable. But this does not seem to work and
152             # I can't figure out why. Somehow it is still being loaded
153             # from C:\Program Files\ArcGIS\Bin whenever the MCR is
154             # initialized.
155             #
156             # To deal with this, we explicitly load it here from the
157             # MATLAB directory. Then, when the MCR initializes,
158             # LoadLibrary detects that the DLL is already in memory
159             # and uses that copy.
160             #
161             # Note that if ArcGIS ever DOES need to use the DLL, when
162             # it calls LoadLibrary it will get the MATLAB version.
163             # This could cause it to fail in an unexpected way. Sadly,
164             # there is no way around this. Because of this
165             # possibility, all GeoEco tools that use MATLAB should be
166             # run as out-of-process geoprocessing tools under Arc 9.3
167             # and later versions, so that the MATLAB DLL is not loaded
168             # into ArcGIS processes such as ArcCatalog.exe or
169             # ArcMap.exe.
170
171             from GeoEco.ArcGIS import GeoprocessorManager
172             if GeoprocessorManager.GetArcGISMajorVersion() >= 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3:
173                 import win32api
174                 Logger.Debug(_('Explicitly loading %s so the MATLAB MCR will use it rather than ArcGIS\'s version of it, which is not compatible.') % os.path.join(dir2, 'xerces-c_2_7.dll'))
175                 MatlabDependency._XercesDLLHandle = win32api.LoadLibrary(os.path.join(dir2, 'xerces-c_2_7.dll'))
176
177             # Set our flag indicating that initialization was successful.
178
179             MatlabDependency._Initialized = True
180
181     _Initialized = False
182     _XercesDLLHandle = None
183
184
185 ###############################################################################
186 # Names exported by this module
187 ###############################################################################
188
189 __all__ = ['MatlabDependency']
Note: See TracBrowser for help on using the browser.