mtd/scripts modify.py, NONE, 1.1 patchin.xml, NONE, 1.1 treewalk.py, NONE, 1.1

gleixner at infradead.org gleixner at infradead.org
Sun Mar 13 13:09:09 EST 2005


Update of /home/cvs/mtd/scripts
In directory phoenix.infradead.org:/tmp/cvs-serv4912

Added Files:
	modify.py patchin.xml treewalk.py 
Log Message:
Reworked kernel patch script. The XML config file works on 2.6 kernels only!
It should have the same functionality as the original patchin.sh script, but
provides more flexibility. The treewalk script can handle arbitrary actions
over a source tree. A rudimentary documentation of the XML entities can be
found in patchin.xml.



--- NEW FILE modify.py ---
#!/usr/bin/env python
#
# Modify a file. Insert part of a file into another file.
# Patterns are used to find the place where to insert / replace
# 
# (C) 2005 Thomas Gleixner <tglx at linutronix.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#

import os
import sys
import getopt
import pprint
import shutil
import string
import smtplib
import socket
import time
import xml.sax
import commands
import re

# patterns
patterns = []

# insert mode
insert = 0

# Print the usage information
def usage(res):
	print "USAGE:"
	print "modify.py <-o options srcfile dstfile>"
	print "  -i insert if srcpattern not exists, otherwise do nothing"
	print "  -p search patterns seperated by ','"
	print "     srcstart,srcend,dststart,dstend"
	sys.exit(res)

def findLines(lines, start, end):
	found = 0
	startline = -1
	linecnt = 0
	
	for line in lines:
		linecnt = linecnt + 1
		if startline < 0:
			if line.find(start) < 0:
				continue
			startline = linecnt

		if line.find(end) >= 0:
			break

	return startline - 1, linecnt - 1	

# Here we go
# Parse the commandline
try:
	(options, arguments) = getopt.getopt(sys.argv[1:],'hip:')
except getopt.GetoptError, ex:
	print
	print "ERROR:"
	print ex.msg
	usage(1)

for option, value in options:
	if option == "-i":
		insert = 1
	elif option == "-p":
		patterns = value.split(',')
	elif option == '-h':
		usage(0)

if len(arguments) != 2:
	usage(1)

# Get filenames
srcfile = arguments[0]
dstfile = arguments[1]

if not os.path.isfile(srcfile):
	print "%s does not exist" %(srcfile)
	sys.exit(1)

if not os.path.isfile(dstfile):
	print "%s does not exist" %(dstfile)
	sys.exit(1)

fd = open(srcfile, 'r')
srclines = fd.readlines()
fd.close()
fd = open(dstfile, 'r')
dstlines = fd.readlines()
fd.close()

# Find source file pattern
(srcstart, srcend) = findLines(srclines,
			       patterns[0], patterns[1])
if srcstart < 0:
	print "Search pattern %s in %s does not exist" %(patterns[0], srcfile)
	sys.exit(1)

# Insert mode. Check whether the section exists or not
if insert > 0:
	(dststart, dstend) = findLines(dstlines,
			       patterns[0], patterns[1])
	if dststart >= 0:
		sys.exit(0)

# Find target file pattern
(dststart, dstend) = findLines(dstlines,
			       patterns[2], patterns[3])

if dststart < 0:
	if insert == 1:
		print "Search pattern %s in %s does not exist" %(patterns[2], dstfile)
	sys.exit(insert)

# Insert / modify the sections
if dststart == 0:
	dststart = 1
newlines = dstlines[0:dststart]
if srcstart == srcend:
	srcend = srcend + 1
newlines = newlines + srclines[srcstart:srcend]
newlines = newlines + dstlines[dstend:]

# write back into destination file
fd = open(dstfile, 'w')
dstlines = fd.writelines(newlines)
fd.close()

--- NEW FILE patchin.xml ---
<!-- Selection description for patchin.py 

