File: clang-deps-launcher.py

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (97 lines) | stat: -rwxr-xr-x 3,268 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python3
"""
Launcher for clang which computes the dependency for the file being compiled.
"""
import sys
import os
import subprocess
import time
import json
import tempfile
import fcntl


TOOLS_DIR = os.path.dirname(os.path.abspath(__file__))


def trim_clang_args_in_cdb(cdb):
    """Trim -Xclang options away since they are not portable"""
    # We also replace the clang exe path so it can find the clang headers.
    clang_exe = os.path.basename(cdb['arguments'][0])
    trim_args = [os.path.join(TOOLS_DIR, clang_exe)]
    skip = False
    for arg in cdb['arguments'][1:]:
        if skip:
            skip = False
            continue
        if arg == '-Xclang':
            skip = True
            continue
        trim_args.append(arg)
    cdb['arguments'] = trim_args


def main():
    # Clang command is the input.
    clang_cmd = sys.argv[1:]
    # Figure out the output path.
    output_idx = len(clang_cmd) - clang_cmd[::-1].index('-o')
    output_object = clang_cmd[output_idx]
    # Write compilation data base to STDOUT, and strip the last ',' so it is a
    # valid JSON object.
    clang_cmd.extend(['-MJ', '-'])
    start = time.perf_counter()
    cdb = subprocess.check_output(clang_cmd).strip()[:-1]
    end = time.perf_counter()
    output_size = os.path.getsize(output_object)

    # load and clean up compiler commands, write to a temp file.
    cdb_object = json.loads(cdb)
    trim_clang_args_in_cdb(cdb_object)

    cdb_output = [ cdb_object ]
    cdb_file = tempfile.NamedTemporaryFile(suffix='.cdb', mode='w+')
    json.dump(cdb_output, cdb_file)
    cdb_file.flush()

    # run clang-scan-deps to collect dependency.
    cas_path = os.getenv('CLANG_SCAN_DEPS_CAS_PATH')
    if cas_path is None:
        scan_deps_extra_opt = '--in-memory-cas'
    else:
        scan_deps_extra_opt = '--cas-path=' + cas_path
    scan_deps = os.path.join(TOOLS_DIR, "clang-scan-deps")
    scan_deps_cmd = [scan_deps, '-compilation-database', cdb_file.name,
                     '-format', 'experimental-tree-full', scan_deps_extra_opt]

    scan_deps_output = subprocess.check_output(scan_deps_cmd)
    cdb_file.close()

    # Write output.
    scan_deps_object = json.loads(scan_deps_output)
    scan_deps_object['time'] = end - start
    scan_deps_object['size'] = output_size

    # Write outoput file.
    # See if the output file environmental variable is set, if set, use
    # accumulated json file format. If not, write next to output object.
    accumulated_file = os.getenv('CLANG_SCAN_DEPS_OUTPUT_FILE')
    if accumulated_file is None:
        output_filename = output_object + '.stats'
        with open(output_filename, 'w+') as of:
            json.dump(scan_deps_object, of)
        return

    # Write accumulated JSON object file format
    json_str = json.dumps(scan_deps_object)
    with open(accumulated_file, 'a+') as of:
        fcntl.flock(of, fcntl.LOCK_EX)  # lock file for write
        of.write(str(len(json_str)))    # write the size of the JSON blob.
        of.write('\n')                  # new line
        of.write(json_str)              # write json blob
        of.write('\n')                  # new line
        fcntl.flock(of, fcntl.LOCK_UN)  # unlock output file


if __name__ == '__main__':
  main()