File: retrieval.py

package info (click to toggle)
petsc 3.22.5%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 516,740 kB
  • sloc: ansic: 814,333; cpp: 50,948; python: 37,416; f90: 17,187; javascript: 3,493; makefile: 3,198; sh: 1,502; xml: 619; objc: 445; java: 13; csh: 1
file content (253 lines) | stat: -rw-r--r-- 10,494 bytes parent folder | download | duplicates (2)
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)