<OPTION name="jffs2" help="Include JFFS2" />
name: Unique option name

help: Helptext for this option

Define commandline options for patchin.py
The options are given with -o opt1,opt2,opt3

<CHECKTARGET file="Makefile" pattern="PATCHLEVEL = 6" 
help="Linux-Kernel 2.6" />
file:    Filename to check in the target directory

pattern: Pattern to search in the file

help:    Help text in case of error

The targetcheck(s) are processed before the update. If
one of the checks fails, the processing is stopped.

<ACTION name="bk" depends="bk" except="rm" location="dest">
	rm -f $destfiles;
	bk co -ql $destfiles;
</ACTION>
name:      Unique action name

depends:   Dependency on option(s) seperated by ','

except:    Except on option(s) seperated by ','

location:  Either 'src' for source path or 'dest' for destination path
           Default is src

mode:      Either 'single' or 'all'. Default is single
           If mode == single, then the action is called 
           for each file seperately and the $*files
           arguments contain only a single file

content:   The action shell command(s) seperated by ';'
           Exported variables: 

           $srcfiles  File(s) selected by update patterns in src path

           $dstfiles  File(s) selected by update patterns in dest path
               
           $srcpath   Source path

           $dstpath   Destination path

           $options   Options given in the selection pattern

<SUBDIR name="include" actions="cp" depends="jffs2" except="bk" recurse="no">
name:      relative path name of the subdirectory

actions:   actions in this subdir. If omitted defaults to all !

depends:   Dependency on option(s) seperated by ','

except:    Except on option(s) seperated by ','

recurse:   Recurse through subdirs. Either yes nor no. Default is no

<UPDATE pattern="*.h" depends="jffs2" except="bk" options="bla" />
<UPDATE pattern="Makefile.common" target="Makefile" />
<EXCLUDE pattern="jffs2-user.h" except="jffs2" depends="bk" />
pattern:   single filename or regular expression

target:    single filename or regular expression

depends:   Dependency on option(s) seperated by ','

except:    Except on option(s) seperated by ','

options:   Options for the action
</SUBDIR>

If depends contains more than one option, then all options must be 
set to make the rule valid (logical AND)
If except contains more than one option, then one option set make 
the rule valid (logical OR)

-->

