]> granicus.if.org Git - esp-idf/blobdiff - tools/gen_esp_err_to_name.py
Component/bt: read multiple return callback status: ESP_GATT_STACK_RSP
[esp-idf] / tools / gen_esp_err_to_name.py
index 932dfd8bc82c523e4955a82153a071c7bcd228a3..2beb5ac9d136847e23150c109835b07c4268e004 100755 (executable)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from __future__ import print_function
+from __future__ import unicode_literals
+import sys
+try:
+    from builtins import str
+    from builtins import range
+    from builtins import object
+except ImportError:
+    # This should not happen because the Python packages are checked before invoking this script. However, here is
+    # some output which should help if we missed something.
+    print('Import has failed probably because of the missing "future" package. Please install all the packages for '
+          'interpreter {} from the requirements.txt file.'.format(sys.executable))
+    # The path to requirements.txt is not provided because this script could be invoked from an IDF project (then the
+    # requirements.txt from the IDF_PATH should be used) or from the documentation project (then the requirements.txt
+    # for the documentation directory should be used).
+    sys.exit(1)
+from io import open
 import os
 import argparse
 import re
 import fnmatch
 import os
 import argparse
 import re
 import fnmatch
-import string
 import collections
 import textwrap
 import collections
 import textwrap
+import functools
 
 # list files here which should not be parsed
 ignore_files = [ 'components/mdns/test_afl_fuzz_host/esp32_compat.h' ]
 
 # list files here which should not be parsed
 ignore_files = [ 'components/mdns/test_afl_fuzz_host/esp32_compat.h' ]
@@ -35,18 +52,20 @@ err_dict = collections.defaultdict(list) #identified errors are stored here; map
 rev_err_dict = dict() #map of error string to error code
 unproc_list = list() #errors with unknown codes which depend on other errors
 
 rev_err_dict = dict() #map of error string to error code
 unproc_list = list() #errors with unknown codes which depend on other errors
 
-class ErrItem:
+class ErrItem(object):
     """
     Contains information about the error:
     - name - error string
     - file - relative path inside the IDF project to the file which defines this error
     """
     Contains information about the error:
     - name - error string
     - file - relative path inside the IDF project to the file which defines this error
+    - include_as - (optional) overwrites the include determined from file
     - comment - (optional) comment for the error
     - rel_str - (optional) error string which is a base for the error
     - rel_off - (optional) offset in relation to the base error
     """
     - comment - (optional) comment for the error
     - rel_str - (optional) error string which is a base for the error
     - rel_off - (optional) offset in relation to the base error
     """
-    def __init__(self, name, file, comment, rel_str = "", rel_off = 0):
+    def __init__(self, name, file, include_as = None, comment = "", rel_str = "", rel_off = 0):
         self.name = name
         self.file = file
         self.name = name
         self.file = file
+        self.include_as = include_as
         self.comment = comment
         self.rel_str = rel_str
         self.rel_off = rel_off
         self.comment = comment
         self.rel_str = rel_str
         self.rel_off = rel_off
@@ -87,7 +106,7 @@ class InputError(RuntimeError):
     def __init__(self, p, e):
         super(InputError, self).__init__(p + ": " + e)
 
     def __init__(self, p, e):
         super(InputError, self).__init__(p + ": " + e)
 
-def process(line, idf_path):
+def process(line, idf_path, include_as):
     """
     Process a line of text from file idf_path (relative to IDF project).
     Fills the global list unproc_list and dictionaries err_dict, rev_err_dict
     """
     Process a line of text from file idf_path (relative to IDF project).
     Fills the global list unproc_list and dictionaries err_dict, rev_err_dict
@@ -99,7 +118,7 @@ def process(line, idf_path):
     words = re.split(r' +', line, 2)
     # words[1] is the error name
     # words[2] is the rest of the line (value, base + value, comment)
     words = re.split(r' +', line, 2)
     # words[1] is the error name
     # words[2] is the rest of the line (value, base + value, comment)
-    if len(words) < 2:
+    if len(words) < 3:
         raise InputError(idf_path, "Error at line %s" % line)
 
     line = ""
         raise InputError(idf_path, "Error at line %s" % line)
 
     line = ""
