diff options
Diffstat (limited to 'meta/lib/bblayers')
-rw-r--r-- | meta/lib/bblayers/buildconf.py | 84 | ||||
-rw-r--r-- | meta/lib/bblayers/create.py | 34 | ||||
-rw-r--r-- | meta/lib/bblayers/makesetup.py | 102 | ||||
-rw-r--r-- | meta/lib/bblayers/setupwriters/oe-setup-layers.py | 117 | ||||
-rw-r--r-- | meta/lib/bblayers/templates/README | 12 | ||||
-rw-r--r-- | meta/lib/bblayers/templates/example.bb | 4 | ||||
-rw-r--r-- | meta/lib/bblayers/templates/layer.conf | 15 |
7 files changed, 350 insertions, 18 deletions
diff --git a/meta/lib/bblayers/buildconf.py b/meta/lib/bblayers/buildconf.py new file mode 100644 index 0000000000..722cf0723c --- /dev/null +++ b/meta/lib/bblayers/buildconf.py @@ -0,0 +1,84 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import logging +import os +import sys + +from bblayers.common import LayerPlugin + +logger = logging.getLogger('bitbake-layers') + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +import oe.buildcfg + +def plugin_init(plugins): + return BuildConfPlugin() + +class BuildConfPlugin(LayerPlugin): + notes_fixme = """FIXME: Please place here the detailed instructions for using this build configuration. +They will be shown to the users when they set up their builds via TEMPLATECONF. +""" + summary_fixme = """FIXME: Please place here the short summary of what this build configuration is for. +It will be shown to the users when they set up their builds via TEMPLATECONF. +""" + + def _save_conf(self, templatename, templatepath, oecorepath, relpaths_to_oecore): + confdir = os.path.join(os.environ["BBPATH"], "conf") + destdir = os.path.join(templatepath, "conf", "templates", templatename) + os.makedirs(destdir, exist_ok=True) + + with open(os.path.join(confdir, "local.conf")) as src: + with open(os.path.join(destdir, "local.conf.sample"), 'w') as dest: + dest.write(src.read()) + + with open(os.path.join(confdir, "bblayers.conf")) as src: + with open(os.path.join(destdir, "bblayers.conf.sample"), 'w') as dest: + bblayers_data = src.read() + + for (abspath, relpath) in relpaths_to_oecore: + bblayers_data = bblayers_data.replace(abspath, "##OEROOT##/" + relpath) + dest.write(bblayers_data) + + with open(os.path.join(destdir, "conf-summary.txt"), 'w') as dest: + dest.write(self.summary_fixme) + with open(os.path.join(destdir, "conf-notes.txt"), 'w') as dest: + dest.write(self.notes_fixme) + + logger.info("""Configuration template placed into {} +Please review the files in there, and particularly provide a configuration summary in {} +and notes in {} +You can try out the configuration with +TEMPLATECONF={} . {}/oe-init-build-env build-try-{}""" +.format(destdir, os.path.join(destdir, "conf-summary.txt"), os.path.join(destdir, "conf-notes.txt"), destdir, oecorepath, templatename)) + + def do_save_build_conf(self, args): + """ Save the currently active build configuration (conf/local.conf, conf/bblayers.conf) as a template into a layer.\n This template can later be used for setting up builds via TEMPLATECONF. """ + layers = oe.buildcfg.get_layer_revisions(self.tinfoil.config_data) + targetlayer = None + oecore = None + + for l in layers: + if os.path.abspath(l[0]) == os.path.abspath(args.layerpath): + targetlayer = l[0] + if l[1] == 'meta': + oecore = os.path.dirname(l[0]) + + if not targetlayer: + logger.error("Layer {} not in one of the currently enabled layers:\n{}".format(args.layerpath, "\n".join([l[0] for l in layers]))) + elif not oecore: + logger.error("Openembedded-core not in one of the currently enabled layers:\n{}".format("\n".join([l[0] for l in layers]))) + else: + relpaths_to_oecore = [(l[0], os.path.relpath(l[0], start=oecore)) for l in layers] + self._save_conf(args.templatename, targetlayer, oecore, relpaths_to_oecore) + + def register_commands(self, sp): + parser_build_conf = self.add_command(sp, 'save-build-conf', self.do_save_build_conf, parserecipes=False) + parser_build_conf.add_argument('layerpath', + help='The path to the layer where the configuration template should be saved.') + parser_build_conf.add_argument('templatename', + help='The name of the configuration template.') diff --git a/meta/lib/bblayers/create.py b/meta/lib/bblayers/create.py index 6a41fe0504..517554c587 100644 --- a/meta/lib/bblayers/create.py +++ b/meta/lib/bblayers/create.py @@ -1,3 +1,9 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: GPL-2.0-only +# + import logging import os import sys @@ -6,6 +12,7 @@ import shutil import bb.utils from bblayers.common import LayerPlugin +from bblayers.action import ActionPlugin logger = logging.getLogger('bitbake-layers') @@ -30,8 +37,11 @@ class CreatePlugin(LayerPlugin): conf = os.path.join(layerdir, 'conf') bb.utils.mkdirhier(conf) + layername = os.path.basename(os.path.normpath(args.layerdir)) + layerid = args.layerid if args.layerid is not None else layername + # Create the README from templates/README - readme_template = read_template('README') % (args.layerdir, args.layerdir, args.layerdir, args.layerdir, args.layerdir, args.layerdir) + readme_template = read_template('README').format(layername=layername) readme = os.path.join(layerdir, 'README') with open(readme, 'w') as fd: fd.write(readme_template) @@ -43,8 +53,12 @@ class CreatePlugin(LayerPlugin): license_dst = os.path.join(layerdir, copying) shutil.copy(license_src, license_dst) + # Get the compat value for core layer. + compat = self.tinfoil.config_data.getVar('LAYERSERIES_CORENAMES') or "" + # Create the layer.conf from templates/layer.conf - layerconf_template = read_template('layer.conf') % (args.layerdir, args.layerdir, args.layerdir, args.priority) + layerconf_template = read_template('layer.conf').format( + layerid=layerid, priority=args.priority, compat=compat) layerconf = os.path.join(conf, 'layer.conf') with open(layerconf, 'w') as fd: fd.write(layerconf_template) @@ -53,14 +67,24 @@ class CreatePlugin(LayerPlugin): example_template = read_template('example.bb') example = os.path.join(layerdir, 'recipes-' + args.examplerecipe, args.examplerecipe) bb.utils.mkdirhier(example) - with open(os.path.join(example, args.examplerecipe + '.bb'), 'w') as fd: + with open(os.path.join(example, args.examplerecipe + '_%s.bb') % args.version, 'w') as fd: fd.write(example_template) - logger.plain('Add your new layer with \'bitbake-layers add-layer %s\'' % args.layerdir) + if args.add_layer: + # Add the layer to bblayers.conf + args.layerdir = [layerdir] + ActionPlugin.do_add_layer(self, args) + logger.plain('Layer added %s' % args.layerdir) + + else: + logger.plain('Add your new layer with \'bitbake-layers add-layer %s\'' % args.layerdir) def register_commands(self, sp): parser_create_layer = self.add_command(sp, 'create-layer', self.do_create_layer, parserecipes=False) parser_create_layer.add_argument('layerdir', help='Layer directory to create') - parser_create_layer.add_argument('--priority', '-p', default=6, help='Layer directory to create') + parser_create_layer.add_argument('--add-layer', '-a', action='store_true', help='Add the layer to bblayers.conf after creation') + parser_create_layer.add_argument('--layerid', '-i', help='Layer id to use if different from layername') + parser_create_layer.add_argument('--priority', '-p', default=6, help='Priority of recipes in layer') parser_create_layer.add_argument('--example-recipe-name', '-e', dest='examplerecipe', default='example', help='Filename of the example recipe') + parser_create_layer.add_argument('--example-recipe-version', '-v', dest='version', default='0.1', help='Version number for the example recipe') diff --git a/meta/lib/bblayers/makesetup.py b/meta/lib/bblayers/makesetup.py new file mode 100644 index 0000000000..99d5973760 --- /dev/null +++ b/meta/lib/bblayers/makesetup.py @@ -0,0 +1,102 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import logging +import os +import sys + +import bb.utils + +from bblayers.common import LayerPlugin + +logger = logging.getLogger('bitbake-layers') + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +import oe.buildcfg + +def plugin_init(plugins): + return MakeSetupPlugin() + +class MakeSetupPlugin(LayerPlugin): + + def _get_remotes_with_url(self, repo_path): + remotes = {} + for r in oe.buildcfg.get_metadata_git_remotes(repo_path): + remotes[r] = {'uri':oe.buildcfg.get_metadata_git_remote_url(repo_path, r)} + return remotes + + def _is_submodule(self, repo_path): + # This is slightly brittle: git does not offer a way to tell whether + # a given repo dir is a submodule checkout, so we need to rely on .git + # being a file (rather than a dir like it is in standalone checkouts). + # The file typically contains a gitdir pointer to elsewhere. + return os.path.isfile(os.path.join(repo_path,".git")) + + def make_repo_config(self, destdir): + """ This is a helper function for the writer plugins that discovers currently configured layers. + The writers do not have to use it, but it can save a bit of work and avoid duplicated code, hence it is + available here. """ + repos = {} + layers = oe.buildcfg.get_layer_revisions(self.tinfoil.config_data) + destdir_repo = oe.buildcfg.get_metadata_git_toplevel(destdir) + + for (l_path, l_name, l_branch, l_rev, l_ismodified) in layers: + if l_name == 'workspace': + continue + if l_ismodified: + logger.error("Layer {name} in {path} has uncommitted modifications or is not in a git repository.".format(name=l_name,path=l_path)) + return + repo_path = oe.buildcfg.get_metadata_git_toplevel(l_path) + + if self._is_submodule(repo_path): + continue + if repo_path not in repos.keys(): + repos[repo_path] = {'path':os.path.basename(repo_path),'git-remote':{ + 'rev':l_rev, + 'branch':l_branch, + 'remotes':self._get_remotes_with_url(repo_path), + 'describe':oe.buildcfg.get_metadata_git_describe(repo_path)}} + if repo_path == destdir_repo: + repos[repo_path]['contains_this_file'] = True + if not repos[repo_path]['git-remote']['remotes'] and not repos[repo_path]['contains_this_file']: + logger.error("Layer repository in {path} does not have any remotes configured. Please add at least one with 'git remote add'.".format(path=repo_path)) + return + + top_path = os.path.commonpath([os.path.dirname(r) for r in repos.keys()]) + + repos_nopaths = {} + for r in repos.keys(): + r_nopath = os.path.basename(r) + repos_nopaths[r_nopath] = repos[r] + r_relpath = os.path.relpath(r, top_path) + repos_nopaths[r_nopath]['path'] = r_relpath + return repos_nopaths + + def do_make_setup(self, args): + """ Writes out a configuration file and/or a script that replicate the directory structure and revisions of the layers in a current build. """ + for p in self.plugins: + if str(p) == args.writer: + p.do_write(self, args) + + def register_commands(self, sp): + parser_setup_layers = self.add_command(sp, 'create-layers-setup', self.do_make_setup, parserecipes=False) + parser_setup_layers.add_argument('destdir', + help='Directory where to write the output\n(if it is inside one of the layers, the layer becomes a bootstrap repository and thus will be excluded from fetching).') + parser_setup_layers.add_argument('--output-prefix', '-o', + help='File name prefix for the output files, if the default (setup-layers) is undesirable.') + + self.plugins = [] + + for path in (self.tinfoil.config_data.getVar('BBPATH').split(':')): + pluginpath = os.path.join(path, 'lib', 'bblayers', 'setupwriters') + bb.utils.load_plugins(logger, self.plugins, pluginpath) + + parser_setup_layers.add_argument('--writer', '-w', choices=[str(p) for p in self.plugins], help="Choose the output format (defaults to oe-setup-layers).\n\nCurrently supported options are:\noe-setup-layers - a self-contained python script and a json config for it.\n\n", default="oe-setup-layers") + + for plugin in self.plugins: + if hasattr(plugin, 'register_arguments'): + plugin.register_arguments(parser_setup_layers) diff --git a/meta/lib/bblayers/setupwriters/oe-setup-layers.py b/meta/lib/bblayers/setupwriters/oe-setup-layers.py new file mode 100644 index 0000000000..59ca968ff3 --- /dev/null +++ b/meta/lib/bblayers/setupwriters/oe-setup-layers.py @@ -0,0 +1,117 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: GPL-2.0-only +# + +import logging +import os +import json +import stat + +logger = logging.getLogger('bitbake-layers') + +def plugin_init(plugins): + return OeSetupLayersWriter() + +class OeSetupLayersWriter(): + + def __str__(self): + return "oe-setup-layers" + + def _write_python(self, input, output): + with open(input) as f: + script = f.read() + with open(output, 'w') as f: + f.write(script) + st = os.stat(output) + os.chmod(output, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) + + def _write_json(self, repos, output): + with open(output, 'w') as f: + json.dump(repos, f, sort_keys=True, indent=4) + + def _read_repo_config(self, json_path): + with open(json_path) as f: + json_config = json.load(f) + + supported_versions = ["1.0"] + if json_config["version"] not in supported_versions: + err = "File {} has version {}, which is not in supported versions: {}".format(json_path, json_config["version"], supported_versions) + logger.error(err) + raise Exception(err) + + return json_config + + def _modify_repo_config(self, json_config, args): + sources = json_config['sources'] + for pair in args.custom_references: + try: + repo, rev = pair.split(':', maxsplit=1) + except ValueError: + err = "Invalid custom reference specified: '{}'. Provide one using 'REPOSITORY:REFERENCE'.".format(pair) + logger.error(err) + raise Exception(err) + if not repo in sources.keys(): + err = "Repository {} does not exist in setup-layers config".format(repo) + logger.error(err) + raise Exception(err) + + layer_remote = json_config['sources'][repo]['git-remote'] + layer_remote['rev'] = rev + # Clear describe + layer_remote['describe'] = '' + + def do_write(self, parent, args): + """ Writes out a python script and a json config that replicate the directory structure and revisions of the layers in a current build. """ + output = args.output_prefix or "setup-layers" + output = os.path.join(os.path.abspath(args.destdir), output) + + if args.update: + # Modify existing layers setup + if args.custom_references is None: + err = "No custom reference specified. Please provide one using '--use-custom-reference REPOSITORY:REFERENCE'." + logger.error(err) + raise Exception(err) + + json = self._read_repo_config(output + ".json") + if not 'sources' in json.keys(): + err = "File {}.json does not contain valid layer sources.".format(output) + logger.error(err) + raise Exception(err) + + else: + # Create new layers setup + if not os.path.exists(args.destdir): + os.makedirs(args.destdir) + repos = parent.make_repo_config(args.destdir) + json = {"version":"1.0","sources":repos} + if not repos: + err = "Could not determine layer sources" + logger.error(err) + raise Exception(err) + + if args.custom_references is not None: + self._modify_repo_config(json, args) + + self._write_json(json, output + ".json") + logger.info('Created {}.json'.format(output)) + if not args.json_only: + self._write_python(os.path.join(os.path.dirname(__file__),'../../../../scripts/oe-setup-layers'), output) + logger.info('Created {}'.format(output)) + + def register_arguments(self, parser): + parser.add_argument('--json-only', action='store_true', + help='When using the oe-setup-layers writer, write only the layer configuruation in json format. Otherwise, also a copy of scripts/oe-setup-layers (from oe-core or poky) is provided, which is a self contained python script that fetches all the needed layers and sets them to correct revisions using the data from the json.') + + parser.add_argument('--update', '-u', + action='store_true', + help=("Instead of writing a new json file, update an existing layer setup json file with custom references provided via the '--use-custom-reference' option." + "\nThis will only update repositories for which a custom reference is specified, all other repositores will be left unchanged.")) + parser.add_argument('--use-custom-reference', '-r', + action='append', + dest='custom_references', + metavar='REPOSITORY:REFERENCE', + help=("A pair consisting of a repository and a custom reference to use for it (by default the currently checked out commit id would be written out)." + "\nThis value can be any reference that 'git checkout' would accept, and is not checked for validity." + "\nThis option can be used multiple times.")) diff --git a/meta/lib/bblayers/templates/README b/meta/lib/bblayers/templates/README index 5a77f8d347..fb2d28e177 100644 --- a/meta/lib/bblayers/templates/README +++ b/meta/lib/bblayers/templates/README @@ -1,4 +1,4 @@ -This README file contains information on the contents of the %s layer. +This README file contains information on the contents of the {layername} layer. Please see the corresponding sections below for details. @@ -18,7 +18,7 @@ Dependencies Patches ======= -Please submit any patches against the %s layer to the xxxx mailing list (xxxx@zzzz.org) +Please submit any patches against the {layername} layer to the xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer: Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> @@ -26,16 +26,16 @@ Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> Table of Contents ================= - I. Adding the %s layer to your build + I. Adding the {layername} layer to your build II. Misc -I. Adding the %s layer to your build +I. Adding the {layername} layer to your build ================================================= -Run 'bitbake-layers add-layer %s' +Run 'bitbake-layers add-layer {layername}' II. Misc ======== ---- replace with specific information about the %s layer --- +--- replace with specific information about the {layername} layer --- diff --git a/meta/lib/bblayers/templates/example.bb b/meta/lib/bblayers/templates/example.bb index c4b873d593..facaae35d2 100644 --- a/meta/lib/bblayers/templates/example.bb +++ b/meta/lib/bblayers/templates/example.bb @@ -2,10 +2,12 @@ SUMMARY = "bitbake-layers recipe" DESCRIPTION = "Recipe created by bitbake-layers" LICENSE = "MIT" -python do_build() { +python do_display_banner() { bb.plain("***********************************************"); bb.plain("* *"); bb.plain("* Example recipe created by bitbake-layers *"); bb.plain("* *"); bb.plain("***********************************************"); } + +addtask display_banner before do_build diff --git a/meta/lib/bblayers/templates/layer.conf b/meta/lib/bblayers/templates/layer.conf index 3c0300226c..dddfbf716e 100644 --- a/meta/lib/bblayers/templates/layer.conf +++ b/meta/lib/bblayers/templates/layer.conf @@ -1,10 +1,13 @@ # We have a conf and classes directory, add to BBPATH -BBPATH .= ":${LAYERDIR}" +BBPATH .= ":${{LAYERDIR}}" # We have recipes-* directories, add to BBFILES -BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ - ${LAYERDIR}/recipes-*/*/*.bbappend" +BBFILES += "${{LAYERDIR}}/recipes-*/*/*.bb \ + ${{LAYERDIR}}/recipes-*/*/*.bbappend" -BBFILE_COLLECTIONS += "%s" -BBFILE_PATTERN_%s = "^${LAYERDIR}/" -BBFILE_PRIORITY_%s = "%s" +BBFILE_COLLECTIONS += "{layerid}" +BBFILE_PATTERN_{layerid} = "^${{LAYERDIR}}/" +BBFILE_PRIORITY_{layerid} = "{priority}" + +LAYERDEPENDS_{layerid} = "core" +LAYERSERIES_COMPAT_{layerid} = "{compat}" |