1#!/usr/bin/env python3
2#
3#  Copyright (c) 2021, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30# This script updates different make/build files (CMakeLists.txt, BUILD.gn,
31# Andriod.mk, Andriod.bp, auto-make) in OpenThread repo based on the
32# current files present in `./src/core/` & `./include/openthread/`
33# folders. This script MUST be called from openthread root folder.
34
35import os
36
37#----------------------------------------------------------------------------------------------
38# Helper functions
39
40
41def get_file_list(path, extension):
42    """Get a sorted list of full file names (with path) in a given `path` folder having a given `extension`"""
43    return sorted([
44        "{}/{}".format(dir_path, file_name)[2:]
45        for dir_path, dir_names, file_names in os.walk(path)
46        for file_name in file_names
47        if file_name.endswith(extension)
48    ])
49
50
51def read_txt_file(file_name):
52    """Read the content of a text file with name `file_name` and return content as a list of lines"""
53    with open(file_name, 'r') as file:
54        lines = file.readlines()
55    return lines
56
57
58def write_txt_file(file_name, lines):
59    """Write a text file with name `file_name` with the content given as a list of `lines`"""
60    with open(file_name, 'w') as file:
61        file.writelines(lines)
62
63
64def update_build_file(file_name, start_string, end_string, new_list, search_string=None):
65    """
66    Update the file given by `file_name` by replacing the list after the first occurrence of `start_string` up to
67    `end_string` with the `new_list`. If `search_string` is given, then the search for `start_string` only starts
68    after seeing the `search_string` line in the file.
69    """
70    STATE_SEARCH = 1
71    STATE_MATCH_START = 2
72    STATE_MATCH_END = 3
73    STATE_DONE = 4
74
75    lines = read_txt_file(file_name)
76    new_lines = []
77    state = STATE_SEARCH if search_string else STATE_MATCH_START
78
79    for line in lines:
80        if state == STATE_SEARCH:
81            new_lines.append(line)
82            if line.startswith(search_string):
83                state = STATE_MATCH_START
84        elif state == STATE_MATCH_START:
85            new_lines.append(line)
86            if line.startswith(start_string):
87                new_lines.extend(new_list)
88                state = STATE_MATCH_END
89        elif state == STATE_MATCH_END:
90            if line.startswith(end_string):
91                new_lines.append(line)
92                state = STATE_DONE
93        else:
94            new_lines.append(line)
95
96    if state != STATE_DONE:
97        raise RuntimeError('failed to update build file: {}'.format(file_name))
98
99    if new_lines != lines:
100        write_txt_file(file_name, new_lines)
101
102
103#----------------------------------------------------------------------------------------------
104
105# Get the list of hpp/h/cpp files in different folders.
106
107src_core_path = "./src/core"
108core_h_files = get_file_list(src_core_path, '.h')
109core_hpp_files = get_file_list(src_core_path, '.hpp')
110core_cpp_files = get_file_list(src_core_path, '.cpp')
111core_cpp_files = [item for item in core_cpp_files if not item.endswith("extension_example.cpp")]
112core_hpp_cpp_files = sorted(core_hpp_files + core_cpp_files)
113core_h_hpp_files = sorted(core_h_files + core_hpp_files)
114
115include_path = "./include/openthread"
116include_h_files = get_file_list(include_path, '.h')
117include_ot_h_files = [name[8:] for name in include_h_files if not name.startswith("include/openthread/platform")]
118include_platform_h_files = [name[8:] for name in include_h_files if name.startswith("include/openthread/platform")]
119
120#----------------------------------------------------------------------------------------------
121# Update CMakeLists.txt files
122
123core_cmakelist_txt_file = "./src/core/CMakeLists.txt"
124
125formatted_list = ["    {}\n".format(file_name[9:]) for file_name in core_cpp_files]
126update_build_file(core_cmakelist_txt_file, "set(COMMON_SOURCES\n", ")\n", formatted_list)
127
128print("Updated " + core_cmakelist_txt_file)
129
130#----------------------------------------------------------------------------------------------
131# Update Build.gn files
132
133core_build_gn_file = "./src/core/BUILD.gn"
134
135formatted_list = ["  \"{}\",\n".format(file_name[9:]) for file_name in core_hpp_cpp_files]
136update_build_file(core_build_gn_file, "openthread_core_files = [\n", "]\n", formatted_list)
137
138formatted_list = ["    \"{}\",\n".format(file_name[9:]) for file_name in core_h_files]
139update_build_file(core_build_gn_file, "  public = [\n", "  ]\n", formatted_list)
140
141print("Updated " + core_build_gn_file)
142
143include_build_gn_file = "./include/openthread/BUILD.gn"
144
145formatted_list = ["    \"{}\",\n".format(file_name[19:]) for file_name in include_h_files]
146update_build_file(include_build_gn_file, "  public = [\n", "  ]\n", formatted_list)
147
148print("Updated " + include_build_gn_file)
149
150#----------------------------------------------------------------------------------------------
151# Update Makefile.am files
152
153core_makefile_am_file = "./src/core/Makefile.am"
154
155formatted_list = ["    {:<45} \\\n".format(file_name[9:]) for file_name in core_cpp_files]
156start_string = "SOURCES_COMMON                                  = \\\n"
157end_string = "    $(NULL)\n"
158update_build_file(core_makefile_am_file, start_string, end_string, formatted_list)
159
160formatted_list = ["    {:<45} \\\n".format(file_name[9:]) for file_name in core_h_hpp_files]
161start_string = "HEADERS_COMMON                                  = \\\n"
162end_string = "    $(NULL)\n"
163update_build_file(core_makefile_am_file, start_string, end_string, formatted_list)
164
165print("Updated " + core_makefile_am_file)
166
167include_makefile_am_file = "./include/Makefile.am"
168
169formatted_list = ["    {:<37} \\\n".format(file_name) for file_name in include_ot_h_files]
170start_string = "openthread_headers                      = \\\n"
171end_string = "    $(NULL)\n"
172update_build_file(include_makefile_am_file, start_string, end_string, formatted_list)
173
174formatted_list = ["    {:<37} \\\n".format(file_name) for file_name in include_platform_h_files]
175start_string = "ot_platform_headers                     = \\\n"
176end_string = "    $(NULL)\n"
177update_build_file(include_makefile_am_file, start_string, end_string, formatted_list)
178
179print("Updated " + include_makefile_am_file)
180