blob: 49a15137821d1185c650aeabf077de36975337b9 [file] [log] [blame]
Jack Jansen65fe8dd2002-11-06 23:15:51 +00001import sys
2import os
3import shutil
4import getopt
5
6def 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')
Jack Jansend892d4e2002-11-15 00:07:31 +0000104 if not os.path.isdir(resdir):
105 os.mkdir(resdir)
Jack Jansen65fe8dd2002-11-06 23:15:51 +0000106 for src in resources:
107 dst = os.path.join(resdir, os.path.split(src)[1])
108 if os.path.isdir(src):
109 shutil.copytree(src, dst)
110 else:
111 shutil.copy2(src, dst)
112
113def usage():
114 print "buildappbundle creates an application bundle"
115 print "Usage:"
116 print " buildappbundle [options] executable"
117 print "Options:"
118 print " --output o Output file; default executable with .app appended, short -o"
119 print " --link Symlink the executable (default: copy), short -l"
120 print " --plist file Plist file (default: generate one), short -p"
121 print " --nib file Main nib file or lproj folder for Cocoa program, short -n"
122 print " --resource r Extra resource file to be copied to Resources, short -r"
123 print " --creator c 4-char creator code (default: ????), short -c"
124 print " --help This message, short -?"
125 sys.exit(1)
126
127def main():
128 output=None
129 copyfunc=None
130 creator=None
131 plist=None
132 nib=None
133 resources=[]
134 SHORTOPTS = "o:ln:r:p:c:?"
135 LONGOPTS=("output=", "link", "nib=", "resource=", "plist=", "creator=", "help")
136 try:
137 options, args = getopt.getopt(sys.argv[1:], SHORTOPTS, LONGOPTS)
138 except getopt.error:
139 usage()
140 if len(args) != 1:
141 usage()
142 for opt, arg in options:
143 if opt in ('-o', '--output'):
144 output = arg
145 elif opt in ('-l', '--link'):
146 copyfunc = os.symlink
147 elif opt in ('-n', '--nib'):
148 nib = arg
149 elif opt in ('-r', '--resource'):
150 resources.append(arg)
151 elif opt in ('-c', '--creator'):
152 creator = arg
153 elif opt in ('-p', '--plist'):
154 plist = arg
155 elif opt in ('-?', '--help'):
156 usage()
157 buildappbundle(args[0], output=output, copyfunc=copyfunc, creator=creator,
158 plist=plist, resources=resources)
159
160if __name__ == '__main__':
161 main()
162