summaryrefslogtreecommitdiffstats
path: root/scripts/devtool
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2014-12-19 11:41:55 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-12-23 10:18:16 +0000
commitcd5ca4a11daff2089ba3031103b9b9dab5b3c94e (patch)
tree2c80f6f2c4a07ce9d88b47c17ca7a424665e2283 /scripts/devtool
parentb7d53f2ebb3219e205ab6b368f7f41b57a5cf89a (diff)
downloadpoky-cd5ca4a11daff2089ba3031103b9b9dab5b3c94e.tar.gz
scripts/devtool: add development helper tool
Provides an easy means to work on developing applications and system components with the build system. For example to "modify" the source for an existing recipe: $ devtool modify -x pango /home/projects/pango Parsing recipes..done. NOTE: Fetching pango... NOTE: Unpacking... NOTE: Patching... NOTE: Source tree extracted to /home/projects/pango NOTE: Recipe pango now set up to build from /home/paul/projects/pango The pango source is now extracted to /home/paul/projects/pango, managed in git, with each patch as a commit, and a bbappend is created in the workspace layer to use the source in /home/paul/projects/pango when building. Additionally, you can add a new piece of software: $ devtool add pv /home/projects/pv NOTE: Recipe /path/to/workspace/recipes/pv/pv.bb has been automatically created; further editing may be required to make it fully functional The latter uses recipetool to create a skeleton recipe and again sets up a bbappend to use the source in /home/projects/pv when building. Having done a "devtool modify", can also write any changes to the external git repository back as patches next to the recipe: $ devtool update-recipe mdadm Parsing recipes..done. NOTE: Removing patch mdadm-3.2.2_fix_for_x32.patch NOTE: Removing patch gcc-4.9.patch NOTE: Updating recipe mdadm_3.3.1.bb [YOCTO #6561] [YOCTO #6653] [YOCTO #6656] (From OE-Core rev: 716d9b1f304a12bab61b15e3ce526977c055f074) Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/devtool')
-rwxr-xr-xscripts/devtool255
1 files changed, 255 insertions, 0 deletions
diff --git a/scripts/devtool b/scripts/devtool
new file mode 100755
index 0000000000..d6e1b9710d
--- /dev/null
+++ b/scripts/devtool
@@ -0,0 +1,255 @@
1#!/usr/bin/env python
2
3# OpenEmbedded Development tool
4#
5# Copyright (C) 2014 Intel Corporation
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20import sys
21import os
22import argparse
23import glob
24import re
25import ConfigParser
26import subprocess
27import logging
28
29basepath = ''
30workspace = {}
31config = None
32context = None
33
34
35scripts_path = os.path.dirname(os.path.realpath(__file__))
36lib_path = scripts_path + '/lib'
37sys.path = sys.path + [lib_path]
38import scriptutils
39logger = scriptutils.logger_create('devtool')
40
41plugins = []
42
43
44class ConfigHandler(object):
45 config_file = ''
46 config_obj = None
47 init_path = ''
48 workspace_path = ''
49
50 def __init__(self, filename):
51 self.config_file = filename
52 self.config_obj = ConfigParser.SafeConfigParser()
53
54 def get(self, section, option, default=None):
55 try:
56 ret = self.config_obj.get(section, option)
57 except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
58 if default != None:
59 ret = default
60 else:
61 raise
62 return ret
63
64 def read(self):
65 if os.path.exists(self.config_file):
66 self.config_obj.read(self.config_file)
67
68 if self.config_obj.has_option('General', 'init_path'):
69 pth = self.get('General', 'init_path')
70 self.init_path = os.path.join(basepath, pth)
71 if not os.path.exists(self.init_path):
72 logger.error('init_path %s specified in config file cannot be found' % pth)
73 return False
74 else:
75 self.config_obj.add_section('General')
76
77 self.workspace_path = self.get('General', 'workspace_path', os.path.join(basepath, 'workspace'))
78 return True
79
80
81 def write(self):
82 logger.debug('writing to config file %s' % self.config_file)
83 self.config_obj.set('General', 'workspace_path', self.workspace_path)
84 with open(self.config_file, 'w') as f:
85 self.config_obj.write(f)
86
87class Context:
88 def __init__(self, **kwargs):
89 self.__dict__.update(kwargs)
90
91
92def read_workspace():
93 global workspace
94 workspace = {}
95 if not os.path.exists(os.path.join(config.workspace_path, 'conf', 'layer.conf')):
96 if context.fixed_setup:
97 logger.error("workspace layer not set up")
98 sys.exit(1)
99 else:
100 logger.info('Creating workspace layer in %s' % config.workspace_path)
101 _create_workspace(config.workspace_path, config, basepath)
102
103 logger.debug('Reading workspace in %s' % config.workspace_path)
104 externalsrc_re = re.compile(r'^EXTERNALSRC(_pn-[a-zA-Z0-9-]*)? =.*$')
105 for fn in glob.glob(os.path.join(config.workspace_path, 'appends', '*.bbappend')):
106 pn = os.path.splitext(os.path.basename(fn))[0].split('_')[0]
107 with open(fn, 'r') as f:
108 for line in f:
109 if externalsrc_re.match(line.rstrip()):
110 splitval = line.split('=', 2)
111 workspace[pn] = splitval[1].strip('" \n\r\t')
112 break
113
114def create_workspace(args, config, basepath, workspace):
115 if args.directory:
116 workspacedir = os.path.abspath(args.directory)
117 else:
118 workspacedir = os.path.abspath(os.path.join(basepath, 'workspace'))
119 _create_workspace(workspacedir, config, basepath, args.create_only)
120
121def _create_workspace(workspacedir, config, basepath, create_only=False):
122 import bb
123
124 confdir = os.path.join(workspacedir, 'conf')
125 if os.path.exists(os.path.join(confdir, 'layer.conf')):
126 logger.info('Specified workspace already set up, leaving as-is')
127 else:
128 # Add a config file
129 bb.utils.mkdirhier(confdir)
130 with open(os.path.join(confdir, 'layer.conf'), 'w') as f:
131 f.write('# ### workspace layer auto-generated by devtool ###\n')
132 f.write('BBPATH =. "$' + '{LAYERDIR}:"\n')
133 f.write('BBFILES += "$' + '{LAYERDIR}/recipes/*/*.bb \\\n')
134 f.write(' $' + '{LAYERDIR}/appends/*.bbappend"\n')
135 f.write('BBFILE_COLLECTIONS += "workspacelayer"\n')
136 f.write('BBFILE_PATTERN_workspacelayer = "^$' + '{LAYERDIR}/"\n')
137 f.write('BBFILE_PATTERN_IGNORE_EMPTY_workspacelayer = "1"\n')
138 f.write('BBFILE_PRIORITY_workspacelayer = "99"\n')
139 # Add a README file
140 with open(os.path.join(workspacedir, 'README'), 'w') as f:
141 f.write('This layer was created by the OpenEmbedded devtool utility in order to\n')
142 f.write('contain recipes and bbappends. In most instances you should use the\n')
143 f.write('devtool utility to manage files within it rather than modifying files\n')
144 f.write('directly (although recipes added with "devtool add" will often need\n')
145 f.write('direct modification.)\n')
146 f.write('\nIf you no longer need to use devtool you can remove the path to this\n')
147 f.write('workspace layer from your conf/bblayers.conf file (and then delete the\n')
148 f.write('layer, if you wish).\n')
149 if not create_only:
150 # Add the workspace layer to bblayers.conf
151 bblayers_conf = os.path.join(basepath, 'conf', 'bblayers.conf')
152 if not os.path.exists(bblayers_conf):
153 logger.error('Unable to find bblayers.conf')
154 return -1
155 bb.utils.edit_bblayers_conf(bblayers_conf, workspacedir, config.workspace_path)
156 if config.workspace_path != workspacedir:
157 # Update our config to point to the new location
158 config.workspace_path = workspacedir
159 config.write()
160
161
162def main():
163 global basepath
164 global config
165 global context
166
167 context = Context(fixed_setup=False)
168
169 # Default basepath
170 basepath = os.path.dirname(os.path.abspath(__file__))
171 pth = basepath
172 while pth != '' and pth != os.sep:
173 if os.path.exists(os.path.join(pth, '.devtoolbase')):
174 context.fixed_setup = True
175 basepath = pth
176 break
177 pth = os.path.dirname(pth)
178
179 parser = argparse.ArgumentParser(description="OpenEmbedded development tool",
180 epilog="Use %(prog)s <command> --help to get help on a specific command")
181 parser.add_argument('--basepath', help='Base directory of SDK / build directory')
182 parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true')
183 parser.add_argument('-q', '--quiet', help='Print only errors', action='store_true')
184 parser.add_argument('--color', help='Colorize output', choices=['auto', 'always', 'never'], default='auto')
185
186 subparsers = parser.add_subparsers(dest="subparser_name")
187
188 if not context.fixed_setup:
189 parser_create_workspace = subparsers.add_parser('create-workspace', help='Set up a workspace')
190 parser_create_workspace.add_argument('directory', nargs='?', help='Directory for the workspace')
191 parser_create_workspace.add_argument('--create-only', action="store_true", help='Only create the workspace, do not alter configuration')
192 parser_create_workspace.set_defaults(func=create_workspace)
193
194 scriptutils.load_plugins(logger, plugins, os.path.join(scripts_path, 'lib', 'devtool'))
195 for plugin in plugins:
196 if hasattr(plugin, 'register_commands'):
197 plugin.register_commands(subparsers, context)
198
199 args = parser.parse_args()
200
201 if args.debug:
202 logger.setLevel(logging.DEBUG)
203 elif args.quiet:
204 logger.setLevel(logging.ERROR)
205
206 if args.basepath:
207 # Override
208 basepath = args.basepath
209 elif not context.fixed_setup:
210 basepath = os.environ.get('BUILDDIR')
211 if not basepath:
212 logger.error("This script can only be run after initialising the build environment (e.g. by using oe-init-build-env)")
213 sys.exit(1)
214
215 logger.debug('Using basepath %s' % basepath)
216
217 config = ConfigHandler(os.path.join(basepath, 'conf', 'devtool.conf'))
218 if not config.read():
219 return -1
220
221 bitbake_subdir = config.get('General', 'bitbake_subdir', '')
222 if bitbake_subdir:
223 # Normally set for use within the SDK
224 logger.debug('Using bitbake subdir %s' % bitbake_subdir)
225 sys.path.insert(0, os.path.join(basepath, bitbake_subdir, 'lib'))
226 core_meta_subdir = config.get('General', 'core_meta_subdir')
227 sys.path.insert(0, os.path.join(basepath, core_meta_subdir, 'lib'))
228 else:
229 # Standard location
230 import scriptpath
231 bitbakepath = scriptpath.add_bitbake_lib_path()
232 if not bitbakepath:
233 logger.error("Unable to find bitbake by searching parent directory of this script or PATH")
234 sys.exit(1)
235 logger.debug('Using standard bitbake path %s' % bitbakepath)
236 scriptpath.add_oe_lib_path()
237
238 scriptutils.logger_setup_color(logger, args.color)
239
240 if args.subparser_name != 'create-workspace':
241 read_workspace()
242
243 ret = args.func(args, config, basepath, workspace)
244
245 return ret
246
247
248if __name__ == "__main__":
249 try:
250 ret = main()
251 except Exception:
252 ret = 1
253 import traceback
254 traceback.print_exc(5)
255 sys.exit(ret)