@@ -109,8 +128,8 @@ def process(line, idf_path):
     # identify possible comment
     m = re.search(r'/\*!<(.+?(?=\*/))', todo_str)
     if m:
     # identify possible comment
     m = re.search(r'/\*!<(.+?(?=\*/))', todo_str)
     if m:
-        comment = string.strip(m.group(1))
-        todo_str = string.strip(todo_str[:m.start()]) # keep just the part before the comment
+        comment = m.group(1).strip()
+        todo_str = todo_str[:m.start()].strip() # keep just the part before the comment
 
     # identify possible parentheses ()
     m = re.search(r'\((.+)\)', todo_str)
 
     # identify possible parentheses ()
     m = re.search(r'\((.+)\)', todo_str)
@@ -143,11 +162,11 @@ def process(line, idf_path):
         related
     except NameError:
         # The value of the error is known at this moment because it do not depends on some other BASE error code
         related
     except NameError:
         # The value of the error is known at this moment because it do not depends on some other BASE error code
-        err_dict[num].append(ErrItem(words[1], idf_path, comment))
+        err_dict[num].append(ErrItem(words[1], idf_path, include_as, comment))
         rev_err_dict[words[1]] = num
     else:
         # Store the information available now and compute the error code later
         rev_err_dict[words[1]] = num
     else:
         # Store the information available now and compute the error code later
-        unproc_list.append(ErrItem(words[1], idf_path, comment, related, num))
+        unproc_list.append(ErrItem(words[1], idf_path, include_as, comment, related, num))
 
 def process_remaining_errors():
     """
 
 def process_remaining_errors():
     """
@@ -163,7 +182,7 @@ def process_remaining_errors():
             base_num = rev_err_dict[item.rel_str]
             base = err_dict[base_num][0]
             num = base_num + item.rel_off
             base_num = rev_err_dict[item.rel_str]
             base = err_dict[base_num][0]
             num = base_num + item.rel_off
-            err_dict[num].append(ErrItem(item.name, item.file, item.comment))
+            err_dict[num].append(ErrItem(item.name, item.file, item.include_as, item.comment))
             rev_err_dict[item.name] = num
         else:
             print(item.rel_str + " referenced by " + item.name + " in " + item.file + " is unknown")
             rev_err_dict[item.name] = num
         else:
             print(item.rel_str + " referenced by " + item.name + " in " + item.file + " is unknown")
@@ -181,14 +200,14 @@ def path_to_include(path):
     are inside the "include" directory. Other special cases need to be handled
     here when the compiler gives an unknown header file error message.
     """
     are inside the "include" directory. Other special cases need to be handled
     here when the compiler gives an unknown header file error message.
     """
-    spl_path = string.split(path, os.sep)
+    spl_path = path.split(os.sep)
     try:
         i = spl_path.index('include')
     except ValueError:
         # no include in the path -> use just the filename
         return os.path.basename(path)
     else:
     try:
         i = spl_path.index('include')
     except ValueError:
         # no include in the path -> use just the filename
         return os.path.basename(path)
     else:
-        return str(os.sep).join(spl_path[i+1:]) # subdirectories and filename in "include"
+        return os.sep.join(spl_path[i+1:]) # subdirectories and filename in "include"
 
 def print_warning(error_list, error_code):
     """
 
 def print_warning(error_list, error_code):
     """
@@ -200,7 +219,7 @@ def print_warning(error_list, error_code):
 
 def max_string_width():
     max = 0
 
 def max_string_width():
     max = 0
-    for k in err_dict.keys():
+    for k in err_dict:
         for e in err_dict[k]:
             x = len(e.name)
             if x > max:
         for e in err_dict[k]:
             x = len(e.name)
             if x > max:
@@ -214,9 +233,12 @@ def generate_c_output(fin, fout):
     """
     # make includes unique by using a set
     includes = set()
     """
     # make includes unique by using a set
     includes = set()
-    for k in err_dict.keys():
+    for k in err_dict:
         for e in err_dict[k]:
         for e in err_dict[k]:
-            includes.add(path_to_include(e.file))
+            if e.include_as:
+                includes.add(e.include_as)
+            else:
+                includes.add(path_to_include(e.file))
 
     # The order in a set in non-deterministic therefore it could happen that the
     # include order will be different in other machines and false difference
 
     # The order in a set in non-deterministic therefore it could happen that the
     # include order will be different in other machines and false difference