<PATCHROOT name="mtd">
	<OPTION name="jffs2" help="Include JFFS2" />
	<OPTION name="jffs3" help="Include JFFS3" />
	<OPTION name="bk" help="Bitkeeper checkout" />
	<OPTION name="cp" help="copy into target tree" />
	<OPTION name="ln" help="link into target tree" />
	<OPTION name="rm" help="remove the patched files" />

	<CHECKTARGET file="Makefile" pattern="PATCHLEVEL = 6" 
		help="Linux-Kernel 2.6" />

	<ACTION name="bk" depends="bk" except="rm" mode="all">
		cd $dstpath;
		rm -f $dstfiles;
		bk co -ql $dstfiles
	</ACTION>

	<ACTION name="cp" depends="cp" except="rm,ln">
		cp -f $srcpath/$srcfiles $dstpath/$dstfiles
	</ACTION>

	<ACTION name="rm" depends="rm" except="cp,ln">
		rm -f $dstpath/$dstfiles
	</ACTION>

	<ACTION name="ln" depends="ln" except="cp,rm">
		rm -f $dstpath/$dstfiles;
		ln -s $srcpath/$srcfiles $dstpath/$dstfiles
	</ACTION>

	<ACTION name="mkdir" except="rm" mode="all">
		mkdir -p $dstpath
	</ACTION>

	<ACTION name="modify" except="rm">
		scripts/modify.py -p $options $srcpath/$srcfiles $dstpath/$dstfiles
	</ACTION>

	<ACTION name="insert" except="rm">
		scripts/modify.py -i -p $options $srcpath/$srcfiles $dstpath/$dstfiles
	</ACTION>

	<ACTIONORDER order="bk,rm,cp,ln" />

	<SUBDIR name="Documentation/DocBook">
		<UPDATE pattern=".*\.tmpl$" />
	</SUBDIR>
	<SUBDIR name="include/mtd">
		<UPDATE pattern=".*\.h$" />
		<EXCLUDE pattern="jffs2-user.h$" except="jffs2" />
	</SUBDIR>
	<SUBDIR name="include/linux" depends="jffs2">
		<UPDATE pattern="jffs2*\.h$" />
	</SUBDIR>
	<SUBDIR name="include/linux" depends="jffs3">
		<UPDATE pattern="jffs3*\.h$" />
	</SUBDIR>
	<SUBDIR name="include/linux/mtd">
		<UPDATE pattern=".*\.h$" />
	</SUBDIR>
	<SUBDIR name="fs" actions="bk,modify">
		<UPDATE pattern="Kconfig" depends="jffs2" 
			options="JFFS2,JFFS3,JFFS2,CRAMFS" />
		<UPDATE pattern="Kconfig" depends="jffs3" 
			options="JFFS3,CRAMFS,JFFS3,CRAMFS" />
	</SUBDIR>
	<SUBDIR name="fs" actions="bk,insert" depends="jffs3">
		<UPDATE pattern="Makefile.jffs3" target="Makefile"
			depends="jffs3" options="JFFS3,JFFS3,JFFS2,JFFS2" />
		<UPDATE pattern="Kconfig" depends="jffs3" 
			options="JFFS3,CRAMFS,CRAMFS,CRAMFS" />
	</SUBDIR>
	<SUBDIR name="fs/jffs2" depends="jffs2">
		<UPDATE pattern=".*\.[ch]$" />
		<UPDATE pattern="Makefile.common" target="Makefile" />
		<EXCLUDE pattern=".*v24.c$" />
	</SUBDIR>
	<SUBDIR name="fs/jffs3" actions="mkdir,rm,cp,ln" depends="jffs3">
		<UPDATE pattern=".*\.[ch]$" />
		<UPDATE pattern="Makefile.common" target="Makefile" />
		<EXCLUDE pattern=".*v24.c$" />
	</SUBDIR>
	<SUBDIR name="drivers/mtd" recurse="yes">
		<UPDATE pattern=".*\.[ch]$" />
		<UPDATE pattern="Kconfig" />
		<UPDATE pattern="Makefile.common" target="Makefile" />
		<EXCLUDE pattern=".*24.c" />
	</SUBDIR>
</PATCHROOT>

--- NEW FILE treewalk.py ---
#!/usr/bin/env python
#
# XML configurable tree walking 
#  
# See treewall.xml for examples and XML entities doc
#
# (C) 2005 Thomas Gleixner <tglx at linutronix.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#

import os
import sys
import getopt
import pprint
import shutil
import string
import smtplib
import socket
import time
import xml.sax
import commands
import re

# Configuration file
config_file = "treewalk.xml"
# Interactive mode
interactive = 0
# Verbose
verbose = 0

# Commandline options
cmdline_actions = []
cmdline_actdict = {}

# Configuration file options with help texts
config_dict = {}

# Actions
actions = {}
default_actionorder = []
default_actions = []

# Subdirectories and subconfiguration
subdirs = []

# Target directory validation check
targetchecks = []

# Print the usage information
def usage(res):
	print "USAGE:"
	print "treewalk.py <-f config -h -i -a actions -v path>"
	print "  -a actions actions depending on config file seperated by ','"
	print "  -f config  configuration file. Default is treewalk.xml"
	print "  -i         interactive mode"
	print "  -v lvl     verbosity level"

	if len(config_dict):
		print "Valid options:"
		for cfg in config_dict.keys():
			print cfg + "\t" + config_dict[cfg]
	sys.exit(res)

def verbose_print(lvl, line):
	if verbose >= lvl:
		print line

