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 | 21 | ||||
-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/layer.conf | 10 |
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 | |||
7 | import logging | ||
8 | import os | ||
9 | import sys | ||
10 | |||
11 | from bblayers.common import LayerPlugin | ||
12 | |||
13 | logger = logging.getLogger('bitbake-layers') | ||
14 | |||
15 | sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) | ||
16 | |||
17 | import oe.buildcfg | ||
18 | |||
19 | def plugin_init(plugins): | ||
20 | return BuildConfPlugin() | ||
21 | |||
22 | class BuildConfPlugin(LayerPlugin): | ||
23 | notes_fixme = """FIXME: Please place here the detailed instructions for using this build configuration. | ||
24 | They 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. | ||
27 | It 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 {} | ||
53 | Please review the files in there, and particularly provide a configuration summary in {} | ||
54 | and notes in {} | ||
55 | You can try out the configuration with | ||
56 | TEMPLATECONF={} . {}/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 | |||
10 | import bb.utils | 12 | import bb.utils |
11 | 13 | ||
12 | from bblayers.common import LayerPlugin | 14 | from bblayers.common import LayerPlugin |
15 | from bblayers.action import ActionPlugin | ||
13 | 16 | ||
14 | logger = logging.getLogger('bitbake-layers') | 17 | logger = 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 | |||
7 | import logging | ||
8 | import os | ||
9 | import sys | ||
10 | |||
11 | import bb.utils | ||
12 | |||
13 | from bblayers.common import LayerPlugin | ||
14 | |||
15 | logger = logging.getLogger('bitbake-layers') | ||
16 | |||
17 | sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) | ||
18 | |||
19 | import oe.buildcfg | ||
20 | |||
21 | def plugin_init(plugins): | ||
22 | return MakeSetupPlugin() | ||
23 | |||
24 | class 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 | |||
7 | import logging | ||
8 | import os | ||
9 | import json | ||
10 | import stat | ||
11 | |||
12 | logger = logging.getLogger('bitbake-layers') | ||
13 | |||
14 | def plugin_init(plugins): | ||
15 | return OeSetupLayersWriter() | ||
16 | |||
17 | class 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}}" | |||
5 | BBFILES += "${{LAYERDIR}}/recipes-*/*/*.bb \ | 5 | BBFILES += "${{LAYERDIR}}/recipes-*/*/*.bb \ |
6 | ${{LAYERDIR}}/recipes-*/*/*.bbappend" | 6 | ${{LAYERDIR}}/recipes-*/*/*.bbappend" |
7 | 7 | ||
8 | BBFILE_COLLECTIONS += "{layername}" | 8 | BBFILE_COLLECTIONS += "{layerid}" |
9 | BBFILE_PATTERN_{layername} = "^${{LAYERDIR}}/" | 9 | BBFILE_PATTERN_{layerid} = "^${{LAYERDIR}}/" |
10 | BBFILE_PRIORITY_{layername} = "{priority}" | 10 | BBFILE_PRIORITY_{layerid} = "{priority}" |
11 | 11 | ||
12 | LAYERDEPENDS_{layername} = "core" | 12 | LAYERDEPENDS_{layerid} = "core" |
13 | LAYERSERIES_COMPAT_{layername} = "{compat}" | 13 | LAYERSERIES_COMPAT_{layerid} = "{compat}" |