15b6bfdb9SJed Brownfrom __future__ import absolute_import 2179860b2SJed Brownimport logger 3179860b2SJed Brown 4179860b2SJed Brownimport os 5e7c47bf1SJed Browntry: 6e7c47bf1SJed Brown from urllib import urlretrieve 7e7c47bf1SJed Brownexcept ImportError: 8e7c47bf1SJed Brown from urllib.request import urlretrieve 9e7c47bf1SJed Browntry: 108f450857SSatish Balay import urlparse as urlparse_local # novermin 119ad79eecSSatish Balayexcept ImportError: 128f450857SSatish Balay from urllib import parse as urlparse_local 13179860b2SJed Brownimport config.base 14728600e6SSatish Balayimport socket 15*fbfe4939SVaclav Haplaimport shutil 16728600e6SSatish Balay 17179860b2SJed Brown# Fix parsing for nonstandard schemes 188f450857SSatish Balayurlparse_local.uses_netloc.extend(['bk', 'ssh', 'svn']) 19179860b2SJed Brown 20179860b2SJed Brownclass Retriever(logger.Logger): 21179860b2SJed Brown def __init__(self, sourceControl, clArgs = None, argDB = None): 22179860b2SJed Brown logger.Logger.__init__(self, clArgs, argDB) 23179860b2SJed Brown self.sourceControl = sourceControl 24179860b2SJed Brown self.stamp = None 25179860b2SJed Brown return 26179860b2SJed Brown 27*fbfe4939SVaclav Hapla def isDirectoryGitRepo(self, directory): 28*fbfe4939SVaclav Hapla from config.base import Configure 29*fbfe4939SVaclav Hapla for loc in ['.git','']: 30*fbfe4939SVaclav Hapla cmd = '%s rev-parse --resolve-git-dir %s' % (self.sourceControl.git, os.path.join(directory,loc)) 31*fbfe4939SVaclav Hapla (output, error, ret) = Configure.executeShellCommand(cmd, checkCommand = Configure.passCheckCommand, log = self.log) 32*fbfe4939SVaclav Hapla if not ret: 33*fbfe4939SVaclav Hapla return True 34*fbfe4939SVaclav Hapla return False 35*fbfe4939SVaclav Hapla 36*fbfe4939SVaclav Hapla @staticmethod 37*fbfe4939SVaclav Hapla def removeTarget(t): 38*fbfe4939SVaclav Hapla if os.path.islink(t) or os.path.isfile(t): 39*fbfe4939SVaclav Hapla os.unlink(t) # same as os.remove(t) 40*fbfe4939SVaclav Hapla elif os.path.isdir(t): 41*fbfe4939SVaclav Hapla shutil.rmtree(t) 42*fbfe4939SVaclav Hapla 43*fbfe4939SVaclav Hapla @staticmethod 44*fbfe4939SVaclav Hapla def getDownloadFailureMessage(package, url, filename=None): 45*fbfe4939SVaclav Hapla slashFilename = '/'+filename if filename else '' 46*fbfe4939SVaclav Hapla return '''\ 47*fbfe4939SVaclav HaplaUnable to download package %s from: %s 48*fbfe4939SVaclav Hapla* If URL specified manually - perhaps there is a typo? 49*fbfe4939SVaclav Hapla* If your network is disconnected - please reconnect and rerun ./configure 50*fbfe4939SVaclav Hapla* Or perhaps you have a firewall blocking the download 51*fbfe4939SVaclav Hapla* You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually 52*fbfe4939SVaclav Hapla* or you can download the above URL manually, to /yourselectedlocation%s 53*fbfe4939SVaclav Hapla and use the configure option: 54*fbfe4939SVaclav Hapla --download-%s=/yourselectedlocation%s 55*fbfe4939SVaclav Hapla ''' % (package.upper(), url, slashFilename, package, slashFilename) 56*fbfe4939SVaclav Hapla 57*fbfe4939SVaclav Hapla @staticmethod 58*fbfe4939SVaclav Hapla def removePrefix(url,prefix): 59*fbfe4939SVaclav Hapla '''Replacement for str.removeprefix() supported only since Python 3.9''' 60*fbfe4939SVaclav Hapla if url.startswith(prefix): 61*fbfe4939SVaclav Hapla return url[len(prefix):] 62*fbfe4939SVaclav Hapla return url 63*fbfe4939SVaclav Hapla 641aefc9f4SSatish Balay def genericRetrieve(self, url, root, package): 65*fbfe4939SVaclav Hapla '''Fetch package from version control repository or tarfile indicated by URL and extract it into root''' 66179860b2SJed Brown 67*fbfe4939SVaclav Hapla parsed = urlparse_local.urlparse(url) 68*fbfe4939SVaclav Hapla if parsed[0] == 'dir': 69*fbfe4939SVaclav Hapla f = self.dirRetrieve 70*fbfe4939SVaclav Hapla elif parsed[0] == 'link': 71*fbfe4939SVaclav Hapla f = self.linkRetrieve 72*fbfe4939SVaclav Hapla elif parsed[0] == 'git': 73*fbfe4939SVaclav Hapla f = self.gitRetrieve 74*fbfe4939SVaclav Hapla elif parsed[0] == 'ssh' and parsed[2].endswith('.git'): 75*fbfe4939SVaclav Hapla f = self.gitRetrieve 76*fbfe4939SVaclav Hapla elif parsed[0] == 'https' and parsed[2].endswith('.git'): 77*fbfe4939SVaclav Hapla f = self.gitRetrieve 78*fbfe4939SVaclav Hapla elif parsed[0] == 'hg': 79*fbfe4939SVaclav Hapla f = self.hgRetrieve 80*fbfe4939SVaclav Hapla elif parsed[0] == 'ssh' and parsed[1].startswith('hg@'): 81*fbfe4939SVaclav Hapla f = self.hgRetrieve 82*fbfe4939SVaclav Hapla elif os.path.isdir(url): 83*fbfe4939SVaclav Hapla if self.isDirectoryGitRepo(url): 84*fbfe4939SVaclav Hapla f = self.gitRetrieve 85*fbfe4939SVaclav Hapla else: 86*fbfe4939SVaclav Hapla f = self.dirRetrieve 87*fbfe4939SVaclav Hapla else: 88*fbfe4939SVaclav Hapla f = self.tarballRetrieve 89*fbfe4939SVaclav Hapla return f(url, root, package) 9052df3566SBarry Smith 91*fbfe4939SVaclav Hapla def dirRetrieve(self, url, root, package): 92*fbfe4939SVaclav Hapla self.logPrint('Retrieving %s as directory' % url, 3, 'install') 93*fbfe4939SVaclav Hapla d = self.removePrefix(url, 'dir://') 94*fbfe4939SVaclav Hapla if not os.path.isdir(d): raise RuntimeError('URL %s is not a directory' % url) 9552df3566SBarry Smith 96*fbfe4939SVaclav Hapla t = os.path.join(root,os.path.basename(d)) 97*fbfe4939SVaclav Hapla self.removeTarget(t) 98*fbfe4939SVaclav Hapla shutil.copytree(d,t) 9952df3566SBarry Smith 100*fbfe4939SVaclav Hapla def linkRetrieve(self, url, root, package): 101*fbfe4939SVaclav Hapla self.logPrint('Retrieving %s as link' % url, 3, 'install') 102*fbfe4939SVaclav Hapla d = self.removePrefix(url, 'link://') 103*fbfe4939SVaclav Hapla if not os.path.isdir(d): raise RuntimeError('URL %s is not pointing to a directory' % url) 1043a911845SSatish Balay 105*fbfe4939SVaclav Hapla t = os.path.join(root,os.path.basename(d)) 106*fbfe4939SVaclav Hapla self.removeTarget(t) 107*fbfe4939SVaclav Hapla os.symlink(os.path.abspath(d),t) 1083a911845SSatish Balay 109*fbfe4939SVaclav Hapla def gitRetrieve(self, url, root, package): 110*fbfe4939SVaclav Hapla self.logPrint('Retrieving %s as git repo' % url, 3, 'install') 111*fbfe4939SVaclav Hapla if not hasattr(self.sourceControl, 'git'): 112*fbfe4939SVaclav Hapla raise RuntimeError('self.sourceControl.git not set') 113*fbfe4939SVaclav Hapla d = self.removePrefix(url, 'git://') 114*fbfe4939SVaclav Hapla if os.path.isdir(d) and not self.isDirectoryGitRepo(d): 115*fbfe4939SVaclav Hapla raise RuntimeError('URL %s is a directory but not a git repository' % url) 11652df3566SBarry Smith 1171aefc9f4SSatish Balay newgitrepo = os.path.join(root,'git.'+package) 118*fbfe4939SVaclav Hapla self.removeTarget(newgitrepo) 11952df3566SBarry Smith 120b93f8388SBarry Smith try: 121*fbfe4939SVaclav Hapla config.base.Configure.executeShellCommand('%s clone %s %s' % (self.sourceControl.git, d, newgitrepo), log = self.log) 1225b6bfdb9SJed Brown except RuntimeError as e: 123b93f8388SBarry Smith self.logPrint('ERROR: '+str(e)) 124b93f8388SBarry Smith err = str(e) 125*fbfe4939SVaclav Hapla failureMessage = self.getDownloadFailureMessage(package, url) 126*fbfe4939SVaclav Hapla raise RuntimeError('Unable to clone '+package+'\n'+err+failureMessage) 1275e208ef3SBarry Smith 128*fbfe4939SVaclav Hapla def hgRetrieve(self, url, root, package): 129*fbfe4939SVaclav Hapla self.logPrint('Retrieving %s as hg repo' % url, 3, 'install') 130*fbfe4939SVaclav Hapla if not hasattr(self.sourceControl, 'hg'): 131*fbfe4939SVaclav Hapla raise RuntimeError('self.sourceControl.hg not set') 132*fbfe4939SVaclav Hapla d = self.removePrefix(url, 'hg://') 1330c3d3c20SBarry Smith 1340c3d3c20SBarry Smith newgitrepo = os.path.join(root,'hg.'+package) 135*fbfe4939SVaclav Hapla self.removeTarget(newgitrepo) 136b93f8388SBarry Smith try: 137*fbfe4939SVaclav Hapla config.base.Configure.executeShellCommand('%s clone %s %s' % (self.sourceControl.hg, d, newgitrepo), log = self.log) 1385b6bfdb9SJed Brown except RuntimeError as e: 139b93f8388SBarry Smith self.logPrint('ERROR: '+str(e)) 140b93f8388SBarry Smith err = str(e) 141*fbfe4939SVaclav Hapla failureMessage = self.getDownloadFailureMessage(package, url) 142*fbfe4939SVaclav Hapla raise RuntimeError('Unable to clone '+package+'\n'+err+failureMessage) 1430c3d3c20SBarry Smith 144*fbfe4939SVaclav Hapla def tarballRetrieve(self, url, root, package): 145*fbfe4939SVaclav Hapla parsed = urlparse_local.urlparse(url) 146*fbfe4939SVaclav Hapla filename = os.path.basename(parsed[2]) 14715ac2963SJed Brown localFile = os.path.join(root,'_d_'+filename) 148*fbfe4939SVaclav Hapla self.logPrint('Retrieving %s as tarball to %s' % (url,localFile) , 3, 'install') 14915ac2963SJed Brown ext = os.path.splitext(localFile)[1] 15015ac2963SJed Brown if ext not in ['.bz2','.tbz','.gz','.tgz','.zip','.ZIP']: 151179860b2SJed Brown raise RuntimeError('Unknown compression type in URL: '+ url) 15215ac2963SJed Brown 153*fbfe4939SVaclav Hapla self.removeTarget(localFile) 154*fbfe4939SVaclav Hapla 155*fbfe4939SVaclav Hapla if parsed[0] == 'file' and not parsed[1]: 156*fbfe4939SVaclav Hapla url = parsed[2] 157*fbfe4939SVaclav Hapla if os.path.exists(url): 158*fbfe4939SVaclav Hapla if not os.path.isfile(url): 159*fbfe4939SVaclav Hapla raise RuntimeError('Local path exists but is not a regular file: '+ url) 160*fbfe4939SVaclav Hapla # copy local file 161*fbfe4939SVaclav Hapla shutil.copyfile(url, localFile) 162*fbfe4939SVaclav Hapla else: 163*fbfe4939SVaclav Hapla # fetch remote file 164179860b2SJed Brown try: 165728600e6SSatish Balay sav_timeout = socket.getdefaulttimeout() 166728600e6SSatish Balay socket.setdefaulttimeout(30) 167e7c47bf1SJed Brown urlretrieve(url, localFile) 168728600e6SSatish Balay socket.setdefaulttimeout(sav_timeout) 1695b6bfdb9SJed Brown except Exception as e: 170728600e6SSatish Balay socket.setdefaulttimeout(sav_timeout) 171*fbfe4939SVaclav Hapla failureMessage = self.getDownloadFailureMessage(package, url, filename) 172179860b2SJed Brown raise RuntimeError(failureMessage) 17315ac2963SJed Brown 17415ac2963SJed Brown self.logPrint('Extracting '+localFile) 17515ac2963SJed Brown if ext in ['.zip','.ZIP']: 17615ac2963SJed Brown config.base.Configure.executeShellCommand('cd '+root+'; unzip '+localFile, log = self.log) 17715ac2963SJed Brown output = config.base.Configure.executeShellCommand('cd '+root+'; zipinfo -1 '+localFile+' | head -n 1', log = self.log) 178179860b2SJed Brown dirname = os.path.normpath(output[0].strip()) 17915ac2963SJed Brown else: 18015ac2963SJed Brown failureMessage = '''\ 18115ac2963SJed BrownDownloaded package %s from: %s is not a tarball. 18215ac2963SJed Brown[or installed python cannot process compressed files] 18315ac2963SJed Brown* If you are behind a firewall - please fix your proxy and rerun ./configure 18415ac2963SJed Brown For example at LANL you may need to set the environmental variable http_proxy (or HTTP_PROXY?) to http://proxyout.lanl.gov 1850aa1f76dSSatish Balay* You can run with --with-packages-download-dir=/adirectory and ./configure will instruct you what packages to download manually 186b93f8388SBarry Smith* or you can download the above URL manually, to /yourselectedlocation/%s 18715ac2963SJed Brown and use the configure option: 18815ac2963SJed Brown --download-%s=/yourselectedlocation/%s 1891aefc9f4SSatish Balay''' % (package.upper(), url, filename, package, filename) 19015ac2963SJed Brown import tarfile 19115ac2963SJed Brown try: 19215ac2963SJed Brown tf = tarfile.open(os.path.join(root, localFile)) 1935b6bfdb9SJed Brown except tarfile.ReadError as e: 194b95f98c7SJed Brown raise RuntimeError(str(e)+'\n'+failureMessage) 19515ac2963SJed Brown if not tf: raise RuntimeError(failureMessage) 1962501eaf6SSatish Balay #git puts 'pax_global_header' as the first entry and some tar utils process this as a file 1972501eaf6SSatish Balay firstname = tf.getnames()[0] 1982501eaf6SSatish Balay if firstname == 'pax_global_header': 1992501eaf6SSatish Balay firstmember = tf.getmembers()[1] 20015ac2963SJed Brown else: 2012501eaf6SSatish Balay firstmember = tf.getmembers()[0] 2022501eaf6SSatish Balay # some tarfiles list packagename/ but some list packagename/filename in the first entry 2032501eaf6SSatish Balay if firstmember.isdir(): 2042501eaf6SSatish Balay dirname = firstmember.name 2052501eaf6SSatish Balay else: 2062501eaf6SSatish Balay dirname = os.path.dirname(firstmember.name) 20715ac2963SJed Brown tf.extractall(root) 20815ac2963SJed Brown tf.close() 20915ac2963SJed Brown 21015ac2963SJed Brown # fix file permissions for the untared tarballs. 21115ac2963SJed Brown try: 2122501eaf6SSatish Balay # check if 'dirname' is set' 2132501eaf6SSatish Balay if dirname: 214179860b2SJed Brown config.base.Configure.executeShellCommand('cd '+root+'; chmod -R a+r '+dirname+';find '+dirname + ' -type d -name "*" -exec chmod a+rx {} \;', log = self.log) 2152501eaf6SSatish Balay else: 2162501eaf6SSatish Balay self.logPrintBox('WARNING: Could not determine dirname extracted by '+localFile+' to fix file permissions') 2175b6bfdb9SJed Brown except RuntimeError as e: 21815ac2963SJed Brown raise RuntimeError('Error changing permissions for '+dirname+' obtained from '+localFile+ ' : '+str(e)) 219179860b2SJed Brown os.unlink(localFile) 220