# Except/depends storage class
class treeApplies:
	def __init__(self, excepts, depends):
		if excepts:
			self.excepts = excepts.split(',')
		else:
			self.excepts = []
		if depends:
			self.depends = depends.split(',')
		else:
			self.depends = []

	def validateOptions(self,dict):
		for e in self.excepts:
			if dict.has_key(e):
				return 0
			
		for e in self.depends:
			if not dict.has_key(e):
				return 0
		return 1

# Action storage class
class treeAction:
	def __init__(self, name, action, excepts, depends, mode):

		self.name = name
		self.action = action
		self.applies = treeApplies(excepts, depends)
		self.mode = (mode and mode == 'all')
		self.active = 1
		return

	# Validate the commandline options with except/depend
	def validateOptions(self,options):
		self.active = self.applies.validateOptions(options)
		return self.active

	def getName(self):
		return self.name
	
	# Set the action
	def setAction(self, action):
		self.action = action.replace("\n", "")

	# Is action active ?
	def isActive(self):
		return self.active
			
	# Get the action for a subdirectory
	def getAction(self, subdir):
		return self.action

	# Get the mode configuration
	def getMode(self):
		return self.mode

	# Run the action
	def run(self, cmd):
		retval = commands.getstatusoutput(cmd)
		if retval[0] != 0:
			sys.stderr.write("Error processing command\n")
			sys.stderr.write(cmd)
			sys.stderr.write('\n')
			sys.stderr.write(retval[1])
			sys.stderr.write('\n')
			sys.exit(1)

	# Process the action
	def process(self, srcpath, dstpath, srcfiles, dstfiles, options):
		cmd = "export srcpath=\'%s\';" %(srcpath)
		cmd = cmd + "export dstpath=\'%s\';" %(dstpath)
		cmd = cmd + "export options=\'%s\';" %(options)
		# All files mode ?
		if self.mode:
			cmd = cmd + "export srcfiles=\'"
			for file in srcfiles:
				cmd = cmd + " %s" %(file)
			cmd = cmd + "\';"
			cmd = cmd + "export dstfiles=\'"
			for file in dstfiles:
				cmd = cmd + " %s" %(file)
			cmd = cmd + "\';"
 			cmd = cmd + self.action
			self.run(cmd)
			return
		cnt = 0
		while cnt < len(srcfiles):
			fcmd = cmd + "export srcfiles=\'%s\';" %(srcfiles[cnt])
			fcmd = fcmd + "export dstfiles=\'%s\';" %(dstfiles[cnt])
 			fcmd = fcmd + self.action
			self.run(fcmd)
			cnt = cnt + 1

# Subdirs storage class
class treeSubdir:
	def __init__(self, name, excepts, depends, actions, recurse):
		self.name = name
		self.applies = treeApplies(excepts, depends)
		self.active = 1
		self.update = []
		self.exclude = []
		if actions:
			self.actions = actions.split(',')
		else:
			self.actions = []
		self.recurse = (recurse and recurse == 'yes')

	# Validate the commandline options with except/depend
	def validateOptions(self,options):
		self.active = self.applies.validateOptions(options)
		for pat in self.update:
			pat.validateOptions(options)
		for pat in self.exclude:
			pat.validateOptions(options)
		return self.active

	# Add an update pattern
	def addUpdate(self,pattern):
		self.update[len(self.update):] = [pattern]

	# Add an exclude pattern
	def addExclude(self,pattern):
		self.exclude[len(self.exclude):] = [pattern]

	# Get update patterns
	def getUpdates(self):
		return self.update

	# Get exclude patterns
	def getExcludes(self):
		return self.exclude

	# Get actions
	def getActions(self):
		return self.actions

	# Is dir active ?
	def isActive(self):
		return self.active

	# Get the directory name
	def getDirname(self):
		return self.name

	# Get recursion info
	def isRecursive(self):
		return self.recurse

