summaryrefslogtreecommitdiffstats
path: root/meta/lib/bblayers
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/bblayers')
-rw-r--r--meta/lib/bblayers/buildconf.py84
-rw-r--r--meta/lib/bblayers/create.py21
-rw-r--r--meta/lib/bblayers/makesetup.py102
-rw-r--r--meta/lib/bblayers/setupwriters/oe-setup-layers.py117
-rw-r--r--meta/lib/bblayers/templates/layer.conf10
5 files changed, 325 insertions, 9 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 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import logging
8import os
9import sys
10
11from bblayers.common import LayerPlugin
12
13logger = logging.getLogger('bitbake-layers')
14
15sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
16
17import oe.buildcfg
18
19def plugin_init(plugins):
20 return BuildConfPlugin()
21
22class BuildConfPlugin(LayerPlugin):
23 notes_fixme = """FIXME: Please place here the detailed instructions for using this build configuration.
24They will be shown to the users when they set up their builds via TEMPLATECONF.
25"""
26 summary_fixme = """FIXME: Please place here the short summary of what this build configuration is for.
27It will be shown to the users when they set up their builds via TEMPLATECONF.
28"""
29
30 def _save_conf(self, templatename, templatepath, oecorepath, relpaths_to_oecore):
31 confdir = os.path.join(os.environ["BBPATH"], "conf")
32 destdir = os.path.join(templatepath, "conf", "templates", templatename)
33 os.makedirs(destdir, exist_ok=True)
34
35 with open(os.path.join(confdir, "local.conf")) as src:
36 with open(os.path.join(destdir, "local.conf.sample"), 'w') as dest:
37 dest.write(src.read())
38
39 with open(os.path.join(confdir, "bblayers.conf")) as src:
40 with open(os.path.join(destdir, "bblayers.conf.sample"), 'w') as dest:
41 bblayers_data = src.read()
42
43 for (abspath, relpath) in relpaths_to_oecore:
44 bblayers_data = bblayers_data.replace(abspath, "##OEROOT##/" + relpath)
45 dest.write(bblayers_data)
46
47 with open(os.path.join(destdir, "conf-summary.txt"), 'w') as dest:
48 dest.write(self.summary_fixme)
49 with open(os.path.join(destdir, "conf-notes.txt"), 'w') as dest:
50 dest.write(self.notes_fixme)
51
52 logger.info("""Configuration template placed into {}
53Please review the files in there, and particularly provide a configuration summary in {}
54and notes in {}
55You can try out the configuration with
56TEMPLATECONF={} . {}/oe-init-build-env build-try-{}"""
57.format(destdir, os.path.join(destdir, "conf-summary.txt"), os.path.join(destdir, "conf-notes.txt"), destdir, oecorepath, templatename))
58
59 def do_save_build_conf(self, args):
60 """ 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. """
61 layers = oe.buildcfg.get_layer_revisions(self.tinfoil.config_data)
62 targetlayer = None
63 oecore = None
64
65 for l in layers:
66 if os.path.abspath(l[0]) == os.path.abspath(args.layerpath):
67 targetlayer = l[0]
68 if l[1] == 'meta':
69 oecore = os.path.dirname(l[0])
70
71 if not targetlayer:
72 logger.error("Layer {} not in one of the currently enabled layers:\n{}".format(args.layerpath, "\n".join([l[0] for l in layers])))
73 elif not oecore:
74 logger.error("Openembedded-core not in one of the currently enabled layers:\n{}".format("\n".join([l[0] for l in layers])))
75 else:
76 relpaths_to_oecore = [(l[0], os.path.relpath(l[0], start=oecore)) for l in layers]
77 self._save_conf(args.templatename, targetlayer, oecore, relpaths_to_oecore)
78
79 def register_commands(self, sp):
80 parser_build_conf = self.add_command(sp, 'save-build-conf', self.do_save_build_conf, parserecipes=False)
81 parser_build_conf.add_argument('layerpath',
82 help='The path to the layer where the configuration template should be saved.')
83 parser_build_conf.add_argument('templatename',
84 help='The name of the configuration template.')
diff --git a/meta/lib/bblayers/create.py b/meta/lib/bblayers/create.py
index 542f31fc81..517554c587 100644
--- a/meta/lib/bblayers/create.py
+++ b/meta/lib/bblayers/create.py
@@ -1,4 +1,6 @@
1# 1#
2# Copyright OpenEmbedded Contributors
3#
2# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
3# 5#
4 6
@@ -10,6 +12,7 @@ import shutil
10import bb.utils 12import bb.utils
11 13
12from bblayers.common import LayerPlugin 14from bblayers.common import LayerPlugin
15from bblayers.action import ActionPlugin
13 16
14logger = logging.getLogger('bitbake-layers') 17logger = logging.getLogger('bitbake-layers')
15 18
@@ -35,6 +38,7 @@ class CreatePlugin(LayerPlugin):
35 bb.utils.mkdirhier(conf) 38 bb.utils.mkdirhier(conf)
36 39
37 layername = os.path.basename(os.path.normpath(args.layerdir)) 40 layername = os.path.basename(os.path.normpath(args.layerdir))
41 layerid = args.layerid if args.layerid is not None else layername
38 42
39 # Create the README from templates/README 43 # Create the README from templates/README
40 readme_template = read_template('README').format(layername=layername) 44 readme_template = read_template('README').format(layername=layername)
@@ -50,11 +54,11 @@ class CreatePlugin(LayerPlugin):
50 shutil.copy(license_src, license_dst) 54 shutil.copy(license_src, license_dst)
51 55
52 # Get the compat value for core layer. 56 # Get the compat value for core layer.
53 compat = self.tinfoil.config_data.getVar('LAYERSERIES_COMPAT_core') or "" 57 compat = self.tinfoil.config_data.getVar('LAYERSERIES_CORENAMES') or ""
54 58
55 # Create the layer.conf from templates/layer.conf 59 # Create the layer.conf from templates/layer.conf
56 layerconf_template = read_template('layer.conf').format( 60 layerconf_template = read_template('layer.conf').format(
57 layername=layername, priority=args.priority, compat=compat) 61 layerid=layerid, priority=args.priority, compat=compat)
58 layerconf = os.path.join(conf, 'layer.conf') 62 layerconf = os.path.join(conf, 'layer.conf')
59 with open(layerconf, 'w') as fd: 63 with open(layerconf, 'w') as fd:
60 fd.write(layerconf_template) 64 fd.write(layerconf_template)
@@ -66,12 +70,21 @@ class CreatePlugin(LayerPlugin):
66 with open(os.path.join(example, args.examplerecipe + '_%s.bb') % args.version, 'w') as fd: 70 with open(os.path.join(example, args.examplerecipe + '_%s.bb') % args.version, 'w') as fd:
67 fd.write(example_template) 71 fd.write(example_template)
68 72
69 logger.plain('Add your new layer with \'bitbake-layers add-layer %s\'' % args.layerdir) 73 if args.add_layer:
74 # Add the layer to bblayers.conf
75 args.layerdir = [layerdir]
76 ActionPlugin.do_add_layer(self, args)
77 logger.plain('Layer added %s' % args.layerdir)
78
79 else:
80 logger.plain('Add your new layer with \'bitbake-layers add-layer %s\'' % args.layerdir)
70 81
71 def register_commands(self, sp): 82 def register_commands(self, sp):
72 parser_create_layer = self.add_command(sp, 'create-layer', self.do_create_layer, parserecipes=False) 83 parser_create_layer = self.add_command(sp, 'create-layer', self.do_create_layer, parserecipes=False)
73 parser_create_layer.add_argument('layerdir', help='Layer directory to create') 84 parser_create_layer.add_argument('layerdir', help='Layer directory to create')
74 parser_create_layer.add_argument('--priority', '-p', default=6, help='Layer directory to create') 85 parser_create_layer.add_argument('--add-layer', '-a', action='store_true', help='Add the layer to bblayers.conf after creation')
86 parser_create_layer.add_argument('--layerid', '-i', help='Layer id to use if different from layername')
87 parser_create_layer.add_argument('--priority', '-p', default=6, help='Priority of recipes in layer')
75 parser_create_layer.add_argument('--example-recipe-name', '-e', dest='examplerecipe', default='example', help='Filename of the example recipe') 88 parser_create_layer.add_argument('--example-recipe-name', '-e', dest='examplerecipe', default='example', help='Filename of the example recipe')
76 parser_create_layer.add_argument('--example-recipe-version', '-v', dest='version', default='0.1', help='Version number for the example recipe') 89 parser_create_layer.add_argument('--example-recipe-version', '-v', dest='version', default='0.1', help='Version number for the example recipe')
77 90
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 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import logging
8import os
9import sys
10
11import bb.utils
12
13from bblayers.common import LayerPlugin
14
15logger = logging.getLogger('bitbake-layers')
16
17sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
18
19import oe.buildcfg
20
21def plugin_init(plugins):
22 return MakeSetupPlugin()
23
24class MakeSetupPlugin(LayerPlugin):
25
26 def _get_remotes_with_url(self, repo_path):
27 remotes = {}
28 for r in oe.buildcfg.get_metadata_git_remotes(repo_path):
29 remotes[r] = {'uri':oe.buildcfg.get_metadata_git_remote_url(repo_path, r)}
30 return remotes
31
32 def _is_submodule(self, repo_path):
33 # This is slightly brittle: git does not offer a way to tell whether
34 # a given repo dir is a submodule checkout, so we need to rely on .git
35 # being a file (rather than a dir like it is in standalone checkouts).
36 # The file typically contains a gitdir pointer to elsewhere.
37 return os.path.isfile(os.path.join(repo_path,".git"))
38
39 def make_repo_config(self, destdir):
40 """ This is a helper function for the writer plugins that discovers currently configured layers.
41 The writers do not have to use it, but it can save a bit of work and avoid duplicated code, hence it is
42 available here. """
43 repos = {}
44 layers = oe.buildcfg.get_layer_revisions(self.tinfoil.config_data)
45 destdir_repo = oe.buildcfg.get_metadata_git_toplevel(destdir)
46
47 for (l_path, l_name, l_branch, l_rev, l_ismodified) in layers:
48 if l_name == 'workspace':
49 continue
50 if l_ismodified:
51 logger.error("Layer {name} in {path} has uncommitted modifications or is not in a git repository.".format(name=l_name,path=l_path))
52 return
53 repo_path = oe.buildcfg.get_metadata_git_toplevel(l_path)
54
55 if self._is_submodule(repo_path):
56 continue
57 if repo_path not in repos.keys():
58 repos[repo_path] = {'path':os.path.basename(repo_path),'git-remote':{
59 'rev':l_rev,
60 'branch':l_branch,
61 'remotes':self._get_remotes_with_url(repo_path),
62 'describe':oe.buildcfg.get_metadata_git_describe(repo_path)}}
63 if repo_path == destdir_repo:
64 repos[repo_path]['contains_this_file'] = True
65 if not repos[repo_path]['git-remote']['remotes'] and not repos[repo_path]['contains_this_file']:
66 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))
67 return
68
69 top_path = os.path.commonpath([os.path.dirname(r) for r in repos.keys()])
70
71 repos_nopaths = {}
72 for r in repos.keys():
73 r_nopath = os.path.basename(r)
74 repos_nopaths[r_nopath] = repos[r]
75 r_relpath = os.path.relpath(r, top_path)
76 repos_nopaths[r_nopath]['path'] = r_relpath
77 return repos_nopaths
78
79 def do_make_setup(self, args):
80 """ Writes out a configuration file and/or a script that replicate the directory structure and revisions of the layers in a current build. """
81 for p in self.plugins:
82 if str(p) == args.writer:
83 p.do_write(self, args)
84
85 def register_commands(self, sp):
86 parser_setup_layers = self.add_command(sp, 'create-layers-setup', self.do_make_setup, parserecipes=False)
87 parser_setup_layers.add_argument('destdir',
88 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).')
89 parser_setup_layers.add_argument('--output-prefix', '-o',
90 help='File name prefix for the output files, if the default (setup-layers) is undesirable.')
91
92 self.plugins = []
93
94 for path in (self.tinfoil.config_data.getVar('BBPATH').split(':')):
95 pluginpath = os.path.join(path, 'lib', 'bblayers', 'setupwriters')
96 bb.utils.load_plugins(logger, self.plugins, pluginpath)
97
98 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")
99
100 for plugin in self.plugins:
101 if hasattr(plugin, 'register_arguments'):
102 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 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import logging
8import os
9import json
10import stat
11
12logger = logging.getLogger('bitbake-layers')
13
14def plugin_init(plugins):
15 return OeSetupLayersWriter()
16
17class OeSetupLayersWriter():
18
19 def __str__(self):
20 return "oe-setup-layers"
21
22 def _write_python(self, input, output):
23 with open(input) as f:
24 script = f.read()
25 with open(output, 'w') as f:
26 f.write(script)
27 st = os.stat(output)
28 os.chmod(output, st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
29
30 def _write_json(self, repos, output):
31 with open(output, 'w') as f:
32 json.dump(repos, f, sort_keys=True, indent=4)
33
34 def _read_repo_config(self, json_path):
35 with open(json_path) as f:
36 json_config = json.load(f)
37
38 supported_versions = ["1.0"]
39 if json_config["version"] not in supported_versions:
40 err = "File {} has version {}, which is not in supported versions: {}".format(json_path, json_config["version"], supported_versions)
41 logger.error(err)
42 raise Exception(err)
43
44 return json_config
45
46 def _modify_repo_config(self, json_config, args):
47 sources = json_config['sources']
48 for pair in args.custom_references:
49 try:
50 repo, rev = pair.split(':', maxsplit=1)
51 except ValueError:
52 err = "Invalid custom reference specified: '{}'. Provide one using 'REPOSITORY:REFERENCE'.".format(pair)
53 logger.error(err)
54 raise Exception(err)
55 if not repo in sources.keys():
56 err = "Repository {} does not exist in setup-layers config".format(repo)
57 logger.error(err)
58 raise Exception(err)
59
60 layer_remote = json_config['sources'][repo]['git-remote']
61 layer_remote['rev'] = rev
62 # Clear describe
63 layer_remote['describe'] = ''
64
65 def do_write(self, parent, args):
66 """ Writes out a python script and a json config that replicate the directory structure and revisions of the layers in a current build. """
67 output = args.output_prefix or "setup-layers"
68 output = os.path.join(os.path.abspath(args.destdir), output)
69
70 if args.update:
71 # Modify existing layers setup
72 if args.custom_references is None:
73 err = "No custom reference specified. Please provide one using '--use-custom-reference REPOSITORY:REFERENCE'."
74 logger.error(err)
75 raise Exception(err)
76
77 json = self._read_repo_config(output + ".json")
78 if not 'sources' in json.keys():
79 err = "File {}.json does not contain valid layer sources.".format(output)
80 logger.error(err)
81 raise Exception(err)
82
83 else:
84 # Create new layers setup
85 if not os.path.exists(args.destdir):
86 os.makedirs(args.destdir)
87 repos = parent.make_repo_config(args.destdir)
88 json = {"version":"1.0","sources":repos}
89 if not repos:
90 err = "Could not determine layer sources"
91 logger.error(err)
92 raise Exception(err)
93
94 if args.custom_references is not None:
95 self._modify_repo_config(json, args)
96
97 self._write_json(json, output + ".json")
98 logger.info('Created {}.json'.format(output))
99 if not args.json_only:
100 self._write_python(os.path.join(os.path.dirname(__file__),'../../../../scripts/oe-setup-layers'), output)
101 logger.info('Created {}'.format(output))
102
103 def register_arguments(self, parser):
104 parser.add_argument('--json-only', action='store_true',
105 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.')
106
107 parser.add_argument('--update', '-u',
108 action='store_true',
109 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."
110 "\nThis will only update repositories for which a custom reference is specified, all other repositores will be left unchanged."))
111 parser.add_argument('--use-custom-reference', '-r',
112 action='append',
113 dest='custom_references',
114 metavar='REPOSITORY:REFERENCE',
115 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)."
116 "\nThis value can be any reference that 'git checkout' would accept, and is not checked for validity."
117 "\nThis option can be used multiple times."))
diff --git a/meta/lib/bblayers/templates/layer.conf b/meta/lib/bblayers/templates/layer.conf
index e2eaff4346..dddfbf716e 100644
--- a/meta/lib/bblayers/templates/layer.conf
+++ b/meta/lib/bblayers/templates/layer.conf
@@ -5,9 +5,9 @@ BBPATH .= ":${{LAYERDIR}}"
5BBFILES += "${{LAYERDIR}}/recipes-*/*/*.bb \ 5BBFILES += "${{LAYERDIR}}/recipes-*/*/*.bb \
6 ${{LAYERDIR}}/recipes-*/*/*.bbappend" 6 ${{LAYERDIR}}/recipes-*/*/*.bbappend"
7 7
8BBFILE_COLLECTIONS += "{layername}" 8BBFILE_COLLECTIONS += "{layerid}"
9BBFILE_PATTERN_{layername} = "^${{LAYERDIR}}/" 9BBFILE_PATTERN_{layerid} = "^${{LAYERDIR}}/"
10BBFILE_PRIORITY_{layername} = "{priority}" 10BBFILE_PRIORITY_{layerid} = "{priority}"
11 11
12LAYERDEPENDS_{layername} = "core" 12LAYERDEPENDS_{layerid} = "core"
13LAYERSERIES_COMPAT_{layername} = "{compat}" 13LAYERSERIES_COMPAT_{layerid} = "{compat}"