blob: 69a470e51faa8093d4c78304088f8da1244943c8 [file] [log] [blame]
Masahiro Yamada60bb7342014-07-30 14:08:17 +09001#!/usr/bin/env python
2#
3# Copyright (C) 2014, Masahiro Yamada <yamada.m@jp.panasonic.com>
4#
5# SPDX-License-Identifier: GPL-2.0+
6#
7
8"""
9A wrapper script to adjust Kconfig for U-Boot
10
11The biggest difference between Linux Kernel and U-Boot in terms of the
12board configuration is that U-Boot has to configure multiple boot images
13per board: Normal, SPL, TPL.
14We need to expand the functions of Kconfig to handle multiple boot
15images.
16
17Instead of touching various parts under the scripts/kconfig/ directory,
18pushing necessary adjustments into this single script would be better
19for code maintainance. All the make targets related to the configuration
20(make %config) should be invoked via this script.
21
22Let's see what is different from the original Kconfig.
23
24- config, menuconfig, etc.
25
26The commands 'make config', 'make menuconfig', etc. are used to create
27or modify the .config file, which stores configs for Normal boot image.
28
29The location of the one for SPL, TPL image is spl/.config, tpl/.config,
30respectively. Use 'make spl/config', 'make spl/menuconfig', etc.
31to create or modify the spl/.config file, which contains configs
32for SPL image.
33Do likewise for the tpl/.config file.
34The generic syntax for SPL, TPL configuration is
35'make <target_image>/<config_command>'.
36
37- silentoldconfig
38
39The command 'make silentoldconfig' updates .config, if necessary, and
40additionally updates include/generated/autoconf.h and files under
41include/configs/ directory. In U-Boot, it should do the same things for
42SPL, TPL images for boards supporting them.
43Depending on whether CONFIG_SPL, CONFIG_TPL is defined or not,
44'make silentoldconfig' iterates three times at most changing the target
45directory.
46
47To sum up, 'make silentoldconfig' possibly updates
48 - .config, include/generated/autoconf.h, include/config/*
49 - spl/.config, spl/include/generated/autoconf.h, spl/include/config/*
50 (in case CONFIG_SPL=y)
51 - tpl/.config, tpl/include/generated/autoconf.h, tpl/include/config/*
52 (in case CONFIG_TPL=y)
53
54- defconfig, <board>_defconfig
55
56The command 'make <board>_defconfig' creates a new .config based on the
57file configs/<board>_defconfig. The command 'make defconfig' is the same
58but the difference is it uses the file specified with KBUILD_DEFCONFIG
59environment.
60
61We need to create .config, spl/.config, tpl/.config for boards where SPL
62and TPL images are supported. One possible solution for that is to have
63multiple defconfig files per board, but it would produce duplication
64among the defconfigs.
65The approach chosen here is to expand the feature and support
66conditional definition in defconfig, that is, each line in defconfig
67files has the form of:
68<condition>:<macro definition>
69
70The '<condition>:' prefix specifies which image the line is valid for.
71The '<condition>:' is one of:
72 None - the line is valid only for Normal image
73 S: - the line is valid only for SPL image
74 T: - the line is valid only for TPL image
75 ST: - the line is valid for SPL and TPL images
76 +S: - the line is valid for Normal and SPL images
77 +T: - the line is valid for Normal and TPL images
78 +ST: - the line is valid for Normal, SPL and SPL images
79
80So, if neither CONFIG_SPL nor CONFIG_TPL is defined, the defconfig file
81has no '<condition>:' part and therefore has the same form of that of
82Linux Kernel.
83
84In U-Boot, for example, a defconfig file can be written like this:
85
86 CONFIG_FOO=100
87 S:CONFIG_FOO=200
88 T:CONFIG_FOO=300
89 ST:CONFIG_BAR=y
90 +S:CONFIG_BAZ=y
91 +T:CONFIG_QUX=y
92 +ST:CONFIG_QUUX=y
93
94The defconfig above is parsed by this script and internally divided into
95three temporary defconfig files.
96
97 - Temporary defconfig for Normal image
98 CONFIG_FOO=100
99 CONFIG_BAZ=y
100 CONFIG_QUX=y
101 CONFIG_QUUX=y
102
103 - Temporary defconfig for SPL image
104 CONFIG_FOO=200
105 CONFIG_BAR=y
106 CONFIG_BAZ=y
107 CONFIG_QUUX=y
108
109 - Temporary defconfig for TPL image
110 CONFIG_FOO=300
111 CONFIG_BAR=y
112 CONFIG_QUX=y
113 CONFIG_QUUX=y
114
115They are passed to scripts/kconfig/conf, each is used for generating
116.config, spl/.config, tpl/.config, respectively.
117
118- savedefconfig
119
120This is the reverse operation of 'make defconfig'.
121If neither CONFIG_SPL nor CONFIG_TPL is defined in the .config file,
122it works as 'make savedefconfig' in Linux Kernel: create the minimal set
123of config based on the .config and save it into 'defconfig' file.
124
125If CONFIG_SPL or CONFIG_TPL is defined, the common lines among .config,
126spl/.config, tpl/.config are coalesced together and output to the file
127'defconfig' in the form like:
128
129 CONFIG_FOO=100
130 S:CONFIG_FOO=200
131 T:CONFIG_FOO=300
132 ST:CONFIG_BAR=y
133 +S:CONFIG_BAZ=y
134 +T:CONFIG_QUX=y
135 +ST:CONFIG_QUUX=y
136
137This can be used as an input of 'make <board>_defconfig' command.
138"""
139
140import errno
141import os
142import re
143import subprocess
144import sys
145
146# Constant variables
147SUB_IMAGES = ('spl', 'tpl')
148IMAGES = ('',) + SUB_IMAGES
149SYMBOL_MAP = {'': '+', 'spl': 'S', 'tpl': 'T'}
150PATTERN_SYMBOL = re.compile(r'(\+?)(S?)(T?):(.*)')
151
152# Environment variables (should be defined in the top Makefile)
153# .get('key', 'default_value') method is useful for standalone testing.
154MAKE = os.environ.get('MAKE', 'make')
155srctree = os.environ.get('srctree', '.')
156KCONFIG_CONFIG = os.environ.get('KCONFIG_CONFIG', '.config')
157
158# Useful shorthand
159build = '%s -f %s/scripts/Makefile.build obj=scripts/kconfig %%s' % (MAKE, srctree)
160autoconf = '%s -f %s/scripts/Makefile.autoconf obj=%%s %%s' % (MAKE, srctree)
161
162### helper functions ###
163def mkdirs(*dirs):
164 """Make directories ignoring 'File exists' error."""
165 for d in dirs:
166 try:
167 os.makedirs(d)
168 except OSError as exception:
169 # Ignore 'File exists' error
170 if exception.errno != errno.EEXIST:
171 raise
172
173def rmfiles(*files):
174 """Remove files ignoring 'No such file or directory' error."""
175 for f in files:
176 try:
177 os.remove(f)
178 except OSError as exception:
179 # Ignore 'No such file or directory' error
180 if exception.errno != errno.ENOENT:
181 raise
182
183def rmdirs(*dirs):
184 """Remove directories ignoring 'No such file or directory'
185 and 'Directory not empty' error.
186 """
187 for d in dirs:
188 try:
189 os.rmdir(d)
190 except OSError as exception:
191 # Ignore 'No such file or directory'
192 # and 'Directory not empty' error
193 if exception.errno != errno.ENOENT and \
194 exception.errno != errno.ENOTEMPTY:
195 raise
196
Masahiro Yamada60bb7342014-07-30 14:08:17 +0900197def run_command(command, callback_on_error=None):
198 """Run the given command in a sub-shell (and exit if it fails).
199
200 Arguments:
201 command: A string of the command
202 callback_on_error: Callback handler invoked just before exit
203 when the command fails (Default=None)
204 """
205 retcode = subprocess.call(command, shell=True)
206 if retcode:
207 if callback_on_error:
208 callback_on_error()
Masahiro Yamada880828d2014-08-16 00:59:26 +0900209 sys.exit("'%s' Failed" % command)
Masahiro Yamada60bb7342014-07-30 14:08:17 +0900210
211def run_make_config(cmd, objdir, callback_on_error=None):
212 """Run the make command in a sub-shell (and exit if it fails).
213
214 Arguments:
215 cmd: Make target such as 'config', 'menuconfig', 'defconfig', etc.
216 objdir: Target directory where the make command is run.
217 Typically '', 'spl', 'tpl' for Normal, SPL, TPL image,
218 respectively.
219 callback_on_error: Callback handler invoked just before exit
220 when the command fails (Default=None)
221 """
222 # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory,
223 # but U-Boot puts them in configs/ directory.
224 # Give SRCARCH=.. to fake scripts/kconfig/Makefile.
225 options = 'SRCARCH=.. KCONFIG_OBJDIR=%s' % objdir
226 if objdir:
227 options += ' KCONFIG_CONFIG=%s/%s' % (objdir, KCONFIG_CONFIG)
228 mkdirs(objdir)
229 run_command(build % cmd + ' ' + options, callback_on_error)
230
231def get_enabled_subimages(ignore_error=False):
232 """Parse .config file to detect if CONFIG_SPL, CONFIG_TPL is enabled
233 and return a tuple of enabled subimages.
234
235 Arguments:
236 ignore_error: Specify the behavior when '.config' is not found;
237 Raise an exception if this flag is False.
238 Return a null tuple if this flag is True.
239
240 Returns:
241 A tuple of enabled subimages as follows:
242 () if neither CONFIG_SPL nor CONFIG_TPL is defined
243 ('spl',) if CONFIG_SPL is defined but CONFIG_TPL is not
244 ('spl', 'tpl') if both CONFIG_SPL and CONFIG_TPL are defined
245 """
246 enabled = ()
247 match_patterns = [ (img, 'CONFIG_' + img.upper() + '=y\n')
248 for img in SUB_IMAGES ]
249 try:
250 f = open(KCONFIG_CONFIG)
251 except IOError as exception:
252 if not ignore_error or exception.errno != errno.ENOENT:
253 raise
254 return enabled
255 with f:
256 for line in f:
257 for img, pattern in match_patterns:
258 if line == pattern:
259 enabled += (img,)
260 return enabled
261
262def do_silentoldconfig(cmd):
263 """Run 'make silentoldconfig' for all the enabled images.
264
265 Arguments:
266 cmd: should always be a string 'silentoldconfig'
267 """
268 run_make_config(cmd, '')
269 subimages = get_enabled_subimages()
270 for obj in subimages:
271 mkdirs(os.path.join(obj, 'include', 'config'),
272 os.path.join(obj, 'include', 'generated'))
273 run_make_config(cmd, obj)
274 remove_auto_conf = lambda : rmfiles('include/config/auto.conf')
275 # If the following part failed, include/config/auto.conf should be deleted
276 # so 'make silentoldconfig' will be re-run on the next build.
277 run_command(autoconf %
278 ('include', 'include/autoconf.mk include/autoconf.mk.dep'),
279 remove_auto_conf)
280 # include/config.h has been updated after 'make silentoldconfig'.
281 # We need to touch include/config/auto.conf so it gets newer
282 # than include/config.h.
283 # Otherwise, 'make silentoldconfig' would be invoked twice.
284 os.utime('include/config/auto.conf', None)
285 for obj in subimages:
286 run_command(autoconf % (obj + '/include',
287 obj + '/include/autoconf.mk'),
288 remove_auto_conf)
289
290def do_tmp_defconfig(output_lines, img):
291 """Helper function for do_board_defconfig().
292
293 Write the defconfig contents into a file '.tmp_defconfig' and
294 invoke 'make .tmp_defconfig'.
295
296 Arguments:
297 output_lines: A sequence of defconfig lines of each image
298 img: Target image. Typically '', 'spl', 'tpl' for
299 Normal, SPL, TPL images, respectively.
300 """
301 TMP_DEFCONFIG = '.tmp_defconfig'
302 TMP_DIRS = ('arch', 'configs')
303 defconfig_path = os.path.join('configs', TMP_DEFCONFIG)
304 mkdirs(*TMP_DIRS)
305 with open(defconfig_path, 'w') as f:
306 f.write(''.join(output_lines[img]))
307 cleanup = lambda: (rmfiles(defconfig_path), rmdirs(*TMP_DIRS))
308 run_make_config(TMP_DEFCONFIG, img, cleanup)
309 cleanup()
310
311def do_board_defconfig(cmd):
312 """Run 'make <board>_defconfig'.
313
314 Arguments:
315 cmd: should be a string '<board>_defconfig'
316 """
317 defconfig_path = os.path.join(srctree, 'configs', cmd)
318 output_lines = dict([ (img, []) for img in IMAGES ])
319 with open(defconfig_path) as f:
320 for line in f:
321 m = PATTERN_SYMBOL.match(line)
322 if m:
323 for idx, img in enumerate(IMAGES):
324 if m.group(idx + 1):
325 output_lines[img].append(m.group(4) + '\n')
326 continue
327 output_lines[''].append(line)
328 do_tmp_defconfig(output_lines, '')
329 for img in get_enabled_subimages():
330 do_tmp_defconfig(output_lines, img)
331
332def do_defconfig(cmd):
333 """Run 'make defconfig'.
334
335 Arguments:
336 cmd: should always be a string 'defconfig'
337 """
338 KBUILD_DEFCONFIG = os.environ['KBUILD_DEFCONFIG']
339 print "*** Default configuration is based on '%s'" % KBUILD_DEFCONFIG
340 do_board_defconfig(KBUILD_DEFCONFIG)
341
342def do_savedefconfig(cmd):
343 """Run 'make savedefconfig'.
344
345 Arguments:
346 cmd: should always be a string 'savedefconfig'
347 """
348 DEFCONFIG = 'defconfig'
349 # Continue even if '.config' does not exist
350 subimages = get_enabled_subimages(True)
351 run_make_config(cmd, '')
352 output_lines = []
353 prefix = {}
354 with open(DEFCONFIG) as f:
355 for line in f:
356 output_lines.append(line)
357 prefix[line] = '+'
358 for img in subimages:
359 run_make_config(cmd, img)
360 unmatched_lines = []
361 with open(DEFCONFIG) as f:
362 for line in f:
363 if line in output_lines:
364 index = output_lines.index(line)
365 output_lines[index:index] = unmatched_lines
366 unmatched_lines = []
367 prefix[line] += SYMBOL_MAP[img]
368 else:
369 ummatched_lines.append(line)
370 prefix[line] = SYMBOL_MAP[img]
371 with open(DEFCONFIG, 'w') as f:
372 for line in output_lines:
373 if prefix[line] == '+':
374 f.write(line)
375 else:
376 f.write(prefix[line] + ':' + line)
377
378def do_others(cmd):
379 """Run the make command other than 'silentoldconfig', 'defconfig',
380 '<board>_defconfig' and 'savedefconfig'.
381
382 Arguments:
383 cmd: Make target in the form of '<target_image>/<config_command>'
384 The field '<target_image>/' is typically empty, 'spl/', 'tpl/'
385 for Normal, SPL, TPL images, respectively.
386 The field '<config_command>' is make target such as 'config',
387 'menuconfig', etc.
388 """
389 objdir, _, cmd = cmd.rpartition('/')
390 run_make_config(cmd, objdir)
391
392cmd_list = {'silentoldconfig': do_silentoldconfig,
393 'defconfig': do_defconfig,
394 'savedefconfig': do_savedefconfig}
395
396def main():
397 cmd = sys.argv[1]
398 if cmd.endswith('_defconfig'):
399 do_board_defconfig(cmd)
400 else:
401 func = cmd_list.get(cmd, do_others)
402 func(cmd)
403
404if __name__ == '__main__':
405 main()