#!/usr/bin/python -u # for testing, also use: #!/usr/bin/python1.5 -u # # program: tpm - Tim's Patch Manager # prepare a patched version of the tree based on original sources # and patches # # Author: Tim Bird - tim.bird ``(at)~~ am.sony.com # Copyright (C) 2003,2004 - Sony Electronics, Inc. # License: GPL 2.0 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # See http://www.gnu.org/licenses/gpl.html for a copy of the # license. # # This is essentially my own custom patch management tool # build phases are: # unpack - not done yet # unarchive_source # apply_patches # copy_to_orig - make a backup copy of the source for later patch # creation # build - not done yet # # To Do for this program: # import sys, os, commands import string major_version = 1 minor_version = 4 default_tarfile = "base/linux-2.4.20.tar.bz2" default_out_dir = "celinux-test" try: default_patch_list_file = os.environ["PATCHLIST"] except: default_patch_list_file = "../../patches/patchlist" def endswith(line, ending): if line[-(len(ending)):] == ending: return 1 else: return 0 def show_version(progpath): progname = os.path.basename(progpath) print "%s - Tim's Patch Manager, version %d.%d" % (progname, major_version, minor_version) def usage(progpath): print """Usage: %s [options] -h, --help show this usage help -t use the specified tarfile as the source base (This overrides a base= setting in the patchlist file) -o specify output directory for new codebase -c copy files to .orig directory after applying patches -b build the tree after unarchiving -a specify architecture to build -f get the list of patches from the file in the patch directory. Note that patches listed in the patchlist file are relative to the directory where the patchlist file is found. -l apply the specified patches, in order -v, --verbose output extra information while running -V, --version display version information """ % os.path.basename(progpath) sys.exit(0) # unarchive_source unarchives the indicated tar file, into the directory # specified. Note that dest_dir replaces the top level # directory name specified in the tar. def unarchive_source(tarfile, dest_dir): global verbose print "Unarchiving %s to directory %s" % (tarfile, dest_dir) dest_dir = os.path.abspath(dest_dir) # make sure directory does not already exist. if os.path.exists(dest_dir): print "Cannot unarchive source file: %s" % tarfile print "Unarchive directory '%s' already exists." % dest_dir sys.exit(1) # determine what flags to use for decompressing and # extracting the tarfile if endswith(tarfile, ".bz2"): tarflags = "-xjf" elif endswith(tarfile, ".tar.gz") or endswith(tarfile, ".tgz"): tarflags = "-xzf" elif endswith(tarfile, ".tar"): tarflags = "-xf" else: print "Cannot unarchive source file: %s" % tarfile print "Don't know unarchive method for file with this extension." sys.exit(1) if verbose: verbose_flag = "-v" else: verbose_flag = "" # get directory to archive into parent_dir = os.path.dirname(dest_dir) unpack_dir = parent_dir+"/dopatch_tmp" # remove junk from previous run, if any if os.path.exists(unpack_dir): action = raw_input("Directory %s already exists, remove or cancel (r/c)? " % unpack_dir) if action=="r" or action=="R": action = raw_input("About to 'rm -rf %s', are you sure (y/n)? " % unpack_dir) if action=="y" or action=="Y": os.system("rm -rf %s" % unpack_dir) else: print "Remove not confirmed." sys.exit(0) else: print "Operation cancelled." sys.exit(0) os.mkdir(unpack_dir) command = "tar -C %s %s %s %s " % (unpack_dir, verbose_flag, tarflags, tarfile) ccode = os.system(command) if ccode: print "Error %s trying to unarchive source file: %s" % (ccode, tarfile) print "Cannot continue" sys.exit(ccode) # FIXTHIS - this code assumes that the archive has a single # top-level directory - the wildcard below is mega-dangerous # FIXTHIS - this code might act funky when parent_dir is # a mount point # move the unpacked dir to the requested dir os.system("mv %s/* %s" % (unpack_dir, dest_dir)) # remove unpack directory (dopatch_tmp) os.rmdir(unpack_dir) # apply_patches applies the list of patches to the files in # the indicated directory def apply_patches(source_dir, patch_dir, patch_list): global verbose # assume all patches use patch level 1 at root # FIXTHIS - this code assumes all patches use patch level # one at the root. # change to the parent of source_dir source_dir = os.path.abspath(source_dir) parent = os.path.basename(source_dir) curdir = os.getcwd() if verbose: silent_flag = "" else: silent_flag = "-s" for patch in patch_list: patch_file = os.path.abspath(patch_dir+"/"+patch) if not os.path.exists(patch_file): raise ValueError, "missing patch file: %s" % patch_file print "Applying patch: %s" % patch_file os.chdir(source_dir) ccode = os.system("patch %s --no-backup-if-mismatch -p1 <%s" % (silent_flag, patch_file)) os.chdir(curdir) def copy_tree(in_dir, extension): global verbose out_dir = in_dir + extension print "Copying to directory %s" % (out_dir) os.system("cp -a %s %s" % (in_dir, out_dir)) # read patch list from a file # patches are individual file names, one per line def read_patch_list(pl_file): global verbose # NOTES: no line can contain an equal sign # comments start with a # # whitespace at start of the line is ignored lines = open(os.path.abspath(pl_file)).readlines() patch_list = [] pl_vars = {} for line in lines: # eat leading and trailing whitespace line = string.strip(line) # skip comment lines and var assignments if len(line)>0 and line[0]=="#": continue if string.find(line,"=") != -1: (name,var) = string.split(line,"=",2) name = string.strip(name) var = string.strip(var) pl_vars[name] = var continue patch_list.append(line) return (patch_list, pl_vars) def main(): global verbose # set default values verbose = 0 build_flag = 0 copy_flag = 0 tarfile = "" out_dir = "" patch_list = [] pl_vars = {} pl_file = "" # parse command line options if "-h" in sys.argv or "--help" in sys.argv: usage(sys.argv[0]) if "-v" in sys.argv or "--verbose" in sys.argv: verbose = 1 if "-b" in sys.argv: build_flag = 1 if "-c" in sys.argv: copy_flag = 1 if "-t" in sys.argv: tarfile = sys.argv[sys.argv.index("-t")+1] if "-o" in sys.argv: out_dir = sys.argv[sys.argv.index("-o")+1] if "-a" in sys.argv: arch = sys.argv[sys.argv.index("-a")+1] if "-l" in sys.argv: patch_list = sys.argv[sys.argv.index("-l")+1] patch_list = patch_list.split(",") if "-f" in sys.argv: pl_file = sys.argv[sys.argv.index("-f")+1] if "-V" in sys.argv or "--version" in sys.argv: show_version(sys.argv[0]) sys.exit(0) # print program information, if verbose if verbose: show_version(sys.argv[0]) if out_dir == "": out_dir = default_out_dir print "Using default output directory: %s" % out_dir if patch_list: print "Using specified patchlist: %s" % patch_list patch_dir = "." else: # get patchlist from file if pl_file == "": pl_file = default_patch_list_file if os.path.exists(pl_file): (patch_list, pl_vars) = read_patch_list(pl_file) print "Using patch list file:", os.path.abspath(pl_file) patch_dir = os.path.dirname(os.path.abspath(pl_file)) else: print "Error: No patch list specified, aborting." sys.exit(1) print "patch_dir=", patch_dir # FIXTHIS - need to code unpack() - not done yet if tarfile == "": if pl_vars.has_key("base"): # base is relative to patch directory tarfile = patch_dir + "/" + pl_vars["base"] print "Using tarfile: %s" % tarfile else: tarfile = default_tarfile print "Using default tarfile: %s" % tarfile else: print "Using tarfile: %s" % tarfile unarchive_source(tarfile, out_dir) if patch_list: apply_patches(out_dir, patch_dir, patch_list) # FIXTHIS - need to code copy_to_orig() - not done yet if copy_flag: copy_tree(out_dir, ".orig") # FIXTHIS - need to code unpack() - not done yet if build_flag: copy_tree(out_dir, ".build") if __name__=="__main__": main()