1c342db35SBrad Bishop# 2c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only 3c342db35SBrad Bishop# 4c342db35SBrad Bishop 5eb8dc403SDave Cobbleyimport oe.path 6eb8dc403SDave Cobbleyimport oe.types 7eb8dc403SDave Cobbley 8eb8dc403SDave Cobbleyclass NotFoundError(bb.BBHandledException): 9eb8dc403SDave Cobbley def __init__(self, path): 10eb8dc403SDave Cobbley self.path = path 11eb8dc403SDave Cobbley 12eb8dc403SDave Cobbley def __str__(self): 13eb8dc403SDave Cobbley return "Error: %s not found." % self.path 14eb8dc403SDave Cobbley 15eb8dc403SDave Cobbleyclass CmdError(bb.BBHandledException): 16eb8dc403SDave Cobbley def __init__(self, command, exitstatus, output): 17eb8dc403SDave Cobbley self.command = command 18eb8dc403SDave Cobbley self.status = exitstatus 19eb8dc403SDave Cobbley self.output = output 20eb8dc403SDave Cobbley 21eb8dc403SDave Cobbley def __str__(self): 22eb8dc403SDave Cobbley return "Command Error: '%s' exited with %d Output:\n%s" % \ 23eb8dc403SDave Cobbley (self.command, self.status, self.output) 24eb8dc403SDave Cobbley 25eb8dc403SDave Cobbley 26eb8dc403SDave Cobbleydef runcmd(args, dir = None): 27eb8dc403SDave Cobbley import pipes 281a4b7ee2SBrad Bishop import subprocess 29eb8dc403SDave Cobbley 30eb8dc403SDave Cobbley if dir: 31eb8dc403SDave Cobbley olddir = os.path.abspath(os.curdir) 32eb8dc403SDave Cobbley if not os.path.exists(dir): 33eb8dc403SDave Cobbley raise NotFoundError(dir) 34eb8dc403SDave Cobbley os.chdir(dir) 35eb8dc403SDave Cobbley # print("cwd: %s -> %s" % (olddir, dir)) 36eb8dc403SDave Cobbley 37eb8dc403SDave Cobbley try: 38eb8dc403SDave Cobbley args = [ pipes.quote(str(arg)) for arg in args ] 39eb8dc403SDave Cobbley cmd = " ".join(args) 40eb8dc403SDave Cobbley # print("cmd: %s" % cmd) 41*d1e89497SAndrew Geissler proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 42*d1e89497SAndrew Geissler stdout, stderr = proc.communicate() 43*d1e89497SAndrew Geissler stdout = stdout.decode('utf-8') 44*d1e89497SAndrew Geissler stderr = stderr.decode('utf-8') 45*d1e89497SAndrew Geissler exitstatus = proc.returncode 46eb8dc403SDave Cobbley if exitstatus != 0: 47*d1e89497SAndrew Geissler raise CmdError(cmd, exitstatus >> 8, "stdout: %s\nstderr: %s" % (stdout, stderr)) 48*d1e89497SAndrew Geissler if " fuzz " in stdout and "Hunk " in stdout: 49d89cb5f0SBrad Bishop # Drop patch fuzz info with header and footer to log file so 50d89cb5f0SBrad Bishop # insane.bbclass can handle to throw error/warning 51*d1e89497SAndrew Geissler bb.note("--- Patch fuzz start ---\n%s\n--- Patch fuzz end ---" % format(stdout)) 52eb8dc403SDave Cobbley 53*d1e89497SAndrew Geissler return stdout 54eb8dc403SDave Cobbley 55eb8dc403SDave Cobbley finally: 56eb8dc403SDave Cobbley if dir: 57eb8dc403SDave Cobbley os.chdir(olddir) 58eb8dc403SDave Cobbley 59eb8dc403SDave Cobbleyclass PatchError(Exception): 60eb8dc403SDave Cobbley def __init__(self, msg): 61eb8dc403SDave Cobbley self.msg = msg 62eb8dc403SDave Cobbley 63eb8dc403SDave Cobbley def __str__(self): 64eb8dc403SDave Cobbley return "Patch Error: %s" % self.msg 65eb8dc403SDave Cobbley 66eb8dc403SDave Cobbleyclass PatchSet(object): 67eb8dc403SDave Cobbley defaults = { 68eb8dc403SDave Cobbley "strippath": 1 69eb8dc403SDave Cobbley } 70eb8dc403SDave Cobbley 71eb8dc403SDave Cobbley def __init__(self, dir, d): 72eb8dc403SDave Cobbley self.dir = dir 73eb8dc403SDave Cobbley self.d = d 74eb8dc403SDave Cobbley self.patches = [] 75eb8dc403SDave Cobbley self._current = None 76eb8dc403SDave Cobbley 77eb8dc403SDave Cobbley def current(self): 78eb8dc403SDave Cobbley return self._current 79eb8dc403SDave Cobbley 80eb8dc403SDave Cobbley def Clean(self): 81eb8dc403SDave Cobbley """ 82eb8dc403SDave Cobbley Clean out the patch set. Generally includes unapplying all 83eb8dc403SDave Cobbley patches and wiping out all associated metadata. 84eb8dc403SDave Cobbley """ 85eb8dc403SDave Cobbley raise NotImplementedError() 86eb8dc403SDave Cobbley 87eb8dc403SDave Cobbley def Import(self, patch, force): 88eb8dc403SDave Cobbley if not patch.get("file"): 89eb8dc403SDave Cobbley if not patch.get("remote"): 90eb8dc403SDave Cobbley raise PatchError("Patch file must be specified in patch import.") 91eb8dc403SDave Cobbley else: 92eb8dc403SDave Cobbley patch["file"] = bb.fetch2.localpath(patch["remote"], self.d) 93eb8dc403SDave Cobbley 94eb8dc403SDave Cobbley for param in PatchSet.defaults: 95eb8dc403SDave Cobbley if not patch.get(param): 96eb8dc403SDave Cobbley patch[param] = PatchSet.defaults[param] 97eb8dc403SDave Cobbley 98eb8dc403SDave Cobbley if patch.get("remote"): 99eb8dc403SDave Cobbley patch["file"] = self.d.expand(bb.fetch2.localpath(patch["remote"], self.d)) 100eb8dc403SDave Cobbley 101eb8dc403SDave Cobbley patch["filemd5"] = bb.utils.md5_file(patch["file"]) 102eb8dc403SDave Cobbley 103eb8dc403SDave Cobbley def Push(self, force): 104eb8dc403SDave Cobbley raise NotImplementedError() 105eb8dc403SDave Cobbley 106eb8dc403SDave Cobbley def Pop(self, force): 107eb8dc403SDave Cobbley raise NotImplementedError() 108eb8dc403SDave Cobbley 109eb8dc403SDave Cobbley def Refresh(self, remote = None, all = None): 110eb8dc403SDave Cobbley raise NotImplementedError() 111eb8dc403SDave Cobbley 112eb8dc403SDave Cobbley @staticmethod 113eb8dc403SDave Cobbley def getPatchedFiles(patchfile, striplevel, srcdir=None): 114eb8dc403SDave Cobbley """ 115eb8dc403SDave Cobbley Read a patch file and determine which files it will modify. 116eb8dc403SDave Cobbley Params: 117eb8dc403SDave Cobbley patchfile: the patch file to read 118eb8dc403SDave Cobbley striplevel: the strip level at which the patch is going to be applied 119eb8dc403SDave Cobbley srcdir: optional path to join onto the patched file paths 120eb8dc403SDave Cobbley Returns: 121eb8dc403SDave Cobbley A list of tuples of file path and change mode ('A' for add, 122eb8dc403SDave Cobbley 'D' for delete or 'M' for modify) 123eb8dc403SDave Cobbley """ 124eb8dc403SDave Cobbley 125eb8dc403SDave Cobbley def patchedpath(patchline): 126eb8dc403SDave Cobbley filepth = patchline.split()[1] 127eb8dc403SDave Cobbley if filepth.endswith('/dev/null'): 128eb8dc403SDave Cobbley return '/dev/null' 129eb8dc403SDave Cobbley filesplit = filepth.split(os.sep) 130eb8dc403SDave Cobbley if striplevel > len(filesplit): 131eb8dc403SDave Cobbley bb.error('Patch %s has invalid strip level %d' % (patchfile, striplevel)) 132eb8dc403SDave Cobbley return None 133eb8dc403SDave Cobbley return os.sep.join(filesplit[striplevel:]) 134eb8dc403SDave Cobbley 135eb8dc403SDave Cobbley for encoding in ['utf-8', 'latin-1']: 136eb8dc403SDave Cobbley try: 137eb8dc403SDave Cobbley copiedmode = False 138eb8dc403SDave Cobbley filelist = [] 139eb8dc403SDave Cobbley with open(patchfile) as f: 140eb8dc403SDave Cobbley for line in f: 141eb8dc403SDave Cobbley if line.startswith('--- '): 142eb8dc403SDave Cobbley patchpth = patchedpath(line) 143eb8dc403SDave Cobbley if not patchpth: 144eb8dc403SDave Cobbley break 145eb8dc403SDave Cobbley if copiedmode: 146eb8dc403SDave Cobbley addedfile = patchpth 147eb8dc403SDave Cobbley else: 148eb8dc403SDave Cobbley removedfile = patchpth 149eb8dc403SDave Cobbley elif line.startswith('+++ '): 150eb8dc403SDave Cobbley addedfile = patchedpath(line) 151eb8dc403SDave Cobbley if not addedfile: 152eb8dc403SDave Cobbley break 153eb8dc403SDave Cobbley elif line.startswith('*** '): 154eb8dc403SDave Cobbley copiedmode = True 155eb8dc403SDave Cobbley removedfile = patchedpath(line) 156eb8dc403SDave Cobbley if not removedfile: 157eb8dc403SDave Cobbley break 158eb8dc403SDave Cobbley else: 159eb8dc403SDave Cobbley removedfile = None 160eb8dc403SDave Cobbley addedfile = None 161eb8dc403SDave Cobbley 162eb8dc403SDave Cobbley if addedfile and removedfile: 163eb8dc403SDave Cobbley if removedfile == '/dev/null': 164eb8dc403SDave Cobbley mode = 'A' 165eb8dc403SDave Cobbley elif addedfile == '/dev/null': 166eb8dc403SDave Cobbley mode = 'D' 167eb8dc403SDave Cobbley else: 168eb8dc403SDave Cobbley mode = 'M' 169eb8dc403SDave Cobbley if srcdir: 170eb8dc403SDave Cobbley fullpath = os.path.abspath(os.path.join(srcdir, addedfile)) 171eb8dc403SDave Cobbley else: 172eb8dc403SDave Cobbley fullpath = addedfile 173eb8dc403SDave Cobbley filelist.append((fullpath, mode)) 174eb8dc403SDave Cobbley except UnicodeDecodeError: 175eb8dc403SDave Cobbley continue 176eb8dc403SDave Cobbley break 177eb8dc403SDave Cobbley else: 178eb8dc403SDave Cobbley raise PatchError('Unable to decode %s' % patchfile) 179eb8dc403SDave Cobbley 180eb8dc403SDave Cobbley return filelist 181eb8dc403SDave Cobbley 182eb8dc403SDave Cobbley 183eb8dc403SDave Cobbleyclass PatchTree(PatchSet): 184eb8dc403SDave Cobbley def __init__(self, dir, d): 185eb8dc403SDave Cobbley PatchSet.__init__(self, dir, d) 186eb8dc403SDave Cobbley self.patchdir = os.path.join(self.dir, 'patches') 187eb8dc403SDave Cobbley self.seriespath = os.path.join(self.dir, 'patches', 'series') 188eb8dc403SDave Cobbley bb.utils.mkdirhier(self.patchdir) 189eb8dc403SDave Cobbley 190eb8dc403SDave Cobbley def _appendPatchFile(self, patch, strippath): 191eb8dc403SDave Cobbley with open(self.seriespath, 'a') as f: 192eb8dc403SDave Cobbley f.write(os.path.basename(patch) + "," + strippath + "\n") 193eb8dc403SDave Cobbley shellcmd = ["cat", patch, ">" , self.patchdir + "/" + os.path.basename(patch)] 194eb8dc403SDave Cobbley runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 195eb8dc403SDave Cobbley 196eb8dc403SDave Cobbley def _removePatch(self, p): 197eb8dc403SDave Cobbley patch = {} 198eb8dc403SDave Cobbley patch['file'] = p.split(",")[0] 199eb8dc403SDave Cobbley patch['strippath'] = p.split(",")[1] 200eb8dc403SDave Cobbley self._applypatch(patch, False, True) 201eb8dc403SDave Cobbley 202eb8dc403SDave Cobbley def _removePatchFile(self, all = False): 203eb8dc403SDave Cobbley if not os.path.exists(self.seriespath): 204eb8dc403SDave Cobbley return 205eb8dc403SDave Cobbley with open(self.seriespath, 'r+') as f: 206eb8dc403SDave Cobbley patches = f.readlines() 207eb8dc403SDave Cobbley if all: 208eb8dc403SDave Cobbley for p in reversed(patches): 209eb8dc403SDave Cobbley self._removePatch(os.path.join(self.patchdir, p.strip())) 210eb8dc403SDave Cobbley patches = [] 211eb8dc403SDave Cobbley else: 212eb8dc403SDave Cobbley self._removePatch(os.path.join(self.patchdir, patches[-1].strip())) 213eb8dc403SDave Cobbley patches.pop() 214eb8dc403SDave Cobbley with open(self.seriespath, 'w') as f: 215eb8dc403SDave Cobbley for p in patches: 216eb8dc403SDave Cobbley f.write(p) 217eb8dc403SDave Cobbley 218eb8dc403SDave Cobbley def Import(self, patch, force = None): 219eb8dc403SDave Cobbley """""" 220eb8dc403SDave Cobbley PatchSet.Import(self, patch, force) 221eb8dc403SDave Cobbley 222eb8dc403SDave Cobbley if self._current is not None: 223eb8dc403SDave Cobbley i = self._current + 1 224eb8dc403SDave Cobbley else: 225eb8dc403SDave Cobbley i = 0 226eb8dc403SDave Cobbley self.patches.insert(i, patch) 227eb8dc403SDave Cobbley 228eb8dc403SDave Cobbley def _applypatch(self, patch, force = False, reverse = False, run = True): 229eb8dc403SDave Cobbley shellcmd = ["cat", patch['file'], "|", "patch", "--no-backup-if-mismatch", "-p", patch['strippath']] 230eb8dc403SDave Cobbley if reverse: 231eb8dc403SDave Cobbley shellcmd.append('-R') 232eb8dc403SDave Cobbley 233eb8dc403SDave Cobbley if not run: 234eb8dc403SDave Cobbley return "sh" + "-c" + " ".join(shellcmd) 235eb8dc403SDave Cobbley 236eb8dc403SDave Cobbley if not force: 237eb8dc403SDave Cobbley shellcmd.append('--dry-run') 238eb8dc403SDave Cobbley 239eb8dc403SDave Cobbley try: 240eb8dc403SDave Cobbley output = runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 241eb8dc403SDave Cobbley 242eb8dc403SDave Cobbley if force: 243eb8dc403SDave Cobbley return 244eb8dc403SDave Cobbley 245eb8dc403SDave Cobbley shellcmd.pop(len(shellcmd) - 1) 246eb8dc403SDave Cobbley output = runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 247eb8dc403SDave Cobbley except CmdError as err: 248eb8dc403SDave Cobbley raise bb.BBHandledException("Applying '%s' failed:\n%s" % 249eb8dc403SDave Cobbley (os.path.basename(patch['file']), err.output)) 250eb8dc403SDave Cobbley 251eb8dc403SDave Cobbley if not reverse: 252eb8dc403SDave Cobbley self._appendPatchFile(patch['file'], patch['strippath']) 253eb8dc403SDave Cobbley 254eb8dc403SDave Cobbley return output 255eb8dc403SDave Cobbley 256eb8dc403SDave Cobbley def Push(self, force = False, all = False, run = True): 257eb8dc403SDave Cobbley bb.note("self._current is %s" % self._current) 258eb8dc403SDave Cobbley bb.note("patches is %s" % self.patches) 259eb8dc403SDave Cobbley if all: 260eb8dc403SDave Cobbley for i in self.patches: 261eb8dc403SDave Cobbley bb.note("applying patch %s" % i) 262eb8dc403SDave Cobbley self._applypatch(i, force) 263eb8dc403SDave Cobbley self._current = i 264eb8dc403SDave Cobbley else: 265eb8dc403SDave Cobbley if self._current is not None: 266eb8dc403SDave Cobbley next = self._current + 1 267eb8dc403SDave Cobbley else: 268eb8dc403SDave Cobbley next = 0 269eb8dc403SDave Cobbley 270eb8dc403SDave Cobbley bb.note("applying patch %s" % self.patches[next]) 271eb8dc403SDave Cobbley ret = self._applypatch(self.patches[next], force) 272eb8dc403SDave Cobbley 273eb8dc403SDave Cobbley self._current = next 274eb8dc403SDave Cobbley return ret 275eb8dc403SDave Cobbley 276eb8dc403SDave Cobbley def Pop(self, force = None, all = None): 277eb8dc403SDave Cobbley if all: 278eb8dc403SDave Cobbley self._removePatchFile(True) 279eb8dc403SDave Cobbley self._current = None 280eb8dc403SDave Cobbley else: 281eb8dc403SDave Cobbley self._removePatchFile(False) 282eb8dc403SDave Cobbley 283eb8dc403SDave Cobbley if self._current == 0: 284eb8dc403SDave Cobbley self._current = None 285eb8dc403SDave Cobbley 286eb8dc403SDave Cobbley if self._current is not None: 287eb8dc403SDave Cobbley self._current = self._current - 1 288eb8dc403SDave Cobbley 289eb8dc403SDave Cobbley def Clean(self): 290eb8dc403SDave Cobbley """""" 291eb8dc403SDave Cobbley self.Pop(all=True) 292eb8dc403SDave Cobbley 293eb8dc403SDave Cobbleyclass GitApplyTree(PatchTree): 294eb8dc403SDave Cobbley patch_line_prefix = '%% original patch' 295eb8dc403SDave Cobbley ignore_commit_prefix = '%% ignore' 296eb8dc403SDave Cobbley 297eb8dc403SDave Cobbley def __init__(self, dir, d): 298eb8dc403SDave Cobbley PatchTree.__init__(self, dir, d) 299eb8dc403SDave Cobbley self.commituser = d.getVar('PATCH_GIT_USER_NAME') 300eb8dc403SDave Cobbley self.commitemail = d.getVar('PATCH_GIT_USER_EMAIL') 301eb8dc403SDave Cobbley 302eb8dc403SDave Cobbley @staticmethod 303eb8dc403SDave Cobbley def extractPatchHeader(patchfile): 304eb8dc403SDave Cobbley """ 305eb8dc403SDave Cobbley Extract just the header lines from the top of a patch file 306eb8dc403SDave Cobbley """ 307eb8dc403SDave Cobbley for encoding in ['utf-8', 'latin-1']: 308eb8dc403SDave Cobbley lines = [] 309eb8dc403SDave Cobbley try: 310eb8dc403SDave Cobbley with open(patchfile, 'r', encoding=encoding) as f: 311eb8dc403SDave Cobbley for line in f: 312eb8dc403SDave Cobbley if line.startswith('Index: ') or line.startswith('diff -') or line.startswith('---'): 313eb8dc403SDave Cobbley break 314eb8dc403SDave Cobbley lines.append(line) 315eb8dc403SDave Cobbley except UnicodeDecodeError: 316eb8dc403SDave Cobbley continue 317eb8dc403SDave Cobbley break 318eb8dc403SDave Cobbley else: 319eb8dc403SDave Cobbley raise PatchError('Unable to find a character encoding to decode %s' % patchfile) 320eb8dc403SDave Cobbley return lines 321eb8dc403SDave Cobbley 322eb8dc403SDave Cobbley @staticmethod 323eb8dc403SDave Cobbley def decodeAuthor(line): 324eb8dc403SDave Cobbley from email.header import decode_header 325eb8dc403SDave Cobbley authorval = line.split(':', 1)[1].strip().replace('"', '') 326eb8dc403SDave Cobbley result = decode_header(authorval)[0][0] 327eb8dc403SDave Cobbley if hasattr(result, 'decode'): 328eb8dc403SDave Cobbley result = result.decode('utf-8') 329eb8dc403SDave Cobbley return result 330eb8dc403SDave Cobbley 331eb8dc403SDave Cobbley @staticmethod 332eb8dc403SDave Cobbley def interpretPatchHeader(headerlines): 333eb8dc403SDave Cobbley import re 33419323693SBrad Bishop author_re = re.compile(r'[\S ]+ <\S+@\S+\.\S+>') 33519323693SBrad Bishop from_commit_re = re.compile(r'^From [a-z0-9]{40} .*') 336eb8dc403SDave Cobbley outlines = [] 337eb8dc403SDave Cobbley author = None 338eb8dc403SDave Cobbley date = None 339eb8dc403SDave Cobbley subject = None 340eb8dc403SDave Cobbley for line in headerlines: 341eb8dc403SDave Cobbley if line.startswith('Subject: '): 342eb8dc403SDave Cobbley subject = line.split(':', 1)[1] 343eb8dc403SDave Cobbley # Remove any [PATCH][oe-core] etc. 344eb8dc403SDave Cobbley subject = re.sub(r'\[.+?\]\s*', '', subject) 345eb8dc403SDave Cobbley continue 346eb8dc403SDave Cobbley elif line.startswith('From: ') or line.startswith('Author: '): 347eb8dc403SDave Cobbley authorval = GitApplyTree.decodeAuthor(line) 348eb8dc403SDave Cobbley # git is fussy about author formatting i.e. it must be Name <email@domain> 349eb8dc403SDave Cobbley if author_re.match(authorval): 350eb8dc403SDave Cobbley author = authorval 351eb8dc403SDave Cobbley continue 352eb8dc403SDave Cobbley elif line.startswith('Date: '): 353eb8dc403SDave Cobbley if date is None: 354eb8dc403SDave Cobbley dateval = line.split(':', 1)[1].strip() 355eb8dc403SDave Cobbley # Very crude check for date format, since git will blow up if it's not in the right 356eb8dc403SDave Cobbley # format. Without e.g. a python-dateutils dependency we can't do a whole lot more 357eb8dc403SDave Cobbley if len(dateval) > 12: 358eb8dc403SDave Cobbley date = dateval 359eb8dc403SDave Cobbley continue 360eb8dc403SDave Cobbley elif not author and line.lower().startswith('signed-off-by: '): 361eb8dc403SDave Cobbley authorval = GitApplyTree.decodeAuthor(line) 362eb8dc403SDave Cobbley # git is fussy about author formatting i.e. it must be Name <email@domain> 363eb8dc403SDave Cobbley if author_re.match(authorval): 364eb8dc403SDave Cobbley author = authorval 365eb8dc403SDave Cobbley elif from_commit_re.match(line): 366eb8dc403SDave Cobbley # We don't want the From <commit> line - if it's present it will break rebasing 367eb8dc403SDave Cobbley continue 368eb8dc403SDave Cobbley outlines.append(line) 369eb8dc403SDave Cobbley 370eb8dc403SDave Cobbley if not subject: 371eb8dc403SDave Cobbley firstline = None 372eb8dc403SDave Cobbley for line in headerlines: 373eb8dc403SDave Cobbley line = line.strip() 374eb8dc403SDave Cobbley if firstline: 375eb8dc403SDave Cobbley if line: 376eb8dc403SDave Cobbley # Second line is not blank, the first line probably isn't usable 377eb8dc403SDave Cobbley firstline = None 378eb8dc403SDave Cobbley break 379eb8dc403SDave Cobbley elif line: 380eb8dc403SDave Cobbley firstline = line 381eb8dc403SDave Cobbley if firstline and not firstline.startswith(('#', 'Index:', 'Upstream-Status:')) and len(firstline) < 100: 382eb8dc403SDave Cobbley subject = firstline 383eb8dc403SDave Cobbley 384eb8dc403SDave Cobbley return outlines, author, date, subject 385eb8dc403SDave Cobbley 386eb8dc403SDave Cobbley @staticmethod 387eb8dc403SDave Cobbley def gitCommandUserOptions(cmd, commituser=None, commitemail=None, d=None): 388eb8dc403SDave Cobbley if d: 389eb8dc403SDave Cobbley commituser = d.getVar('PATCH_GIT_USER_NAME') 390eb8dc403SDave Cobbley commitemail = d.getVar('PATCH_GIT_USER_EMAIL') 391eb8dc403SDave Cobbley if commituser: 392eb8dc403SDave Cobbley cmd += ['-c', 'user.name="%s"' % commituser] 393eb8dc403SDave Cobbley if commitemail: 394eb8dc403SDave Cobbley cmd += ['-c', 'user.email="%s"' % commitemail] 395eb8dc403SDave Cobbley 396eb8dc403SDave Cobbley @staticmethod 397eb8dc403SDave Cobbley def prepareCommit(patchfile, commituser=None, commitemail=None): 398eb8dc403SDave Cobbley """ 399eb8dc403SDave Cobbley Prepare a git commit command line based on the header from a patch file 400eb8dc403SDave Cobbley (typically this is useful for patches that cannot be applied with "git am" due to formatting) 401eb8dc403SDave Cobbley """ 402eb8dc403SDave Cobbley import tempfile 403eb8dc403SDave Cobbley # Process patch header and extract useful information 404eb8dc403SDave Cobbley lines = GitApplyTree.extractPatchHeader(patchfile) 405eb8dc403SDave Cobbley outlines, author, date, subject = GitApplyTree.interpretPatchHeader(lines) 406eb8dc403SDave Cobbley if not author or not subject or not date: 407eb8dc403SDave Cobbley try: 408eb8dc403SDave Cobbley shellcmd = ["git", "log", "--format=email", "--follow", "--diff-filter=A", "--", patchfile] 409eb8dc403SDave Cobbley out = runcmd(["sh", "-c", " ".join(shellcmd)], os.path.dirname(patchfile)) 410eb8dc403SDave Cobbley except CmdError: 411eb8dc403SDave Cobbley out = None 412eb8dc403SDave Cobbley if out: 413eb8dc403SDave Cobbley _, newauthor, newdate, newsubject = GitApplyTree.interpretPatchHeader(out.splitlines()) 414eb8dc403SDave Cobbley if not author: 415eb8dc403SDave Cobbley # If we're setting the author then the date should be set as well 416eb8dc403SDave Cobbley author = newauthor 417eb8dc403SDave Cobbley date = newdate 418eb8dc403SDave Cobbley elif not date: 419eb8dc403SDave Cobbley # If we don't do this we'll get the current date, at least this will be closer 420eb8dc403SDave Cobbley date = newdate 421eb8dc403SDave Cobbley if not subject: 422eb8dc403SDave Cobbley subject = newsubject 4234ed12e16SAndrew Geissler if subject and not (outlines and outlines[0].strip() == subject): 424eb8dc403SDave Cobbley outlines.insert(0, '%s\n\n' % subject.strip()) 425eb8dc403SDave Cobbley 426eb8dc403SDave Cobbley # Write out commit message to a file 427eb8dc403SDave Cobbley with tempfile.NamedTemporaryFile('w', delete=False) as tf: 428eb8dc403SDave Cobbley tmpfile = tf.name 429eb8dc403SDave Cobbley for line in outlines: 430eb8dc403SDave Cobbley tf.write(line) 431eb8dc403SDave Cobbley # Prepare git command 432eb8dc403SDave Cobbley cmd = ["git"] 433eb8dc403SDave Cobbley GitApplyTree.gitCommandUserOptions(cmd, commituser, commitemail) 434eb8dc403SDave Cobbley cmd += ["commit", "-F", tmpfile] 435eb8dc403SDave Cobbley # git doesn't like plain email addresses as authors 436eb8dc403SDave Cobbley if author and '<' in author: 437eb8dc403SDave Cobbley cmd.append('--author="%s"' % author) 438eb8dc403SDave Cobbley if date: 439eb8dc403SDave Cobbley cmd.append('--date="%s"' % date) 440eb8dc403SDave Cobbley return (tmpfile, cmd) 441eb8dc403SDave Cobbley 442eb8dc403SDave Cobbley @staticmethod 443eb8dc403SDave Cobbley def extractPatches(tree, startcommit, outdir, paths=None): 444eb8dc403SDave Cobbley import tempfile 445eb8dc403SDave Cobbley import shutil 446eb8dc403SDave Cobbley tempdir = tempfile.mkdtemp(prefix='oepatch') 447eb8dc403SDave Cobbley try: 448eb8dc403SDave Cobbley shellcmd = ["git", "format-patch", "--no-signature", "--no-numbered", startcommit, "-o", tempdir] 449eb8dc403SDave Cobbley if paths: 450eb8dc403SDave Cobbley shellcmd.append('--') 451eb8dc403SDave Cobbley shellcmd.extend(paths) 452eb8dc403SDave Cobbley out = runcmd(["sh", "-c", " ".join(shellcmd)], tree) 453eb8dc403SDave Cobbley if out: 454eb8dc403SDave Cobbley for srcfile in out.split(): 455eb8dc403SDave Cobbley for encoding in ['utf-8', 'latin-1']: 456eb8dc403SDave Cobbley patchlines = [] 457eb8dc403SDave Cobbley outfile = None 458eb8dc403SDave Cobbley try: 459eb8dc403SDave Cobbley with open(srcfile, 'r', encoding=encoding) as f: 460eb8dc403SDave Cobbley for line in f: 4614ed12e16SAndrew Geissler if line.startswith(GitApplyTree.patch_line_prefix): 462eb8dc403SDave Cobbley outfile = line.split()[-1].strip() 463eb8dc403SDave Cobbley continue 4644ed12e16SAndrew Geissler if line.startswith(GitApplyTree.ignore_commit_prefix): 465eb8dc403SDave Cobbley continue 466eb8dc403SDave Cobbley patchlines.append(line) 467eb8dc403SDave Cobbley except UnicodeDecodeError: 468eb8dc403SDave Cobbley continue 469eb8dc403SDave Cobbley break 470eb8dc403SDave Cobbley else: 471eb8dc403SDave Cobbley raise PatchError('Unable to find a character encoding to decode %s' % srcfile) 472eb8dc403SDave Cobbley 473eb8dc403SDave Cobbley if not outfile: 474eb8dc403SDave Cobbley outfile = os.path.basename(srcfile) 475eb8dc403SDave Cobbley with open(os.path.join(outdir, outfile), 'w') as of: 476eb8dc403SDave Cobbley for line in patchlines: 477eb8dc403SDave Cobbley of.write(line) 478eb8dc403SDave Cobbley finally: 479eb8dc403SDave Cobbley shutil.rmtree(tempdir) 480eb8dc403SDave Cobbley 481eb8dc403SDave Cobbley def _applypatch(self, patch, force = False, reverse = False, run = True): 482eb8dc403SDave Cobbley import shutil 483eb8dc403SDave Cobbley 484eb8dc403SDave Cobbley def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True): 485eb8dc403SDave Cobbley if reverse: 486eb8dc403SDave Cobbley shellcmd.append('-R') 487eb8dc403SDave Cobbley 488eb8dc403SDave Cobbley shellcmd.append(patch['file']) 489eb8dc403SDave Cobbley 490eb8dc403SDave Cobbley if not run: 491eb8dc403SDave Cobbley return "sh" + "-c" + " ".join(shellcmd) 492eb8dc403SDave Cobbley 493eb8dc403SDave Cobbley return runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 494eb8dc403SDave Cobbley 495eb8dc403SDave Cobbley # Add hooks which add a pointer to the original patch file name in the commit message 496eb8dc403SDave Cobbley reporoot = (runcmd("git rev-parse --show-toplevel".split(), self.dir) or '').strip() 497eb8dc403SDave Cobbley if not reporoot: 498eb8dc403SDave Cobbley raise Exception("Cannot get repository root for directory %s" % self.dir) 499eb8dc403SDave Cobbley hooks_dir = os.path.join(reporoot, '.git', 'hooks') 500eb8dc403SDave Cobbley hooks_dir_backup = hooks_dir + '.devtool-orig' 501eb8dc403SDave Cobbley if os.path.lexists(hooks_dir_backup): 502eb8dc403SDave Cobbley raise Exception("Git hooks backup directory already exists: %s" % hooks_dir_backup) 503eb8dc403SDave Cobbley if os.path.lexists(hooks_dir): 504eb8dc403SDave Cobbley shutil.move(hooks_dir, hooks_dir_backup) 505eb8dc403SDave Cobbley os.mkdir(hooks_dir) 506eb8dc403SDave Cobbley commithook = os.path.join(hooks_dir, 'commit-msg') 507eb8dc403SDave Cobbley applyhook = os.path.join(hooks_dir, 'applypatch-msg') 508eb8dc403SDave Cobbley with open(commithook, 'w') as f: 509eb8dc403SDave Cobbley # NOTE: the formatting here is significant; if you change it you'll also need to 510eb8dc403SDave Cobbley # change other places which read it back 5114ed12e16SAndrew Geissler f.write('echo "\n%s: $PATCHFILE" >> $1' % GitApplyTree.patch_line_prefix) 512eb8dc403SDave Cobbley os.chmod(commithook, 0o755) 513eb8dc403SDave Cobbley shutil.copy2(commithook, applyhook) 514eb8dc403SDave Cobbley try: 515eb8dc403SDave Cobbley patchfilevar = 'PATCHFILE="%s"' % os.path.basename(patch['file']) 516eb8dc403SDave Cobbley try: 517eb8dc403SDave Cobbley shellcmd = [patchfilevar, "git", "--work-tree=%s" % reporoot] 518eb8dc403SDave Cobbley self.gitCommandUserOptions(shellcmd, self.commituser, self.commitemail) 519*d1e89497SAndrew Geissler shellcmd += ["am", "-3", "--keep-cr", "--no-scissors", "-p%s" % patch['strippath']] 520eb8dc403SDave Cobbley return _applypatchhelper(shellcmd, patch, force, reverse, run) 521eb8dc403SDave Cobbley except CmdError: 522eb8dc403SDave Cobbley # Need to abort the git am, or we'll still be within it at the end 523eb8dc403SDave Cobbley try: 524eb8dc403SDave Cobbley shellcmd = ["git", "--work-tree=%s" % reporoot, "am", "--abort"] 525eb8dc403SDave Cobbley runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 526eb8dc403SDave Cobbley except CmdError: 527eb8dc403SDave Cobbley pass 528eb8dc403SDave Cobbley # git am won't always clean up after itself, sadly, so... 529eb8dc403SDave Cobbley shellcmd = ["git", "--work-tree=%s" % reporoot, "reset", "--hard", "HEAD"] 530eb8dc403SDave Cobbley runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 531eb8dc403SDave Cobbley # Also need to take care of any stray untracked files 532eb8dc403SDave Cobbley shellcmd = ["git", "--work-tree=%s" % reporoot, "clean", "-f"] 533eb8dc403SDave Cobbley runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 534eb8dc403SDave Cobbley 535eb8dc403SDave Cobbley # Fall back to git apply 536eb8dc403SDave Cobbley shellcmd = ["git", "--git-dir=%s" % reporoot, "apply", "-p%s" % patch['strippath']] 537eb8dc403SDave Cobbley try: 538eb8dc403SDave Cobbley output = _applypatchhelper(shellcmd, patch, force, reverse, run) 539eb8dc403SDave Cobbley except CmdError: 540eb8dc403SDave Cobbley # Fall back to patch 541eb8dc403SDave Cobbley output = PatchTree._applypatch(self, patch, force, reverse, run) 542eb8dc403SDave Cobbley # Add all files 543eb8dc403SDave Cobbley shellcmd = ["git", "add", "-f", "-A", "."] 544eb8dc403SDave Cobbley output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 545eb8dc403SDave Cobbley # Exclude the patches directory 546eb8dc403SDave Cobbley shellcmd = ["git", "reset", "HEAD", self.patchdir] 547eb8dc403SDave Cobbley output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 548eb8dc403SDave Cobbley # Commit the result 549eb8dc403SDave Cobbley (tmpfile, shellcmd) = self.prepareCommit(patch['file'], self.commituser, self.commitemail) 550eb8dc403SDave Cobbley try: 551eb8dc403SDave Cobbley shellcmd.insert(0, patchfilevar) 552eb8dc403SDave Cobbley output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir) 553eb8dc403SDave Cobbley finally: 554eb8dc403SDave Cobbley os.remove(tmpfile) 555eb8dc403SDave Cobbley return output 556eb8dc403SDave Cobbley finally: 557eb8dc403SDave Cobbley shutil.rmtree(hooks_dir) 558eb8dc403SDave Cobbley if os.path.lexists(hooks_dir_backup): 559eb8dc403SDave Cobbley shutil.move(hooks_dir_backup, hooks_dir) 560eb8dc403SDave Cobbley 561eb8dc403SDave Cobbley 562eb8dc403SDave Cobbleyclass QuiltTree(PatchSet): 563eb8dc403SDave Cobbley def _runcmd(self, args, run = True): 564eb8dc403SDave Cobbley quiltrc = self.d.getVar('QUILTRCFILE') 565eb8dc403SDave Cobbley if not run: 566eb8dc403SDave Cobbley return ["quilt"] + ["--quiltrc"] + [quiltrc] + args 567eb8dc403SDave Cobbley runcmd(["quilt"] + ["--quiltrc"] + [quiltrc] + args, self.dir) 568eb8dc403SDave Cobbley 569eb8dc403SDave Cobbley def _quiltpatchpath(self, file): 570eb8dc403SDave Cobbley return os.path.join(self.dir, "patches", os.path.basename(file)) 571eb8dc403SDave Cobbley 572eb8dc403SDave Cobbley 573eb8dc403SDave Cobbley def __init__(self, dir, d): 574eb8dc403SDave Cobbley PatchSet.__init__(self, dir, d) 575eb8dc403SDave Cobbley self.initialized = False 576eb8dc403SDave Cobbley p = os.path.join(self.dir, 'patches') 577eb8dc403SDave Cobbley if not os.path.exists(p): 578eb8dc403SDave Cobbley os.makedirs(p) 579eb8dc403SDave Cobbley 580eb8dc403SDave Cobbley def Clean(self): 581eb8dc403SDave Cobbley try: 582eb8dc403SDave Cobbley self._runcmd(["pop", "-a", "-f"]) 583eb8dc403SDave Cobbley oe.path.remove(os.path.join(self.dir, "patches","series")) 584eb8dc403SDave Cobbley except Exception: 585eb8dc403SDave Cobbley pass 586eb8dc403SDave Cobbley self.initialized = True 587eb8dc403SDave Cobbley 588eb8dc403SDave Cobbley def InitFromDir(self): 589eb8dc403SDave Cobbley # read series -> self.patches 590eb8dc403SDave Cobbley seriespath = os.path.join(self.dir, 'patches', 'series') 591eb8dc403SDave Cobbley if not os.path.exists(self.dir): 592eb8dc403SDave Cobbley raise NotFoundError(self.dir) 593eb8dc403SDave Cobbley if os.path.exists(seriespath): 594eb8dc403SDave Cobbley with open(seriespath, 'r') as f: 595eb8dc403SDave Cobbley for line in f.readlines(): 596eb8dc403SDave Cobbley patch = {} 597eb8dc403SDave Cobbley parts = line.strip().split() 598eb8dc403SDave Cobbley patch["quiltfile"] = self._quiltpatchpath(parts[0]) 599eb8dc403SDave Cobbley patch["quiltfilemd5"] = bb.utils.md5_file(patch["quiltfile"]) 600eb8dc403SDave Cobbley if len(parts) > 1: 601eb8dc403SDave Cobbley patch["strippath"] = parts[1][2:] 602eb8dc403SDave Cobbley self.patches.append(patch) 603eb8dc403SDave Cobbley 604eb8dc403SDave Cobbley # determine which patches are applied -> self._current 605eb8dc403SDave Cobbley try: 606eb8dc403SDave Cobbley output = runcmd(["quilt", "applied"], self.dir) 607eb8dc403SDave Cobbley except CmdError: 608eb8dc403SDave Cobbley import sys 609eb8dc403SDave Cobbley if sys.exc_value.output.strip() == "No patches applied": 610eb8dc403SDave Cobbley return 611eb8dc403SDave Cobbley else: 612eb8dc403SDave Cobbley raise 613eb8dc403SDave Cobbley output = [val for val in output.split('\n') if not val.startswith('#')] 614eb8dc403SDave Cobbley for patch in self.patches: 615eb8dc403SDave Cobbley if os.path.basename(patch["quiltfile"]) == output[-1]: 616eb8dc403SDave Cobbley self._current = self.patches.index(patch) 617eb8dc403SDave Cobbley self.initialized = True 618eb8dc403SDave Cobbley 619eb8dc403SDave Cobbley def Import(self, patch, force = None): 620eb8dc403SDave Cobbley if not self.initialized: 621eb8dc403SDave Cobbley self.InitFromDir() 622eb8dc403SDave Cobbley PatchSet.Import(self, patch, force) 623eb8dc403SDave Cobbley oe.path.symlink(patch["file"], self._quiltpatchpath(patch["file"]), force=True) 624eb8dc403SDave Cobbley with open(os.path.join(self.dir, "patches", "series"), "a") as f: 625eb8dc403SDave Cobbley f.write(os.path.basename(patch["file"]) + " -p" + patch["strippath"] + "\n") 626eb8dc403SDave Cobbley patch["quiltfile"] = self._quiltpatchpath(patch["file"]) 627eb8dc403SDave Cobbley patch["quiltfilemd5"] = bb.utils.md5_file(patch["quiltfile"]) 628eb8dc403SDave Cobbley 629eb8dc403SDave Cobbley # TODO: determine if the file being imported: 630eb8dc403SDave Cobbley # 1) is already imported, and is the same 631eb8dc403SDave Cobbley # 2) is already imported, but differs 632eb8dc403SDave Cobbley 633eb8dc403SDave Cobbley self.patches.insert(self._current or 0, patch) 634eb8dc403SDave Cobbley 635eb8dc403SDave Cobbley 636eb8dc403SDave Cobbley def Push(self, force = False, all = False, run = True): 637eb8dc403SDave Cobbley # quilt push [-f] 638eb8dc403SDave Cobbley 639eb8dc403SDave Cobbley args = ["push"] 640eb8dc403SDave Cobbley if force: 641eb8dc403SDave Cobbley args.append("-f") 642eb8dc403SDave Cobbley if all: 643eb8dc403SDave Cobbley args.append("-a") 644eb8dc403SDave Cobbley if not run: 645eb8dc403SDave Cobbley return self._runcmd(args, run) 646eb8dc403SDave Cobbley 647eb8dc403SDave Cobbley self._runcmd(args) 648eb8dc403SDave Cobbley 649eb8dc403SDave Cobbley if self._current is not None: 650eb8dc403SDave Cobbley self._current = self._current + 1 651eb8dc403SDave Cobbley else: 652eb8dc403SDave Cobbley self._current = 0 653eb8dc403SDave Cobbley 654eb8dc403SDave Cobbley def Pop(self, force = None, all = None): 655eb8dc403SDave Cobbley # quilt pop [-f] 656eb8dc403SDave Cobbley args = ["pop"] 657eb8dc403SDave Cobbley if force: 658eb8dc403SDave Cobbley args.append("-f") 659eb8dc403SDave Cobbley if all: 660eb8dc403SDave Cobbley args.append("-a") 661eb8dc403SDave Cobbley 662eb8dc403SDave Cobbley self._runcmd(args) 663eb8dc403SDave Cobbley 664eb8dc403SDave Cobbley if self._current == 0: 665eb8dc403SDave Cobbley self._current = None 666eb8dc403SDave Cobbley 667eb8dc403SDave Cobbley if self._current is not None: 668eb8dc403SDave Cobbley self._current = self._current - 1 669eb8dc403SDave Cobbley 670eb8dc403SDave Cobbley def Refresh(self, **kwargs): 671eb8dc403SDave Cobbley if kwargs.get("remote"): 672eb8dc403SDave Cobbley patch = self.patches[kwargs["patch"]] 673eb8dc403SDave Cobbley if not patch: 674eb8dc403SDave Cobbley raise PatchError("No patch found at index %s in patchset." % kwargs["patch"]) 675eb8dc403SDave Cobbley (type, host, path, user, pswd, parm) = bb.fetch.decodeurl(patch["remote"]) 676eb8dc403SDave Cobbley if type == "file": 677eb8dc403SDave Cobbley import shutil 678eb8dc403SDave Cobbley if not patch.get("file") and patch.get("remote"): 679eb8dc403SDave Cobbley patch["file"] = bb.fetch2.localpath(patch["remote"], self.d) 680eb8dc403SDave Cobbley 681eb8dc403SDave Cobbley shutil.copyfile(patch["quiltfile"], patch["file"]) 682eb8dc403SDave Cobbley else: 683eb8dc403SDave Cobbley raise PatchError("Unable to do a remote refresh of %s, unsupported remote url scheme %s." % (os.path.basename(patch["quiltfile"]), type)) 684eb8dc403SDave Cobbley else: 685eb8dc403SDave Cobbley # quilt refresh 686eb8dc403SDave Cobbley args = ["refresh"] 687eb8dc403SDave Cobbley if kwargs.get("quiltfile"): 688eb8dc403SDave Cobbley args.append(os.path.basename(kwargs["quiltfile"])) 689eb8dc403SDave Cobbley elif kwargs.get("patch"): 690eb8dc403SDave Cobbley args.append(os.path.basename(self.patches[kwargs["patch"]]["quiltfile"])) 691eb8dc403SDave Cobbley self._runcmd(args) 692eb8dc403SDave Cobbley 693eb8dc403SDave Cobbleyclass Resolver(object): 694eb8dc403SDave Cobbley def __init__(self, patchset, terminal): 695eb8dc403SDave Cobbley raise NotImplementedError() 696eb8dc403SDave Cobbley 697eb8dc403SDave Cobbley def Resolve(self): 698eb8dc403SDave Cobbley raise NotImplementedError() 699eb8dc403SDave Cobbley 700eb8dc403SDave Cobbley def Revert(self): 701eb8dc403SDave Cobbley raise NotImplementedError() 702eb8dc403SDave Cobbley 703eb8dc403SDave Cobbley def Finalize(self): 704eb8dc403SDave Cobbley raise NotImplementedError() 705eb8dc403SDave Cobbley 706eb8dc403SDave Cobbleyclass NOOPResolver(Resolver): 707eb8dc403SDave Cobbley def __init__(self, patchset, terminal): 708eb8dc403SDave Cobbley self.patchset = patchset 709eb8dc403SDave Cobbley self.terminal = terminal 710eb8dc403SDave Cobbley 711eb8dc403SDave Cobbley def Resolve(self): 712eb8dc403SDave Cobbley olddir = os.path.abspath(os.curdir) 713eb8dc403SDave Cobbley os.chdir(self.patchset.dir) 714eb8dc403SDave Cobbley try: 715eb8dc403SDave Cobbley self.patchset.Push() 716eb8dc403SDave Cobbley except Exception: 717eb8dc403SDave Cobbley import sys 718eb8dc403SDave Cobbley os.chdir(olddir) 719eb8dc403SDave Cobbley raise 720eb8dc403SDave Cobbley 721eb8dc403SDave Cobbley# Patch resolver which relies on the user doing all the work involved in the 722eb8dc403SDave Cobbley# resolution, with the exception of refreshing the remote copy of the patch 723eb8dc403SDave Cobbley# files (the urls). 724eb8dc403SDave Cobbleyclass UserResolver(Resolver): 725eb8dc403SDave Cobbley def __init__(self, patchset, terminal): 726eb8dc403SDave Cobbley self.patchset = patchset 727eb8dc403SDave Cobbley self.terminal = terminal 728eb8dc403SDave Cobbley 729eb8dc403SDave Cobbley # Force a push in the patchset, then drop to a shell for the user to 730eb8dc403SDave Cobbley # resolve any rejected hunks 731eb8dc403SDave Cobbley def Resolve(self): 732eb8dc403SDave Cobbley olddir = os.path.abspath(os.curdir) 733eb8dc403SDave Cobbley os.chdir(self.patchset.dir) 734eb8dc403SDave Cobbley try: 735eb8dc403SDave Cobbley self.patchset.Push(False) 736eb8dc403SDave Cobbley except CmdError as v: 737eb8dc403SDave Cobbley # Patch application failed 738eb8dc403SDave Cobbley patchcmd = self.patchset.Push(True, False, False) 739eb8dc403SDave Cobbley 740eb8dc403SDave Cobbley t = self.patchset.d.getVar('T') 741eb8dc403SDave Cobbley if not t: 742eb8dc403SDave Cobbley bb.msg.fatal("Build", "T not set") 743eb8dc403SDave Cobbley bb.utils.mkdirhier(t) 744eb8dc403SDave Cobbley import random 745eb8dc403SDave Cobbley rcfile = "%s/bashrc.%s.%s" % (t, str(os.getpid()), random.random()) 746eb8dc403SDave Cobbley with open(rcfile, "w") as f: 747eb8dc403SDave Cobbley f.write("echo '*** Manual patch resolution mode ***'\n") 748eb8dc403SDave Cobbley f.write("echo 'Dropping to a shell, so patch rejects can be fixed manually.'\n") 749eb8dc403SDave Cobbley f.write("echo 'Run \"quilt refresh\" when patch is corrected, press CTRL+D to exit.'\n") 750eb8dc403SDave Cobbley f.write("echo ''\n") 751eb8dc403SDave Cobbley f.write(" ".join(patchcmd) + "\n") 752eb8dc403SDave Cobbley os.chmod(rcfile, 0o775) 753eb8dc403SDave Cobbley 754eb8dc403SDave Cobbley self.terminal("bash --rcfile " + rcfile, 'Patch Rejects: Please fix patch rejects manually', self.patchset.d) 755eb8dc403SDave Cobbley 756eb8dc403SDave Cobbley # Construct a new PatchSet after the user's changes, compare the 757eb8dc403SDave Cobbley # sets, checking patches for modifications, and doing a remote 758eb8dc403SDave Cobbley # refresh on each. 759eb8dc403SDave Cobbley oldpatchset = self.patchset 760eb8dc403SDave Cobbley self.patchset = oldpatchset.__class__(self.patchset.dir, self.patchset.d) 761eb8dc403SDave Cobbley 762eb8dc403SDave Cobbley for patch in self.patchset.patches: 763eb8dc403SDave Cobbley oldpatch = None 764eb8dc403SDave Cobbley for opatch in oldpatchset.patches: 765eb8dc403SDave Cobbley if opatch["quiltfile"] == patch["quiltfile"]: 766eb8dc403SDave Cobbley oldpatch = opatch 767eb8dc403SDave Cobbley 768eb8dc403SDave Cobbley if oldpatch: 769eb8dc403SDave Cobbley patch["remote"] = oldpatch["remote"] 770eb8dc403SDave Cobbley if patch["quiltfile"] == oldpatch["quiltfile"]: 771eb8dc403SDave Cobbley if patch["quiltfilemd5"] != oldpatch["quiltfilemd5"]: 772eb8dc403SDave Cobbley bb.note("Patch %s has changed, updating remote url %s" % (os.path.basename(patch["quiltfile"]), patch["remote"])) 773eb8dc403SDave Cobbley # user change? remote refresh 774eb8dc403SDave Cobbley self.patchset.Refresh(remote=True, patch=self.patchset.patches.index(patch)) 775eb8dc403SDave Cobbley else: 776eb8dc403SDave Cobbley # User did not fix the problem. Abort. 777eb8dc403SDave Cobbley raise PatchError("Patch application failed, and user did not fix and refresh the patch.") 778eb8dc403SDave Cobbley except Exception: 779eb8dc403SDave Cobbley os.chdir(olddir) 780eb8dc403SDave Cobbley raise 781eb8dc403SDave Cobbley os.chdir(olddir) 782eb8dc403SDave Cobbley 783eb8dc403SDave Cobbley 784eb8dc403SDave Cobbleydef patch_path(url, fetch, workdir, expand=True): 78519323693SBrad Bishop """Return the local path of a patch, or return nothing if this isn't a patch""" 786eb8dc403SDave Cobbley 787eb8dc403SDave Cobbley local = fetch.localpath(url) 78819323693SBrad Bishop if os.path.isdir(local): 78919323693SBrad Bishop return 790eb8dc403SDave Cobbley base, ext = os.path.splitext(os.path.basename(local)) 791eb8dc403SDave Cobbley if ext in ('.gz', '.bz2', '.xz', '.Z'): 792eb8dc403SDave Cobbley if expand: 793eb8dc403SDave Cobbley local = os.path.join(workdir, base) 794eb8dc403SDave Cobbley ext = os.path.splitext(base)[1] 795eb8dc403SDave Cobbley 796eb8dc403SDave Cobbley urldata = fetch.ud[url] 797eb8dc403SDave Cobbley if "apply" in urldata.parm: 798eb8dc403SDave Cobbley apply = oe.types.boolean(urldata.parm["apply"]) 799eb8dc403SDave Cobbley if not apply: 800eb8dc403SDave Cobbley return 801eb8dc403SDave Cobbley elif ext not in (".diff", ".patch"): 802eb8dc403SDave Cobbley return 803eb8dc403SDave Cobbley 804eb8dc403SDave Cobbley return local 805eb8dc403SDave Cobbley 806eb8dc403SDave Cobbleydef src_patches(d, all=False, expand=True): 807eb8dc403SDave Cobbley workdir = d.getVar('WORKDIR') 808eb8dc403SDave Cobbley fetch = bb.fetch2.Fetch([], d) 809eb8dc403SDave Cobbley patches = [] 810eb8dc403SDave Cobbley sources = [] 811eb8dc403SDave Cobbley for url in fetch.urls: 812eb8dc403SDave Cobbley local = patch_path(url, fetch, workdir, expand) 813eb8dc403SDave Cobbley if not local: 814eb8dc403SDave Cobbley if all: 815eb8dc403SDave Cobbley local = fetch.localpath(url) 816eb8dc403SDave Cobbley sources.append(local) 817eb8dc403SDave Cobbley continue 818eb8dc403SDave Cobbley 819eb8dc403SDave Cobbley urldata = fetch.ud[url] 820eb8dc403SDave Cobbley parm = urldata.parm 821eb8dc403SDave Cobbley patchname = parm.get('pname') or os.path.basename(local) 822eb8dc403SDave Cobbley 823eb8dc403SDave Cobbley apply, reason = should_apply(parm, d) 824eb8dc403SDave Cobbley if not apply: 825eb8dc403SDave Cobbley if reason: 826eb8dc403SDave Cobbley bb.note("Patch %s %s" % (patchname, reason)) 827eb8dc403SDave Cobbley continue 828eb8dc403SDave Cobbley 829eb8dc403SDave Cobbley patchparm = {'patchname': patchname} 830eb8dc403SDave Cobbley if "striplevel" in parm: 831eb8dc403SDave Cobbley striplevel = parm["striplevel"] 832eb8dc403SDave Cobbley elif "pnum" in parm: 833eb8dc403SDave Cobbley #bb.msg.warn(None, "Deprecated usage of 'pnum' url parameter in '%s', please use 'striplevel'" % url) 834eb8dc403SDave Cobbley striplevel = parm["pnum"] 835eb8dc403SDave Cobbley else: 836eb8dc403SDave Cobbley striplevel = '1' 837eb8dc403SDave Cobbley patchparm['striplevel'] = striplevel 838eb8dc403SDave Cobbley 839eb8dc403SDave Cobbley patchdir = parm.get('patchdir') 840eb8dc403SDave Cobbley if patchdir: 841eb8dc403SDave Cobbley patchparm['patchdir'] = patchdir 842eb8dc403SDave Cobbley 843eb8dc403SDave Cobbley localurl = bb.fetch.encodeurl(('file', '', local, '', '', patchparm)) 844eb8dc403SDave Cobbley patches.append(localurl) 845eb8dc403SDave Cobbley 846eb8dc403SDave Cobbley if all: 847eb8dc403SDave Cobbley return sources 848eb8dc403SDave Cobbley 849eb8dc403SDave Cobbley return patches 850eb8dc403SDave Cobbley 851eb8dc403SDave Cobbley 852eb8dc403SDave Cobbleydef should_apply(parm, d): 853c342db35SBrad Bishop import bb.utils 854eb8dc403SDave Cobbley if "mindate" in parm or "maxdate" in parm: 855eb8dc403SDave Cobbley pn = d.getVar('PN') 856eb8dc403SDave Cobbley srcdate = d.getVar('SRCDATE_%s' % pn) 857eb8dc403SDave Cobbley if not srcdate: 858eb8dc403SDave Cobbley srcdate = d.getVar('SRCDATE') 859eb8dc403SDave Cobbley 860eb8dc403SDave Cobbley if srcdate == "now": 861eb8dc403SDave Cobbley srcdate = d.getVar('DATE') 862eb8dc403SDave Cobbley 863eb8dc403SDave Cobbley if "maxdate" in parm and parm["maxdate"] < srcdate: 864eb8dc403SDave Cobbley return False, 'is outdated' 865eb8dc403SDave Cobbley 866eb8dc403SDave Cobbley if "mindate" in parm and parm["mindate"] > srcdate: 867eb8dc403SDave Cobbley return False, 'is predated' 868eb8dc403SDave Cobbley 869eb8dc403SDave Cobbley 870eb8dc403SDave Cobbley if "minrev" in parm: 871eb8dc403SDave Cobbley srcrev = d.getVar('SRCREV') 872eb8dc403SDave Cobbley if srcrev and srcrev < parm["minrev"]: 873eb8dc403SDave Cobbley return False, 'applies to later revisions' 874eb8dc403SDave Cobbley 875eb8dc403SDave Cobbley if "maxrev" in parm: 876eb8dc403SDave Cobbley srcrev = d.getVar('SRCREV') 877eb8dc403SDave Cobbley if srcrev and srcrev > parm["maxrev"]: 878eb8dc403SDave Cobbley return False, 'applies to earlier revisions' 879eb8dc403SDave Cobbley 880eb8dc403SDave Cobbley if "rev" in parm: 881eb8dc403SDave Cobbley srcrev = d.getVar('SRCREV') 882eb8dc403SDave Cobbley if srcrev and parm["rev"] not in srcrev: 883eb8dc403SDave Cobbley return False, "doesn't apply to revision" 884eb8dc403SDave Cobbley 885eb8dc403SDave Cobbley if "notrev" in parm: 886eb8dc403SDave Cobbley srcrev = d.getVar('SRCREV') 887eb8dc403SDave Cobbley if srcrev and parm["notrev"] in srcrev: 888eb8dc403SDave Cobbley return False, "doesn't apply to revision" 889eb8dc403SDave Cobbley 890c342db35SBrad Bishop if "maxver" in parm: 891c342db35SBrad Bishop pv = d.getVar('PV') 892c342db35SBrad Bishop if bb.utils.vercmp_string_op(pv, parm["maxver"], ">"): 893c342db35SBrad Bishop return False, "applies to earlier version" 894c342db35SBrad Bishop 895c342db35SBrad Bishop if "minver" in parm: 896c342db35SBrad Bishop pv = d.getVar('PV') 897c342db35SBrad Bishop if bb.utils.vercmp_string_op(pv, parm["minver"], "<"): 898c342db35SBrad Bishop return False, "applies to later version" 899c342db35SBrad Bishop 900eb8dc403SDave Cobbley return True, None 901eb8dc403SDave Cobbley 902