I have developed the following script to create a dmg file from
files/directories indicated. The script runs only in Panther and I
test for that. I am enclosing it for your comments. I am relatively
new to MacOSX and Python so this is a good way for me to learn. If
this is not the proper way to get code reviewed, then I apoligize now,
but please indicate that to me and a better way of doing it. Thanks
for your help. Feel free to use the script for whatever purposes you
like if you find it helpful.
-----Script------
#!/usr/bin/env python
"""mkdmg.py -- Copy files to a .dmg file
This copies the specified files and directories to a newly
created compressed DMG file for Mac OS X 10.3 and above.
!!! USE AT YOUR OWN RISK !!!
"""
__version__ = 1.0
__license__ = "none"
######################################################################
# Imports
######################################################################
import os
import sys
import datetime
import glob
import fnmatch
import shutil
import string
import copy
import getopt
import commands
import optparse
import tempfile
import time
import rmwCommon
if sys.platform == 'darwin':
import Carbon
import macostools
######################################################################
# Global Data
######################################################################
fDebug = 0
fSkip = 0
######################################################################
# dmgFS Class
######################################################################
class dmgFS:
"""A class for a Sparse Disk Image (dmg) for Mac OS X.
This is intended to create a OS X Disk Image File (with extension
.dmg)
containing archives of arbitrary files.
"""
def __del__( self ):
"Delete the object."
if fDebug:
print "dmgFS::del()"
# Unmount it if necessary.
if self.fMounted:
self.detachSparseImage( )
self.fMounted = 0
# Delete it if it shows being created.
if self.fCreated:
self.destroySparseImage( )
self.fCreated = 0
def __init__( self, szArchive="mkdmg" ):
"Initialize the object."
if fDebug:
print "dmgFS::init(%s)" % ( szArchive )
# setup Temporary dmg file.
# self.szTempDir = tempfile.gettempdir( )
self.szTempDir = "$HOME/Desktop"
self.szTempFile = szArchive
self.szTempPath = os.path.join( self.szTempDir,
(self.szTempFile + ".dmg") )
self.szTempPath = os.path.normpath( self.szTempPath )
if fDebug:
print "\tszTempPath=\"%s\"" % ( self.szTempPath )
self.szSparsePath = os.path.join( self.szTempDir, (szArchive +
".dmg.sparseimage") )
self.szSparsePath = os.path.normpath( self.szSparsePath )
if fDebug:
print "\tszSparsePath=\"%s\"" % ( self.szSparsePath )
oDT = datetime.datetime.utcnow( )
szDT = oDT.isoformat( ' ' )
szWrk = "-" + szDT[0:4] + szDT[5:7] + szDT[8:10]
szWrk = szWrk + szDT[11:13] + szDT[14:16] + szDT[17:19]
szWrk = szWrk + "-z.dmg"
self.szFinalPath = os.path.join( self.szTempDir, (szArchive +
szWrk) )
self.szFinalPath = os.path.normpath( self.szFinalPath )
if fDebug:
print "\tszFinalPath=\"%s\"" % ( self.szFinalPath )
# variables set later
self.listPaths = [ ]
self.fCreated = 0
self.fMounted = 0
self.iSizeK = 0
def addPath( self, szPath ):
"Add a Path to the Sparse Image."
if fDebug:
print "addPath(%s)" % ( szPath )
szWork = os.path.normpath( szPath )
szWork = os.path.expanduser( szWork )
szWork = os.path.expandvars( szWork )
szWork = os.path.abspath( szWork )
if fDebug:
print "\tabs.path=", szWork
# Figure out the source name.
if os.path.exists( szWork ):
pass
else:
if fDebug:
print "\tfSkip =", fSkip
if fSkip:
return
print "ERROR - %s does not exist to be copied!" % ( szPath )
raise ValueError
# iSize = os.path.getsize( szWork )
oDS = rmwCommon.DirectorySize( szWork )
iSizeK = (oDS.size() + 1024 - 1) / 1024
if fDebug:
print "\tos.path.size =", iSizeK
if os.path.isdir( szWork ):
if szWork[-1] == "/":
szWork = szWork[:-2]
if fDebug:
print "\tDirectory = %s" % ( szWork )
elif os.path.isfile( szWork ):
if fDebug:
print "\tFile = %s" % ( szWork )
else:
print "ERROR - %s is not a directory or file!" % ( szPath )
raise ValueError
self.iSizeK += iSizeK
self.listPaths.append( szWork )
def attachSparseImage( self ):
"Attach the Sparse Image."
if fDebug:
print "attachSparseImage()"
szCmdA = "hdiutil attach %s" % ( self.szSparsePath )
szCmdB = " 2>/dev/null >/dev/null"
szCmd = szCmdA + szCmdB
szCmd = szCmdA
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
self.fMounted = 1
else:
print "ERROR - Could not mount %s." % ( self.szSparsePath )
raise OSError
listWork = tupleResult[1].split()
if fDebug:
print "\tlistWork = ", listWork
self.szDevice = listWork[4]
if fDebug:
print "\tszDevice = ", self.szDevice
self.szVolume = listWork[10]
if fDebug:
print "\tszVolume = ", self.szVolume
return 0
def compressSparseImage( self ):
"Compress the Sparse Image."
if fDebug:
print "compressSparseImage()"
szCmdA = "hdiutil convert \"%s\" -format UDZO" % (
self.szSparsePath )
szCmdB = " -o \"%s\" -imagekey zlib-devel=9" % (
self.szFinalPath )
szCmdC = " 2>/dev/null >/dev/null"
szCmd = szCmdA + szCmdB + szCmdC
szCmd = szCmdA + szCmdB
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
pass
else:
print "ERROR - Could not compress SparseImage!"
raise OSError
def copyPaths( self ):
"Copy Paths to the Sparse Image."
if fDebug:
print "copyPaths( )"
if self.fMounted:
pass
else:
print "ERROR - SparseImage is not mounted!"
raise OSError
# Copy all the paths.
for szPath in self.listPaths:
self.copyPathToSparseImage( szPath )
def copyPathToSparseImage( self, szPath ):
"Copy a Path to the Sparse Image."
if fDebug:
print "copyPathToSparseImage(%s)" % ( szPath )
if self.fMounted:
pass
else:
print "ERROR - SparseImage is not mounted!"
raise OSError
# Figure out the source name.
if os.path.exists( szPath ):
pass
else:
print "ERROR - %s does not exist to be copied!" % ( szPath )
raise NameError
if fDebug:
print "\tos.path.size =", os.path.getsize( szPath )
if os.path.isdir( szPath ):
if szPath[-1] == "/":
szPath = szPath[:-2]
if fDebug:
print "\tDirectory = %s" % ( szPath )
szSrc = szPath
szDest = os.path.join( self.szVolume, os.path.split( szPath
)[1] )
elif os.path.isfile( szPath ):
if fDebug:
print "\tFile = %s" % ( szPath )
szSrc = szPath
szDest = os.path.join( self.szVolume, os.path.split( szPath
)[1] )
else:
print "ERROR - object, %s, needs to be a file or
directory!" % (szPath)
raise ValueError
# Copy the data to the Sparse Image.
szCmdA = "ditto -rsrcFork \"%s\" \"%s\"" % ( szSrc, szDest )
szCmdB = " 2>/dev/null >/dev/null"
szCmd = szCmdA + szCmdB
szCmd = szCmdA
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
pass
else:
print "ERROR - Copy %s to SparseImage FAILED!"
raise OSError
def createSparseImage( self ):
"Create the Sparse Image."
if fDebug:
print "createSparseImage(%d)" % ( self.iSizeK )
if self.iSizeK > 0:
pass
else:
print "ERROR - No files or directories to add to dmg!"
raise ValueError
iSizeM = ((2 * self.iSizeK) + 1024 - 1) / 1024
szCmdA = "hdiutil create \"%s\"" % ( self.szTempPath )
szCmdB = " -volname \"%s\" " % ( self.szTempFile )
szCmdC = " -megabytes %d -type SPARSE -fs HFS+ -autostretch" %
( iSizeM )
szCmdD = " 2>/dev/null >/dev/null"
# szCmd = szCmdA + szCmdB + szCmdC + szCmdD
szCmd = szCmdA + szCmdB + szCmdC
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
self.fCreated = 1
else:
print "ERROR - Could not create SparseImage!"
raise OSError
def destroySparseImage( self ):
"Destroy the Sparse Image."
if fDebug:
print "destroySparseImage()"
if self.fCreated:
pass
else:
print "ERROR - SparseImage does not exist so it cant be
destroyed!"
raise StandardError
# Cant destroy it if it is mounted.
if self.fMounted:
print "ERROR - Tried to destroy mounted SparseImage!"
raise StandardError
# Remove the file.
szCmd = "rm \"%s\"" % ( self.szSparsePath )
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
self.fCreated = 0
else:
print "ERROR - Could not destroy SparseImage!"
raise OSError
def detachSparseImage( self ):
"Detach the Sparse Image."
if fDebug:
print "detachSparseImage()"
if self.fMounted:
pass
else:
print "ERROR - SparseImage is not mounted!"
raise StandardError
szCmdA = "hdiutil detach -debug -verbose %s" % ( self.szDevice )
szCmdB = " 2>/dev/null >/dev/null"
szCmd = szCmdA + szCmdB
szCmd = szCmdA
# Unfortunately, the previous operations against the dmg are
somewhat
# queued w/o a means of knowing when they are done. So, we
will wait
# for successful completion of the detach for a specified
period of
# 10 mins (arbitrarily chosen) before aborting the detach.
for i in range( 60 ):
cWait = 10
if fDebug:
print "\tWaiting for %d seconds..." % (cWait)
time.sleep( cWait )
if fDebug:
print "\tExecuting %s..." % ( szCmd )
tupleResult = commands.getstatusoutput( szCmd )
if fDebug:
print "\tResult = %s, %s..." % ( tupleResult[0],
tupleResult[1] )
if tupleResult[0] == 0:
break
if tupleResult[0] == 0:
self.fMounted = 0
else:
print "ERROR - Could not unmount SparseImage!"
raise OSError
def run( self ):
"run creating the compressed image."
if fDebug:
print "run()"
self.createSparseImage( );
self.attachSparseImage( );
self.copyPaths( )
self.detachSparseImage( );
self.compressSparseImage( );
######################################################################
# Command-line interface
######################################################################
def main():
"Command-line interface."
global fDebug
global fSkip
szUsage = "usage: %prog [options] arg1 arg2 ..."
oCmdPrs = optparse.OptionParser( usage=szUsage )
oCmdPrs.add_option( "-d", "--debug", action="store_true",
dest="fDebug", default=False,
help="Set debug mode"
)
oCmdPrs.add_option( "-f", "--file",
dest="filename", metavar="FILE",
help="Read input file with one directory or
file per line"
)
oCmdPrs.add_option( "-s", "--skip", action="store_true",
dest="fSkip", default=False,
help="Allow files or directories that don't
exist to be skipped"
)
(oOptions, oArgs) = oCmdPrs.parse_args( )
if oOptions.fDebug:
fDebug = 1
print "In DEBUG Mode..."
if oOptions.fSkip:
fSkip = 1
print "In File/Directory skip Mode..."
if fDebug:
print "platform=%s" % sys.platform
if oOptions.filename:
print "-f name =", oOptions.filename
if sys.platform == "darwin":
pass
else:
oCmdPrs.error( "MacOSX 10.3 or above required" )
if len(oArgs) > 0:
pass
elif oOptions.filename:
pass
else:
oCmdPrs.error( "too few arguments" )
try:
oDmg = dmgFS( )
if oOptions.filename:
szWork = os.path.normpath( oOptions.filename )
szWork = os.path.expanduser( szWork )
szWork = os.path.expandvars( szWork )
szWork = os.path.abspath( szWork )
if fDebug:
print "\tabs.path=", szWork
if os.path.exists( szWork ):
pass
else:
print "ERROR - Could not file %s (%s)!" %
(oOptions.filename, szWork)
raise StandardError
fileInput = open( szWork, "r" )
for szLine in fileInput:
szLine = szLine.strip( )
if szLine == "":
continue
if szLine[0] == '#':
continue
oDmg.addPath( szLine )
for szPath in oArgs:
oDmg.addPath( szPath )
oDmg.run( )
finally:
oDmg = None
if __name__ == "__main__":
tupleName = os.uname( )
if tupleName[0] == "Darwin":
iRel = tupleName[2].split('.')
if iRel[0] > 6:
main( )
else:
print "Error - mkdmg only works in Darwin 7.0 and above!"
exit( 4 )
else:
print "Error - mkdmg only works in Darwin 7.0 and above!"
exit( 4 )
-----End of Script-----
-----Sample --file input file-----
# Define the Files/Directories to be backed up.
~/.xchat2
~/bin
~/Desktop
~/Documents
~/Library/Mail
~/Library/Safari/Bookmarks.plist
#~/Movies
#~/Music
#~/Pictures
~/rmwDPorts
~/rmwHtml
~/rmwLocal
~/rmwTest
-----End of Sample input file-----