[Python-checkins] python/dist/src/Mac/Lib bundlebuilder.py,1.1,1.2

jvr@users.sourceforge.net jvr@users.sourceforge.net
Thu, 21 Nov 2002 15:19:39 -0800


Update of /cvsroot/python/python/dist/src/Mac/Lib
In directory sc8-pr-cvs1:/tmp/cvs-serv7855

Modified Files:
	bundlebuilder.py 
Log Message:
added command line interface; refactored a bit; little things.

Index: bundlebuilder.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Mac/Lib/bundlebuilder.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** bundlebuilder.py	21 Nov 2002 10:23:04 -0000	1.1
--- bundlebuilder.py	21 Nov 2002 23:19:37 -0000	1.2
***************
*** 4,16 ****
  bundlebuilder.py -- Tools to assemble MacOS X (application) bundles.
  
! This module contains three classes to build so called "bundles" for
  MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass
! specialized in building application bundles. CocoaAppBuilder is a
! further specialization of AppBuilder.
  
! [Bundle|App|CocoaApp]Builder objects are instantiated with a bunch
! of keyword arguments, and have a build() method that will do all the
! work. See the class doc strings for a description of the constructor
! arguments.
  
  """
--- 4,25 ----
  bundlebuilder.py -- Tools to assemble MacOS X (application) bundles.
  
! This module contains two classes to build so called "bundles" for
  MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass
! specialized in building application bundles.
  
! [Bundle|App]Builder objects are instantiated with a bunch of keyword
! arguments, and have a build() method that will do all the work. See
! the class doc strings for a description of the constructor arguments.
! 
! The module contains a main program that can be used in two ways:
! 
!   % python bundlebuilder.py [options] build
!   % python buildapp.py [options] build
! 
! Where "buildapp.py" is a user-supplied setup.py-like script following
! this model:
! 
!   from bundlebuilder import buildapp
!   buildapp(<lots-of-keyword-args>)
  
  """
***************
*** 18,32 ****
  #
  # XXX Todo:
- # - a command line interface, also for use with the buildapp() and
- #   buildcocoaapp() convenience functions.
  # - modulefinder support to build standalone apps
  #
  
! __all__ = ["BundleBuilder", "AppBuilder", "CocoaAppBuilder",
! 		"buildapp", "buildcocoaapp"]
  
  
  import sys
  import os, errno, shutil
  from plistlib import Plist
  
--- 27,40 ----
  #
  # XXX Todo:
  # - modulefinder support to build standalone apps
+ # - consider turning this into a distutils extension
  #
  
! __all__ = ["BundleBuilder", "AppBuilder", "buildapp"]
  
  
  import sys
  import os, errno, shutil
+ import getopt
  from plistlib import Plist
  
***************
*** 63,83 ****
  	"""
  
! 	def __init__(self, name, plist=None, type="APPL", creator="????",
  			resources=None, files=None, builddir="build", platform="MacOS",
  			symlink=0, verbosity=1):
  		"""See the class doc string for a description of the arguments."""
- 		self.name, ext = os.path.splitext(name)
- 		if not ext:
- 			ext = ".bundle"
- 		self.bundleextension = ext
  		if plist is None:
  			plist = Plist()
  		self.plist = plist
  		self.type = type
  		self.creator = creator
- 		if files is None:
- 			files = []
- 		if resources is None:
- 			resources = []
  		self.resources = resources
  		self.files = files
--- 71,88 ----
  	"""
  
! 	def __init__(self, name=None, plist=None, type="APPL", creator="????",
  			resources=None, files=None, builddir="build", platform="MacOS",
  			symlink=0, verbosity=1):
  		"""See the class doc string for a description of the arguments."""
  		if plist is None:
  			plist = Plist()
+ 		if resources is None:
+ 			resources = []
+ 		if files is None:
+ 			files = []
+ 		self.name = name
  		self.plist = plist
  		self.type = type
  		self.creator = creator
  		self.resources = resources
  		self.files = files
***************
*** 85,94 ****
  		self.platform = platform
  		self.symlink = symlink
- 		# misc (derived) attributes
- 		self.bundlepath = pathjoin(builddir, self.name + self.bundleextension)
- 		self.execdir = pathjoin("Contents", platform)
- 		self.resdir = pathjoin("Contents", "Resources")
  		self.verbosity = verbosity
  
  	def build(self):
  		"""Build the bundle."""
