blob: d54dd4e1c8c1845118b0ecb7f6e1e05ac2f766c1 [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
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900363def cleanup_headers(configs, dry_run):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900364 """Delete config defines from board headers.
365
366 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900367 configs: A list of CONFIGs to remove.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900368 dry_run: make no changes, but still display log.
369 """
370 while True:
371 choice = raw_input('Clean up headers? [y/n]: ').lower()
372 print choice
373 if choice == 'y' or choice == 'n':
374 break
375
376 if choice == 'n':
377 return
378
379 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900380 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900381 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
382 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
383
Joe Hershberger60727f52015-05-19 13:21:21 -0500384 for dir in 'include', 'arch', 'board':
385 for (dirpath, dirnames, filenames) in os.walk(dir):
386 for filename in filenames:
387 if not fnmatch.fnmatch(filename, '*~'):
388 cleanup_one_header(os.path.join(dirpath, filename),
389 patterns, dry_run)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900390
391### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900392class Progress:
393
394 """Progress Indicator"""
395
396 def __init__(self, total):
397 """Create a new progress indicator.
398
399 Arguments:
400 total: A number of defconfig files to process.
401 """
402 self.current = 0
403 self.total = total
404
405 def inc(self):
406 """Increment the number of processed defconfig files."""
407
408 self.current += 1
409
410 def show(self):
411 """Display the progress."""
412 print ' %d defconfigs out of %d\r' % (self.current, self.total),
413 sys.stdout.flush()
414
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900415class KconfigParser:
416
417 """A parser of .config and include/autoconf.mk."""
418
419 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
420 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
421
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900422 def __init__(self, configs, options, progress, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900423 """Create a new parser.
424
425 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900426 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900427 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900428 progress: A progress indicator
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900429 build_dir: Build directory.
430 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900431 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900432 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900433 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900434 self.build_dir = build_dir
435
436 def get_cross_compile(self):
437 """Parse .config file and return CROSS_COMPILE.
438
439 Returns:
440 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900441 Return a NULL string for architectures that do not require
442 compiler prefix (Sandbox and native build is the case).
443 Return None if the specified compiler is missing in your PATH.
444 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900445 """
446 arch = ''
447 cpu = ''
448 dotconfig = os.path.join(self.build_dir, '.config')
449 for line in open(dotconfig):
450 m = self.re_arch.match(line)
451 if m:
452 arch = m.group(1)
453 continue
454 m = self.re_cpu.match(line)
455 if m:
456 cpu = m.group(1)
457
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900458 if not arch:
459 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900460
461 # fix-up for aarch64
462 if arch == 'arm' and cpu == 'armv8':
463 arch = 'aarch64'
464
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900465 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900466
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900467 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900468 """Parse .config, defconfig, include/autoconf.mk for one config.
469
470 This function looks for the config options in the lines from
471 defconfig, .config, and include/autoconf.mk in order to decide
472 which action should be taken for this defconfig.
473
474 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900475 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900476 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900477 autoconf_lines: lines from the include/autoconf.mk file.
478
479 Returns:
480 A tupple of the action for this defconfig and the line
481 matched for the config.
482 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900483 not_set = '# %s is not set' % config
484
Masahiro Yamadacc008292016-05-19 15:51:56 +0900485 for line in dotconfig_lines:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900486 line = line.rstrip()
487 if line.startswith(config + '=') or line == not_set:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900488 old_val = line
489 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900490 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900491 return (ACTION_NO_ENTRY, config)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900492
493 for line in autoconf_lines:
494 line = line.rstrip()
495 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900496 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900497 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900498 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900499 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900500
Masahiro Yamadacc008292016-05-19 15:51:56 +0900501 if old_val == new_val:
502 return (ACTION_NO_CHANGE, new_val)
503
504 # If this CONFIG is neither bool nor trisate
505 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
506 # tools/scripts/define2mk.sed changes '1' to 'y'.
507 # This is a problem if the CONFIG is int type.
508 # Check the type in Kconfig and handle it correctly.
509 if new_val[-2:] == '=y':
510 new_val = new_val[:-1] + '1'
511
512 return (ACTION_MOVE, new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900513
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900514 def update_dotconfig(self, defconfig):
515 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900516
Masahiro Yamadacc008292016-05-19 15:51:56 +0900517 This function parses the generated .config and include/autoconf.mk
518 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900519 Move the config option(s) to the .config as needed.
520 Also, display the log to show what happened to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521
522 Arguments:
523 defconfig: defconfig name.
524 """
525
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900526 dotconfig_path = os.path.join(self.build_dir, '.config')
527 autoconf_path = os.path.join(self.build_dir, 'include', 'autoconf.mk')
528 results = []
529
Masahiro Yamadacc008292016-05-19 15:51:56 +0900530 with open(dotconfig_path) as f:
531 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900532
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900533 with open(autoconf_path) as f:
534 autoconf_lines = f.readlines()
535
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900536 for config in self.configs:
537 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500538 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900539 results.append(result)
540
541 log = ''
542
543 for (action, value) in results:
544 if action == ACTION_MOVE:
545 actlog = "Move '%s'" % value
546 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900547 elif action == ACTION_NO_ENTRY:
548 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900549 log_color = COLOR_LIGHT_BLUE
Masahiro Yamadacc008292016-05-19 15:51:56 +0900550 elif action == ACTION_NO_CHANGE:
551 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
552 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900553 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900554 else:
555 sys.exit("Internal Error. This should not happen.")
556
557 log += log_msg(self.options.color, log_color, defconfig, actlog)
558
559 # Some threads are running in parallel.
560 # Print log in one shot to not mix up logs from different threads.
561 print log,
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900562 self.progress.show()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900563
Masahiro Yamadae423d172016-05-19 15:51:49 +0900564 with open(dotconfig_path, 'a') as f:
565 for (action, value) in results:
566 if action == ACTION_MOVE:
567 f.write(value + '\n')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900568
569 os.remove(os.path.join(self.build_dir, 'include', 'config', 'auto.conf'))
570 os.remove(autoconf_path)
571
572class Slot:
573
574 """A slot to store a subprocess.
575
576 Each instance of this class handles one subprocess.
577 This class is useful to control multiple threads
578 for faster processing.
579 """
580
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900581 def __init__(self, configs, options, progress, devnull, make_cmd):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900582 """Create a new process slot.
583
584 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900585 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900586 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900587 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900588 devnull: A file object of '/dev/null'.
589 make_cmd: command name of GNU Make.
590 """
591 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900592 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900593 self.build_dir = tempfile.mkdtemp()
594 self.devnull = devnull
595 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900596 self.parser = KconfigParser(configs, options, progress, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900597 self.state = STATE_IDLE
598 self.failed_boards = []
599
600 def __del__(self):
601 """Delete the working directory
602
603 This function makes sure the temporary directory is cleaned away
604 even if Python suddenly dies due to error. It should be done in here
605 because it is guranteed the destructor is always invoked when the
606 instance of the class gets unreferenced.
607
608 If the subprocess is still running, wait until it finishes.
609 """
610 if self.state != STATE_IDLE:
611 while self.ps.poll() == None:
612 pass
613 shutil.rmtree(self.build_dir)
614
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900615 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900616 """Assign a new subprocess for defconfig and add it to the slot.
617
618 If the slot is vacant, create a new subprocess for processing the
619 given defconfig and add it to the slot. Just returns False if
620 the slot is occupied (i.e. the current subprocess is still running).
621
622 Arguments:
623 defconfig: defconfig name.
624
625 Returns:
626 Return True on success or False on failure
627 """
628 if self.state != STATE_IDLE:
629 return False
630 cmd = list(self.make_cmd)
631 cmd.append(defconfig)
Joe Hershberger25400092015-05-19 13:21:23 -0500632 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
633 stderr=subprocess.PIPE)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900634 self.defconfig = defconfig
635 self.state = STATE_DEFCONFIG
636 return True
637
638 def poll(self):
639 """Check the status of the subprocess and handle it as needed.
640
641 Returns True if the slot is vacant (i.e. in idle state).
642 If the configuration is successfully finished, assign a new
643 subprocess to build include/autoconf.mk.
644 If include/autoconf.mk is generated, invoke the parser to
645 parse the .config and the include/autoconf.mk, and then set the
646 slot back to the idle state.
647
648 Returns:
649 Return True if the subprocess is terminated, False otherwise
650 """
651 if self.state == STATE_IDLE:
652 return True
653
654 if self.ps.poll() == None:
655 return False
656
657 if self.ps.poll() != 0:
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900658 print >> sys.stderr, log_msg(self.options.color, COLOR_LIGHT_RED,
659 self.defconfig, "Failed to process."),
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500660 if self.options.verbose:
661 print >> sys.stderr, color_text(self.options.color,
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900662 COLOR_LIGHT_CYAN,
663 self.ps.stderr.read())
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900664 self.progress.inc()
665 self.progress.show()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900666 if self.options.exit_on_error:
667 sys.exit("Exit on error.")
Masahiro Yamadaff8725b2016-05-19 15:51:51 +0900668 # If --exit-on-error flag is not set, skip this board and continue.
669 # Record the failed board.
670 self.failed_boards.append(self.defconfig)
671 self.state = STATE_IDLE
672 return True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900673
674 if self.state == STATE_AUTOCONF:
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900675 self.parser.update_dotconfig(self.defconfig)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500676
677 """Save off the defconfig in a consistent way"""
678 cmd = list(self.make_cmd)
679 cmd.append('savedefconfig')
680 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Joe Hershberger25400092015-05-19 13:21:23 -0500681 stderr=subprocess.PIPE)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500682 self.state = STATE_SAVEDEFCONFIG
683 return False
684
685 if self.state == STATE_SAVEDEFCONFIG:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900686 if not self.options.dry_run:
687 shutil.move(os.path.join(self.build_dir, 'defconfig'),
688 os.path.join('configs', self.defconfig))
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900689 self.progress.inc()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900690 self.state = STATE_IDLE
691 return True
692
Joe Hershberger25400092015-05-19 13:21:23 -0500693 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900694 if self.cross_compile is None:
695 print >> sys.stderr, log_msg(self.options.color, COLOR_YELLOW,
696 self.defconfig,
697 "Compiler is missing. Do nothing."),
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900698 self.progress.inc()
699 self.progress.show()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900700 if self.options.exit_on_error:
701 sys.exit("Exit on error.")
702 # If --exit-on-error flag is not set, skip this board and continue.
703 # Record the failed board.
704 self.failed_boards.append(self.defconfig)
705 self.state = STATE_IDLE
706 return True
707
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900708 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -0500709 if self.cross_compile:
710 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -0500711 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900712 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -0500713 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Joe Hershberger25400092015-05-19 13:21:23 -0500714 stderr=subprocess.PIPE)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900715 self.state = STATE_AUTOCONF
716 return False
717
718 def get_failed_boards(self):
719 """Returns a list of failed boards (defconfigs) in this slot.
720 """
721 return self.failed_boards
722
723class Slots:
724
725 """Controller of the array of subprocess slots."""
726
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900727 def __init__(self, configs, options, progress):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900728 """Create a new slots controller.
729
730 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900731 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900732 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900733 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900734 """
735 self.options = options
736 self.slots = []
737 devnull = get_devnull()
738 make_cmd = get_make_cmd()
739 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900740 self.slots.append(Slot(configs, options, progress, devnull,
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900741 make_cmd))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900742
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900743 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900744 """Add a new subprocess if a vacant slot is found.
745
746 Arguments:
747 defconfig: defconfig name to be put into.
748
749 Returns:
750 Return True on success or False on failure
751 """
752 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900753 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900754 return True
755 return False
756
757 def available(self):
758 """Check if there is a vacant slot.
759
760 Returns:
761 Return True if at lease one vacant slot is found, False otherwise.
762 """
763 for slot in self.slots:
764 if slot.poll():
765 return True
766 return False
767
768 def empty(self):
769 """Check if all slots are vacant.
770
771 Returns:
772 Return True if all the slots are vacant, False otherwise.
773 """
774 ret = True
775 for slot in self.slots:
776 if not slot.poll():
777 ret = False
778 return ret
779
780 def show_failed_boards(self):
781 """Display all of the failed boards (defconfigs)."""
782 failed_boards = []
783
784 for slot in self.slots:
785 failed_boards += slot.get_failed_boards()
786
787 if len(failed_boards) > 0:
788 msg = [ "The following boards were not processed due to error:" ]
789 msg += failed_boards
790 for line in msg:
791 print >> sys.stderr, color_text(self.options.color,
792 COLOR_LIGHT_RED, line)
793
Joe Hershberger2559cd82015-05-19 13:21:22 -0500794 with open('moveconfig.failed', 'w') as f:
795 for board in failed_boards:
796 f.write(board + '\n')
797
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900798def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900799 """Move config options to defconfig files.
800
801 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900802 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900803 options: option flags
804 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900805 if len(configs) == 0:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900806 print 'Nothing to do. exit.'
807 sys.exit(0)
808
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900809 print 'Move %s (jobs: %d)' % (', '.join(configs), options.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900810
Joe Hershberger91040e82015-05-19 13:21:19 -0500811 if options.defconfigs:
812 defconfigs = [line.strip() for line in open(options.defconfigs)]
813 for i, defconfig in enumerate(defconfigs):
814 if not defconfig.endswith('_defconfig'):
815 defconfigs[i] = defconfig + '_defconfig'
816 if not os.path.exists(os.path.join('configs', defconfigs[i])):
817 sys.exit('%s - defconfig does not exist. Stopping.' %
818 defconfigs[i])
819 else:
820 # All the defconfig files to be processed
821 defconfigs = []
822 for (dirpath, dirnames, filenames) in os.walk('configs'):
823 dirpath = dirpath[len('configs') + 1:]
824 for filename in fnmatch.filter(filenames, '*_defconfig'):
825 defconfigs.append(os.path.join(dirpath, filename))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900826
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900827 progress = Progress(len(defconfigs))
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900828 slots = Slots(configs, options, progress)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900829
830 # Main loop to process defconfig files:
831 # Add a new subprocess into a vacant slot.
832 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900833 for defconfig in defconfigs:
834 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900835 while not slots.available():
836 # No available slot: sleep for a while
837 time.sleep(SLEEP_TIME)
838
839 # wait until all the subprocesses finish
840 while not slots.empty():
841 time.sleep(SLEEP_TIME)
842
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900843 progress.show()
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -0500844 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900845 slots.show_failed_boards()
846
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900847def bad_recipe(filename, linenum, msg):
848 """Print error message with the file name and the line number and exit."""
849 sys.exit("%s: line %d: error : " % (filename, linenum) + msg)
850
851def parse_recipe(filename):
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900852 """Parse the recipe file and retrieve CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900853
854 This function parses the given recipe file and gets the name,
855 the type, and the default value of the target config options.
856
857 Arguments:
858 filename: path to file to be parsed.
859 Returns:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900860 A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900861 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900862 configs = []
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900863 linenum = 1
864
865 for line in open(filename):
866 tokens = line.split()
867 if len(tokens) != 3:
868 bad_recipe(filename, linenum,
869 "%d fields in this line. Each line must contain 3 fields"
870 % len(tokens))
871
872 (config, type, default) = tokens
873
874 # prefix the option name with CONFIG_ if missing
875 if not config.startswith('CONFIG_'):
876 config = 'CONFIG_' + config
877
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900878 configs.append(config)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900879 linenum += 1
880
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900881 return configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900882
883def main():
884 try:
885 cpu_count = multiprocessing.cpu_count()
886 except NotImplementedError:
887 cpu_count = 1
888
889 parser = optparse.OptionParser()
890 # Add options here
891 parser.add_option('-c', '--color', action='store_true', default=False,
892 help='display the log in color')
Joe Hershberger91040e82015-05-19 13:21:19 -0500893 parser.add_option('-d', '--defconfigs', type='string',
894 help='a file containing a list of defconfigs to move')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900895 parser.add_option('-n', '--dry-run', action='store_true', default=False,
896 help='perform a trial run (show log with no changes)')
897 parser.add_option('-e', '--exit-on-error', action='store_true',
898 default=False,
899 help='exit immediately on any error')
Joe Hershberger2144f882015-05-19 13:21:20 -0500900 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
901 action='store_true', default=False,
902 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900903 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
904 help='the number of jobs to run simultaneously')
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500905 parser.add_option('-v', '--verbose', action='store_true', default=False,
906 help='show any build errors as boards are built')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900907 parser.usage += ' recipe_file\n\n' + \
908 'The recipe_file should describe config options you want to move.\n' + \
909 'Each line should contain config_name, type, default_value\n\n' + \
910 'Example:\n' + \
911 'CONFIG_FOO bool n\n' + \
912 'CONFIG_BAR int 100\n' + \
913 'CONFIG_BAZ string "hello"\n'
914
915 (options, args) = parser.parse_args()
916
917 if len(args) != 1:
918 parser.print_usage()
919 sys.exit(1)
920
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900921 configs = parse_recipe(args[0])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900922
Joe Hershberger2144f882015-05-19 13:21:20 -0500923 check_top_directory()
924
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900925 check_clean_directory()
926
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900927 update_cross_compile(options.color)
Masahiro Yamada4b430c92016-05-19 15:51:52 +0900928
Joe Hershberger2144f882015-05-19 13:21:20 -0500929 if not options.cleanup_headers_only:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900930 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -0500931
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900932 cleanup_headers(configs, options.dry_run)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900933
934if __name__ == '__main__':
935 main()