This adds support to generate SPDX 2.2 tag-value documents via the
new west spdx command. The CMake file-based APIs are leveraged to
create relationships from source files to the corresponding
generated build files. SPDX-License-Identifier comments in source
files are scanned and filled into the SPDX documents.
Before `west build` is run, a specific file must be created in the
build directory so that the CMake API reply will run. This can be
done by running:
west spdx --init -d BUILD_DIR
After `west build` is run, SPDX generation is then activated by
calling `west spdx`; currently this requires passing the build
directory as a parameter again:
west spdx -d BUILD_DIR
This will generate three SPDX documents in `BUILD_DIR/spdx/`:
1) `app.spdx`: This contains the bill-of-materials for the
application source files used for the build.
2) `zephyr.spdx`: This contains the bill-of-materials for the
specific Zephyr source code files that are used for the build.
3) `build.spdx`: This contains the bill-of-materials for the built
output files.
Each file in the bill-of-materials is scanned, so that its hashes
(SHA256 and SHA1) can be recorded, along with any detected licenses
if an `SPDX-License-Identifier` appears in the file.
SPDX Relationships are created to indicate dependencies between
CMake build targets; build targets that are linked together; and
source files that are compiled to generate the built library files.
`west spdx` can be called with optional parameters for further
configuration:
* `-n PREFIX`: specifies a prefix for the Document Namespaces that
will be included in the generated SPDX documents. See SPDX spec 2.2
section 2.5 at
https://spdx.github.io/spdx-spec/2-document-creation-information/.
If -n is omitted, a default namespace will be generated according
to the default format described in section 2.5 using a random UUID.
* `-s SPDX_DIR`: specifies an alternate directory where the SPDX
documents should be written. If not specified, they will be saved
in `BUILD_DIR/spdx/`.
* `--analyze-includes`: in addition to recording the compiled
source code files (e.g. `.c`, `.S`) in the bills-of-materials, if
this flag is specified, `west spdx` will attempt to determine the
specific header files that are included for each `.c` file. This
will take longer, as it performs a dry run using the C compiler
for each `.c` file (using the same arguments that were passed to it
for the actual build).
* `--include-sdk`: if `--analyze-includes` is used, then adding
`--include-sdk` will create a fourth SPDX document, `sdk.spdx`,
which will list any header files included from the SDK.
Signed-off-by: Steve Winslow <steve@swinslow.net>
66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
# Copyright (c) 2021 The Linux Foundation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from subprocess import run, PIPE
|
|
|
|
from west import log
|
|
|
|
# Given a path to the applicable C compiler, a C source file, and the
|
|
# corresponding TargetCompileGroup, determine which include files would
|
|
# be used.
|
|
# Arguments:
|
|
# 1) path to applicable C compiler
|
|
# 2) C source file being analyzed
|
|
# 3) TargetCompileGroup for the current target
|
|
# Returns: list of paths to include files, or [] on error or empty findings.
|
|
def getCIncludes(compilerPath, srcFile, tcg):
|
|
log.dbg(f" - getting includes for {srcFile}")
|
|
|
|
# prepare fragments
|
|
fragments = [fr for fr in tcg.compileCommandFragments if fr.strip() != ""]
|
|
|
|
# prepare include arguments
|
|
includes = ["-I" + incl.path for incl in tcg.includes]
|
|
|
|
# prepare defines
|
|
defines = ["-D" + d.define for d in tcg.defines]
|
|
|
|
# prepare command invocation
|
|
cmd = [compilerPath, "-E", "-H"] + fragments + includes + defines + [srcFile]
|
|
|
|
cp = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
|
|
if cp.returncode != 0:
|
|
log.dbg(f" - calling {compilerPath} failed with error code {cp.returncode}")
|
|
return []
|
|
else:
|
|
# response will be in cp.stderr, not cp.stdout
|
|
return extractIncludes(cp.stderr)
|
|
|
|
# Parse the response from the CC -E -H call, to extract the include file paths
|
|
def extractIncludes(resp):
|
|
includes = set()
|
|
|
|
# lines we want will start with one or more periods, followed by
|
|
# a space and then the include file path, e.g.:
|
|
# .... /home/steve/programming/zephyr/zephyrproject/zephyr/include/kernel.h
|
|
# the number of periods indicates the depth of nesting (for transitively-
|
|
# included files), but here we aren't going to care about that. We'll
|
|
# treat everything as tied to the corresponding source file.
|
|
|
|
# once we hit the line "Multiple include guards may be useful for:",
|
|
# we're done; ignore everything after that
|
|
|
|
for rline in resp.splitlines():
|
|
if rline.startswith("Multiple include guards"):
|
|
break
|
|
if rline[0] == ".":
|
|
sline = rline.split(" ", maxsplit=1)
|
|
if len(sline) != 2:
|
|
continue
|
|
includes.add(sline[1])
|
|
|
|
includesList = list(includes)
|
|
includesList.sort()
|
|
return includesList
|