]> git.ipfire.org Git - people/ms/u-boot.git/blame - tools/moveconfig.py
spl: fdt: support for fdt fixup for falcon boot
[people/ms/u-boot.git] / tools / moveconfig.py
CommitLineData
5a27c734
MY
1#!/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
b6ef393a 20First, you must edit the Kconfig to add the menu entries for the configs
96464bad
JH
21you are moving.
22
b6ef393a
MY
23And then run this tool giving CONFIG names you want to move.
24For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25simply type as follows:
5a27c734 26
b6ef393a 27 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
5a27c734 28
b6ef393a 29The tool walks through all the defconfig files and move the given CONFIGs.
5a27c734
MY
30
31The log is also displayed on the terminal.
32
1d085568 33The log is printed for each defconfig as follows:
5a27c734 34
1d085568
MY
35<defconfig_name>
36 <action1>
37 <action2>
38 <action3>
39 ...
5a27c734 40
1d085568
MY
41<defconfig_name> is the name of the defconfig.
42
43<action*> shows what the tool did for that defconfig.
c21fc7e2 44It looks like one of the following:
5a27c734
MY
45
46 - Move 'CONFIG_... '
47 This config option was moved to the defconfig
48
cc008299 49 - CONFIG_... is not defined in Kconfig. Do nothing.
916224c3
MY
50 The entry for this CONFIG was not found in Kconfig. The option is not
51 defined in the config header, either. So, this case can be just skipped.
52
53 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
54 This option is defined in the config header, but its entry was not found
55 in Kconfig.
cc008299
MY
56 There are two common cases:
57 - You forgot to create an entry for the CONFIG before running
58 this tool, or made a typo in a CONFIG passed to this tool.
59 - The entry was hidden due to unmet 'depends on'.
916224c3
MY
60 The tool does not know if the result is reasonable, so please check it
61 manually.
cc008299
MY
62
63 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
64 The define in the config header matched the one in Kconfig.
65 We do not need to touch it.
5a27c734 66
90ed6cba
MY
67 - Compiler is missing. Do nothing.
68 The compiler specified for this architecture was not found
69 in your PATH environment.
70 (If -e option is passed, the tool exits immediately.)
71
72 - Failed to process.
5a27c734
MY
73 An error occurred during processing this defconfig. Skipped.
74 (If -e option is passed, the tool exits immediately on error.)
75
76Finally, you will be asked, Clean up headers? [y/n]:
77
78If you say 'y' here, the unnecessary config defines are removed
79from the config headers (include/configs/*.h).
80It just uses the regex method, so you should not rely on it.
81Just in case, please do 'git diff' to see what happened.
82
83
b6ef393a
MY
84How does it work?
85-----------------
5a27c734
MY
86
87This tool runs configuration and builds include/autoconf.mk for every
88defconfig. The config options defined in Kconfig appear in the .config
89file (unless they are hidden because of unmet dependency.)
90On the other hand, the config options defined by board headers are seen
91in include/autoconf.mk. The tool looks for the specified options in both
b6ef393a
MY
92of them to decide the appropriate action for the options. If the given
93config option is found in the .config, but its value does not match the
94one from the board header, the config option in the .config is replaced
95with the define in the board header. Then, the .config is synced by
96"make savedefconfig" and the defconfig is updated with it.
5a27c734
MY
97
98For faster processing, this tool handles multi-threading. It creates
99separate build directories where the out-of-tree build is run. The
100temporary build directories are automatically created and deleted as
101needed. The number of threads are chosen based on the number of the CPU
102cores of your system although you can change it via -j (--jobs) option.
103
104
105Toolchains
106----------
107
108Appropriate toolchain are necessary to generate include/autoconf.mk
109for all the architectures supported by U-Boot. Most of them are available
110at the kernel.org site, some are not provided by kernel.org.
111
112The default per-arch CROSS_COMPILE used by this tool is specified by
113the list below, CROSS_COMPILE. You may wish to update the list to
114use your own. Instead of modifying the list directly, you can give
115them via environments.
116
117
118Available options
119-----------------
120
121 -c, --color
122 Surround each portion of the log with escape sequences to display it
123 in color on the terminal.
124
9ede2123
SG
125 -C, --commit
126 Create a git commit with the changes when the operation is complete. A
127 standard commit message is used which may need to be edited.
128
91040e85 129 -d, --defconfigs
0dbc9b59
MY
130 Specify a file containing a list of defconfigs to move. The defconfig
131 files can be given with shell-style wildcards.
91040e85 132
5a27c734 133 -n, --dry-run
b6ef393a 134 Perform a trial run that does not make any changes. It is useful to
5a27c734
MY
135 see what is going to happen before one actually runs it.
136
137 -e, --exit-on-error
138 Exit immediately if Make exits with a non-zero status while processing
139 a defconfig file.
140
8513dc04
MY
141 -s, --force-sync
142 Do "make savedefconfig" forcibly for all the defconfig files.
143 If not specified, "make savedefconfig" only occurs for cases
144 where at least one CONFIG was moved.
145
07913d1e
MY
146 -S, --spl
147 Look for moved config options in spl/include/autoconf.mk instead of
148 include/autoconf.mk. This is useful for moving options for SPL build
149 because SPL related options (mostly prefixed with CONFIG_SPL_) are
150 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
151
2144f880
JH
152 -H, --headers-only
153 Only cleanup the headers; skip the defconfig processing
154
5a27c734
MY
155 -j, --jobs
156 Specify the number of threads to run simultaneously. If not specified,
157 the number of threads is the same as the number of CPU cores.
158
6b96c1a1
JH
159 -r, --git-ref
160 Specify the git ref to clone for building the autoconf.mk. If unspecified
161 use the CWD. This is useful for when changes to the Kconfig affect the
162 default values and you want to capture the state of the defconfig from
163 before that change was in effect. If in doubt, specify a ref pre-Kconfig
164 changes (use HEAD if Kconfig changes are not committed). Worst case it will
165 take a bit longer to run, but will always do the right thing.
166
95bf9c7e
JH
167 -v, --verbose
168 Show any build errors as boards are built
169
6b403dfd
SG
170 -y, --yes
171 Instead of prompting, automatically go ahead with all operations. This
172 includes cleaning up headers and CONFIG_SYS_EXTRA_OPTIONS.
173
5a27c734
MY
174To see the complete list of supported options, run
175
176 $ tools/moveconfig.py -h
177
178"""
179
8ba1f5de 180import copy
f2f6981a 181import difflib
c8e1b10d 182import filecmp
5a27c734 183import fnmatch
0dbc9b59 184import glob
5a27c734
MY
185import multiprocessing
186import optparse
187import os
188import re
189import shutil
190import subprocess
191import sys
192import tempfile
193import time
194
195SHOW_GNU_MAKE = 'scripts/show-gnu-make'
196SLEEP_TIME=0.03
197
198# Here is the list of cross-tools I use.
199# Most of them are available at kernel.org
c21fc7e2 200# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
5a27c734 201# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
4440ecec 202# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
5a27c734
MY
203# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
204# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
205CROSS_COMPILE = {
206 'arc': 'arc-linux-',
207 'aarch64': 'aarch64-linux-',
208 'arm': 'arm-unknown-linux-gnueabi-',
209 'avr32': 'avr32-linux-',
5a27c734
MY
210 'm68k': 'm68k-linux-',
211 'microblaze': 'microblaze-linux-',
212 'mips': 'mips-linux-',
213 'nds32': 'nds32le-linux-',
214 'nios2': 'nios2-linux-gnu-',
5a27c734
MY
215 'powerpc': 'powerpc-linux-',
216 'sh': 'sh-linux-gnu-',
88e1346e
MY
217 'x86': 'i386-linux-',
218 'xtensa': 'xtensa-linux-'
5a27c734
MY
219}
220
221STATE_IDLE = 0
222STATE_DEFCONFIG = 1
223STATE_AUTOCONF = 2
96464bad 224STATE_SAVEDEFCONFIG = 3
5a27c734
MY
225
226ACTION_MOVE = 0
cc008299 227ACTION_NO_ENTRY = 1
916224c3
MY
228ACTION_NO_ENTRY_WARN = 2
229ACTION_NO_CHANGE = 3
5a27c734
MY
230
231COLOR_BLACK = '0;30'
232COLOR_RED = '0;31'
233COLOR_GREEN = '0;32'
234COLOR_BROWN = '0;33'
235COLOR_BLUE = '0;34'
236COLOR_PURPLE = '0;35'
237COLOR_CYAN = '0;36'
238COLOR_LIGHT_GRAY = '0;37'
239COLOR_DARK_GRAY = '1;30'
240COLOR_LIGHT_RED = '1;31'
241COLOR_LIGHT_GREEN = '1;32'
242COLOR_YELLOW = '1;33'
243COLOR_LIGHT_BLUE = '1;34'
244COLOR_LIGHT_PURPLE = '1;35'
245COLOR_LIGHT_CYAN = '1;36'
246COLOR_WHITE = '1;37'
247
248### helper functions ###
249def get_devnull():
250 """Get the file object of '/dev/null' device."""
251 try:
252 devnull = subprocess.DEVNULL # py3k
253 except AttributeError:
254 devnull = open(os.devnull, 'wb')
255 return devnull
256
257def check_top_directory():
258 """Exit if we are not at the top of source directory."""
259 for f in ('README', 'Licenses'):
260 if not os.path.exists(f):
261 sys.exit('Please run at the top of source directory.')
262
bd63e5ba
MY
263def check_clean_directory():
264 """Exit if the source tree is not clean."""
265 for f in ('.config', 'include/config'):
266 if os.path.exists(f):
267 sys.exit("source tree is not clean, please run 'make mrproper'")
268
5a27c734
MY
269def get_make_cmd():
270 """Get the command name of GNU Make.
271
272 U-Boot needs GNU Make for building, but the command name is not
273 necessarily "make". (for example, "gmake" on FreeBSD).
274 Returns the most appropriate command name on your system.
275 """
276 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
277 ret = process.communicate()
278 if process.returncode:
279 sys.exit('GNU Make not found')
280 return ret[0].rstrip()
281
0dbc9b59
MY
282def get_matched_defconfigs(defconfigs_file):
283 """Get all the defconfig files that match the patterns in a file."""
284 defconfigs = []
285 for i, line in enumerate(open(defconfigs_file)):
286 line = line.strip()
287 if not line:
288 continue # skip blank lines silently
289 pattern = os.path.join('configs', line)
290 matched = glob.glob(pattern) + glob.glob(pattern + '_defconfig')
291 if not matched:
292 print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
293 (defconfigs_file, i + 1, line)
294
295 defconfigs += matched
296
297 # use set() to drop multiple matching
298 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
299
684c306e
MY
300def get_all_defconfigs():
301 """Get all the defconfig files under the configs/ directory."""
302 defconfigs = []
303 for (dirpath, dirnames, filenames) in os.walk('configs'):
304 dirpath = dirpath[len('configs') + 1:]
305 for filename in fnmatch.filter(filenames, '*_defconfig'):
306 defconfigs.append(os.path.join(dirpath, filename))
307
308 return defconfigs
309
5a27c734
MY
310def color_text(color_enabled, color, string):
311 """Return colored string."""
312 if color_enabled:
1d085568
MY
313 # LF should not be surrounded by the escape sequence.
314 # Otherwise, additional whitespace or line-feed might be printed.
315 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
316 for s in string.split('\n') ])
5a27c734
MY
317 else:
318 return string
319
e9ea1221 320def show_diff(a, b, file_path, color_enabled):
f2f6981a
MY
321 """Show unidified diff.
322
323 Arguments:
324 a: A list of lines (before)
325 b: A list of lines (after)
326 file_path: Path to the file
e9ea1221 327 color_enabled: Display the diff in color
f2f6981a
MY
328 """
329
330 diff = difflib.unified_diff(a, b,
331 fromfile=os.path.join('a', file_path),
332 tofile=os.path.join('b', file_path))
333
334 for line in diff:
e9ea1221
MY
335 if line[0] == '-' and line[1] != '-':
336 print color_text(color_enabled, COLOR_RED, line),
337 elif line[0] == '+' and line[1] != '+':
338 print color_text(color_enabled, COLOR_GREEN, line),
339 else:
340 print line,
f2f6981a 341
90ed6cba 342def update_cross_compile(color_enabled):
1cc0a9f4 343 """Update per-arch CROSS_COMPILE via environment variables
5a27c734
MY
344
345 The default CROSS_COMPILE values are available
346 in the CROSS_COMPILE list above.
347
1cc0a9f4 348 You can override them via environment variables
5a27c734
MY
349 CROSS_COMPILE_{ARCH}.
350
351 For example, if you want to override toolchain prefixes
352 for ARM and PowerPC, you can do as follows in your shell:
353
354 export CROSS_COMPILE_ARM=...
355 export CROSS_COMPILE_POWERPC=...
90ed6cba
MY
356
357 Then, this function checks if specified compilers really exist in your
358 PATH environment.
5a27c734
MY
359 """
360 archs = []
361
362 for arch in os.listdir('arch'):
363 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
364 archs.append(arch)
365
366 # arm64 is a special case
367 archs.append('aarch64')
368
369 for arch in archs:
370 env = 'CROSS_COMPILE_' + arch.upper()
371 cross_compile = os.environ.get(env)
90ed6cba
MY
372 if not cross_compile:
373 cross_compile = CROSS_COMPILE.get(arch, '')
374
375 for path in os.environ["PATH"].split(os.pathsep):
376 gcc_path = os.path.join(path, cross_compile + 'gcc')
377 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
378 break
379 else:
380 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
381 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
382 % (cross_compile, arch))
383 cross_compile = None
384
385 CROSS_COMPILE[arch] = cross_compile
5a27c734 386
8ba1f5de
MY
387def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
388 extend_post):
389 """Extend matched lines if desired patterns are found before/after already
390 matched lines.
391
392 Arguments:
393 lines: A list of lines handled.
394 matched: A list of line numbers that have been already matched.
395 (will be updated by this function)
396 pre_patterns: A list of regular expression that should be matched as
397 preamble.
398 post_patterns: A list of regular expression that should be matched as
399 postamble.
400 extend_pre: Add the line number of matched preamble to the matched list.
401 extend_post: Add the line number of matched postamble to the matched list.
402 """
403 extended_matched = []
404
405 j = matched[0]
406
407 for i in matched:
408 if i == 0 or i < j:
409 continue
410 j = i
411 while j in matched:
412 j += 1
413 if j >= len(lines):
414 break
415
416 for p in pre_patterns:
417 if p.search(lines[i - 1]):
418 break
419 else:
420 # not matched
421 continue
422
423 for p in post_patterns:
424 if p.search(lines[j]):
425 break
426 else:
427 # not matched
428 continue
429
430 if extend_pre:
431 extended_matched.append(i - 1)
432 if extend_post:
433 extended_matched.append(j)
434
435 matched += extended_matched
436 matched.sort()
437
85edfc1f
CP
438def confirm(options, prompt):
439 if not options.yes:
440 while True:
441 choice = raw_input('{} [y/n]: '.format(prompt))
442 choice = choice.lower()
443 print choice
444 if choice == 'y' or choice == 'n':
445 break
446
447 if choice == 'n':
448 return False
449
450 return True
451
e9ea1221 452def cleanup_one_header(header_path, patterns, options):
5a27c734
MY
453 """Clean regex-matched lines away from a file.
454
455 Arguments:
456 header_path: path to the cleaned file.
457 patterns: list of regex patterns. Any lines matching to these
458 patterns are deleted.
e9ea1221 459 options: option flags.
5a27c734
MY
460 """
461 with open(header_path) as f:
462 lines = f.readlines()
463
464 matched = []
465 for i, line in enumerate(lines):
a3a779f7
MY
466 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
467 matched.append(i)
468 continue
5a27c734 469 for pattern in patterns:
8ba1f5de 470 if pattern.search(line):
5a27c734
MY
471 matched.append(i)
472 break
473
8ba1f5de
MY
474 if not matched:
475 return
476
477 # remove empty #ifdef ... #endif, successive blank lines
478 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
479 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
480 pattern_endif = re.compile(r'#\s*endif\W') # #endif
481 pattern_blank = re.compile(r'^\s*$') # empty line
482
483 while True:
484 old_matched = copy.copy(matched)
485 extend_matched_lines(lines, matched, [pattern_if],
486 [pattern_endif], True, True)
487 extend_matched_lines(lines, matched, [pattern_elif],
488 [pattern_elif, pattern_endif], True, False)
489 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
490 [pattern_blank], False, True)
491 extend_matched_lines(lines, matched, [pattern_blank],
492 [pattern_elif, pattern_endif], True, False)
493 extend_matched_lines(lines, matched, [pattern_blank],
494 [pattern_blank], True, False)
495 if matched == old_matched:
496 break
497
f2f6981a
MY
498 tolines = copy.copy(lines)
499
500 for i in reversed(matched):
501 tolines.pop(i)
502
e9ea1221 503 show_diff(lines, tolines, header_path, options.color)
8ba1f5de 504
e9ea1221 505 if options.dry_run:
5a27c734
MY
506 return
507
508 with open(header_path, 'w') as f:
f2f6981a
MY
509 for line in tolines:
510 f.write(line)
5a27c734 511
e9ea1221 512def cleanup_headers(configs, options):
5a27c734
MY
513 """Delete config defines from board headers.
514
515 Arguments:
b134bc13 516 configs: A list of CONFIGs to remove.
e9ea1221 517 options: option flags.
5a27c734 518 """
85edfc1f
CP
519 if not confirm(options, 'Clean up headers?'):
520 return
5a27c734
MY
521
522 patterns = []
b134bc13 523 for config in configs:
5a27c734
MY
524 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
525 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
526
60727f51
JH
527 for dir in 'include', 'arch', 'board':
528 for (dirpath, dirnames, filenames) in os.walk(dir):
dc6de50b
MY
529 if dirpath == os.path.join('include', 'generated'):
530 continue
60727f51
JH
531 for filename in filenames:
532 if not fnmatch.fnmatch(filename, '*~'):
533 cleanup_one_header(os.path.join(dirpath, filename),
e9ea1221 534 patterns, options)
5a27c734 535
9ab0296a
MY
536def cleanup_one_extra_option(defconfig_path, configs, options):
537 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
538
539 Arguments:
540 defconfig_path: path to the cleaned defconfig file.
541 configs: A list of CONFIGs to remove.
542 options: option flags.
543 """
544
545 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
546 end = '"\n'
547
548 with open(defconfig_path) as f:
549 lines = f.readlines()
550
551 for i, line in enumerate(lines):
552 if line.startswith(start) and line.endswith(end):
553 break
554 else:
555 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
556 return
557
558 old_tokens = line[len(start):-len(end)].split(',')
559 new_tokens = []
560
561 for token in old_tokens:
562 pos = token.find('=')
563 if not (token[:pos] if pos >= 0 else token) in configs:
564 new_tokens.append(token)
565
566 if new_tokens == old_tokens:
567 return
568
569 tolines = copy.copy(lines)
570
571 if new_tokens:
572 tolines[i] = start + ','.join(new_tokens) + end
573 else:
574 tolines.pop(i)
575
576 show_diff(lines, tolines, defconfig_path, options.color)
577
578 if options.dry_run:
579 return
580
581 with open(defconfig_path, 'w') as f:
582 for line in tolines:
583 f.write(line)
584
585def cleanup_extra_options(configs, options):
586 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
587
588 Arguments:
589 configs: A list of CONFIGs to remove.
590 options: option flags.
591 """
85edfc1f
CP
592 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
593 return
9ab0296a
MY
594
595 configs = [ config[len('CONFIG_'):] for config in configs ]
596
597 defconfigs = get_all_defconfigs()
598
599 for defconfig in defconfigs:
600 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
601 options)
602
ca43834d
CP
603def cleanup_whitelist(configs, options):
604 """Delete config whitelist entries
605
606 Arguments:
607 configs: A list of CONFIGs to remove.
608 options: option flags.
609 """
610 if not confirm(options, 'Clean up whitelist entries?'):
611 return
612
613 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
614 lines = f.readlines()
615
616 lines = [x for x in lines if x.strip() not in configs]
617
618 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
619 f.write(''.join(lines))
620
f90df596
CP
621def find_matching(patterns, line):
622 for pat in patterns:
623 if pat.search(line):
624 return True
625 return False
626
627def cleanup_readme(configs, options):
628 """Delete config description in README
629
630 Arguments:
631 configs: A list of CONFIGs to remove.
632 options: option flags.
633 """
634 if not confirm(options, 'Clean up README?'):
635 return
636
637 patterns = []
638 for config in configs:
639 patterns.append(re.compile(r'^\s+%s' % config))
640
641 with open('README') as f:
642 lines = f.readlines()
643
644 found = False
645 newlines = []
646 for line in lines:
647 if not found:
648 found = find_matching(patterns, line)
649 if found:
650 continue
651
652 if found and re.search(r'^\s+CONFIG', line):
653 found = False
654
655 if not found:
656 newlines.append(line)
657
658 with open('README', 'w') as f:
659 f.write(''.join(newlines))
660
ca43834d 661
5a27c734 662### classes ###
c5e60fd4
MY
663class Progress:
664
665 """Progress Indicator"""
666
667 def __init__(self, total):
668 """Create a new progress indicator.
669
670 Arguments:
671 total: A number of defconfig files to process.
672 """
673 self.current = 0
674 self.total = total
675
676 def inc(self):
677 """Increment the number of processed defconfig files."""
678
679 self.current += 1
680
681 def show(self):
682 """Display the progress."""
683 print ' %d defconfigs out of %d\r' % (self.current, self.total),
684 sys.stdout.flush()
685
5a27c734
MY
686class KconfigParser:
687
688 """A parser of .config and include/autoconf.mk."""
689
690 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
691 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
692
522e8dcb 693 def __init__(self, configs, options, build_dir):
5a27c734
MY
694 """Create a new parser.
695
696 Arguments:
b134bc13 697 configs: A list of CONFIGs to move.
5a27c734
MY
698 options: option flags.
699 build_dir: Build directory.
700 """
b134bc13 701 self.configs = configs
5a27c734 702 self.options = options
1f16992e
MY
703 self.dotconfig = os.path.join(build_dir, '.config')
704 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
07913d1e
MY
705 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
706 'autoconf.mk')
1f16992e
MY
707 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
708 'auto.conf')
5da4f857 709 self.defconfig = os.path.join(build_dir, 'defconfig')
5a27c734
MY
710
711 def get_cross_compile(self):
712 """Parse .config file and return CROSS_COMPILE.
713
714 Returns:
715 A string storing the compiler prefix for the architecture.
90ed6cba
MY
716 Return a NULL string for architectures that do not require
717 compiler prefix (Sandbox and native build is the case).
718 Return None if the specified compiler is missing in your PATH.
719 Caller should distinguish '' and None.
5a27c734
MY
720 """
721 arch = ''
722 cpu = ''
1f16992e 723 for line in open(self.dotconfig):
5a27c734
MY
724 m = self.re_arch.match(line)
725 if m:
726 arch = m.group(1)
727 continue
728 m = self.re_cpu.match(line)
729 if m:
730 cpu = m.group(1)
731
90ed6cba
MY
732 if not arch:
733 return None
5a27c734
MY
734
735 # fix-up for aarch64
736 if arch == 'arm' and cpu == 'armv8':
737 arch = 'aarch64'
738
90ed6cba 739 return CROSS_COMPILE.get(arch, None)
5a27c734 740
b134bc13 741 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
5a27c734
MY
742 """Parse .config, defconfig, include/autoconf.mk for one config.
743
744 This function looks for the config options in the lines from
745 defconfig, .config, and include/autoconf.mk in order to decide
746 which action should be taken for this defconfig.
747
748 Arguments:
b134bc13 749 config: CONFIG name to parse.
cc008299 750 dotconfig_lines: lines from the .config file.
5a27c734
MY
751 autoconf_lines: lines from the include/autoconf.mk file.
752
753 Returns:
754 A tupple of the action for this defconfig and the line
755 matched for the config.
756 """
5a27c734
MY
757 not_set = '# %s is not set' % config
758
5a27c734
MY
759 for line in autoconf_lines:
760 line = line.rstrip()
761 if line.startswith(config + '='):
cc008299 762 new_val = line
5a27c734 763 break
5a27c734 764 else:
cc008299
MY
765 new_val = not_set
766
916224c3
MY
767 for line in dotconfig_lines:
768 line = line.rstrip()
769 if line.startswith(config + '=') or line == not_set:
770 old_val = line
771 break
772 else:
773 if new_val == not_set:
774 return (ACTION_NO_ENTRY, config)
775 else:
776 return (ACTION_NO_ENTRY_WARN, config)
777
cc008299
MY
778 # If this CONFIG is neither bool nor trisate
779 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
780 # tools/scripts/define2mk.sed changes '1' to 'y'.
781 # This is a problem if the CONFIG is int type.
782 # Check the type in Kconfig and handle it correctly.
783 if new_val[-2:] == '=y':
784 new_val = new_val[:-1] + '1'
5a27c734 785
5030159e
MY
786 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
787 new_val)
5a27c734 788
1d085568 789 def update_dotconfig(self):
6ff36d21 790 """Parse files for the config options and update the .config.
5a27c734 791
cc008299
MY
792 This function parses the generated .config and include/autoconf.mk
793 searching the target options.
6ff36d21 794 Move the config option(s) to the .config as needed.
5a27c734
MY
795
796 Arguments:
797 defconfig: defconfig name.
522e8dcb
MY
798
799 Returns:
7fb0bacd
MY
800 Return a tuple of (updated flag, log string).
801 The "updated flag" is True if the .config was updated, False
802 otherwise. The "log string" shows what happend to the .config.
5a27c734
MY
803 """
804
5a27c734 805 results = []
7fb0bacd 806 updated = False
916224c3 807 suspicious = False
07913d1e
MY
808 rm_files = [self.config_autoconf, self.autoconf]
809
810 if self.options.spl:
811 if os.path.exists(self.spl_autoconf):
812 autoconf_path = self.spl_autoconf
813 rm_files.append(self.spl_autoconf)
814 else:
815 for f in rm_files:
816 os.remove(f)
817 return (updated, suspicious,
818 color_text(self.options.color, COLOR_BROWN,
819 "SPL is not enabled. Skipped.") + '\n')
820 else:
821 autoconf_path = self.autoconf
5a27c734 822
1f16992e 823 with open(self.dotconfig) as f:
cc008299 824 dotconfig_lines = f.readlines()
5a27c734 825
07913d1e 826 with open(autoconf_path) as f:
5a27c734
MY
827 autoconf_lines = f.readlines()
828
b134bc13
MY
829 for config in self.configs:
830 result = self.parse_one_config(config, dotconfig_lines,
96464bad 831 autoconf_lines)
5a27c734
MY
832 results.append(result)
833
834 log = ''
835
836 for (action, value) in results:
837 if action == ACTION_MOVE:
838 actlog = "Move '%s'" % value
839 log_color = COLOR_LIGHT_GREEN
cc008299
MY
840 elif action == ACTION_NO_ENTRY:
841 actlog = "%s is not defined in Kconfig. Do nothing." % value
5a27c734 842 log_color = COLOR_LIGHT_BLUE
916224c3
MY
843 elif action == ACTION_NO_ENTRY_WARN:
844 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
845 log_color = COLOR_YELLOW
846 suspicious = True
cc008299
MY
847 elif action == ACTION_NO_CHANGE:
848 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
849 % value
5a27c734 850 log_color = COLOR_LIGHT_PURPLE
07913d1e
MY
851 elif action == ACTION_SPL_NOT_EXIST:
852 actlog = "SPL is not enabled for this defconfig. Skip."
853 log_color = COLOR_PURPLE
5a27c734
MY
854 else:
855 sys.exit("Internal Error. This should not happen.")
856
1d085568 857 log += color_text(self.options.color, log_color, actlog) + '\n'
5a27c734 858
1f16992e 859 with open(self.dotconfig, 'a') as f:
e423d17f
MY
860 for (action, value) in results:
861 if action == ACTION_MOVE:
862 f.write(value + '\n')
7fb0bacd 863 updated = True
5a27c734 864
5da4f857 865 self.results = results
07913d1e
MY
866 for f in rm_files:
867 os.remove(f)
5a27c734 868
916224c3 869 return (updated, suspicious, log)
522e8dcb 870
5da4f857
MY
871 def check_defconfig(self):
872 """Check the defconfig after savedefconfig
873
874 Returns:
875 Return additional log if moved CONFIGs were removed again by
876 'make savedefconfig'.
877 """
878
879 log = ''
880
881 with open(self.defconfig) as f:
882 defconfig_lines = f.readlines()
883
884 for (action, value) in self.results:
885 if action != ACTION_MOVE:
886 continue
887 if not value + '\n' in defconfig_lines:
888 log += color_text(self.options.color, COLOR_YELLOW,
889 "'%s' was removed by savedefconfig.\n" %
890 value)
891
892 return log
893
5a27c734
MY
894class Slot:
895
896 """A slot to store a subprocess.
897
898 Each instance of this class handles one subprocess.
899 This class is useful to control multiple threads
900 for faster processing.
901 """
902
6b96c1a1 903 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
5a27c734
MY
904 """Create a new process slot.
905
906 Arguments:
b134bc13 907 configs: A list of CONFIGs to move.
5a27c734 908 options: option flags.
c5e60fd4 909 progress: A progress indicator.
5a27c734
MY
910 devnull: A file object of '/dev/null'.
911 make_cmd: command name of GNU Make.
6b96c1a1
JH
912 reference_src_dir: Determine the true starting config state from this
913 source tree.
5a27c734
MY
914 """
915 self.options = options
c5e60fd4 916 self.progress = progress
5a27c734
MY
917 self.build_dir = tempfile.mkdtemp()
918 self.devnull = devnull
919 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
6b96c1a1 920 self.reference_src_dir = reference_src_dir
522e8dcb 921 self.parser = KconfigParser(configs, options, self.build_dir)
5a27c734 922 self.state = STATE_IDLE
09c6c066
MY
923 self.failed_boards = set()
924 self.suspicious_boards = set()
5a27c734
MY
925
926 def __del__(self):
927 """Delete the working directory
928
929 This function makes sure the temporary directory is cleaned away
930 even if Python suddenly dies due to error. It should be done in here
f2dae751 931 because it is guaranteed the destructor is always invoked when the
5a27c734
MY
932 instance of the class gets unreferenced.
933
934 If the subprocess is still running, wait until it finishes.
935 """
936 if self.state != STATE_IDLE:
937 while self.ps.poll() == None:
938 pass
939 shutil.rmtree(self.build_dir)
940
c5e60fd4 941 def add(self, defconfig):
5a27c734
MY
942 """Assign a new subprocess for defconfig and add it to the slot.
943
944 If the slot is vacant, create a new subprocess for processing the
945 given defconfig and add it to the slot. Just returns False if
946 the slot is occupied (i.e. the current subprocess is still running).
947
948 Arguments:
949 defconfig: defconfig name.
950
951 Returns:
952 Return True on success or False on failure
953 """
954 if self.state != STATE_IDLE:
955 return False
e307fa9d 956
5a27c734 957 self.defconfig = defconfig
1d085568 958 self.log = ''
f432c33f 959 self.current_src_dir = self.reference_src_dir
e307fa9d 960 self.do_defconfig()
5a27c734
MY
961 return True
962
963 def poll(self):
964 """Check the status of the subprocess and handle it as needed.
965
966 Returns True if the slot is vacant (i.e. in idle state).
967 If the configuration is successfully finished, assign a new
968 subprocess to build include/autoconf.mk.
969 If include/autoconf.mk is generated, invoke the parser to
7fb0bacd
MY
970 parse the .config and the include/autoconf.mk, moving
971 config options to the .config as needed.
972 If the .config was updated, run "make savedefconfig" to sync
973 it, update the original defconfig, and then set the slot back
974 to the idle state.
5a27c734
MY
975
976 Returns:
977 Return True if the subprocess is terminated, False otherwise
978 """
979 if self.state == STATE_IDLE:
980 return True
981
982 if self.ps.poll() == None:
983 return False
984
985 if self.ps.poll() != 0:
e307fa9d
MY
986 self.handle_error()
987 elif self.state == STATE_DEFCONFIG:
f432c33f 988 if self.reference_src_dir and not self.current_src_dir:
6b96c1a1
JH
989 self.do_savedefconfig()
990 else:
991 self.do_autoconf()
e307fa9d 992 elif self.state == STATE_AUTOCONF:
f432c33f
MY
993 if self.current_src_dir:
994 self.current_src_dir = None
6b96c1a1
JH
995 self.do_defconfig()
996 else:
997 self.do_savedefconfig()
e307fa9d
MY
998 elif self.state == STATE_SAVEDEFCONFIG:
999 self.update_defconfig()
1000 else:
1001 sys.exit("Internal Error. This should not happen.")
5a27c734 1002
e307fa9d 1003 return True if self.state == STATE_IDLE else False
96464bad 1004
e307fa9d
MY
1005 def handle_error(self):
1006 """Handle error cases."""
8513dc04 1007
e307fa9d
MY
1008 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1009 "Failed to process.\n")
1010 if self.options.verbose:
1011 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
1012 self.ps.stderr.read())
1013 self.finish(False)
96464bad 1014
e307fa9d
MY
1015 def do_defconfig(self):
1016 """Run 'make <board>_defconfig' to create the .config file."""
c8e1b10d 1017
e307fa9d
MY
1018 cmd = list(self.make_cmd)
1019 cmd.append(self.defconfig)
1020 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
f432c33f
MY
1021 stderr=subprocess.PIPE,
1022 cwd=self.current_src_dir)
e307fa9d 1023 self.state = STATE_DEFCONFIG
c8e1b10d 1024
e307fa9d
MY
1025 def do_autoconf(self):
1026 """Run 'make include/config/auto.conf'."""
5a27c734 1027
25400090 1028 self.cross_compile = self.parser.get_cross_compile()
90ed6cba 1029 if self.cross_compile is None:
1d085568
MY
1030 self.log += color_text(self.options.color, COLOR_YELLOW,
1031 "Compiler is missing. Do nothing.\n")
4efef998 1032 self.finish(False)
e307fa9d 1033 return
90ed6cba 1034
5a27c734 1035 cmd = list(self.make_cmd)
25400090
JH
1036 if self.cross_compile:
1037 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
7740f653 1038 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
5a27c734 1039 cmd.append('include/config/auto.conf')
25400090 1040 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
f432c33f
MY
1041 stderr=subprocess.PIPE,
1042 cwd=self.current_src_dir)
5a27c734 1043 self.state = STATE_AUTOCONF
e307fa9d
MY
1044
1045 def do_savedefconfig(self):
1046 """Update the .config and run 'make savedefconfig'."""
1047
916224c3
MY
1048 (updated, suspicious, log) = self.parser.update_dotconfig()
1049 if suspicious:
1050 self.suspicious_boards.add(self.defconfig)
e307fa9d
MY
1051 self.log += log
1052
1053 if not self.options.force_sync and not updated:
1054 self.finish(True)
1055 return
1056 if updated:
1057 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1058 "Syncing by savedefconfig...\n")
1059 else:
1060 self.log += "Syncing by savedefconfig (forced by option)...\n"
1061
1062 cmd = list(self.make_cmd)
1063 cmd.append('savedefconfig')
1064 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1065 stderr=subprocess.PIPE)
1066 self.state = STATE_SAVEDEFCONFIG
1067
1068 def update_defconfig(self):
1069 """Update the input defconfig and go back to the idle state."""
1070
fc2661ee
MY
1071 log = self.parser.check_defconfig()
1072 if log:
09c6c066 1073 self.suspicious_boards.add(self.defconfig)
fc2661ee 1074 self.log += log
e307fa9d
MY
1075 orig_defconfig = os.path.join('configs', self.defconfig)
1076 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1077 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1078
1079 if updated:
06cc1d36 1080 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
e307fa9d
MY
1081 "defconfig was updated.\n")
1082
1083 if not self.options.dry_run and updated:
1084 shutil.move(new_defconfig, orig_defconfig)
1085 self.finish(True)
5a27c734 1086
4efef998
MY
1087 def finish(self, success):
1088 """Display log along with progress and go to the idle state.
1d085568
MY
1089
1090 Arguments:
4efef998
MY
1091 success: Should be True when the defconfig was processed
1092 successfully, or False when it fails.
1d085568
MY
1093 """
1094 # output at least 30 characters to hide the "* defconfigs out of *".
1095 log = self.defconfig.ljust(30) + '\n'
1096
1097 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1098 # Some threads are running in parallel.
1099 # Print log atomically to not mix up logs from different threads.
4efef998
MY
1100 print >> (sys.stdout if success else sys.stderr), log
1101
1102 if not success:
1103 if self.options.exit_on_error:
1104 sys.exit("Exit on error.")
1105 # If --exit-on-error flag is not set, skip this board and continue.
1106 # Record the failed board.
09c6c066 1107 self.failed_boards.add(self.defconfig)
4efef998 1108
1d085568
MY
1109 self.progress.inc()
1110 self.progress.show()
4efef998 1111 self.state = STATE_IDLE
1d085568 1112
5a27c734 1113 def get_failed_boards(self):
09c6c066 1114 """Returns a set of failed boards (defconfigs) in this slot.
5a27c734
MY
1115 """
1116 return self.failed_boards
1117
fc2661ee 1118 def get_suspicious_boards(self):
09c6c066 1119 """Returns a set of boards (defconfigs) with possible misconversion.
fc2661ee 1120 """
916224c3 1121 return self.suspicious_boards - self.failed_boards
fc2661ee 1122
5a27c734
MY
1123class Slots:
1124
1125 """Controller of the array of subprocess slots."""
1126
6b96c1a1 1127 def __init__(self, configs, options, progress, reference_src_dir):
5a27c734
MY
1128 """Create a new slots controller.
1129
1130 Arguments:
b134bc13 1131 configs: A list of CONFIGs to move.
5a27c734 1132 options: option flags.
c5e60fd4 1133 progress: A progress indicator.
6b96c1a1
JH
1134 reference_src_dir: Determine the true starting config state from this
1135 source tree.
5a27c734
MY
1136 """
1137 self.options = options
1138 self.slots = []
1139 devnull = get_devnull()
1140 make_cmd = get_make_cmd()
1141 for i in range(options.jobs):
b134bc13 1142 self.slots.append(Slot(configs, options, progress, devnull,
6b96c1a1 1143 make_cmd, reference_src_dir))
5a27c734 1144
c5e60fd4 1145 def add(self, defconfig):
5a27c734
MY
1146 """Add a new subprocess if a vacant slot is found.
1147
1148 Arguments:
1149 defconfig: defconfig name to be put into.
1150
1151 Returns:
1152 Return True on success or False on failure
1153 """
1154 for slot in self.slots:
c5e60fd4 1155 if slot.add(defconfig):
5a27c734
MY
1156 return True
1157 return False
1158
1159 def available(self):
1160 """Check if there is a vacant slot.
1161
1162 Returns:
1163 Return True if at lease one vacant slot is found, False otherwise.
1164 """
1165 for slot in self.slots:
1166 if slot.poll():
1167 return True
1168 return False
1169
1170 def empty(self):
1171 """Check if all slots are vacant.
1172
1173 Returns:
1174 Return True if all the slots are vacant, False otherwise.
1175 """
1176 ret = True
1177 for slot in self.slots:
1178 if not slot.poll():
1179 ret = False
1180 return ret
1181
1182 def show_failed_boards(self):
1183 """Display all of the failed boards (defconfigs)."""
09c6c066 1184 boards = set()
96dccd97 1185 output_file = 'moveconfig.failed'
5a27c734
MY
1186
1187 for slot in self.slots:
09c6c066 1188 boards |= slot.get_failed_boards()
96dccd97
MY
1189
1190 if boards:
1191 boards = '\n'.join(boards) + '\n'
1192 msg = "The following boards were not processed due to error:\n"
1193 msg += boards
1194 msg += "(the list has been saved in %s)\n" % output_file
1195 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1196 msg)
1197
1198 with open(output_file, 'w') as f:
1199 f.write(boards)
2559cd89 1200
fc2661ee
MY
1201 def show_suspicious_boards(self):
1202 """Display all boards (defconfigs) with possible misconversion."""
09c6c066 1203 boards = set()
fc2661ee
MY
1204 output_file = 'moveconfig.suspicious'
1205
1206 for slot in self.slots:
09c6c066 1207 boards |= slot.get_suspicious_boards()
fc2661ee
MY
1208
1209 if boards:
1210 boards = '\n'.join(boards) + '\n'
1211 msg = "The following boards might have been converted incorrectly.\n"
1212 msg += "It is highly recommended to check them manually:\n"
1213 msg += boards
1214 msg += "(the list has been saved in %s)\n" % output_file
1215 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1216 msg)
1217
1218 with open(output_file, 'w') as f:
1219 f.write(boards)
1220
5cc42a51
MY
1221class ReferenceSource:
1222
1223 """Reference source against which original configs should be parsed."""
1224
1225 def __init__(self, commit):
1226 """Create a reference source directory based on a specified commit.
1227
1228 Arguments:
1229 commit: commit to git-clone
1230 """
1231 self.src_dir = tempfile.mkdtemp()
1232 print "Cloning git repo to a separate work directory..."
1233 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1234 cwd=self.src_dir)
1235 print "Checkout '%s' to build the original autoconf.mk." % \
1236 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1237 subprocess.check_output(['git', 'checkout', commit],
1238 stderr=subprocess.STDOUT, cwd=self.src_dir)
6b96c1a1
JH
1239
1240 def __del__(self):
5cc42a51 1241 """Delete the reference source directory
6b96c1a1
JH
1242
1243 This function makes sure the temporary directory is cleaned away
1244 even if Python suddenly dies due to error. It should be done in here
1245 because it is guaranteed the destructor is always invoked when the
1246 instance of the class gets unreferenced.
1247 """
5cc42a51
MY
1248 shutil.rmtree(self.src_dir)
1249
1250 def get_dir(self):
1251 """Return the absolute path to the reference source directory."""
6b96c1a1 1252
5cc42a51 1253 return self.src_dir
6b96c1a1 1254
b134bc13 1255def move_config(configs, options):
5a27c734
MY
1256 """Move config options to defconfig files.
1257
1258 Arguments:
b134bc13 1259 configs: A list of CONFIGs to move.
5a27c734
MY
1260 options: option flags
1261 """
b134bc13 1262 if len(configs) == 0:
6a9f79f7
MY
1263 if options.force_sync:
1264 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1265 else:
1266 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1267 else:
1268 print 'Move ' + ', '.join(configs),
1269 print '(jobs: %d)\n' % options.jobs
5a27c734 1270
6b96c1a1 1271 if options.git_ref:
5cc42a51
MY
1272 reference_src = ReferenceSource(options.git_ref)
1273 reference_src_dir = reference_src.get_dir()
1274 else:
f432c33f 1275 reference_src_dir = None
6b96c1a1 1276
91040e85 1277 if options.defconfigs:
0dbc9b59 1278 defconfigs = get_matched_defconfigs(options.defconfigs)
91040e85 1279 else:
684c306e 1280 defconfigs = get_all_defconfigs()
5a27c734 1281
c5e60fd4 1282 progress = Progress(len(defconfigs))
6b96c1a1 1283 slots = Slots(configs, options, progress, reference_src_dir)
5a27c734
MY
1284
1285 # Main loop to process defconfig files:
1286 # Add a new subprocess into a vacant slot.
1287 # Sleep if there is no available slot.
c5e60fd4
MY
1288 for defconfig in defconfigs:
1289 while not slots.add(defconfig):
5a27c734
MY
1290 while not slots.available():
1291 # No available slot: sleep for a while
1292 time.sleep(SLEEP_TIME)
1293
1294 # wait until all the subprocesses finish
1295 while not slots.empty():
1296 time.sleep(SLEEP_TIME)
1297
2e2ce6c0 1298 print ''
5a27c734 1299 slots.show_failed_boards()
fc2661ee 1300 slots.show_suspicious_boards()
5a27c734 1301
5a27c734
MY
1302def main():
1303 try:
1304 cpu_count = multiprocessing.cpu_count()
1305 except NotImplementedError:
1306 cpu_count = 1
1307
1308 parser = optparse.OptionParser()
1309 # Add options here
1310 parser.add_option('-c', '--color', action='store_true', default=False,
1311 help='display the log in color')
9ede2123
SG
1312 parser.add_option('-C', '--commit', action='store_true', default=False,
1313 help='Create a git commit for the operation')
91040e85
JH
1314 parser.add_option('-d', '--defconfigs', type='string',
1315 help='a file containing a list of defconfigs to move')
5a27c734
MY
1316 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1317 help='perform a trial run (show log with no changes)')
1318 parser.add_option('-e', '--exit-on-error', action='store_true',
1319 default=False,
1320 help='exit immediately on any error')
8513dc04
MY
1321 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1322 help='force sync by savedefconfig')
07913d1e
MY
1323 parser.add_option('-S', '--spl', action='store_true', default=False,
1324 help='parse config options defined for SPL build')
2144f880
JH
1325 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1326 action='store_true', default=False,
1327 help='only cleanup the headers')
5a27c734
MY
1328 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1329 help='the number of jobs to run simultaneously')
6b96c1a1
JH
1330 parser.add_option('-r', '--git-ref', type='string',
1331 help='the git ref to clone for building the autoconf.mk')
6b403dfd
SG
1332 parser.add_option('-y', '--yes', action='store_true', default=False,
1333 help="respond 'yes' to any prompts")
95bf9c7e
JH
1334 parser.add_option('-v', '--verbose', action='store_true', default=False,
1335 help='show any build errors as boards are built')
b6ef393a 1336 parser.usage += ' CONFIG ...'
5a27c734 1337
b6ef393a 1338 (options, configs) = parser.parse_args()
5a27c734 1339
6a9f79f7 1340 if len(configs) == 0 and not options.force_sync:
5a27c734
MY
1341 parser.print_usage()
1342 sys.exit(1)
1343
b6ef393a
MY
1344 # prefix the option name with CONFIG_ if missing
1345 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1346 for config in configs ]
5a27c734 1347
2144f880
JH
1348 check_top_directory()
1349
1350 if not options.cleanup_headers_only:
f7536f79
MY
1351 check_clean_directory()
1352 update_cross_compile(options.color)
b134bc13 1353 move_config(configs, options)
2144f880 1354
6a9f79f7 1355 if configs:
e9ea1221 1356 cleanup_headers(configs, options)
9ab0296a 1357 cleanup_extra_options(configs, options)
ca43834d 1358 cleanup_whitelist(configs, options)
f90df596 1359 cleanup_readme(configs, options)
5a27c734 1360
9ede2123
SG
1361 if options.commit:
1362 subprocess.call(['git', 'add', '-u'])
1363 if configs:
1364 msg = 'Convert %s %sto Kconfig' % (configs[0],
1365 'et al ' if len(configs) > 1 else '')
1366 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1367 '\n '.join(configs))
1368 else:
1369 msg = 'configs: Resync with savedefconfig'
1370 msg += '\n\nRsync all defconfig files using moveconfig.py'
1371 subprocess.call(['git', 'commit', '-s', '-m', msg])
1372
5a27c734
MY
1373if __name__ == '__main__':
1374 main()