@@ -226,7 +248,7 @@ def generate_c_output(fin, fout):
     include_list.sort()
 
     max_width = max_string_width() + 17 + 1 # length of "    ERR_TBL_IT()," with spaces is 17
     include_list.sort()
 
     max_width = max_string_width() + 17 + 1 # length of "    ERR_TBL_IT()," with spaces is 17
-    max_decdig = max(len(str(k)) for k in err_dict.keys())
+    max_decdig = max(len(str(k)) for k in err_dict)
 
     for line in fin:
         if re.match(r'@COMMENT@', line):
 
     for line in fin:
         if re.match(r'@COMMENT@', line):
@@ -239,7 +261,7 @@ def generate_c_output(fin, fout):
             last_file = ""
             for k in sorted(err_dict.keys()):
                 if len(err_dict[k]) > 1:
             last_file = ""
             for k in sorted(err_dict.keys()):
                 if len(err_dict[k]) > 1:
-                    err_dict[k].sort()
+                    err_dict[k].sort(key=functools.cmp_to_key(ErrItem.__cmp__))
                     print_warning(err_dict[k], k)
                 for e in err_dict[k]:
                     if e.file != last_file:
                     print_warning(err_dict[k], k)
                 for e in err_dict[k]:
                     if e.file != last_file:
@@ -291,28 +313,39 @@ def main():
     parser.add_argument('--rst_output', help='Generate .rst output and save it into this file')
     args = parser.parse_args()
 
     parser.add_argument('--rst_output', help='Generate .rst output and save it into this file')
     args = parser.parse_args()
 
+    include_as_pattern = re.compile(r'\s*//\s*{}: [^"]* "([^"]+)"'.format(os.path.basename(__file__)))
+    define_pattern = re.compile(r'\s*#define\s+(ESP_ERR_|ESP_OK|ESP_FAIL)')
+
     for root, dirnames, filenames in os.walk(idf_path):
         for filename in fnmatch.filter(filenames, '*.[ch]'):
             full_path = os.path.join(root, filename)
             path_in_idf = os.path.relpath(full_path, idf_path)
             if path_in_idf in ignore_files or path_in_idf.startswith(ignore_dirs):
                 continue
     for root, dirnames, filenames in os.walk(idf_path):
         for filename in fnmatch.filter(filenames, '*.[ch]'):
             full_path = os.path.join(root, filename)
             path_in_idf = os.path.relpath(full_path, idf_path)
             if path_in_idf in ignore_files or path_in_idf.startswith(ignore_dirs):
                 continue
-            with open(full_path, "r+") as f:
-                for line in f:
-                    # match also ESP_OK and ESP_FAIL because some of ESP_ERRs are referencing them
-                    if re.match(r"\s*#define\s+(ESP_ERR_|ESP_OK|ESP_FAIL)", line):
-                        try:
-                            process(str.strip(line), path_in_idf)
-                        except InputError as e:
-                            print (e)
+            with open(full_path, encoding='utf-8') as f:
+                try:
+                    include_as = None
+                    for line in f:
+                        line = line.strip()
+                        m = include_as_pattern.search(line)
+                        if m:
+                            include_as = m.group(1)
+                        # match also ESP_OK and ESP_FAIL because some of ESP_ERRs are referencing them
+                        elif define_pattern.match(line):
+                            try:
+                                process(line, path_in_idf, include_as)
+                            except InputError as e:
+                                print(e)
+                except UnicodeDecodeError:
+                    raise ValueError("The encoding of {} is not Unicode.".format(path_in_idf))
 
     process_remaining_errors()
 
     if args.rst_output is not None:
 
     process_remaining_errors()
 
     if args.rst_output is not None:
-        with open(args.rst_output, 'w') as fout:
+        with open(args.rst_output, 'w', encoding='utf-8') as fout:
             generate_rst_output(fout)
     else:
             generate_rst_output(fout)
     else:
-        with open(args.c_input, 'r') as fin, open(args.c_output, 'w') as fout:
+        with open(args.c_input, 'r', encoding='utf-8') as fin, open(args.c_output, 'w', encoding='utf-8') as fout:
             generate_c_output(fin, fout)
 
 if __name__ == "__main__":
             generate_c_output(fin, fout)
 
 if __name__ == "__main__":