Module EDUtilsLibraryInstaller
[hide private]
[frames] | no frames]

Source Code for Module EDUtilsLibraryInstaller

  1  #!/usr/bin/env python 
  2  # -*- coding: utf8 -*- 
  3  # 
  4  #    Project: PROJECT 
  5  #             http://www.edna-site.org 
  6  # 
  7  #    File: "$Id$" 
  8  # 
  9  #    Copyright (C) European Synchrotron Radiation Facility, Grenoble, France 
 10  # 
 11  #    Principal author:       Jerome Kieffer (Jerome.Kieffer@ESRF.eu) 
 12  # 
 13  #    This program is free software: you can redistribute it and/or modify 
 14  #    it under the terms of the GNU General Public License as published by 
 15  #    the Free Software Foundation, either version 3 of the License, or 
 16  #    (at your option) any later version. 
 17  # 
 18  #    This program is distributed in the hope that it will be useful, 
 19  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 20  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 21  #    GNU General Public License for more details. 
 22  # 
 23  #    You should have received a copy of the GNU General Public License 
 24  #    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 25  # 
 26  # 
 27  # 
 28   
 29  """EDNA external libraries builder and installer, useful for PIL, numpy, scipy, Fabio, ... """ 
 30   
 31  __contact__ = "Jerome.Kieffer@ESRF.eu" 
 32  __author__ = "Jerome Kieffer" 
 33  __license__ = "GPLv3+" 
 34  __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" 
 35   
 36  import os, shutil, sys, zipfile, tarfile, urllib2, threading 
 37  from   EDVerbose        import  EDVerbose 
 38  from EDUtilsPlatform    import EDUtilsPlatform 
 39  from EDUtilsPath        import EDUtilsPath 
 40   
 41  for strOneArg in sys.argv: 
 42      if strOneArg.lower() in ["-d", "--debug"]: 
 43          EDVerbose.setVerboseDebugOn() 
