summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/checktransupdate.py307
-rwxr-xr-xscripts/documentation-file-ref-check245
-rwxr-xr-xscripts/find-unused-docs.sh62
-rwxr-xr-xscripts/get_abi.py214
-rwxr-xr-xscripts/get_feat.pl641
-rwxr-xr-xscripts/kernel-doc.pl2439
-rwxr-xr-xscripts/test_doc_build.py513
7 files changed, 0 insertions, 4421 deletions
diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py
deleted file mode 100755
index e39529e46c3d..000000000000
--- a/scripts/checktransupdate.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-This script helps track the translation status of the documentation
-in different locales, e.g., zh_CN. More specially, it uses `git log`
-commit to find the latest english commit from the translation commit
-(order by author date) and the latest english commits from HEAD. If
-differences occur, report the file and commits that need to be updated.
-
-The usage is as follows:
-- ./scripts/checktransupdate.py -l zh_CN
-This will print all the files that need to be updated or translated in the zh_CN locale.
-- ./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst
-This will only print the status of the specified file.
-
-The output is something like:
-Documentation/dev-tools/kfence.rst
-No translation in the locale of zh_CN
-
-Documentation/translations/zh_CN/dev-tools/testing-overview.rst
-commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs")
-1 commits needs resolving in total
-"""
-
-import os
-import re
-import time
-import logging
-from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction
-from datetime import datetime
-
-
-def get_origin_path(file_path):
- """Get the origin path from the translation path"""
- paths = file_path.split("/")
- tidx = paths.index("translations")
- opaths = paths[:tidx]
- opaths += paths[tidx + 2 :]
- return "/".join(opaths)
-
-
-def get_latest_commit_from(file_path, commit):
- """Get the latest commit from the specified commit for the specified file"""
- command = f"git log --pretty=format:%H%n%aD%n%cD%n%n%B {commit} -1 -- {file_path}"
- logging.debug(command)
- pipe = os.popen(command)
- result = pipe.read()
- result = result.split("\n")
- if len(result) <= 1:
- return None
-
- logging.debug("Result: %s", result[0])
-
- return {
- "hash": result[0],
- "author_date": datetime.strptime(result[1], "%a, %d %b %Y %H:%M:%S %z"),
- "commit_date": datetime.strptime(result[2], "%a, %d %b %Y %H:%M:%S %z"),
- "message": result[4:],
- }
-
-
-def get_origin_from_trans(origin_path, t_from_head):
- """Get the latest origin commit from the translation commit"""
- o_from_t = get_latest_commit_from(origin_path, t_from_head["hash"])
- while o_from_t is not None and o_from_t["author_date"] > t_from_head["author_date"]:
- o_from_t = get_latest_commit_from(origin_path, o_from_t["hash"] + "^")
- if o_from_t is not None:
- logging.debug("tracked origin commit id: %s", o_from_t["hash"])
- return o_from_t
-
-
-def get_origin_from_trans_smartly(origin_path, t_from_head):
- """Get the latest origin commit from the formatted translation commit:
- (1) update to commit HASH (TITLE)
- (2) Update the translation through commit HASH (TITLE)
- """
- # catch flag for 12-bit commit hash
- HASH = r'([0-9a-f]{12})'
- # pattern 1: contains "update to commit HASH"
- pat_update_to = re.compile(rf'update to commit {HASH}')
- # pattern 2: contains "Update the translation through commit HASH"
- pat_update_translation = re.compile(rf'Update the translation through commit {HASH}')
-
- origin_commit_hash = None
- for line in t_from_head["message"]:
- # check if the line matches the first pattern
- match = pat_update_to.search(line)
- if match:
- origin_commit_hash = match.group(1)
- break
- # check if the line matches the second pattern
- match = pat_update_translation.search(line)
- if match:
- origin_commit_hash = match.group(1)
- break
- if origin_commit_hash is None:
- return None
- o_from_t = get_latest_commit_from(origin_path, origin_commit_hash)
- if o_from_t is not None:
- logging.debug("tracked origin commit id: %s", o_from_t["hash"])
- return o_from_t
-
-
-def get_commits_count_between(opath, commit1, commit2):
- """Get the commits count between two commits for the specified file"""
- command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}"
- logging.debug(command)
- pipe = os.popen(command)
- result = pipe.read().split("\n")
- # filter out empty lines
- result = list(filter(lambda x: x != "", result))
- return result
-
-
-def pretty_output(commit):
- """Pretty print the commit message"""
- command = f"git log --pretty='format:%h (\"%s\")' -1 {commit}"
- logging.debug(command)
- pipe = os.popen(command)
- return pipe.read()
-
-
-def valid_commit(commit):
- """Check if the commit is valid or not"""
- msg = pretty_output(commit)
- return "Merge tag" not in msg
-
-def check_per_file(file_path):
- """Check the translation status for the specified file"""
- opath = get_origin_path(file_path)
-
- if not os.path.isfile(opath):
- logging.error("Cannot find the origin path for {file_path}")
- return
-
- o_from_head = get_latest_commit_from(opath, "HEAD")
- t_from_head = get_latest_commit_from(file_path, "HEAD")
-
- if o_from_head is None or t_from_head is None:
- logging.error("Cannot find the latest commit for %s", file_path)
- return
-
- o_from_t = get_origin_from_trans_smartly(opath, t_from_head)
- # notice, o_from_t from get_*_smartly() is always more accurate than from get_*()
- if o_from_t is None:
- o_from_t = get_origin_from_trans(opath, t_from_head)
-
- if o_from_t is None:
- logging.error("Error: Cannot find the latest origin commit for %s", file_path)
- return
-
- if o_from_head["hash"] == o_from_t["hash"]:
- logging.debug("No update needed for %s", file_path)
- else:
- logging.info(file_path)
- commits = get_commits_count_between(
- opath, o_from_t["hash"], o_from_head["hash"]
- )
- count = 0
- for commit in commits:
- if valid_commit(commit):
- logging.info("commit %s", pretty_output(commit))
- count += 1
- logging.info("%d commits needs resolving in total\n", count)
-
-
-def valid_locales(locale):
- """Check if the locale is valid or not"""
- script_path = os.path.dirname(os.path.abspath(__file__))
- linux_path = os.path.join(script_path, "..")
- if not os.path.isdir(f"{linux_path}/Documentation/translations/{locale}"):
- raise ArgumentTypeError("Invalid locale: {locale}")
- return locale
-
-
-def list_files_with_excluding_folders(folder, exclude_folders, include_suffix):
- """List all files with the specified suffix in the folder and its subfolders"""
- files = []
- stack = [folder]
-
- while stack:
- pwd = stack.pop()
- # filter out the exclude folders
- if os.path.basename(pwd) in exclude_folders:
- continue
- # list all files and folders
- for item in os.listdir(pwd):
- ab_item = os.path.join(pwd, item)
- if os.path.isdir(ab_item):
- stack.append(ab_item)
- else:
- if ab_item.endswith(include_suffix):
- files.append(ab_item)
-
- return files
-
-
-class DmesgFormatter(logging.Formatter):
- """Custom dmesg logging formatter"""
- def format(self, record):
- timestamp = time.time()
- formatted_time = f"[{timestamp:>10.6f}]"
- log_message = f"{formatted_time} {record.getMessage()}"
- return log_message
-
-
-def config_logging(log_level, log_file="checktransupdate.log"):
- """configure logging based on the log level"""
- # set up the root logger
- logger = logging.getLogger()
- logger.setLevel(log_level)
-
- # Create console handler
- console_handler = logging.StreamHandler()
- console_handler.setLevel(log_level)
-
- # Create file handler
- file_handler = logging.FileHandler(log_file)
- file_handler.setLevel(log_level)
-
- # Create formatter and add it to the handlers
- formatter = DmesgFormatter()
- console_handler.setFormatter(formatter)
- file_handler.setFormatter(formatter)
-
- # Add the handler to the logger
- logger.addHandler(console_handler)
- logger.addHandler(file_handler)
-
-
-def main():
- """Main function of the script"""
- script_path = os.path.dirname(os.path.abspath(__file__))
- linux_path = os.path.join(script_path, "..")
-
- parser = ArgumentParser(description="Check the translation update")
- parser.add_argument(
- "-l",
- "--locale",
- default="zh_CN",
- type=valid_locales,
- help="Locale to check when files are not specified",
- )
-
- parser.add_argument(
- "--print-missing-translations",
- action=BooleanOptionalAction,
- default=True,
- help="Print files that do not have translations",
- )
-
- parser.add_argument(
- '--log',
- default='INFO',
- choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
- help='Set the logging level')
-
- parser.add_argument(
- '--logfile',
- default='checktransupdate.log',
- help='Set the logging file (default: checktransupdate.log)')
-
- parser.add_argument(
- "files", nargs="*", help="Files to check, if not specified, check all files"
- )
- args = parser.parse_args()
-
- # Configure logging based on the --log argument
- log_level = getattr(logging, args.log.upper(), logging.INFO)
- config_logging(log_level)
-
- # Get files related to linux path
- files = args.files
- if len(files) == 0:
- offical_files = list_files_with_excluding_folders(
- os.path.join(linux_path, "Documentation"), ["translations", "output"], "rst"
- )
-
- for file in offical_files:
- # split the path into parts
- path_parts = file.split(os.sep)
- # find the index of the "Documentation" directory
- kindex = path_parts.index("Documentation")
- # insert the translations and locale after the Documentation directory
- new_path_parts = path_parts[:kindex + 1] + ["translations", args.locale] \
- + path_parts[kindex + 1 :]
- # join the path parts back together
- new_file = os.sep.join(new_path_parts)
- if os.path.isfile(new_file):
- files.append(new_file)
- else:
- if args.print_missing_translations:
- logging.info(os.path.relpath(os.path.abspath(file), linux_path))
- logging.info("No translation in the locale of %s\n", args.locale)
-
- files = list(map(lambda x: os.path.relpath(os.path.abspath(x), linux_path), files))
-
- # cd to linux root directory
- os.chdir(linux_path)
-
- for file in files:
- check_per_file(file)
-
-
-if __name__ == "__main__":
- main()
diff --git a/scripts/documentation-file-ref-check b/scripts/documentation-file-ref-check
deleted file mode 100755
index 408b1dbe7884..000000000000
--- a/scripts/documentation-file-ref-check
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-#
-# Treewide grep for references to files under Documentation, and report
-# non-existing files in stderr.
-
-use warnings;
-use strict;
-use Getopt::Long qw(:config no_auto_abbrev);
-
-# NOTE: only add things here when the file was gone, but the text wants
-# to mention a past documentation file, for example, to give credits for
-# the original work.
-my %false_positives = (
- "Documentation/scsi/scsi_mid_low_api.rst" => "Documentation/Configure.help",
- "drivers/vhost/vhost.c" => "Documentation/virtual/lguest/lguest.c",
-);
-
-my $scriptname = $0;
-$scriptname =~ s,.*/([^/]+/),$1,;
-
-# Parse arguments
-my $help = 0;
-my $fix = 0;
-my $warn = 0;
-
-if (! -e ".git") {
- printf "Warning: can't check if file exists, as this is not a git tree\n";
- exit 0;
-}
-
-GetOptions(
- 'fix' => \$fix,
- 'warn' => \$warn,
- 'h|help|usage' => \$help,
-);
-
-if ($help != 0) {
- print "$scriptname [--help] [--fix]\n";
- exit -1;
-}
-
-# Step 1: find broken references
-print "Finding broken references. This may take a while... " if ($fix);
-
-my %broken_ref;
-
-my $doc_fix = 0;
-
-open IN, "git grep ':doc:\`' Documentation/|"
- or die "Failed to run git grep";
-while (<IN>) {
- next if (!m,^([^:]+):.*\:doc\:\`([^\`]+)\`,);
- next if (m,sphinx/,);
-
- my $file = $1;
- my $d = $1;
- my $doc_ref = $2;
-
- my $f = $doc_ref;
-
- $d =~ s,(.*/).*,$1,;
- $f =~ s,.*\<([^\>]+)\>,$1,;
-
- if ($f =~ m,^/,) {
- $f = "$f.rst";
- $f =~ s,^/,Documentation/,;
- } else {
- $f = "$d$f.rst";
- }
-
- next if (grep -e, glob("$f"));
-
- if ($fix && !$doc_fix) {
- print STDERR "\nWARNING: Currently, can't fix broken :doc:`` fields\n";
- }
- $doc_fix++;
-
- print STDERR "$file: :doc:`$doc_ref`\n";
-}
-close IN;
-
-open IN, "git grep 'Documentation/'|"
- or die "Failed to run git grep";
-while (<IN>) {
- next if (!m/^([^:]+):(.*)/);
-
- my $f = $1;
- my $ln = $2;
-
- # On linux-next, discard the Next/ directory
- next if ($f =~ m,^Next/,);
-
- # Makefiles and scripts contain nasty expressions to parse docs
- next if ($f =~ m/Makefile/ || $f =~ m/\.(sh|py|pl|~|rej|org|orig)$/);
-
- # It doesn't make sense to parse hidden files
- next if ($f =~ m#/\.#);
-
- # Skip this script
- next if ($f eq $scriptname);
-
- # Ignore the dir where documentation will be built
- next if ($ln =~ m,\b(\S*)Documentation/output,);
-
- if ($ln =~ m,\b(\S*)(Documentation/[A-Za-z0-9\_\.\,\~/\*\[\]\?+-]*)(.*),) {
- my $prefix = $1;
- my $ref = $2;
- my $base = $2;
- my $extra = $3;
-
- # some file references are like:
- # /usr/src/linux/Documentation/DMA-{API,mapping}.txt
- # For now, ignore them
- next if ($extra =~ m/^{/);
-
- # Remove footnotes at the end like:
- # Documentation/devicetree/dt-object-internal.txt[1]
- $ref =~ s/(txt|rst)\[\d+]$/$1/;
-
- # Remove ending ']' without any '['
- $ref =~ s/\].*// if (!($ref =~ m/\[/));
-
- # Remove puntuation marks at the end
- $ref =~ s/[\,\.]+$//;
-
- my $fulref = "$prefix$ref";
-
- $fulref =~ s/^(\<file|ref)://;
- $fulref =~ s/^[\'\`]+//;
- $fulref =~ s,^\$\(.*\)/,,;
- $base =~ s,.*/,,;
-
- # Remove URL false-positives
- next if ($fulref =~ m/^http/);
-
- # Remove sched-pelt false-positive
- next if ($fulref =~ m,^Documentation/scheduler/sched-pelt$,);
-
- # Discard some build examples from Documentation/target/tcm_mod_builder.rst
- next if ($fulref =~ m,mnt/sdb/lio-core-2.6.git/Documentation/target,);
-
- # Check if exists, evaluating wildcards
- next if (grep -e, glob("$ref $fulref"));
-
- # Accept relative Documentation patches for tools/
- if ($f =~ m/tools/) {
- my $path = $f;
- $path =~ s,(.*)/.*,$1,;
- $path =~ s,testing/selftests/bpf,bpf/bpftool,;
- next if (grep -e, glob("$path/$ref $path/../$ref $path/$fulref"));
- }
-
- # Discard known false-positives
- if (defined($false_positives{$f})) {
- next if ($false_positives{$f} eq $fulref);
- }
-
- if ($fix) {
- if (!($ref =~ m/(scripts|Kconfig|Kbuild)/)) {
- $broken_ref{$ref}++;
- }
- } elsif ($warn) {
- print STDERR "Warning: $f references a file that doesn't exist: $fulref\n";
- } else {
- print STDERR "$f: $fulref\n";
- }
- }
-}
-close IN;
-
-exit 0 if (!$fix);
-
-# Step 2: Seek for file name alternatives
-print "Auto-fixing broken references. Please double-check the results\n";
-
-foreach my $ref (keys %broken_ref) {
- my $new =$ref;
-
- my $basedir = ".";
- # On translations, only seek inside the translations directory
- $basedir = $1 if ($ref =~ m,(Documentation/translations/[^/]+),);
-
- # get just the basename
- $new =~ s,.*/,,;
-
- my $f="";
-
- # usual reason for breakage: DT file moved around
- if ($ref =~ /devicetree/) {
- # usual reason for breakage: DT file renamed to .yaml
- if (!$f) {
- my $new_ref = $ref;
- $new_ref =~ s/\.txt$/.yaml/;
- $f=$new_ref if (-f $new_ref);
- }
-
- if (!$f) {
- my $search = $new;
- $search =~ s,^.*/,,;
- $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
- if (!$f) {
- # Manufacturer name may have changed
- $search =~ s/^.*,//;
- $f = qx(find Documentation/devicetree/ -iname "*$search*") if ($search);
- }
- }
- }
-
- # usual reason for breakage: file renamed to .rst
- if (!$f) {
- $new =~ s/\.txt$/.rst/;
- $f=qx(find $basedir -iname $new) if ($new);
- }
-
- # usual reason for breakage: use dash or underline
- if (!$f) {
- $new =~ s/[-_]/[-_]/g;
- $f=qx(find $basedir -iname $new) if ($new);
- }
-
- # Wild guess: seek for the same name on another place
- if (!$f) {
- $f = qx(find $basedir -iname $new) if ($new);
- }
-
- my @find = split /\s+/, $f;
-
- if (!$f) {
- print STDERR "ERROR: Didn't find a replacement for $ref\n";
- } elsif (scalar(@find) > 1) {
- print STDERR "WARNING: Won't auto-replace, as found multiple files close to $ref:\n";
- foreach my $j (@find) {
- $j =~ s,^./,,;
- print STDERR " $j\n";
- }
- } else {
- $f = $find[0];
- $f =~ s,^./,,;
- print "INFO: Replacing $ref to $f\n";
- foreach my $j (qx(git grep -l $ref)) {
- qx(sed "s\@$ref\@$f\@g" -i $j);
- }
- }
-}
diff --git a/scripts/find-unused-docs.sh b/scripts/find-unused-docs.sh
deleted file mode 100755
index d6d397fbf917..000000000000
--- a/scripts/find-unused-docs.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-# (c) 2017, Jonathan Corbet <corbet@lwn.net>
-# sayli karnik <karniksayli1995@gmail.com>
-#
-# This script detects files with kernel-doc comments for exported functions
-# that are not included in documentation.
-#
-# usage: Run 'scripts/find-unused-docs.sh directory' from top level of kernel
-# tree.
-#
-# example: $scripts/find-unused-docs.sh drivers/scsi
-#
-# Licensed under the terms of the GNU GPL License
-
-if ! [ -d "Documentation" ]; then
- echo "Run from top level of kernel tree"
- exit 1
-fi
-
-if [ "$#" -ne 1 ]; then
- echo "Usage: scripts/find-unused-docs.sh directory"
- exit 1
-fi
-
-if ! [ -d "$1" ]; then
- echo "Directory $1 doesn't exist"
- exit 1
-fi
-
-cd "$( dirname "${BASH_SOURCE[0]}" )"
-cd ..
-
-cd Documentation/
-
-echo "The following files contain kerneldoc comments for exported functions \
-that are not used in the formatted documentation"
-
-# FILES INCLUDED
-
-files_included=($(grep -rHR ".. kernel-doc" --include \*.rst | cut -d " " -f 3))
-
-declare -A FILES_INCLUDED
-
-for each in "${files_included[@]}"; do
- FILES_INCLUDED[$each]="$each"
- done
-
-cd ..
-
-# FILES NOT INCLUDED
-
-for file in `find $1 -name '*.c'`; do
-
- if [[ ${FILES_INCLUDED[$file]+_} ]]; then
- continue;
- fi
- str=$(PYTHONDONTWRITEBYTECODE=1 scripts/kernel-doc -export "$file" 2>/dev/null)
- if [[ -n "$str" ]]; then
- echo "$file"
- fi
- done
-
diff --git a/scripts/get_abi.py b/scripts/get_abi.py
deleted file mode 100755
index 7ce4748a46d2..000000000000
--- a/scripts/get_abi.py
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env python3
-# pylint: disable=R0903
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
-# SPDX-License-Identifier: GPL-2.0
-
-"""
-Parse ABI documentation and produce results from it.
-"""
-
-import argparse
-import logging
-import os
-import sys
-
-# Import Python modules
-
-LIB_DIR = "lib/abi"
-SRC_DIR = os.path.dirname(os.path.realpath(__file__))
-
-sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
-
-from abi_parser import AbiParser # pylint: disable=C0413
-from abi_regex import AbiRegex # pylint: disable=C0413
-from helpers import ABI_DIR, DEBUG_HELP # pylint: disable=C0413
-from system_symbols import SystemSymbols # pylint: disable=C0413
-
-# Command line classes
-
-
-REST_DESC = """
-Produce output in ReST format.
-
-The output is done on two sections:
-
-- Symbols: show all parsed symbols in alphabetic order;
-- Files: cross reference the content of each file with the symbols on it.
-"""
-
-class AbiRest:
- """Initialize an argparse subparser for rest output"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("rest",
- formatter_class=argparse.RawTextHelpFormatter,
- description=REST_DESC)
-
- parser.add_argument("--enable-lineno", action="store_true",
- help="enable lineno")
- parser.add_argument("--raw", action="store_true",
- help="output text as contained in the ABI files. "
- "It not used, output will contain dynamically"
- " generated cross references when possible.")
- parser.add_argument("--no-file", action="store_true",
- help="Don't the files section")
- parser.add_argument("--show-hints", help="Show-hints")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.check_issues()
-
- for t in parser.doc(args.raw, not args.no_file):
- if args.enable_lineno:
- print (f".. LINENO {t[1]}#{t[2]}\n\n")
-
- print(t[0])
-
-class AbiValidate:
- """Initialize an argparse subparser for ABI validation"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("validate",
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description="list events")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.check_issues()
-
-
-class AbiSearch:
- """Initialize an argparse subparser for ABI search"""
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("search",
- formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- description="Search ABI using a regular expression")
-
- parser.add_argument("expression",
- help="Case-insensitive search pattern for the ABI symbol")
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- parser = AbiParser(args.dir, debug=args.debug)
- parser.parse_abi()
- parser.search_symbols(args.expression)
-
-UNDEFINED_DESC="""
-Check undefined ABIs on local machine.
-
-Read sysfs devnodes and check if the devnodes there are defined inside
-ABI documentation.
-
-The search logic tries to minimize the number of regular expressions to
-search per each symbol.
-
-By default, it runs on a single CPU, as Python support for CPU threads
-is still experimental, and multi-process runs on Python is very slow.
-
-On experimental tests, if the number of ABI symbols to search per devnode
-is contained on a limit of ~150 regular expressions, using a single CPU
-is a lot faster than using multiple processes. However, if the number of
-regular expressions to check is at the order of ~30000, using multiple
-CPUs speeds up the check.
-"""
-
-class AbiUndefined:
- """
- Initialize an argparse subparser for logic to check undefined ABI at
- the current machine's sysfs
- """
-
- def __init__(self, subparsers):
- """Initialize argparse subparsers"""
-
- parser = subparsers.add_parser("undefined",
- formatter_class=argparse.RawTextHelpFormatter,
- description=UNDEFINED_DESC)
-
- parser.add_argument("-S", "--sysfs-dir", default="/sys",
- help="directory where sysfs is mounted")
- parser.add_argument("-s", "--search-string",
- help="search string regular expression to limit symbol search")
- parser.add_argument("-H", "--show-hints", action="store_true",
- help="Hints about definitions for missing ABI symbols.")
- parser.add_argument("-j", "--jobs", "--max-workers", type=int, default=1,
- help="If bigger than one, enables multiprocessing.")
- parser.add_argument("-c", "--max-chunk-size", type=int, default=50,
- help="Maximum number of chunk size")
- parser.add_argument("-f", "--found", action="store_true",
- help="Also show found items. "
- "Helpful to debug the parser."),
- parser.add_argument("-d", "--dry-run", action="store_true",
- help="Don't actually search for undefined. "
- "Helpful to debug the parser."),
-
- parser.set_defaults(func=self.run)
-
- def run(self, args):
- """Run subparser"""
-
- abi = AbiRegex(args.dir, debug=args.debug,
- search_string=args.search_string)
-
- abi_symbols = SystemSymbols(abi=abi, hints=args.show_hints,
- sysfs=args.sysfs_dir)
-
- abi_symbols.check_undefined_symbols(dry_run=args.dry_run,
- found=args.found,
- max_workers=args.jobs,
- chunk_size=args.max_chunk_size)
-
-
-def main():
- """Main program"""
-
- parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
-
- parser.add_argument("-d", "--debug", type=int, default=0, help="debug level")
- parser.add_argument("-D", "--dir", default=ABI_DIR, help=DEBUG_HELP)
-
- subparsers = parser.add_subparsers()
-
- AbiRest(subparsers)
- AbiValidate(subparsers)
- AbiSearch(subparsers)
- AbiUndefined(subparsers)
-
- args = parser.parse_args()
-
- if args.debug:
- level = logging.DEBUG
- else:
- level = logging.INFO
-
- logging.basicConfig(level=level, format="[%(levelname)s] %(message)s")
-
- if "func" in args:
- args.func(args)
- else:
- sys.exit(f"Please specify a valid command for {sys.argv[0]}")
-
-
-# Call main method
-if __name__ == "__main__":
- main()
diff --git a/scripts/get_feat.pl b/scripts/get_feat.pl
deleted file mode 100755
index 40fb28c8424e..000000000000
--- a/scripts/get_feat.pl
+++ /dev/null
@@ -1,641 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-
-use strict;
-use Pod::Usage;
-use Getopt::Long;
-use File::Find;
-use Fcntl ':mode';
-use Cwd 'abs_path';
-
-my $help;
-my $man;
-my $debug;
-my $arch;
-my $feat;
-my $enable_fname;
-
-my $basename = abs_path($0);
-$basename =~ s,/[^/]+$,/,;
-
-my $prefix=$basename . "../Documentation/features";
-
-# Used only at for full features output. The script will auto-adjust
-# such values for the minimal possible values
-my $status_size = 1;
-my $description_size = 1;
-
-GetOptions(
- "debug|d+" => \$debug,
- "dir=s" => \$prefix,
- 'help|?' => \$help,
- 'arch=s' => \$arch,
- 'feat=s' => \$feat,
- 'feature=s' => \$feat,
- "enable-fname" => \$enable_fname,
- man => \$man
-) or pod2usage(2);
-
-pod2usage(1) if $help;
-pod2usage(-exitstatus => 0, -verbose => 2) if $man;
-
-pod2usage(1) if (scalar @ARGV < 1 || @ARGV > 2);
-
-my ($cmd, $arg) = @ARGV;
-
-pod2usage(2) if ($cmd ne "current" && $cmd ne "rest" && $cmd ne "validate"
- && $cmd ne "ls" && $cmd ne "list");
-
-require Data::Dumper if ($debug);
-
-my %data;
-my %archs;
-
-#
-# Displays an error message, printing file name and line
-#
-sub parse_error($$$$) {
- my ($file, $ln, $msg, $data) = @_;
-
- $data =~ s/\s+$/\n/;
-
- print STDERR "Warning: file $file#$ln:\n\t$msg";
-
- if ($data ne "") {
- print STDERR ". Line\n\t\t$data";
- } else {
- print STDERR "\n";
- }
-}
-
-#
-# Parse a features file, storing its contents at %data
-#
-
-my $h_name = "Feature";
-my $h_kconfig = "Kconfig";
-my $h_description = "Description";
-my $h_subsys = "Subsystem";
-my $h_status = "Status";
-my $h_arch = "Architecture";
-
-my $max_size_name = length($h_name);
-my $max_size_kconfig = length($h_kconfig);
-my $max_size_description = length($h_description);
-my $max_size_subsys = length($h_subsys);
-my $max_size_status = length($h_status);
-
-my $max_size_arch = 0;
-my $max_size_arch_with_header;
-my $max_description_word = 0;
-
-sub parse_feat {
- my $file = $File::Find::name;
-
- my $mode = (stat($file))[2];
- return if ($mode & S_IFDIR);
- return if ($file =~ m,($prefix)/arch-support.txt,);
- return if (!($file =~ m,arch-support.txt$,));
-
- if ($enable_fname) {
- printf ".. FILE %s\n", abs_path($file);
- }
-
- my $subsys = "";
- $subsys = $2 if ( m,.*($prefix)/([^/]+).*,);
-
- if (length($subsys) > $max_size_subsys) {
- $max_size_subsys = length($subsys);
- }
-
- my $name;
- my $kconfig;
- my $description;
- my $comments = "";
- my $last_status;
- my $ln;
- my %arch_table;
-
- print STDERR "Opening $file\n" if ($debug > 1);
- open IN, $file;
-
- while(<IN>) {
- $ln++;
-
- if (m/^\#\s+Feature\s+name:\s*(.*\S)/) {
- $name = $1;
- if (length($name) > $max_size_name) {
- $max_size_name = length($name);
- }
- next;
- }
- if (m/^\#\s+Kconfig:\s*(.*\S)/) {
- $kconfig = $1;
- if (length($kconfig) > $max_size_kconfig) {
- $max_size_kconfig = length($kconfig);
- }
- next;
- }
- if (m/^\#\s+description:\s*(.*\S)/) {
- $description = $1;
- if (length($description) > $max_size_description) {
- $max_size_description = length($description);
- }
-
- foreach my $word (split /\s+/, $description) {
- if (length($word) > $max_description_word) {
- $max_description_word = length($word);
- }
- }
-
- next;
- }
- next if (m/^\\s*$/);
- next if (m/^\s*\-+\s*$/);
- next if (m/^\s*\|\s*arch\s*\|\s*status\s*\|\s*$/);
-
- if (m/^\#\s*(.*)/) {
- $comments .= "$1\n";
- next;
- }
- if (m/^\s*\|\s*(\S+):\s*\|\s*(\S+)\s*\|\s*$/) {
- my $a = $1;
- my $status = $2;
-
- if (length($status) > $max_size_status) {
- $max_size_status = length($status);
- }
- if (length($a) > $max_size_arch) {
- $max_size_arch = length($a);
- }
-
- $status = "---" if ($status =~ m/^\.\.$/);
-
- $archs{$a} = 1;
- $arch_table{$a} = $status;
- next;
- }
-
- #Everything else is an error
- parse_error($file, $ln, "line is invalid", $_);
- }
- close IN;
-
- if (!$name) {
- parse_error($file, $ln, "Feature name not found", "");
- return;
- }
-
- parse_error($file, $ln, "Subsystem not found", "") if (!$subsys);
- parse_error($file, $ln, "Kconfig not found", "") if (!$kconfig);
- parse_error($file, $ln, "Description not found", "") if (!$description);
-
- if (!%arch_table) {
- parse_error($file, $ln, "Architecture table not found", "");
- return;
- }
-
- $data{$name}->{where} = $file;
- $data{$name}->{subsys} = $subsys;
- $data{$name}->{kconfig} = $kconfig;
- $data{$name}->{description} = $description;
- $data{$name}->{comments} = $comments;
- $data{$name}->{table} = \%arch_table;
-
- $max_size_arch_with_header = $max_size_arch + length($h_arch);
-}
-
-#
-# Output feature(s) for a given architecture
-#
-sub output_arch_table {
- my $title = "Feature status on $arch architecture";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
- printf "%-${max_size_subsys}s ", $h_subsys;
- printf "%-${max_size_name}s ", $h_name;
- printf "%-${max_size_kconfig}s ", $h_kconfig;
- printf "%-${max_size_status}s ", $h_status;
- printf "%-${max_size_description}s\n", $h_description;
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
-
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
- ("\L$a" cmp "\L$b")
- } keys %data) {
- next if ($feat && $name ne $feat);
-
- my %arch_table = %{$data{$name}->{table}};
- printf "%-${max_size_subsys}s ", $data{$name}->{subsys};
- printf "%-${max_size_name}s ", $name;
- printf "%-${max_size_kconfig}s ", $data{$name}->{kconfig};
- printf "%-${max_size_status}s ", $arch_table{$arch};
- printf "%-s\n", $data{$name}->{description};
- }
-
- print "=" x $max_size_subsys;
- print " ";
- print "=" x $max_size_name;
- print " ";
- print "=" x $max_size_kconfig;
- print " ";
- print "=" x $max_size_status;
- print " ";
- print "=" x $max_size_description;
- print "\n";
-}
-
-#
-# list feature(s) for a given architecture
-#
-sub list_arch_features {
- print "#\n# Kernel feature support matrix of the '$arch' architecture:\n#\n";
-
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) ||
- ("\L$a" cmp "\L$b")
- } keys %data) {
- next if ($feat && $name ne $feat);
-
- my %arch_table = %{$data{$name}->{table}};
-
- my $status = $arch_table{$arch};
- $status = " " x ((4 - length($status)) / 2) . $status;
-
- printf " %${max_size_subsys}s/ ", $data{$name}->{subsys};
- printf "%-${max_size_name}s: ", $name;
- printf "%-5s| ", $status;
- printf "%${max_size_kconfig}s # ", $data{$name}->{kconfig};
- printf " %s\n", $data{$name}->{description};
- }
-}
-
-#
-# Output a feature on all architectures
-#
-sub output_feature {
- my $title = "Feature $feat";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- print ":Subsystem: $data{$feat}->{subsys} \n" if ($data{$feat}->{subsys});
- print ":Kconfig: $data{$feat}->{kconfig} \n" if ($data{$feat}->{kconfig});
-
- my $desc = $data{$feat}->{description};
- $desc =~ s/^([a-z])/\U$1/;
- $desc =~ s/\.?\s*//;
- print "\n$desc.\n\n";
-
- my $com = $data{$feat}->{comments};
- $com =~ s/^\s+//;
- $com =~ s/\s+$//;
- if ($com) {
- print "Comments\n";
- print "--------\n\n";
- print "$com\n\n";
- }
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- printf "%-${max_size_arch}s ", $h_arch;
- printf "%-${max_size_status}s", $h_status . "\n";
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-
- my %arch_table = %{$data{$feat}->{table}};
- foreach my $arch (sort keys %arch_table) {
- printf "%-${max_size_arch}s ", $arch;
- printf "%-${max_size_status}s\n", $arch_table{$arch};
- }
-
- print "=" x $max_size_arch_with_header;
- print " ";
- print "=" x $max_size_status;
- print "\n";
-}
-
-#
-# Output all features for all architectures
-#
-
-sub matrix_lines($$$) {
- my $desc_size = shift;
- my $status_size = shift;
- my $header = shift;
- my $fill;
- my $ln_marker;
-
- if ($header) {
- $ln_marker = "=";
- } else {
- $ln_marker = "-";
- }
-
- $fill = $ln_marker;
-
- print "+";
- print $fill x $max_size_name;
- print "+";
- print $fill x $desc_size;
- print "+";
- print $ln_marker x $status_size;
- print "+\n";
-}
-
-sub output_matrix {
- my $title = "Feature status on all architectures";
- my $notcompat = "Not compatible";
-
- print "=" x length($title) . "\n";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
- my $desc_title = "$h_kconfig / $h_description";
-
- my $desc_size = $max_size_kconfig + 4;
- if (!$description_size) {
- $desc_size = $max_size_description if ($max_size_description > $desc_size);
- } else {
- $desc_size = $description_size if ($description_size > $desc_size);
- }
- $desc_size = $max_description_word if ($max_description_word > $desc_size);
-
- $desc_size = length($desc_title) if (length($desc_title) > $desc_size);
-
- $max_size_status = length($notcompat) if (length($notcompat) > $max_size_status);
-
- # Ensure that the status will fit
- my $min_status_size = $max_size_status + $max_size_arch + 6;
- $status_size = $min_status_size if ($status_size < $min_status_size);
-
-
- my $cur_subsys = "";
- foreach my $name (sort {
- ($data{$a}->{subsys} cmp $data{$b}->{subsys}) or
- ("\L$a" cmp "\L$b")
- } keys %data) {
-
- if ($cur_subsys ne $data{$name}->{subsys}) {
- if ($cur_subsys ne "") {
- printf "\n";
- }
-
- $cur_subsys = $data{$name}->{subsys};
-
- my $title = "Subsystem: $cur_subsys";
- print "$title\n";
- print "=" x length($title) . "\n\n";
-
-
- matrix_lines($desc_size, $status_size, 0);
-
- printf "|%-${max_size_name}s", $h_name;
- printf "|%-${desc_size}s", $desc_title;
-
- printf "|%-${status_size}s|\n", "Status per architecture";
- matrix_lines($desc_size, $status_size, 1);
- }
-
- my %arch_table = %{$data{$name}->{table}};
- my $cur_status = "";
-
- my (@lines, @descs);
- my $line = "";
- foreach my $arch (sort {
- ($arch_table{$b} cmp $arch_table{$a}) or
- ("\L$a" cmp "\L$b")
- } keys %arch_table) {
-
- my $status = $arch_table{$arch};
-
- if ($status eq "---") {
- $status = $notcompat;
- }
-
- if ($status ne $cur_status) {
- if ($line ne "") {
- push @lines, $line;
- $line = "";
- }
- $line = "- **" . $status . "**: " . $arch;
- } elsif (length($line) + length ($arch) + 2 < $status_size) {
- $line .= ", " . $arch;
- } else {
- push @lines, $line;
- $line = " " . $arch;
- }
- $cur_status = $status;
- }
- push @lines, $line if ($line ne "");
-
- my $description = $data{$name}->{description};
- while (length($description) > $desc_size) {
- my $d = substr $description, 0, $desc_size;
-
- # Ensure that it will end on a space
- # if it can't, it means that the size is too small
- # Instead of aborting it, let's print what we have
- if (!($d =~ s/^(.*)\s+.*/$1/)) {
- $d = substr $d, 0, -1;
- push @descs, "$d\\";
- $description =~ s/^\Q$d\E//;
- } else {
- push @descs, $d;
- $description =~ s/^\Q$d\E\s+//;
- }
- }
- push @descs, $description;
-
- # Ensure that the full description will be printed
- push @lines, "" while (scalar(@lines) < 2 + scalar(@descs));
-
- my $ln = 0;
- for my $line(@lines) {
- if (!$ln) {
- printf "|%-${max_size_name}s", $name;
- printf "|%-${desc_size}s", "``" . $data{$name}->{kconfig} . "``";
- } elsif ($ln >= 2 && scalar(@descs)) {
- printf "|%-${max_size_name}s", "";
- printf "|%-${desc_size}s", shift @descs;
- } else {
- printf "|%-${max_size_name}s", "";
- printf "|%-${desc_size}s", "";
- }
-
- printf "|%-${status_size}s|\n", $line;
-
- $ln++;
- }
- matrix_lines($desc_size, $status_size, 0);
- }
-}
-
-
-#
-# Parses all feature files located at $prefix dir
-#
-find({wanted =>\&parse_feat, no_chdir => 1}, $prefix);
-
-print STDERR Data::Dumper->Dump([\%data], [qw(*data)]) if ($debug);
-
-#
-# Handles the command
-#
-if ($cmd eq "current") {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
-}
-
-if ($cmd eq "ls" or $cmd eq "list") {
- if (!$arch) {
- $arch = qx(uname -m | sed 's/x86_64/x86/' | sed 's/i386/x86/' | sed 's/s390x/s390/');
- $arch =~s/\s+$//;
- }
-
- list_arch_features;
-
- exit;
-}
-
-if ($cmd ne "validate") {
- if ($arch) {
- output_arch_table;
- } elsif ($feat) {
- output_feature;
- } else {
- output_matrix;
- }
-}
-
-__END__
-
-=head1 NAME
-
-get_feat.pl - parse the Linux Feature files and produce a ReST book.
-
-=head1 SYNOPSIS
-
-B<get_feat.pl> [--debug] [--man] [--help] [--dir=<dir>] [--arch=<arch>]
- [--feature=<feature>|--feat=<feature>] <COMAND> [<ARGUMENT>]
-
-Where <COMMAND> can be:
-
-=over 8
-
-B<current> - output table in ReST compatible ASCII format
- with features for this machine's architecture
-
-B<rest> - output table(s) in ReST compatible ASCII format
- with features in ReST markup language. The output
- is affected by --arch or --feat/--feature flags.
-
-B<validate> - validate the contents of the files under
- Documentation/features.
-
-B<ls> or B<list> - list features for this machine's architecture,
- using an easier to parse format.
- The output is affected by --arch flag.
-
-=back
-
-=head1 OPTIONS
-
-=over 8
-
-=item B<--arch>
-
-Output features for an specific architecture, optionally filtering for
-a single specific feature.
-
-=item B<--feat> or B<--feature>
-
-Output features for a single specific feature.
-
-=item B<--dir>
-
-Changes the location of the Feature files. By default, it uses
-the Documentation/features directory.
-
-=item B<--enable-fname>
-
-Prints the file name of the feature files. This can be used in order to
-track dependencies during documentation build.
-
-=item B<--debug>
-
-Put the script in verbose mode, useful for debugging. Can be called multiple
-times, to increase verbosity.
-
-=item B<--help>
-
-Prints a brief help message and exits.
-
-=item B<--man>
-
-Prints the manual page and exits.
-
-=back
-
-=head1 DESCRIPTION
-
-Parse the Linux feature files from Documentation/features (by default),
-optionally producing results at ReST format.
-
-It supports output data per architecture, per feature or a
-feature x arch matrix.
-
-When used with B<rest> command, it will use either one of the tree formats:
-
-If neither B<--arch> or B<--feature> arguments are used, it will output a
-matrix with features per architecture.
-
-If B<--arch> argument is used, it will output the features availability for
-a given architecture.
-
-If B<--feat> argument is used, it will output the content of the feature
-file using ReStructured Text markup.
-
-=head1 BUGS
-
-Report bugs to Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
-
-=head1 COPYRIGHT
-
-Copyright (c) 2019 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
-
-License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
-
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.
-
-=cut
diff --git a/scripts/kernel-doc.pl b/scripts/kernel-doc.pl
deleted file mode 100755
index 5db23cbf4eb2..000000000000
--- a/scripts/kernel-doc.pl
+++ /dev/null
@@ -1,2439 +0,0 @@
-#!/usr/bin/env perl
-# SPDX-License-Identifier: GPL-2.0
-# vim: softtabstop=4
-
-use warnings;
-use strict;
-
-## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
-## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ##
-## Copyright (C) 2001 Simon Huggins ##
-## Copyright (C) 2005-2012 Randy Dunlap ##
-## Copyright (C) 2012 Dan Luedtke ##
-## ##
-## #define enhancements by Armin Kuster <akuster@mvista.com> ##
-## Copyright (c) 2000 MontaVista Software, Inc. ##
-#
-# Copyright (C) 2022 Tomasz Warniełło (POD)
-
-use Pod::Usage qw/pod2usage/;
-
-=head1 NAME
-
-kernel-doc - Print formatted kernel documentation to stdout
-
-=head1 SYNOPSIS
-
- kernel-doc [-h] [-v] [-Werror] [-Wall] [-Wreturn] [-Wshort-desc[ription]] [-Wcontents-before-sections]
- [ -man |
- -rst [-enable-lineno] |
- -none
- ]
- [
- -export |
- -internal |
- [-function NAME] ... |
- [-nosymbol NAME] ...
- ]
- [-no-doc-sections]
- [-export-file FILE] ...
- FILE ...
-
-Run `kernel-doc -h` for details.
-
-=head1 DESCRIPTION
-
-Read C language source or header FILEs, extract embedded documentation comments,
-and print formatted documentation to standard output.
-
-The documentation comments are identified by the "/**" opening comment mark.
-
-See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
-
-=cut
-
-# more perldoc at the end of the file
-
-## init lots of data
-
-my $errors = 0;
-my $warnings = 0;
-my $anon_struct_union = 0;
-
-# match expressions used to find embedded type information
-my $type_constant = '\b``([^\`]+)``\b';
-my $type_constant2 = '\%([-_*\w]+)';
-my $type_func = '(\w+)\(\)';
-my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
-my $type_param_ref = '([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)';
-my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params
-my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params
-my $type_env = '(\$\w+)';
-my $type_enum = '\&(enum\s*([_\w]+))';
-my $type_struct = '\&(struct\s*([_\w]+))';
-my $type_typedef = '\&(typedef\s*([_\w]+))';
-my $type_union = '\&(union\s*([_\w]+))';
-my $type_member = '\&([_\w]+)(\.|->)([_\w]+)';
-my $type_fallback = '\&([_\w]+)';
-my $type_member_func = $type_member . '\(\)';
-
-# Output conversion substitutions.
-# One for each output format
-
-# these are pretty rough
-my @highlights_man = (
- [$type_constant, "\$1"],
- [$type_constant2, "\$1"],
- [$type_func, "\\\\fB\$1\\\\fP"],
- [$type_enum, "\\\\fI\$1\\\\fP"],
- [$type_struct, "\\\\fI\$1\\\\fP"],
- [$type_typedef, "\\\\fI\$1\\\\fP"],
- [$type_union, "\\\\fI\$1\\\\fP"],
- [$type_param, "\\\\fI\$1\\\\fP"],
- [$type_param_ref, "\\\\fI\$1\$2\\\\fP"],
- [$type_member, "\\\\fI\$1\$2\$3\\\\fP"],
- [$type_fallback, "\\\\fI\$1\\\\fP"]
- );
-my $blankline_man = "";
-
-# rst-mode
-my @highlights_rst = (
- [$type_constant, "``\$1``"],
- [$type_constant2, "``\$1``"],
-
- # Note: need to escape () to avoid func matching later
- [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"],
- [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"],
- [$type_fp_param, "**\$1\\\\(\\\\)**"],
- [$type_fp_param2, "**\$1\\\\(\\\\)**"],
- [$type_func, "\$1()"],
- [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"],
- [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"],
-
- # in rst this can refer to any type
- [$type_fallback, "\\:c\\:type\\:`\$1`"],
- [$type_param_ref, "**\$1\$2**"]
- );
-my $blankline_rst = "\n";
-
-# read arguments
-if ($#ARGV == -1) {
- pod2usage(
- -message => "No arguments!\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
-}
-
-my $kernelversion;
-
-my $dohighlight = "";
-
-my $verbose = 0;
-my $Werror = 0;
-my $Wreturn = 0;
-my $Wshort_desc = 0;
-my $output_mode = "rst";
-my $output_preformatted = 0;
-my $no_doc_sections = 0;
-my $enable_lineno = 0;
-my @highlights = @highlights_rst;
-my $blankline = $blankline_rst;
-my $modulename = "Kernel API";
-
-use constant {
- OUTPUT_ALL => 0, # output all symbols and doc sections
- OUTPUT_INCLUDE => 1, # output only specified symbols
- OUTPUT_EXPORTED => 2, # output exported symbols
- OUTPUT_INTERNAL => 3, # output non-exported symbols
-};
-my $output_selection = OUTPUT_ALL;
-my $show_not_found = 0; # No longer used
-
-my @export_file_list;
-
-my @build_time;
-if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) &&
- (my $seconds = `date -d "${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') {
- @build_time = gmtime($seconds);
-} else {
- @build_time = localtime;
-}
-
-my $man_date = ('January', 'February', 'March', 'April', 'May', 'June',
- 'July', 'August', 'September', 'October',
- 'November', 'December')[$build_time[4]] .
- " " . ($build_time[5]+1900);
-
-# Essentially these are globals.
-# They probably want to be tidied up, made more localised or something.
-# CAVEAT EMPTOR! Some of the others I localised may not want to be, which
-# could cause "use of undefined value" or other bugs.
-my ($function, %function_table, %parametertypes, $declaration_purpose);
-my %nosymbol_table = ();
-my $declaration_start_line;
-my ($type, $declaration_name, $return_type);
-my ($newsection, $newcontents, $prototype, $brcount);
-
-if (defined($ENV{'KBUILD_VERBOSE'}) && $ENV{'KBUILD_VERBOSE'} =~ '1') {
- $verbose = 1;
-}
-
-if (defined($ENV{'KCFLAGS'})) {
- my $kcflags = "$ENV{'KCFLAGS'}";
-
- if ($kcflags =~ /(\s|^)-Werror(\s|$)/) {
- $Werror = 1;
- }
-}
-
-# reading this variable is for backwards compat just in case
-# someone was calling it with the variable from outside the
-# kernel's build system
-if (defined($ENV{'KDOC_WERROR'})) {
- $Werror = "$ENV{'KDOC_WERROR'}";
-}
-# other environment variables are converted to command-line
-# arguments in cmd_checkdoc in the build system
-
-# Generated docbook code is inserted in a template at a point where
-# docbook v3.1 requires a non-zero sequence of RefEntry's; see:
-# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html
-# We keep track of number of generated entries and generate a dummy
-# if needs be to ensure the expanded template can be postprocessed
-# into html.
-my $section_counter = 0;
-
-my $lineprefix="";
-
-# Parser states
-use constant {
- STATE_NORMAL => 0, # normal code
- STATE_NAME => 1, # looking for function name
- STATE_BODY_MAYBE => 2, # body - or maybe more description
- STATE_BODY => 3, # the body of the comment
- STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line
- STATE_PROTO => 5, # scanning prototype
- STATE_DOCBLOCK => 6, # documentation block
- STATE_INLINE => 7, # gathering doc outside main block
-};
-my $state;
-my $leading_space;
-
-# Inline documentation state
-use constant {
- STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE)
- STATE_INLINE_NAME => 1, # looking for member name (@foo:)
- STATE_INLINE_TEXT => 2, # looking for member documentation
- STATE_INLINE_END => 3, # done
- STATE_INLINE_ERROR => 4, # error - Comment without header was found.
- # Spit a warning as it's not
- # proper kernel-doc and ignore the rest.
-};
-my $inline_doc_state;
-
-#declaration types: can be
-# 'function', 'struct', 'union', 'enum', 'typedef'
-my $decl_type;
-
-# Name of the kernel-doc identifier for non-DOC markups
-my $identifier;
-
-my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start.
-my $doc_end = '\*/';
-my $doc_com = '\s*\*\s*';
-my $doc_com_body = '\s*\* ?';
-my $doc_decl = $doc_com . '(\w+)';
-# @params and a strictly limited set of supported section names
-# Specifically:
-# Match @word:
-# @...:
-# @{section-name}:
-# while trying to not match literal block starts like "example::"
-#
-my $doc_sect = $doc_com .
- '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$';
-my $doc_content = $doc_com_body . '(.*)';
-my $doc_block = $doc_com . 'DOC:\s*(.*)?';
-my $doc_inline_start = '^\s*/\*\*\s*$';
-my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)';
-my $doc_inline_end = '^\s*\*/\s*$';
-my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$';
-my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;';
-my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*;';
-my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)};
-my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i;
-
-my %parameterdescs;
-my %parameterdesc_start_lines;
-my @parameterlist;
-my %sections;
-my @sectionlist;
-my %section_start_lines;
-my $sectcheck;
-my $struct_actual;
-
-my $contents = "";
-my $new_start_line = 0;
-
-# the canonical section names. see also $doc_sect above.
-my $section_default = "Description"; # default section
-my $section_intro = "Introduction";
-my $section = $section_default;
-my $section_context = "Context";
-my $section_return = "Return";
-
-my $undescribed = "-- undescribed --";
-
-reset_state();
-
-while ($ARGV[0] =~ m/^--?(.*)/) {
- my $cmd = $1;
- shift @ARGV;
- if ($cmd eq "man") {
- $output_mode = "man";
- @highlights = @highlights_man;
- $blankline = $blankline_man;
- } elsif ($cmd eq "rst") {
- $output_mode = "rst";
- @highlights = @highlights_rst;
- $blankline = $blankline_rst;
- } elsif ($cmd eq "none") {
- $output_mode = "none";
- } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document
- $modulename = shift @ARGV;
- } elsif ($cmd eq "function") { # to only output specific functions
- $output_selection = OUTPUT_INCLUDE;
- $function = shift @ARGV;
- $function_table{$function} = 1;
- } elsif ($cmd eq "nosymbol") { # Exclude specific symbols
- my $symbol = shift @ARGV;
- $nosymbol_table{$symbol} = 1;
- } elsif ($cmd eq "export") { # only exported symbols
- $output_selection = OUTPUT_EXPORTED;
- %function_table = ();
- } elsif ($cmd eq "internal") { # only non-exported symbols
- $output_selection = OUTPUT_INTERNAL;
- %function_table = ();
- } elsif ($cmd eq "export-file") {
- my $file = shift @ARGV;
- push(@export_file_list, $file);
- } elsif ($cmd eq "v") {
- $verbose = 1;
- } elsif ($cmd eq "Werror") {
- $Werror = 1;
- } elsif ($cmd eq "Wreturn") {
- $Wreturn = 1;
- } elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") {
- $Wshort_desc = 1;
- } elsif ($cmd eq "Wall") {
- $Wreturn = 1;
- $Wshort_desc = 1;
- } elsif (($cmd eq "h") || ($cmd eq "help")) {
- pod2usage(-exitval => 0, -verbose => 2);
- } elsif ($cmd eq 'no-doc-sections') {
- $no_doc_sections = 1;
- } elsif ($cmd eq 'enable-lineno') {
- $enable_lineno = 1;
- } elsif ($cmd eq 'show-not-found') {
- $show_not_found = 1; # A no-op but don't fail
- } else {
- # Unknown argument
- pod2usage(
- -message => "Argument unknown!\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
- }
- if ($#ARGV < 0){
- pod2usage(
- -message => "FILE argument missing\n",
- -exitval => 1,
- -verbose => 99,
- -sections => 'SYNOPSIS',
- -output => \*STDERR,
- );
- }
-}
-
-# continue execution near EOF;
-
-sub findprog($)
-{
- foreach(split(/:/, $ENV{PATH})) {
- return "$_/$_[0]" if(-x "$_/$_[0]");
- }
-}
-
-# get kernel version from env
-sub get_kernel_version() {
- my $version = 'unknown kernel version';
-
- if (defined($ENV{'KERNELVERSION'})) {
- $version = $ENV{'KERNELVERSION'};
- }
- return $version;
-}
-
-#
-sub print_lineno {
- my $lineno = shift;
- if ($enable_lineno && defined($lineno)) {
- print ".. LINENO " . $lineno . "\n";
- }
-}
-
-sub emit_warning {
- my $location = shift;
- my $msg = shift;
- print STDERR "$location: warning: $msg";
- ++$warnings;
-}
-##
-# dumps section contents to arrays/hashes intended for that purpose.
-#
-sub dump_section {
- my $file = shift;
- my $name = shift;
- my $contents = join "\n", @_;
-
- if ($name =~ m/$type_param/) {
- $name = $1;
- $parameterdescs{$name} = $contents;
- $sectcheck = $sectcheck . $name . " ";
- $parameterdesc_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- } elsif ($name eq "@\.\.\.") {
- $name = "...";
- $parameterdescs{$name} = $contents;
- $sectcheck = $sectcheck . $name . " ";
- $parameterdesc_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- } else {
- if (defined($sections{$name}) && ($sections{$name} ne "")) {
- # Only warn on user specified duplicate section names.
- if ($name ne $section_default) {
- emit_warning("${file}:$.", "duplicate section name '$name'\n");
- }
- $sections{$name} .= $contents;
- } else {
- $sections{$name} = $contents;
- push @sectionlist, $name;
- $section_start_lines{$name} = $new_start_line;
- $new_start_line = 0;
- }
- }
-}
-
-##
-# dump DOC: section after checking that it should go out
-#
-sub dump_doc_section {
- my $file = shift;
- my $name = shift;
- my $contents = join "\n", @_;
-
- if ($no_doc_sections) {
- return;
- }
-
- return if (defined($nosymbol_table{$name}));
-
- if (($output_selection == OUTPUT_ALL) ||
- (($output_selection == OUTPUT_INCLUDE) &&
- defined($function_table{$name})))
- {
- dump_section($file, $name, $contents);
- output_blockhead({'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'module' => $modulename,
- 'content-only' => ($output_selection != OUTPUT_ALL), });
- }
-}
-
-##
-# output function
-#
-# parameterdescs, a hash.
-# function => "function name"
-# parameterlist => @list of parameters
-# parameterdescs => %parameter descriptions
-# sectionlist => @list of sections
-# sections => %section descriptions
-#
-
-sub output_highlight {
- my $contents = join "\n",@_;
- my $line;
-
-# DEBUG
-# if (!defined $contents) {
-# use Carp;
-# confess "output_highlight got called with no args?\n";
-# }
-
-# print STDERR "contents b4:$contents\n";
- eval $dohighlight;
- die $@ if $@;
-# print STDERR "contents af:$contents\n";
-
- foreach $line (split "\n", $contents) {
- if (! $output_preformatted) {
- $line =~ s/^\s*//;
- }
- if ($line eq ""){
- if (! $output_preformatted) {
- print $lineprefix, $blankline;
- }
- } else {
- if ($output_mode eq "man" && substr($line, 0, 1) eq ".") {
- print "\\&$line";
- } else {
- print $lineprefix, $line;
- }
- }
- print "\n";
- }
-}
-
-##
-# output function in man
-sub output_function_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
- my $func_macro = $args{'func_macro'};
- my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
-
- print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print $args{'function'} . " \\- " . $args{'purpose'} . "\n";
-
- print ".SH SYNOPSIS\n";
- if ($args{'functiontype'} ne "") {
- print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n";
- } else {
- print ".B \"" . $args{'function'} . "\n";
- }
- $count = 0;
- my $parenth = "(";
- my $post = ",";
- foreach my $parameter (@{$args{'parameterlist'}}) {
- if ($count == $#{$args{'parameterlist'}}) {
- $post = ");";
- }
- $type = $args{'parametertypes'}{$parameter};
- if ($type =~ m/$function_pointer/) {
- # pointer-to-function
- print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n";
- } else {
- $type =~ s/([^\*])$/$1 /;
- print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n";
- }
- $count++;
- $parenth = "";
- }
-
- $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
- if ($paramcount >= 0) {
- print ".SH ARGUMENTS\n";
- }
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"", uc $section, "\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output enum in man
-sub output_enum_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
-
- print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n";
-
- print ".SH SYNOPSIS\n";
- print "enum " . $args{'enum'} . " {\n";
- $count = 0;
- foreach my $parameter (@{$args{'parameterlist'}}) {
- print ".br\n.BI \" $parameter\"\n";
- if ($count == $#{$args{'parameterlist'}}) {
- print "\n};\n";
- last;
- } else {
- print ", \n.br\n";
- }
- $count++;
- }
-
- print ".SH Constants\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output struct in man
-sub output_struct_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n";
-
- my $declaration = $args{'definition'};
- $declaration =~ s/\t/ /g;
- $declaration =~ s/\n/"\n.br\n.BI \"/g;
- print ".SH SYNOPSIS\n";
- print $args{'type'} . " " . $args{'struct'} . " {\n.br\n";
- print ".BI \"$declaration\n};\n.br\n\n";
-
- print ".SH Members\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- ($parameter =~ /^#/) && next;
-
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
- print ".IP \"" . $parameter . "\" 12\n";
- output_highlight($args{'parameterdescs'}{$parameter_name});
- }
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output typedef in man
-sub output_typedef_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- print ".SH NAME\n";
- print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n";
-
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-sub output_blockhead_man(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $count;
-
- print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n";
-
- foreach $section (@{$args{'sectionlist'}}) {
- print ".SH \"$section\"\n";
- output_highlight($args{'sections'}{$section});
- }
-}
-
-##
-# output in restructured text
-#
-
-#
-# This could use some work; it's used to output the DOC: sections, and
-# starts by putting out the name of the doc section itself, but that tends
-# to duplicate a header already in the template file.
-#
-sub output_blockhead_rst(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
-
- foreach $section (@{$args{'sectionlist'}}) {
- next if (defined($nosymbol_table{$section}));
-
- if ($output_selection != OUTPUT_INCLUDE) {
- print ".. _$section:\n\n";
- print "**$section**\n\n";
- }
- print_lineno($section_start_lines{$section});
- output_highlight_rst($args{'sections'}{$section});
- print "\n";
- }
-}
-
-#
-# Apply the RST highlights to a sub-block of text.
-#
-sub highlight_block($) {
- # The dohighlight kludge requires the text be called $contents
- my $contents = shift;
- eval $dohighlight;
- die $@ if $@;
- return $contents;
-}
-
-#
-# Regexes used only here.
-#
-my $sphinx_literal = '^[^.].*::$';
-my $sphinx_cblock = '^\.\.\ +code-block::';
-
-sub output_highlight_rst {
- my $input = join "\n",@_;
- my $output = "";
- my $line;
- my $in_literal = 0;
- my $litprefix;
- my $block = "";
-
- foreach $line (split "\n",$input) {
- #
- # If we're in a literal block, see if we should drop out
- # of it. Otherwise pass the line straight through unmunged.
- #
- if ($in_literal) {
- if (! ($line =~ /^\s*$/)) {
- #
- # If this is the first non-blank line in a literal
- # block we need to figure out what the proper indent is.
- #
- if ($litprefix eq "") {
- $line =~ /^(\s*)/;
- $litprefix = '^' . $1;
- $output .= $line . "\n";
- } elsif (! ($line =~ /$litprefix/)) {
- $in_literal = 0;
- } else {
- $output .= $line . "\n";
- }
- } else {
- $output .= $line . "\n";
- }
- }
- #
- # Not in a literal block (or just dropped out)
- #
- if (! $in_literal) {
- $block .= $line . "\n";
- if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) {
- $in_literal = 1;
- $litprefix = "";
- $output .= highlight_block($block);
- $block = ""
- }
- }
- }
-
- if ($block) {
- $output .= highlight_block($block);
- }
-
- $output =~ s/^\n+//g;
- $output =~ s/\n+$//g;
-
- foreach $line (split "\n", $output) {
- print $lineprefix . $line . "\n";
- }
-}
-
-sub output_function_rst(%) {
- my %args = %{$_[0]};
- my ($parameter, $section);
- my $oldprefix = $lineprefix;
-
- my $signature = "";
- my $func_macro = $args{'func_macro'};
- my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty
-
- if ($func_macro) {
- $signature = $args{'function'};
- } else {
- if ($args{'functiontype'}) {
- $signature = $args{'functiontype'} . " ";
- }
- $signature .= $args{'function'} . " (";
- }
-
- my $count = 0;
- foreach my $parameter (@{$args{'parameterlist'}}) {
- if ($count ne 0) {
- $signature .= ", ";
- }
- $count++;
- $type = $args{'parametertypes'}{$parameter};
-
- if ($type =~ m/$function_pointer/) {
- # pointer-to-function
- $signature .= $1 . $parameter . ") (" . $2 . ")";
- } else {
- $signature .= $type;
- }
- }
-
- if (!$func_macro) {
- $signature .= ")";
- }
-
- if ($args{'typedef'} || $args{'functiontype'} eq "") {
- print ".. c:macro:: ". $args{'function'} . "\n\n";
-
- if ($args{'typedef'}) {
- print_lineno($declaration_start_line);
- print " **Typedef**: ";
- $lineprefix = "";
- output_highlight_rst($args{'purpose'});
- print "\n\n**Syntax**\n\n";
- print " ``$signature``\n\n";
- } else {
- print "``$signature``\n\n";
- }
- } else {
- print ".. c:function:: $signature\n\n";
- }
-
- if (!$args{'typedef'}) {
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
- }
-
- #
- # Put our descriptive text into a container (thus an HTML <div>) to help
- # set the function prototypes apart.
- #
- $lineprefix = " ";
- if ($paramcount >= 0) {
- print ".. container:: kernelindent\n\n";
- print $lineprefix . "**Parameters**\n\n";
- }
- foreach $parameter (@{$args{'parameterlist'}}) {
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
- $type = $args{'parametertypes'}{$parameter};
-
- if ($type ne "") {
- print $lineprefix . "``$type``\n";
- } else {
- print $lineprefix . "``$parameter``\n";
- }
-
- print_lineno($parameterdesc_start_lines{$parameter_name});
-
- $lineprefix = " ";
- if (defined($args{'parameterdescs'}{$parameter_name}) &&
- $args{'parameterdescs'}{$parameter_name} ne $undescribed) {
- output_highlight_rst($args{'parameterdescs'}{$parameter_name});
- } else {
- print $lineprefix . "*undescribed*\n";
- }
- $lineprefix = " ";
- print "\n";
- }
-
- output_section_rst(@_);
- $lineprefix = $oldprefix;
-}
-
-sub output_section_rst(%) {
- my %args = %{$_[0]};
- my $section;
- my $oldprefix = $lineprefix;
-
- foreach $section (@{$args{'sectionlist'}}) {
- print $lineprefix . "**$section**\n\n";
- print_lineno($section_start_lines{$section});
- output_highlight_rst($args{'sections'}{$section});
- print "\n";
- }
- print "\n";
-}
-
-sub output_enum_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
- my $count;
- my $outer;
-
- my $name = $args{'enum'};
- print "\n\n.. c:enum:: " . $name . "\n\n";
-
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- print ".. container:: kernelindent\n\n";
- $outer = $lineprefix . " ";
- $lineprefix = $outer . " ";
- print $outer . "**Constants**\n\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- print $outer . "``$parameter``\n";
-
- if ($args{'parameterdescs'}{$parameter} ne $undescribed) {
- output_highlight_rst($args{'parameterdescs'}{$parameter});
- } else {
- print $lineprefix . "*undescribed*\n";
- }
- print "\n";
- }
- print "\n";
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-sub output_typedef_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
- my $name;
-
- $name = $args{'typedef'};
-
- print "\n\n.. c:type:: " . $name . "\n\n";
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-sub output_struct_rst(%) {
- my %args = %{$_[0]};
- my ($parameter);
- my $oldprefix = $lineprefix;
-
- my $name = $args{'struct'};
- if ($args{'type'} eq 'union') {
- print "\n\n.. c:union:: " . $name . "\n\n";
- } else {
- print "\n\n.. c:struct:: " . $name . "\n\n";
- }
-
- print_lineno($declaration_start_line);
- $lineprefix = " ";
- output_highlight_rst($args{'purpose'});
- print "\n";
-
- print ".. container:: kernelindent\n\n";
- print $lineprefix . "**Definition**::\n\n";
- my $declaration = $args{'definition'};
- $lineprefix = $lineprefix . " ";
- $declaration =~ s/\t/$lineprefix/g;
- print $lineprefix . $args{'type'} . " " . $args{'struct'} . " {\n$declaration" . $lineprefix . "};\n\n";
-
- $lineprefix = " ";
- print $lineprefix . "**Members**\n\n";
- foreach $parameter (@{$args{'parameterlist'}}) {
- ($parameter =~ /^#/) && next;
-
- my $parameter_name = $parameter;
- $parameter_name =~ s/\[.*//;
-
- ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
- $type = $args{'parametertypes'}{$parameter};
- print_lineno($parameterdesc_start_lines{$parameter_name});
- print $lineprefix . "``" . $parameter . "``\n";
- $lineprefix = " ";
- output_highlight_rst($args{'parameterdescs'}{$parameter_name});
- $lineprefix = " ";
- print "\n";
- }
- print "\n";
-
- $lineprefix = $oldprefix;
- output_section_rst(@_);
-}
-
-## none mode output functions
-
-sub output_function_none(%) {
-}
-
-sub output_enum_none(%) {
-}
-
-sub output_typedef_none(%) {
-}
-
-sub output_struct_none(%) {
-}
-
-sub output_blockhead_none(%) {
-}
-
-##
-# generic output function for all types (function, struct/union, typedef, enum);
-# calls the generated, variable output_ function name based on
-# functype and output_mode
-sub output_declaration {
- no strict 'refs';
- my $name = shift;
- my $functype = shift;
- my $func = "output_${functype}_$output_mode";
-
- return if (defined($nosymbol_table{$name}));
-
- if (($output_selection == OUTPUT_ALL) ||
- (($output_selection == OUTPUT_INCLUDE ||
- $output_selection == OUTPUT_EXPORTED) &&
- defined($function_table{$name})) ||
- ($output_selection == OUTPUT_INTERNAL &&
- !($functype eq "function" && defined($function_table{$name}))))
- {
- &$func(@_);
- $section_counter++;
- }
-}
-
-##
-# generic output function - calls the right one based on current output mode.
-sub output_blockhead {
- no strict 'refs';
- my $func = "output_blockhead_" . $output_mode;
- &$func(@_);
- $section_counter++;
-}
-
-##
-# takes a declaration (struct, union, enum, typedef) and
-# invokes the right handler. NOT called for functions.
-sub dump_declaration($$) {
- no strict 'refs';
- my ($prototype, $file) = @_;
- my $func = "dump_" . $decl_type;
- &$func(@_);
-}
-
-sub dump_union($$) {
- dump_struct(@_);
-}
-
-sub dump_struct($$) {
- my $x = shift;
- my $file = shift;
- my $decl_type;
- my $members;
- my $type = qr{struct|union};
- # For capturing struct/union definition body, i.e. "{members*}qualifiers*"
- my $qualifiers = qr{$attribute|__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned};
- my $definition_body = qr{\{(.*)\}\s*$qualifiers*};
- my $struct_members = qr{($type)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;};
-
- if ($x =~ /($type)\s+(\w+)\s*$definition_body/) {
- $decl_type = $1;
- $declaration_name = $2;
- $members = $3;
- } elsif ($x =~ /typedef\s+($type)\s*$definition_body\s*(\w+)\s*;/) {
- $decl_type = $1;
- $declaration_name = $3;
- $members = $2;
- }
-
- if ($members) {
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n");
- return;
- }
-
- # ignore members marked private:
- $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
- $members =~ s/\/\*\s*private:.*//gosi;
- # strip comments:
- $members =~ s/\/\*.*?\*\///gos;
- # strip attributes
- $members =~ s/\s*$attribute/ /gi;
- $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__counted_by\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__counted_by_(le|be)\s*\([^;]*\)/ /gos;
- $members =~ s/\s*__packed\s*/ /gos;
- $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos;
- $members =~ s/\s*____cacheline_aligned_in_smp/ /gos;
- $members =~ s/\s*____cacheline_aligned/ /gos;
- # unwrap struct_group():
- # - first eat non-declaration parameters and rewrite for final match
- # - then remove macro, outer parens, and trailing semicolon
- $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos;
- $members =~ s/\bstruct_group_attr\s*\(([^,]*,){2}/STRUCT_GROUP(/gos;
- $members =~ s/\bstruct_group_tagged\s*\(([^,]*),([^,]*),/struct $1 $2; STRUCT_GROUP(/gos;
- $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos;
- $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos;
-
- my $args = qr{([^,)]+)};
- # replace DECLARE_BITMAP
- $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos;
- $members =~ s/DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, PHY_INTERFACE_MODE_MAX)/gos;
- $members =~ s/DECLARE_BITMAP\s*\($args,\s*$args\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos;
- # replace DECLARE_HASHTABLE
- $members =~ s/DECLARE_HASHTABLE\s*\($args,\s*$args\)/unsigned long $1\[1 << (($2) - 1)\]/gos;
- # replace DECLARE_KFIFO
- $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos;
- # replace DECLARE_KFIFO_PTR
- $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos;
- # replace DECLARE_FLEX_ARRAY
- $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos;
- #replace DEFINE_DMA_UNMAP_ADDR
- $members =~ s/DEFINE_DMA_UNMAP_ADDR\s*\($args\)/dma_addr_t $1/gos;
- #replace DEFINE_DMA_UNMAP_LEN
- $members =~ s/DEFINE_DMA_UNMAP_LEN\s*\($args\)/__u32 $1/gos;
- my $declaration = $members;
-
- # Split nested struct/union elements as newer ones
- while ($members =~ m/$struct_members/) {
- my $newmember;
- my $maintype = $1;
- my $ids = $4;
- my $content = $3;
- foreach my $id(split /,/, $ids) {
- $newmember .= "$maintype $id; ";
-
- $id =~ s/[:\[].*//;
- $id =~ s/^\s*\**(\S+)\s*/$1/;
- foreach my $arg (split /;/, $content) {
- next if ($arg =~ m/^\s*$/);
- if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) {
- # pointer-to-function
- my $type = $1;
- my $name = $2;
- my $extra = $3;
- next if (!$name);
- if ($id =~ m/^\s*$/) {
- # anonymous struct/union
- $newmember .= "$type$name$extra; ";
- } else {
- $newmember .= "$type$id.$name$extra; ";
- }
- } else {
- my $type;
- my $names;
- $arg =~ s/^\s+//;
- $arg =~ s/\s+$//;
- # Handle bitmaps
- $arg =~ s/:\s*\d+\s*//g;
- # Handle arrays
- $arg =~ s/\[.*\]//g;
- # The type may have multiple words,
- # and multiple IDs can be defined, like:
- # const struct foo, *bar, foobar
- # So, we remove spaces when parsing the
- # names, in order to match just names
- # and commas for the names
- $arg =~ s/\s*,\s*/,/g;
- if ($arg =~ m/(.*)\s+([\S+,]+)/) {
- $type = $1;
- $names = $2;
- } else {
- $newmember .= "$arg; ";
- next;
- }
- foreach my $name (split /,/, $names) {
- $name =~ s/^\s*\**(\S+)\s*/$1/;
- next if (($name =~ m/^\s*$/));
- if ($id =~ m/^\s*$/) {
- # anonymous struct/union
- $newmember .= "$type $name; ";
- } else {
- $newmember .= "$type $id.$name; ";
- }
- }
- }
- }
- }
- $members =~ s/$struct_members/$newmember/;
- }
-
- # Ignore other nested elements, like enums
- $members =~ s/(\{[^\{\}]*\})//g;
-
- create_parameterlist($members, ';', $file, $declaration_name);
- check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual);
-
- # Adjust declaration for better display
- $declaration =~ s/([\{;])/$1\n/g;
- $declaration =~ s/\}\s+;/};/g;
- # Better handle inlined enums
- do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/);
-
- my @def_args = split /\n/, $declaration;
- my $level = 1;
- $declaration = "";
- foreach my $clause (@def_args) {
- $clause =~ s/^\s+//;
- $clause =~ s/\s+$//;
- $clause =~ s/\s+/ /;
- next if (!$clause);
- $level-- if ($clause =~ m/(\})/ && $level > 1);
- if (!($clause =~ m/^\s*#/)) {
- $declaration .= "\t" x $level;
- }
- $declaration .= "\t" . $clause . "\n";
- $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/));
- }
- output_declaration($declaration_name,
- 'struct',
- {'struct' => $declaration_name,
- 'module' => $modulename,
- 'definition' => $declaration,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'type' => $decl_type
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse struct or union!\n";
- ++$errors;
- }
-}
-
-
-sub show_warnings($$) {
- my $functype = shift;
- my $name = shift;
-
- return 0 if (defined($nosymbol_table{$name}));
-
- return 1 if ($output_selection == OUTPUT_ALL);
-
- if ($output_selection == OUTPUT_EXPORTED) {
- if (defined($function_table{$name})) {
- return 1;
- } else {
- return 0;
- }
- }
- if ($output_selection == OUTPUT_INTERNAL) {
- if (!($functype eq "function" && defined($function_table{$name}))) {
- return 1;
- } else {
- return 0;
- }
- }
- if ($output_selection == OUTPUT_INCLUDE) {
- if (defined($function_table{$name})) {
- return 1;
- } else {
- return 0;
- }
- }
- die("Please add the new output type at show_warnings()");
-}
-
-sub dump_enum($$) {
- my $x = shift;
- my $file = shift;
- my $members;
-
- # ignore members marked private:
- $x =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi;
- $x =~ s/\/\*\s*private:.*}/}/gosi;
-
- $x =~ s@/\*.*?\*/@@gos; # strip comments.
- # strip #define macros inside enums
- $x =~ s@#\s*((define|ifdef|if)\s+|endif)[^;]*;@@gos;
-
- if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) {
- $declaration_name = $2;
- $members = $1;
- } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) {
- $declaration_name = $1;
- $members = $2;
- }
-
- if ($members) {
- if ($identifier ne $declaration_name) {
- if ($identifier eq "") {
- emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n");
- } else {
- emit_warning("${file}:$.", "expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n");
- }
- return;
- }
- $declaration_name = "(anonymous)" if ($declaration_name eq "");
-
- my %_members;
-
- $members =~ s/\s+$//;
- $members =~ s/\([^;]*?[\)]//g;
-
- foreach my $arg (split ',', $members) {
- $arg =~ s/^\s*(\w+).*/$1/;
- push @parameterlist, $arg;
- if (!$parameterdescs{$arg}) {
- $parameterdescs{$arg} = $undescribed;
- if (show_warnings("enum", $declaration_name)) {
- emit_warning("${file}:$.", "Enum value '$arg' not described in enum '$declaration_name'\n");
- }
- }
- $_members{$arg} = 1;
- }
-
- while (my ($k, $v) = each %parameterdescs) {
- if (!exists($_members{$k})) {
- if (show_warnings("enum", $declaration_name)) {
- emit_warning("${file}:$.", "Excess enum value '$k' description in '$declaration_name'\n");
- }
- }
- }
-
- output_declaration($declaration_name,
- 'enum',
- {'enum' => $declaration_name,
- 'module' => $modulename,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse enum!\n";
- ++$errors;
- }
-}
-
-my $typedef_type = qr { ((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s* }x;
-my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x;
-my $typedef_args = qr { \s*\((.*)\); }x;
-
-my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x;
-my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x;
-
-sub dump_typedef($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@/\*.*?\*/@@gos; # strip comments.
-
- # Parse function typedef prototypes
- if ($x =~ $typedef1 || $x =~ $typedef2) {
- $return_type = $1;
- $declaration_name = $2;
- my $args = $3;
- $return_type =~ s/^\s+//;
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
- return;
- }
-
- create_parameterlist($args, ',', $file, $declaration_name);
-
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'typedef' => 1,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- return;
- }
-
- while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) {
- $x =~ s/\(*.\)\s*;$/;/;
- $x =~ s/\[*.\]\s*;$/;/;
- }
-
- if ($x =~ /typedef.*\s+(\w+)\s*;/) {
- $declaration_name = $1;
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n");
- return;
- }
-
- output_declaration($declaration_name,
- 'typedef',
- {'typedef' => $declaration_name,
- 'module' => $modulename,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose
- });
- } else {
- print STDERR "${file}:$.: error: Cannot parse typedef!\n";
- ++$errors;
- }
-}
-
-sub save_struct_actual($) {
- my $actual = shift;
-
- # strip all spaces from the actual param so that it looks like one string item
- $actual =~ s/\s*//g;
- $struct_actual = $struct_actual . $actual . " ";
-}
-
-sub create_parameterlist($$$$) {
- my $args = shift;
- my $splitter = shift;
- my $file = shift;
- my $declaration_name = shift;
- my $type;
- my $param;
-
- # temporarily replace commas inside function pointer definition
- my $arg_expr = qr{\([^\),]+};
- while ($args =~ /$arg_expr,/) {
- $args =~ s/($arg_expr),/$1#/g;
- }
-
- foreach my $arg (split($splitter, $args)) {
- # strip comments
- $arg =~ s/\/\*.*\*\///;
- # ignore argument attributes
- $arg =~ s/\sPOS0?\s/ /;
- # strip leading/trailing spaces
- $arg =~ s/^\s*//;
- $arg =~ s/\s*$//;
- $arg =~ s/\s+/ /;
-
- if ($arg =~ /^#/) {
- # Treat preprocessor directive as a typeless variable just to fill
- # corresponding data structures "correctly". Catch it later in
- # output_* subs.
- push_parameter($arg, "", "", $file);
- } elsif ($arg =~ m/\(.+\)\s*\(/) {
- # pointer-to-function
- $arg =~ tr/#/,/;
- $arg =~ m/[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)/;
- $param = $1;
- $type = $arg;
- $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- } elsif ($arg =~ m/\(.+\)\s*\[/) {
- # array-of-pointers
- $arg =~ tr/#/,/;
- $arg =~ m/[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)/;
- $param = $1;
- $type = $arg;
- $type =~ s/([^\(]+\(\*?)\s*$param/$1/;
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- } elsif ($arg) {
- $arg =~ s/\s*:\s*/:/g;
- $arg =~ s/\s*\[/\[/g;
-
- my @args = split('\s*,\s*', $arg);
- if ($args[0] =~ m/\*/) {
- $args[0] =~ s/(\*+)\s*/ $1/;
- }
-
- my @first_arg;
- if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) {
- shift @args;
- push(@first_arg, split('\s+', $1));
- push(@first_arg, $2);
- } else {
- @first_arg = split('\s+', shift @args);
- }
-
- unshift(@args, pop @first_arg);
- $type = join " ", @first_arg;
-
- foreach $param (@args) {
- if ($param =~ m/^(\*+)\s*(.*)/) {
- save_struct_actual($2);
-
- push_parameter($2, "$type $1", $arg, $file, $declaration_name);
- } elsif ($param =~ m/(.*?):(\w+)/) {
- if ($type ne "") { # skip unnamed bit-fields
- save_struct_actual($1);
- push_parameter($1, "$type:$2", $arg, $file, $declaration_name)
- }
- } else {
- save_struct_actual($param);
- push_parameter($param, $type, $arg, $file, $declaration_name);
- }
- }
- }
- }
-}
-
-sub push_parameter($$$$$) {
- my $param = shift;
- my $type = shift;
- my $org_arg = shift;
- my $file = shift;
- my $declaration_name = shift;
-
- if (($anon_struct_union == 1) && ($type eq "") &&
- ($param eq "}")) {
- return; # ignore the ending }; from anon. struct/union
- }
-
- $anon_struct_union = 0;
- $param =~ s/[\[\)].*//;
-
- if ($type eq "" && $param =~ /\.\.\.$/)
- {
- if (!$param =~ /\w\.\.\.$/) {
- # handles unnamed variable parameters
- $param = "...";
- } elsif ($param =~ /\w\.\.\.$/) {
- # for named variable parameters of the form `x...`, remove the dots
- $param =~ s/\.\.\.$//;
- }
- if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") {
- $parameterdescs{$param} = "variable arguments";
- }
- }
- elsif ($type eq "" && ($param eq "" or $param eq "void"))
- {
- $param="void";
- $parameterdescs{void} = "no arguments";
- }
- elsif ($type eq "" && ($param eq "struct" or $param eq "union"))
- # handle unnamed (anonymous) union or struct:
- {
- $type = $param;
- $param = "{unnamed_" . $param . "}";
- $parameterdescs{$param} = "anonymous\n";
- $anon_struct_union = 1;
- }
- elsif ($param =~ "__cacheline_group" )
- # handle cache group enforcing variables: they do not need be described in header files
- {
- return; # ignore __cacheline_group_begin and __cacheline_group_end
- }
-
- # warn if parameter has no description
- # (but ignore ones starting with # as these are not parameters
- # but inline preprocessor statements);
- # Note: It will also ignore void params and unnamed structs/unions
- if (!defined $parameterdescs{$param} && $param !~ /^#/) {
- $parameterdescs{$param} = $undescribed;
-
- if (show_warnings($type, $declaration_name) && $param !~ /\./) {
- emit_warning("${file}:$.", "Function parameter or struct member '$param' not described in '$declaration_name'\n");
- }
- }
-
- # strip spaces from $param so that it is one continuous string
- # on @parameterlist;
- # this fixes a problem where check_sections() cannot find
- # a parameter like "addr[6 + 2]" because it actually appears
- # as "addr[6", "+", "2]" on the parameter list;
- # but it's better to maintain the param string unchanged for output,
- # so just weaken the string compare in check_sections() to ignore
- # "[blah" in a parameter string;
- ###$param =~ s/\s*//g;
- push @parameterlist, $param;
- $org_arg =~ s/\s\s+/ /g;
- $parametertypes{$param} = $org_arg;
-}
-
-sub check_sections($$$$$) {
- my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_;
- my @sects = split ' ', $sectcheck;
- my @prms = split ' ', $prmscheck;
- my $err;
- my ($px, $sx);
- my $prm_clean; # strip trailing "[array size]" and/or beginning "*"
-
- foreach $sx (0 .. $#sects) {
- $err = 1;
- foreach $px (0 .. $#prms) {
- $prm_clean = $prms[$px];
- $prm_clean =~ s/\[.*\]//;
- $prm_clean =~ s/$attribute//i;
- # ignore array size in a parameter string;
- # however, the original param string may contain
- # spaces, e.g.: addr[6 + 2]
- # and this appears in @prms as "addr[6" since the
- # parameter list is split at spaces;
- # hence just ignore "[..." for the sections check;
- $prm_clean =~ s/\[.*//;
-
- ##$prm_clean =~ s/^\**//;
- if ($prm_clean eq $sects[$sx]) {
- $err = 0;
- last;
- }
- }
- if ($err) {
- if ($decl_type eq "function") {
- emit_warning("${file}:$.",
- "Excess function parameter " .
- "'$sects[$sx]' " .
- "description in '$decl_name'\n");
- } elsif (($decl_type eq "struct") or
- ($decl_type eq "union")) {
- emit_warning("${file}:$.",
- "Excess $decl_type member " .
- "'$sects[$sx]' " .
- "description in '$decl_name'\n");
- }
- }
- }
-}
-
-##
-# Checks the section describing the return value of a function.
-sub check_return_section {
- my $file = shift;
- my $declaration_name = shift;
- my $return_type = shift;
-
- # Ignore an empty return type (It's a macro)
- # Ignore functions with a "void" return type. (But don't ignore "void *")
- if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) {
- return;
- }
-
- if (!defined($sections{$section_return}) ||
- $sections{$section_return} eq "")
- {
- emit_warning("${file}:$.",
- "No description found for return value of " .
- "'$declaration_name'\n");
- }
-}
-
-##
-# takes a function prototype and the name of the current file being
-# processed and spits out all the details stored in the global
-# arrays/hashes.
-sub dump_function($$) {
- my $prototype = shift;
- my $file = shift;
- my $func_macro = 0;
-
- print_lineno($new_start_line);
-
- $prototype =~ s/^static +//;
- $prototype =~ s/^extern +//;
- $prototype =~ s/^asmlinkage +//;
- $prototype =~ s/^inline +//;
- $prototype =~ s/^__inline__ +//;
- $prototype =~ s/^__inline +//;
- $prototype =~ s/^__always_inline +//;
- $prototype =~ s/^noinline +//;
- $prototype =~ s/^__FORTIFY_INLINE +//;
- $prototype =~ s/__init +//;
- $prototype =~ s/__init_or_module +//;
- $prototype =~ s/__deprecated +//;
- $prototype =~ s/__flatten +//;
- $prototype =~ s/__meminit +//;
- $prototype =~ s/__must_check +//;
- $prototype =~ s/__weak +//;
- $prototype =~ s/__sched +//;
- $prototype =~ s/_noprof//;
- $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//;
- $prototype =~ s/__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +//;
- $prototype =~ s/__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +//;
- $prototype =~ s/DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)/$1, $2/;
- my $define = $prototype =~ s/^#\s*define\s+//; #ak added
- $prototype =~ s/__attribute_const__ +//;
- $prototype =~ s/__attribute__\s*\(\(
- (?:
- [\w\s]++ # attribute name
- (?:\([^)]*+\))? # attribute arguments
- \s*+,? # optional comma at the end
- )+
- \)\)\s+//x;
-
- # Yes, this truly is vile. We are looking for:
- # 1. Return type (may be nothing if we're looking at a macro)
- # 2. Function name
- # 3. Function parameters.
- #
- # All the while we have to watch out for function pointer parameters
- # (which IIRC is what the two sections are for), C types (these
- # regexps don't even start to express all the possibilities), and
- # so on.
- #
- # If you mess with these regexps, it's a good idea to check that
- # the following functions' documentation still comes out right:
- # - parport_register_device (function pointer parameters)
- # - atomic_set (macro)
- # - pci_match_device, __copy_to_user (long return type)
- my $name = qr{[a-zA-Z0-9_~:]+};
- my $prototype_end1 = qr{[^\(]*};
- my $prototype_end2 = qr{[^\{]*};
- my $prototype_end = qr{\(($prototype_end1|$prototype_end2)\)};
- my $type1 = qr{[\w\s]+};
- my $type2 = qr{$type1\*+};
-
- if ($define && $prototype =~ m/^()($name)\s+/) {
- # This is an object-like macro, it has no return type and no parameter
- # list.
- # Function-like macros are not allowed to have spaces between
- # declaration_name and opening parenthesis (notice the \s+).
- $return_type = $1;
- $declaration_name = $2;
- $func_macro = 1;
- } elsif ($prototype =~ m/^()($name)\s*$prototype_end/ ||
- $prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ ||
- $prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) {
- $return_type = $1;
- $declaration_name = $2;
- my $args = $3;
-
- create_parameterlist($args, ',', $file, $declaration_name);
- } else {
- emit_warning("${file}:$.", "cannot understand function prototype: '$prototype'\n");
- return;
- }
-
- if ($identifier ne $declaration_name) {
- emit_warning("${file}:$.", "expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n");
- return;
- }
-
- my $prms = join " ", @parameterlist;
- check_sections($file, $declaration_name, "function", $sectcheck, $prms);
-
- # This check emits a lot of warnings at the moment, because many
- # functions don't have a 'Return' doc section. So until the number
- # of warnings goes sufficiently down, the check is only performed in
- # -Wreturn mode.
- # TODO: always perform the check.
- if ($Wreturn && !$func_macro) {
- check_return_section($file, $declaration_name, $return_type);
- }
-
- # The function parser can be called with a typedef parameter.
- # Handle it.
- if ($return_type =~ /typedef/) {
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'typedef' => 1,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'func_macro' => $func_macro
- });
- } else {
- output_declaration($declaration_name,
- 'function',
- {'function' => $declaration_name,
- 'module' => $modulename,
- 'functiontype' => $return_type,
- 'parameterlist' => \@parameterlist,
- 'parameterdescs' => \%parameterdescs,
- 'parametertypes' => \%parametertypes,
- 'sectionlist' => \@sectionlist,
- 'sections' => \%sections,
- 'purpose' => $declaration_purpose,
- 'func_macro' => $func_macro
- });
- }
-}
-
-sub reset_state {
- $function = "";
- %parameterdescs = ();
- %parametertypes = ();
- @parameterlist = ();
- %sections = ();
- @sectionlist = ();
- $sectcheck = "";
- $struct_actual = "";
- $prototype = "";
-
- $state = STATE_NORMAL;
- $inline_doc_state = STATE_INLINE_NA;
-}
-
-sub tracepoint_munge($) {
- my $file = shift;
- my $tracepointname = 0;
- my $tracepointargs = 0;
-
- if ($prototype =~ m/TRACE_EVENT\((.*?),/) {
- $tracepointname = $1;
- }
- if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) {
- $tracepointname = $1;
- }
- if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) {
- $tracepointname = $2;
- }
- $tracepointname =~ s/^\s+//; #strip leading whitespace
- if ($prototype =~ m/TP_PROTO\((.*?)\)/) {
- $tracepointargs = $1;
- }
- if (($tracepointname eq 0) || ($tracepointargs eq 0)) {
- emit_warning("${file}:$.", "Unrecognized tracepoint format: \n".
- "$prototype\n");
- } else {
- $prototype = "static inline void trace_$tracepointname($tracepointargs)";
- $identifier = "trace_$identifier";
- }
-}
-
-sub syscall_munge() {
- my $void = 0;
-
- $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's
-## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) {
- if ($prototype =~ m/SYSCALL_DEFINE0/) {
- $void = 1;
-## $prototype = "long sys_$1(void)";
- }
-
- $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name
- if ($prototype =~ m/long (sys_.*?),/) {
- $prototype =~ s/,/\(/;
- } elsif ($void) {
- $prototype =~ s/\)/\(void\)/;
- }
-
- # now delete all of the odd-number commas in $prototype
- # so that arg types & arg names don't have a comma between them
- my $count = 0;
- my $len = length($prototype);
- if ($void) {
- $len = 0; # skip the for-loop
- }
- for (my $ix = 0; $ix < $len; $ix++) {
- if (substr($prototype, $ix, 1) eq ',') {
- $count++;
- if ($count % 2 == 1) {
- substr($prototype, $ix, 1) = ' ';
- }
- }
- }
-}
-
-sub process_proto_function($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
-
- if ($x =~ /^#/ && $x !~ /^#\s*define/) {
- # do nothing
- } elsif ($x =~ /([^\{]*)/) {
- $prototype .= $1;
- }
-
- if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) {
- $prototype =~ s@/\*.*?\*/@@gos; # strip comments.
- $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
- $prototype =~ s@^\s+@@gos; # strip leading spaces
-
- # Handle prototypes for function pointers like:
- # int (*pcs_config)(struct foo)
- $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos;
-
- if ($prototype =~ /SYSCALL_DEFINE/) {
- syscall_munge();
- }
- if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ ||
- $prototype =~ /DEFINE_SINGLE_EVENT/)
- {
- tracepoint_munge($file);
- }
- dump_function($prototype, $file);
- reset_state();
- }
-}
-
-sub process_proto_type($$) {
- my $x = shift;
- my $file = shift;
-
- $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
- $x =~ s@^\s+@@gos; # strip leading spaces
- $x =~ s@\s+$@@gos; # strip trailing spaces
- $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line
-
- if ($x =~ /^#/) {
- # To distinguish preprocessor directive from regular declaration later.
- $x .= ";";
- }
-
- while (1) {
- if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) {
- if( length $prototype ) {
- $prototype .= " "
- }
- $prototype .= $1 . $2;
- ($2 eq '{') && $brcount++;
- ($2 eq '}') && $brcount--;
- if (($2 eq ';') && ($brcount == 0)) {
- dump_declaration($prototype, $file);
- reset_state();
- last;
- }
- $x = $3;
- } else {
- $prototype .= $x;
- last;
- }
- }
-}
-
-
-sub map_filename($) {
- my $file;
- my ($orig_file) = @_;
-
- if (defined($ENV{'SRCTREE'})) {
- $file = "$ENV{'SRCTREE'}" . "/" . $orig_file;
- } else {
- $file = $orig_file;
- }
-
- return $file;
-}
-
-sub process_export_file($) {
- my ($orig_file) = @_;
- my $file = map_filename($orig_file);
-
- if (!open(IN,"<$file")) {
- print STDERR "Error: Cannot open file $file\n";
- ++$errors;
- return;
- }
-
- while (<IN>) {
- if (/$export_symbol/) {
- next if (defined($nosymbol_table{$2}));
- $function_table{$2} = 1;
- }
- if (/$export_symbol_ns/) {
- next if (defined($nosymbol_table{$2}));
- $function_table{$2} = 1;
- }
- }
-
- close(IN);
-}
-
-#
-# Parsers for the various processing states.
-#
-# STATE_NORMAL: looking for the /** to begin everything.
-#
-sub process_normal() {
- if (/$doc_start/o) {
- $state = STATE_NAME; # next line is always the function name
- $declaration_start_line = $. + 1;
- }
-}
-
-#
-# STATE_NAME: Looking for the "name - description" line
-#
-sub process_name($$) {
- my $file = shift;
- my $descr;
-
- if (/$doc_block/o) {
- $state = STATE_DOCBLOCK;
- $contents = "";
- $new_start_line = $.;
-
- if ( $1 eq "" ) {
- $section = $section_intro;
- } else {
- $section = $1;
- }
- } elsif (/$doc_decl/o) {
- $identifier = $1;
- my $is_kernel_comment = 0;
- my $decl_start = qr{$doc_com};
- # test for pointer declaration type, foo * bar() - desc
- my $fn_type = qr{\w+\s*\*\s*};
- my $parenthesis = qr{\(\w*\)};
- my $decl_end = qr{[-:].*};
- if (/^$decl_start([\w\s]+?)$parenthesis?\s*$decl_end?$/) {
- $identifier = $1;
- }
- if ($identifier =~ m/^(struct|union|enum|typedef)\b\s*(\S*)/) {
- $decl_type = $1;
- $identifier = $2;
- $is_kernel_comment = 1;
- }
- # Look for foo() or static void foo() - description; or misspelt
- # identifier
- elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ ||
- /^$decl_start$fn_type?(\w+[^-:]*)$parenthesis?\s*$decl_end$/) {
- $identifier = $1;
- $decl_type = 'function';
- $identifier =~ s/^define\s+//;
- $is_kernel_comment = 1;
- }
- $identifier =~ s/\s+$//;
-
- $state = STATE_BODY;
- # if there's no @param blocks need to set up default section
- # here
- $contents = "";
- $section = $section_default;
- $new_start_line = $. + 1;
- if (/[-:](.*)/) {
- # strip leading/trailing/multiple spaces
- $descr= $1;
- $descr =~ s/^\s*//;
- $descr =~ s/\s*$//;
- $descr =~ s/\s+/ /g;
- $declaration_purpose = $descr;
- $state = STATE_BODY_MAYBE;
- } else {
- $declaration_purpose = "";
- }
-
- if (!$is_kernel_comment) {
- emit_warning("${file}:$.", "This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n$_");
- $state = STATE_NORMAL;
- }
-
- if (($declaration_purpose eq "") && $Wshort_desc) {
- emit_warning("${file}:$.", "missing initial short description on line:\n$_");
- }
-
- if ($identifier eq "" && $decl_type ne "enum") {
- emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n$_");
- $state = STATE_NORMAL;
- }
-
- if ($verbose) {
- print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n";
- }
- } else {
- emit_warning("${file}:$.", "Cannot understand $_ on line $. - I thought it was a doc line\n");
- $state = STATE_NORMAL;
- }
-}
-
-
-#
-# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
-#
-sub process_body($$) {
- my $file = shift;
-
- if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $new_start_line = $.;
- $contents = "";
- }
-
- if (/$doc_sect/i) { # case insensitive for supported section names
- $newsection = $1;
- $newcontents = $2;
-
- # map the supported section names to the canonical names
- if ($newsection =~ m/^description$/i) {
- $newsection = $section_default;
- } elsif ($newsection =~ m/^context$/i) {
- $newsection = $section_context;
- } elsif ($newsection =~ m/^returns?$/i) {
- $newsection = $section_return;
- } elsif ($newsection =~ m/^\@return$/) {
- # special: @return is a section, not a param description
- $newsection = $section_return;
- }
-
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- }
-
- $state = STATE_BODY;
- $contents = $newcontents;
- $new_start_line = $.;
- while (substr($contents, 0, 1) eq " ") {
- $contents = substr($contents, 1);
- }
- if ($contents ne "") {
- $contents .= "\n";
- }
- $section = $newsection;
- $leading_space = undef;
- } elsif (/$doc_end/) {
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- # look for doc_com + <text> + doc_end:
- if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') {
- emit_warning("${file}:$.", "suspicious ending line: $_");
- }
-
- $prototype = "";
- $state = STATE_PROTO;
- $brcount = 0;
- $new_start_line = $. + 1;
- } elsif (/$doc_content/) {
- if ($1 eq "") {
- if ($section eq $section_context) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- $new_start_line = $.;
- $state = STATE_BODY;
- } else {
- if ($section ne $section_default) {
- $state = STATE_BODY_WITH_BLANK_LINE;
- } else {
- $state = STATE_BODY;
- }
- $contents .= "\n";
- }
- } elsif ($state == STATE_BODY_MAYBE) {
- # Continued declaration purpose
- chomp($declaration_purpose);
- $declaration_purpose .= " " . $1;
- $declaration_purpose =~ s/\s+/ /g;
- } else {
- my $cont = $1;
- if ($section =~ m/^@/ || $section eq $section_context) {
- if (!defined $leading_space) {
- if ($cont =~ m/^(\s+)/) {
- $leading_space = $1;
- } else {
- $leading_space = "";
- }
- }
- $cont =~ s/^$leading_space//;
- }
- $contents .= $cont . "\n";
- }
- } else {
- # i dont know - bad line? ignore.
- emit_warning("${file}:$.", "bad line: $_");
- }
-}
-
-
-#
-# STATE_PROTO: reading a function/whatever prototype.
-#
-sub process_proto($$) {
- my $file = shift;
-
- if (/$doc_inline_oneline/) {
- $section = $1;
- $contents = $2;
- if ($contents ne "") {
- $contents .= "\n";
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- } elsif (/$doc_inline_start/) {
- $state = STATE_INLINE;
- $inline_doc_state = STATE_INLINE_NAME;
- } elsif ($decl_type eq 'function') {
- process_proto_function($_, $file);
- } else {
- process_proto_type($_, $file);
- }
-}
-
-#
-# STATE_DOCBLOCK: within a DOC: block.
-#
-sub process_docblock($$) {
- my $file = shift;
-
- if (/$doc_end/) {
- dump_doc_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- $function = "";
- %parameterdescs = ();
- %parametertypes = ();
- @parameterlist = ();
- %sections = ();
- @sectionlist = ();
- $prototype = "";
- $state = STATE_NORMAL;
- } elsif (/$doc_content/) {
- if ( $1 eq "" ) {
- $contents .= $blankline;
- } else {
- $contents .= $1 . "\n";
- }
- }
-}
-
-#
-# STATE_INLINE: docbook comments within a prototype.
-#
-sub process_inline($$) {
- my $file = shift;
-
- # First line (state 1) needs to be a @parameter
- if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) {
- $section = $1;
- $contents = $2;
- $new_start_line = $.;
- if ($contents ne "") {
- while (substr($contents, 0, 1) eq " ") {
- $contents = substr($contents, 1);
- }
- $contents .= "\n";
- }
- $inline_doc_state = STATE_INLINE_TEXT;
- # Documentation block end */
- } elsif (/$doc_inline_end/) {
- if (($contents ne "") && ($contents ne "\n")) {
- dump_section($file, $section, $contents);
- $section = $section_default;
- $contents = "";
- }
- $state = STATE_PROTO;
- $inline_doc_state = STATE_INLINE_NA;
- # Regular text
- } elsif (/$doc_content/) {
- if ($inline_doc_state == STATE_INLINE_TEXT) {
- $contents .= $1 . "\n";
- # nuke leading blank lines
- if ($contents =~ /^\s*$/) {
- $contents = "";
- }
- } elsif ($inline_doc_state == STATE_INLINE_NAME) {
- $inline_doc_state = STATE_INLINE_ERROR;
- emit_warning("${file}:$.", "Incorrect use of kernel-doc format: $_");
- }
- }
-}
-
-
-sub process_file($) {
- my $file;
- my ($orig_file) = @_;
-
- $file = map_filename($orig_file);
-
- if (!open(IN_FILE,"<$file")) {
- print STDERR "Error: Cannot open file $file\n";
- ++$errors;
- return;
- }
-
- $. = 1;
-
- $section_counter = 0;
- while (<IN_FILE>) {
- while (!/^ \*/ && s/\\\s*$//) {
- $_ .= <IN_FILE>;
- }
- # Replace tabs by spaces
- while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
- # Hand this line to the appropriate state handler
- if ($state == STATE_NORMAL) {
- process_normal();
- } elsif ($state == STATE_NAME) {
- process_name($file, $_);
- } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE ||
- $state == STATE_BODY_WITH_BLANK_LINE) {
- process_body($file, $_);
- } elsif ($state == STATE_INLINE) { # scanning for inline parameters
- process_inline($file, $_);
- } elsif ($state == STATE_PROTO) {
- process_proto($file, $_);
- } elsif ($state == STATE_DOCBLOCK) {
- process_docblock($file, $_);
- }
- }
-
- # Make sure we got something interesting.
- if (!$section_counter && $output_mode ne "none") {
- if ($output_selection == OUTPUT_INCLUDE) {
- emit_warning("${file}:1", "'$_' not found\n")
- for keys %function_table;
- } else {
- emit_warning("${file}:1", "no structured comments found\n");
- }
- }
- close IN_FILE;
-}
-
-$kernelversion = get_kernel_version();
-
-# generate a sequence of code that will splice in highlighting information
-# using the s// operator.
-for (my $k = 0; $k < @highlights; $k++) {
- my $pattern = $highlights[$k][0];
- my $result = $highlights[$k][1];
-# print STDERR "scanning pattern:$pattern, highlight:($result)\n";
- $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n";
-}
-
-if ($output_selection == OUTPUT_EXPORTED ||
- $output_selection == OUTPUT_INTERNAL) {
-
- push(@export_file_list, @ARGV);
-
- foreach (@export_file_list) {
- chomp;
- process_export_file($_);
- }
-}
-
-foreach (@ARGV) {
- chomp;
- process_file($_);
-}
-if ($verbose && $errors) {
- print STDERR "$errors errors\n";
-}
-if ($verbose && $warnings) {
- print STDERR "$warnings warnings\n";
-}
-
-if ($Werror && $warnings) {
- print STDERR "$warnings warnings as Errors\n";
- exit($warnings);
-} else {
- exit($output_mode eq "none" ? 0 : $errors)
-}
-
-__END__
-
-=head1 OPTIONS
-
-=head2 Output format selection (mutually exclusive):
-
-=over 8
-
-=item -man
-
-Output troff manual page format.
-
-=item -rst
-
-Output reStructuredText format. This is the default.
-
-=item -none
-
-Do not output documentation, only warnings.
-
-=back
-
-=head2 Output format modifiers
-
-=head3 reStructuredText only
-
-=head2 Output selection (mutually exclusive):
-
-=over 8
-
-=item -export
-
-Only output documentation for the symbols that have been exported using
-EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE.
-
-=item -internal
-
-Only output documentation for the symbols that have NOT been exported using
-EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE.
-
-=item -function NAME
-
-Only output documentation for the given function or DOC: section title.
-All other functions and DOC: sections are ignored.
-
-May be specified multiple times.
-
-=item -nosymbol NAME
-
-Exclude the specified symbol from the output documentation.
-
-May be specified multiple times.
-
-=back
-
-=head2 Output selection modifiers:
-
-=over 8
-
-=item -no-doc-sections
-
-Do not output DOC: sections.
-
-=item -export-file FILE
-
-Specify an additional FILE in which to look for EXPORT_SYMBOL information.
-
-To be used with -export or -internal.
-
-May be specified multiple times.
-
-=back
-
-=head3 reStructuredText only
-
-=over 8
-
-=item -enable-lineno
-
-Enable output of .. LINENO lines.
-
-=back
-
-=head2 Other parameters:
-
-=over 8
-
-=item -h, -help
-
-Print this help.
-
-=item -v
-
-Verbose output, more warnings and other information.
-
-=item -Werror
-
-Treat warnings as errors.
-
-=back
-
-=cut
diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py
deleted file mode 100755
index 47b4606569f9..000000000000
--- a/scripts/test_doc_build.py
+++ /dev/null
@@ -1,513 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: GPL-2.0
-# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
-#
-# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301
-
-"""
-Install minimal supported requirements for different Sphinx versions
-and optionally test the build.
-"""
-
-import argparse
-import asyncio
-import os.path
-import shutil
-import sys
-import time
-import subprocess
-
-# Minimal python version supported by the building system.
-
-PYTHON = os.path.basename(sys.executable)
-
-min_python_bin = None
-
-for i in range(9, 13):
- p = f"python3.{i}"
- if shutil.which(p):
- min_python_bin = p
- break
-
-if not min_python_bin:
- min_python_bin = PYTHON
-
-# Starting from 8.0, Python 3.9 is not supported anymore.
-PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON}
-
-DEFAULT_VERSIONS_TO_TEST = [
- (3, 4, 3), # Minimal supported version
- (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9
- (6, 1, 1), # Debian 12
- (7, 2, 1), # openSUSE Leap 15.6
- (7, 2, 6), # Ubuntu 24.04 LTS
- (7, 4, 7), # Ubuntu 24.10
- (7, 3, 0), # openSUSE Tumbleweed
- (8, 1, 3), # Fedora 42
- (8, 2, 3) # Latest version - covers rolling distros
-]
-
-# Sphinx versions to be installed and their incremental requirements
-SPHINX_REQUIREMENTS = {
- # Oldest versions we support for each package required by Sphinx 3.4.3
- (3, 4, 3): {
- "docutils": "0.16",
- "alabaster": "0.7.12",
- "babel": "2.8.0",
- "certifi": "2020.6.20",
- "docutils": "0.16",
- "idna": "2.10",
- "imagesize": "1.2.0",
- "Jinja2": "2.11.2",
- "MarkupSafe": "1.1.1",
- "packaging": "20.4",
- "Pygments": "2.6.1",
- "PyYAML": "5.1",
- "requests": "2.24.0",
- "snowballstemmer": "2.0.0",
- "sphinxcontrib-applehelp": "1.0.2",
- "sphinxcontrib-devhelp": "1.0.2",
- "sphinxcontrib-htmlhelp": "1.0.3",
- "sphinxcontrib-jsmath": "1.0.1",
- "sphinxcontrib-qthelp": "1.0.3",
- "sphinxcontrib-serializinghtml": "1.1.4",
- "urllib3": "1.25.9",
- },
-
- # Update package dependencies to a more modern base. The goal here
- # is to avoid to many incremental changes for the next entries
- (3, 5, 0): {
- "alabaster": "0.7.13",
- "babel": "2.17.0",
- "certifi": "2025.6.15",
- "idna": "3.10",
- "imagesize": "1.4.1",
- "packaging": "25.0",
- "Pygments": "2.8.1",
- "requests": "2.32.4",
- "snowballstemmer": "3.0.1",
- "sphinxcontrib-applehelp": "1.0.4",
- "sphinxcontrib-htmlhelp": "2.0.1",
- "sphinxcontrib-serializinghtml": "1.1.5",
- "urllib3": "2.0.0",
- },
-
- # Starting from here, ensure all docutils versions are covered with
- # supported Sphinx versions. Other packages are upgraded only when
- # required by pip
- (4, 0, 0): {
- "PyYAML": "5.1",
- },
- (4, 1, 0): {
- "docutils": "0.17",
- "Pygments": "2.19.1",
- "Jinja2": "3.0.3",
- "MarkupSafe": "2.0",
- },
- (4, 3, 0): {},
- (4, 4, 0): {},
- (4, 5, 0): {
- "docutils": "0.17.1",
- },
- (5, 0, 0): {},
- (5, 1, 0): {},
- (5, 2, 0): {
- "docutils": "0.18",
- "Jinja2": "3.1.2",
- "MarkupSafe": "2.0",
- "PyYAML": "5.3.1",
- },
- (5, 3, 0): {
- "docutils": "0.18.1",
- },
- (6, 0, 0): {},
- (6, 1, 0): {},
- (6, 2, 0): {
- "PyYAML": "5.4.1",
- },
- (7, 0, 0): {},
- (7, 1, 0): {},
- (7, 2, 0): {
- "docutils": "0.19",
- "PyYAML": "6.0.1",
- "sphinxcontrib-serializinghtml": "1.1.9",
- },
- (7, 2, 6): {
- "docutils": "0.20",
- },
- (7, 3, 0): {
- "alabaster": "0.7.14",
- "PyYAML": "6.0.1",
- "tomli": "2.0.1",
- },
- (7, 4, 0): {
- "docutils": "0.20.1",
- "PyYAML": "6.0.1",
- },
- (8, 0, 0): {
- "docutils": "0.21",
- },
- (8, 1, 0): {
- "docutils": "0.21.1",
- "PyYAML": "6.0.1",
- "sphinxcontrib-applehelp": "1.0.7",
- "sphinxcontrib-devhelp": "1.0.6",
- "sphinxcontrib-htmlhelp": "2.0.6",
- "sphinxcontrib-qthelp": "1.0.6",
- },
- (8, 2, 0): {
- "docutils": "0.21.2",
- "PyYAML": "6.0.1",
- "sphinxcontrib-serializinghtml": "1.1.9",
- },
-}
-
-
-class AsyncCommands:
- """Excecute command synchronously"""
-
- def __init__(self, fp=None):
-
- self.stdout = None
- self.stderr = None
- self.output = None
- self.fp = fp
-
- def log(self, out, verbose, is_info=True):
- out = out.removesuffix('\n')
-
- if verbose:
- if is_info:
- print(out)
- else:
- print(out, file=sys.stderr)
-
- if self.fp:
- self.fp.write(out + "\n")
-
- async def _read(self, stream, verbose, is_info):
- """Ancillary routine to capture while displaying"""
-
- while stream is not None:
- line = await stream.readline()
- if line:
- out = line.decode("utf-8", errors="backslashreplace")
- self.log(out, verbose, is_info)
- if is_info:
- self.stdout += out
- else:
- self.stderr += out
- else:
- break
-
- async def run(self, cmd, capture_output=False, check=False,
- env=None, verbose=True):
-
- """
- Execute an arbitrary command, handling errors.
-
- Please notice that this class is not thread safe
- """
-
- self.stdout = ""
- self.stderr = ""
-
- self.log("$ " + " ".join(cmd), verbose)
-
- proc = await asyncio.create_subprocess_exec(cmd[0],
- *cmd[1:],
- env=env,
- stdout=asyncio.subprocess.PIPE,
- stderr=asyncio.subprocess.PIPE)
-
- # Handle input and output in realtime
- await asyncio.gather(
- self._read(proc.stdout, verbose, True),
- self._read(proc.stderr, verbose, False),
- )
-
- await proc.wait()
-
- if check and proc.returncode > 0:
- raise subprocess.CalledProcessError(returncode=proc.returncode,
- cmd=" ".join(cmd),
- output=self.stdout,
- stderr=self.stderr)
-
- if capture_output:
- if proc.returncode > 0:
- self.log(f"Error {proc.returncode}", verbose=True, is_info=False)
- return ""
-
- return self.output
-
- ret = subprocess.CompletedProcess(args=cmd,
- returncode=proc.returncode,
- stdout=self.stdout,
- stderr=self.stderr)
-
- return ret
-
-
-class SphinxVenv:
- """
- Installs Sphinx on one virtual env per Sphinx version with a minimal
- set of dependencies, adjusting them to each specific version.
- """
-
- def __init__(self):
- """Initialize instance variables"""
-
- self.built_time = {}
- self.first_run = True
-
- async def _handle_version(self, args, fp,
- cur_ver, cur_requirements, python_bin):
- """Handle a single Sphinx version"""
-
- cmd = AsyncCommands(fp)
-
- ver = ".".join(map(str, cur_ver))
-
- if not self.first_run and args.wait_input and args.build:
- ret = input("Press Enter to continue or 'a' to abort: ").strip().lower()
- if ret == "a":
- print("Aborted.")
- sys.exit()
- else:
- self.first_run = False
-
- venv_dir = f"Sphinx_{ver}"
- req_file = f"requirements_{ver}.txt"
-
- cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True)
-
- # Create venv
- await cmd.run([python_bin, "-m", "venv", venv_dir],
- verbose=args.verbose, check=True)
- pip = os.path.join(venv_dir, "bin/pip")
-
- # Create install list
- reqs = []
- for pkg, verstr in cur_requirements.items():
- reqs.append(f"{pkg}=={verstr}")
-
- reqs.append(f"Sphinx=={ver}")
-
- await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose)
-
- # Freeze environment
- result = await cmd.run([pip, "freeze"], verbose=False, check=True)
-
- # Pip install succeeded. Write requirements file
- if args.req_file:
- with open(req_file, "w", encoding="utf-8") as fp:
- fp.write(result.stdout)
-
- if args.build:
- start_time = time.time()
-
- # Prepare a venv environment
- env = os.environ.copy()
- bin_dir = os.path.join(venv_dir, "bin")
- env["PATH"] = bin_dir + ":" + env["PATH"]
- env["VIRTUAL_ENV"] = venv_dir
- if "PYTHONHOME" in env:
- del env["PYTHONHOME"]
-
- # Test doc build
- await cmd.run(["make", "cleandocs"], env=env, check=True)
- make = ["make"]
-
- if args.output:
- sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build")
- make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"]
-
- if args.make_args:
- make += args.make_args
-
- make += args.targets
-
- if args.verbose:
- cmd.log(f". {bin_dir}/activate", verbose=True)
- await cmd.run(make, env=env, check=True, verbose=True)
- if args.verbose:
- cmd.log("deactivate", verbose=True)
-
- end_time = time.time()
- elapsed_time = end_time - start_time
- hours, minutes = divmod(elapsed_time, 3600)
- minutes, seconds = divmod(minutes, 60)
-
- hours = int(hours)
- minutes = int(minutes)
- seconds = int(seconds)
-
- self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
-
- cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True)
-
- async def run(self, args):
- """
- Navigate though multiple Sphinx versions, handling each of them
- on a loop.
- """
-
- if args.log:
- fp = open(args.log, "w", encoding="utf-8")
- if not args.verbose:
- args.verbose = False
- else:
- fp = None
- if not args.verbose:
- args.verbose = True
-
- cur_requirements = {}
- python_bin = min_python_bin
-
- vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions)
-
- for cur_ver in sorted(vers):
- if cur_ver in SPHINX_REQUIREMENTS:
- new_reqs = SPHINX_REQUIREMENTS[cur_ver]
- cur_requirements.update(new_reqs)
-
- if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715
- python_bin = PYTHON_VER_CHANGES[cur_ver]
-
- if cur_ver not in args.versions:
- continue
-
- if args.min_version:
- if cur_ver < args.min_version:
- continue
-
- if args.max_version:
- if cur_ver > args.max_version:
- break
-
- await self._handle_version(args, fp, cur_ver, cur_requirements,
- python_bin)
-
- if args.build:
- cmd = AsyncCommands(fp)
- cmd.log("\nSummary:", verbose=True)
- for ver, elapsed_time in sorted(self.built_time.items()):
- cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}",
- verbose=True)
-
- if fp:
- fp.close()
-
-def parse_version(ver_str):
- """Convert a version string into a tuple."""
-
- return tuple(map(int, ver_str.split(".")))
-
-
-DEFAULT_VERS = " - "
-DEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}",
- DEFAULT_VERSIONS_TO_TEST))
-
-SCRIPT = os.path.relpath(__file__)
-
-DESCRIPTION = f"""
-This tool allows creating Python virtual environments for different
-Sphinx versions that are supported by the Linux Kernel build system.
-
-Besides creating the virtual environment, it can also test building
-the documentation using "make htmldocs" (and/or other doc targets).
-
-If called without "--versions" argument, it covers the versions shipped
-on major distros, plus the lowest supported version:
-
-{DEFAULT_VERS}
-
-A typical usage is to run:
-
- {SCRIPT} -m -l sphinx_builds.log
-
-This will create one virtual env for the default version set and run
-"make htmldocs" for each version, creating a log file with the
-excecuted commands on it.
-
-NOTE: The build time can be very long, specially on old versions. Also, there
-is a known bug with Sphinx version 6.0.x: each subprocess uses a lot of
-memory. That, together with "-jauto" may cause OOM killer to cause
-failures at the doc generation. To minimize the risk, you may use the
-"-a" command line parameter to constrain the built directories and/or
-reduce the number of threads from "-jauto" to, for instance, "-j4":
-
- {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'"
-
-"""
-
-MAKE_TARGETS = [
- "htmldocs",
- "texinfodocs",
- "infodocs",
- "latexdocs",
- "pdfdocs",
- "epubdocs",
- "xmldocs",
-]
-
-async def main():
- """Main program"""
-
- parser = argparse.ArgumentParser(description=DESCRIPTION,
- formatter_class=argparse.RawDescriptionHelpFormatter)
-
- ver_group = parser.add_argument_group("Version range options")
-
- ver_group.add_argument('-V', '--versions', nargs="*",
- default=DEFAULT_VERSIONS_TO_TEST,type=parse_version,
- help='Sphinx versions to test')
- ver_group.add_argument('--min-version', "--min", type=parse_version,
- help='Sphinx minimal version')
- ver_group.add_argument('--max-version', "--max", type=parse_version,
- help='Sphinx maximum version')
- ver_group.add_argument('-f', '--full', action='store_true',
- help='Add all Sphinx (major,minor) supported versions to the version range')
-
- build_group = parser.add_argument_group("Build options")
-
- build_group.add_argument('-b', '--build', action='store_true',
- help='Build documentation')
- build_group.add_argument('-a', '--make-args', nargs="*",
- help='extra arguments for make, like SPHINXDIRS=netlink/specs',
- )
- build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS,
- default=[MAKE_TARGETS[0]],
- help="make build targets. Default: htmldocs.")
- build_group.add_argument("-o", '--output',
- help="output directory for the make O=OUTPUT")
-
- other_group = parser.add_argument_group("Other options")
-
- other_group.add_argument('-r', '--req-file', action='store_true',
- help='write a requirements.txt file')
- other_group.add_argument('-l', '--log',
- help='Log command output on a file')
- other_group.add_argument('-v', '--verbose', action='store_true',
- help='Verbose all commands')
- other_group.add_argument('-i', '--wait-input', action='store_true',
- help='Wait for an enter before going to the next version')
-
- args = parser.parse_args()
-
- if not args.make_args:
- args.make_args = []
-
- sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys()))
-
- if args.full:
- args.versions += list(SPHINX_REQUIREMENTS.keys())
-
- venv = SphinxVenv()
- await venv.run(args)
-
-
-# Call main method
-if __name__ == "__main__":
- asyncio.run(main())