Repository: raspberrypi/pico-sdk
File: tools/extract_cmake_functions.py (Python)

Code before:
    if not brief:
        logger.warning("{}:{} has no brief description".format(group, name))

    return ret.strip(), brief, ';'.join(params)


# Scan all CMakeLists.txt and .cmake files in the specific path, recursively.

for dirpath, dirnames, filenames in os.walk(scandir):
    for filename in filenames:
        if filename in skip_files:
            continue
        group = os.path.basename(dirpath)
        if group in skip_groups:
            continue
        file_ext = os.path.splitext(filename)[1]
        if filename == 'CMakeLists.txt' or file_ext == '.cmake':
            file_path = os.path.join(dirpath, filename)

            with open(file_path, encoding="ISO-8859-1") as fh:
                text = fh.read()
                for match in CMAKE_FUNCTION_RE.finditer(text):
                    name = match.group(4)
                    signature = match.group(1).strip()
                    if signature.startswith(name):
                        description, brief, params = process_commands(match.group(2).replace('#', ''), name, group, signature)
                        all_functions[name] = (group, signature, brief, description, params)

                for match in CMAKE_PICO_FUNCTIONS_RE.finditer(text):
                    name = match.group(1)
                    if name not in all_functions and name not in allowed_missing_functions:
                        logger.warning("{} function has no description in {}".format(name, file_path))


with open(outfile, 'w', newline='') as csvfile:
    fieldnames = ('name', 'group', 'signature', 'brief', 'description', 'params')
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore', dialect='excel-tab')

    writer.writeheader()
    for name, (group, signature, brief, description, params) in sorted(all_functions.items(), key=lambda x: x[1][0]):
        writer.writerow({'name': name, 'group': group, 'signature': signature, 'brief': brief, 'description': description, 'params': params})


Review comment (suggestion):
One of the descriptions has a genuine `#` inside it (`# To add additional directories to the gatt #import path, add them to the end of the argument list.`), so IMHO the `.replace('#', '')` here ought to be moved inside the line-by-line processing in `process_commands` and then you could do something like `line = re.sub('^\s+#', '', line)` to only remove the `#` from the start of lines.

Code after:
    desc = ''
    for line in description.split('\n'):
        line = line.strip()
        if line.startswith('\\'):
            _, command, remainder = line.split('\\', 2)
            remainder = remainder.strip()
            if command == 'param':
                # Parameter name and description
                params.append(remainder)
            elif command == 'brief':
                # Brief description
                brief = remainder
                desc += brief + '\\n'
            elif command == 'brief_nodesc':
                # Brief description which should not be included in the main description
                brief = remainder
            elif command == 'ingroup':
                # Group name override
                group = remainder
            else:
                logger.error("{}:{} has unknown command: {}".format(group, name, command))
        elif '\\' in line:
            logger.error("{}:{} has a line containing '\\': {}".format(group, name, line))
        else:
            desc += line + '\\n'
    # Check that there are no semicolons in the parameter descriptions, as that's the delimiter for the parameter list
    if any([';' in x for x in params]):
        logger.error("{}:{} has a parameter description containing ';'".format(group, name))
    # Check that all parameters are in the signature
    signature_words = set(re.split('\W+', signature))
    for param in params:
        param_name = param.split(' ', maxsplit=1)[0]
        if param_name not in signature_words:
            logger.error("{}:{} has a parameter {} which is not in the signature {}".format(group, name, param_name, signature))
    # Check that the brief description is not empty
    if not brief:
        logger.warning("{}:{} has no brief description".format(group, name))
    # Check that the group has a description
    if group not in group_names_descriptions:
        logger.error("{} has no group description (referenced from {})".format(group, name))

    desc = re.sub(r'^(\\n)*(.*?)(\\n)*$', r'\2', desc)
    return desc.strip(), brief, ';'.join(params), group


def sort_functions(item):
    group = item['group']
    name = item['name']

    precedence = 5
    if name.startswith('_desc_'):