#!/usr/bin/python3
"""List every source-tree file a kbuild build compiled, from its .*.o.cmd files.

Usage: compiled-files BUILD_DIR SOURCE_DIR > compiled-files.txt

Prints one path per line, relative to SOURCE_DIR and sorted: the .c/.S file of
each object plus every header it included.  Generated files (those outside
SOURCE_DIR) are left out, since CVE records only name source-tree files.  Used
to mark kernel CVEs in code this flavour does not build as not affected.
"""
import functools
import os
import sys

build, source = (os.path.realpath(p) for p in sys.argv[1:3])


@functools.cache
def resolve(path):
    # .cmd paths are absolute or relative to the top of the build tree; the
    # same headers recur in nearly every file, so resolve each only once
    path = os.path.realpath(os.path.join(build, path))
    if path.startswith(source + os.sep):
        return os.path.relpath(path, source)
    return None


found = set()
for top, _, names in os.walk(build):
    for name in names:
        if not (name.startswith('.') and name.endswith('.o.cmd')):
            continue
        in_deps = False
        with open(os.path.join(top, name), errors='replace') as f:
            for line in f:
                if in_deps:
                    # "deps_foo.o := \" is followed by one dependency per
                    # line, each ending in "\", up to a blank line
                    dep = line.strip().removesuffix('\\').strip()
                    if not dep:
                        if not line.strip():
                            in_deps = False
                        continue
                    if not dep.startswith('$(wildcard'):
                        found.add(resolve(dep))
                elif line.startswith('source_'):
                    found.add(resolve(line.split(':=', 1)[1].strip()))
                elif line.startswith('deps_'):
                    in_deps = True

found.discard(None)
if not found:
    sys.exit(f'compiled-files: no compiled source files found under {build}')
sys.stdout.writelines(path + '\n' for path in sorted(found))