# Storage class for file patterns
class treePattern:
	def __init__(self, pattern, target, excepts, depends, options):
		self.pattern = pattern
		self.target = target
		self.applies = treeApplies(excepts, depends)
		self.options = options
		self.active = 1

	# Validate the commandline options with except/depend
	def validateOptions(self,options):
		self.active = self.applies.validateOptions(options)
		return self.active

	# Update pattern active ?
	def isActive(self):
		return self.active

	# Get update pattern
	def getPattern(self):
		return self.pattern

	# Get target pattern
	def getTarget(self):
		return self.target

	# Get options
	def getOptions(self):
		return self.options

# configuration parser
class docHandler(xml.sax.ContentHandler):

	def __init__(self):
		self.subdir = -1
		self.action = ""
		return
    
	def startElement(self, name, attrs):
		if name == "OPTION":
			config_dict[attrs.get('name')] = attrs.get('help')

		elif name == "ACTION":
			act = attrs.get('name')
			self.action = act
			actions[act] = treeAction(self.action,
						    "",
						    attrs.get('except'),
						    attrs.get('depends'),
						    attrs.get('mode'))
		elif name == "ACTIONORDER":
			globals()['default_actionorder'] = attrs.get('order').split(',')
			
		elif name == "SUBDIR":
			i = len(subdirs)
			subdirs[i:] = [treeSubdir(attrs.get('name'),
						  attrs.get('except'),
						  attrs.get('depends'),
						  attrs.get('actions'),
						  attrs.get('recurse'))]
			self.subdir = i
			
		elif name == 'UPDATE':
			i = self.subdir
			subdirs[i].addUpdate(
				treePattern(attrs.get('pattern'),
					     attrs.get('target'),
					     attrs.get('except'),
					     attrs.get('depends'),
					     attrs.get('options')))
					     
		elif name == 'EXCLUDE':
			i = self.subdir
			subdirs[i].addExclude(
				treePattern(attrs.get('pattern'),
					     attrs.get('target'),
					     attrs.get('except'),
					     attrs.get('depends'),
					     attrs.get('options')))
		elif name == 'CHECKTARGET':
			i = len(targetchecks)
			targetchecks[i:] = [
				treePattern(attrs.get('file'),
					     attrs.get('pattern'),
					     attrs.get('except'),
					     attrs.get('depends'),
					     attrs.get('help'))]

		self.element = name
		self.content = ""

	def characters(self, ch):
		self.content = self.content + ch

	def endElement(self, name):
		if name == 'OPTION':
			return
		
		elif name == "ACTION":
			actions[self.action].setAction(self.content)
			
		elif name == 'SUBDIR':
			self.subdir = -1

# error handler
class errHandler(xml.sax.ErrorHandler):
	def __init__(self):
		return

	def error(self, exception):
		sys.stderr.write("%s\n" % exception)

	def fatalError(self, exception):
		sys.stderr.write("Fatal error while parsing configuration\n")
		sys.stderr.write("%s\n" % exception)
		sys.exit(1)

# parse the configuration file
def parseConfig(file):
	# handlers
	dh = docHandler()
	eh = errHandler()

	# Create an XML parser
	parser = xml.sax.make_parser()

	# Set the handlers
	parser.setContentHandler(dh)
	parser.setErrorHandler(eh)

	fd = open(file, 'r')

	# Parse the file
	parser.parse(fd)
	fd.close()

