]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - scripts/multiconfig.py
mx6sxsabresd: Add Ethernet support
[karo-tx-uboot.git] / scripts / multiconfig.py
1 #!/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 """
9 A wrapper script to adjust Kconfig for U-Boot
10
11 The biggest difference between Linux Kernel and U-Boot in terms of the
12 board configuration is that U-Boot has to configure multiple boot images
13 per board: Normal, SPL, TPL.
14 We need to expand the functions of Kconfig to handle multiple boot
15 images.
16
17 Instead of touching various parts under the scripts/kconfig/ directory,
18 pushing necessary adjustments into this single script would be better
19 for code maintainance. All the make targets related to the configuration
20 (make %config) should be invoked via this script.
21
22 Let's see what is different from the original Kconfig.
23
24 - config, menuconfig, etc.
25
26 The commands 'make config', 'make menuconfig', etc. are used to create
27 or modify the .config file, which stores configs for Normal boot image.
28
29 The location of the one for SPL, TPL image is spl/.config, tpl/.config,
30 respectively. Use 'make spl/config', 'make spl/menuconfig', etc.
31 to create or modify the spl/.config file, which contains configs
32 for SPL image.
33 Do likewise for the tpl/.config file.
34 The generic syntax for SPL, TPL configuration is
35 'make <target_image>/<config_command>'.
36
37 - silentoldconfig
38
39 The command 'make silentoldconfig' updates .config, if necessary, and
40 additionally updates include/generated/autoconf.h and files under
41 include/configs/ directory. In U-Boot, it should do the same things for
42 SPL, TPL images for boards supporting them.
43 Depending on whether CONFIG_SPL, CONFIG_TPL is defined or not,
44 'make silentoldconfig' iterates three times at most changing the target
45 directory.
46
47 To 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
56 The command 'make <board>_defconfig' creates a new .config based on the
57 file configs/<board>_defconfig. The command 'make defconfig' is the same
58 but the difference is it uses the file specified with KBUILD_DEFCONFIG
59 environment.
60
61 We need to create .config, spl/.config, tpl/.config for boards where SPL
62 and TPL images are supported. One possible solution for that is to have
63 multiple defconfig files per board, but it would produce duplication
64 among the defconfigs.
65 The approach chosen here is to expand the feature and support
66 conditional definition in defconfig, that is, each line in defconfig
67 files has the form of:
68 <condition>:<macro definition>
69
70 The '<condition>:' prefix specifies which image the line is valid for.
71 The '<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
80 So, if neither CONFIG_SPL nor CONFIG_TPL is defined, the defconfig file
81 has no '<condition>:' part and therefore has the same form of that of
82 Linux Kernel.
83
84 In 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
94 The defconfig above is parsed by this script and internally divided into
95 three 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
115 They are passed to scripts/kconfig/conf, each is used for generating
116 .config, spl/.config, tpl/.config, respectively.
117
118 - savedefconfig
119
120 This is the reverse operation of 'make defconfig'.
121 If neither CONFIG_SPL nor CONFIG_TPL is defined in the .config file,
122 it works as 'make savedefconfig' in Linux Kernel: create the minimal set
123 of config based on the .config and save it into 'defconfig' file.
124
125 If CONFIG_SPL or CONFIG_TPL is defined, the common lines among .config,
126 spl/.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
137 This can be used as an input of 'make <board>_defconfig' command.
138 """
139
140 import errno
141 import os
142 import re
143 import subprocess
144 import sys
145
146 # Constant variables
147 SUB_IMAGES = ('spl', 'tpl')
148 IMAGES = ('',) + SUB_IMAGES
149 SYMBOL_MAP = {'': '+', 'spl': 'S', 'tpl': 'T'}
150 PATTERN_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.
154 MAKE = os.environ.get('MAKE', 'make')
155 srctree = os.environ.get('srctree', '.')
156 KCONFIG_CONFIG = os.environ.get('KCONFIG_CONFIG', '.config')
157
158 # Useful shorthand
159 build = '%s -f %s/scripts/Makefile.build obj=scripts/kconfig %%s' % (MAKE, srctree)
160 autoconf = '%s -f %s/scripts/Makefile.autoconf obj=%%s %%s' % (MAKE, srctree)
161
162 ### helper functions ###
163 def 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
173 def 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
183 def 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
197 def error(msg):
198     """Output the given argument to stderr and exit with return code 1."""
199     print >> sys.stderr, msg
200     sys.exit(1)
201
202 def run_command(command, callback_on_error=None):
203     """Run the given command in a sub-shell (and exit if it fails).
204
205     Arguments:
206       command: A string of the command
207       callback_on_error: Callback handler invoked just before exit
208                          when the command fails (Default=None)
209     """
210     retcode = subprocess.call(command, shell=True)
211     if retcode:
212         if callback_on_error:
213             callback_on_error()
214         error("'%s' Failed" % command)
215
216 def run_make_config(cmd, objdir, callback_on_error=None):
217     """Run the make command in a sub-shell (and exit if it fails).
218
219     Arguments:
220       cmd: Make target such as 'config', 'menuconfig', 'defconfig', etc.
221       objdir: Target directory where the make command is run.
222               Typically '', 'spl', 'tpl' for Normal, SPL, TPL image,
223               respectively.
224       callback_on_error: Callback handler invoked just before exit
225                          when the command fails (Default=None)
226     """
227     # Linux expects defconfig files in arch/$(SRCARCH)/configs/ directory,
228     # but U-Boot puts them in configs/ directory.
229     # Give SRCARCH=.. to fake scripts/kconfig/Makefile.
230     options = 'SRCARCH=.. KCONFIG_OBJDIR=%s' % objdir
231     if objdir:
232         options += ' KCONFIG_CONFIG=%s/%s' % (objdir, KCONFIG_CONFIG)
233         mkdirs(objdir)
234     run_command(build % cmd + ' ' + options, callback_on_error)
235
236 def get_enabled_subimages(ignore_error=False):
237     """Parse .config file to detect if CONFIG_SPL, CONFIG_TPL is enabled
238     and return a tuple of enabled subimages.
239
240     Arguments:
241       ignore_error: Specify the behavior when '.config' is not found;
242                     Raise an exception if this flag is False.
243                     Return a null tuple if this flag is True.
244
245     Returns:
246       A tuple of enabled subimages as follows:
247         ()             if neither CONFIG_SPL nor CONFIG_TPL is defined
248         ('spl',)       if CONFIG_SPL is defined but CONFIG_TPL is not
249         ('spl', 'tpl') if both CONFIG_SPL and CONFIG_TPL are defined
250     """
251     enabled = ()
252     match_patterns = [ (img, 'CONFIG_' + img.upper() + '=y\n')
253                                                         for img in SUB_IMAGES ]
254     try:
255         f = open(KCONFIG_CONFIG)
256     except IOError as exception:
257         if not ignore_error or exception.errno != errno.ENOENT:
258             raise
259         return enabled
260     with f:
261         for line in f:
262             for img, pattern in match_patterns:
263                 if line == pattern:
264                     enabled += (img,)
265     return enabled
266
267 def do_silentoldconfig(cmd):
268     """Run 'make silentoldconfig' for all the enabled images.
269
270     Arguments:
271       cmd: should always be a string 'silentoldconfig'
272     """
273     run_make_config(cmd, '')
274     subimages = get_enabled_subimages()
275     for obj in subimages:
276         mkdirs(os.path.join(obj, 'include', 'config'),
277                os.path.join(obj, 'include', 'generated'))
278         run_make_config(cmd, obj)
279     remove_auto_conf = lambda : rmfiles('include/config/auto.conf')
280     # If the following part failed, include/config/auto.conf should be deleted
281     # so 'make silentoldconfig' will be re-run on the next build.
282     run_command(autoconf %
283                 ('include', 'include/autoconf.mk include/autoconf.mk.dep'),
284                 remove_auto_conf)
285     # include/config.h has been updated after 'make silentoldconfig'.
286     # We need to touch include/config/auto.conf so it gets newer
287     # than include/config.h.
288     # Otherwise, 'make silentoldconfig' would be invoked twice.
289     os.utime('include/config/auto.conf', None)
290     for obj in subimages:
291         run_command(autoconf % (obj + '/include',
292                                 obj + '/include/autoconf.mk'),
293                     remove_auto_conf)
294
295 def do_tmp_defconfig(output_lines, img):
296     """Helper function for do_board_defconfig().
297
298     Write the defconfig contents into a file '.tmp_defconfig' and
299     invoke 'make .tmp_defconfig'.
300
301     Arguments:
302       output_lines: A sequence of defconfig lines of each image
303       img: Target image. Typically '', 'spl', 'tpl' for
304            Normal, SPL, TPL images, respectively.
305     """
306     TMP_DEFCONFIG = '.tmp_defconfig'
307     TMP_DIRS = ('arch', 'configs')
308     defconfig_path = os.path.join('configs', TMP_DEFCONFIG)
309     mkdirs(*TMP_DIRS)
310     with open(defconfig_path, 'w') as f:
311         f.write(''.join(output_lines[img]))
312     cleanup = lambda: (rmfiles(defconfig_path), rmdirs(*TMP_DIRS))
313     run_make_config(TMP_DEFCONFIG, img, cleanup)
314     cleanup()
315
316 def do_board_defconfig(cmd):
317     """Run 'make <board>_defconfig'.
318
319     Arguments:
320       cmd: should be a string '<board>_defconfig'
321     """
322     defconfig_path = os.path.join(srctree, 'configs', cmd)
323     output_lines = dict([ (img, []) for img in IMAGES ])
324     with open(defconfig_path) as f:
325         for line in f:
326             m = PATTERN_SYMBOL.match(line)
327             if m:
328                 for idx, img in enumerate(IMAGES):
329                     if m.group(idx + 1):
330                         output_lines[img].append(m.group(4) + '\n')
331                 continue
332             output_lines[''].append(line)
333     do_tmp_defconfig(output_lines, '')
334     for img in get_enabled_subimages():
335         do_tmp_defconfig(output_lines, img)
336
337 def do_defconfig(cmd):
338     """Run 'make defconfig'.
339
340     Arguments:
341       cmd: should always be a string 'defconfig'
342     """
343     KBUILD_DEFCONFIG = os.environ['KBUILD_DEFCONFIG']
344     print "*** Default configuration is based on '%s'" % KBUILD_DEFCONFIG
345     do_board_defconfig(KBUILD_DEFCONFIG)
346
347 def do_savedefconfig(cmd):
348     """Run 'make savedefconfig'.
349
350     Arguments:
351       cmd: should always be a string 'savedefconfig'
352     """
353     DEFCONFIG = 'defconfig'
354     # Continue even if '.config' does not exist
355     subimages = get_enabled_subimages(True)
356     run_make_config(cmd, '')
357     output_lines = []
358     prefix = {}
359     with open(DEFCONFIG) as f:
360         for line in f:
361             output_lines.append(line)
362             prefix[line] = '+'
363     for img in subimages:
364         run_make_config(cmd, img)
365         unmatched_lines = []
366         with open(DEFCONFIG) as f:
367             for line in f:
368                 if line in output_lines:
369                     index = output_lines.index(line)
370                     output_lines[index:index] = unmatched_lines
371                     unmatched_lines = []
372                     prefix[line] += SYMBOL_MAP[img]
373                 else:
374                     ummatched_lines.append(line)
375                     prefix[line] = SYMBOL_MAP[img]
376     with open(DEFCONFIG, 'w') as f:
377         for line in output_lines:
378             if prefix[line] == '+':
379                 f.write(line)
380             else:
381                 f.write(prefix[line] + ':' + line)
382
383 def do_others(cmd):
384     """Run the make command other than 'silentoldconfig', 'defconfig',
385     '<board>_defconfig' and 'savedefconfig'.
386
387     Arguments:
388       cmd: Make target in the form of '<target_image>/<config_command>'
389            The field '<target_image>/' is typically empty, 'spl/', 'tpl/'
390            for Normal, SPL, TPL images, respectively.
391            The field '<config_command>' is make target such as 'config',
392            'menuconfig', etc.
393     """
394     objdir, _, cmd = cmd.rpartition('/')
395     run_make_config(cmd, objdir)
396
397 cmd_list = {'silentoldconfig': do_silentoldconfig,
398             'defconfig': do_defconfig,
399             'savedefconfig': do_savedefconfig}
400
401 def main():
402     cmd = sys.argv[1]
403     if cmd.endswith('_defconfig'):
404         do_board_defconfig(cmd)
405     else:
406         func = cmd_list.get(cmd, do_others)
407         func(cmd)
408
409 if __name__ == '__main__':
410     main()