| 1 | #!/usr/bin/env python
|
|---|
| 2 | #
|
|---|
| 3 | # setup.py - GeoEco Python package build script
|
|---|
| 4 | #
|
|---|
| 5 | # Copyright (C) 2007 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 distutils.core
|
|---|
| 23 | import glob
|
|---|
| 24 | import inspect
|
|---|
| 25 | import os
|
|---|
| 26 | import os.path
|
|---|
| 27 | import re
|
|---|
| 28 | import shutil
|
|---|
| 29 | import stat
|
|---|
| 30 | import subprocess
|
|---|
| 31 | import sys
|
|---|
| 32 | import time
|
|---|
| 33 | import types
|
|---|
| 34 | import xml.dom.minidom
|
|---|
| 35 |
|
|---|
| 36 | print(u'Python ' + sys.version)
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 | # Helper functions
|
|---|
| 40 |
|
|---|
| 41 | def AppendMetadataXMLForModule(moduleName, modulePath, node, document):
|
|---|
| 42 | u"""Append the metadata for the moduleName module, located at modulePath, as child XML elements of node. If this is a package, recurse down subpackages and submodules."""
|
|---|
| 43 |
|
|---|
| 44 | import GeoEco
|
|---|
| 45 | import GeoEco.Metadata
|
|---|
| 46 |
|
|---|
| 47 | # Import the module.
|
|---|
| 48 |
|
|---|
| 49 | print(u' ' + moduleName)
|
|---|
| 50 | __import__(moduleName, globals(), locals())
|
|---|
| 51 |
|
|---|
| 52 | # Append the module's metadata as XML elements.
|
|---|
| 53 |
|
|---|
| 54 | if isinstance(sys.modules[moduleName].__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(sys.modules[moduleName].__doc__.Obj, GeoEco.Metadata.ModuleMetadata):
|
|---|
| 55 | sys.modules[moduleName].__doc__.Obj.AppendXMLNodes(node, document)
|
|---|
| 56 | else:
|
|---|
| 57 | GeoEco.Metadata.ModuleMetadata(moduleName).AppendXMLNodes(node, document)
|
|---|
| 58 | submodulesNode = node.appendChild(document.createElement(u'Modules'))
|
|---|
| 59 |
|
|---|
| 60 | # If this is a directory, then it is a Python package. Process the
|
|---|
| 61 | # submodules and subpackages.
|
|---|
| 62 |
|
|---|
| 63 | if os.path.isdir(modulePath):
|
|---|
| 64 | paths = glob.glob(os.path.join(modulePath, u'*'))
|
|---|
| 65 | for p in paths:
|
|---|
| 66 | if os.path.isfile(p) and p.endswith(u'.py') and os.path.basename(p) != u'__init__.py':
|
|---|
| 67 | submoduleName = moduleName + u'.' + os.path.basename(p)[:-3]
|
|---|
| 68 | elif os.path.isdir(p) and p.lower() != u'.svn' and os.path.isfile(os.path.join(p, u'__init__.py')):
|
|---|
| 69 | submoduleName = moduleName + u'.' + os.path.basename(p)
|
|---|
| 70 | else:
|
|---|
| 71 | continue
|
|---|
| 72 | AppendMetadataXMLForModule(submoduleName, p, submodulesNode.appendChild(document.createElement(u'ModuleMetadata')), document)
|
|---|
| 73 |
|
|---|
| 74 |
|
|---|
| 75 | def WriteArcGISWrapperScriptForMethod(moduleName, method, scriptsDir):
|
|---|
| 76 | u"""Write a Python wrapper script to scriptsDir for invoking method from an ArcGIS toolbox."""
|
|---|
| 77 |
|
|---|
| 78 | import GeoEco.ArcGIS
|
|---|
| 79 |
|
|---|
| 80 | GeoEco.ArcGIS.ValidateMethodMetadataForExposureAsArcGISTool(moduleName, method.Class.Name, method.Name)
|
|---|
| 81 | fileName = os.path.join(scriptsDir, u'%s.%s.py' % (method.Class.Name, method.Name))
|
|---|
| 82 | print(u' Writing \"%s\"' % fileName)
|
|---|
| 83 | f = file(fileName, u'w')
|
|---|
| 84 | f.write('import GeoEco.ArcGIS\n')
|
|---|
| 85 | f.write('GeoEco.ArcGIS.ExecuteMethodFromCommandLineAsArcGISTool(\'%s\', \'%s\', \'%s\')\n' % (str(moduleName), str(method.Class.Name), str(method.Name)))
|
|---|
| 86 | f.close()
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 | def WriteArcGISWrapperScriptsForMethodsInModule(moduleName, modulePath, scriptsDir):
|
|---|
| 90 | u"""For all methods of all classes in the specified module, if the method is flagged for exposure as ArcGIS tool, write a Python wrapper script to scriptsDir for invoking the method from an ArcGIS toolbox. If the specified module is a package, recurse down subpackages and submodules."""
|
|---|
| 91 |
|
|---|
| 92 | import GeoEco.Metadata
|
|---|
| 93 |
|
|---|
| 94 | # Write the wrapper scripts for all of the methods in this module that are
|
|---|
| 95 | # flagged for exposure as ArcGIS tools.
|
|---|
| 96 |
|
|---|
| 97 | arcGISMethods = []
|
|---|
| 98 |
|
|---|
| 99 | if isinstance(sys.modules[moduleName].__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(sys.modules[moduleName].__doc__.Obj, GeoEco.Metadata.ModuleMetadata):
|
|---|
| 100 | for (className, cls) in sys.modules[moduleName].__dict__.items():
|
|---|
| 101 | if inspect.isclass(cls) and isinstance(cls.__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(cls.__doc__.Obj, GeoEco.Metadata.ClassMetadata) and cls.__doc__.Obj.Module == sys.modules[moduleName].__doc__.Obj:
|
|---|
| 102 | for (methodName, method) in inspect.getmembers(cls, inspect.ismethod):
|
|---|
| 103 | if isinstance(method.__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(method.__doc__.Obj, GeoEco.Metadata.MethodMetadata) and method.__doc__.Obj.IsExposedAsArcGISTool:
|
|---|
| 104 | WriteArcGISWrapperScriptForMethod(moduleName, method.__doc__.Obj, scriptsDir)
|
|---|
| 105 | arcGISMethods.append(method.__doc__.Obj)
|
|---|
| 106 |
|
|---|
| 107 | # If this is a directory, then it is a Python package. Process the
|
|---|
| 108 | # submodules and subpackages.
|
|---|
| 109 |
|
|---|
| 110 | if os.path.isdir(modulePath):
|
|---|
| 111 | paths = glob.glob(os.path.join(modulePath, u'*'))
|
|---|
| 112 | for p in paths:
|
|---|
| 113 | if os.path.isfile(p) and p.endswith(u'.py') and os.path.basename(p) != u'__init__.py':
|
|---|
| 114 | submoduleName = moduleName + u'.' + os.path.basename(p)[:-3]
|
|---|
| 115 | elif os.path.isdir(p) and p.lower() != u'.svn' and os.path.isfile(os.path.join(p, u'__init__.py')):
|
|---|
| 116 | submoduleName = moduleName + u'.' + os.path.basename(p)
|
|---|
| 117 | else:
|
|---|
| 118 | continue
|
|---|
| 119 | arcGISMethods.extend(WriteArcGISWrapperScriptsForMethodsInModule(submoduleName, p, scriptsDir))
|
|---|
| 120 |
|
|---|
| 121 | # Return the list of methods that are flagged for exposure as ArcGIS tools.
|
|---|
| 122 |
|
|---|
| 123 | return arcGISMethods
|
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 | def GetCOMClassesInModule(moduleName, modulePath):
|
|---|
| 127 | u"""Return all classes in the specified module that are flagged for exposure using Microsoft COM. If the specified module is a package, recurse down subpackages and submodules."""
|
|---|
| 128 |
|
|---|
| 129 | import GeoEco.Metadata
|
|---|
| 130 |
|
|---|
| 131 | # Add all classes from the specified module.
|
|---|
| 132 |
|
|---|
| 133 | comClasses = []
|
|---|
| 134 |
|
|---|
| 135 | if isinstance(sys.modules[moduleName].__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(sys.modules[moduleName].__doc__.Obj, GeoEco.Metadata.ModuleMetadata):
|
|---|
| 136 | for (className, cls) in sys.modules[moduleName].__dict__.items():
|
|---|
| 137 | if inspect.isclass(cls) and isinstance(cls.__doc__, GeoEco.Metadata.DynamicDocString) and isinstance(cls.__doc__.Obj, GeoEco.Metadata.ClassMetadata) and cls.__doc__.Obj.Module == sys.modules[moduleName].__doc__.Obj and cls.__doc__.Obj.IsExposedAsCOMServer:
|
|---|
| 138 | comClasses.append(cls)
|
|---|
| 139 |
|
|---|
| 140 | # If the specified module is a directory, then it is a Python package.
|
|---|
| 141 | # Process the submodules and subpackages.
|
|---|
| 142 |
|
|---|
| 143 | if os.path.isdir(modulePath):
|
|---|
| 144 | paths = glob.glob(os.path.join(modulePath, u'*'))
|
|---|
| 145 | for p in paths:
|
|---|
| 146 | if os.path.isfile(p) and p.endswith(u'.py') and os.path.basename(p) != u'__init__.py':
|
|---|
| 147 | submoduleName = moduleName + u'.' + os.path.basename(p)[:-3]
|
|---|
| 148 | elif os.path.isdir(p) and p.lower() != u'.svn' and os.path.isfile(os.path.join(p, u'__init__.py')):
|
|---|
| 149 | submoduleName = moduleName + u'.' + os.path.basename(p)
|
|---|
| 150 | else:
|
|---|
| 151 | continue
|
|---|
| 152 | moreCOMClasses = GetCOMClassesInModule(submoduleName, p)
|
|---|
| 153 | comClasses.extend(moreCOMClasses)
|
|---|
| 154 |
|
|---|
| 155 | # Return a list of classes that are flagged for exposure using COM, so
|
|---|
| 156 | # the caller does not have to enumerate them again.
|
|---|
| 157 |
|
|---|
| 158 | return comClasses
|
|---|
| 159 |
|
|---|
| 160 |
|
|---|
| 161 | def RunXslTransform(setupDir, xmlInputFile, xslFile, outputFile):
|
|---|
| 162 | u"""Transform xmlInputFile using xslFile into outputFile, using the GNOME xsltproc program."""
|
|---|
| 163 |
|
|---|
| 164 | args = [os.path.join(setupDir, u'..', u'Bin', sys.platform.lower(), u'xsltproc'),
|
|---|
| 165 | #u'-v', # Uncomment this for verbose output from xsltproc
|
|---|
| 166 | u'-o',
|
|---|
| 167 | outputFile,
|
|---|
| 168 | xslFile,
|
|---|
| 169 | xmlInputFile]
|
|---|
| 170 |
|
|---|
| 171 | if sys.platform.lower() == u'win32':
|
|---|
| 172 | args[0] = args[0] + u'.exe'
|
|---|
| 173 |
|
|---|
| 174 | try:
|
|---|
| 175 | p = subprocess.Popen(args, stderr=subprocess.PIPE)
|
|---|
| 176 | except:
|
|---|
| 177 | print(u'Failed to invoke %s' % u' '.join(args))
|
|---|
| 178 | raise
|
|---|
| 179 | retcode = p.wait()
|
|---|
| 180 |
|
|---|
| 181 | # Occasionally xsltproc seems to fail with transient but obscure messages
|
|---|
| 182 | # such as:
|
|---|
| 183 | #
|
|---|
| 184 | # error : No such file or directory
|
|---|
| 185 | # I/O error : No such file or directory
|
|---|
| 186 | # error : Unknown IO error
|
|---|
| 187 | #
|
|---|
| 188 | # If we receive an error on the STDERR pipe, try again just to make sure
|
|---|
| 189 | # it is not a transient error.
|
|---|
| 190 |
|
|---|
| 191 | msg = p.stderr.read()
|
|---|
| 192 | p.stderr.close()
|
|---|
| 193 | if len(msg) > 0:
|
|---|
| 194 | try:
|
|---|
| 195 | p = subprocess.Popen(args)
|
|---|
| 196 | except:
|
|---|
| 197 | print(u'Failed to invoke %s' % u' '.join(args))
|
|---|
| 198 | raise
|
|---|
| 199 | retcode = p.wait()
|
|---|
| 200 |
|
|---|
| 201 | if retcode != 0:
|
|---|
| 202 | print(u'A non-zero exit code was returned by %s' % u' '.join(args))
|
|---|
| 203 | sys.exit(u'xsltproc failed and returned exit code %i.' % retcode)
|
|---|
| 204 |
|
|---|
| 205 |
|
|---|
| 206 | def CopyTree(src, dest):
|
|---|
| 207 | u"""Copy the directory tree src to dest but omit any subversion directories (.SVN)."""
|
|---|
| 208 |
|
|---|
| 209 | if os.path.basename(src).lower() == u'.svn':
|
|---|
| 210 | return
|
|---|
| 211 | if not os.path.isdir(src):
|
|---|
| 212 | raise IOError(u'The src directory "%s" does not exist' % src)
|
|---|
| 213 | if os.path.exists(dest):
|
|---|
| 214 | raise IOError(u'The dest "%s" already exists' % dest)
|
|---|
| 215 | os.mkdir(dest)
|
|---|
| 216 | for name in os.listdir(src):
|
|---|
| 217 | if os.path.isfile(os.path.join(src, name)):
|
|---|
| 218 | shutil.copy2(os.path.join(src, name), os.path.join(dest, name))
|
|---|
| 219 | elif os.path.isdir(os.path.join(src, name)) and name != u'.svn':
|
|---|
| 220 | CopyTree(os.path.join(src, name), os.path.join(dest, name))
|
|---|
| 221 |
|
|---|
| 222 |
|
|---|
| 223 | # Entry point
|
|---|
| 224 |
|
|---|
| 225 | def main():
|
|---|
| 226 | u"""Entry point."""
|
|---|
| 227 |
|
|---|
| 228 | # Determine the path to the directory containing this file (setup.py) and
|
|---|
| 229 | # log a startup message.
|
|---|
| 230 |
|
|---|
| 231 | if os.path.isabs(__file__):
|
|---|
| 232 | setupDir = os.path.dirname(__file__)
|
|---|
| 233 | elif len(os.path.dirname(__file__)) <= 0:
|
|---|
| 234 | setupDir = os.getcwd()
|
|---|
| 235 | else:
|
|---|
| 236 | setupDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
|
|---|
| 237 |
|
|---|
| 238 | print(u'')
|
|---|
| 239 | print(u'********************************************************************************')
|
|---|
| 240 | print(u'* Initializing')
|
|---|
| 241 | print(u'********************************************************************************')
|
|---|
| 242 | print(u'')
|
|---|
| 243 |
|
|---|
| 244 | print(u'GeoEco setup script (last modified %s)' % time.asctime(time.localtime(os.stat(os.path.join(setupDir, u'setup.py')).st_mtime)))
|
|---|
| 245 |
|
|---|
| 246 | # Validate that we're building for a supported operating system, and import
|
|---|
| 247 | # os-specific modules.
|
|---|
| 248 |
|
|---|
| 249 | if sys.platform.lower() != u'win32':
|
|---|
| 250 | sys.exit(u'Currently, GeoEco can only be built for 32-bit Microsoft Windows, although the GeoEco base modules were all written to be platform independent. The Windows-only constraint will be removed once the development team completes testing of GeoEco on non-Windows platforms.')
|
|---|
| 251 |
|
|---|
| 252 | if sys.platform.lower() == u'win32':
|
|---|
| 253 | major, minor, build, platform, text = sys.getwindowsversion()
|
|---|
| 254 | if major < 5:
|
|---|
| 255 | sys.exit(u'GeoEco cannot be built on Microsoft Windows versions prior to Windows 2000.')
|
|---|
| 256 | if text is not None and len(text) > 0:
|
|---|
| 257 | print(u'Building GeoEco on Microsoft Windows %i.%i build %i with %s' % (major, minor, build, text))
|
|---|
| 258 | else:
|
|---|
| 259 | print(u'Building GeoEco on Microsoft Windows %i.%i build %i' % (major, minor, build))
|
|---|
| 260 |
|
|---|
| 261 | import pythoncom
|
|---|
| 262 |
|
|---|
| 263 | # Make a temporary copy of the source code, so that changes can be made to
|
|---|
| 264 | # it without affecting the original code. Changes include distutils
|
|---|
| 265 | # compiling the .py files and some additional files being generated. We do
|
|---|
| 266 | # not want these files ending up in the subversion database.
|
|---|
| 267 |
|
|---|
| 268 | if not os.path.isdir(os.path.join(setupDir, u'build')):
|
|---|
| 269 | os.makedirs(os.path.join(setupDir, u'build'))
|
|---|
| 270 | print(u'Created directory %s.' % os.path.join(setupDir, u'build'))
|
|---|
| 271 | srcDir = os.path.join(setupDir, u'build', u'src')
|
|---|
| 272 | if os.path.isdir(srcDir):
|
|---|
| 273 | print(u'Removing existing directory %s...' % srcDir)
|
|---|
| 274 | for root, dirs, files in os.walk(srcDir):
|
|---|
| 275 | for d in dirs:
|
|---|
| 276 | os.chmod(os.path.join(root, d), stat.S_IWRITE)
|
|---|
| 277 | for f in files:
|
|---|
| 278 | os.chmod(os.path.join(root, f), stat.S_IWRITE)
|
|---|
| 279 | shutil.rmtree(srcDir, False)
|
|---|
| 280 | print(u'Copying directory %s to %s...' % (os.path.join(setupDir, u'src'), srcDir))
|
|---|
| 281 | CopyTree(os.path.join(setupDir, u'src'), srcDir)
|
|---|
| 282 |
|
|---|
| 283 | # Add the temporary source code directory to the front of the Python import
|
|---|
| 284 | # path, so we can import GeoEco modules from this directory.
|
|---|
| 285 |
|
|---|
| 286 | sys.path.insert(0, srcDir)
|
|---|
| 287 |
|
|---|
| 288 | # Define parameters that we will ultimately pass to distutils.core.setup.
|
|---|
| 289 | # Various parts of this script modify these parameters.
|
|---|
| 290 |
|
|---|
| 291 | packages = ['GeoEco', 'GeoEco.DataManagement', 'GeoEco.Test', 'GeoEco.Tools'] # TODO: Generate this list automatically
|
|---|
| 292 | packageData = {'GeoEco': [os.path.join(u'Configuration', u'*')]}
|
|---|
| 293 | scripts = []
|
|---|
| 294 |
|
|---|
| 295 | # Append the LICENSE.txt file to the list of package data.
|
|---|
| 296 |
|
|---|
| 297 | shutil.copy2(os.path.join(setupDir, u'..', u'LICENSE.txt'), os.path.join(srcDir, u'GeoEco', u'LICENSE.txt'))
|
|---|
| 298 | packageData[u'GeoEco'].append(u'LICENSE.txt')
|
|---|
| 299 |
|
|---|
| 300 | # Append platform-specific binaries to the list of package data.
|
|---|
| 301 |
|
|---|
| 302 | if sys.platform.lower() == u'win32':
|
|---|
| 303 | packageData[u'GeoEco'].append(os.path.join(u'Bin', u'win32', u'*.*'))
|
|---|
| 304 |
|
|---|
| 305 | # C / C++ extension modules.
|
|---|
| 306 | #
|
|---|
| 307 | # TODO: make this dynamic, so we don't have to edit the list every time we
|
|---|
| 308 | # add an extension module.
|
|---|
| 309 |
|
|---|
| 310 | extensionModules=[distutils.core.Extension('GeoEco.DataManagement.BinaryRasterUtils', sources=['src/GeoEco/DataManagement/BinaryRasterUtils.cpp']),
|
|---|
| 311 | distutils.core.Extension('GeoEco.MetadataUtils', sources=['src/GeoEco/MetadataUtils.cpp'])]
|
|---|
| 312 |
|
|---|
| 313 | # Write the metadata for all GeoEco modules to Metadata.xml. This will serve as
|
|---|
| 314 | # input to subsequent build operations.
|
|---|
| 315 |
|
|---|
| 316 | print(u'')
|
|---|
| 317 | print(u'********************************************************************************')
|
|---|
| 318 | print(u'* Generating Metadata.xml')
|
|---|
| 319 | print(u'********************************************************************************')
|
|---|
| 320 | print(u'')
|
|---|
| 321 |
|
|---|
| 322 | import GeoEco
|
|---|
| 323 | import GeoEco.Metadata
|
|---|
| 324 |
|
|---|
| 325 | print(u'Generating XML metadata for Python modules:')
|
|---|
| 326 | dom = xml.dom.minidom.getDOMImplementation()
|
|---|
| 327 | document = dom.createDocument(None, u'ModuleMetadata', None)
|
|---|
| 328 | document.documentElement.setAttribute(u'xmlns:xsi', u'http://www.w3.org/2001/XMLSchema-instance')
|
|---|
| 329 | AppendMetadataXMLForModule(u'GeoEco', sys.modules[u'GeoEco'].__path__[0], document.documentElement, document)
|
|---|
| 330 |
|
|---|
| 331 | print(u'Writing %s...' % os.path.join(srcDir, u'Metadata.xml'))
|
|---|
| 332 | f = file(os.path.join(srcDir, u'Metadata.xml'), 'wb')
|
|---|
| 333 | document.writexml(f)
|
|---|
| 334 | f.close()
|
|---|
| 335 |
|
|---|
| 336 | # If running on Windows, generate the ArcGIS toolbox.
|
|---|
| 337 |
|
|---|
| 338 | if sys.platform.lower() == u'win32':
|
|---|
| 339 | print(u'')
|
|---|
| 340 | print(u'********************************************************************************')
|
|---|
| 341 | print(u'* Generating the ArcGIS toolbox')
|
|---|
| 342 | print(u'********************************************************************************')
|
|---|
| 343 | print(u'')
|
|---|
| 344 |
|
|---|
| 345 | # Create the directories that will hold the ArcGIS-related files.
|
|---|
| 346 |
|
|---|
| 347 | arcGISDir = os.path.join(srcDir, u'GeoEco', u'ArcGISToolbox')
|
|---|
| 348 | os.makedirs(os.path.join(arcGISDir, u'Scripts'))
|
|---|
| 349 | print(u'Created %s.' % os.path.join(arcGISDir, u'Scripts'))
|
|---|
| 350 |
|
|---|
| 351 | # Create the ArcGIS wrapper scripts.
|
|---|
| 352 |
|
|---|
| 353 | import GeoEco.ArcGIS
|
|---|
| 354 |
|
|---|
| 355 | print(u'Writing ArcGIS wrapper scripts:')
|
|---|
| 356 | arcGISMethods = WriteArcGISWrapperScriptsForMethodsInModule(u'GeoEco', sys.modules[u'GeoEco'].__path__[0], os.path.join(arcGISDir, u'Scripts'))
|
|---|
| 357 |
|
|---|
| 358 | # TODO: validate that the same tool name is not exposed twice (Trac ticket #29)
|
|---|
| 359 |
|
|---|
| 360 | # Create the ArcGIS toolbox.
|
|---|
| 361 |
|
|---|
| 362 | args = [os.path.join(setupDir, u'..', u'VisualStudioSolutions', u'CreateArcGISToolbox', u'bin', u'Release', u'CreateArcGISToolbox.exe'),
|
|---|
| 363 | os.path.join(srcDir, u'Metadata.xml'),
|
|---|
| 364 | os.path.join(arcGISDir, u'Marine Geospatial Ecology Tools'),
|
|---|
| 365 | u'GeoEco',
|
|---|
| 366 | u'Scripts',
|
|---|
| 367 | os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference')]
|
|---|
| 368 |
|
|---|
| 369 | print(u'Invoking %s' % u' '.join(args))
|
|---|
| 370 | p = subprocess.Popen(args)
|
|---|
| 371 | retcode = p.wait()
|
|---|
| 372 | if retcode != 0:
|
|---|
| 373 | sys.exit(u'CreateArcGISToolBox failed and returned exit code %i.' % retcode)
|
|---|
| 374 |
|
|---|
| 375 | # Copy in binaries written by GeoEco developers.
|
|---|
| 376 |
|
|---|
| 377 | shutil.copyfile(os.path.join(setupDir, u'..', u'VisualStudioSolutions', u'RegisterToolboxWithArcCatalog', u'bin', u'Release', u'RegisterToolboxWithArcCatalog.exe'), os.path.join(arcGISDir, u'Bin', u'RegisterToolboxWithArcCatalog.exe'))
|
|---|
| 378 |
|
|---|
| 379 | # Ensure these files are provided to distutils.
|
|---|
| 380 |
|
|---|
| 381 | packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'*.*'))
|
|---|
| 382 | packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'Bin', u'*.*'))
|
|---|
| 383 | packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'Rasters', u'*.*'))
|
|---|
| 384 | packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'Rasters', u'*', u'*.*'))
|
|---|
| 385 | packageData[u'GeoEco'].append(os.path.join(u'ArcGISToolbox', u'Scripts', u'*.*'))
|
|---|
| 386 |
|
|---|
| 387 | # If running on Windows, generate the COM type library for classes that can
|
|---|
| 388 | # be invoked through COM.
|
|---|
| 389 |
|
|---|
| 390 | if sys.platform.lower() == u'win32':
|
|---|
| 391 | print(u'')
|
|---|
| 392 | print(u'********************************************************************************')
|
|---|
| 393 | print(u'* Generating the Microsoft COM type library')
|
|---|
| 394 | print(u'********************************************************************************')
|
|---|
| 395 | print(u'')
|
|---|
| 396 |
|
|---|
| 397 | # Open the IDL file for writing.
|
|---|
| 398 |
|
|---|
| 399 | comDir = os.path.join(srcDir, u'GeoEco', u'COM')
|
|---|
| 400 | idlFilePath = os.path.join(comDir, u'GeoEco.idl')
|
|---|
| 401 | print(u'Opening %s for writing.' % idlFilePath)
|
|---|
| 402 | idlFile = file(idlFilePath, u'w')
|
|---|
| 403 | idlFile.write('// IDL for GeoEco Type Library\n')
|
|---|
| 404 | idlFile.write('//\n')
|
|---|
| 405 | idlFile.write('// GeoEco is a Python package that provides geospatial ecology tools.\n')
|
|---|
| 406 | idlFile.write('// Many of these tools are exposed as COM classes. Each class has a\n')
|
|---|
| 407 | idlFile.write('// ProgID beginning with "GeoEco." and exposes a dual interface.\n')
|
|---|
| 408 | idlFile.write('// Late-binding clients such as script engines can invoke the classes\n')
|
|---|
| 409 | idlFile.write('// through the IDispatch interface, while early-binding clients such\n')
|
|---|
| 410 | idlFile.write('// as C# can import the GeoEco Type Library and invoke the classes\n')
|
|---|
| 411 | idlFile.write('// through the more-efficient vtable interface.\n')
|
|---|
| 412 | idlFile.write('//\n')
|
|---|
| 413 | idlFile.write('// This IDL file defines all of the interfaces and classes exported by\n')
|
|---|
| 414 | idlFile.write('// the GeoEco type library.\n')
|
|---|
| 415 | idlFile.write('\n')
|
|---|
| 416 | idlFile.write('import "oaidl.idl";\n')
|
|---|
| 417 | idlFile.write('import "ocidl.idl";\n')
|
|---|
| 418 | idlFile.write('\n')
|
|---|
| 419 |
|
|---|
| 420 | # Obtain a list of all of the classes flagged for exposure through COM.
|
|---|
| 421 | # Validate the classes' metadata. Also validate that each
|
|---|
| 422 | # class has a unique name across the entire set of classes, regardless
|
|---|
| 423 | # of which module it appears in. This means developers cannot
|
|---|
| 424 | # disambiguate classes with the same names using the Python module
|
|---|
| 425 | # hierarchy. The benefit of this restriction is that we don't have to
|
|---|
| 426 | # generate multiple type libraries, or a single type library that
|
|---|
| 427 | # prepends the full list of module names to the class name.
|
|---|
| 428 |
|
|---|
| 429 | print(u'Validating classes flagged for exposure by COM:')
|
|---|
| 430 |
|
|---|
| 431 | import pythoncom
|
|---|
| 432 | import GeoEco.COM
|
|---|
| 433 |
|
|---|
| 434 | classDict = {}
|
|---|
| 435 | guidDict = {}
|
|---|
| 436 |
|
|---|
| 437 | comClasses = GetCOMClassesInModule(u'GeoEco', sys.modules[u'GeoEco'].__path__[0])
|
|---|
| 438 | for cls in comClasses:
|
|---|
| 439 | GeoEco.COM.ValidateClassMetadata(cls)
|
|---|
| 440 | classMetadata = cls.__doc__.Obj
|
|---|
| 441 |
|
|---|
| 442 | if classDict.has_key(classMetadata.Name):
|
|---|
| 443 | sys.exit(u'Two classes flagged for exposure by COM have the same class name: %s.%s and %s.%s. To be exposed by COM, each class must have a unique name, regardless of what module it appears in. Please change the name of one of these classes.' % (classMetadata.Module.Name, classMetadata.Name, classDict[classMetadata.Name].Module.Name, classDict[classMetadata.Name].Name))
|
|---|
| 444 | classDict[classMetadata.Name] = classMetadata
|
|---|
| 445 |
|
|---|
| 446 | if guidDict.has_key(classMetadata.COMIID):
|
|---|
| 447 | sys.exit(u'Two classes flagged for exposure by COM are using the same GUID %s: %s.%s is using it for COMIID and %s.%s is using it for %s. To be exposed by COM, each class must have a unique GUID for COMIID and COMCLSID. Here is a new unique GUID you can use: %s' % (classMetadata.COMIID, classMetadata.Module.Name, classMetadata.Name, guidDict[classMetadata.COMIID][1].Module.Name, guidDict[classMetadata.COMIID][1].Name, guidDict[classMetadata.COMIID][0], repr(unicode(pythoncom.CreateGuid()))))
|
|---|
| 448 | guidDict[classMetadata.COMIID] = (u'COMIID', classMetadata)
|
|---|
| 449 |
|
|---|
| 450 | if guidDict.has_key(classMetadata.COMCLSID):
|
|---|
| 451 | sys.exit(u'Two classes flagged for exposure by COM are using the same GUID %s: %s.%s is using it for COMCLSID and %s.%s is using it for %s. To be exposed by COM, each class must have a unique GUID for COMIID and COMCLSID. Here is a new unique GUID you can use: %s' % (classMetadata.COMIID, classMetadata.Module.Name, classMetadata.Name, guidDict[classMetadata.COMIID][1].Module.Name, guidDict[classMetadata.COMIID][1].Name, guidDict[classMetadata.COMIID][0], repr(unicode(pythoncom.CreateGuid()))))
|
|---|
| 452 | guidDict[classMetadata.COMIID] = (u'COMCLSID', classMetadata)
|
|---|
| 453 |
|
|---|
| 454 | print(' %s.%s as ProgID %s' % (cls.__doc__.Obj.Module.Name, cls.__doc__.Obj.Name, cls.__doc__.Obj.COMVersionIndependentProgID))
|
|---|
| 455 |
|
|---|
| 456 | # Write the interface definitions to the IDL file.
|
|---|
| 457 |
|
|---|
| 458 | print(u'Writing interface definitions to %s.' % idlFilePath)
|
|---|
| 459 | for cls in comClasses:
|
|---|
| 460 | idlFile.write(GeoEco.COM.GetIDLInterfaceDefinitionFromMetadata(cls.__doc__.Obj))
|
|---|
| 461 | idlFile.write('\n')
|
|---|
| 462 |
|
|---|
| 463 | # Write type library header to the IDL file.
|
|---|
| 464 |
|
|---|
| 465 | idlFile.write('[\n')
|
|---|
| 466 | idlFile.write(' uuid(%s),\n' % GeoEco.COM.TypeLibraryGUID[1:-1])
|
|---|
| 467 | idlFile.write(' version(%i.%i),\n' % (GeoEco.COM.TypeLibraryVersion[0], GeoEco.COM.TypeLibraryVersion[1]))
|
|---|
| 468 | idlFile.write(' helpstring("GeoEco %i.%i Type Library")\n' % (GeoEco.COM.TypeLibraryVersion[0], GeoEco.COM.TypeLibraryVersion[1]))
|
|---|
| 469 | idlFile.write(']\n')
|
|---|
| 470 | idlFile.write('library GeoEco\n')
|
|---|
| 471 | idlFile.write('{\n')
|
|---|
| 472 | idlFile.write(' importlib("stdole32.tlb");\n')
|
|---|
| 473 | idlFile.write(' importlib("stdole2.tlb");\n')
|
|---|
| 474 |
|
|---|
| 475 | # Write the coclass definitions to the IDL file.
|
|---|
| 476 |
|
|---|
| 477 | for cls in comClasses:
|
|---|
| 478 | idlFile.write('\n')
|
|---|
| 479 | idlFile.write(' [\n')
|
|---|
| 480 | idlFile.write(' uuid(%s),\n' % cls.__doc__.Obj.COMCLSID[1:-1])
|
|---|
| 481 | idlFile.write(' helpstring("%s Class")\n' % cls.__doc__.Obj.Name)
|
|---|
| 482 | idlFile.write(' ]\n')
|
|---|
| 483 | idlFile.write(' coclass %s\n' % cls.__doc__.Obj.Name)
|
|---|
| 484 | idlFile.write(' {\n')
|
|---|
| 485 | idlFile.write(' [default] interface I%s;\n' % cls.__doc__.Obj.Name)
|
|---|
| 486 | idlFile.write(' };\n')
|
|---|
| 487 |
|
|---|
| 488 | # Close the IDL file.
|
|---|
| 489 |
|
|---|
| 490 | idlFile.write('};\n')
|
|---|
| 491 | idlFile.close()
|
|---|
| 492 |
|
|---|
| 493 | # Execute the MIDL compiler on the IDL file to produce the TLB file.
|
|---|
| 494 |
|
|---|
| 495 | tlbFilePath = os.path.splitext(idlFilePath)[0] + u'.tlb'
|
|---|
| 496 |
|
|---|
| 497 | args = [u'midl.exe', u'/out', os.path.dirname(idlFilePath), u'/tlb', tlbFilePath, u'/win32', idlFilePath]
|
|---|
| 498 | print('Invoking %s' % ' '.join(args))
|
|---|
| 499 | try:
|
|---|
| 500 | p = subprocess.Popen(args)
|
|---|
| 501 | except WindowsError, e:
|
|---|
| 502 | if e.errno == 2:
|
|---|
| 503 | print(e.__class__.__name__ + u': ' + unicode(e))
|
|---|
| 504 | sys.exit(u'The most common cause for failure here is forgetting to register the Microsoft Visual Studio environment variables. This is usually best accomplished by running "C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\Tools\\vsvars32.bat" prior to executing setup.py.')
|
|---|
| 505 | else:
|
|---|
| 506 | raise
|
|---|
| 507 | retcode = p.wait()
|
|---|
| 508 | if retcode != 0:
|
|---|
| 509 | sys.exit(u'midl.exe failed and returned exit code %i.' % retcode)
|
|---|
| 510 | for ext in [u'_i.c', u'_p.c', u'.h']:
|
|---|
| 511 | if os.path.exists(os.path.splitext(idlFilePath)[0] + ext):
|
|---|
| 512 | os.remove(os.path.splitext(idlFilePath)[0] + ext)
|
|---|
| 513 | if os.path.exists(os.path.join(os.path.dirname(idlFilePath), u'dlldata.c')):
|
|---|
| 514 | os.remove(os.path.join(os.path.dirname(idlFilePath), u'dlldata.c'))
|
|---|
| 515 |
|
|---|
| 516 | packageData[u'GeoEco'].append(os.path.join(u'COM', u'*'))
|
|---|
| 517 |
|
|---|
| 518 | # If running on Windows, generate the postinstall script.
|
|---|
| 519 |
|
|---|
| 520 | if sys.platform.lower() == u'win32':
|
|---|
| 521 | print(u'')
|
|---|
| 522 | print(u'********************************************************************************')
|
|---|
| 523 | print(u'* Generating the Windows postinstall script')
|
|---|
| 524 | print(u'********************************************************************************')
|
|---|
| 525 | print(u'')
|
|---|
| 526 |
|
|---|
| 527 | # Generate the list of classes that need COM registration.
|
|---|
| 528 |
|
|---|
| 529 | classesForCOMRegistration = '[' + ', '.join(map(lambda cls: '[' + repr(str(cls.__doc__.Obj.Module.Name)) + ', ' + repr(str(cls.__doc__.Obj.Name)) + ']', comClasses)) + ']'
|
|---|
| 530 |
|
|---|
| 531 | # Write the install script.
|
|---|
| 532 |
|
|---|
| 533 | postInstallFilePath = os.path.join(srcDir, u'GeoEcoPostInstall.py')
|
|---|
| 534 | print(u'Opening %s for writing.' % postInstallFilePath)
|
|---|
| 535 | f = file(postInstallFilePath, u'w')
|
|---|
| 536 |
|
|---|
| 537 | f.write("""# GeoEco package post-install script.
|
|---|
| 538 | #
|
|---|
| 539 | # Do not delete this file! It runs during uninstallation of the GeoEco package.
|
|---|
| 540 | # It will be deleted automatically as part of uninstallation of GeoEco.
|
|---|
| 541 |
|
|---|
| 542 | import os
|
|---|
| 543 | import os.path
|
|---|
| 544 | import shutil
|
|---|
| 545 | import stat
|
|---|
| 546 | import sys
|
|---|
| 547 | import subprocess
|
|---|
| 548 |
|
|---|
| 549 | import GeoEco
|
|---|
| 550 | geoEcoRoot = os.path.dirname(sys.modules['GeoEco'].__file__)
|
|---|
| 551 |
|
|---|
| 552 | ###############################################################################
|
|---|
| 553 | # INPUT DATA
|
|---|
| 554 | ###############################################################################
|
|---|
| 555 |
|
|---|
| 556 | classesForCOMRegistration = """ + classesForCOMRegistration + """
|
|---|
| 557 |
|
|---|
| 558 | ###############################################################################
|
|---|
| 559 | # HELPER FUNCTIONS
|
|---|
| 560 | ###############################################################################
|
|---|
| 561 |
|
|---|
| 562 | def ChmodRecursive(root, mode): |
|---|
| 563 | if os.path.isfile(root): |
|---|
| 564 | os.chmod(root, mode) |
|---|
| 565 | elif os.path.isdir(root): |
|---|
| 566 | os.chmod(root, mode) |
|---|
| 567 | for (root, dirs, files) in os.walk(root): |
|---|
| 568 | for name in files: |
|---|
| 569 | os.chmod(os.path.join(root, name), mode) |
|---|
| 570 | for name in dirs: |
|---|
| 571 | os.chmod(os.path.join(root, name), mode)
|
|---|
| 572 |
|
|---|
| 573 | def RegisterToolboxWithArcCatalog(action):
|
|---|
| 574 | assert action == 'register' or action == 'unregister'
|
|---|
| 575 |
|
|---|
| 576 | # Set/remove the read-only flag on the ArcGIS toolbox directory. This is
|
|---|
| 577 | # recommended by the ArcGIS documentation, so that users do not accidentally
|
|---|
| 578 | # tamper with the toolbox.
|
|---|
| 579 |
|
|---|
| 580 | if action == 'register':
|
|---|
| 581 | ChmodRecursive(os.path.join(geoEcoRoot, 'ArcGISToolbox'), stat.S_IREAD)
|
|---|
| 582 | else:
|
|---|
| 583 | ChmodRecursive(os.path.join(geoEcoRoot, 'ArcGISToolbox'), stat.S_IWRITE)
|
|---|
| 584 |
|
|---|
| 585 | # Import pywin32 modules that we need.
|
|---|
| 586 |
|
|---|
| 587 | try:
|
|---|
| 588 | import pywintypes
|
|---|
| 589 | import win32api
|
|---|
| 590 | import win32con
|
|---|
| 591 | import win32file
|
|---|
| 592 | import win32pipe
|
|---|
| 593 | import win32process
|
|---|
| 594 | except:
|
|---|
| 595 | sys.exit('ERROR: Python for Windows Extensions (pywin32) is not installed. GeoEco requires this Python package. Please uninstall GeoEco, download pywin32 for your version of Python from http://sourceforge.net/projects/pywin32, install it, and install GeoEco again.')
|
|---|
| 596 |
|
|---|
| 597 | # Ensure the ArcGIS is installed by reading the InstallDir from the registry.
|
|---|
| 598 |
|
|---|
| 599 | try:
|
|---|
| 600 | hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0')
|
|---|
| 601 | try:
|
|---|
| 602 | (arcGISInstallDir, installDirType) = win32api.RegQueryValueEx(hkey, 'InstallDir')
|
|---|
| 603 | finally:
|
|---|
| 604 | try:
|
|---|
| 605 | win32api.RegCloseKey(hkey)
|
|---|
| 606 | except:
|
|---|
| 607 | pass
|
|---|
| 608 | if not isinstance(arcGISInstallDir, basestring) or not os.path.isdir(arcGISInstallDir):
|
|---|
| 609 | raise ValueError('The directory specified by the InstallDir value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key does not exist.')
|
|---|
| 610 | if not os.path.isfile(os.path.join(arcGISInstallDir, 'Bin', 'ArcCatalog.exe')):
|
|---|
| 611 | raise ValueError('The ArcGIS file "%s" does not exist.', os.path.join(arcGISInstallDir, 'Bin', 'ArcCatalog.exe'))
|
|---|
| 612 | except Exception, e:
|
|---|
| 613 | if action == 'register':
|
|---|
| 614 | print('WARNING: ArcGIS does not appear to be installed. If you install ArcGIS in the future and want to invoke GeoEco tools from geoprocessing models, you can manually add the toolbox from this location:')
|
|---|
| 615 | print('')
|
|---|
| 616 | print(os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx'))
|
|---|
| 617 | return False
|
|---|
| 618 |
|
|---|
| 619 | # Run RegisterToolboxWithArcCatalog.exe.
|
|---|
| 620 |
|
|---|
| 621 | args = [os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Bin', 'RegisterToolboxWithArcCatalog.exe'),
|
|---|
| 622 | action,
|
|---|
| 623 | os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx')]
|
|---|
| 624 |
|
|---|
| 625 | try:
|
|---|
| 626 | p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True)
|
|---|
| 627 | except Exception, e:
|
|---|
| 628 | if action == 'register':
|
|---|
| 629 | print('WARNING: Failed to add the ArcGIS toolbox to ArcCatalog. When attempting to start the toolbox registration program "%s register \\'%s\\'", Python reported an error: %s' % (os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Bin', 'RegisterToolboxWithArcCatalog.exe'), os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx'), str(e).strip()))
|
|---|
| 630 | print('')
|
|---|
| 631 | print('You can manually add the toolbox to ArcCatalog from this location:')
|
|---|
| 632 | print('')
|
|---|
| 633 | print(os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx'))
|
|---|
| 634 | return False
|
|---|
| 635 |
|
|---|
| 636 | # Wait for RegisterToolboxWithArcCatalog.exe to complete and
|
|---|
| 637 | # capture any error message.
|
|---|
| 638 |
|
|---|
| 639 | retcode = p.wait()
|
|---|
| 640 | if retcode != 0:
|
|---|
| 641 | if action == 'register':
|
|---|
| 642 | print('WARNING: Failed to add the ArcGIS toolbox to ArcCatalog. The toolbox registration program "%s register \\'%s\\'" reported: %s' % (os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Bin', 'RegisterToolboxWithArcCatalog.exe'), os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx'), p.stderr.read().strip()))
|
|---|
| 643 | print('')
|
|---|
| 644 | print('You can manually add the toolbox to ArcCatalog from this location:')
|
|---|
| 645 | print('')
|
|---|
| 646 | print(os.path.join(geoEcoRoot, 'ArcGISToolbox', 'Marine Geospatial Ecology Tools.tbx'))
|
|---|
| 647 | return False
|
|---|
| 648 |
|
|---|
| 649 | # Return successfully.
|
|---|
| 650 |
|
|---|
| 651 | return True
|
|---|
| 652 |
|
|---|
| 653 | def RegisterClassesAsCOMServers():
|
|---|
| 654 |
|
|---|
| 655 | # Import pywin32 modules that we need.
|
|---|
| 656 |
|
|---|
| 657 | try:
|
|---|
| 658 | import pythoncom
|
|---|
| 659 | import win32com.server.register
|
|---|
| 660 | except:
|
|---|
| 661 | sys.exit('ERROR: Python for Windows Extensions (pywin32) is not installed. GeoEco requires this Python package. Please uninstall GeoEco, download pywin32 for your version of Python from http://sourceforge.net/projects/pywin32, install it, and install GeoEco again.')
|
|---|
| 662 |
|
|---|
| 663 | # Register the classes.
|
|---|
| 664 |
|
|---|
| 665 | success = True
|
|---|
| 666 | import GeoEco.COM
|
|---|
| 667 | for [moduleName, className] in classesForCOMRegistration:
|
|---|
| 668 | if not GeoEco.COM.RegisterCOMServerUsingClassMetadata(moduleName, className):
|
|---|
| 669 | success = False
|
|---|
| 670 |
|
|---|
| 671 | # Register the type library.
|
|---|
| 672 |
|
|---|
| 673 | tlbFile = os.path.join(os.path.dirname(sys.modules['GeoEco.COM'].__file__), 'COM', 'GeoEco.tlb')
|
|---|
| 674 | try:
|
|---|
| 675 | tli = pythoncom.LoadTypeLib(tlbFile)
|
|---|
| 676 | except Exception, e:
|
|---|
| 677 | print('WARNING: Failed to load the GeoEco COM type library. Since the library could not be loaded, it will not be registered with COM, and you will not be able to call GeoEco objects from early-bound programming languages such as C#. You can try registration again by uninstalling and reinstalling GeoEco. The pythoncom.LoadTypeLib(\\'%s\\') function reported: %s: %s' % (tlbFile, e.__class__.__name__, str(e)))
|
|---|
| 678 | success = False
|
|---|
| 679 |
|
|---|
| 680 | if success:
|
|---|
| 681 | try:
|
|---|
| 682 | pythoncom.RegisterTypeLib(tli, tlbFile)
|
|---|
| 683 | except Exception, e:
|
|---|
| 684 | print('WARNING: Failed to register the GeoEco COM type library. You will not be able to call GeoEco objects from early-bound programming languages such as C#. You can try registration again by uninstalling and reinstalling GeoEco. The pythoncom.RegisterTypeLib(tli, \\'%s\\') function reported: %s: %s' % (tlbFile, e.__class__.__name__, str(e)))
|
|---|
| 685 | success = False
|
|---|
| 686 |
|
|---|
| 687 | return success
|
|---|
| 688 |
|
|---|
| 689 | def UnregisterClassesAsCOMServers():
|
|---|
| 690 |
|
|---|
| 691 | # Import pywin32 modules that we need.
|
|---|
| 692 |
|
|---|
| 693 | try:
|
|---|
| 694 | import pythoncom
|
|---|
| 695 | import win32com.server.register
|
|---|
| 696 | except:
|
|---|
| 697 | return
|
|---|
| 698 |
|
|---|
| 699 | # Unregister the classes.
|
|---|
| 700 |
|
|---|
| 701 | import GeoEco.COM
|
|---|
| 702 | for [moduleName, className] in classesForCOMRegistration:
|
|---|
| 703 | GeoEco.COM.UnregisterCOMServerUsingClassMetadata(moduleName, className)
|
|---|
| 704 |
|
|---|
| 705 | # Unregister the type library:
|
|---|
| 706 |
|
|---|
| 707 | try:
|
|---|
| 708 | pythoncom.UnRegisterTypeLib('""" + GeoEco.COM.TypeLibraryGUID + '\', ' + str(GeoEco.COM.TypeLibraryVersion[0]) + ', ' + str(GeoEco.COM.TypeLibraryVersion[1]) + ', ' + str(GeoEco.COM.TypeLibraryLCID) + """, pythoncom.SYS_WIN32)
|
|---|
| 709 | except:
|
|---|
| 710 | pass
|
|---|
| 711 |
|
|---|
| 712 | def CreateShortcuts():
|
|---|
| 713 |
|
|---|
| 714 | try:
|
|---|
| 715 |
|
|---|
| 716 | # Delete GeoEco's directory in the All Users' Programs menu, if it exists.
|
|---|
| 717 |
|
|---|
| 718 | programsDir = get_special_folder_path("CSIDL_COMMON_PROGRAMS")
|
|---|
| 719 | geoEcoProgramsDir = os.path.join(programsDir, 'Marine Geospatial Ecology Tools')
|
|---|
| 720 | if os.path.exists(geoEcoProgramsDir):
|
|---|
| 721 | shutil.rmtree(geoEcoProgramsDir)
|
|---|
| 722 |
|
|---|
| 723 | # Create GeoEco's directory in the All Users' Programs menu.
|
|---|
| 724 |
|
|---|
| 725 | os.mkdir(geoEcoProgramsDir)
|
|---|
| 726 | directory_created(geoEcoProgramsDir)
|
|---|
| 727 |
|
|---|
| 728 | # Create shortcuts for the documentation.
|
|---|
| 729 |
|
|---|
| 730 | docDir = os.path.join(geoEcoProgramsDir, 'Documentation')
|
|---|
| 731 | os.mkdir(docDir)
|
|---|
| 732 | directory_created(docDir)
|
|---|
| 733 | create_shortcut(os.path.join(geoEcoRoot, 'Documentation', 'GettingStarted.html'), 'Getting Started With Marine Geospatial Ecology Tools', os.path.join(geoEcoProgramsDir, 'Documentation', 'Getting Started.lnk'))
|
|---|
| 734 | file_created(os.path.join(geoEcoProgramsDir, 'Documentation', 'Getting Started.lnk'))
|
|---|
| 735 | create_shortcut(os.path.join(geoEcoRoot, 'Documentation', 'ArcGISReference', 'ArcGISReference.html'), 'Marine Geospatial Ecology Tools ArcGIS Geoprocessing Reference', os.path.join(geoEcoProgramsDir, 'Documentation', 'ArcGIS Geoprocessing Reference.lnk'))
|
|---|
| 736 | file_created(os.path.join(geoEcoProgramsDir, 'Documentation', 'ArcGIS Geoprocessing Reference.lnk'))
|
|---|
| 737 | create_shortcut(os.path.join(geoEcoRoot, 'Documentation', 'PythonReference', 'PythonReference.html'), 'GeoEco Python Reference', os.path.join(geoEcoProgramsDir, 'Documentation', 'GeoEco Python Reference.lnk'))
|
|---|
| 738 | file_created(os.path.join(geoEcoProgramsDir, 'Documentation', 'GeoEco Python Reference.lnk'))
|
|---|
| 739 | create_shortcut(os.path.join(geoEcoRoot, 'LICENSE.txt'), 'Marine Geospatial Ecology Tools Software License', os.path.join(geoEcoProgramsDir, 'Documentation', 'Software License.lnk'))
|
|---|
| 740 | file_created(os.path.join(geoEcoProgramsDir, 'Documentation', 'Software License.lnk'))
|
|---|
| 741 |
|
|---|
| 742 | except Exception, e:
|
|---|
| 743 | print('WARNING: Failed to create shortcuts in the Windows "Programs" menu.')
|
|---|
| 744 | return False
|
|---|
| 745 |
|
|---|
| 746 | return True
|
|---|
| 747 |
|
|---|
| 748 |
|
|---|
| 749 | ###############################################################################
|
|---|
| 750 | # MAIN SCRIPT
|
|---|
| 751 | ###############################################################################
|
|---|
| 752 |
|
|---|
| 753 | # If argv[1] is '-install' run the installation script.
|
|---|
| 754 |
|
|---|
| 755 | if len(sys.argv) >= 2 and sys.argv[1].lower() == '-install':
|
|---|
| 756 | shortcutsCreated = CreateShortcuts()
|
|---|
| 757 | arcGISToolboxInstalled = RegisterToolboxWithArcCatalog('register')
|
|---|
| 758 | allCOMClassesRegistered = RegisterClassesAsCOMServers()
|
|---|
| 759 |
|
|---|
| 760 | if shortcutsCreated and arcGISToolboxInstalled and allCOMClassesRegistered:
|
|---|
| 761 | print('All installation tasks completed successfully.')
|
|---|
| 762 | else:
|
|---|
| 763 | print('')
|
|---|
| 764 | print('Some installation tasks failed. Please review the error messages above.')
|
|---|
| 765 |
|
|---|
| 766 | # If argv[1] is '-remove' run the uninstallation script. Hide any errors because
|
|---|
| 767 | # they are not critical.
|
|---|
| 768 |
|
|---|
| 769 | elif len(sys.argv) >= 2 and sys.argv[1].lower() == '-remove':
|
|---|
| 770 | RegisterToolboxWithArcCatalog('unregister')
|
|---|
| 771 | UnregisterClassesAsCOMServers()
|
|---|
| 772 |
|
|---|
| 773 | # If argv[1] is absent or unrecognized, report an error.
|
|---|
| 774 |
|
|---|
| 775 | else:
|
|---|
| 776 | print('ERROR: Invalid command line arguments.')
|
|---|
| 777 | print('')
|
|---|
| 778 | print('USAGE: GeoEcoPostInstall.py {-install|-remove}')
|
|---|
| 779 | """)
|
|---|
| 780 | f.close()
|
|---|
| 781 | scripts.append(postInstallFilePath)
|
|---|
| 782 |
|
|---|
| 783 | # Generate the documentation.
|
|---|
| 784 |
|
|---|
| 785 | print(u'')
|
|---|
| 786 | print(u'********************************************************************************')
|
|---|
| 787 | print(u'* Generating documentation')
|
|---|
| 788 | print(u'********************************************************************************')
|
|---|
| 789 | print(u'')
|
|---|
| 790 |
|
|---|
| 791 | docFiles = [u'LICENSE.txt', os.path.join(u'Documentation', u'GettingStarted.html'), os.path.join(u'Documentation', u'*.css')]
|
|---|
| 792 |
|
|---|
| 793 | # Generate the Python reference documentation.
|
|---|
| 794 |
|
|---|
| 795 | print('Generating Python reference documentation...')
|
|---|
| 796 | RunXslTransform(setupDir, os.path.join(srcDir, u'Metadata.xml'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'PythonReference', u'PythonReference.xsl'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'PythonReference', u'PythonReference.html'))
|
|---|
| 797 | docFiles.append(os.path.join(u'Documentation', u'PythonReference', u'*.css'))
|
|---|
| 798 | docFiles.append(os.path.join(u'Documentation', u'PythonReference', u'*.png'))
|
|---|
| 799 | docFiles.append(os.path.join(u'Documentation', u'PythonReference', u'*.html'))
|
|---|
| 800 |
|
|---|
| 801 | # If running on Windows, generate ArcGIS reference documentation.
|
|---|
| 802 |
|
|---|
| 803 | if sys.platform.lower() == u'win32':
|
|---|
| 804 | print('Generating ArcGIS reference documentation...')
|
|---|
| 805 |
|
|---|
| 806 | # Ensure that we include the other files that are referenced by the HTML
|
|---|
| 807 | # files that make up the ArcGIS documentation.
|
|---|
| 808 |
|
|---|
| 809 | docFiles.append(os.path.join(u'Documentation', u'ArcGISReference', u'*.css'))
|
|---|
| 810 | docFiles.append(os.path.join(u'Documentation', u'ArcGISReference', u'*.gif'))
|
|---|
| 811 |
|
|---|
| 812 | # Generate ArcGISReference.html.
|
|---|
| 813 |
|
|---|
| 814 | RunXslTransform(setupDir, os.path.join(srcDir, u'Metadata.xml'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', u'ArcGISReference.xsl'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', u'ArcGISReference.html'))
|
|---|
| 815 | docFiles.append(os.path.join(u'Documentation', u'ArcGISReference', u'ArcGISReference.html'))
|
|---|
| 816 |
|
|---|
| 817 | # Get the path to the ArcGIS installation directory on this machine (the
|
|---|
| 818 | # build machine), so we can locate an XSL stylesheet that comes with
|
|---|
| 819 | # ArcGIS. We will use a modified version of this stylesheet to transform
|
|---|
| 820 | # the XML metadata for each tool into HTML.
|
|---|
| 821 |
|
|---|
| 822 | import win32api, win32con
|
|---|
| 823 | hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0')
|
|---|
| 824 | (arcGISInstallDir, installDirType) = win32api.RegQueryValueEx(hkey, 'InstallDir')
|
|---|
| 825 | if not isinstance(arcGISInstallDir, basestring) or not os.path.isdir(arcGISInstallDir):
|
|---|
| 826 | raise ValueError('The directory specified by the InstallDir value of the HKEY_LOCAL_MACHINE\\SOFTWARE\\ESRI\\ArcInfo\\Desktop\\8.0 registry key does not exist.')
|
|---|
| 827 |
|
|---|
| 828 | # Make some necessary changes ot the ArcGIS stylesheet using our own
|
|---|
| 829 | # XSL transform.
|
|---|
| 830 |
|
|---|
| 831 | RunXslTransform(setupDir, os.path.join(arcGISInstallDir, u'ArcToolbox', u'Stylesheets', u'geoprocessing_help.xsl'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', u'Tweak_geoprocessing_help.xsl'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', u'GeoEco_geoprocessing_help.xsl'))
|
|---|
| 832 |
|
|---|
| 833 | # Generate the HTML file for each tool.
|
|---|
| 834 |
|
|---|
| 835 | for method in arcGISMethods:
|
|---|
| 836 |
|
|---|
| 837 | # Transform the ESRI metadata XML into HTML, using the XSL
|
|---|
| 838 | # stylesheet that comes with ArcGIS.
|
|---|
| 839 |
|
|---|
| 840 | RunXslTransform(setupDir, os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', method.Class.Name + u'.' + method.Name + u'.xml'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', u'GeoEco_geoprocessing_help.xsl'), os.path.join(srcDir, u'GeoEco', u'Documentation', u'ArcGISReference', method.Class.Name + u'.' + method.Name + u'.html'))
|
|---|
| 841 | docFiles.append(os.path.join(u'Documentation', u'ArcGISReference', method.Class.Name + u'.' + method.Name + u'.html'))
|
|---|
| 842 |
|
|---|
| 843 | packageData[u'GeoEco'].extend(docFiles)
|
|---|
| 844 |
|
|---|
| 845 | # Run distutils setup.
|
|---|
| 846 |
|
|---|
| 847 | print(u'')
|
|---|
| 848 | print(u'********************************************************************************')
|
|---|
| 849 | print(u'* Building/installing the python package')
|
|---|
| 850 | print(u'********************************************************************************')
|
|---|
| 851 | print(u'')
|
|---|
| 852 |
|
|---|
| 853 | distutils.core.setup(name='GeoEco',
|
|---|
| 854 | version=str(GeoEco.__version__),
|
|---|
| 855 | description='Geospatial Ecology Tools',
|
|---|
| 856 | author='Jason Roberts',
|
|---|
| 857 | author_email='jason.roberts@duke.edu',
|
|---|
| 858 | url='http://code.env.duke.edu/projects/mget',
|
|---|
| 859 | package_dir={u'': srcDir},
|
|---|
| 860 | packages=packages,
|
|---|
| 861 | package_data=packageData,
|
|---|
| 862 | scripts=scripts,
|
|---|
| 863 | ext_modules=extensionModules)
|
|---|
| 864 |
|
|---|
| 865 | # If running on Windows and Python 2.5, generate the Trac online
|
|---|
| 866 | # documentation.
|
|---|
| 867 |
|
|---|
| 868 | if sys.platform.lower() == u'win32' and sys.version_info[0] == 2 and sys.version_info[1] == 5:
|
|---|
| 869 |
|
|---|
| 870 | print(u'')
|
|---|
| 871 | print(u'********************************************************************************')
|
|---|
| 872 | print(u'* Generating Trac online documentation...')
|
|---|
| 873 | print(u'********************************************************************************')
|
|---|
| 874 | print(u'')
|
|---|
| 875 |
|
|---|
| 876 | # First run AdjustHtmlUrlsForTracWiki.xsl on all HTML files, so that the
|
|---|
| 877 | # URLs within the files will resolve properly.
|
|---|
| 878 |
|
|---|
| 879 | print(u'Running AdjustHtmlUrlsForTracWiki.xsl on HTML documentation files...')
|
|---|
| 880 |
|
|---|
| 881 | for df in docFiles:
|
|---|
| 882 | files = glob.glob(os.path.join(srcDir, u'GeoEco', df))
|
|---|
| 883 | for src in files:
|
|---|
| 884 | dest = src + '.tmp'
|
|---|
| 885 | if src.endswith('.html') or src.endswith('.htm'):
|
|---|
| 886 | RunXslTransform(setupDir, src, os.path.join(srcDir, u'GeoEco', u'Documentation', u'AdjustHtmlUrlsForTracWiki.xsl'), dest)
|
|---|
| 887 | else:
|
|---|
| 888 | shutil.copy2(src, dest)
|
|---|
| 889 |
|
|---|
| 890 | # Now move the updated files to the dist directory.
|
|---|
| 891 |
|
|---|
| 892 | print(u'Updating files in %s...' % os.path.join(setupDir, u'dist', 'TracOnlineDocumentation'))
|
|---|
| 893 |
|
|---|
| 894 | for df in docFiles:
|
|---|
| 895 | files = glob.glob(os.path.join(srcDir, u'GeoEco', df))
|
|---|
| 896 | for src in files:
|
|---|
| 897 | tmpSrc = src + '.tmp'
|
|---|
| 898 | dest = os.path.join(os.path.join(setupDir, u'dist', 'TracOnlineDocumentation'), src[len(os.path.join(srcDir, u'GeoEco')) + 1:])
|
|---|
| 899 | if not os.path.isdir(os.path.dirname(dest)):
|
|---|
| 900 | os.mkdir(os.path.dirname(dest))
|
|---|
| 901 | if os.path.isfile(dest):
|
|---|
| 902 | os.remove(dest)
|
|---|
| 903 | shutil.move(tmpSrc, dest)
|
|---|
| 904 |
|
|---|
| 905 | # Clean up temporary files.
|
|---|
| 906 |
|
|---|
| 907 | print(u'')
|
|---|
| 908 | print(u'********************************************************************************')
|
|---|
| 909 | print(u'* Cleaning up')
|
|---|
| 910 | print(u'********************************************************************************')
|
|---|
| 911 | print(u'')
|
|---|
| 912 |
|
|---|
| 913 | print(u'Removing directory %s...' % os.path.join(setupDir, u'build'))
|
|---|
| 914 | for root, dirs, files in os.walk(os.path.join(setupDir, u'build')):
|
|---|
| 915 | for d in dirs:
|
|---|
| 916 | os.chmod(os.path.join(root, d), stat.S_IWRITE)
|
|---|
| 917 | for f in files:
|
|---|
| 918 | os.chmod(os.path.join(root, f), stat.S_IWRITE)
|
|---|
| 919 | shutil.rmtree(os.path.join(setupDir, u'build'), False)
|
|---|
| 920 | print(u'')
|
|---|
| 921 | print(u'***** BUILD SUCCESSFUL *****')
|
|---|
| 922 |
|
|---|
| 923 | if __name__ == u'__main__':
|
|---|
| 924 | main()
|
|---|
| 925 |
|
|---|
| 926 | sys.exit(0)
|
|---|