#!/usr/bin/env python3 # # Copyright Bloomberg Finance LP # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . # # Authors: # Chandan Singh """A Source implementation for applying local patches **Usage:** .. code:: yaml # Specify the local source kind kind: patch # Specify the project relative path to a patch file path: files/somefile.diff # Optionally specify the root directory for the patch # directory: path/to/stage # Optionally specify the strip level, defaults to 1 strip-level: 1 """ import os from buildstream import Source, SourceError, Consistency from buildstream import utils class PatchSource(Source): def configure(self, node): self.path = self.node_get_member(node, str, "path") self.strip_level = self.node_get_member(node, int, "strip-level", 1) self.fullpath = os.path.join(self.get_project_directory(), self.path) def preflight(self): # Check if the configured file really exists if not os.path.exists(self.fullpath): raise SourceError("Specified path '{}' does not exist".format(self.path), reason="patch-no-exist") elif not os.path.isfile(self.fullpath): raise SourceError("Specified path '{}' must be a file".format(self.path), reason="patch-not-a-file") # Check if patch is installed, get the binary at the same time self.host_patch = utils.get_host_tool("patch") def get_unique_key(self): return [self.path, utils.sha256sum(self.fullpath), self.strip_level] def get_consistency(self): return Consistency.CACHED def get_ref(self): return None # pragma: nocover def set_ref(self, ref, node): pass # pragma: nocover def fetch(self): # Nothing to do here for a local source pass # pragma: nocover def stage(self, directory): with self.timed_activity("Applying local patch: {}".format(self.path)): if not os.path.isdir(directory): raise SourceError( "Patch directory '{}' does not exist".format(directory), reason="patch-no-directory" ) elif not os.listdir(directory): raise SourceError("Empty patch directory '{}'".format(directory), reason="patch-no-files") strip_level_option = "-p{}".format(self.strip_level) self.call([self.host_patch, strip_level_option, "-i", self.fullpath, "-d", directory], fail="Failed to apply patch {}".format(self.path)) # Plugin entry point def setup(): return PatchSource