--- 90,111 ----
  		self.platform = platform
  		self.symlink = symlink
  		self.verbosity = verbosity
  
+ 	def setup(self):
+ 		self.name, ext = os.path.splitext(self.name)
+ 		if not ext:
+ 			ext = ".bundle"
+ 		self.bundleextension = ext
+ 		# misc (derived) attributes
+ 		self.bundlepath = pathjoin(self.builddir, self.name + self.bundleextension)
+ 		self.execdir = pathjoin("Contents", self.platform)
+ 
+ 		plist = plistDefaults.copy()
+ 		plist.CFBundleName = self.name
+ 		plist.CFBundlePackageType = self.type
+ 		plist.CFBundleSignature = self.creator
+ 		plist.update(self.plist)
+ 		self.plist = plist
+ 
  	def build(self):
  		"""Build the bundle."""
***************
*** 125,135 ****
  		#
  		# Write Contents/Info.plist
- 		plist = plistDefaults.copy()
- 		plist.CFBundleName = self.name
- 		plist.CFBundlePackageType = self.type
- 		plist.CFBundleSignature = self.creator
- 		plist.update(self.plist)
  		infoplist = pathjoin(contents, "Info.plist")
! 		plist.write(infoplist)
  
  	def _copyFiles(self):
--- 142,147 ----
  		#
  		# Write Contents/Info.plist
  		infoplist = pathjoin(contents, "Info.plist")
! 		self.plist.write(infoplist)
  
  	def _copyFiles(self):
***************
*** 145,149 ****
  			msg = "Copying"
  		for src, dst in files:
! 			self.message("%s %s to %s" % (msg, src, dst), 2)
  			dst = pathjoin(self.bundlepath, dst)
  			if self.symlink:
--- 157,164 ----
  			msg = "Copying"
  		for src, dst in files:
! 			if os.path.isdir(src):
! 				self.message("%s %s/ to %s/" % (msg, src, dst), 2)
! 			else:
! 				self.message("%s %s to %s" % (msg, src, dst), 2)
  			dst = pathjoin(self.bundlepath, dst)
  			if self.symlink:
***************
*** 154,158 ****
  	def message(self, msg, level=0):
  		if level <= self.verbosity:
! 			sys.stderr.write(msg + "\n")
  
  
--- 169,181 ----
  	def message(self, msg, level=0):
  		if level <= self.verbosity:
! 			indent = ""
! 			if level > 1:
! 				indent = (level - 1) * "  "
! 			sys.stderr.write(indent + msg + "\n")
! 
! 	def report(self):
! 		# XXX something decent
! 		import pprint
! 		pprint.pprint(self.__dict__)
  
  
***************
*** 167,176 ****
  assert os.path.exists(mainprogram)
  argv.insert(1, mainprogram)
! %(executable)s
  os.execve(executable, argv, os.environ)
  """
  
! executableTemplate = "executable = os.path.join(resources, \"%s\")"
! 
  
  class AppBuilder(BundleBuilder):
--- 190,201 ----
  assert os.path.exists(mainprogram)
  argv.insert(1, mainprogram)
! os.environ["PYTHONPATH"] = resources
! %(setpythonhome)s
! %(setexecutable)s
  os.execve(executable, argv, os.environ)
  """
  
! setExecutableTemplate = """executable = os.path.join(resources, "%s")"""
! pythonhomeSnippet = """os.environ["home"] = resources"""
  
  class AppBuilder(BundleBuilder):
***************
*** 178,182 ****
  	"""This class extends the BundleBuilder constructor with these
  	arguments:
! 	
  		mainprogram: A Python main program. If this argument is given,
  			the main executable in the bundle will be a small wrapper
--- 203,207 ----
  	"""This class extends the BundleBuilder constructor with these
  	arguments:
! 
  		mainprogram: A Python main program. If this argument is given,
  			the main executable in the bundle will be a small wrapper
***************
*** 186,229 ****
  			be invoked by the wrapper program mentioned above. Else
  			it will simply be used as the main executable.
