diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-29 23:03:17 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-29 23:03:17 +0300 |
commit | 668c35f69cc750aaf07bd5fe7710a47e2aed6e43 (patch) | |
tree | 3a54f093d548b62b143bf1bc47aa657be17924f8 /scripts/gen_compile_commands.py | |
parent | d8372ba8ce288acdfce67cb873b2a741785c2e88 (diff) | |
parent | dec28d8ea2f731b7ec68a2c9421e99a165d47b57 (diff) | |
download | linux-668c35f69cc750aaf07bd5fe7710a47e2aed6e43.tar.xz |
Merge tag 'kbuild-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild
Pull Kbuild updates from Masahiro Yamada:
"Kbuild core:
- remove unneeded $(call cc-option,...) switches
- consolidate Clang compiler flags into CLANG_FLAGS
- announce the deprecation of SUBDIRS
- fix single target build for external module
- simplify the dependencies of 'prepare' stage targets
- allow fixdep to directly write to .*.cmd files
- simplify dependency generation for CONFIG_TRIM_UNUSED_KSYMS
- change if_changed_rule to accept multi-line recipe
- move .SECONDARY special target to scripts/Kbuild.include
- remove redundant 'set -e'
- improve parallel execution for CONFIG_HEADERS_CHECK
- misc cleanups
Treewide fixes and cleanups
- set Clang flags correctly for PowerPC boot images
- fix UML build error with CONFIG_GCC_PLUGINS
- remove unneeded patterns from .gitignore files
- refactor firmware/Makefile
- remove unneeded rules for *offsets.s
- avoid unneeded regeneration of intermediate .s files
- clean up ./Kbuild
Modpost:
- remove unused -M, -K options
- fix false positive warnings about section mismatch
- use simple devtable lookup instead of linker magic
- misc cleanups
Coccinelle:
- relax boolinit.cocci checks for overall consistency
- fix warning messages of boolinit.cocci
Other tools:
- improve -dirty check of scripts/setlocalversion
- add a tool to generate compile_commands.json from .*.cmd files"
* tag 'kbuild-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild: (51 commits)
kbuild: remove unused cmd_gentimeconst
kbuild: remove $(obj)/ prefixes in ./Kbuild
treewide: add intermediate .s files to targets
treewide: remove explicit rules for *offsets.s
firmware: refactor firmware/Makefile
firmware: remove unnecessary patterns from .gitignore
scripts: remove unnecessary ihex2fw and check-lc_ctypes from .gitignore
um: remove unused filechk_gen_header in Makefile
scripts: add a tool to produce a compile_commands.json file
kbuild: add -Werror=implicit-int flag unconditionally
kbuild: add -Werror=strict-prototypes flag unconditionally
kbuild: add -fno-PIE flag unconditionally
scripts: coccinelle: Correct warning message
scripts: coccinelle: only suggest true/false in files that already use them
kbuild: handle part-of-module correctly for *.ll and *.symtypes
kbuild: refactor part-of-module
kbuild: refactor quiet_modtag
kbuild: remove redundant quiet_modtag for $(obj-m)
kbuild: refactor Makefile.asm-generic
user/Makefile: Fix typo and capitalization in comment section
...
Diffstat (limited to 'scripts/gen_compile_commands.py')
-rwxr-xr-x | scripts/gen_compile_commands.py | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/scripts/gen_compile_commands.py b/scripts/gen_compile_commands.py new file mode 100755 index 000000000000..7915823b92a5 --- /dev/null +++ b/scripts/gen_compile_commands.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) Google LLC, 2018 +# +# Author: Tom Roeder <tmroeder@google.com> +# +"""A tool for generating compile_commands.json in the Linux kernel.""" + +import argparse +import json +import logging +import os +import re + +_DEFAULT_OUTPUT = 'compile_commands.json' +_DEFAULT_LOG_LEVEL = 'WARNING' + +_FILENAME_PATTERN = r'^\..*\.cmd$' +_LINE_PATTERN = r'^cmd_[^ ]*\.o := (.* )([^ ]*\.c)$' +_VALID_LOG_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] + +# A kernel build generally has over 2000 entries in its compile_commands.json +# database. If this code finds 500 or fewer, then warn the user that they might +# not have all the .cmd files, and they might need to compile the kernel. +_LOW_COUNT_THRESHOLD = 500 + + +def parse_arguments(): + """Sets up and parses command-line arguments. + + Returns: + log_level: A logging level to filter log output. + directory: The directory to search for .cmd files. + output: Where to write the compile-commands JSON file. + """ + usage = 'Creates a compile_commands.json database from kernel .cmd files' + parser = argparse.ArgumentParser(description=usage) + + directory_help = ('Path to the kernel source directory to search ' + '(defaults to the working directory)') + parser.add_argument('-d', '--directory', type=str, help=directory_help) + + output_help = ('The location to write compile_commands.json (defaults to ' + 'compile_commands.json in the search directory)') + parser.add_argument('-o', '--output', type=str, help=output_help) + + log_level_help = ('The level of log messages to produce (one of ' + + ', '.join(_VALID_LOG_LEVELS) + '; defaults to ' + + _DEFAULT_LOG_LEVEL + ')') + parser.add_argument( + '--log_level', type=str, default=_DEFAULT_LOG_LEVEL, + help=log_level_help) + + args = parser.parse_args() + + log_level = args.log_level + if log_level not in _VALID_LOG_LEVELS: + raise ValueError('%s is not a valid log level' % log_level) + + directory = args.directory or os.getcwd() + output = args.output or os.path.join(directory, _DEFAULT_OUTPUT) + directory = os.path.abspath(directory) + + return log_level, directory, output + + +def process_line(root_directory, file_directory, command_prefix, relative_path): + """Extracts information from a .cmd line and creates an entry from it. + + Args: + root_directory: The directory that was searched for .cmd files. Usually + used directly in the "directory" entry in compile_commands.json. + file_directory: The path to the directory the .cmd file was found in. + command_prefix: The extracted command line, up to the last element. + relative_path: The .c file from the end of the extracted command. + Usually relative to root_directory, but sometimes relative to + file_directory and sometimes neither. + + Returns: + An entry to append to compile_commands. + + Raises: + ValueError: Could not find the extracted file based on relative_path and + root_directory or file_directory. + """ + # The .cmd files are intended to be included directly by Make, so they + # escape the pound sign '#', either as '\#' or '$(pound)' (depending on the + # kernel version). The compile_commands.json file is not interepreted + # by Make, so this code replaces the escaped version with '#'. + prefix = command_prefix.replace('\#', '#').replace('$(pound)', '#') + + cur_dir = root_directory + expected_path = os.path.join(cur_dir, relative_path) + if not os.path.exists(expected_path): + # Try using file_directory instead. Some of the tools have a different + # style of .cmd file than the kernel. + cur_dir = file_directory + expected_path = os.path.join(cur_dir, relative_path) + if not os.path.exists(expected_path): + raise ValueError('File %s not in %s or %s' % + (relative_path, root_directory, file_directory)) + return { + 'directory': cur_dir, + 'file': relative_path, + 'command': prefix + relative_path, + } + + +def main(): + """Walks through the directory and finds and parses .cmd files.""" + log_level, directory, output = parse_arguments() + + level = getattr(logging, log_level) + logging.basicConfig(format='%(levelname)s: %(message)s', level=level) + + filename_matcher = re.compile(_FILENAME_PATTERN) + line_matcher = re.compile(_LINE_PATTERN) + + compile_commands = [] + for dirpath, _, filenames in os.walk(directory): + for filename in filenames: + if not filename_matcher.match(filename): + continue + filepath = os.path.join(dirpath, filename) + + with open(filepath, 'rt') as f: + for line in f: + result = line_matcher.match(line) + if not result: + continue + + try: + entry = process_line(directory, dirpath, + result.group(1), result.group(2)) + compile_commands.append(entry) + except ValueError as err: + logging.info('Could not add line from %s: %s', + filepath, err) + + with open(output, 'wt') as f: + json.dump(compile_commands, f, indent=2, sort_keys=True) + + count = len(compile_commands) + if count < _LOW_COUNT_THRESHOLD: + logging.warning( + 'Found %s entries. Have you compiled the kernel?', count) + + +if __name__ == '__main__': + main() |