#!/usr/bin/python # -*- coding: UTF-8 -*- ## # drop_script # DropScript droplet generation script # # Created by Wilfredo Sánchez on Sun May 02 2004. # Copyright (c) 2004 Wilfredo Sánchez Vega. All rights reserved. ## # FIXME: Bob suggests I use plistlib import sys import os import re import shutil debug = False ## # Handle command line ## source_script = sys.argv[0] if len(sys.argv) != 2: print "%s takes exactly one argument" % (os.path.basename(source_script)) sys.exit(1) new_script = sys.argv[1] source_app = os.path.dirname(os.path.dirname(os.path.dirname(source_script))) source_name = os.path.splitext(os.path.basename(source_app))[0] destination = os.path.dirname(new_script) base_name = os.path.basename(os.path.splitext(new_script)[0]) droplet_name = "Drop" + base_name droplet_path = os.path.join(destination, droplet_name + ".app") droplet_contents = os.path.join(droplet_path, "Contents") droplet_bindir = os.path.join(droplet_contents, "MacOS") droplet_executable = os.path.join(droplet_bindir, droplet_name) droplet_resources = os.path.join(droplet_contents, "Resources") droplet_script = os.path.join(droplet_resources, "drop_script") droplet_plist = os.path.join(droplet_contents, "Info.plist") i = 0 while (os.path.exists(droplet_path)): i += 1 droplet_name = "Drop" + base_name + "-" + str(i) droplet_path = os.path.join(destination, droplet_name + ".app") ## # Functions ## def parse_script_options(script_filename): """ Read the specified script and pull out DropScript options. Options are specified on a line that begins with '# ' as the first two characters, followed by the option name, followed by ':', followed by the option value. Note that this assumes that one can do this without breaking the syntax of the script. Since most scripting languages use '#' as to denote a comment, this generally seems like a reasonable choice. See the DropScript docs for information about avaliable options. """ options = {} regex_option = re.compile(r'^#[ \t]*([A-Z]+)[ \t]*:[ \t]*(.*)$') script_file = open(script_filename) regex_comment = r'([ \t]*#.*)?$' for line in script_file: match = regex_option.search(line) if match: option = match.group(1) value = match.group(2) warning = "%s: WARNING: Invalid %s: %r" % (source_script, option, value) if option == "EXTENSIONS" or option == "OSTYPES": regex_extensions = re.compile(r'^("[^"]*")+' + regex_comment) if regex_extensions.search(value): extensions = regex_extensions.sub(r'\1', value) options[option] = extensions elif re.search(regex_comment, value): pass else: print warning elif option == "ROLE": match = re.search(r'^([A-Za-z]+)' + regex_comment, value) if match: options[option] = match.group(1) else: print warning elif option == "SERVICEMENU": match = re.search(r'^(.+?)' + regex_comment, value) if match: options[option] = match.group(1) else: print warning script_file.close() if not ("ROLE" in options and options["ROLE"]): options["ROLE"] = "Editor" if not ("SERVICEMENU" in options and options["SERVICEMENU"]): options["SERVICEMENU"] = "DropScript/" + base_name if (not ("EXTENSIONS" in options and options["EXTENSIONS"]) and not ("OSTYPES" in options and options["OSTYPES" ]) ): options["EXTENSIONS"] = '"*"' options["OSTYPES" ] = '"****"' else: if not "EXTENSIONS" in options: options["EXTENSIONS"] = "" if not "OSTYPES" in options: options["OSTYPES" ] = "" return options def write_plist(plist_filename, options): """ Write an Info.plist file with the given options. """ def expand_tokens(tokens): """ Replace: "x" "y" ... With: x y ... """ result = "" state = False for c in tokens: if c == '"': if state: result = result + "" else: result = result + "" state = not state else: result = result + c if state: raise RuntimeError("mismatched quotes in token list") return result plist_file = open(plist_filename, "w") plist_file.write(""" CFBundleDevelopmentRegion English CFBundleDocumentTypes CFBundleTypeExtensions """ + expand_tokens(options["EXTENSIONS"]) + """ CFBundleTypeName NSStringPboardType CFBundleTypeOSTypes """ + expand_tokens(options["OSTYPES"]) + """ CFBundleTypeRole """ + options["ROLE"] + """ CFBundleExecutable """ + droplet_name + """ CFBundleIconFile DropScript.icns CFBundleIdentifier net.wsanchez.DropScript.""" + droplet_name + """ CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 0.0 NSMainNibFile MainMenu NSPrincipalClass NSApplication NSServices NSMenuItem default """ + options["SERVICEMENU"] + """ NSMessage dropService NSPortName DropScript NSSendTypes NSFilenamesPboardType """) plist_file.close() ## # Do The Right Thing ## try: if debug: print "source_name: %s" % source_name if debug: print "source_app: %s" % source_app if debug: print "source_script: %s" % source_script if debug: print "new_script: %s" % new_script if debug: print "destination: %s" % destination if debug: print "droplet_name: %s" % droplet_name if debug: print "droplet_path: %s" % droplet_path if debug: print "droplet_script: %s" % droplet_script if debug: print "droplet_plist: %s" % droplet_plist if debug: print "droplet_executable: %s" % droplet_executable # Get application options from script options = parse_script_options(new_script) if debug: print "options: %s" % options # Copy the primordial applet to the new applet's location shutil.copytree(source_app, droplet_path) # Move Contents/MacOS/DropScript to Contents/MacOS/ if debug: print "Moving %s to %s..." % (os.path.join(droplet_bindir, source_name), droplet_executable) os.rename(os.path.join(droplet_bindir, source_name), droplet_executable) # Replace Contents/Resources/drop_script if debug: print "Removing %s..." % droplet_script os.remove(droplet_script) if debug: print "Copying %s to %s..." % (new_script, droplet_script) shutil.copyfile(new_script, droplet_script) os.chmod(droplet_script, 0755) # Edit Info.plist if debug: print "Editing plist %s..." % droplet_plist write_plist(droplet_plist, options) if debug: print "Created new drop application %s." % droplet_path except Exception, e: raise # Delete new droplet on error pass # Show error panel on error pass