1# Copyright (c) 2021 The Linux Foundation
2#
3# SPDX-License-Identifier: Apache-2.0
4
5from subprocess import run, PIPE
6
7from west import log
8
9
10# Given a path to the applicable C compiler, a C source file, and the
11# corresponding TargetCompileGroup, determine which include files would
12# be used.
13# Arguments:
14#   1) path to applicable C compiler
15#   2) C source file being analyzed
16#   3) TargetCompileGroup for the current target
17# Returns: list of paths to include files, or [] on error or empty findings.
18def getCIncludes(compilerPath, srcFile, tcg):
19    log.dbg(f"    - getting includes for {srcFile}")
20
21    # prepare fragments
22    fragments = [fr for fr in tcg.compileCommandFragments if fr.strip() != ""]
23
24    # prepare include arguments
25    includes = ["-I" + incl.path for incl in tcg.includes]
26
27    # prepare defines
28    defines = ["-D" + d.define for d in tcg.defines]
29
30    # prepare command invocation
31    cmd = [compilerPath, "-E", "-H"] + fragments + includes + defines + [srcFile]
32
33    cp = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
34    if cp.returncode != 0:
35        log.dbg(f"    - calling {compilerPath} failed with error code {cp.returncode}")
36        return []
37    else:
38        # response will be in cp.stderr, not cp.stdout
39        return extractIncludes(cp.stderr)
40
41
42# Parse the response from the CC -E -H call, to extract the include file paths
43def extractIncludes(resp):
44    includes = set()
45
46    # lines we want will start with one or more periods, followed by
47    # a space and then the include file path, e.g.:
48    # .... /home/steve/programming/zephyr/zephyrproject/zephyr/include/kernel.h
49    # the number of periods indicates the depth of nesting (for transitively-
50    # included files), but here we aren't going to care about that. We'll
51    # treat everything as tied to the corresponding source file.
52
53    # once we hit the line "Multiple include guards may be useful for:",
54    # we're done; ignore everything after that
55
56    for rline in resp.splitlines():
57        if rline.startswith("Multiple include guards"):
58            break
59        if rline[0] == ".":
60            sline = rline.split(" ", maxsplit=1)
61            if len(sline) != 2:
62                continue
63            includes.add(sline[1])
64
65    includesList = list(includes)
66    includesList.sort()
67    return includesList
68