! 	
  	For the other keyword arguments see the BundleBuilder doc string.
  	"""
  
  	def __init__(self, name=None, mainprogram=None, executable=None,
! 			**kwargs):
  		"""See the class doc string for a description of the arguments."""
! 		if mainprogram is None and executable is None:
  			raise TypeError, ("must specify either or both of "
  					"'executable' and 'mainprogram'")
! 		if name is not None:
  			pass
! 		elif mainprogram is not None:
! 			name = os.path.splitext(os.path.basename(mainprogram))[0]
  		elif executable is not None:
! 			name = os.path.splitext(os.path.basename(executable))[0]
! 		if name[-4:] != ".app":
! 			name += ".app"
  
! 		self.mainprogram = mainprogram
! 		self.executable = executable
  
! 		BundleBuilder.__init__(self, name=name, **kwargs)
  
  	def preProcess(self):
! 		self.plist.CFBundleExecutable = self.name
  		if self.executable is not None:
  			if self.mainprogram is None:
  				execpath = pathjoin(self.execdir, self.name)
  			else:
! 				execpath = pathjoin(self.resdir, os.path.basename(self.executable))
  			self.files.append((self.executable, execpath))
  			# For execve wrapper
! 			executable = executableTemplate % os.path.basename(self.executable)
  		else:
! 			executable = ""  # XXX for locals() call
  
  		if self.mainprogram is not None:
  			mainname = os.path.basename(self.mainprogram)
! 			self.files.append((self.mainprogram, pathjoin(self.resdir, mainname)))
  			# Create execve wrapper
  			mainprogram = self.mainprogram  # XXX for locals() call
--- 211,267 ----
  			be invoked by the wrapper program mentioned above. Else
  			it will simply be used as the main executable.
! 		nibname: The name of the main nib, for Cocoa apps. Defaults
! 			to None, but must be specified when building a Cocoa app.
! 
  	For the other keyword arguments see the BundleBuilder doc string.
  	"""
  
  	def __init__(self, name=None, mainprogram=None, executable=None,
! 			nibname=None, **kwargs):
  		"""See the class doc string for a description of the arguments."""
! 		self.mainprogram = mainprogram
! 		self.executable = executable
! 		self.nibname = nibname
! 		BundleBuilder.__init__(self, name=name, **kwargs)
! 
! 	def setup(self):
! 		if self.mainprogram is None and self.executable is None:
  			raise TypeError, ("must specify either or both of "
  					"'executable' and 'mainprogram'")
! 
! 		if self.name is not None:
  			pass
! 		elif self.mainprogram is not None:
! 			self.name = os.path.splitext(os.path.basename(self.mainprogram))[0]
  		elif executable is not None:
! 			self.name = os.path.splitext(os.path.basename(self.executable))[0]
! 		if self.name[-4:] != ".app":
! 			self.name += ".app"
! 		self.plist.CFBundleExecutable = self.name
  
! 		if self.nibname:
! 			self.plist.NSMainNibFile = self.nibname
! 			if not hasattr(self.plist, "NSPrincipalClass"):
! 				self.plist.NSPrincipalClass = "NSApplication"
  
! 		BundleBuilder.setup(self)
  
  	def preProcess(self):
! 		resdir = pathjoin("Contents", "Resources")
  		if self.executable is not None:
  			if self.mainprogram is None:
  				execpath = pathjoin(self.execdir, self.name)
  			else:
! 				execpath = pathjoin(resdir, os.path.basename(self.executable))
  			self.files.append((self.executable, execpath))
  			# For execve wrapper
! 			setexecutable = setExecutableTemplate % os.path.basename(self.executable)
  		else:
! 			setexecutable = ""  # XXX for locals() call
  
  		if self.mainprogram is not None:
+ 			setpythonhome = ""  # pythonhomeSnippet if we're making a standalone app
  			mainname = os.path.basename(self.mainprogram)
! 			self.files.append((self.mainprogram, pathjoin(resdir, mainname)))
  			# Create execve wrapper
  			mainprogram = self.mainprogram  # XXX for locals() call
