A first shot at binary packages support for prefix. In itself, binary
authorFabian Groffen <grobian@gentoo.org>
Mon, 27 Aug 2007 19:21:53 +0000 (19:21 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 27 Aug 2007 19:21:53 +0000 (19:21 -0000)
packages work fine of course, but once you want to share the binary
package between users with different prefixes (like from a binhost),
then you need to cope with that.  After some testing chpathtool appears
to be able to successfully change paths encoded in binaries, in order to
adjust to the proper prefix.  For a binhost setting, binary packages
need to have an artificially long and annoying prefix to make it work
the most people.

svn path=/main/branches/prefix/; revision=7706

pym/portage/__init__.py
src/Makefile.am
src/chpathtool.c [new file with mode: 0644]

index e36bff6a20eb5cdb5ab7bab5b59be79d8879f296..924be6f699a2a5fe8ebf3a3a7f865441600861d0 100644 (file)
@@ -5134,6 +5134,16 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, vartree=None, prev_mtimes
                        return 1
                mycat = mycat.strip()
 
+               eapi = xptbz2.getfile("EAPI")
+               if not eapi:
+                       writemsg("!!! EAPI info missing from info chunk, aborting...\n",
+                               noiselevel=-1)
+                       return 1
+               elif eapi.strip() != portage.const.EAPI:
+                       writemsg("!!! EAPI mismatch, got: %s, supported: %s\n" %
+                               (eapi.strip(), portage.const.EAPI), noiselevel=-1)
+                       return 1
+
                # These are the same directories that would be used at build time.
                builddir = os.path.join(
                        mysettings["PORTAGE_TMPDIR"], "portage", mycat, mypkg)
@@ -5148,6 +5158,7 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, vartree=None, prev_mtimes
                portage.util.ensure_dirs(catdir,
                        uid=portage_uid, gid=portage_gid, mode=070, mask=0)
                builddir_lock = portage.locks.lockdir(builddir)
+               chost = mysettings["CHOST"]
                try:
                        portage.locks.unlockdir(catdir_lock)
                finally:
@@ -5171,6 +5182,21 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, vartree=None, prev_mtimes
 
                debug = mysettings.get("PORTAGE_DEBUG", "") == "1"
 
+               # We want to install in "our" prefix, not the binary one
+               buildprefix = mysettings.get("EPREFIX", "")
+               if len(buildprefix) < len(EPREFIX):
+                       writemsg("!!! Unsuitable binary package, prefix absent or length is too small for this system\n",
+                               noiselevel=-1)
+                       return 1
+               mysettings["EPREFIX"] = EPREFIX
+
+               # Let's assure that what we dump into our system has also a
+               # chance of being able to run...
+               if mysettings.get("CHOST", "") != chost:
+                       writemsg("!!! Incompatible binary package: made for %s, you are on %s\n" % mysettings.get("CHOST", ""), chost,
+                               noiselevel=-1)
+                       return 1
+
                # Eventually we'd like to pass in the saved ebuild env here.
                retval = doebuild(myebuild, "setup", myroot, mysettings, debug=debug,
                        tree="bintree", mydbapi=mydbapi, vartree=vartree)
@@ -5180,7 +5206,7 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, vartree=None, prev_mtimes
 
                writemsg_stdout(">>> Extracting %s\n" % mypkg)
                retval = portage.process.spawn_bash(
-                       "bzip2 -dqc -- '%s' | tar -xp -C '%s' -f -" % (mytbz2, pkgloc),
+                       "bzip2 -dqc -- '%s' | chpathtool - - '%s' '%s' | tar -xp -C '%s' -f -" % (mytbz2, buildprefix, EPREFIX, pkgloc),
                        env=mysettings.environ())
                if retval != os.EX_OK:
                        writemsg("!!! Error Extracting '%s'\n" % mytbz2, noiselevel=-1)
@@ -5188,6 +5214,13 @@ def pkgmerge(mytbz2, myroot, mysettings, mydbapi=None, vartree=None, prev_mtimes
                #portage.locks.unlockfile(tbz2_lock)
                #tbz2_lock = None
 
+               # the extracted package put everything in buildprefix, so we
+               # just have to move it to the right EPREFIX
+               if buildprefix != EPREFIX:
+                       shutil.copytree(os.path.join(pkgloc,
+                               buildprefix.lstrip(os.path.sep)), os.path.join(pkgloc,
+                                       EPREFIX_LSTRIP), symlinks=true)
+
                mylink = dblink(mycat, mypkg, myroot, mysettings, vartree=vartree,
                        treetype="bintree")
                retval = mylink.merge(pkgloc, infloc, myroot, myebuild, cleanup=0,
index 4c7b06b470bca3afc07228989af53da46ae4ffac..7543ce7e2562bcaee88bf969be07b6147514b974 100644 (file)
@@ -1,11 +1,13 @@
 SUBDIRS        =
 
-bin_PROGRAMS   =
+bin_PROGRAMS   = chpathtool
 bindir = @PORTAGE_BASE@/bin
 if BUILD_TBZ2TOOL
 bin_PROGRAMS += tbz2tool
 endif
 
+chpathtool_SOURCES = chpathtool.c
+
 tbz2tool_SOURCES = tbz2tool.c
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/chpathtool.c b/src/chpathtool.c
new file mode 100644 (file)
index 0000000..2ebadf6
--- /dev/null
@@ -0,0 +1,176 @@
+/* Copyright Gentoo Foundation 2006-2007
+ * Author: Fabian Groffen <grobian@gentoo.org>
+ * $Id: $
+ *
+ * chpathtool replaces a given string (magic) into another (value),
+ * thereby paying attention to the original size of magic in order not
+ * to change offsets in the file changed.  To achieve this goal, value
+ * is not greater in size than magic, and the difference in size between
+ * the two is compensated by adding NULL-bytes at the end of a modified
+ * string.  The end of a string is considered to be at the first
+ * NULL-byte encountered after magic.  If no such NULL-byte is found, as
+ * in a plain text file, the padding NULL-bytes are silently dropped.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <errno.h>
+
+/* Don't allocate too much, or you'll be paying for waiting on IO,
+ * size -1 to align in memory. */
+#define BUFSIZE 8095
+
+/**
+ * Writes padding zero-bytes after the first encountered zero-byte.
+ * Returns padding if no zero-byte was seen, or 0 if padding was
+ * applied.
+ */
+static size_t padonwrite(size_t padding, char *buf, size_t len, FILE *fout) {
+       char *z;
+       if (padding == 0 || (z = memchr(buf, '\0', len)) == NULL) {
+               /* cheap case, nothing complicated to do here */
+               fwrite(buf, len, 1, fout);
+       } else {
+               /* found a zero-byte, insert padding */
+               fwrite(buf, z - buf, 1, fout);
+               /* now pad with zeros so we don't screw up
+                * the positions in the file */
+               buf[0] = '\0';
+               while (padding > 0) {
+                       fwrite(buf, 1, 1, fout);
+                       padding--;
+               }
+               fwrite(z, len - (z - buf), 1, fout);
+       }
+
+       return(padding);
+}
+
+/**
+ * Searches buf for an occurrence of needle, doing a byte-based match,
+ * disregarding end of string markers (zero-bytes), as strstr does.
+ * Returns a pointer to the first occurrence of needle in buf, or NULL
+ * if not found.  If a partial match is found at the end of the buffer
+ * (size < strlen(needle)) it is returned as well.
+ */
+static char *memstr(const char *buf, const char *needle, size_t len) {
+       const char *ret;
+       size_t off;
+       for (ret = buf; ret - buf < len; ret++) {
+               off = 0;
+               while (needle[off] != '\0' &&
+                               (ret - buf) + off < len &&
+                               needle[off] == ret[off])
+               {
+                       off++;
+               }
+               if (needle[off] == '\0' || (ret - buf) + off == len)
+                       return((char *)ret);
+       }
+       return(NULL);
+}
+
+int main(int argc, char **argv) {
+       FILE *fin;
+       FILE *fout;
+       char *magic;
+       char *value;
+       char buf[BUFSIZE + 1];
+       char *tmp;
+       size_t len;
+       size_t pos;
+       size_t padding;
+       size_t magiclen;
+       size_t valuelen;
+
+       if (argc != 5) {
+               fprintf(stderr, "usage: in-file out-file magic value\n");
+               return(-1);
+       }
+
+       magic    = argv[3];
+       value    = argv[4];
+       magiclen = strlen(magic);
+       valuelen = strlen(value);
+
+       if (magiclen < valuelen) {
+               fprintf(stderr, "value length (%zd) is bigger than "
+                               "the magic length (%zd)\n", valuelen, magiclen);
+               return(-1);
+       }
+       if (magiclen > BUFSIZE) {
+               fprintf(stderr, "magic length (%zd) is bigger than "
+                               "BUFSIZE (%d), unable to process\n", magiclen, BUFSIZE);
+               return(-1);
+       }
+
+       if (strcmp(argv[1], "-") == 0) {
+               fin  = stdin;
+       } else {
+               fin  = fopen(argv[1], "r");
+       }
+       if (fin == NULL) {
+               fprintf(stderr, "unable to open %s: %s\n", argv[1], strerror(errno));
+               return(-1);
+       }
+
+       if (strcmp(argv[2], "-") == 0) {
+               fout = stdout;
+       } else {
+               fout = fopen(argv[2], "w");
+       }
+       if (fin == NULL) {
+               fprintf(stderr, "unable to open %s: %s\n", argv[2], strerror(errno));
+               return(-1);
+       }
+
+       /* make sure there is a trailing zero-byte, such that strstr and
+        * strchr won't go out of bounds causing segfaults.  */
+       buf[BUFSIZE] = '\0';
+
+       pos = 0;
+       padding = 0;
+       while ((len = fread(buf + pos, 1, BUFSIZE - pos, fin)) != 0 || pos > 0) {
+               len += pos;
+               if ((tmp = memstr(buf, magic, len)) != NULL) {
+                       if (tmp == buf) {
+                               /* do some magic, overwrite it basically */
+                               fwrite(value, valuelen, 1, fout);
+                               /* store what we need to correct */
+                               padding += magiclen - valuelen;
+                               /* move away the magic */
+                               pos = len - magiclen;
+                               memmove(buf, buf + magiclen, pos);
+                               continue;
+                       } else {
+                               /* move this bunch to the front */
+                               pos = len - (tmp - buf);
+                       }
+               } else {
+                       /* magic is not in here, since memchr also returns a match
+                        * if incomplete but at the end of the string, here we can
+                        * always read a new block. */
+                       if (len != BUFSIZE) {
+                               /* last piece */
+                               padding = padonwrite(padding, buf, len, fout);
+                               break;
+                       } else {
+                               pos = 0;
+                               tmp = buf + len;
+                       }
+               }
+               padding = padonwrite(padding, buf, len - pos, fout);
+               if (pos > 0)
+                       memmove(buf, tmp, pos);
+       }
+       fflush(fout);
+       fclose(fout);
+       fclose(fin);
+
+       if (padding != 0) {
+               fprintf(stderr, "warning: couldn't find a location to write "
+                               "%zd padding bytes\n", padding);
+       }
+
+       return(0);
+}