1# ex:ts=4:sw=4:sts=4:et 2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- 3""" 4BitBake 'Fetch' implementation for perforce 5 6""" 7 8# Copyright (C) 2003, 2004 Chris Larson 9# Copyright (C) 2016 Kodak Alaris, Inc. 10# 11# This program is free software; you can redistribute it and/or modify 12# it under the terms of the GNU General Public License version 2 as 13# published by the Free Software Foundation. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License along 21# with this program; if not, write to the Free Software Foundation, Inc., 22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23# 24# Based on functions from the base bb module, Copyright 2003 Holger Schurig 25 26import os 27import logging 28import bb 29from bb.fetch2 import FetchMethod 30from bb.fetch2 import FetchError 31from bb.fetch2 import logger 32from bb.fetch2 import runfetchcmd 33 34class Perforce(FetchMethod): 35 """ Class to fetch from perforce repositories """ 36 def supports(self, ud, d): 37 """ Check to see if a given url can be fetched with perforce. """ 38 return ud.type in ['p4'] 39 40 def urldata_init(self, ud, d): 41 """ 42 Initialize perforce specific variables within url data. If P4CONFIG is 43 provided by the env, use it. If P4PORT is specified by the recipe, use 44 its values, which may override the settings in P4CONFIG. 45 """ 46 ud.basecmd = d.getVar('FETCHCMD_p4') 47 if not ud.basecmd: 48 ud.basecmd = "/usr/bin/env p4" 49 50 ud.dldir = d.getVar('P4DIR') 51 if not ud.dldir: 52 ud.dldir = '%s/%s' % (d.getVar('DL_DIR'), 'p4') 53 54 path = ud.url.split('://')[1] 55 path = path.split(';')[0] 56 delim = path.find('@'); 57 if delim != -1: 58 (ud.user, ud.pswd) = path.split('@')[0].split(':') 59 ud.path = path.split('@')[1] 60 else: 61 ud.path = path 62 63 ud.usingp4config = False 64 p4port = d.getVar('P4PORT') 65 66 if p4port: 67 logger.debug(1, 'Using recipe provided P4PORT: %s' % p4port) 68 ud.host = p4port 69 else: 70 logger.debug(1, 'Trying to use P4CONFIG to automatically set P4PORT...') 71 ud.usingp4config = True 72 p4cmd = '%s info | grep "Server address"' % ud.basecmd 73 bb.fetch2.check_network_access(d, p4cmd, ud.url) 74 ud.host = runfetchcmd(p4cmd, d, True) 75 ud.host = ud.host.split(': ')[1].strip() 76 logger.debug(1, 'Determined P4PORT to be: %s' % ud.host) 77 if not ud.host: 78 raise FetchError('Could not determine P4PORT from P4CONFIG') 79 80 if ud.path.find('/...') >= 0: 81 ud.pathisdir = True 82 else: 83 ud.pathisdir = False 84 85 cleanedpath = ud.path.replace('/...', '').replace('/', '.') 86 cleanedhost = ud.host.replace(':', '.') 87 ud.pkgdir = os.path.join(ud.dldir, cleanedhost, cleanedpath) 88 89 ud.setup_revisions(d) 90 91 ud.localfile = d.expand('%s_%s_%s.tar.gz' % (cleanedhost, cleanedpath, ud.revision)) 92 93 def _buildp4command(self, ud, d, command, depot_filename=None): 94 """ 95 Build a p4 commandline. Valid commands are "changes", "print", and 96 "files". depot_filename is the full path to the file in the depot 97 including the trailing '#rev' value. 98 """ 99 p4opt = "" 100 101 if ud.user: 102 p4opt += ' -u "%s"' % (ud.user) 103 104 if ud.pswd: 105 p4opt += ' -P "%s"' % (ud.pswd) 106 107 if ud.host and not ud.usingp4config: 108 p4opt += ' -p %s' % (ud.host) 109 110 if hasattr(ud, 'revision') and ud.revision: 111 pathnrev = '%s@%s' % (ud.path, ud.revision) 112 else: 113 pathnrev = '%s' % (ud.path) 114 115 if depot_filename: 116 if ud.pathisdir: # Remove leading path to obtain filename 117 filename = depot_filename[len(ud.path)-1:] 118 else: 119 filename = depot_filename[depot_filename.rfind('/'):] 120 filename = filename[:filename.find('#')] # Remove trailing '#rev' 121 122 if command == 'changes': 123 p4cmd = '%s%s changes -m 1 //%s' % (ud.basecmd, p4opt, pathnrev) 124 elif command == 'print': 125 if depot_filename != None: 126 p4cmd = '%s%s print -o "p4/%s" "%s"' % (ud.basecmd, p4opt, filename, depot_filename) 127 else: 128 raise FetchError('No depot file name provided to p4 %s' % command, ud.url) 129 elif command == 'files': 130 p4cmd = '%s%s files //%s' % (ud.basecmd, p4opt, pathnrev) 131 else: 132 raise FetchError('Invalid p4 command %s' % command, ud.url) 133 134 return p4cmd 135 136 def _p4listfiles(self, ud, d): 137 """ 138 Return a list of the file names which are present in the depot using the 139 'p4 files' command, including trailing '#rev' file revision indicator 140 """ 141 p4cmd = self._buildp4command(ud, d, 'files') 142 bb.fetch2.check_network_access(d, p4cmd, ud.url) 143 p4fileslist = runfetchcmd(p4cmd, d, True) 144 p4fileslist = [f.rstrip() for f in p4fileslist.splitlines()] 145 146 if not p4fileslist: 147 raise FetchError('Unable to fetch listing of p4 files from %s@%s' % (ud.host, ud.path)) 148 149 count = 0 150 filelist = [] 151 152 for filename in p4fileslist: 153 item = filename.split(' - ') 154 lastaction = item[1].split() 155 logger.debug(1, 'File: %s Last Action: %s' % (item[0], lastaction[0])) 156 if lastaction[0] == 'delete': 157 continue 158 filelist.append(item[0]) 159 160 return filelist 161 162 def download(self, ud, d): 163 """ Get the list of files, fetch each one """ 164 filelist = self._p4listfiles(ud, d) 165 if not filelist: 166 raise FetchError('No files found in depot %s@%s' % (ud.host, ud.path)) 167 168 bb.utils.remove(ud.pkgdir, True) 169 bb.utils.mkdirhier(ud.pkgdir) 170 171 for afile in filelist: 172 p4fetchcmd = self._buildp4command(ud, d, 'print', afile) 173 bb.fetch2.check_network_access(d, p4fetchcmd, ud.url) 174 runfetchcmd(p4fetchcmd, d, workdir=ud.pkgdir) 175 176 runfetchcmd('tar -czf %s p4' % (ud.localpath), d, cleanup=[ud.localpath], workdir=ud.pkgdir) 177 178 def clean(self, ud, d): 179 """ Cleanup p4 specific files and dirs""" 180 bb.utils.remove(ud.localpath) 181 bb.utils.remove(ud.pkgdir, True) 182 183 def supports_srcrev(self): 184 return True 185 186 def _revision_key(self, ud, d, name): 187 """ Return a unique key for the url """ 188 return 'p4:%s' % ud.pkgdir 189 190 def _latest_revision(self, ud, d, name): 191 """ Return the latest upstream scm revision number """ 192 p4cmd = self._buildp4command(ud, d, "changes") 193 bb.fetch2.check_network_access(d, p4cmd, ud.url) 194 tip = runfetchcmd(p4cmd, d, True) 195 196 if not tip: 197 raise FetchError('Could not determine the latest perforce changelist') 198 199 tipcset = tip.split(' ')[1] 200 logger.debug(1, 'p4 tip found to be changelist %s' % tipcset) 201 return tipcset 202 203 def sortable_revision(self, ud, d, name): 204 """ Return a sortable revision number """ 205 return False, self._build_revision(ud, d) 206 207 def _build_revision(self, ud, d): 208 return ud.revision 209 210