44 45 -class EDUtilsLibraryInstaller:
46 """ 47 This class helps to install to install an external library within EDNA 48 """ 49 iMAX_DOWNLOAD_TIME = 60 #in seconds
50 - def __init__(self, _strLibraryDirectory, _strArchiveName=None, _strSourceDir=None):
51 """ 52 Constructor of the class EDUtilsLibraryInstaller, 53 54 @param _strLibraryDirectory: the name of the directory where the library is, like "20090711-SciPy-0.7.1" 55 @type _strLibraryDirectory: python string 56 @param _strArchiveName: the name of the archive file. if None, the system will try to guess it, searching in _strLibraryDirectory for a tar.gz, tar.bz2 or a .zip 57 @type _strArchiveName: python string 58 @param _strSourceDir: the name of the directory where the setup.py file is, this could be guessed as well is None. 59 @type _strSourceDir: python string 60 """ 61 self.__strLibraryDirectory = _strLibraryDirectory 62 self.__strArchiveName = _strArchiveName 63 self.__strSourceDir = _strSourceDir 64 self.__strDestinationDirectory = None 65 if os.environ.has_key("EDNA_HOME"): 66 self.__libraryPath = os.path.join(EDUtilsPath.EDNA_HOME, "libraries", self.__strLibraryDirectory) 67 else: 68 self.__libraryPath = os.path.dirname(os.path.abspath(sys.argv[0]))
69 70 @staticmethod
71 - def checkPythonVersion():
72 """ 73 Checks that we are using at least python2.5 as zip and tarfiles modules are required to uncompress archive 74 even if those modules exists since python2.3 their API has changed in python2.5. 75 """ 76 if sys.version_info < (2, 5): 77 raise ImportError, "Version of python too old, please use a python 2.5 or newer.\nCurrently you are using Python " + sys.version
78 79
80 - def dependency(self, _strLibraryName, _strLibraryDirectory, _tupleVersion=None, _strMethodToGetVersion=None):
81 """Tries to resolve a dependency on an external library like numpy 82 83 @param _strLibraryName: name of the library, "numpy" for example 84 @type _strLibraryName: Python string 85 @param _strLibraryDirectory: the name of the directory where the dependency library is, like "20090405-Numpy-1.3" 86 @type _strLibraryDirectory: Python string 87 @param _tupleVersion: the minimum version of the library to be installed. 88 @type: _tuplVersion: tuple of integer 89 @param _strMethodToGetVersion: for numpy it would be "version.version" 90 @type _strMethodToGetVersion: string 91 """ 92 dictModulesBeforeImport = sys.modules.copy() 93 strDepLibPath = os.path.join(os.path.dirname(self.__libraryPath), _strLibraryDirectory, EDUtilsPlatform.architecture) 94 EDVerbose.DEBUG("LibInstaller.dependency: strDepLibPath=%s" % strDepLibPath) 95 try: 96 mylib = __import__(_strLibraryName) 97 except Exception: 98 if os.path.isdir(strDepLibPath)and (strDepLibPath not in sys.path): 99 sys.path.insert(1, strDepLibPath) 100 if os.environ.has_key("PYTHONPATH"): 101 os.environ["PYTHONPATH"] += os.pathsep + strDepLibPath 102 else: 103 os.environ["PYTHONPATH"] = strDepLibPath 104 105 else: 106 installLibrary(strDepLibPath) 107 mylib = __import__(_strLibraryName) 108 109 tupleVersionObt = None 110 if _strMethodToGetVersion is not None and _tupleVersion is not None: 111 # mylib = __import__(_strLibraryName) 112 try: 113 versionObt = eval("mylib.%s" % (_strMethodToGetVersion)) 114 except Exception: 115 EDVerbose.WARNING("Unable to execute version %s.%s" % (_strLibraryName, _strMethodToGetVersion)) 116 versionObt = None 117 if isinstance(versionObt, (unicode, str)): 118 try: 119 tupleVersionObt = tuple(map(int, versionObt.split("."))) 120 except Exception: 121 EDVerbose.WARNING("Unable to understand Version %s" % versionObt) 122 versionObt = None 123 elif isinstance(versionObt, tuple): 124 tupleVersionObt = versionObt 125 else: 126 del dictModulesBeforeImport 127 # EDVerbose.screen("Version of Numpy Library found: %s" % numpy.version.version) 128 return 129 if tupleVersionObt is not None: 130 if tupleVersionObt < _tupleVersion: 131 EDVerbose.WARNING("Wrong library version: %s < %s" % (tupleVersionObt, _tupleVersion)) 132 for oneModule in sys.modules.copy(): 133 if oneModule not in dictModulesBeforeImport: 134 del sys.modules[ oneModule ] 135 if os.path.isdir(strDepLibPath):# and (strDepLibPath not in sys.path): 136 sys.path.insert(1, strDepLibPath) 137 if "PYTHONPATH" in os.environ: 138 os.environ["PYTHONPATH"] = strDepLibPath + os.pathsep + os.environ["PYTHONPATH"] 139 else: 140 os.environ["PYTHONPATH"] = strDepLibPath 141 142 else: 143 installLibrary(strDepLibPath) 144 __import__(_strLibraryName) 145 146 del dictModulesBeforeImport
147 148 149 150 @staticmethod
151 - def searchCLib(_strLibName):
152 """ 153 Search in the LD_PRELOAD, LD_LIBRARY_PATH and subsequently in /etc/ld.so.conf & /etc/ld.so.conf.d/* for a library named 154 155 @param _strLibName: name of the file or library to look for 156 @type _strLibName: python string 157 @return: the name of the path where the library is (or None if it was not found 158 @rtype: string or None 159 """ 160 strLibPath = None 161 listLib = [] 162 if "LD_PRELOAD" in os.environ: 163 for oneLib in os.environ["LD_PRELOAD"].split(":"): 164 if oneLib not in listLib and os.path.isdir(oneLib): 165 listLib.append(oneLib) 166 if "LD_LIBRARY_PATH" in os.environ: 167 for oneLib in os.environ["LD_LIBRARY_PATH"].split(":"): 168 if oneLib not in listLib and os.path.isdir(oneLib): 169 listLib.append(oneLib) 170 if os.path.isdir("/etc/ld.so.conf"): 171 listLdSoConf = open("/etc/ld.so.conf").readlines() 172 listLdSoConf.reverse() 173 for oneLib in listLdSoConf: 174 oneLibS = oneLib.strip() 175 if oneLibS.startswith("include"): 176 includeDir = os.path.dirname(oneLibS.split()[1]) 177 for strLdFile in os.listdir(includeDir): 178 confFile = os.path.join(includeDir, strLdFile) 179 for subdir in open(confFile).readlines(): 180 subdirS = subdir.strip() 181 if subdirS not in listLib and os.path.isdir(oneLibS): 182 listLib.append(subdirS) 183 elif oneLibS not in listLib and os.path.isdir(oneLibS): 184 listLib.append(oneLibS) 185 for oneLib in ["/usr/local/lib", "/usr/lib", "/lib"]: 186 if os.path.isdir(oneLib) and oneLib not in listLib: 187 listLib.append(oneLib) 188 189 190 for oneLib in listLib: 191 for oneFile in os.listdir(oneLib): 192 if oneFile.startswith(_strLibName): 193 strLibPath = oneLib 194 return strLibPath 195 return strLibPath
196 197
198 - def getArchiveName(self):
199 """ 200 Tries to guess the name of the archive from it's extension 201 202 @return: Name of the archive 203 @rtype: python string 204 """ 205 if self.__strArchiveName == None: 206 for oneFile in os.listdir(self.__libraryPath): 207 if os.path.isfile(oneFile) and \ 208 os.path.splitext(oneFile)[1].lower() in[".gz", ".bz2", ".zip", ".tgz", ".tbz", ".tbz2"]: 209 self.__strArchiveName = oneFile 210 return self.__strArchiveName
211
212 - def unZipArchive(self):
213 """ 214 Uncompress the archived installer by using tar or zip 215 """ 216 if self.__strArchiveName == None: 217 self.getArchiveName() 218 cwd = os.getcwd() 219 EDVerbose.DEBUG("Unzipping archive %s in directory %s." % (self.__strArchiveName, self.__libraryPath)) 220 221 os.chdir(self.__libraryPath) 222 strArchiveNameLower = self.__strArchiveName.lower() 223 if strArchiveNameLower.endswith(".zip"): 224 zipped = zipfile.ZipFile(self.__strArchiveName) 225 if self.__strSourceDir == None: 226 self.__strSourceDir = os.path.dirname(zipped.filelist[0].filename) 227 if self.__strSourceDir == "": 228 self.__strSourceDir = zipped.filelist[0].filename 229 zipped.extractall() 230 zipped.close() 231 elif strArchiveNameLower.endswith(".tgz") or strArchiveNameLower.endswith(".tar.gz") \ 232 or strArchiveNameLower.endswith(".tbz") or strArchiveNameLower.endswith(".tar.bz2") : 233 tar = tarfile.open(self.__strArchiveName) 234 if self.__strSourceDir == None: 235 self.__strSourceDir = os.path.dirname(tar.getmembers()[0].name) 236 if self.__strSourceDir == "": 237 self.__strSourceDir = tar.getmembers()[0].name 238 tar.extractall() 239 tar.close() 240 os.chdir(cwd)
241 242
243 - def cleanSources(self):
244 """ 245 Remove the source tree and clean up the installation directory to save some place 246 """ 247 for root, dirs, files in os.walk(os.path.join(self.__libraryPath, self.__strSourceDir), topdown=False): 248 for name in files: 249 os.remove(os.path.join(root, name)) 250 for name in dirs: 251 os.rmdir(os.path.join(root, name)) 252 os.rmdir(os.path.join(self.__libraryPath, self.__strSourceDir))
253 254
255 - def buildSources(self, _strOptions=""):
256 """ 257 Runs the setup.py to install the program within EDNA 258 259 @param _strOptions: options to be passed to setup.py (beside build) 260 @type _strOptions: python string 261 """ 262 cwd = os.getcwd() 263 os.chdir(os.path.join(self.__libraryPath, self.__strSourceDir)) 264 sys.path = [sys.path[0], os.path.join(self.__libraryPath, self.__strSourceDir)] + sys.path[1:] 265 EDVerbose.DEBUG("%s$ python setup.py build %s " % (os.getcwd(), _strOptions)) 266 os.system("%s setup.py build %s 2>&1" % (sys.executable, _strOptions)) 267 os.chdir(cwd)
268 269
270 - def installGeneric(self, _strPrefix, _strStartSubDir=None):
271 """ 272 Install/Move the source/build/prefix to the library directory 273 very specific to EDNA 274 @param _strPrefix: prefix of the path like "build/lib.linux-x86_64-2.5" 275 @type _strPrefix: string 276 @param _strStartSubDir: prefix of the path like "numpy/core/include/numpy" 277 @type _strStartSubDir: string 278 """ 279 if _strStartSubDir is None: 280 if _strPrefix is None: 281 build = os.path.join(self.__libraryPath, _strPrefix) 282 else: 283 build = os.path.join(self.__libraryPath, self.__strSourceDir, _strPrefix) 284 dest = self.getDestinationDirectory() 285 else: 286 if _strPrefix is None: 287 build = os.path.join(self.__libraryPath, self.__strSourceDir, _strStartSubDir) 288 else: 289 build = os.path.join(self.__libraryPath, self.__strSourceDir, _strPrefix, _strStartSubDir) 290 dest = os.path.join(self.getDestinationDirectory(), _strStartSubDir) 291 EDVerbose.DEBUG("Installing python library from %s to %s" % (build, dest)) 292 if os.path.isdir(build): 293 if os.path.isdir(dest): 294 EDVerbose.DEBUG("walking in %s" % build) 295 for dirpath, dirnames, filenames in os.walk(build): 296 EDVerbose.DEBUG("%s, %s, %s" % (dirpath, dirnames, filenames)) 297 shortdir = dirpath[len(build) + 1:] 298 for oneDir in dirnames: 299 if not os.path.isdir(os.path.join(dest, shortdir, oneDir)): 300 os.makedirs(os.path.join(dest, shortdir, oneDir)) 301 for oneFile in filenames: 302 target = os.path.join(shortdir, oneFile) 303 # EDVerbose.DEBUG("target %s" % target) 304 if not os.path.isfile(os.path.join(dest, target)): 305 shutil.move(os.path.join(build, target), os.path.join(dest, target)) 306 else: 307 EDVerbose.DEBUG("mv %s to %s" % (build, dest)) 308 shutil.move(build, dest) 309 else: 310 EDVerbose.ERROR("Error in installing the library: No %s" % build)
311 312
313 - def installBuilt(self):
314 """ 315 Install/Move the source/build/arch to the library directory 316 very specific to EDNA 317 """ 318 if os.path.isdir(os.path.join(self.__libraryPath, self.__strSourceDir, "build", EDUtilsPlatform.systemArchitecture)): 319 self.installGeneric(os.path.join("build", EDUtilsPlatform.systemArchitecture)) 320 elif os.path.isdir(os.path.join(self.__libraryPath, self.__strSourceDir, "build", "lib")): 321 self.installGeneric(os.path.join("build", "lib"))
322 323
324 - def installSources(self):
325 """ 326 Install/Move the source/build/src.arch to the library directory 327 very specific to EDNA 328 """ 329 self.installGeneric(os.path.join("build", "src" + EDUtilsPlatform.systemArchitecture[3:]))
330 331
332 - def configure(self, _strOptions=""):
333 """ 334 Run the configure program to configure the set-up 335 336 @param _strOptions: options to be passed to configure 337 @type _strOptions: python string 338 """ 339 cwd = os.getcwd() 340 EDVerbose.DEBUG("dir=" + os.path.join(self.__libraryPath, self.__strSourceDir)) 341 os.chdir(os.path.join(self.__libraryPath, self.__strSourceDir)) 342 EDVerbose.DEBUG("%s$ ./configure %s" % (os.getcwd(), _strOptions)) 343 strOutput = os.popen("./configure %s 2>&1" % _strOptions).read() 344 EDVerbose.DEBUG(strOutput) 345 os.chdir(cwd)
346 347
348 - def make(self, _strOptions=""):
349 """ 350 Run the make program configure program to compile the library 351 352 @param _strOptions: options to be passed to make program 353 @type _strOptions: python string 354 """ 355 cwd = os.getcwd() 356 EDVerbose.DEBUG("dir=" + os.path.join(self.__libraryPath, self.__strSourceDir)) 357 os.chdir(os.path.join(self.__libraryPath, self.__strSourceDir)) 358 EDVerbose.DEBUG("%s$ make %s" % (os.getcwd(), _strOptions)) 359 strOutput = os.popen("make %s 2>&1" % (_strOptions)).read() 360 EDVerbose.DEBUG(strOutput) 361 os.chdir(cwd)
362 363
364 - def downloadLibrary(self, _strServer="http://www.edna-site.org/pub/libraries"):
365 """ 366 Download the given library from edna-site by default or another server if provided. 367 368 @param _strServer: optionally, the name of the server 369 @type _strServer: python string 370 """ 371 if not os.path.exists(self.__strArchiveName): 372 EDVerbose.screen("Trying to download library %s, timeout set to %d s" % (self.__strArchiveName, EDUtilsLibraryInstaller.iMAX_DOWNLOAD_TIME)) 373 if os.environ.has_key("http_proxy"): 374 dictProxies = {'http': os.environ["http_proxy"]} 375 proxy_handler = urllib2.ProxyHandler(dictProxies) 376 opener = urllib2.build_opener(proxy_handler).open 377 else: 378 opener = urllib2.urlopen 379 strURL = "/".join((_strServer, self.__strArchiveName)) 380 # Nota: since python2.6 there is a timeout in the urllib2 381 if sys.version > (2, 6): 382 data = opener(strURL, data=None, timeout=EDUtilsLibraryInstaller.iMAX_DOWNLOAD_TIME).read() 383 else: #python 2.5, we use a different thread 384 timer = threading.Timer(EDUtilsLibraryInstaller.iMAX_DOWNLOAD_TIME + 1, timeoutDuringDownload) 385 timer.start() 386 data = opener(strURL, data=None).read() 387 timer.cancel() 388 389 try: 390 open(self.__strArchiveName, "wb").write(data) 391 except IOError: 392 raise IOError, "unable to write downloaded data to disk at " + self.__strArchiveName 393 394 if os.path.exists(self.__strArchiveName): 395 EDVerbose.screen("Library %s successfully downloaded." % self.__strArchiveName) 396 else: 397 raise RuntimeError, "Could not automatically download libraries %r! \n \ 398 If you are behind a firewall, please set the environment variable http_proxy. \n \ 399 Otherwise please try to download the images manually from \n \ 400 http://www.edna-site.org/pub/libraries" % self.__strArchiveName
401 402
403 - def getDestinationDirectory(self):
404 """ 405 Getter for the distinationDirectory: 406 407 @return: /$EDNA_HOME/libraries/libName/lib.linux-x86_64-2.x 408 @rtype: string 409 """ 410 if self.__strDestinationDirectory is None: 411 self.__strDestinationDirectory = os.path.join(self.__libraryPath, EDUtilsPlatform.architecture) 412 EDVerbose.DEBUG("Setting DestinationDirectory to %s" % self.__strDestinationDirectory) 413 if not os.path.isdir(self.__strDestinationDirectory): 414 os.makedirs(self.__strDestinationDirectory) 415 return self.__strDestinationDirectory
416 417
418 - def getSourceDirectory(self):
419 """ 420 Getter for the source directory: 421 422 @return: /$EDNA_HOME/libraries/libName/myLib/ 423 @rtype: string 424 """ 425 return self.__strSourceDir
426 427
428 - def getLibraryDirectory(self):
429 """ 430 Getter for the Libary Path: 431 432 @return: .... 433 @rtype: string 434 """ 435 return self.__strLibraryDirectory
436 437 438 @staticmethod
439 - def getArchitecture():
440 """ 441 here only fro compatibility reason ... please use EDUtilsPlatform.architecture 442 443 @return lib.linux-i386-2.6 444 @rtype: string 445 """ 446 return EDUtilsPlatform.architecture
447
448 449 -def timeoutDuringDownload():
450 """ 451 Function called after a timeout in the download part ... just raise an Exception. 452 """ 453 raise RuntimeError("Could not automatically download library ! \n \ 454 If you are behind a firewall, please set the environment variable http_proxy. \n \ 455 Otherwise please try to download the images manually from \n \ 456 http://www.edna-site.org/pub/libraries")
457
458 459 -def installLibrary(_strPath):
460 """ 461 Runs the EDNA library installer in the given directory (no dependencies inside the kernel) 462 @param _strPath: full path of the diresctory where the install script is 463 @type _strPath: string 464 """ 465 EDVerbose.DEBUG("Building H5Py %s" % _strPath) 466 cwd = os.getcwd() 467 if _strPath in sys.path: sys.path.remove(_strPath) 468 os.chdir(os.path.dirname(_strPath)) 469 os.system("%s install.py" % sys.executable) 470 os.chdir(cwd) 471 sys.path.insert(1, _strPath)
472