#!/usr/bin/env python '''\ wmlmove -- generate commands to move a unit in the Wesnoth tree usage: wmlmove --help wmlmove [--revert] {src...} {dst} wmlmove --delete src This script facilitates moving or deleting units (referenced by their .cfg file) along with all of their associated resources (images and sounds). It may be run from the Wesnoth top-level directory or anywhere beneath. For purposes of this command, a "namespace identifier" is either the basename of a campaign subdirectory or the special name "core", indicating the units, images and sounds directories in data/core. A source must be a namespace identifier, followed by "::", followed by the name of a unit (the basename of its path without the .cfg extwension). A destination must be a namespace identifier. The script generates a sequence of shell commands. These commands will move the unit and all its resources to appropriate places under the target campaign or core directories. Subversion move commands will be used on version-controlled files. A resource is considered to belong to a unit (and moves with it) if the resource is included in the unit .cfg and leives near the unit .cfg. "Near" for a unit in the core data means anywhere in the core data, but not in a campaign directory. "Near" for a unit under a data/campaign directory means anywhere in that same campaign directory, but not in core data or any other campaign directory. The --revert option generates commands used to revert the result of a specified move, undoing version-control operations as needed. The --delete option removes a unit and its associated resources. The --listspaces option lists all namespace identifiers. The --imageclass option may be used to insert a directory path segment into each destination filename just before the basename. This is useful for moves to core; you typically will need to say (for example) --imageclass monsters or --imageclass undead to get images associated with a campaign to the right subdirectory of core/images/units. Note: After performing a move generated with this command, use wmlscope to check that you have not left any references to the moved units dangling. Also be aware that this tool does nothing to update the unit translation files. Here is an example: wmlmove --imageclass=elves-wood Heir_To_The_Throne::Elvish_Lord Heir_To_The_Throne::Elvish_High_Lord Heir_To_The_Throne::Elvish_Lady core ''' import sys, os, time, re, getopt, sre_constants, md5 from wesnoth.wmltools import * if __name__ == "__main__": # Process options. (options, arguments) = getopt.getopt(sys.argv[1:], "dhi:lLr", ['delete', 'help', 'imageclass=', 'list', 'listspaces', 'revert']) listem = False listspaces = False iclass = None delete = False revert = False for (switch, val) in options: if switch in ('-h', '--help'): sys.stderr.write(__doc__) sys.exit(0) elif switch in ('-d', '--delete'): delete = True elif switch in ('-i', '--imageclass'): iclass = val elif switch in ('-l', '--list'): listem = True elif switch in ('-L', '--listspaces'): listspaces = True elif switch in ('-r', '--revert'): listem = True if not listspaces: if len(arguments) == 0: sys.stderr.write("wmlmove: at least one path to a unit is required.\n") sys.stderr.write(__doc__) sys.exit(1) if not delete: if len(arguments) == 1: sys.stderr.write("wmlmove: a campaign name or 'core' is required.\n") sys.stderr.write(__doc__) sys.exit(1) else: dst = arguments.pop() # First, pop upward to the top-level directory. upwards = os.getcwd().split(os.sep) upwards.reverse() for pathpart in upwards: if pathpart == "wesnoth": break else: os.chdir("..") else: sys.stderr.write("wmlmove: must be run from within a Battle " "for Wesnoth source tree.\n") sys.exit(1) if listspaces: print "\n".join(map(os.path.basename, scopelist())) sys.exit(0) # Locate the unit .cfgs to be moved. srclist = [] for src in arguments: try: (namespace, resource) = src.split("::") except ValueError: sys.stderr.write( "wmlmove: source name %s not in the form namespace::resource.\n" % src) sys.exit(1) if not is_namespace(namespace): sys.stderr.write("wmlmove: no such scope as %s.\n" % namespace) sys.exit(1) src = resolve_unit_cfg(namespace, resource) if not os.path.exists(src): sys.stderr.write("wmlmove: can't find %s to move it.\n" % src) sys.exit(1) srclist.append(src) # Validate the destination. if not delete: dstdir = namespace_directory(dst) if dstdir == None: sys.stderr.write("wmlmove: invalid namespace %s\n" % dstdir) sys.exit(1) # Cross-reference all files. cref = CrossRef(scopelist()) # Filter reference information on all files referenced in the source .cfgs srcrefs = cref.subtract(srclist) # List all relevant resources and their other references. if listem: for (name, defloc) in srcrefs.fileref.items(): print "Resource %s is used in %d files:" % \ (defloc, len(defloc.references)) defloc.dump_references() # Generate the actual move commands print "# Generated script from", " ".join(sys.argv[1:]) if not delete and not revert: print ''' replace() # Replace a specified string with another in any number of files { left="$1"; right="$2"; shift; shift; for file in $* do if grep "$left" $file >/dev/null 2>&1 then overwrite $file sed "s@$left@$right@g" $file fi done } overwrite() # Replace a file with itself filtered by a command { opath=$PATH PATH=/bin:/usr/bin file=$1; shift new=/tmp/over$$; old=/tmp/under$$ trap \'rm -f $new $old ; exit 1\' 1 2 15 if PATH=$opath "$@" >$new then cp $file $old # save the old file trap "" 1 2 15 # We are committed; ignore signals cp $new $file else echo "overwrite: $1 failed, $file unchanged" 1 >&2 exit 1 fi rm -f $new $old } ''' print "chdir", os.getcwd() if delete: print "# Image deletions:" for (name, defloc) in srcrefs.fileref.items(): for namespace in map(directory_namespace, srclist): if namespace_member(name, namespace) and cref.refcount(name) == 0: if revert: print vcundelete(name) else: print vcdelete(name) break print "" print "# .cfg deletions" for filename in srclist: if revert: print vcundelete(filename) else: print vcdelete(filename) else: if iclass == None: print "# Defaulting image subclass to 'monsters', use -i to set it." iclass = 'monsters' print "# Image moves:" for (name, defloc) in srcrefs.fileref.items(): source = directory_namespace(name) target = resolve_unit_image(dst, iclass, os.path.basename(name)) if revert: if not namespace_member(name, source): print vcunmove(name, target) else: if not namespace_member(name, dst): print vcmove(name, target) print "" print "# .cfg moves and transformations" for filename in srclist: source = directory_namespace(filename) target = resolve_unit_cfg(dst, os.path.basename(filename)) if revert: if not namespace_member(filename, source): print vcunmove(filename, target) else: if not namespace_member(filename, dst): print vcmove(filename, target) if iclass: print "replace 'units/' 'units/%s/' %s" % (iclass, target) if dst == "core": print "replace '#textdomain wesnoth.*' '#textdomain wesnoth' %s" % target else: print "echo 'Warning: text domain will require manual change.'" # wmlmove ends here