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