***************
*** 235,254 ****
  
  
- class CocoaAppBuilder(AppBuilder):
- 
- 	"""Tiny specialization of AppBuilder. It has an extra constructor
- 	argument called 'nibname' which defaults to 'MainMenu'. It will
- 	set the appropriate fields in the plist.
- 	"""
- 
- 	def __init__(self, nibname="MainMenu", **kwargs):
- 		"""See the class doc string for a description of the arguments."""
- 		self.nibname = nibname
- 		AppBuilder.__init__(self, **kwargs)
- 		self.plist.NSMainNibFile = self.nibname
- 		if not hasattr(self.plist, "NSPrincipalClass"):
- 			self.plist.NSPrincipalClass = "NSApplication"
- 
- 
  def copy(src, dst, mkdirs=0):
  	"""Copy a file or a directory."""
--- 273,276 ----
***************
*** 288,307 ****
  
  
! def buildapp(**kwargs):
! 	# XXX cmd line argument parsing
! 	builder = AppBuilder(**kwargs)
! 	builder.build()
  
  
! def buildcocoaapp(**kwargs):
! 	# XXX cmd line argument parsing
! 	builder = CocoaAppBuilder(**kwargs)
! 	builder.build()
  
  
  if __name__ == "__main__":
! 	# XXX This test is meant to be run in the Examples/TableModel/ folder
! 	# of the pyobj project... It will go as soon as I've written a proper
! 	# main program.
! 	buildcocoaapp(mainprogram="TableModel.py",
! 		resources=["English.lproj", "nibwrapper.py"], verbosity=4)
--- 310,404 ----
  
  
! cmdline_doc = """\
! Usage:
!   python [options] command
!   python mybuildscript.py [options] command
  
+ Commands:
+   build      build the application
+   report     print a report
  
! Options:
!   -b, --builddir=DIR     the build directory; defaults to "build"
!   -n, --name=NAME        application name
!   -r, --resource=FILE    extra file or folder to be copied to Resources
!   -e, --executable=FILE  the executable to be used
!   -m, --mainprogram=FILE the Python main program
!   -p, --plist=FILE       .plist file (default: generate one)
!       --nib=NAME         main nib name
!   -c, --creator=CCCC     4-char creator code (default: '????')
!   -l, --link             symlink files/folder instead of copying them
!   -v, --verbose          increase verbosity level
!   -q, --quiet            decrease verbosity level
!   -h, --help             print this message
! """
! 
! def usage(msg=None):
! 	if msg:
! 		print msg
! 	print cmdline_doc
! 	sys.exit(1)
! 
! def main(builder=None):
! 	if builder is None:
! 		builder = AppBuilder(verbosity=1)
! 
! 	shortopts = "b:n:r:e:m:c:plhvq"
! 	longopts = ("builddir=", "name=", "resource=", "executable=",
! 		"mainprogram=", "creator=", "nib=", "plist=", "link", "help",
! 		"verbose", "quiet")
! 
! 	try:
! 		options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
! 	except getopt.error:
! 		usage()
! 
! 	for opt, arg in options:
! 		if opt in ('-b', '--builddir'):
! 			builder.builddir = arg
! 		elif opt in ('-n', '--name'):
! 			builder.name = arg
! 		elif opt in ('-r', '--resource'):
! 			builder.resources.append(arg)
! 		elif opt in ('-e', '--executable'):
! 			builder.executable = arg
! 		elif opt in ('-m', '--mainprogram'):
! 			builder.mainprogram = arg
! 		elif opt in ('-c', '--creator'):
! 			builder.creator = arg
! 		elif opt == "--nib":
! 			builder.nibname = arg
! 		elif opt in ('-p', '--plist'):
! 			builder.plist = Plist.fromFile(arg)
! 		elif opt in ('-l', '--link'):
! 			builder.symlink = 1
! 		elif opt in ('-h', '--help'):
! 			usage()
! 		elif opt in ('-v', '--verbose'):
! 			builder.verbosity += 1
! 		elif opt in ('-q', '--quiet'):
! 			builder.verbosity -= 1
! 
! 	if len(args) != 1:
! 		usage("Must specify one command ('build', 'report' or 'help')")
! 	command = args[0]
! 
! 	if command == "build":
! 		builder.setup()
! 		builder.build()
! 	elif command == "report":
! 		builder.setup()
! 		builder.report()
! 	elif command == "help":
! 		usage()
! 	else:
! 		usage("Unknown command '%s'" % command)
! 
! 
! def buildapp(**kwargs):
! 	builder = AppBuilder(**kwargs)
! 	main(builder)
  
  
  if __name__ == "__main__":
! 	main()