1#!/usr/bin/env python3
2# compile_commands.json generator
3# Copyright (C) 2024 Intel Corporation
4#
5# This software may be distributed under the terms of the BSD license.
6# See README for more details.
7
8import os
9import sys
10import glob
11import json
12import argparse
13
14parser = argparse.ArgumentParser(description='Read each of the build directories that are given and generate '
15                                 'a compile_commands.json file. If a source file is found multiple times, '
16                                 'then the compile flags from the last build directory will be used.')
17parser.add_argument('-o', '--output', default='compile_commands.json', type=str,
18                    help="Output file to generate")
19parser.add_argument('builddirs', nargs='+', type=str, metavar="builddir",
20                    help='Build directories to search')
21
22args = parser.parse_args()
23
24files = {}
25
26for builddir in args.builddirs:
27    for cmd_file in glob.glob('**/*.o.cmd', root_dir=builddir, recursive=True):
28        with open(os.path.join(builddir, cmd_file), encoding='ascii') as f:
29            base_dir, cmd = f.readline().split(':', 1)
30            src_file = cmd.rsplit(maxsplit=1)[1]
31
32        src_file = os.path.abspath(os.path.join(base_dir, src_file))
33        files[src_file] = {
34            'command': cmd.strip(),
35            'directory': base_dir,
36            'file': src_file,
37        }
38
39flist = json.dumps(sorted(list(files.values()), key=lambda k: k['file']), indent=2, sort_keys=True)
40
41try:
42   # Avoid writing the file if it did not change, first read original
43   with open(args.output, 'rt', encoding='UTF-8') as f:
44       orig = []
45       while data := f.read():
46           orig.append(data)
47       orig = ''.join(orig)
48except OSError:
49    orig = ''
50
51# And only write if something changed
52if orig != flist:
53    with open(args.output, 'wt', encoding='UTF-8') as f:
54        f.write(flist)
55