1+ import sys
2+ import os
3+ import shutil
4+ import getopt
5+
6+ def buildappbundle (executable , output = None , copyfunc = None , creator = None ,
7+ plist = None , nib = None , resources = None ):
8+ if not output :
9+ output = os .path .split (executable )[1 ] + '.app'
10+ if not copyfunc :
11+ copyfunc = shutil .copy2
12+ if not creator :
13+ creator = '????'
14+ if not resources :
15+ resources = []
16+ if nib :
17+ resources = resources + [nib ]
18+ #
19+ # Create the main directory structure
20+ #
21+ if not os .path .isdir (output ):
22+ os .mkdir (output )
23+ contents = os .path .join (output , 'Contents' )
24+ if not os .path .isdir (contents ):
25+ os .mkdir (contents )
26+ macos = os .path .join (contents , 'MacOS' )
27+ if not os .path .isdir (macos ):
28+ os .mkdir (macos )
29+ #
30+ # Create the executable
31+ #
32+ shortname = os .path .split (executable )[1 ]
33+ execname = os .path .join (macos , shortname )
34+ try :
35+ os .remove (execname )
36+ except OSError :
37+ pass
38+ copyfunc (executable , execname )
39+ #
40+ # Create the PkgInfo file
41+ #
42+ pkginfo = os .path .join (contents , 'PkgInfo' )
43+ open (pkginfo , 'wb' ).write ('APPL' + creator )
44+ if plist :
45+ # A plist file is specified. Read it.
46+ plistdata = open (plist ).read ()
47+ else :
48+ #
49+ # If we have a main NIB we create the extra Cocoa specific info for the plist file
50+ #
51+ if not nib :
52+ nibname = ""
53+ else :
54+ nibname , ext = os .path .splitext (os .path .split (nib )[1 ])
55+ if ext == '.lproj' :
56+ # Special case: if the main nib is a .lproj we assum a directory
57+ # and use the first nib from there
58+ files = os .listdir (nib )
59+ for f in files :
60+ if f [- 4 :] == '.nib' :
61+ nibname = os .path .split (f )[1 ][:- 4 ]
62+ break
63+ else :
64+ nibname = ""
65+ if nibname :
66+ cocoainfo = """
67+ <key>NSMainNibFile</key>
68+ <string>%s</string>
69+ <key>NSPrincipalClass</key>
70+ <string>NSApplication</string>""" % nibname
71+ else :
72+ cocoainfo = ""
73+ plistdata = \
74+ """<?xml version="1.0" encoding="UTF-8"?>
75+ <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
76+ <plist version="0.9">
77+ <dict>
78+ <key>CFBundleDevelopmentRegion</key>
79+ <string>English</string>
80+ <key>CFBundleExecutable</key>
81+ <string>%s</string>
82+ <key>CFBundleInfoDictionaryVersion</key>
83+ <string>6.0</string>
84+ <key>CFBundlePackageType</key>
85+ <string>APPL</string>
86+ <key>CFBundleSignature</key>
87+ <string>%s</string>
88+ <key>CFBundleVersion</key>
89+ <string>0.1</string>
90+ %s
91+ </dict>
92+ </plist>
93+ """ % (shortname , creator , cocoainfo )
94+ #
95+ # Next, we create the plist file
96+ #
97+ infoplist = os .path .join (contents , 'Info.plist' )
98+ open (infoplist , 'w' ).write (plistdata )
99+ #
100+ # Finally, if there are nibs or other resources to copy we do so.
101+ #
102+ if resources :
103+ resdir = os .path .join (contents , 'Resources' )
104+ for src in resources :
105+ dst = os .path .join (resdir , os .path .split (src )[1 ])
106+ if os .path .isdir (src ):
107+ shutil .copytree (src , dst )
108+ else :
109+ shutil .copy2 (src , dst )
110+
111+ def usage ():
112+ print "buildappbundle creates an application bundle"
113+ print "Usage:"
114+ print " buildappbundle [options] executable"
115+ print "Options:"
116+ print " --output o Output file; default executable with .app appended, short -o"
117+ print " --link Symlink the executable (default: copy), short -l"
118+ print " --plist file Plist file (default: generate one), short -p"
119+ print " --nib file Main nib file or lproj folder for Cocoa program, short -n"
120+ print " --resource r Extra resource file to be copied to Resources, short -r"
121+ print " --creator c 4-char creator code (default: ????), short -c"
122+ print " --help This message, short -?"
123+ sys .exit (1 )
124+
125+ def main ():
126+ output = None
127+ copyfunc = None
128+ creator = None
129+ plist = None
130+ nib = None
131+ resources = []
132+ SHORTOPTS = "o:ln:r:p:c:?"
133+ LONGOPTS = ("output=" , "link" , "nib=" , "resource=" , "plist=" , "creator=" , "help" )
134+ try :
135+ options , args = getopt .getopt (sys .argv [1 :], SHORTOPTS , LONGOPTS )
136+ except getopt .error :
137+ usage ()
138+ if len (args ) != 1 :
139+ usage ()
140+ for opt , arg in options :
141+ if opt in ('-o' , '--output' ):
142+ output = arg
143+ elif opt in ('-l' , '--link' ):
144+ copyfunc = os .symlink
145+ elif opt in ('-n' , '--nib' ):
146+ nib = arg
147+ elif opt in ('-r' , '--resource' ):
148+ resources .append (arg )
149+ elif opt in ('-c' , '--creator' ):
150+ creator = arg
151+ elif opt in ('-p' , '--plist' ):
152+ plist = arg
153+ elif opt in ('-?' , '--help' ):
154+ usage ()
155+ buildappbundle (args [0 ], output = output , copyfunc = copyfunc , creator = creator ,
156+ plist = plist , resources = resources )
157+
158+ if __name__ == '__main__' :
159+ main ()
160+
0 commit comments