blob: 1eece2b0501d61b5d1e7e8e5f351634288b6083f [file] [log] [blame]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors:
# Chandan Singh <csingh43@bloomberg.net>
# Tiago Gomes <tiago.gomes@codethink.co.uk>
#
# This plugin was originally developped in the https://github.com/apache/buildstream/
# repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6
#
"""
patch - apply locally stored patches
====================================
**Host dependencies:**
* patch
**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 strip level, defaults to 1
strip-level: 1
See `built-in functionality doumentation
<https://docs.buildstream.build/master/buildstream.source.html#core-source-builtins>`_ for
details on common configuration options for sources.
"""
import os
from buildstream import Source, SourceError
from buildstream import utils
class PatchSource(Source):
# pylint: disable=attribute-defined-outside-init
BST_MIN_VERSION = "2.0"
BST_REQUIRES_PREVIOUS_SOURCES_STAGE = True
def configure(self, node):
node.validate_keys(["path", "strip-level", *Source.COMMON_CONFIG_KEYS])
self.path = self.node_get_project_path(node.get_scalar("path"), check_is_file=True)
self.strip_level = node.get_int("strip-level", default=1)
self.fullpath = os.path.join(self.get_project_directory(), self.path)
def preflight(self):
# 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 is_resolved(self):
return True
def is_cached(self):
return True
def load_ref(self, node):
pass
def get_ref(self):
return None # pragma: nocover
def set_ref(self, ref, node):
pass # pragma: nocover
def fetch(self): # pylint: disable=arguments-differ
# 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)):
# Bail out with a comprehensive message if the target directory is empty
if not os.listdir(directory):
raise SourceError(
"Nothing to patch in 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