<div dir="ltr">Master has just landed the Python3 port, so can you rebase this to current git master?<div><br></div><div>Thanks,</div><div>Ross</div></div><div class="gmail_extra"><br><div class="gmail_quote">On 25 May 2016 at 20:48, Andrew Bradford <span dir="ltr">&lt;<a href="mailto:andrew@bradfordembedded.com" target="_blank">andrew@bradfordembedded.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">From: Andrew Bradford &lt;<a href="mailto:andrew.bradford@kodakalaris.com">andrew.bradford@kodakalaris.com</a>&gt;<br>
<br>
In recipes which use the perforce fetcher, enable use of SRCREV to<br>
specify any of: ${AUTOREV}, changeset number, p4date, or label.  This is<br>
more in-line with how the other fetchers work for source control<br>
systems.<br>
<br>
Allow use of the P4CONFIG env variable to define the server URL,<br>
username, and password if not provided in a recipe.<br>
<br>
This does change existing perforce fetcher usage by recipes and will<br>
need those recipes which use the perforce fetcher to be updated.  No<br>
recipes in oe-core use the perforce fetcher.<br>
<br>
References [YOCTO #6303]<br>
<br>
Signed-off-by: Andrew Bradford &lt;<a href="mailto:andrew.bradford@kodakalaris.com">andrew.bradford@kodakalaris.com</a>&gt;<br>
---<br>
 lib/bb/fetch2/__init__.py |   3 +-<br>
 lib/bb/fetch2/perforce.py | 279 ++++++++++++++++++++++++----------------------<br>
 2 files changed, 147 insertions(+), 135 deletions(-)<br>
<br>
diff --git a/lib/bb/fetch2/__init__.py b/lib/bb/fetch2/__init__.py<br>
index 7d2f350..b6e9f9a 100644<br>
--- a/lib/bb/fetch2/__init__.py<br>
+++ b/lib/bb/fetch2/__init__.py<br>
@@ -809,7 +809,8 @@ def runfetchcmd(cmd, d, quiet=False, cleanup=None):<br>
                   &#39;GIT_SMART_HTTP&#39;,<br>
                   &#39;SSH_AUTH_SOCK&#39;, &#39;SSH_AGENT_PID&#39;,<br>
                   &#39;SOCKS5_USER&#39;, &#39;SOCKS5_PASSWD&#39;,<br>
-                  &#39;DBUS_SESSION_BUS_ADDRESS&#39;]<br>
+                  &#39;DBUS_SESSION_BUS_ADDRESS&#39;,<br>
+                  &#39;P4CONFIG&#39;]<br>
<br>
     if not cleanup:<br>
         cleanup = []<br>
diff --git a/lib/bb/fetch2/perforce.py b/lib/bb/fetch2/perforce.py<br>
index 3a10c7c..b84489a 100644<br>
--- a/lib/bb/fetch2/perforce.py<br>
+++ b/lib/bb/fetch2/perforce.py<br>
@@ -1,14 +1,12 @@<br>
 # ex:ts=4:sw=4:sts=4:et<br>
 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-<br>
 &quot;&quot;&quot;<br>
-BitBake &#39;Fetch&#39; implementations<br>
-<br>
-Classes for obtaining upstream sources for the<br>
-BitBake build tools.<br>
+BitBake &#39;Fetch&#39; implementation for perforce<br>
<br>
 &quot;&quot;&quot;<br>
<br>
 # Copyright (C) 2003, 2004  Chris Larson<br>
+# Copyright (C) 2016 Kodak Alaris, Inc.<br>
 #<br>
 # This program is free software; you can redistribute it and/or modify<br>
 # it under the terms of the GNU General Public License version 2 as<br>
@@ -25,9 +23,8 @@ BitBake build tools.<br>
 #<br>
 # Based on functions from the base bb module, Copyright 2003 Holger Schurig<br>
<br>
-from future_builtins import zip<br>
 import os<br>
-import subprocess<br>
+import sys<br>
 import logging<br>
 import bb<br>
 from   bb import data<br>
@@ -37,151 +34,165 @@ from   bb.fetch2 import logger<br>
 from   bb.fetch2 import runfetchcmd<br>
<br>
 class Perforce(FetchMethod):<br>
+    &quot;&quot;&quot; Class to fetch from perforce repositories &quot;&quot;&quot;<br>
     def supports(self, ud, d):<br>
+        &quot;&quot;&quot; Check to see if a given url can be fetched with perforce. &quot;&quot;&quot;<br>
         return ud.type in [&#39;p4&#39;]<br>
<br>
-    def doparse(url, d):<br>
-        parm = {}<br>
-        path = url.split(&quot;://&quot;)[1]<br>
+    def urldata_init(self, ud, d):<br>
+        &quot;&quot;&quot;<br>
+        Initialize perforce specific variables within url data.  If P4PORT is<br>
+        not provided by the recipe but P4CONFIG is provided by the env, let<br>
+        perforce use P4CONFIG as it naturally does to set P4USER, P4PASSWD, and<br>
+        P4PORT.<br>
+        &quot;&quot;&quot;<br>
+        ud.basecmd = d.getVar(&#39;FETCHCMD_p4&#39;, True)<br>
+        ud.usingp4config = False<br>
+<br>
+        path = ud.url.split(&quot;://&quot;)[1]<br>
+        path = path.split(&quot;;&quot;)[0]<br>
         delim = path.find(&quot;@&quot;);<br>
         if delim != -1:<br>
-            (user, pswd, host, port) = path.split(&#39;@&#39;)[0].split(&quot;:&quot;)<br>
-            path = path.split(&#39;@&#39;)[1]<br>
+            (ud.user, ud.pswd) = path.split(&#39;@&#39;)[0].split(&quot;:&quot;)<br>
+            ud.path = path.split(&#39;@&#39;)[1]<br>
         else:<br>
-            (host, port) = d.getVar(&#39;P4PORT&#39;, False).split(&#39;:&#39;)<br>
-            user = &quot;&quot;<br>
-            pswd = &quot;&quot;<br>
-<br>
-        if path.find(&quot;;&quot;) != -1:<br>
-            keys=[]<br>
-            values=[]<br>
-            plist = path.split(&#39;;&#39;)<br>
-            for item in plist:<br>
-                if item.count(&#39;=&#39;):<br>
-                    (key, value) = item.split(&#39;=&#39;)<br>
-                    keys.append(key)<br>
-                    values.append(value)<br>
-<br>
-            parm = dict(zip(keys, values))<br>
-        path = &quot;//&quot; + path.split(&#39;;&#39;)[0]<br>
-        host += &quot;:%s&quot; % (port)<br>
-        parm[&quot;cset&quot;] = Perforce.getcset(d, path, host, user, pswd, parm)<br>
-<br>
-        return host, path, user, pswd, parm<br>
-    doparse = staticmethod(doparse)<br>
-<br>
-    def getcset(d, depot, host, user, pswd, parm):<br>
-        p4opt = &quot;&quot;<br>
-        if &quot;cset&quot; in parm:<br>
-            return parm[&quot;cset&quot;];<br>
-        if user:<br>
-            p4opt += &quot; -u %s&quot; % (user)<br>
-        if pswd:<br>
-            p4opt += &quot; -P %s&quot; % (pswd)<br>
-        if host:<br>
-            p4opt += &quot; -p %s&quot; % (host)<br>
-<br>
-        p4date = d.getVar(&quot;P4DATE&quot;, True)<br>
-        if &quot;revision&quot; in parm:<br>
-            depot += &quot;#%s&quot; % (parm[&quot;revision&quot;])<br>
-        elif &quot;label&quot; in parm:<br>
-            depot += &quot;@%s&quot; % (parm[&quot;label&quot;])<br>
-        elif p4date:<br>
-            depot += &quot;@%s&quot; % (p4date)<br>
-<br>
-        p4cmd = d.getVar(&#39;FETCHCMD_p4&#39;, True) or &quot;p4&quot;<br>
-        logger.debug(1, &quot;Running %s%s changes -m 1 %s&quot;, p4cmd, p4opt, depot)<br>
-        p4file, errors = bb.process.run(&quot;%s%s changes -m 1 %s&quot; % (p4cmd, p4opt, depot))<br>
-        cset = p4file.strip()<br>
-        logger.debug(1, &quot;READ %s&quot;, cset)<br>
-        if not cset:<br>
-            return -1<br>
-<br>
-        return cset.split(&#39; &#39;)[1]<br>
-    getcset = staticmethod(getcset)<br>
-<br>
-    def urldata_init(self, ud, d):<br>
-        (host, path, user, pswd, parm) = Perforce.doparse(ud.url, d)<br>
+            ud.path = path<br>
<br>
-        base_path = path.replace(&#39;/...&#39;, &#39;&#39;)<br>
-        base_path = self._strip_leading_slashes(base_path)<br>
-<br>
-        if &quot;label&quot; in parm:<br>
-            version = parm[&quot;label&quot;]<br>
+        p4port = d.getVar(&#39;P4PORT&#39;, True)<br>
+        if p4port:<br>
+            ud.host = p4port<br>
         else:<br>
-            version = Perforce.getcset(d, path, host, user, pswd, parm)<br>
-<br>
-        ud.localfile = data.expand(&#39;%s+%s+%s.tar.gz&#39; % (host, base_path.replace(&#39;/&#39;, &#39;.&#39;), version), d)<br>
-<br>
-    def download(self, ud, d):<br>
+            p4config = d.getVar(&#39;P4CONFIG&#39;, True)<br>
+            if p4config:<br>
+                <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;Using P4CONFIG to specify P4PORT&quot;)<br>
+                ud.usingp4config = True<br>
+                ud.host, errors = bb.process.run(&quot;p4 info | grep \&quot;Server address\&quot;&quot;)<br>
+                ud.host = ud.host.split(&#39;: &#39;)[1].strip()<br>
+                <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;Determined P4PORT to be: %s&quot; % ud.host)<br>
+                if not ud.host:<br>
+                    raise FetchError(&quot;Could not determine server name from P4CONFIG&quot;)<br>
+            else:<br>
+                raise FetchError(&quot;SRC_URI is not valid, did you forget to specify P4PORT or P4CONFIG?&quot;)<br>
+<br>
+        relpath = ud.path.replace(&#39;/...&#39;, &#39;&#39;)<br>
+        relpath = self._strip_leading_slashes(relpath)<br>
+        relpath = relpath.replace(&#39;/&#39;, &#39;.&#39;)<br>
+        cleanedhost = ud.host.replace(&#39;:&#39;, &#39;.&#39;)<br>
+        ud.pkgdir = os.path.join(data.expand(&#39;${P4DIR}&#39;, d), cleanedhost, relpath)<br>
+<br>
+        ud.setup_revisons(d)<br>
+<br>
+        ud.localfile = data.expand(&#39;%s_%s_%s.tar.gz&#39; % (cleanedhost, relpath, ud.revision), d)<br>
+<br>
+    def _buildp4command(self, ud, d, command, depot_filename=None):<br>
         &quot;&quot;&quot;<br>
-        Fetch urls<br>
+        Build a p4 commandline.  Valid commands are &quot;changes&quot;, &quot;print&quot;, and<br>
+        &quot;files&quot;.  depot_filename is the full path to the file in the depot<br>
+        including the trailing &#39;#rev&#39; value.<br>
         &quot;&quot;&quot;<br>
-<br>
-        (host, depot, user, pswd, parm) = Perforce.doparse(ud.url, d)<br>
-<br>
-        if depot.find(&#39;/...&#39;) != -1:<br>
-            path = depot[:depot.find(&#39;/...&#39;)]<br>
-        else:<br>
-            path = depot[:depot.rfind(&#39;/&#39;)]<br>
-<br>
-        module = parm.get(&#39;module&#39;, os.path.basename(path))<br>
-<br>
-        # Get the p4 command<br>
         p4opt = &quot;&quot;<br>
-        if user:<br>
-            p4opt += &quot; -u %s&quot; % (user)<br>
-<br>
-        if pswd:<br>
-            p4opt += &quot; -P %s&quot; % (pswd)<br>
-<br>
-        if host:<br>
-            p4opt += &quot; -p %s&quot; % (host)<br>
-<br>
-        p4cmd = d.getVar(&#39;FETCHCMD_p4&#39;, True) or &quot;p4&quot;<br>
-<br>
-        # create temp directory<br>
-        logger.debug(2, &quot;Fetch: creating temporary directory&quot;)<br>
-        bb.utils.mkdirhier(d.expand(&#39;${WORKDIR}&#39;))<br>
-        mktemp = d.getVar(&quot;FETCHCMD_p4mktemp&quot;, True) or d.expand(&quot;mktemp -d -q &#39;${WORKDIR}/oep4.XXXXXX&#39;&quot;)<br>
-        tmpfile, errors = bb.process.run(mktemp)<br>
-        tmpfile = tmpfile.strip()<br>
-        if not tmpfile:<br>
-            raise FetchError(&quot;Fetch: unable to create temporary directory.. make sure &#39;mktemp&#39; is in the PATH.&quot;, ud.url)<br>
-<br>
-        if &quot;label&quot; in parm:<br>
-            depot = &quot;%s@%s&quot; % (depot, parm[&quot;label&quot;])<br>
+        if ud.user:<br>
+            p4opt += &quot; -u \&quot;%s\&quot;&quot; % (ud.user)<br>
+        if ud.pswd:<br>
+            p4opt += &quot; -P \&quot;%s\&quot;&quot; % (ud.pswd)<br>
+        if ud.host and not ud.usingp4config:<br>
+            p4opt += &quot; -p %s&quot; % (ud.host)<br>
+        if hasattr(ud, &#39;revision&#39;) and ud.revision:<br>
+                pathnrev = &quot;%s@%s&quot; % (ud.path, ud.revision)<br>
         else:<br>
-            cset = Perforce.getcset(d, depot, host, user, pswd, parm)<br>
-            depot = &quot;%s@%s&quot; % (depot, cset)<br>
-<br>
-        os.chdir(tmpfile)<br>
-        <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;Fetch &quot; + ud.url)<br>
-        <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;%s%s files %s&quot;, p4cmd, p4opt, depot)<br>
-        p4file, errors = bb.process.run(&quot;%s%s files %s&quot; % (p4cmd, p4opt, depot))<br>
-        p4file = [f.rstrip() for f in p4file.splitlines()]<br>
+            pathnrev = &quot;%s&quot; % (ud.path)<br>
+<br>
+        if depot_filename:<br>
+            # FIXME: handle when SRC_URI is a single file and not a dir<br>
+            filename = depot_filename[len(ud.path)-1:] # Remove leading path<br>
+            where = filename.find(&quot;#&quot;) # Remove trailing &#39;#rev&#39;<br>
+            filename = filename[:where]<br>
+<br>
+        if command == &quot;changes&quot;:<br>
+            p4cmd = &quot;%s%s changes -m 1 //%s&quot; % (ud.basecmd, p4opt, pathnrev)<br>
+        elif command == &quot;print&quot;:<br>
+            if depot_filename != None:<br>
+                p4cmd = &quot;%s%s print -o \&quot;p4/%s\&quot; \&quot;%s\&quot;&quot; % (ud.basecmd, p4opt, filename, depot_filename)<br>
+            else:<br>
+                raise FetchError(&quot;No depot file name provided to p4 %s&quot; % command, ud.url)<br>
+        elif command == &quot;files&quot;:<br>
+            p4cmd = &quot;%s%s files //%s&quot; % (ud.basecmd, p4opt, pathnrev)<br>
+        else:<br>
+            raise FetchError(&quot;Invalid p4 command %s&quot; % command, ud.url)<br>
<br>
-        if not p4file:<br>
-            raise FetchError(&quot;Fetch: unable to get the P4 files from %s&quot; % depot, ud.url)<br>
+        return p4cmd<br>
<br>
+    def _p4listfiles(self, ud, d):<br>
+        &quot;&quot;&quot;<br>
+        Return a list of the file names which are present in the depot using the<br>
+        &#39;p4 files&#39; command, including trailing &#39;#rev&#39; file revision indicator<br>
+        &quot;&quot;&quot;<br>
+        p4cmd = self._buildp4command(ud, d, &quot;files&quot;)<br>
+        bb.fetch2.check_network_access(d, p4cmd)<br>
+        p4fileslist = runfetchcmd(p4cmd, d, True)<br>
+        p4fileslist = [f.rstrip() for f in p4fileslist.splitlines()]<br>
+        if not p4fileslist:<br>
+            raise FetchError(&quot;Unable to fetch listing of p4 files from %s@%s&quot; % ud.host, ud.path)<br>
         count = 0<br>
-<br>
-        for file in p4file:<br>
-            list = file.split()<br>
-<br>
-            if list[2] == &quot;delete&quot;:<br>
+        filelist = []<br>
+        for filename in p4fileslist:<br>
+            item = filename.split(&quot; - &quot;)<br>
+            lastaction = item[1].split()<br>
+            <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;File: &quot; + item[0] + &quot; Last Action: &quot; + lastaction[0])<br>
+            if lastaction[0] == &quot;delete&quot;:<br>
                 continue<br>
+            filelist.append(item[0])<br>
<br>
-            dest = list[0][len(path)+1:]<br>
-            where = dest.find(&quot;#&quot;)<br>
+        return filelist<br>
<br>
-            subprocess.call(&quot;%s%s print -o %s/%s %s&quot; % (p4cmd, p4opt, module, dest[:where], list[0]), shell=True)<br>
-            count = count + 1<br>
+    def download(self, ud, d):<br>
+        &quot;&quot;&quot; Get the list of files, fetch each one &quot;&quot;&quot;<br>
+        filelist = self._p4listfiles(ud, d)<br>
+<br>
+        if not filelist:<br>
+            raise FetchError(&quot;No files found in depot %s@%s&quot; % ud.host, ud.path)<br>
<br>
-        if count == 0:<br>
-            logger.error()<br>
-            raise FetchError(&quot;Fetch: No files gathered from the P4 fetch&quot;, ud.url)<br>
+        <a href="http://logger.info" rel="noreferrer" target="_blank">logger.info</a>(&quot;Fetch &quot; + ud.url)<br>
+        bb.utils.remove(ud.pkgdir, True)<br>
+        bb.utils.mkdirhier(ud.pkgdir)<br>
+        os.chdir(ud.pkgdir)<br>
+<br>
+        for afile in filelist:<br>
+            p4fetchcmd = self._buildp4command(ud, d, &quot;print&quot;, afile)<br>
+            logger.debug(1, &quot;Running %s&quot;, p4fetchcmd)<br>
+            bb.fetch2.check_network_access(d, p4fetchcmd)<br>
+            runfetchcmd(p4fetchcmd, d)<br>
+<br>
+        os.chdir(ud.pkgdir)<br>
+        runfetchcmd(&quot;tar -czf %s p4&quot; % (ud.localpath), d, cleanup = [ud.localpath])<br>
+<br>
+    def clean(self, ud, d):<br>
+        &quot;&quot;&quot; Cleanup p4 specific files and dirs&quot;&quot;&quot;<br>
+        bb.utils.remove(ud.localpath)<br>
+        bb.utils.remove(ud.pkgdir, True)<br>
+<br>
+    def supports_srcrev(self):<br>
+        return True<br>
+<br>
+    def _revision_key(self, ud, d, name):<br>
+        &quot;&quot;&quot; Return a unique key for the url &quot;&quot;&quot;<br>
+        return &quot;p4:&quot; + ud.pkgdir<br>
+<br>
+    def _latest_revision(self, ud, d, name):<br>
+        &quot;&quot;&quot; Return the latest upstream scm revision number &quot;&quot;&quot;<br>
+        p4cmd = self._buildp4command(ud, d, &quot;changes&quot;)<br>
+        bb.fetch2.check_network_access(d, p4cmd)<br>
+        tip = runfetchcmd(p4cmd, d, True)<br>
+        if not tip:<br>
+            raise FetchError(&quot;Could not determine the latest perforce changeset&quot;)<br>
+        tipcset = tip.split(&#39; &#39;)[1]<br>
+        logger.debug(1, &quot;p4 tip found to be changeset %s&quot;, tipcset)<br>
+        return tipcset<br>
+<br>
+    def sortable_revision(self, ud, d, name):<br>
+        &quot;&quot;&quot; Return a sortable revision number &quot;&quot;&quot;<br>
+        return False, self._build_revision(ud, d)<br>
+<br>
+    def _build_revision(self, ud, d):<br>
+        return ud.revision<br>
<br>
-        runfetchcmd(&quot;tar -czf %s %s&quot; % (ud.localpath, module), d, cleanup = [ud.localpath])<br>
-        # cleanup<br>
-        bb.utils.prunedir(tmpfile)<br>
<span class="HOEnZb"><font color="#888888">--<br>
2.8.1<br>
<br>
--<br>
_______________________________________________<br>
bitbake-devel mailing list<br>
<a href="mailto:bitbake-devel@lists.openembedded.org">bitbake-devel@lists.openembedded.org</a><br>
<a href="http://lists.openembedded.org/mailman/listinfo/bitbake-devel" rel="noreferrer" target="_blank">http://lists.openembedded.org/mailman/listinfo/bitbake-devel</a><br>
</font></span></blockquote></div><br></div>