Browse Source

Fix issue installing pdeploy-generated .pkg on OS X El Capitan

rdb 10 years ago
parent
commit
75f7e70767
1 changed files with 64 additions and 8 deletions
  1. 64 8
      direct/src/p3d/DeploymentTools.py

+ 64 - 8
direct/src/p3d/DeploymentTools.py

@@ -5,6 +5,7 @@ to build for as many platforms as possible. """
 __all__ = ["Standalone", "Installer"]
 
 import os, sys, subprocess, tarfile, shutil, time, zipfile, glob, socket, getpass, struct
+import gzip
 from cStringIO import StringIO
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.AppRunnerGlobal import appRunner
@@ -977,7 +978,7 @@ class Installer:
         if self.licensefile:
             shutil.copyfile(self.licensefile.toOsSpecific(), Filename(output, "Contents/Resources/License.txt").toOsSpecific())
         pkginfo = open(Filename(output, "Contents/PkgInfo").toOsSpecific(), "w")
-        pkginfo.write("pkmkrpkg1")
+        pkginfo.write("pmkrpkg1")
         pkginfo.close()
         pkginfo = open(Filename(output, "Contents/Resources/package_version").toOsSpecific(), "w")
         pkginfo.write("major: 1\nminor: 9")
@@ -1062,18 +1063,18 @@ class Installer:
         plist.write('</plist>\n')
         plist.close()
 
-        if hasattr(tarfile, "PAX_FORMAT"):
-            archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", format = tarfile.PAX_FORMAT, tarinfo = TarInfoRootOSX)
-        else:
-            archive = tarfile.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), "w:gz", tarinfo = TarInfoRootOSX)
-        archive.add(appfn.toOsSpecific(), appname)
+        # OS X El Capitan no longer accepts .pax archives - it must be a CPIO archive named .pax.
+        archive = gzip.open(Filename(output, "Contents/Archive.pax.gz").toOsSpecific(), 'wb')
+        self.__ino = 0
+        self.__writeCPIO(archive, appfn, appname)
+        archive.write(b"0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!\0")
         archive.close()
 
         # Put the .pkg into a zipfile
-        archive = Filename(output.getDirname(), "%s %s.zip" % (self.fullname, self.version))
+        zip_fn = Filename(output.getDirname(), "%s %s.pkg.zip" % (self.fullname, self.version))
         dir = Filename(output.getDirname())
         dir.makeAbsolute()
-        zip = zipfile.ZipFile(archive.toOsSpecific(), 'w')
+        zip = zipfile.ZipFile(zip_fn.toOsSpecific(), 'w')
         for root, dirs, files in self.os_walk(output.toOsSpecific()):
             for name in files:
                 file = Filename.fromOsSpecific(os.path.join(root, name))
@@ -1084,6 +1085,61 @@ class Installer:
 
         return output
 
+    def __writeCPIO(self, archive, fn, name):
+        """ Adds the given fn under the given name to the CPIO archive. """
+
+        st = os.lstat(fn.toOsSpecific())
+
+        archive.write(b"070707") # magic
+        archive.write(b"000000") # dev
+
+        # Synthesize an inode number, different for each entry.
+        self.__ino += 1
+        archive.write("%06o" % (self.__ino))
+
+        # Determine based on the type which mode to write.
+        if os.path.islink(fn.toOsSpecific()):
+            archive.write("%06o" % (st.st_mode))
+            target = os.path.readlink(fn.toOsSpecific()).encode('utf-8')
+            size = len(target)
+        elif os.path.isdir(fn.toOsSpecific()):
+            archive.write(b"040755")
+            size = 0
+        elif not fn.getExtension():  # Binary file?
+            archive.write(b"100755")
+            size = st.st_size
+        else:
+            archive.write(b"100644")
+            size = st.st_size
+
+        archive.write("000000") # uid (root)
+        archive.write("000000") # gid (wheel)
+        archive.write("%06o" % (st.st_nlink))
+        archive.write("000000") # rdev
+        archive.write("%011o" % (st.st_mtime))
+        archive.write("%06o" % (len(name) + 1))
+        archive.write("%011o" % (size))
+
+        # Write the filename, plus terminating NUL byte.
+        archive.write(name.encode('utf-8'))
+        archive.write(b"\0")
+
+        # Copy the file data to the archive.
+        if os.path.islink(fn.toOsSpecific()):
+            archive.write(target)
+        elif size:
+            handle = open(fn.toOsSpecific(), 'rb')
+            data = handle.read(1024 * 1024)
+            while data:
+                archive.write(data)
+                data = handle.read(1024 * 1024)
+            handle.close()
+
+        # If this is a directory, recurse.
+        if os.path.isdir(fn.toOsSpecific()):
+            for child in os.listdir(fn.toOsSpecific()):
+                self.__writeCPIO(archive, Filename(fn, child), name + "/" + child)
+
     def buildNSIS(self, output, platform):
         # Check if we have makensis first
         makensis = None