1#!/usr/bin/env python
2#
3# Copyright 2019 Espressif Systems (Shanghai) PTE LTD
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from __future__ import print_function, unicode_literals
18
19import argparse
20import os
21import sys
22from io import open
23
24from check_kconfigs import valid_directory
25from idf_ci_utils import get_submodule_dirs
26
27# FILES_TO_CHECK used as "startswith" pattern to match sdkconfig.defaults variants
28FILES_TO_CHECK = ('sdkconfig.ci', 'sdkconfig.defaults')
29
30# ignored directories (makes sense only when run on IDF_PATH)
31# Note: IGNORE_DIRS is a tuple in order to be able to use it directly with the startswith() built-in function which
32# accepts tuples but no lists.
33IGNORE_DIRS = (
34)
35
36
37def _parse_path(path, sep=None):
38    ret = set()
39    with open(path, 'r', encoding='utf-8') as f:
40        for line in f:
41            line = line.strip()
42            if not line.startswith('#') and len(line) > 0:
43                ret.add(line.split(sep)[0])
44    return ret
45
46
47def _valid_directory(path):
48    if not os.path.isdir(path):
49        raise argparse.ArgumentTypeError('{} is not a valid directory!'.format(path))
50    return path
51
52
53def main():
54    parser = argparse.ArgumentParser(description='Kconfig options checker')
55    parser.add_argument('files', nargs='*',
56                        help='Kconfig files')
57    parser.add_argument('--includes', '-d', nargs='*',
58                        help='Extra paths for recursively searching Kconfig files. (for example $IDF_PATH)',
59                        type=valid_directory)
60    parser.add_argument('--exclude-submodules', action='store_true',
61                        help='Exclude submodules')
62    args = parser.parse_args()
63
64    success_counter = 0
65    failure_counter = 0
66    ignore_counter = 0
67
68    deprecated_options = set()
69
70    ignore_dirs = IGNORE_DIRS
71    if args.exclude_submodules:
72        for submodule in get_submodule_dirs(full_path=True):
73            ignore_dirs = ignore_dirs + tuple(submodule)
74
75    files = [os.path.abspath(file_path) for file_path in args.files]
76
77    if args.includes:
78        for directory in args.includes:
79            for root, dirnames, filenames in os.walk(directory):
80                for filename in filenames:
81                    full_path = os.path.join(root, filename)
82                    if filename.startswith(FILES_TO_CHECK):
83                        files.append(full_path)
84                    elif filename == 'sdkconfig.rename':
85                        deprecated_options |= _parse_path(full_path)
86
87    for full_path in files:
88        if full_path.startswith(ignore_dirs):
89            print('{}: Ignored'.format(full_path))
90            ignore_counter += 1
91            continue
92        used_options = _parse_path(full_path, '=')
93        used_deprecated_options = deprecated_options & used_options
94        if len(used_deprecated_options) > 0:
95            print('{}: The following options are deprecated: {}'
96                  .format(full_path, ', '.join(used_deprecated_options)))
97            failure_counter += 1
98        else:
99            print('{}: OK'.format(full_path))
100            success_counter += 1
101
102    if ignore_counter > 0:
103        print('{} files have been ignored.'.format(ignore_counter))
104    if success_counter > 0:
105        print('{} files have been successfully checked.'.format(success_counter))
106    if failure_counter > 0:
107        print('{} files have errors. Please take a look at the log.'.format(failure_counter))
108        return 1
109
110    if not files:
111        print('WARNING: no files specified. Please specify files or use '
112              '"--includes" to search Kconfig files recursively')
113    return 0
114
115
116if __name__ == '__main__':
117    sys.exit(main())
118