implements patch support for repos
With this commit its now possible to patch 3rd party repos before bitbake is started. Example: This is our repo: . ├── kas.yml ├── first-patch.patch └── quilt-patches ├── second-patch.patch ├── third-patch.patch └── series Content of kas.yml: header: version: 8 repos: my: third-party: url: "git://example.com/third-party.git" refspec: "35adf4...34" patches: 01-first: repo: my path: "first-patch.patch" 02-second: repo: my path: "quilt-patches" Currently only 'git' repositories can be patched. Signed-off-by: Claudius Heine <ch@denx.de>
This commit is contained in:
parent
b9032ec025
commit
e8851a5fb3
@ -66,3 +66,12 @@ Added
|
|||||||
|
|
||||||
- ``type`` property to ``repos`` to be able to express which version control
|
- ``type`` property to ``repos`` to be able to express which version control
|
||||||
system to use.
|
system to use.
|
||||||
|
|
||||||
|
Version 8
|
||||||
|
---------
|
||||||
|
|
||||||
|
Added
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- ``patches`` property to ``repos`` to be able to apply additional patches to
|
||||||
|
the repo.
|
||||||
|
@ -352,6 +352,22 @@ Configuration reference
|
|||||||
'0', 'false']``. This way it is possible to overwrite the inclusion
|
'0', 'false']``. This way it is possible to overwrite the inclusion
|
||||||
of a layer in latter loaded configuration files.
|
of a layer in latter loaded configuration files.
|
||||||
|
|
||||||
|
* ``patches``: dict [optional]
|
||||||
|
Contains the patches that should be applied to this repo before it is
|
||||||
|
used.
|
||||||
|
|
||||||
|
* ``<patches-id>``: dict [optional]
|
||||||
|
One entry in patches with its specific and unique id. All available
|
||||||
|
patch entries are applied in the order of their sorted
|
||||||
|
``<patches-id>``.
|
||||||
|
|
||||||
|
* ``repo``: string [required]
|
||||||
|
The identifier of the repo where the path of this entry is relative
|
||||||
|
to.
|
||||||
|
|
||||||
|
* ``path``: string [required]
|
||||||
|
The path to one patch file or a quilt formatted patchset directory.
|
||||||
|
|
||||||
* ``bblayers_conf_header``: dict [optional]
|
* ``bblayers_conf_header``: dict [optional]
|
||||||
This contains strings that should be added to the ``bblayers.conf`` before
|
This contains strings that should be added to the ``bblayers.conf`` before
|
||||||
any layers are included.
|
any layers are included.
|
||||||
|
@ -28,5 +28,5 @@ __copyright__ = 'Copyright (c) Siemens AG, 2017'
|
|||||||
__version__ = '0.17.0'
|
__version__ = '0.17.0'
|
||||||
|
|
||||||
# Please update docs/format-changelog.rst when changing the file version.
|
# Please update docs/format-changelog.rst when changing the file version.
|
||||||
__file_version__ = 7
|
__file_version__ = 8
|
||||||
__compatible_file_version__ = 1
|
__compatible_file_version__ = 1
|
||||||
|
@ -29,7 +29,7 @@ from .libkas import find_program, run_cmd, kasplugin
|
|||||||
from .libcmds import (Macro, Command, SetupDir, SetupProxy,
|
from .libcmds import (Macro, Command, SetupDir, SetupProxy,
|
||||||
CleanupSSHAgent, SetupSSHAgent, SetupEnviron,
|
CleanupSSHAgent, SetupSSHAgent, SetupEnviron,
|
||||||
WriteConfig, SetupHome, ReposFetch,
|
WriteConfig, SetupHome, ReposFetch,
|
||||||
ReposCheckout)
|
ReposApplyPatches, ReposCheckout)
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
||||||
@ -86,11 +86,12 @@ class Build:
|
|||||||
macro.add(ReposFetch())
|
macro.add(ReposFetch())
|
||||||
macro.add(ReposCheckout())
|
macro.add(ReposCheckout())
|
||||||
macro.add(SetupEnviron())
|
macro.add(SetupEnviron())
|
||||||
|
macro.add(SetupHome())
|
||||||
|
macro.add(ReposApplyPatches())
|
||||||
|
|
||||||
macro.add(WriteConfig())
|
macro.add(WriteConfig())
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
macro.add(SetupHome())
|
|
||||||
macro.add(BuildCommand(args.task))
|
macro.add(BuildCommand(args.task))
|
||||||
|
|
||||||
if 'SSH_PRIVATE_KEY' in os.environ:
|
if 'SSH_PRIVATE_KEY' in os.environ:
|
||||||
|
@ -142,6 +142,29 @@ CONFIGSCHEMA = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'patches': {
|
||||||
|
'type': 'object',
|
||||||
|
'additionalProperties': {
|
||||||
|
'oneOf': [
|
||||||
|
{
|
||||||
|
'type': 'object',
|
||||||
|
'additionalProperties': False,
|
||||||
|
'required': ['repo', 'path'],
|
||||||
|
'properties': {
|
||||||
|
'repo': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
'path': {
|
||||||
|
'type': 'string'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'null'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ import logging
|
|||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
from .libkas import (ssh_cleanup_agent, ssh_setup_agent, ssh_no_host_key_check,
|
from .libkas import (ssh_cleanup_agent, ssh_setup_agent, ssh_no_host_key_check,
|
||||||
get_build_environ, repos_fetch)
|
get_build_environ, repos_fetch, repos_apply_patches)
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
||||||
@ -92,6 +92,8 @@ class SetupHome(Command):
|
|||||||
fds.write('\n')
|
fds.write('\n')
|
||||||
with open(self.tmpdirname + '/.netrc', 'w') as fds:
|
with open(self.tmpdirname + '/.netrc', 'w') as fds:
|
||||||
fds.write('\n')
|
fds.write('\n')
|
||||||
|
shutil.copyfile(os.path.expanduser('~/.gitconfig'),
|
||||||
|
self.tmpdirname + '/.gitconfig')
|
||||||
config.environ['HOME'] = self.tmpdirname
|
config.environ['HOME'] = self.tmpdirname
|
||||||
|
|
||||||
|
|
||||||
@ -204,6 +206,18 @@ class ReposFetch(Command):
|
|||||||
repos_fetch(config, config.get_repos())
|
repos_fetch(config, config.get_repos())
|
||||||
|
|
||||||
|
|
||||||
|
class ReposApplyPatches(Command):
|
||||||
|
"""
|
||||||
|
Applies the patches defined in the configuration to the repositories.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'repos_apply_patches'
|
||||||
|
|
||||||
|
def execute(self, config):
|
||||||
|
repos_apply_patches(config, config.get_repos())
|
||||||
|
|
||||||
|
|
||||||
class ReposCheckout(Command):
|
class ReposCheckout(Command):
|
||||||
"""
|
"""
|
||||||
Ensures that the right revision of each repo is check out.
|
Ensures that the right revision of each repo is check out.
|
||||||
|
@ -184,6 +184,27 @@ def repos_fetch(config, repos):
|
|||||||
sys.exit(task.result())
|
sys.exit(task.result())
|
||||||
|
|
||||||
|
|
||||||
|
def repos_apply_patches(config, repos):
|
||||||
|
"""
|
||||||
|
Applies the patches to the repositories.
|
||||||
|
"""
|
||||||
|
tasks = []
|
||||||
|
for repo in repos:
|
||||||
|
if not hasattr(asyncio, 'ensure_future'):
|
||||||
|
# pylint: disable=no-member,deprecated-method
|
||||||
|
task = asyncio.async(repo.apply_patches_async(config))
|
||||||
|
else:
|
||||||
|
task = asyncio.ensure_future(repo.apply_patches_async(config))
|
||||||
|
tasks.append(task)
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(asyncio.wait(tasks))
|
||||||
|
|
||||||
|
for task in tasks:
|
||||||
|
if task.result():
|
||||||
|
sys.exit(task.result())
|
||||||
|
|
||||||
|
|
||||||
def get_build_environ(config, build_dir):
|
def get_build_environ(config, build_dir):
|
||||||
"""
|
"""
|
||||||
Create the build environment variables.
|
Create the build environment variables.
|
||||||
|
83
kas/repos.py
83
kas/repos.py
@ -38,12 +38,14 @@ class Repo:
|
|||||||
Represents a repository in the kas configuration.
|
Represents a repository in the kas configuration.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, url, path, refspec, layers, disable_operations):
|
def __init__(self, url, path, refspec, layers, patches,
|
||||||
|
disable_operations):
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
self.url = url
|
self.url = url
|
||||||
self.path = path
|
self.path = path
|
||||||
self.refspec = refspec
|
self.refspec = refspec
|
||||||
self._layers = layers
|
self._layers = layers
|
||||||
|
self._patches = patches
|
||||||
self.name = os.path.basename(self.path)
|
self.name = os.path.basename(self.path)
|
||||||
self.operations_disabled = disable_operations
|
self.operations_disabled = disable_operations
|
||||||
|
|
||||||
@ -78,6 +80,15 @@ class Repo:
|
|||||||
str(laydict[x]).lower() not in
|
str(laydict[x]).lower() not in
|
||||||
['disabled', 'excluded', 'n', 'no', '0', 'false'],
|
['disabled', 'excluded', 'n', 'no', '0', 'false'],
|
||||||
layers_dict))
|
layers_dict))
|
||||||
|
patches_dict = repo_config.get('patches', {})
|
||||||
|
patches = list(
|
||||||
|
{
|
||||||
|
'id': p,
|
||||||
|
'repo': patches_dict[p]['repo'],
|
||||||
|
'path': patches_dict[p]['path'],
|
||||||
|
}
|
||||||
|
for p in sorted(patches_dict)
|
||||||
|
if patches_dict[p])
|
||||||
url = repo_config.get('url', None)
|
url = repo_config.get('url', None)
|
||||||
name = repo_config.get('name', name)
|
name = repo_config.get('name', name)
|
||||||
typ = repo_config.get('type', 'git')
|
typ = repo_config.get('type', 'git')
|
||||||
@ -104,9 +115,10 @@ class Repo:
|
|||||||
path = os.path.join(config.kas_work_dir, path)
|
path = os.path.join(config.kas_work_dir, path)
|
||||||
|
|
||||||
if typ == 'git':
|
if typ == 'git':
|
||||||
return GitRepo(url, path, refspec, layers, disable_operations)
|
return GitRepo(url, path, refspec, layers, patches,
|
||||||
|
disable_operations)
|
||||||
if typ == 'hg':
|
if typ == 'hg':
|
||||||
return MercurialRepo(url, path, refspec, layers,
|
return MercurialRepo(url, path, refspec, layers, patches,
|
||||||
disable_operations)
|
disable_operations)
|
||||||
raise NotImplementedError('Repo typ "%s" not supported.' % typ)
|
raise NotImplementedError('Repo typ "%s" not supported.' % typ)
|
||||||
|
|
||||||
@ -209,6 +221,58 @@ class RepoImpl(Repo):
|
|||||||
|
|
||||||
run_cmd(self.checkout_cmd(), cwd=self.path)
|
run_cmd(self.checkout_cmd(), cwd=self.path)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def apply_patches_async(self, config):
|
||||||
|
"""
|
||||||
|
Applies patches to repository asynchronously.
|
||||||
|
"""
|
||||||
|
if self.operations_disabled:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
for patch in self._patches:
|
||||||
|
other_repo = config.repo_dict.get(patch['repo'], None)
|
||||||
|
|
||||||
|
if not other_repo:
|
||||||
|
logging.warning('Could not find referenced repo. '
|
||||||
|
'(missing repo: %s, repo: %s, '
|
||||||
|
'patch entry: %s)',
|
||||||
|
patch['repo'],
|
||||||
|
self.name,
|
||||||
|
patch['id'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
path = os.path.join(other_repo.path, patch['path'])
|
||||||
|
cmd = []
|
||||||
|
|
||||||
|
if os.path.isfile(path):
|
||||||
|
cmd = self.apply_patches_file_cmd(path)
|
||||||
|
elif os.path.isdir(path):
|
||||||
|
cmd = self.apply_patches_quilt_cmd(path)
|
||||||
|
else:
|
||||||
|
logging.warning('Could not find patch. '
|
||||||
|
'(patch path: %s, repo: %s, patch entry: %s)',
|
||||||
|
path,
|
||||||
|
self.name,
|
||||||
|
patch['id'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
(retc, output) = yield from run_cmd_async(cmd,
|
||||||
|
env=config.environ,
|
||||||
|
cwd=self.path,
|
||||||
|
fail=False)
|
||||||
|
|
||||||
|
if retc:
|
||||||
|
logging.error('Could not apply patch. Please fix repos and '
|
||||||
|
'patches. (patch path: %s, repo: %s, patch '
|
||||||
|
'entry: %s, vcs output: %s)',
|
||||||
|
path, self.name, patch['id'], output)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
logging.info('Patch applied. '
|
||||||
|
'(patch path: %s, repo: %s, patch entry: %s)',
|
||||||
|
path, self.name, patch['id'])
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class GitRepo(RepoImpl):
|
class GitRepo(RepoImpl):
|
||||||
"""
|
"""
|
||||||
@ -238,6 +302,13 @@ class GitRepo(RepoImpl):
|
|||||||
return ['git', 'checkout', '-q',
|
return ['git', 'checkout', '-q',
|
||||||
'{refspec}'.format(refspec=self.refspec)]
|
'{refspec}'.format(refspec=self.refspec)]
|
||||||
|
|
||||||
|
def apply_patches_file_cmd(self, path):
|
||||||
|
return ['git', 'am', '-q', path]
|
||||||
|
|
||||||
|
def apply_patches_quilt_cmd(self, path):
|
||||||
|
return ['git', 'quiltimport', '--author', 'kas <kas@example.com>',
|
||||||
|
'--patches', path]
|
||||||
|
|
||||||
|
|
||||||
class MercurialRepo(RepoImpl):
|
class MercurialRepo(RepoImpl):
|
||||||
"""
|
"""
|
||||||
@ -262,3 +333,9 @@ class MercurialRepo(RepoImpl):
|
|||||||
|
|
||||||
def checkout_cmd(self):
|
def checkout_cmd(self):
|
||||||
return ['hg', 'checkout', '{refspec}'.format(refspec=self.refspec)]
|
return ['hg', 'checkout', '{refspec}'.format(refspec=self.refspec)]
|
||||||
|
|
||||||
|
def apply_patches_file_cmd(self, path):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def apply_patches_quilt_cmd(self, path):
|
||||||
|
raise NotImplementedError()
|
||||||
|
12
kas/shell.py
12
kas/shell.py
@ -30,7 +30,7 @@ from kas.libkas import kasplugin
|
|||||||
from kas.config import Config
|
from kas.config import Config
|
||||||
from kas.libcmds import (Macro, Command, SetupDir, SetupProxy, SetupEnviron,
|
from kas.libcmds import (Macro, Command, SetupDir, SetupProxy, SetupEnviron,
|
||||||
WriteConfig, SetupHome, ReposFetch, ReposCheckout,
|
WriteConfig, SetupHome, ReposFetch, ReposCheckout,
|
||||||
CleanupSSHAgent, SetupSSHAgent)
|
ReposApplyPatches, CleanupSSHAgent, SetupSSHAgent)
|
||||||
|
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
__copyright__ = 'Copyright (c) Siemens AG, 2017'
|
||||||
@ -91,13 +91,15 @@ class Shell:
|
|||||||
if not args.keep_config_unchanged:
|
if not args.keep_config_unchanged:
|
||||||
macro.add(ReposFetch())
|
macro.add(ReposFetch())
|
||||||
macro.add(ReposCheckout())
|
macro.add(ReposCheckout())
|
||||||
macro.add(SetupEnviron())
|
|
||||||
|
macro.add(SetupEnviron())
|
||||||
|
macro.add(SetupHome())
|
||||||
|
|
||||||
|
if not args.keep_config_unchanged:
|
||||||
|
macro.add(ReposApplyPatches())
|
||||||
macro.add(WriteConfig())
|
macro.add(WriteConfig())
|
||||||
else:
|
|
||||||
macro.add(SetupEnviron())
|
|
||||||
|
|
||||||
# Shell
|
# Shell
|
||||||
macro.add(SetupHome())
|
|
||||||
macro.add(ShellCommand(args.command))
|
macro.add(ShellCommand(args.command))
|
||||||
|
|
||||||
if 'SSH_PRIVATE_KEY' in os.environ:
|
if 'SSH_PRIVATE_KEY' in os.environ:
|
||||||
|
Loading…
Reference in New Issue
Block a user