--- embedaddon/rsync/packaging/patch-update 2012/02/17 15:09:30 1.1.1.1 +++ embedaddon/rsync/packaging/patch-update 2021/03/17 00:32:36 1.1.1.4 @@ -1,230 +1,244 @@ -#!/usr/bin/perl +#!/usr/bin/env -S python3 -B + # This script is used to turn one or more of the "patch/BASE/*" branches # into one or more diffs in the "patches" directory. Pass the option # --gen if you want generated files in the diffs. Pass the name of # one or more diffs if you want to just update a subset of all the # diffs. -use strict; -use warnings; -use Getopt::Long; +import os, sys, re, argparse, time, shutil -my $patches_dir = 'patches'; -my $tmp_dir = "patches.$$"; -my $make_gen_cmd = 'make -f prepare-source.mak conf && ./config.status && make gen'; +sys.path = ['packaging'] + sys.path -&Getopt::Long::Configure('bundling'); -&usage if !&GetOptions( - 'branch|b=s' => \( my $master_branch = 'master' ), - 'skip-check' => \( my $skip_branch_check ), - 'shell|s' => \( my $launch_shell ), - 'gen:s' => \( my $incl_generated_files ), - 'help|h' => \( my $help_opt ), -); -&usage if $help_opt; +from pkglib import * -if (defined $incl_generated_files) { - $patches_dir = $incl_generated_files if $incl_generated_files ne ''; - $incl_generated_files = 1; -} +MAKE_GEN_CMDS = [ + './prepare-source'.split(), + 'cd build && if test -f config.status ; then ./config.status ; else ../configure ; fi', + 'make -C build gen'.split(), + ] +TMP_DIR = "patches.gen" -die "No '$patches_dir' directory was found.\n" unless -d $patches_dir; -die "No '.git' directory present in the current dir.\n" unless -d '.git'; +os.environ['GIT_MERGE_AUTOEDIT'] = 'no' -require 'packaging/git-status.pl'; -check_git_state($master_branch, !$skip_branch_check, 1); +def main(): + global master_commit, parent_patch, description, completed, last_touch -my $master_commit; -open PIPE, '-|', "git log -1 --no-color $master_branch" or die $!; -while () { - if (/^commit (\S+)/) { - $master_commit = $1; - last; - } -} -close PIPE; -die "Unable to determine commit hash for master branch: $master_branch\n" unless defined $master_commit; + if not os.path.isdir(args.patches_dir): + die(f'No "{args.patches_dir}" directory was found.') + if not os.path.isdir('.git'): + die('No ".git" directory present in the current dir.') -my @extra_files; -open(IN, '<', 'Makefile.in') or die "Couldn't open Makefile.in: $!\n"; -while () { - if (s/^GENFILES=//) { - while (s/\\$//) { - $_ .= ; - } - @extra_files = split(' ', $_); - last; - } -} -close IN; + starting_branch, args.base_branch = check_git_state(args.base_branch, not args.skip_check, args.patches_dir) -if ($incl_generated_files) { - die "'$tmp_dir' must not exist in the current directory.\n" if -e $tmp_dir; - mkdir($tmp_dir, 0700) or die "Unable to mkdir($tmp_dir): $!\n"; - system "$make_gen_cmd && rsync -a @extra_files $tmp_dir/master/" and exit 1; -} -our $last_touch = time; + master_commit = latest_git_hash(args.base_branch) -my %patches; + if cmd_txt_chk(['packaging/prep-auto-dir']) == '': + die('You must setup an auto-build-save dir to use this script.') -# Start by finding all patches so that we can load all possible parents. -open(PIPE, '-|', 'git', 'branch', '-l') or die $!; -while () { - if (m# patch/\Q$master_branch\E/(.*)#o) { - $patches{$1} = 1; - } -} -close PIPE; + if args.gen: + if os.path.lexists(TMP_DIR): + die(f'"{TMP_DIR}" must not exist in the current directory.') + gen_files = get_gen_files() + os.mkdir(TMP_DIR, 0o700) + for cmd in MAKE_GEN_CMDS: + cmd_chk(cmd) + cmd_chk(['rsync', '-a', *gen_files, f'{TMP_DIR}/master/']) -my @patches = sort keys %patches; + last_touch = int(time.time()) -my(%parent, %description); -foreach my $patch (@patches) { - my $branch = "patch/$master_branch/$patch"; - my $desc = ''; - open(PIPE, '-|', 'git', 'diff', '-U1000', "$master_branch...$branch", '--', "PATCH.$patch") or die $!; - while () { - last if /^@@ /; - } - while () { - next unless s/^[ +]//; - if (m#patch -p1 = time; -system "git checkout $master_branch" and exit 1; + completed = set() -exit; + for patch in patches: + if patch in completed: + continue + if not update_patch(patch): + break + if args.gen: + shutil.rmtree(TMP_DIR) -sub update_patch -{ - my($patch) = @_; + while last_touch >= int(time.time()): + time.sleep(1) + cmd_chk(['git', 'checkout', starting_branch]) + cmd_chk(['packaging/prep-auto-dir'], discard='output') - my $parent = $parent{$patch}; - my $based_on; - if (defined $parent) { - unless ($completed{$parent}++) { - update_patch($parent); - } - $based_on = $parent = "patch/$master_branch/$parent"; - } else { - $parent = $master_branch; - $based_on = $master_commit; - } - print "======== $patch ========\n"; +def update_patch(patch): + global last_touch - sleep 1 while $incl_generated_files && $last_touch >= time; - system "git checkout patch/$master_branch/$patch" and return 0; + completed.add(patch) # Mark it as completed early to short-circuit any (bogus) dependency loops. - my $ok = system("git merge $based_on") == 0; - if (!$ok || $launch_shell) { - my($parent_dir) = $parent =~ m{([^/]+)$}; - print qq|"git merge $based_on" incomplete -- please fix.\n| if !$ok; - $ENV{PS1} = "[$parent_dir] $patch: "; - while (1) { - if (system($ENV{SHELL}) != 0) { - print "Abort? [n/y] "; - $_ = ; - next unless /^y/i; - return 0; - } - my($cur_branch, $is_clean, $status) = check_git_status(0); - last if $is_clean; - print $status; - } - } + parent = parent_patch.get(patch, None) + if parent: + if parent not in completed: + if not update_patch(parent): + return 0 + based_on = parent = f"patch/{args.base_branch}/{parent}" + else: + parent = args.base_branch + based_on = master_commit - open(OUT, '>', "$patches_dir/$patch.diff") or die $!; - print OUT $description{$patch}, "\nbased-on: $based_on\n"; + print(f"======== {patch} ========") - if ($incl_generated_files) { - system "$make_gen_cmd && rsync -a @extra_files $tmp_dir/$patch/" and exit 1; - } - $last_touch = time; + while args.gen and last_touch >= int(time.time()): + time.sleep(1) - open(PIPE, '-|', 'git', 'diff', $based_on) or die $!; - DIFF: while () { - while (m{^diff --git a/PATCH}) { - while () { - last if m{^diff --git a/}; - } - last DIFF if !defined $_; - } - next if /^index /; - print OUT $_; - } - close PIPE; + branch = f"patch/{args.base_branch}/{patch}" + s = cmd_run(['git', 'checkout', branch]) + if s.returncode != 0: + return 0 - if ($incl_generated_files) { - my $parent_dir; - if ($parent eq $master_branch) { - $parent_dir = 'master'; - } else { - ($parent_dir) = $parent =~ m{([^/]+)$}; - } - open(PIPE, '-|', 'diff', '-up', "$tmp_dir/$parent_dir", "$tmp_dir/$patch") or die $!; - while () { - s#^(diff -up) $tmp_dir/[^/]+/(.*?) $tmp_dir/[^/]+/(.*)#$1 a/$2 b/$3#o; - s#^\Q---\E $tmp_dir/[^/]+/([^\t]+)\t.*#--- a/$1#o; - s#^\Q+++\E $tmp_dir/[^/]+/([^\t]+)\t.*#+++ b/$1#o; - print OUT $_; - } - close PIPE; - } + s = cmd_run(['git', 'merge', based_on]) + ok = s.returncode == 0 + skip_shell = False + if not ok or args.cmd or args.make or args.shell: + cmd_chk(['packaging/prep-auto-dir'], discard='output') + if not ok: + print(f'"git merge {based_on}" incomplete -- please fix.') + if not run_a_shell(parent, patch): + return 0 + if not args.make and not args.cmd: + skip_shell = True + if args.make: + if cmd_run(['packaging/smart-make']).returncode != 0: + if not run_a_shell(parent, patch): + return 0 + if not args.cmd: + skip_shell = True + if args.cmd: + if cmd_run(args.cmd).returncode != 0: + if not run_a_shell(parent, patch): + return 0 + skip_shell = True + if args.shell and not skip_shell: + if not run_a_shell(parent, patch): + return 0 - close OUT; + with open(f"{args.patches_dir}/{patch}.diff", 'w', encoding='utf-8') as fh: + fh.write(description[patch]) + fh.write(f"\nbased-on: {based_on}\n") - 1; -} + if args.gen: + gen_files = get_gen_files() + for cmd in MAKE_GEN_CMDS: + cmd_chk(cmd) + cmd_chk(['rsync', '-a', *gen_files, f"{TMP_DIR}/{patch}/"]) + else: + gen_files = [ ] + last_touch = int(time.time()) -exit; + proc = cmd_pipe(['git', 'diff', based_on]) + skipping = False + for line in proc.stdout: + if skipping: + if not re.match(r'^diff --git a/', line): + continue + skipping = False + elif re.match(r'^diff --git a/PATCH', line): + skipping = True + continue + if not re.match(r'^index ', line): + fh.write(line) + proc.communicate() -sub usage -{ - die <