blob: 96834c4f776b1fc137f840fee1687024f463df74 [file] [log] [blame]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001#!/usr/bin/env python2
2#
3# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4#
5# SPDX-License-Identifier: GPL-2.0+
6#
7
8"""
9Move config options from headers to defconfig files.
10
11Since Kconfig was introduced to U-Boot, we have worked on moving
12config options from headers to Kconfig (defconfig).
13
14This tool intends to help this tremendous work.
15
16
17Usage
18-----
19
20This tool takes one input file. (let's say 'recipe' file here.)
21The recipe describes the list of config options you want to move.
22Each line takes the form:
23<config_name> <type> <default>
24(the fields must be separated with whitespaces.)
25
26<config_name> is the name of config option.
27
28<type> is the type of the option. It must be one of bool, tristate,
29string, int, and hex.
30
31<default> is the default value of the option. It must be appropriate
32value corresponding to the option type. It must be either y or n for
33the bool type. Tristate options can also take m (although U-Boot has
34not supported the module feature).
35
36You can add two or more lines in the recipe file, so you can move
37multiple options at once.
38
39Let's say, for example, you want to move CONFIG_CMD_USB and
40CONFIG_SYS_TEXT_BASE.
41
42The type should be bool, hex, respectively. So, the recipe file
43should look like this:
44
45 $ cat recipe
46 CONFIG_CMD_USB bool n
47 CONFIG_SYS_TEXT_BASE hex 0x00000000
48
Joe Hershberger96464ba2015-05-19 13:21:17 -050049Next you must edit the Kconfig to add the menu entries for the configs
50you are moving.
51
Masahiro Yamada5a27c732015-05-20 11:36:07 +090052And then run this tool giving the file name of the recipe
53
54 $ tools/moveconfig.py recipe
55
56The tool walks through all the defconfig files to move the config
57options specified by the recipe file.
58
59The log is also displayed on the terminal.
60
61Each line is printed in the format
62<defconfig_name> : <action>
63
64<defconfig_name> is the name of the defconfig
65(without the suffix _defconfig).
66
67<action> shows what the tool did for that defconfig.
68It looks like one of the followings:
69
70 - Move 'CONFIG_... '
71 This config option was moved to the defconfig
72
Masahiro Yamadacc008292016-05-19 15:51:56 +090073 - CONFIG_... is not defined in Kconfig. Do nothing.
74 The entry for this CONFIG was not found in Kconfig.
75 There are two common cases:
76 - You forgot to create an entry for the CONFIG before running
77 this tool, or made a typo in a CONFIG passed to this tool.
78 - The entry was hidden due to unmet 'depends on'.
79 This is correct behavior.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090080
Masahiro Yamadacc008292016-05-19 15:51:56 +090081 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
82 The define in the config header matched the one in Kconfig.
83 We do not need to touch it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090084
85 - Undefined. Do nothing.
86 This config option was not found in the config header.
87 Nothing to do.
88
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +090089 - Compiler is missing. Do nothing.
90 The compiler specified for this architecture was not found
91 in your PATH environment.
92 (If -e option is passed, the tool exits immediately.)
93
94 - Failed to process.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090095 An error occurred during processing this defconfig. Skipped.
96 (If -e option is passed, the tool exits immediately on error.)
97
98Finally, you will be asked, Clean up headers? [y/n]:
99
100If you say 'y' here, the unnecessary config defines are removed
101from the config headers (include/configs/*.h).
102It just uses the regex method, so you should not rely on it.
103Just in case, please do 'git diff' to see what happened.
104
105
106How does it works?
107------------------
108
109This tool runs configuration and builds include/autoconf.mk for every
110defconfig. The config options defined in Kconfig appear in the .config
111file (unless they are hidden because of unmet dependency.)
112On the other hand, the config options defined by board headers are seen
113in include/autoconf.mk. The tool looks for the specified options in both
114of them to decide the appropriate action for the options. If the option
115is found in the .config or the value is the same as the specified default,
116the option does not need to be touched. If the option is found in
117include/autoconf.mk, but not in the .config, and the value is different
118from the default, the tools adds the option to the defconfig.
119
120For faster processing, this tool handles multi-threading. It creates
121separate build directories where the out-of-tree build is run. The
122temporary build directories are automatically created and deleted as
123needed. The number of threads are chosen based on the number of the CPU
124cores of your system although you can change it via -j (--jobs) option.
125
126
127Toolchains
128----------
129
130Appropriate toolchain are necessary to generate include/autoconf.mk
131for all the architectures supported by U-Boot. Most of them are available
132at the kernel.org site, some are not provided by kernel.org.
133
134The default per-arch CROSS_COMPILE used by this tool is specified by
135the list below, CROSS_COMPILE. You may wish to update the list to
136use your own. Instead of modifying the list directly, you can give
137them via environments.
138
139
140Available options
141-----------------
142
143 -c, --color
144 Surround each portion of the log with escape sequences to display it
145 in color on the terminal.
146
Joe Hershberger91040e82015-05-19 13:21:19 -0500147 -d, --defconfigs
148 Specify a file containing a list of defconfigs to move
149
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900150 -n, --dry-run
151 Peform a trial run that does not make any changes. It is useful to
152 see what is going to happen before one actually runs it.
153
154 -e, --exit-on-error
155 Exit immediately if Make exits with a non-zero status while processing
156 a defconfig file.
157
Joe Hershberger2144f882015-05-19 13:21:20 -0500158 -H, --headers-only
159 Only cleanup the headers; skip the defconfig processing
160
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900161 -j, --jobs
162 Specify the number of threads to run simultaneously. If not specified,
163 the number of threads is the same as the number of CPU cores.
164
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500165 -v, --verbose
166 Show any build errors as boards are built
167
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900168To see the complete list of supported options, run
169
170 $ tools/moveconfig.py -h
171
172"""
173
174import fnmatch
175import multiprocessing
176import optparse
177import os
178import re
179import shutil
180import subprocess
181import sys
182import tempfile
183import time
184
185SHOW_GNU_MAKE = 'scripts/show-gnu-make'
186SLEEP_TIME=0.03
187
188# Here is the list of cross-tools I use.
189# Most of them are available at kernel.org
190# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the followings:
191# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
192# blackfin: http://sourceforge.net/projects/adi-toolchain/files/
Bin Meng4440ece2015-09-25 01:22:39 -0700193# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900194# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
195# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
Bin Menge8aebc42016-02-21 21:18:02 -0800196#
197# openrisc kernel.org toolchain is out of date, download latest one from
198# http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900199CROSS_COMPILE = {
200 'arc': 'arc-linux-',
201 'aarch64': 'aarch64-linux-',
202 'arm': 'arm-unknown-linux-gnueabi-',
203 'avr32': 'avr32-linux-',
204 'blackfin': 'bfin-elf-',
205 'm68k': 'm68k-linux-',
206 'microblaze': 'microblaze-linux-',
207 'mips': 'mips-linux-',
208 'nds32': 'nds32le-linux-',
209 'nios2': 'nios2-linux-gnu-',
Bin Menge8aebc42016-02-21 21:18:02 -0800210 'openrisc': 'or1k-elf-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900211 'powerpc': 'powerpc-linux-',
212 'sh': 'sh-linux-gnu-',
213 'sparc': 'sparc-linux-',
214 'x86': 'i386-linux-'
215}
216
217STATE_IDLE = 0
218STATE_DEFCONFIG = 1
219STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500220STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900221
222ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900223ACTION_NO_ENTRY = 1
224ACTION_NO_CHANGE = 2
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900225
226COLOR_BLACK = '0;30'
227COLOR_RED = '0;31'
228COLOR_GREEN = '0;32'
229COLOR_BROWN = '0;33'
230COLOR_BLUE = '0;34'
231COLOR_PURPLE = '0;35'
232COLOR_CYAN = '0;36'
233COLOR_LIGHT_GRAY = '0;37'
234COLOR_DARK_GRAY = '1;30'
235COLOR_LIGHT_RED = '1;31'
236COLOR_LIGHT_GREEN = '1;32'
237COLOR_YELLOW = '1;33'
238COLOR_LIGHT_BLUE = '1;34'
239COLOR_LIGHT_PURPLE = '1;35'
240COLOR_LIGHT_CYAN = '1;36'
241COLOR_WHITE = '1;37'
242
243### helper functions ###
244def get_devnull():
245 """Get the file object of '/dev/null' device."""
246 try:
247 devnull = subprocess.DEVNULL # py3k
248 except AttributeError:
249 devnull = open(os.devnull, 'wb')
250 return devnull
251
252def check_top_directory():
253 """Exit if we are not at the top of source directory."""
254 for f in ('README', 'Licenses'):
255 if not os.path.exists(f):
256 sys.exit('Please run at the top of source directory.')
257
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900258def check_clean_directory():
259 """Exit if the source tree is not clean."""
260 for f in ('.config', 'include/config'):
261 if os.path.exists(f):
262 sys.exit("source tree is not clean, please run 'make mrproper'")
263
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900264def get_make_cmd():
265 """Get the command name of GNU Make.
266
267 U-Boot needs GNU Make for building, but the command name is not
268 necessarily "make". (for example, "gmake" on FreeBSD).
269 Returns the most appropriate command name on your system.
270 """
271 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
272 ret = process.communicate()
273 if process.returncode:
274 sys.exit('GNU Make not found')
275 return ret[0].rstrip()
276
277def color_text(color_enabled, color, string):
278 """Return colored string."""
279 if color_enabled:
280 return '\033[' + color + 'm' + string + '\033[0m'
281 else:
282 return string
283
284def log_msg(color_enabled, color, defconfig, msg):
285 """Return the formated line for the log."""
286 return defconfig[:-len('_defconfig')].ljust(37) + ': ' + \
287 color_text(color_enabled, color, msg) + '\n'
288
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900289def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400290 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900291
292 The default CROSS_COMPILE values are available
293 in the CROSS_COMPILE list above.
294
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400295 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900296 CROSS_COMPILE_{ARCH}.
297
298 For example, if you want to override toolchain prefixes
299 for ARM and PowerPC, you can do as follows in your shell:
300
301 export CROSS_COMPILE_ARM=...
302 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900303
304 Then, this function checks if specified compilers really exist in your
305 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900306 """
307 archs = []
308
309 for arch in os.listdir('arch'):
310 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
311 archs.append(arch)
312
313 # arm64 is a special case
314 archs.append('aarch64')
315
316 for arch in archs:
317 env = 'CROSS_COMPILE_' + arch.upper()
318 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900319 if not cross_compile:
320 cross_compile = CROSS_COMPILE.get(arch, '')
321
322 for path in os.environ["PATH"].split(os.pathsep):
323 gcc_path = os.path.join(path, cross_compile + 'gcc')
324 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
325 break
326 else:
327 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
328 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
329 % (cross_compile, arch))
330 cross_compile = None
331
332 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900333
334def cleanup_one_header(header_path, patterns, dry_run):
335 """Clean regex-matched lines away from a file.
336
337 Arguments:
338 header_path: path to the cleaned file.
339 patterns: list of regex patterns. Any lines matching to these
340 patterns are deleted.
341 dry_run: make no changes, but still display log.
342 """
343 with open(header_path) as f:
344 lines = f.readlines()
345
346 matched = []
347 for i, line in enumerate(lines):
348 for pattern in patterns:
349 m = pattern.search(line)
350 if m:
351 print '%s: %s: %s' % (header_path, i + 1, line),
352 matched.append(i)
353 break
354
355 if dry_run or not matched:
356 return
357
358 with open(header_path, 'w') as f:
359 for i, line in enumerate(lines):
360 if not i in matched:
361 f.write(line)
362
363def cleanup_headers(config_attrs, dry_run):
364 """Delete config defines from board headers.
365
366 Arguments:
367 config_attrs: A list of dictionaris, each of them includes the name,
368 the type, and the default value of the target config.
369 dry_run: make no changes, but still display log.
370 """
371 while True:
372 choice = raw_input('Clean up headers? [y/n]: ').lower()
373 print choice
374 if choice == 'y' or choice == 'n':
375 break
376
377 if choice == 'n':
378 return
379
380 patterns = []
381 for config_attr in config_attrs:
382 config = config_attr['config']
383 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
384 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
385
Joe Hershberger60727f52015-05-19 13:21:21 -0500386 for dir in 'include', 'arch', 'board':
387 for (dirpath, dirnames, filenames) in os.walk(dir):
388 for filename in filenames:
389 if not fnmatch.fnmatch(filename, '*~'):
390 cleanup_one_header(os.path.join(dirpath, filename),
391 patterns, dry_run)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900392
393### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900394class Progress:
395
396 """Progress Indicator"""
397
398 def __init__(self, total):
399 """Create a new progress indicator.
400
401 Arguments:
402 total: A number of defconfig files to process.
403 """
404 self.current = 0
405 self.total = total
406
407 def inc(self):
408 """Increment the number of processed defconfig files."""
409
410 self.current += 1
411
412 def show(self):
413 """Display the progress."""
414 print ' %d defconfigs out of %d\r' % (self.current, self.total),
415 sys.stdout.flush()
416
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900417class KconfigParser:
418
419 """A parser of .config and include/autoconf.mk."""
420
421 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
422 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
423
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900424 def __init__(self, config_attrs, options, progress, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900425 """Create a new parser.
426
427 Arguments:
428 config_attrs: A list of dictionaris, each of them includes the name,
429 the type, and the default value of the target config.
430 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900431 progress: A progress indicator
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900432 build_dir: Build directory.
433 """
434 self.config_attrs = config_attrs
435 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900436 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900437 self.build_dir = build_dir
438
439 def get_cross_compile(self):
440 """Parse .config file and return CROSS_COMPILE.
441
442 Returns:
443 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900444 Return a NULL string for architectures that do not require
445 compiler prefix (Sandbox and native build is the case).
446 Return None if the specified compiler is missing in your PATH.
447 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900448 """
449 arch = ''
450 cpu = ''
451 dotconfig = os.path.join(self.build_dir, '.config')
452 for line in open(dotconfig):
453 m = self.re_arch.match(line)
454 if m:
455 arch = m.group(1)
456 continue
457 m = self.re_cpu.match(line)
458 if m:
459 cpu = m.group(1)
460
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900461 if not arch:
462 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900463
464 # fix-up for aarch64
465 if arch == 'arm' and cpu == 'armv8':
466 arch = 'aarch64'
467
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900468 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900469
Masahiro Yamadacc008292016-05-19 15:51:56 +0900470 def parse_one_config(self, config_attr, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900471 """Parse .config, defconfig, include/autoconf.mk for one config.
472
473 This function looks for the config options in the lines from
474 defconfig, .config, and include/autoconf.mk in order to decide
475 which action should be taken for this defconfig.
476
477 Arguments:
478 config_attr: A dictionary including the name, the type,
479 and the default value of the target config.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900480 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900481 autoconf_lines: lines from the include/autoconf.mk file.
482
483 Returns:
484 A tupple of the action for this defconfig and the line
485 matched for the config.
486 """
487 config = config_attr['config']
488 not_set = '# %s is not set' % config
489
Masahiro Yamadacc008292016-05-19 15:51:56 +0900490 for line in dotconfig_lines:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900491 line = line.rstrip()
492 if line.startswith(config + '=') or line == not_set:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900493 old_val = line
494 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900495 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900496 return (ACTION_NO_ENTRY, config)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900497
498 for line in autoconf_lines:
499 line = line.rstrip()
500 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900501 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900502 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900503 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900504 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900505
Masahiro Yamadacc008292016-05-19 15:51:56 +0900506 if old_val == new_val:
507 return (ACTION_NO_CHANGE, new_val)
508
509 # If this CONFIG is neither bool nor trisate
510 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
511 # tools/scripts/define2mk.sed changes '1' to 'y'.
512 # This is a problem if the CONFIG is int type.
513 # Check the type in Kconfig and handle it correctly.
514 if new_val[-2:] == '=y':
515 new_val = new_val[:-1] + '1'
516
517 return (ACTION_MOVE, new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900518
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900519 def update_dotconfig(self, defconfig):
520 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521
Masahiro Yamadacc008292016-05-19 15:51:56 +0900522 This function parses the generated .config and include/autoconf.mk
523 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900524 Move the config option(s) to the .config as needed.
525 Also, display the log to show what happened to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900526
527 Arguments:
528 defconfig: defconfig name.
529 """
530
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900531 dotconfig_path = os.path.join(self.build_dir, '.config')
532 autoconf_path = os.path.join(self.build_dir, 'include', 'autoconf.mk')
533 results = []
534
Masahiro Yamadacc008292016-05-19 15:51:56 +0900535 with open(dotconfig_path) as f:
536 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900537
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900538 with open(autoconf_path) as f:
539 autoconf_lines = f.readlines()
540
541 for config_attr in self.config_attrs:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900542 result = self.parse_one_config(config_attr, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500543 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900544 results.append(result)
545
546 log = ''
547
548 for (action, value) in results:
549 if action == ACTION_MOVE:
550 actlog = "Move '%s'" % value
551 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900552 elif action == ACTION_NO_ENTRY:
553 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900554 log_color = COLOR_LIGHT_BLUE
Masahiro Yamadacc008292016-05-19 15:51:56 +0900555 elif action == ACTION_NO_CHANGE:
556 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
557 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900558 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900559 else:
560 sys.exit("Internal Error. This should not happen.")
561
562 log += log_msg(self.options.color, log_color, defconfig, actlog)
563
564 # Some threads are running in parallel.
565 # Print log in one shot to not mix up logs from different threads.
566 print log,
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900567 self.progress.show()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900568
Masahiro Yamadae423d172016-05-19 15:51:49 +0900569 with open(dotconfig_path, 'a') as f:
570 for (action, value) in results:
571 if action == ACTION_MOVE:
572 f.write(value + '\n')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900573
574 os.remove(os.path.join(self.build_dir, 'include', 'config', 'auto.conf'))
575 os.remove(autoconf_path)
576
577class Slot:
578
579 """A slot to store a subprocess.
580
581 Each instance of this class handles one subprocess.
582 This class is useful to control multiple threads
583 for faster processing.
584 """
585
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900586 def __init__(self, config_attrs, options, progress, devnull, make_cmd):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900587 """Create a new process slot.
588
589 Arguments:
590 config_attrs: A list of dictionaris, each of them includes the name,
591 the type, and the default value of the target config.
592 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900593 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900594 devnull: A file object of '/dev/null'.
595 make_cmd: command name of GNU Make.
596 """
597 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900598 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900599 self.build_dir = tempfile.mkdtemp()
600 self.devnull = devnull
601 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900602 self.parser = KconfigParser(config_attrs, options, progress,
603 self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900604 self.state = STATE_IDLE
605 self.failed_boards = []
606
607 def __del__(self):
608 """Delete the working directory
609
610 This function makes sure the temporary directory is cleaned away
611 even if Python suddenly dies due to error. It should be done in here
612 because it is guranteed the destructor is always invoked when the
613 instance of the class gets unreferenced.
614
615 If the subprocess is still running, wait until it finishes.
616 """
617 if self.state != STATE_IDLE:
618 while self.ps.poll() == None:
619 pass
620 shutil.rmtree(self.build_dir)
621
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900622 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900623 """Assign a new subprocess for defconfig and add it to the slot.
624
625 If the slot is vacant, create a new subprocess for processing the
626 given defconfig and add it to the slot. Just returns False if
627 the slot is occupied (i.e. the current subprocess is still running).
628
629 Arguments:
630 defconfig: defconfig name.
631
632 Returns:
633 Return True on success or False on failure
634 """
635 if self.state != STATE_IDLE:
636 return False
637 cmd = list(self.make_cmd)
638 cmd.append(defconfig)
Joe Hershberger25400092015-05-19 13:21:23 -0500639 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
640 stderr=subprocess.PIPE)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900641 self.defconfig = defconfig
642 self.state = STATE_DEFCONFIG
643 return True
644
645 def poll(self):
646 """Check the status of the subprocess and handle it as needed.
647
648 Returns True if the slot is vacant (i.e. in idle state).
649 If the configuration is successfully finished, assign a new
650 subprocess to build include/autoconf.mk.
651 If include/autoconf.mk is generated, invoke the parser to
652 parse the .config and the include/autoconf.mk, and then set the
653 slot back to the idle state.
654
655 Returns:
656 Return True if the subprocess is terminated, False otherwise
657 """
658 if self.state == STATE_IDLE:
659 return True
660
661 if self.ps.poll() == None:
662 return False
663
664 if self.ps.poll() != 0:
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900665 print >> sys.stderr, log_msg(self.options.color, COLOR_LIGHT_RED,
666 self.defconfig, "Failed to process."),
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500667 if self.options.verbose:
668 print >> sys.stderr, color_text(self.options.color,
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900669 COLOR_LIGHT_CYAN,
670 self.ps.stderr.read())
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900671 self.progress.inc()
672 self.progress.show()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900673 if self.options.exit_on_error:
674 sys.exit("Exit on error.")
Masahiro Yamadaff8725b2016-05-19 15:51:51 +0900675 # If --exit-on-error flag is not set, skip this board and continue.
676 # Record the failed board.
677 self.failed_boards.append(self.defconfig)
678 self.state = STATE_IDLE
679 return True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900680
681 if self.state == STATE_AUTOCONF:
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900682 self.parser.update_dotconfig(self.defconfig)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500683
684 """Save off the defconfig in a consistent way"""
685 cmd = list(self.make_cmd)
686 cmd.append('savedefconfig')
687 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Joe Hershberger25400092015-05-19 13:21:23 -0500688 stderr=subprocess.PIPE)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500689 self.state = STATE_SAVEDEFCONFIG
690 return False
691
692 if self.state == STATE_SAVEDEFCONFIG:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900693 if not self.options.dry_run:
694 shutil.move(os.path.join(self.build_dir, 'defconfig'),
695 os.path.join('configs', self.defconfig))
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900696 self.progress.inc()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900697 self.state = STATE_IDLE
698 return True
699
Joe Hershberger25400092015-05-19 13:21:23 -0500700 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900701 if self.cross_compile is None:
702 print >> sys.stderr, log_msg(self.options.color, COLOR_YELLOW,
703 self.defconfig,
704 "Compiler is missing. Do nothing."),
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900705 self.progress.inc()
706 self.progress.show()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900707 if self.options.exit_on_error:
708 sys.exit("Exit on error.")
709 # If --exit-on-error flag is not set, skip this board and continue.
710 # Record the failed board.
711 self.failed_boards.append(self.defconfig)
712 self.state = STATE_IDLE
713 return True
714
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900715 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -0500716 if self.cross_compile:
717 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -0500718 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900719 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -0500720 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Joe Hershberger25400092015-05-19 13:21:23 -0500721 stderr=subprocess.PIPE)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900722 self.state = STATE_AUTOCONF
723 return False
724
725 def get_failed_boards(self):
726 """Returns a list of failed boards (defconfigs) in this slot.
727 """
728 return self.failed_boards
729
730class Slots:
731
732 """Controller of the array of subprocess slots."""
733
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900734 def __init__(self, config_attrs, options, progress):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900735 """Create a new slots controller.
736
737 Arguments:
738 config_attrs: A list of dictionaris containing the name, the type,
739 and the default value of the target CONFIG.
740 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900741 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900742 """
743 self.options = options
744 self.slots = []
745 devnull = get_devnull()
746 make_cmd = get_make_cmd()
747 for i in range(options.jobs):
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900748 self.slots.append(Slot(config_attrs, options, progress, devnull,
749 make_cmd))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900750
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900751 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900752 """Add a new subprocess if a vacant slot is found.
753
754 Arguments:
755 defconfig: defconfig name to be put into.
756
757 Returns:
758 Return True on success or False on failure
759 """
760 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900761 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900762 return True
763 return False
764
765 def available(self):
766 """Check if there is a vacant slot.
767
768 Returns:
769 Return True if at lease one vacant slot is found, False otherwise.
770 """
771 for slot in self.slots:
772 if slot.poll():
773 return True
774 return False
775
776 def empty(self):
777 """Check if all slots are vacant.
778
779 Returns:
780 Return True if all the slots are vacant, False otherwise.
781 """
782 ret = True
783 for slot in self.slots:
784 if not slot.poll():
785 ret = False
786 return ret
787
788 def show_failed_boards(self):
789 """Display all of the failed boards (defconfigs)."""
790 failed_boards = []
791
792 for slot in self.slots:
793 failed_boards += slot.get_failed_boards()
794
795 if len(failed_boards) > 0:
796 msg = [ "The following boards were not processed due to error:" ]
797 msg += failed_boards
798 for line in msg:
799 print >> sys.stderr, color_text(self.options.color,
800 COLOR_LIGHT_RED, line)
801
Joe Hershberger2559cd82015-05-19 13:21:22 -0500802 with open('moveconfig.failed', 'w') as f:
803 for board in failed_boards:
804 f.write(board + '\n')
805
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900806def move_config(config_attrs, options):
807 """Move config options to defconfig files.
808
809 Arguments:
810 config_attrs: A list of dictionaris, each of them includes the name,
811 the type, and the default value of the target config.
812 options: option flags
813 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900814 if len(config_attrs) == 0:
815 print 'Nothing to do. exit.'
816 sys.exit(0)
817
818 print 'Move the following CONFIG options (jobs: %d)' % options.jobs
819 for config_attr in config_attrs:
820 print ' %s (type: %s, default: %s)' % (config_attr['config'],
821 config_attr['type'],
822 config_attr['default'])
823
Joe Hershberger91040e82015-05-19 13:21:19 -0500824 if options.defconfigs:
825 defconfigs = [line.strip() for line in open(options.defconfigs)]
826 for i, defconfig in enumerate(defconfigs):
827 if not defconfig.endswith('_defconfig'):
828 defconfigs[i] = defconfig + '_defconfig'
829 if not os.path.exists(os.path.join('configs', defconfigs[i])):
830 sys.exit('%s - defconfig does not exist. Stopping.' %
831 defconfigs[i])
832 else:
833 # All the defconfig files to be processed
834 defconfigs = []
835 for (dirpath, dirnames, filenames) in os.walk('configs'):
836 dirpath = dirpath[len('configs') + 1:]
837 for filename in fnmatch.filter(filenames, '*_defconfig'):
838 defconfigs.append(os.path.join(dirpath, filename))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900839
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900840 progress = Progress(len(defconfigs))
841 slots = Slots(config_attrs, options, progress)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900842
843 # Main loop to process defconfig files:
844 # Add a new subprocess into a vacant slot.
845 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900846 for defconfig in defconfigs:
847 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900848 while not slots.available():
849 # No available slot: sleep for a while
850 time.sleep(SLEEP_TIME)
851
852 # wait until all the subprocesses finish
853 while not slots.empty():
854 time.sleep(SLEEP_TIME)
855
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900856 progress.show()
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -0500857 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900858 slots.show_failed_boards()
859
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900860def bad_recipe(filename, linenum, msg):
861 """Print error message with the file name and the line number and exit."""
862 sys.exit("%s: line %d: error : " % (filename, linenum) + msg)
863
864def parse_recipe(filename):
865 """Parse the recipe file and retrieve the config attributes.
866
867 This function parses the given recipe file and gets the name,
868 the type, and the default value of the target config options.
869
870 Arguments:
871 filename: path to file to be parsed.
872 Returns:
873 A list of dictionaris, each of them includes the name,
874 the type, and the default value of the target config.
875 """
876 config_attrs = []
877 linenum = 1
878
879 for line in open(filename):
880 tokens = line.split()
881 if len(tokens) != 3:
882 bad_recipe(filename, linenum,
883 "%d fields in this line. Each line must contain 3 fields"
884 % len(tokens))
885
886 (config, type, default) = tokens
887
888 # prefix the option name with CONFIG_ if missing
889 if not config.startswith('CONFIG_'):
890 config = 'CONFIG_' + config
891
892 # sanity check of default values
893 if type == 'bool':
894 if not default in ('y', 'n'):
895 bad_recipe(filename, linenum,
896 "default for bool type must be either y or n")
897 elif type == 'tristate':
898 if not default in ('y', 'm', 'n'):
899 bad_recipe(filename, linenum,
900 "default for tristate type must be y, m, or n")
901 elif type == 'string':
902 if default[0] != '"' or default[-1] != '"':
903 bad_recipe(filename, linenum,
904 "default for string type must be surrounded by double-quotations")
905 elif type == 'int':
906 try:
907 int(default)
908 except:
909 bad_recipe(filename, linenum,
910 "type is int, but default value is not decimal")
911 elif type == 'hex':
912 if len(default) < 2 or default[:2] != '0x':
913 bad_recipe(filename, linenum,
914 "default for hex type must be prefixed with 0x")
915 try:
916 int(default, 16)
917 except:
918 bad_recipe(filename, linenum,
919 "type is hex, but default value is not hexadecimal")
920 else:
921 bad_recipe(filename, linenum,
922 "unsupported type '%s'. type must be one of bool, tristate, string, int, hex"
923 % type)
924
925 config_attrs.append({'config': config, 'type': type, 'default': default})
926 linenum += 1
927
928 return config_attrs
929
930def main():
931 try:
932 cpu_count = multiprocessing.cpu_count()
933 except NotImplementedError:
934 cpu_count = 1
935
936 parser = optparse.OptionParser()
937 # Add options here
938 parser.add_option('-c', '--color', action='store_true', default=False,
939 help='display the log in color')
Joe Hershberger91040e82015-05-19 13:21:19 -0500940 parser.add_option('-d', '--defconfigs', type='string',
941 help='a file containing a list of defconfigs to move')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900942 parser.add_option('-n', '--dry-run', action='store_true', default=False,
943 help='perform a trial run (show log with no changes)')
944 parser.add_option('-e', '--exit-on-error', action='store_true',
945 default=False,
946 help='exit immediately on any error')
Joe Hershberger2144f882015-05-19 13:21:20 -0500947 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
948 action='store_true', default=False,
949 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900950 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
951 help='the number of jobs to run simultaneously')
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500952 parser.add_option('-v', '--verbose', action='store_true', default=False,
953 help='show any build errors as boards are built')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900954 parser.usage += ' recipe_file\n\n' + \
955 'The recipe_file should describe config options you want to move.\n' + \
956 'Each line should contain config_name, type, default_value\n\n' + \
957 'Example:\n' + \
958 'CONFIG_FOO bool n\n' + \
959 'CONFIG_BAR int 100\n' + \
960 'CONFIG_BAZ string "hello"\n'
961
962 (options, args) = parser.parse_args()
963
964 if len(args) != 1:
965 parser.print_usage()
966 sys.exit(1)
967
968 config_attrs = parse_recipe(args[0])
969
Joe Hershberger2144f882015-05-19 13:21:20 -0500970 check_top_directory()
971
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900972 check_clean_directory()
973
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900974 update_cross_compile(options.color)
Masahiro Yamada4b430c92016-05-19 15:51:52 +0900975
Joe Hershberger2144f882015-05-19 13:21:20 -0500976 if not options.cleanup_headers_only:
977 move_config(config_attrs, options)
978
979 cleanup_headers(config_attrs, options.dry_run)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900980
981if __name__ == '__main__':
982 main()