1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
from __future__ import absolute_import
import logger
import os
from urllib import parse as urlparse_local
import config.base
import socket
import shutil
# Fix parsing for nonstandard schemes
urlparse_local.uses_netloc.extend(['bk', 'ssh', 'svn'])
class Retriever(logger.Logger):
def __init__(self, sourceControl, clArgs = None, argDB = None):
logger.Logger.__init__(self, clArgs, argDB)
self.sourceControl = sourceControl
self.gitsubmodules = []
self.gitprereq = 1
self.git_urls = []
self.hg_urls = []
self.dir_urls = []
self.link_urls = []
self.tarball_urls = []
self.stamp = None
self.ver = 'unknown'
return
def isGitURL(self, url):
parsed = urlparse_local.urlparse(url)
if (parsed[0] == 'git') or (parsed[0] == 'ssh' and parsed[2].endswith('.git')) or (parsed[0] == 'https' and parsed[2].endswith('.git')):
return True
elif os.path.isdir(url) and self.isDirectoryGitRepo(url):
return True
return False
def setupURLs(self,packagename,urls,gitsubmodules,gitprereq):
self.packagename = packagename
self.gitsubmodules = gitsubmodules
self.gitprereq = gitprereq
for url in urls:
parsed = urlparse_local.urlparse(url)
if self.isGitURL(url):
self.git_urls.append(self.removePrefix(url,'git://'))
elif parsed[0] == 'hg'or (parsed[0] == 'ssh' and parsed[1].startswith('hg@')):
self.hg_urls.append(self.removePrefix(url,'hg://'))
elif parsed[0] == 'dir' or os.path.isdir(url):
self.dir_urls.append(self.removePrefix(url,'dir://'))
elif parsed[0] == 'link':
self.link_urls.append(self.removePrefix(url,'link://'))
else:
self.tarball_urls.extend([url])
def isDirectoryGitRepo(self, directory):
if not hasattr(self.sourceControl, 'git'):
self.logPrint('git not found in self.sourceControl - cannot evaluate isDirectoryGitRepo(): '+directory)
return False
from config.base import Configure
for loc in ['.git','']:
cmd = '%s rev-parse --resolve-git-dir %s' % (self.sourceControl.git, os.path.join(directory,loc))
(output, error, ret) = Configure.executeShellCommand(cmd, checkCommand = Configure.passCheckCommand, log = self.log)
if not ret:
return True
return False
@staticmethod
def removeTarget(t):
if os.path.islink(t) or os.path.isfile(t):
os.unlink(t) # same as os.remove(t)
elif os.path.isdir(t):
shutil.rmtree(t)
@staticmethod
def getDownloadFailureMessage(package, url, filename=None):
slashFilename = '/'+filename if filename else ''
return '''\
Unable to download package %s from: %s
* If URL specified manually - perhaps there is a typo?
* If your network is disconnected - please reconnect and rerun ./configure
* Or perhaps you have a firewall blocking the download
* You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually
* or you can download the above URL manually, to /yourselectedlocation%s
and use the configure option:
--download-%s=/yourselectedlocation%s
''' % (package.upper(), url, slashFilename, package, slashFilename)
@staticmethod
def removePrefix(url,prefix):
'''Replacement for str.removeprefix() supported only since Python 3.9'''
if url.startswith(prefix):
return url[len(prefix):]
return url
def generateURLs(self):
if hasattr(self.sourceControl, 'git') and self.gitprereq:
for url in self.git_urls:
yield('git',url)
else:
self.logPrint('Git not found or gitprereq check failed! skipping giturls: '+str(self.git_urls)+'\n')
if hasattr(self.sourceControl, 'hg'):
for url in self.hg_urls:
yield('hg',url)
else:
self.logPrint('Hg not found - skipping hgurls: '+str(self.hg_urls)+'\n')
for url in self.dir_urls:
yield('dir',url)
for url in self.link_urls:
yield('link',url)
for url in self.tarball_urls:
yield('tarball',url)
def genericRetrieve(self,proto,url,root):
'''Fetch package from version control repository or tarfile indicated by URL and extract it into root'''
if proto == 'git':
return self.gitRetrieve(url,root)
elif proto == 'hg':
return self.hgRetrieve(url,root)
elif proto == 'dir':
return self.dirRetrieve(url,root)
elif proto == 'link':
self.linkRetrieve(url,root)
elif proto == 'tarball':
self.tarballRetrieve(url,root)
def dirRetrieve(self, url, root):
self.logPrint('Retrieving %s as directory' % url, 3, 'install')
if not os.path.isdir(url): raise RuntimeError('URL %s is not a directory' % url)
t = os.path.join(root,os.path.basename(url))
self.removeTarget(t)
shutil.copytree(url,t)
def linkRetrieve(self, url, root):
self.logPrint('Retrieving %s as link' % url, 3, 'install')
if not os.path.isdir(url): raise RuntimeError('URL %s is not pointing to a directory' % url)
t = os.path.join(root,os.path.basename(url))
self.removeTarget(t)
os.symlink(os.path.abspath(url),t)
def gitRetrieve(self, url, root):
self.logPrint('Retrieving %s as git repo' % url, 3, 'install')
if not hasattr(self.sourceControl, 'git'):
raise RuntimeError('self.sourceControl.git not set')
if os.path.isdir(url) and not self.isDirectoryGitRepo(url):
raise RuntimeError('URL %s is a directory but not a git repository' % url)
newgitrepo = os.path.join(root,'git.'+self.packagename)
self.removeTarget(newgitrepo)
try:
submodopt =''
for itm in self.gitsubmodules:
submodopt += ' --recurse-submodules='+itm
config.base.Configure.executeShellCommand('%s clone %s %s %s' % (self.sourceControl.git, submodopt, url, newgitrepo), log = self.log, timeout = 120.0)
except RuntimeError as e:
self.logPrint('ERROR: '+str(e))
failureMessage = self.getDownloadFailureMessage(self.packagename, url)
raise RuntimeError('Unable to clone '+self.packagename+'\n'+str(e)+'\n'+failureMessage)
def hgRetrieve(self, url, root):
self.logPrint('Retrieving %s as hg repo' % url, 3, 'install')
if not hasattr(self.sourceControl, 'hg'):
raise RuntimeError('self.sourceControl.hg not set')
newgitrepo = os.path.join(root,'hg.'+self.packagename)
self.removeTarget(newgitrepo)
try:
config.base.Configure.executeShellCommand('%s clone %s %s' % (self.sourceControl.hg, url, newgitrepo), log = self.log, timeout = 120.0)
except RuntimeError as e:
self.logPrint('ERROR: '+str(e))
failureMessage = self.getDownloadFailureMessage(self.packagename, url)
raise RuntimeError('Unable to clone '+self.packagename+'\n'+str(e)+'\n'+failureMessage)
def tarballRetrieve(self, url, root):
parsed = urlparse_local.urlparse(url)
filename = os.path.basename(parsed[2])
localFile = os.path.join(root,'_d_'+filename)
self.logPrint('Retrieving %s as tarball to %s' % (url,localFile) , 3, 'install')
ext = os.path.splitext(localFile)[1]
if ext not in ['.bz2','.tbz','.gz','.tgz','.zip','.ZIP']:
raise RuntimeError('Unknown compression type in URL: '+ url)
self.removeTarget(localFile)
if parsed[0] == 'file' and not parsed[1]:
url = parsed[2]
if os.path.exists(url):
if not os.path.isfile(url):
raise RuntimeError('Local path exists but is not a regular file: '+ url)
# copy local file
shutil.copyfile(url, localFile)
else:
# fetch remote file
try:
from urllib.request import Request, urlopen
sav_timeout = socket.getdefaulttimeout()
socket.setdefaulttimeout(30)
req = Request(url)
req.headers['User-Agent'] = 'PetscConfigure/'+self.ver
with open(localFile, 'wb') as f:
f.write(urlopen(req).read())
socket.setdefaulttimeout(sav_timeout)
except Exception as e:
socket.setdefaulttimeout(sav_timeout)
failureMessage = self.getDownloadFailureMessage(self.packagename, url, filename)
raise RuntimeError(str(e)+'\n'+failureMessage)
self.logPrint('Extracting '+localFile)
if ext in ['.zip','.ZIP']:
config.base.Configure.executeShellCommand('cd '+root+'; unzip '+localFile, log = self.log)
output = config.base.Configure.executeShellCommand('cd '+root+'; zipinfo -1 '+localFile+' | head -n 1', log = self.log)
dirname = os.path.normpath(output[0].strip())
else:
failureMessage = '''\
Downloaded package %s from: %s is not a tarball.
[or installed python cannot process compressed files]
* If you are behind a firewall - please fix your proxy and rerun ./configure
For example at LANL you may need to set the environmental variable http_proxy (or HTTP_PROXY?) to http://proxyout.lanl.gov
* You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually
* or you can download the above URL manually, to /yourselectedlocation/%s
and use the configure option:
--download-%s=/yourselectedlocation/%s
''' % (self.packagename.upper(), url, filename, self.packagename, filename)
import tarfile
try:
tf = tarfile.open(os.path.join(root, localFile))
except tarfile.ReadError as e:
raise RuntimeError(str(e)+'\n'+failureMessage)
if not tf: raise RuntimeError(failureMessage)
#git puts 'pax_global_header' as the first entry and some tar utils process this as a file
firstname = tf.getnames()[0]
if firstname == 'pax_global_header':
firstmember = tf.getmembers()[1]
else:
firstmember = tf.getmembers()[0]
# some tarfiles list packagename/ but some list packagename/filename in the first entry
if firstmember.isdir():
dirname = firstmember.name
else:
dirname = os.path.dirname(firstmember.name)
tf.extractall(root)
tf.close()
# fix file permissions for the untared tarballs.
try:
# check if 'dirname' is set'
if dirname:
config.base.Configure.executeShellCommand('cd '+root+'; chmod -R a+r '+dirname+';find '+dirname + r' -type d -name "*" -exec chmod a+rx {} \;', log = self.log)
else:
self.logPrintBox('WARNING: Could not determine dirname extracted by '+localFile+' to fix file permissions')
except RuntimeError as e:
raise RuntimeError('Error changing permissions for '+dirname+' obtained from '+localFile+ ' : '+str(e))
os.unlink(localFile)
|