^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) #!/usr/bin/env python3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) # SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) # Copyright (C) Google LLC, 2018
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) # Author: Tom Roeder <tmroeder@google.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) """A tool for generating compile_commands.json in the Linux kernel."""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) import argparse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) import json
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) import logging
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) import os
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) import re
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) import subprocess
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) import sys
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) _DEFAULT_OUTPUT = 'compile_commands.json'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) _DEFAULT_LOG_LEVEL = 'WARNING'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) _FILENAME_PATTERN = r'^\..*\.cmd$'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) _LINE_PATTERN = r'^cmd_[^ ]*\.o := (.* )([^ ]*\.c)$'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) _VALID_LOG_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) def parse_arguments():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) """Sets up and parses command-line arguments.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) Returns:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) log_level: A logging level to filter log output.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) directory: The work directory where the objects were built.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) ar: Command used for parsing .a archives.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) output: Where to write the compile-commands JSON file.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) paths: The list of files/directories to handle to find .cmd files.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) usage = 'Creates a compile_commands.json database from kernel .cmd files'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) parser = argparse.ArgumentParser(description=usage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) directory_help = ('specify the output directory used for the kernel build '
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) '(defaults to the working directory)')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) parser.add_argument('-d', '--directory', type=str, default='.',
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) help=directory_help)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) output_help = ('path to the output command database (defaults to ' +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) _DEFAULT_OUTPUT + ')')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) parser.add_argument('-o', '--output', type=str, default=_DEFAULT_OUTPUT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) help=output_help)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) log_level_help = ('the level of log messages to produce (defaults to ' +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) _DEFAULT_LOG_LEVEL + ')')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) parser.add_argument('--log_level', choices=_VALID_LOG_LEVELS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) default=_DEFAULT_LOG_LEVEL, help=log_level_help)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) ar_help = 'command used for parsing .a archives'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) parser.add_argument('-a', '--ar', type=str, default='llvm-ar', help=ar_help)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) paths_help = ('directories to search or files to parse '
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) '(files should be *.o, *.a, or modules.order). '
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) 'If nothing is specified, the current directory is searched')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) parser.add_argument('paths', type=str, nargs='*', help=paths_help)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) args = parser.parse_args()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) return (args.log_level,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) os.path.abspath(args.directory),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) args.output,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) args.ar,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) args.paths if len(args.paths) > 0 else [args.directory])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) def cmdfiles_in_dir(directory):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) """Generate the iterator of .cmd files found under the directory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) Walk under the given directory, and yield every .cmd file found.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) directory: The directory to search for .cmd files.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) Yields:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) The path to a .cmd file.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) filename_matcher = re.compile(_FILENAME_PATTERN)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) for dirpath, _, filenames in os.walk(directory):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) for filename in filenames:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if filename_matcher.match(filename):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) yield os.path.join(dirpath, filename)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) def to_cmdfile(path):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) """Return the path of .cmd file used for the given build artifact
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) Path: file path
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) Returns:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) The path to .cmd file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) dir, base = os.path.split(path)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return os.path.join(dir, '.' + base + '.cmd')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) def cmdfiles_for_o(obj):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) """Generate the iterator of .cmd files associated with the object
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) Yield the .cmd file used to build the given object
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) obj: The object path
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) Yields:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) The path to .cmd file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) yield to_cmdfile(obj)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) def cmdfiles_for_a(archive, ar):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) """Generate the iterator of .cmd files associated with the archive.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) Parse the given archive, and yield every .cmd file used to build it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) archive: The archive to parse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) Yields:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) The path to every .cmd file found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) for obj in subprocess.check_output([ar, '-t', archive]).decode().split():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) yield to_cmdfile(obj)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) def cmdfiles_for_modorder(modorder):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) """Generate the iterator of .cmd files associated with the modules.order.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) Parse the given modules.order, and yield every .cmd file used to build the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) contained modules.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) modorder: The modules.order file to parse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) Yields:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) The path to every .cmd file found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) with open(modorder) as f:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) for line in f:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) ko = line.rstrip()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) base, ext = os.path.splitext(ko)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if ext != '.ko':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) sys.exit('{}: module path must end with .ko'.format(ko))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) mod = base + '.mod'
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) # The first line of *.mod lists the objects that compose the module.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) with open(mod) as m:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) for obj in m.readline().split():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) yield to_cmdfile(obj)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) def process_line(root_directory, command_prefix, file_path):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) """Extracts information from a .cmd line and creates an entry from it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) Args:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) root_directory: The directory that was searched for .cmd files. Usually
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) used directly in the "directory" entry in compile_commands.json.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) command_prefix: The extracted command line, up to the last element.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) file_path: The .c file from the end of the extracted command.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) Usually relative to root_directory, but sometimes absolute.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) Returns:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) An entry to append to compile_commands.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) Raises:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) ValueError: Could not find the extracted file based on file_path and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) root_directory or file_directory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) """
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) # The .cmd files are intended to be included directly by Make, so they
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) # escape the pound sign '#', either as '\#' or '$(pound)' (depending on the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) # kernel version). The compile_commands.json file is not interepreted
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) # by Make, so this code replaces the escaped version with '#'.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) prefix = command_prefix.replace('\#', '#').replace('$(pound)', '#')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) # Use os.path.abspath() to normalize the path resolving '.' and '..' .
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) abs_path = os.path.abspath(os.path.join(root_directory, file_path))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if not os.path.exists(abs_path):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) raise ValueError('File %s not found' % abs_path)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 'directory': root_directory,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 'file': abs_path,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 'command': prefix + file_path,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) def main():
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) """Walks through the directory and finds and parses .cmd files."""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) log_level, directory, output, ar, paths = parse_arguments()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) level = getattr(logging, log_level)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) logging.basicConfig(format='%(levelname)s: %(message)s', level=level)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) line_matcher = re.compile(_LINE_PATTERN)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) compile_commands = []
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) for path in paths:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) # If 'path' is a directory, handle all .cmd files under it.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) # Otherwise, handle .cmd files associated with the file.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) # Most of built-in objects are linked via archives (built-in.a or lib.a)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) # but some objects are linked to vmlinux directly.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) # Modules are listed in modules.order.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) if os.path.isdir(path):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) cmdfiles = cmdfiles_in_dir(path)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) elif path.endswith('.o'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) cmdfiles = cmdfiles_for_o(path)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) elif path.endswith('.a'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) cmdfiles = cmdfiles_for_a(path, ar)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) elif path.endswith('modules.order'):
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) cmdfiles = cmdfiles_for_modorder(path)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) else:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) sys.exit('{}: unknown file type'.format(path))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) for cmdfile in cmdfiles:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) with open(cmdfile, 'rt') as f:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) result = line_matcher.match(f.readline())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if result:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) try:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) entry = process_line(directory, result.group(1),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) result.group(2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) compile_commands.append(entry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) except ValueError as err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) logging.info('Could not add line from %s: %s',
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) cmdfile, err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) with open(output, 'wt') as f:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) json.dump(compile_commands, f, indent=2, sort_keys=True)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) if __name__ == '__main__':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) main()