Jack Jansen | 65fe8dd | 2002-11-06 23:15:51 +0000 | [diff] [blame^] | 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 | |