#!/usr/bin/env python # import time # from stat import * import sys, os, re import optparse import ConfigParser import subprocess import shutil # -------------------------------------------------------------------------- class Options : def __init__ (self) : self.compare = False self.create_current = False self.delta = False self.diff = False self.evaluate = False self.files = False self.list_names = False self.new_patches = False self.create_orig = False self.create_patched = False self.recall = False self.recall_prev = False self.store = False self.unpack = False self.overwrite = False self.module = "" self.new_patch = "" self.compare_dir1 = "" self.compare_dir2 = "" self.compare_result = "" # -------------------------------------------------------------------------- class Patch : def __init__ (self) : self.file_name = "" self.patch_id = "" self.patch_title = "" self.patch_level = 0 self.file_pack = "" # -------------------------------------------------------------------------- def show_title (txt) : print "\033[1;33m" + txt + "\033[0m" # yellow def show_info (txt) : print "\033[1;34m" + txt + "\033[0m" # blue def show_success (txt) : print "\033[1;32m" + txt + "\033[0m" # green def show_error (txt) : print "\033[1;31m" + txt + "\033[0m" # red raise Exception (txt) # -------------------------------------------------------------------------- def read_options () : parser = optparse.OptionParser () parser.add_option("-c", "--compare", dest="compare", action="store_true", help="compare two directories") parser.add_option( "--current", dest="create_current", action="store_true", help="copy files from working directory to ...-current directory") parser.add_option("-d", "--delta", dest="delta", action="store_true", help="compare patched sources and working directory") parser.add_option( "--diff", dest="diff", action="store_true", help="compare original sources and working directory") parser.add_option("-e", "--eval", "--evaluate", dest="evaluate", action="store_true", help="try all patches, stop on first reject") parser.add_option("-f", "--files", dest="files", action="store_true", help="pack ...-files.tgz") parser.add_option("-l", "--list", dest="list_names", action="store_true", help="list patch names") parser.add_option( "--new", dest="new_patches", action="store_true", help="create new patches with -p1 options") parser.add_option( "--orig", dest="create_orig", action="store_true", help="copy original sources to ...-orig directory") parser.add_option( "--patched", dest="create_patched", action="store_true", help="copy patched sources to ...-patched directory") parser.add_option("-r", "--recall", dest="recall", action="store_true", help="apply patches") parser.add_option( "--recall-prev", dest="recall_prev", action="store_true", help="apply patches to ...-prev directory") parser.add_option("-s", "--store", dest="store", action="store_true", help="create patch file") parser.add_option("-u", "--unpack", dest="unpack", action="store_true", help="unpack sources to working directory, apply patches") parser.add_option( "--overwrite", dest="overwrite", action="store_true", help="overwrite sources in working directory, apply patches") parser.add_option("-m", "--module", dest="module", help="module directory", default="") global opt global arg opt = Options () (opt, arg) = parser.parse_args (values=opt) if (int (opt.create_current) + int (opt.compare) + int (opt.delta) + int (opt.diff) + int (opt.evaluate) + int (opt.files) + int (opt.list_names) + int (opt.new_patches) + int (opt.create_orig) + int (opt.create_patched) + int (opt.recall) + int (opt.recall_prev) + int (opt.store) + int (opt.unpack) + int (opt.overwrite) > 1) : parser.error("Incompatible options") if opt.recall or opt.recall_prev : # one argument required if len (arg) == 0 : parser.error("missing argument") if len (arg) > 1 : parser.error("too many arguments") opt.new_patch = arg [0] elif opt.compare : if len (arg) < 2 or len (arg) > 3 : parser.error("two or three arguments required") opt.compare_dir1 = arg [0] opt.compare_dir2 = arg [1] if len (arg) > 2 : opt.compare_result = arg [2] else : opt.compare_result = "" else : # no argument allowed if len (arg) != 0 : parser.error("unknown argument") # -------------------------------------------------------------------------- def read_top_level_configuration () : filename = "versions.cfg" section = "versions" cfg = ConfigParser.RawConfigParser() cfg.read(filename) # default empty values if not cfg.has_section (section) : cfg.add_section (section) if not cfg.has_option (section, "module") : cfg.set (section, "module", "") # read values / update values from command line any = False if opt.module != "" : cfg.set (section, "module", opt.module) any = True else : opt.module = cfg.get (section, "module") # update configuration if any : with open(filename, "wb") as configfile: cfg.write(configfile) print "module = " + opt.module def read_configuration (directory) : result = "" if os.path.exists (directory) : filename = directory + "/" + "versions.cfg" section = "versions" cfg = ConfigParser.RawConfigParser() cfg.read(filename) if cfg.has_section (section) : if cfg.has_option (section, "patch") : result = cfg.get (section, "patch") print "patch = " + result return result def write_configuration (directory, patch_id) : filename = directory + "/" + "versions.cfg" section = "versions" cfg = ConfigParser.RawConfigParser() cfg.read(filename) if not cfg.has_section (section) : cfg.add_section (section) cfg.set (section, "patch", patch_id) # update configuration with open(filename, "wb") as configfile: cfg.write(configfile) print "patch = " + patch_id # ------------------------------- def verify_dir (target): "optionally create new directory" if not os.path.exists (target) : os.mkdir (target) def empty_dir (target): "create empty directory or check empty directory" if os.path.exists (target) : os.rmdir (target) os.mkdir (target) def erase_dir (target): "erase directory, all subdirectories and files" if os.path.exists (target) : shutil.rmtree (target) # -------------------------------------------------------------------------- def remove_prefix (txt, prefix) : if txt.startswith (prefix) : return txt [ len (prefix) : len (txt) ] else : return txt def remove_suffix (txt, suffix) : if txt.endswith (suffix) : return txt [ 0 : len (txt) - len (suffix) ] else : return txt def remove_number (txt) : m = re.search ("(-[0-9.]*)$", txt) if m : k = len (m.group (1)) return txt [ 0 : len (txt) - k ] else : return txt # -------------------------------------------------------------------------- def find_backup_version () : global backup_version backup_version = 0 verify_dir ("backup") pattern = re.compile("\.([0-9][0-9]*)$") items = os.listdir ("backup") for name in items : result = pattern.search (name) v = int (result.group (1)) # print name, result.group (1), v if v > backup_version : backup_version = v backup_version = backup_version + 1 print "backup_version =", backup_version # -------------------------------------------------------------------------- def find_patch_list () : global patch_list patch_list = [ ] module_id = opt.module module_num = "" m = re.search ("^(.*)-([0-9.]*)$", module_id) if m : module_id = m.group (1) module_num = m.group (2) p_pattern = re.compile("(-p([0-9][0-9]*))$") v_pattern = re.compile("(-v([0-9.][0-9.]*))$") items = os.listdir ("components") items.sort () for file_name in items : if file_name.startswith (module_id + "-") and file_name.endswith (".diff") : patch_id = file_name patch_id = remove_suffix (patch_id, ".diff"); patch_level = -1 ver_num = "" m = p_pattern.search (patch_id) if m : patch_id = patch_id [ 0 : len (patch_id) - len (m.group (1)) ] patch_level = int (m.group (2)) patch_title = patch_id patch_id = remove_prefix (patch_id, module_id + "-") m = v_pattern.search (patch_id) if m : patch_id = patch_id [ 0 : len (patch_id) - len (m.group (1)) ] ver_num = m.group (2) skip = False if ver_num != "" : if module_num == ver_num or module_num.startswith (ver_num + ".") : skip = False else : skip = True if patch_level < 0 : # patch_level = 4 patch_level = 1 file_pack = patch_title + "-files.tgz"; if not (file_pack in items) : file_pack = "" if not skip : p = Patch () p.file_name = file_name p.patch_id = patch_id p.patch_title = patch_title p.patch_level = patch_level p.file_pack = file_pack patch_list.append (p) if opt.list_names : show_info ("patch " + p.file_name + " " + p.patch_id + " " + p.patch_title + " " + str (p.patch_level) + " " + p.file_pack) # file archives patch_dict = { } for p in patch_list : patch_dict [p.patch_id] = p # patch_id or patch_title for file_name in items : if file_name.startswith (module_id + "-") and file_name.endswith ("-files.tgz") : patch_id = file_name patch_id = remove_prefix (patch_id, module_id + "-") patch_id = remove_suffix (patch_id, "-files.tgz"); # -------------------------------------------------------------------------- def find_patch_index (name) : # show_title ("find_patch_index (" + name + ")") patch_index = -1 if name != "" : i = 0 for p in patch_list : # show_title ("compare = " + p.patch_id) if p.patch_id == name : patch_index = i i = i + 1 if patch_index < 0 : show_error ("Cannot find patch index for " + name) # show_title ("find_patch_index = " + str (patch_index)) return patch_index # -------------------------------------------------------------------------- def backup_file (file_name) : if os.path.exists (file_name) : number = str (backup_version) while len (number) < 3 : number = "0" + number target = "backup/" + os.path.basename (file_name) + "." + number os.rename (file_name, target) print "backup " + file_name + " -> " + target # -------------------------------------------------------------------------- def diff (source, target, diff_file, all_files=False, all_lines=False, check=True) : backup_file (diff_file) cmd = "diff" cmd = cmd + " -b" # Ignore changes in the amount of white space. cmd = cmd + " -E" # Ignore changes due to tab expansion. cmd = cmd + " -B" # Ignore changes whose lines are all blank. cmd = cmd + " -w" # Ignore all white space. if not all_files : cmd = cmd + " -x *~" cmd = cmd + " -x *.o" cmd = cmd + " -x *.so" cmd = cmd + " -x *.orig" cmd = cmd + " -x *.rej" cmd = cmd + " -U 5 -r" cmd = cmd + " " + source cmd = cmd + " " + target if not all_lines : cmd = cmd + ' | grep -v "^Only in "' cmd = cmd + ' | grep -v "^Files .* differ$"' cmd = cmd + " > " + diff_file print cmd # if check : # subprocess.check_call (cmd, shell=True) # else : # subprocess.call (cmd, shell=True) subprocess.call (cmd, shell=True) show_success ("created " + diff_file) # -------------------------------------------------------------------------- def apply_patch (target, p, check=True) : save = os.getcwd () os.chdir (target) show_info ("patching " + target + " with " + p.file_name) cmd = "patch " if p.patch_level >= 0 : cmd = cmd + "-p" + str (p.patch_level) + " " cmd = cmd + "< ../components/" + p.file_name print cmd result = subprocess.call ([cmd], stdout=sys.stdout, shell=True) if result != 0 and check: show_error ("cannot patch " + target + " with " + p.file_name) if p.file_pack != "" : show_info ("unpacking " + p.file_pack) unpack_archive ("../components/" + p.file_pack); os.chdir (save) return result # -------------------------------------------------------------------------- def archive_letter (archive) : cmd = "" if archive.endswith (".tgz") : cmd = "z" elif archive.endswith (".tar.gz") : cmd = "z" elif archive.endswith (".tar.bz2") : cmd = "j" elif archive.endswith (".tar.xz") : cmd = "J" else : show_error ("Unknown extension: " + archive) return cmd def unpack_archive (archive, options = "") : cmd = archive_letter (archive) cmd = "x" + cmd + "f" cmd = "tar " + cmd + " " + archive + " " + options subprocess.check_call ([ cmd ], shell=True) def pack_archive (archive, options = "") : cmd = archive_letter (archive) cmd = "c" + cmd + "f" cmd = "tar " + cmd + " " + archive + " " + options subprocess.check_call ([ cmd ], shell=True) # -------------------------------------------------------------------------- def unpack_src (target) : empty_dir ("unpack") save = os.getcwd () os.chdir ("unpack") show_info ("unpacking " + target) unpack_archive ("../" + opt.module + ".tar.bz2") # unpack_archive ("../" + opt.module + ".tar.gz") os.chdir (save) os.rename ("unpack" + "/" + opt.module, target) def unpack_orig (target) : "unpack source (if directory does not already exists)" if not os.path.exists (target) : unpack_src (target) def unpack_new (target) : "erase original directory and unpack sources" erase_dir (target) unpack_src (target) # -------------------------------------------------------------------------- def compare_directories () : "compare two directories" output = opt.compare_result if output == "" : output = "delta/" + opt.module + ".compare" # diff (opt.compare_dir1, opt.compare_dir2, output, all_files=True, all_lines=True, check=False) diff (opt.compare_dir1, opt.compare_dir2, output) # -------------------------------------------------------------------------- def copy_current_files () : "copy files from working directory to ...-current directory" source = opt.module target = opt.module + "-current" original = opt.module + "-orig" erase_dir (target) unpack_orig (original) for path, dirs, files in os.walk (source) : if path == source : local_path = "" else : local_path = remove_prefix (path, source + "/") os.makedirs (os.path.join (target, local_path)) for name in files : file_name = os.path.join (local_path, name) copy = False if os.path.exists (original + "/" + file_name) : copy = True elif (name.endswith (".h") or name.endswith (".hpp") or name.endswith (".cc") or name.endswith (".cpp") ) : copy = True if copy : show_info ("copying " + source + "/" + file_name + " " + target + "/" + file_name) shutil.copy2 (source + "/" + file_name, target + "/" + file_name) show_success ("files copied from " + source + " to " + target) # -------------------------------------------------------------------------- def copy_orig_files () : "copy original sources to ...-orig directory" source = opt.module + "-orig" unpack_orig (source) def copy_patched_files () : "copy patched sources to ...-patched directory" source = opt.module + "-patched" unpack_new (source) for p in patch_list : apply_patch (source, p, check=False) # -------------------------------------------------------------------------- def calculate_delta () : "compare patched sources and working directory" verify_dir ("delta") output = "delta/" + opt.module + ".delta" source = opt.module + "-patched" unpack_new (source) for p in patch_list : apply_patch (source, p, check=False) diff (source, opt.module, output) def calculate_diff () : "compare original sources and working directory" verify_dir ("delta") output = "delta/" + opt.module + ".diff" source = opt.module + "-orig" unpack_orig (source) diff (source, opt.module, output) # -------------------------------------------------------------------------- def evaluate_patches () : "try all patches, stop on first reject" prev = opt.module + "-prev" next = opt.module + "-next" # store_changes (next) unpack_new (next) unpack_new (prev) for p in patch_list : show_title (p.patch_id) write_configuration (next, p.patch_id) apply_patch (next, p, check=True) apply_patch (prev, p, check=True) def create_new_patches () : "create new patches with -p1 options" prev = opt.module + "-prev" next = opt.module + "-next" # store_changes (next) unpack_new (next) unpack_new (prev) output_directory = "new_components" verify_dir (output_directory) for p in patch_list : show_title (p.patch_id) write_configuration (next, p.patch_id) apply_patch (next, p, check=True) store_changes (next, output_directory) apply_patch (prev, p, check=True) # -------------------------------------------------------------------------- def pack_files () : save = os.getcwd () os.chdir (opt.module) # working ditrectory for p in patch_list : if p.file_pack != "" : file_list = "../components/" + remove_suffix (p.file_pack, ".tgz") + ".txt" if os.path.exists (file_list) : file_names = "" f = open (file_list, "r") for line in f : line = line.strip () if line != "" : file_names = file_names + " " + line file_pack = "../components/" + p.file_pack show_info ("packing " + p.file_pack + file_names) pack_archive (file_pack, file_names) os.chdir (save) # working ditrectory # -------------------------------------------------------------------------- def recall_patches () : prev = opt.module + "-prev" next = opt.module + "-next" # store_changes (next) patch_index = find_patch_index (opt.new_patch) unpack_new (next) unpack_new (prev) for i in range (0, patch_index-1+1) : p = patch_list [i] write_configuration (next, p.patch_id) apply_patch (next, p, check=False) apply_patch (prev, p, check=False) p = patch_list [patch_index] write_configuration (next, p.patch_id) apply_patch (next, p, check=False) def recall_prev_patches () : prev = opt.module + "-prev" patch_index = find_patch_index (opt.new_patch) unpack_new (prev) for i in range (0, patch_index+1) : p = patch_list [i] apply_patch (prev, p, check=False) # -------------------------------------------------------------------------- def store_changes (directory, output_directory = "") : show_title (directory) patch_id = read_configuration (directory) if patch_id != "" : patch_index = find_patch_index (patch_id) p = patch_list [patch_index] prev = opt.module + "-prev" next = opt.module + "-next" if output_directory == "" : # output = "components/" + p.patch_title + "-p1.diff" output = "components/" + p.patch_title + ".diff" else : output = output_directory + "/" + p.patch_title + ".diff" diff (prev, next, output) def store_patch () : next = opt.module + "-next" store_changes (next) # -------------------------------------------------------------------------- def unpack_sources () : "unpack and patch sources (into working directory)" target = opt.module # working directory if not os.path.exists (target) : unpack_src (target) # optionaly unpack for p in patch_list : patch_mark = "_" + p.patch_title + "_patched" # patch_mark = "_" + remove_suffix (p.file_name, ".diff") + "_patched" if not os.path.exists (patch_mark) : result = apply_patch (target, p, check=False) if result == 0 : subprocess.check_call (["touch " + patch_mark], shell=True) def overwrite_sources () : "unpack and patch sources (overwrite source files in working directory)" target = opt.module # working directory show_info ("unpacking " + target) # always unpack unpack_archive (opt.module + ".tar.bz2") for p in patch_list : patch_mark = "_" + p.patch_title + "_patched" # patch_mark = "_" + remove_suffix (p.file_name, ".diff") + "_patched" # do not check patch_mark result = apply_patch (target, p, check=False) if result == 0 : subprocess.check_call (["touch " + patch_mark], shell=True) # -------------------------------------------------------------------------- def main () : read_options () read_top_level_configuration () if opt.module == "" : show_error ("Missing module name") find_backup_version () find_patch_list () if opt.compare: compare_directories () if opt.create_current: copy_current_files () if opt.delta: calculate_delta () if opt.diff: calculate_diff () if opt.evaluate: evaluate_patches () if opt.files: pack_files () if opt.new_patches: create_new_patches () if opt.create_orig: copy_orig_files () if opt.create_patched: copy_patched_files () if opt.recall: recall_patches () if opt.recall_prev: recall_prev_patches () if opt.store: store_patch () if opt.unpack: unpack_sources () if opt.overwrite: overwrite_sources () # -------------------------------------------------------------------------- if __name__ == "__main__" : main ()