Here is a modified version that includes the previous patch for 5.x.Y
plus support for python 3.8
-- eric
#!/usr/bin/python3
#
# ketchup 1.0.1
# http://github.com/psomas/ketchup
# git://github.com/psomas/ketchup.git
#
# Orignial code by:
# Copyright 2004 Matt Mackall <m...@selenic.com>
#
# Added code for new RT location and maintained until 2011 by:
# Steven Rostedt <srost...@redhat.com>
#
# Now maintained by:
# Stratos Psomadakis <pso...@cslab.ece.ntua.gr>
#
# Contributors:
# Baruch Even <bar...@debian.org>
# Pavel Machek <pa...@ucw.cz>
# Johann Felix Soden <joh...@gmx.de>
# Carsten Emde <c.e...@osadl.org>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
#
# Usage:
#
# in an existing kernel directory, run:
#
# ketchup <version>
#
# where version is a complete kernel version, or a branch name to grab
# the latest version
#
# You can override some variables by creating a ~/.ketchuprc file.
# The ~/.ketchuprc is just a Python script, eg. it might look like this:
#
# kernel_url = 'http://kernel.localdomain/pub/linux/kernel'
# archive = os.environ["HOME"] + '/tmp/ketchup-archive'
# gpg = '/weird/path/to/gpg'
#
import re, sys, urllib, os, getopt, glob, shutil, subprocess
def error(*args):
sys.stderr.write("ketchup: ")
for a in args:
sys.stderr.write(str(a))
sys.stderr.write("\n")
def qprint(*args):
if not options["quiet"]:
sys.stdout.write(" ".join(map(str, args)))
sys.stdout.write("\n")
def lprint(*args):
sys.stdout.write(" ".join(map(str, args)))
sys.stdout.write("\n")
def fancyopts(args, options, state, syntax=''):
long = []
short = ''
map = {}
dt = {}
def help(state, opt, arg, options = options, syntax = syntax):
lprint("Usage: ", syntax)
for s, l, d, c in options:
opt = ' '
if s: opt = opt + '-' + s + ' '
if l: opt = opt + '--' + l + ' '
if d: opt = opt + '(' + str(d) + ')'
lprint(opt)
if c: lprint(' %s' % c)
sys.exit(0)
options = [('h', 'help', help, 'Show usage info')] + options
for s, l, d, c in options:
map['-'+s] = map['--'+l]=l
state[l] = d
dt[l] = type(d)
if not d is None and not type(d) is type(help): s, l = s + ':', l + '='
if s: short = short + s
if l: long.append(l)
if "KETCHUP_OPTS" in os.environ:
args = os.environ["KETCHUP_OPTS"].split() + args
try:
opts, args = getopt.getopt(args, short, long)
except getopt.GetoptError:
help(state, None, args)
sys.exit(-1)
for opt, arg in opts:
if dt[map[opt]] is type(help): state[map[opt]](state,map[opt],arg)
elif dt[map[opt]] is type(1): state[map[opt]] = int(arg)
elif dt[map[opt]] is type(''): state[map[opt]] = arg
elif dt[map[opt]] is type([]): state[map[opt]].append(arg)
elif dt[map[opt]] is type(None): state[map[opt]] = 1
return args
# Default values
kernel_url = 'https://cdn.kernel.org/pub/linux/kernel'
archive = os.environ["HOME"] + "/.ketchup"
rename_prefix = 'linux-'
rename_with_localversion = False
wget = "/usr/bin/wget"
gpg = "/usr/bin/gpg"
precommand = postcommand = None
default_tree = None
local_trees = {}
# Functions to parse version strings
def tree(ver):
# every version must start with x.y, exit otherwise
t = re.match(r'((\d+)\.\d+)', ver)
if t == None:
error("Invalid tree version: %s!" % ver)
sys.exit(-1)
# linux versions >=3.0 use only the first digit to define the 'tree'
ret = int(t.group(2))
if ret >= 3:
return ret
# 2.x linux trees
return float(t.group(1))
def rev(ver):
p = pre(ver)
# 3.x trees
if tree(ver) >= 3:
r = re.match(r'\d+\.(\d+)', ver)
# 2.x trees
else:
r = re.match(r'\d+\.\d+\.(\d+)', ver)
# if there's no match, the version is invalid
try: r = int(r.group(1))
except:
error("Invalid tree version!")
sys.exit(-1)
#for -rc versions return the previous stable version
if p: r = r - 1
if r == -1 and tree(ver) == 3: r = 39
if r == -1 and tree(ver)>3: r = 19
return r
def pre(ver):
# 3.x trees
if tree(ver) >= 3:
p = re.match(r'\d+\.\d+(\.\d+)?-(rc\d+)', ver)
# 2.x trees
else:
p = re.match(r'\d+\.\d+\.\d+(\.\d+)?-(rc\d+)', ver)
try: return p.group(2)
except: return None
def post(ver):
# 3.x trees
if tree(ver) >= 3:
p = re.match(r'\d+\.\d+\.(\d+)', ver)
# 2.x trees
else:
p = re.match(r'\d+\.\d+\.\d+\.(\d+)', ver)
try: return p.group(1)
except: return None
def prenum(ver):
# 3.x trees
if tree(ver) >= 3:
p = re.match(r'\d+\.\d+(\.\d+)?-rc(\d+)', ver)
# 2.x trees
else:
p = re.match(r'\d+\.\d+\.\d+(\.\d+)?-rc(\d+)', ver)
try: return p.group(2)
except: return None
def prebase(ver):
# 3.x trees
if tree(ver) >= 3:
p = re.match(r'(\d+\.\d+(\.\d+)?(-rc\d+)?)', ver)
else:
p = re.match(r'(\d+\.\d+\.\d+(\.\d+)?(-rc\d+)?)', ver)
try: return p.group(1)
except: return None
def revbase(ver):
t = tree(ver)
r = rev(ver)
# make sure 3.0-rcs are patched correctly against 2.6.39
if t == 3 and r == 39:
t = 2.6
if t == 4 and r == 19:
t = 3
return "%s.%s" % (t, r)
def base(ver):
v = revbase(ver)
if post(ver): v += "." + post(ver)
return v
def forkname(ver):
# 3.x trees
if tree(ver) >= 3:
f =
re.match(r'\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?(-([a-zA-Z]+)(-)?(\d+)?)?',
ver)
# 2.x trees
else:
f =
re.match(r'\d+\.\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?(-([a-zA-Z]+)(-)?(\d+)?)?',
ver)
try: return f.group(5)
except: return None
def forknum(ver):
# 3.x trees
if tree(ver) >=3:
f =
re.match(r'\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?(-([a-zA-Z]+)(-)?(\d+)?)?',
ver)
#2.x trees
else:
f =
re.match(r'\d+\.\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?(-([a-zA-Z]+)(-)?(\d+)?)?',
ver)
try: return int(f.group(7))
except: return None
def fork(ver):
# 3.x trees
if tree(ver) >= 3:
f =
re.match(r'\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?((\.\d+)?-(\w+(-)?(\d+)?))?',
ver)
# 2.x trees
else:
f =
re.match(r'\d+\.\d+\.\d+(\.\d+)?(-(rc|pre)\d+)?((\.\d+)?-(\w+(-)?(\d+)?))?',
ver)
try: return f.group(4)
except: return None
#FIXME: at some point the Makefile for 3.x kernels will change
def get_ver(makefile):
""" Read the version information from the specified makefile """
part = {}
parts = "VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION CKVERSION".split(' ')
m = open(makefile)
for l in m.readlines():
for p in parts:
try: part[p] = re.match(r'%s\s*=\s*(\S+)' % p, l).group(1)
except: pass
# atm we use VERSION.PATCHLEVEL for Linus' releases and EXTRAVERSION for
# the -stable releases
if part['VERSION'] >= '3'and part['SUBLEVEL'] == '0':
version = "%s.%s" % tuple([part[p] for p in parts[:2]])
else:
version = "%s.%s.%s" % tuple([part[p] for p in parts[:3]])
version += part.get("EXTRAVERSION","")
version += part.get("CKVERSION","")
n = None
try: n=open("localversion-rt")
except IOError:
pass
if n:
version += n.read()[:-1]
# required for -next patchset
try: n = open("localversion-next")
except: return version
version += n.read()[:-1]
return version
def get_localversion():
v = ''
for name in glob.glob('localversion*'):
try: v += open(name).readline().strip()
except: pass
try:
c = open('.config').read()
v += re.search(r'^CONFIG_LOCALVERSION="(.+)"', c, re.M).group(1)
except: pass
return v
def compare_ver(a, b):
"""
Compare kernel versions a and b
"""
if a == b: return 0
c = cmp(float(tree(a)), float(tree(b)))
if c: return c
# compare correctly the 3.0-rc versions
ra, rb = rev(a), rev(b)
if (tree(a) == 3 and ra == 39):
ra = -1
if (tree(a) > 3 and ra == 19):
ra = -1
if (tree(b) == 3 and rb == 39):
rb = -1
if (tree(b) > 3 and rb == 19):
rb = -1
c = cmp(ra, rb)
if c: return c
c = cmp(int(post(a) or 0), int(post(b) or 0))
if c: return c
c = cmp(prenum(a), prenum(b))
if c: return c
c = cmp(forkname(a), forkname(b))
if c: return c
return cmp(forknum(a), forknum(b))
def last(url, pat="(.*/)"):
print(url, pat)
tokenize = re.compile(r'(\d+)|(\D+)').findall
def natural_sortkey(string):
return tuple(int(num) if num else alpha for num, alpha in
tokenize(string))
n = None
res = []
for l in urllib.urlopen(url).readlines():
search_res = re.finditer('(?i)<a\s+href="%s"\s*>' % pat, l)
for m in search_res:
if ".." not in m.group(0): res.append(m.group(1))
if res:
res.sort(key=natural_sortkey)
n=res[-1]
return n
def latest_mmotm(url, pat):
for l in urllib.urlopen(url + "/broken-out/mm.patch").readlines():
m = re.search('-EXTRAVERSION = (\S+)', l)
if m: n = m.group(1)
latest = latest_dir(os.path.dirname(version_info['4-rc'][1]),
('patch-(.*%s).xz' % n))
return latest + "-mm1"
def latest_ck(url, pat):
url = "http://ck.kolivas.org/patches/3.0/"
url += last(url,"(\d.*)/")
part = fork(last(url,"(\d.*)/"))
stable = latest_dir(os.path.dirname(version_info['3'][1][0]),
version_info['3'][2])
return stable + part
def latest_pf(url, pat):
url = "http://pf.natalenko.name/sources/"
url += last(url)
part = last(url,"(.*patch.*)")
print("Part: ", part)
part = re.search('\d+(.\d+)+-pf\d+', part).group(0)
return part
def latest_dir_lt(url, pat):
"""Find the latest link to the stable release series that is used local"""
cwd=os.getcwd()
lv = None
if os.path.isdir(options["directory"]):
os.chdir(options["directory"])
try:
lv = get_ver('Makefile')
except:
lv = None
os.chdir(cwd)
if not lv:
qprint("No local version found. Use newest kernel release instead.")
return latest_dir(url, pat)
local_revbase = revbase(lv)
p = []
url = url % { "kernel_url": kernel_url }
for l in urllib.urlopen(url).readlines():
m = re.search('"%s"' % pat, l)
if m and revbase(m.group(1))==local_revbase:
p.append(m.group(1))
if not p: return None
p.sort(compare_ver)
return p[-1]
def latest_rt(url, pat):
""" Find the link to the latest rt kernel patch """
urls = []
url_parent = os.path.dirname(url)
postfix = ""
while '%' in url_parent[1:]:
postfix = os.path.basename(url) + "/" + postfix
url_parent = os.path.dirname(url_parent)
if postfix:
postfix = "/" + postfix
url_parent = url_parent % { "kernel_url": kernel_url }
if "2.6" in pat:
m = last(url_parent, "(2\.6.*/)*")
else:
m = last(url_parent, "(\d.*/)")
if not m:
return None
l = latest_dir(url_parent + '/' + m + postfix , pat)
return l
def latest_dir(url, pat):
"""Find the latest link matching pat at url after sorting"""
p = []
url = url % { "kernel_url": kernel_url, "revbase":"" }
for l in urllib.urlopen(url).readlines():
m = re.search('"%s"' % pat, l)
if m: p.append(m.group(1))
if not p: return None
p.sort(compare_ver)
return p[-1]
def find_info(ver):
t = tree(ver)
# 3.x and 2.x kernels must be handled differently
if t >= 3:
b = "%d" % t
else:
b = "%.1f" % t
f = forkname(ver)
p = pre(ver)
s = b
if f:
s = "%s-%s" % (b, f)
elif p:
s = "%s-rc" % b
return version_info[s]
def version_urls(ver):
""" Return the URL for the patch associated with the specified version """
i = find_info(ver)[1]
if type(i) != type([]):
i = [i]
v = {
'full': ver,
'tree': tree(ver),
'base': base(ver),
'prebase': prebase(ver),
'fork': fork(ver),
'revbase': revbase(ver),
'kernel_url': kernel_url
}
l = []
for e in i:
l.append(e % v)
return l
def patch_path(ver):
patch_name = os.path.basename(version_urls(ver)[0])
if re.search(r'\d+\.\d+', patch_name) == None:
suf = re.search(r'\.tar\.(gz|(bz(\d+)?))', patch_name).group(0)
patch_name = re.sub(r'(\.tar\.(gz|(bz(\d+)?)))', "-%s%s" % (ver, suf),
patch_name)
return os.path.join(archive, patch_name)
def download(url, f):
qprint("Downloading %s" % os.path.basename(url))
if options["dry-run"]:
return 1
if not options["wget"]:
p = urllib.urlopen(url).read()
if p.find("<title>404") != -1:
return None
open(f, 'w').write(p)
else:
e = os.system("%s -c -O %s %s" %
(options["wget"], f + ".partial", url))
if e & 255:
error("wget terminated by signal.")
sys.exit(1)
if e:
try:
# remove empty files
if os.path.getsize(f+".partial")==0:
os.unlink(f+".partial")
except OSError:
pass
return None
os.rename(f + ".partial", f)
return 1
def check_if_gpg_key_available(url, f, sign):
if options["no-gpg"] or options["dry-run"] or not options["gpg-path"]:
return 1
qprint("Check if GPG key is available...")
sf = f + sign
if not download(url+sign, sf):
sf = os.path.splitext(f)[0] + sign
sign_url = os.path.splitext(url)[0] + sign
if not download(sign_url, sf):
error("signature download failed")
return 0
process= subprocess.Popen([options["gpg-path"], "--no-tty", "--batch",
"--verify",sf,sf],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output, unused_err = process.communicate()
r = process.poll()
if r == 2: # key is not available
qprint(output)
error("The GPG key seems not to be in the keyring. Please fix this and
try again.")
qprint("In the case potential malicious kernel code is not a problem,\n"
"you can skip the verifying by using --no-gpg.")
return -1
if r < 0: # killed by signal
return -1
return 1
def verify(url, f, sign):
if options["no-gpg"] or options["dry-run"] or not options["gpg-path"]:
return 1
need_extract=False
sf = f + sign
sign_url = url + sign
if not os.path.isfile(sf) and not download(sign_url, sf):
sf = os.path.splitext(f)[0] + sign
sign_url = os.path.splitext(url)[0] + sign
need_extract=True
if not os.path.isfile(sf) and not download(sign_url, sf):
error("signature download failed")
error("removing files...")
os.unlink(sf)
return 0
qprint("Verifying signature...")
if need_extract:
if f[-4:] == ".bz2":
r = os.system("bzcat %s | %s --verify %s -" %
(f,options["gpg-path"], sf))
elif f[-3:] == ".gz":
r = os.system("zcat %s | %s --verify %s -" %
(f,options["gpg-path"], sf))
elif f[-3:] == ".xz":
r = os.system("xzcat %s | %s --verify %s -" %
(f,options["gpg-path"], sf))
else:
error("Cannot check signature: Unknown compression algorithm")
error("removing files...")
os.unlink(f)
os.unlink(sf)
return 1
else:
r = os.system("%s --verify %s %s" % (options["gpg-path"], sf, f))
if r:
error("gpg returned %d" % r)
error("removing files...")
os.unlink(f)
os.unlink(sf)
return 0
return 1
def trydownload(urls, f, sign):
for url in urls:
if sign:
result=check_if_gpg_key_available(url, f, sign)
if result < 0: # gpg key not available
return None
elif result==0: # download failed
continue
if os.path.isfile(f):
if not sign or verify(url, f, sign):
return f
if url[-4:] == ".bz2":
try_exts=['.xz', '.bz2', '.gz', '']
for ext in try_exts:
f2=f[:-4]+ext
if os.path.isfile(f2):
if not sign or verify(url, f2, sign):
return f2
for ext in try_exts:
f2=f[:-4]+ext
url2 = url[:-4] + ext
if download(url2, f2):
if not sign or verify(url2, f2, sign):
return f2
elif download(url, f):
if not sign or verify(url, f, sign):
return f
return None
def get_patch(ver):
"""Return the path to patch for given ver, downloading if necessary"""
f = patch_path(ver)
if os.path.exists(f):
return f
if re.search(r'-mm1', ver) != None:
if pre(find_ver('3-rc')) != pre(ver):
error("-mm1 patchset can only be applied to the latest -rc
release!")
sys.exit(-1)
if f[-4:] == ".bz2":
f2 = f[:-4] + ".xz"
if os.path.exists(f2):
return f2
f2 = f[:-4] + ".gz"
if os.path.exists(f2):
return f2
f2 = f[:-4]
if os.path.exists(f2):
return f2
urls = version_urls(ver)
sign = find_info(ver)[3]
if sign == 1: sign = ".sign"
f = trydownload(urls, f, sign)
if not f:
error("patch download failed")
sys.exit(-1)
return f
def apply_mmotm(ver, reverse = 0):
if reverse:
err = os.system("quilt pop -a &> .patchdiag")
if err:
sys.stderr.write(open(".patchdiag").read())
os.unlink(".patchdiag")
os.system("rm -rf patches .pc Next")
else:
old = os.getcwd()
os.mkdir("patches")
os.chdir("patches")
err = os.system("zcat %s | tar -xf -" % patch_path(ver))
if err:
error("Unpacking failed: ", err)
os.chdir(old)
os.system("rm -rf patches")
sys.exit(-1)
os.system("mv broken-out/*.patch ./")
os.chdir(old)
err = os.system("quilt push -a --leave-rejects &> .patchdiag")
if err:
sys.stderr.write(open(".patchdiag").read())
os.unlink(".patchdiag")
return err
def apply_patch(ver, reverse = 0):
"""Find the patch to upgrade from the predecessor of ver to ver and
apply or reverse it."""
p = get_patch(ver)
r = ""
if reverse:
r = " -R"
qprint("Applying %s%s" % (os.path.basename(p), r))
if options["dry-run"] or options["only-dl"]:
return ver
if forkname(ver) == "mm":
return apply_mmotm(ver, reverse)
def cmd(patch, reverse, dry):
base = "patch -l -p1%s" % reverse
if dry:
base += " --dry-run"
if p[-4:] == ".bz2":
pipe = "bzcat %s | %s" % (patch, base)
elif p[-3:] == ".gz":
pipe = "zcat %s | %s" % (patch, base)
elif p[-3:] == ".xz":
pipe = "xzcat %s | %s" % (patch, base)
else:
pipe = "%s < %s" % (base, patch)
err = os.system(pipe + " > .patchdiag")
if err:
sys.stderr.write(open(".patchdiag").read())
os.unlink(".patchdiag")
return err
err = cmd(p, r, 1)
if err:
if os.WIFSIGNALED(err):
error("patch %s failed: killed with signal %d" % (p,
os.WTERMSIG(err)))
else:
error("patch %s failed: return value %d" % (p, os.WEXITSTATUS(err)))
sys.exit(-1)
err = cmd(p, r, 0)
if err:
if os.WIFSIGNALED(err):
error("patch %s failed while it was supposed to apply: signal: %d"
% (p, os.WTERMSIG(err)))
else:
error("patch %s failed while it was supposed to apply: %d" % (p,
os.WEXITSTATUS(err)))
sys.exit(-1)
def untar(tarfile):
old = os.getcwd()
if os.path.exists("ketchup-tmp"):
error("Please remove the ketchup-tmp directory by hand.")
sys.exit(-1)
os.mkdir("ketchup-tmp")
try:
os.chdir("ketchup-tmp")
if tarfile[-4:] == ".bz2":
err = os.system("tar -jxf %s" % tarfile)
elif tarfile[-3:] == ".gz":
err = os.system("tar -zxf %s" % tarfile)
elif tarfile[-3:] == ".xz":
err = os.system("xzcat %s | tar -xf -" % tarfile)
elif tarfile[-4:] == ".tar":
err = os.system("tar -xf %s" % tarfile)
else:
err="Unknown compression algorithm."
if err:
error("Unpacking failed: ", err)
sys.exit(-1)
ldir = glob.glob("linux*")[0]
for f in os.listdir(ldir):
os.rename(ldir + "/" + f, "../" + f)
finally:
os.chdir(old)
shutil.rmtree("ketchup-tmp")
def install_nearest(ver):
t = tree(ver)
tarballs = glob.glob(archive + "/linux-%s.*.tar.xz" % t)
list = []
for f in tarballs:
m = re.match(r'.*/linux-(.*).tar.xz$', f)
v = m.group(1)
d = abs(rev(v) - rev(ver))
list.append((d, f, v))
list.sort()
# ugly hack, upstream dir is /v3.x (not v3)
# FIXME: we assume that this will be the case for linux-4.0 and later
releases
# if not, we'll have to fix it again :)
if t >= 3:
t = "%s.x" % t
if not list or (options["full-tarball"] and list[0][0]):
f = "linux-%s.tar.xz" % ver
url = "%s/v%s/%s" % (kernel_url, t, f)
url_longterm = "%s/v%s/longterm/v%s/%s" % (kernel_url, t, revbase(ver),
f)
f = archive + "/" + f
sign = find_info(ver)[3]
if sign == 1: sign = ".sign"
f = trydownload([url, url_longterm], f, sign)
if not f:
error("Tarball download failed")
sys.exit(-1)
else:
f = list[0][1]
ver = list[0][2]
qprint("Unpacking %s" % os.path.basename(f))
if options["dry-run"] or options["only-dl"]: return ver
untar(f)
return ver
def find_ver(ver):
if ver in version_info.keys():
v = version_info[ver]
d = v[1]
if not type(d) is type([]):
d = [d]
for n in d:
r = v[0](os.path.dirname(n), v[2])
if r:
return r
else:
return ver
def transform(a, b):
if a == b:
qprint("Nothing to do!")
return
if not b:
error("No version found.")
sys.exit(-1)
if not a:
a = install_nearest(base(b))
t = tree(a)
# 2.4 -> >=2.6 and >=2.6 -> 2.4 are not supported
if (t ==2.4 and tree(b) != 2.4) or (tree(b) == 2.4 and t != 2.4):
error("Can't patch %s to %s" % (tree(a), tree(b)))
sys.exit(-1)
if fork(a):
apply_patch(a, 1)
a = prebase(a)
if prebase(a) != prebase(b):
if pre(a):
apply_patch(a, 1)
a = base(a)
if post(a) and (post(a) != post(b) or rev(a) != rev(b)):
apply_patch(prebase(a), 1)
# handle the transition from 2.6.39 to 3.0
ra, rb = rev(a), rev(b)
if tree(a) == tree(b) and ra > rb:
for r in range(ra, rb, -1):
apply_patch("%s.%s" % (tree(a), r), -1)
if tree(a) == tree(b) and ra < rb:
for r in range(ra + 1, rb + 1):
apply_patch("%s.%s" % (tree(b), r))
if tree(a) > tree(b):
for r in range(ra, -1, -1):
apply_patch("%s.%s" % (tree(a), r), -1)
if tree(a)==2.6:
for r in range(39, rb, -1):
apply_patch("%s.%s" % (tree(b), r), -1)
else:
for r in range(19, rb, -1):
apply_patch("%s.%s" % (tree(b), r), -1)
if tree(b) > tree(a):
if tree(a)==2.6:
for r in range(ra + 1, 40):
apply_patch("%s.%s" % (tree(a), r))
if rb != 39:
for r in range(0, rb + 1, 1):
apply_patch("%s.%s" % (tree(b), r))
else:
for r in range(ra + 1, 20):
apply_patch("%s.%s" % (tree(a), r))
if rb != 19:
for r in range(0, rb + 1, 1):
apply_patch("%s.%s" % (tree(b), r))
a = revbase(b)
if post(b) and post(a) != post(b):
apply_patch(prebase(b), 0)
a = base(b)
if pre(b):
apply_patch(prebase(b))
a = prebase(b)
if fork(b):
a = apply_patch(b)
def rename_dir(v):
"""Rename the current directory to linux-v, where v is the function arg"""
if rename_with_localversion:
v += get_localversion()
cwd = os.getcwd()
basedir = os.path.dirname(cwd)
newdir = os.path.join(basedir, rename_prefix + v)
if newdir == cwd:
return
if os.access(newdir, os.F_OK):
error("Cannot rename directory, destination exists: %s", newdir);
return
os.rename(cwd, newdir)
qprint('Current directory renamed to %s' % newdir)
# latest lookup function, canonical urls, pattern for lookup function,
# signature flag, description
version_info = {
'2.4': (latest_dir,
"%(kernel_url)s" + "/v2.4" + "/patch-%(base)s.bz2",
r'patch-(.*?).bz2',
1, "2.4 kernel series"),
'2.6': (latest_dir,
["%(kernel_url)s" + "/v2.6" + "/patch-%(prebase)s.bz2",
"%(kernel_url)s" +
"/v2.6/longterm/v%(revbase)s/patch-%(prebase)s.bz2"],
r'linux-(.*?).tar.bz2',
1, "2.6 kernel series"),
'2.6-lt': (latest_dir_lt,
["%(kernel_url)s" + "/v2.6" + "/patch-%(prebase)s.bz2",
"%(kernel_url)s" +
"/v2.6/longterm/v%(revbase)s/patch-%(prebase)s.bz2"],
r'patch-(.*?).bz2',
1, "2.6 kernel series - update (only) to newer longterm stable
releases (fourth number of 2.6 kernels)"),
'2.6-rc': (latest_dir,
"%(kernel_url)s" + "/v2.6" + "/testing/patch-%(prebase)s.bz2",
r'linux-(.*?).tar.xz',
1, "2.6 kernel series prereleases"),
'2.6-rt': (latest_rt,
["%(kernel_url)s" +
"/projects/rt/%(revbase)s/patch-%(full)s.bz2",
"%(kernel_url)s" +
"/projects/rt/%(revbase)s/older/patch-%(full)s.bz2"],
r'patch-(2.6.*?).bz2',
1, "Ingo Molnar's realtime-preempt kernel"),
'2.6-pf': (latest_pf,
"http://pf.natalenko.name/sources" + "/%(revbase)s"
+ "/patch-%(full)s.bz2",
r'patch-(.*?).bz2',
0, "-pf kernel patchset"),
'3-pf': (latest_pf,
"http://pf.natalenko.name/sources" + "/%(revbase)s"
+ "/patch-%(full)s.bz2",
r'patch-(.*?).bz2',
0, "-pf kernel patchset"),
'4-pf': (latest_pf,
"http://pf.natalenko.name/sources" + "/%(revbase)s"
+ "/patch-%(full)s.bz2",
r'patch-(.*?).bz2',
0, "-pf kernel patchset"),
'3-next': (latest_dir,
"%(kernel_url)s" + "/next" + "/patch-v%(prebase)s%(fork)s.xz",
r'patch-v(.*?).xz',
1, "3 linux-next tree"),
'3-rc': (latest_dir,
"%(kernel_url)s" + "/v3.x" + "/testing/patch-%(prebase)s.xz",
r'patch-(.*?).xz',
1, "3.x stable kernel series prereleases"),
# current -next releases, for 4.x kernels
'4-next': (latest_dir,
"%(kernel_url)s" + "/next" + "/patch-v%(prebase)s%(fork)s.xz",
r'patch-v(.*?).xz',
1, "4 linux-next tree"),
'4-mm': (latest_mmotm,
"http://ozlabs.org/~akpm/mmotm/broken-out.tar.gz",
r'broken-out.tar.gz',
0, "-mmotm quilt patchset"),
'4-rc': (latest_dir,
"%(kernel_url)s" + "/v4.x" + "/testing/patch-%(prebase)s.xz",
r'patch-(.*?).xz',
1, "current stable kernel series prereleases"),
'3-rt': (latest_rt,
["%(kernel_url)s" +
"/projects/rt/%(revbase)s/patch-%(full)s.patch.xz",
"%(kernel_url)s" +
"/projects/rt/%(revbase)s/older/patch-%(full)s.patch.xz",
],
r'patch-(3(\.\d+)*-rt\d+).patch.xz', # TODO: add git-based releases
1, "PREEMPT_RT real-time kernel (only based directly on stable and
-rc kernels.)"),
'3-ck': (latest_ck,
"http://ck.kolivas.org/patches/3.0/" +
"/%(revbase)s/%(revbase)s%(fork)s/patch-%(revbase)s%(fork)s.xz",
"", 0,
"Con Kolivas' -ck patchset"),
'4-rt': (latest_rt,
["%(kernel_url)s" +
"/projects/rt/%(revbase)s/patch-%(full)s.patch.xz",
"%(kernel_url)s" +
"/projects/rt/%(revbase)s/older/patch-%(full)s.patch.xz",
],
r'patch-([456](\.\d+)*-rt\d+).patch.xz', # TODO: add git-based
releases
1, "PREEMPT_RT real-time kernel (only based directly on stable and
-rc kernels.)"),
'3-lt': (latest_dir_lt,
"%(kernel_url)s" + "/v3.x" + "/patch-%(prebase)s.xz",
r'patch-(.*?).xz',
1, "3.x kernel series - update (only) to newer longterm stable
releases (third number of 3.x kernels)"),
'4-lt': (latest_dir_lt,
"%(kernel_url)s" + "/v4.x" + "/patch-%(prebase)s.xz",
r'patch-(.*?).xz',
1, "4.x kernel series - update (only) to newer longterm stable
releases (third number of 4.x kernels)"),
'3': (latest_dir,
["%(kernel_url)s" + "/v3.x" + "/patch-%(prebase)s.xz"],
r'patch-(.*?).xz',
1, "last stable kernel series"),
'4': (latest_dir,
["%(kernel_url)s" + "/v4.x" + "/patch-%(prebase)s.xz"],
r'patch-(.*?).xz',
1, "current stable kernel series"),
'5': (latest_dir,
["%(kernel_url)s" + "/v5.x" + "/patch-%(prebase)s.xz"],
r'patch-(.*?).xz',
1, "current stable kernel series"),
}
# Override defaults with ~/.ketchuprc which is just a Python script
rcpath = os.path.expanduser('~/.ketchuprc')
if os.path.isfile(rcpath):
try:
execfile(rcpath)
except Exception as e:
sys.exit('Failed parsing %s\nError was: %s' % (rcpath, e))
# Add local trees
for k,v in local_trees.items():
version_info[k] = v
# Environment variables override defaults and ketchuprc
kernel_url = os.environ.get("KETCHUP_URL", kernel_url)
archive = os.environ.get("KETCHUP_ARCH", archive)
# And finally command line overrides everything
if not os.path.exists(wget): wget = ""
if not os.path.exists(gpg): gpg = ""
options = {}
opts = [
('a', 'archive', archive, 'cache directory'),
('d', 'directory', '.', 'directory to update'),
('f', 'full-tarball', None, 'if unpacking a tarball, download the latest'),
('g', 'gpg-path', gpg, 'path for GnuPG'),
('G', 'no-gpg', None, 'disable GPG signature verification'),
('k', 'kernel-url', kernel_url, 'base url for kernel.org mirror'),
('l', 'list-trees', None, 'list supported trees'),
('m', 'show-makefile', None, 'output version in makefile <arg>'),
('n', 'dry-run', None, 'don\'t download or apply patches'),
('o', 'only-dl', None, 'don\'t apply patches'),
('p', 'show-previous', None, 'output version previous to <arg>'),
('q', 'quiet', None, 'reduce output'),
('r', 'rename-directory', None, 'rename updated directory to %s<v>'
% rename_prefix),
('s', 'show-latest', None, 'output the latest version of <arg>'),
('u', 'show-url', None, 'output URL for <arg>'),
('w', 'wget', wget, 'command to use for wget'),
]
args = fancyopts(sys.argv[1:], opts, options,
'ketchup [options] [ver]')
archive = options["archive"]
kernel_url = options["kernel-url"]
if options["no-gpg"]: options["gpg-path"] = ''
# Process args
if not os.path.exists(options["directory"]):
qprint("Creating target directory", options["directory"])
os.mkdir(options["directory"])
os.chdir(options["directory"])
if os.path.isfile(".ketchuprc"):
try:
execfile(".ketchuprc")
except Exception as e:
sys.exit('Failed parsing .ketchuprc\nError was: %s' % (e))
if options["list-trees"]:
l = version_info.keys()
l.sort()
for tree in l:
if version_info[tree][3] == 0:
lprint(tree, "(unsigned)")
else:
lprint(tree, "(signed)")
lprint(" " + version_info[tree][4])
sys.exit(0)
if options["show-makefile"] and len(args) < 2:
if not args:
lprint(get_ver("Makefile"))
else:
lprint(get_ver(args[0]))
sys.exit(0)
if len(args) == 0 and default_tree:
qprint("Using default tree \"%s\"" % (default_tree))
args.append(default_tree)
if len(args) != 1:
error("No version given on command line and no default in configuration")
sys.exit(-1)
if options["show-latest"]:
lprint(find_ver(args[0]))
sys.exit(0)
if options["show-url"]:
lprint(version_urls(find_ver(args[0]))[0])
sys.exit(0)
if options["show-previous"]:
v = find_ver(args[0])
p = prebase(v)
if p == v: p = base(v)
if p == v:
if rev(v) > 0: p = "%.1f.%s" % (tree(v), rev(v) -1)
else: p = "unknown"
lprint(p)
sys.exit(0)
if not os.path.exists(options["archive"]):
qprint("Creating cache directory", options["archive"])
os.mkdir(options["archive"])
if precommand and os.system(precommand):
sys.exit('Precommand "%s" failed!' % precommand)
try:
a = get_ver('Makefile')
except:
a = None
if not a and os.listdir("."):
error("Can't find kernel version for non-empty directory")
sys.exit(-1)
b = find_ver(args[0])
qprint("%s -> %s" % (a, b))
transform(a, b)
if options["rename-directory"] and not options["dry-run"] and not
options["only-dl"] :
rename_dir(b)
if postcommand and os.system(postcommand):
sys.exit('Postcommand "%s" failed!' % postcommand)