# Process actions
def processDiractions(dir, srcpath, dstpath, diractions, srcfiles):

	# Scan the update sections
	for upd in dir.getUpdates():
		# skip inactive update rules
		if upd.isActive() == 0:
			continue
		update = []
		src_update = []
		dst_update = []

		# Select files by selection pattern
		pattern = re.compile(upd.getPattern())
		for file in srcfiles:
			if pattern.match(file):
				update[len(update):] = [file]

		# Process excludes
		for excl in dir.getExcludes():
			# skip inactive rules
			if excl.isActive() == 0:
				continue
			# Get exclude pattern
			pat = re.compile(excl.getPattern())
			for file in update:
				if not pat.match(file):
					src_update[len(src_update):] = [file]

		# If no excludes given, process all source matches			
		if len(dir.getExcludes()) == 0:
			src_update = update

		# Updates available ?	
		if len(src_update) == 0:
			continue
		
		# Create dest filenames
		repl = upd.getTarget() 
		for file in src_update:
			if repl:
				dstfile = pattern.sub(repl,file)
			else:
				dstfile = file
			verbose_print(2, dstfile)
			dst_update[len(dst_update):] = [dstfile]

		# Process the actions
		for action in diractions:
			verbose_print(2, "Action: %s" %(action.getName()))
			action.process(srcpath,
				       dstpath,
				       src_update,
				       dst_update,
				       upd.options)


# Process subdirectory
def processSubdir(dir, srcbasedir, dstbasedir):
	
	# Skip inactive subdirs
	if dir.isActive() == 0:
		return

	# Check valid actions
	diractions = []
	if dir.getActions():
		for act in dir.getActions():
			if not actions.has_key(act):
				continue
			if actions[act].isActive() > 0:
				diractions[len(diractions):] = [actions[act]]
	else:
		diractions = default_actions

	if len(diractions) == 0:
		return
	
	# Set the pathnames
	srcpath = os.path.join(srcbasedir, dir.getDirname())
	dstpath = os.path.join(dstbasedir, dir.getDirname())
	# Read files in source directory
	srcfiles = os.listdir(srcpath)

	verbose_print(1, "Processing %s" %(srcpath))

	processDiractions(dir, srcpath, dstpath, diractions, srcfiles)

	# Handle directory recursive ?
	if not dir.isRecursive():
		return

	dirname = dir.name

	for file in srcfiles:
		if not os.path.isdir(os.path.join(srcpath,file)):
			continue
		dir.name = os.path.join(dirname, file)
		processSubdir(dir, srcbasedir, dstbasedir)
		
# Here we go
# Parse the commandline
try:
	(options, arguments) = getopt.getopt(sys.argv[1:],'a:f:hiv:')
except getopt.GetoptError, ex:
	print
	print "ERROR:"
	print ex.msg
	usage(1)

for option, value in options:
	if option == "-f":
		config_file = value

	elif option == "-a":
		cmdline_actions = value.split(',')
	
	elif option == '-i':
		interactive = 1

	elif option == '-v':
		if value:
			verbose = int(value)
		else:
			verbose = 1

	elif option == '-h':
		usage(0)

# Read the configuration
parseConfig(config_file)

for act in cmdline_actions:
	if len(act) == 0:
		continue
	if not config_dict.has_key(act):
		print "Invalid action: " + act
		print 
		usage(1)
	cmdline_actdict[act] = act


# Build default actions list
#  - order by configured actionsorder
#  - validate against commandline options
for action in default_actionorder:
	if not actions.has_key(action):
		continue
	if actions[action].validateOptions(cmdline_actdict) == 0:
		continue
	default_actions[len(default_actions):] = [actions[action]]


for dir in subdirs:
	dir.validateOptions(cmdline_actdict)

if len(arguments) != 1:
	usage(1)

# Get current directory
srcbase = os.getcwd()
dstbase = arguments[0]

# Do target checks
for check in targetchecks:
	try:
		fd = open(os.path.join(dstbase,check.getPattern()))
		lines = fd.readlines()
		fd.close()
		found = 0
		for line in lines:
			if line.find(check.getTarget()) >= 0:
				found = 1
				break;
		if found == 0:
			raise exception(found)
	except:		
		print
		print "Target-Directory check failed: " + check.getOptions()
		sys.exit(1)
		
# Ok, the target seems to be what we are looking for
# Now process the subdirectory entries
for dir in subdirs:
	processSubdir(dir, srcbase, dstbase)
		





More information about the linux-mtd-cvs mailing list