You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
434 lines
17 KiB
434 lines
17 KiB
# Copyright (c) 2020 The Linux Foundation |
|
# |
|
# SPDX-License-Identifier: Apache-2.0 |
|
|
|
import json |
|
import os |
|
|
|
from west import log |
|
|
|
import zspdx.cmakefileapi |
|
|
|
|
|
def parseReply(replyIndexPath): |
|
replyDir, _ = os.path.split(replyIndexPath) |
|
|
|
# first we need to find the codemodel reply file |
|
try: |
|
with open(replyIndexPath) as indexFile: |
|
js = json.load(indexFile) |
|
|
|
# get reply object |
|
reply_dict = js.get("reply", {}) |
|
if reply_dict == {}: |
|
log.err('no "reply" field found in index file') |
|
return None |
|
# get codemodel object |
|
cm_dict = reply_dict.get("codemodel-v2", {}) |
|
if cm_dict == {}: |
|
log.err('no "codemodel-v2" field found in "reply" object in index file') |
|
return None |
|
# and get codemodel filename |
|
jsonFile = cm_dict.get("jsonFile", "") |
|
if jsonFile == "": |
|
log.err('no "jsonFile" field found in "codemodel-v2" object in index file') |
|
return None |
|
|
|
return parseCodemodel(replyDir, jsonFile) |
|
|
|
except OSError as e: |
|
log.err(f"Error loading {replyIndexPath}: {str(e)}") |
|
return None |
|
except json.decoder.JSONDecodeError as e: |
|
log.err(f"Error parsing JSON in {replyIndexPath}: {str(e)}") |
|
return None |
|
|
|
def parseCodemodel(replyDir, codemodelFile): |
|
codemodelPath = os.path.join(replyDir, codemodelFile) |
|
|
|
try: |
|
with open(codemodelPath) as cmFile: |
|
js = json.load(cmFile) |
|
|
|
cm = zspdx.cmakefileapi.Codemodel() |
|
|
|
# for correctness, check kind and version |
|
kind = js.get("kind", "") |
|
if kind != "codemodel": |
|
log.err('Error loading CMake API reply: expected "kind":"codemodel" ' |
|
f'in {codemodelPath}, got {kind}') |
|
return None |
|
version = js.get("version", {}) |
|
versionMajor = version.get("major", -1) |
|
if versionMajor != 2: |
|
if versionMajor == -1: |
|
log.err("Error loading CMake API reply: expected major version 2 " |
|
f"in {codemodelPath}, no version found") |
|
return None |
|
log.err("Error loading CMake API reply: expected major version 2 " |
|
f"in {codemodelPath}, got {versionMajor}") |
|
return None |
|
|
|
# get paths |
|
paths_dict = js.get("paths", {}) |
|
cm.paths_source = paths_dict.get("source", "") |
|
cm.paths_build = paths_dict.get("build", "") |
|
|
|
# get configurations |
|
configs_arr = js.get("configurations", []) |
|
for cfg_dict in configs_arr: |
|
cfg = parseConfig(cfg_dict, replyDir) |
|
if cfg: |
|
cm.configurations.append(cfg) |
|
|
|
# and after parsing is done, link all the indices |
|
linkCodemodel(cm) |
|
|
|
return cm |
|
|
|
except OSError as e: |
|
log.err(f"Error loading {codemodelPath}: {str(e)}") |
|
return None |
|
except json.decoder.JSONDecodeError as e: |
|
log.err(f"Error parsing JSON in {codemodelPath}: {str(e)}") |
|
return None |
|
|
|
def parseConfig(cfg_dict, replyDir): |
|
cfg = zspdx.cmakefileapi.Config() |
|
cfg.name = cfg_dict.get("name", "") |
|
|
|
# parse and add each directory |
|
dirs_arr = cfg_dict.get("directories", []) |
|
for dir_dict in dirs_arr: |
|
if dir_dict != {}: |
|
cfgdir = zspdx.cmakefileapi.ConfigDir() |
|
cfgdir.source = dir_dict.get("source", "") |
|
cfgdir.build = dir_dict.get("build", "") |
|
cfgdir.parentIndex = dir_dict.get("parentIndex", -1) |
|
cfgdir.childIndexes = dir_dict.get("childIndexes", []) |
|
cfgdir.projectIndex = dir_dict.get("projectIndex", -1) |
|
cfgdir.targetIndexes = dir_dict.get("targetIndexes", []) |
|
minCMakeVer_dict = dir_dict.get("minimumCMakeVersion", {}) |
|
cfgdir.minimumCMakeVersion = minCMakeVer_dict.get("string", "") |
|
cfgdir.hasInstallRule = dir_dict.get("hasInstallRule", False) |
|
cfg.directories.append(cfgdir) |
|
|
|
# parse and add each project |
|
projects_arr = cfg_dict.get("projects", []) |
|
for prj_dict in projects_arr: |
|
if prj_dict != {}: |
|
prj = zspdx.cmakefileapi.ConfigProject() |
|
prj.name = prj_dict.get("name", "") |
|
prj.parentIndex = prj_dict.get("parentIndex", -1) |
|
prj.childIndexes = prj_dict.get("childIndexes", []) |
|
prj.directoryIndexes = prj_dict.get("directoryIndexes", []) |
|
prj.targetIndexes = prj_dict.get("targetIndexes", []) |
|
cfg.projects.append(prj) |
|
|
|
# parse and add each target |
|
cfgTargets_arr = cfg_dict.get("targets", []) |
|
for cfgTarget_dict in cfgTargets_arr: |
|
if cfgTarget_dict != {}: |
|
cfgTarget = zspdx.cmakefileapi.ConfigTarget() |
|
cfgTarget.name = cfgTarget_dict.get("name", "") |
|
cfgTarget.id = cfgTarget_dict.get("id", "") |
|
cfgTarget.directoryIndex = cfgTarget_dict.get("directoryIndex", -1) |
|
cfgTarget.projectIndex = cfgTarget_dict.get("projectIndex", -1) |
|
cfgTarget.jsonFile = cfgTarget_dict.get("jsonFile", "") |
|
|
|
if cfgTarget.jsonFile != "": |
|
cfgTarget.target = parseTarget(os.path.join(replyDir, cfgTarget.jsonFile)) |
|
else: |
|
cfgTarget.target = None |
|
|
|
cfg.configTargets.append(cfgTarget) |
|
|
|
return cfg |
|
|
|
def parseTarget(targetPath): |
|
try: |
|
with open(targetPath) as targetFile: |
|
js = json.load(targetFile) |
|
|
|
target = zspdx.cmakefileapi.Target() |
|
|
|
target.name = js.get("name", "") |
|
target.id = js.get("id", "") |
|
target.type = parseTargetType(js.get("type", "UNKNOWN")) |
|
target.backtrace = js.get("backtrace", -1) |
|
target.folder = js.get("folder", "") |
|
|
|
# get paths |
|
paths_dict = js.get("paths", {}) |
|
target.paths_source = paths_dict.get("source", "") |
|
target.paths_build = paths_dict.get("build", "") |
|
|
|
target.nameOnDisk = js.get("nameOnDisk", "") |
|
|
|
# parse artifacts if present |
|
artifacts_arr = js.get("artifacts", []) |
|
target.artifacts = [] |
|
for artifact_dict in artifacts_arr: |
|
artifact_path = artifact_dict.get("path", "") |
|
if artifact_path != "": |
|
target.artifacts.append(artifact_path) |
|
|
|
target.isGeneratorProvided = js.get("isGeneratorProvided", False) |
|
|
|
# call separate functions to parse subsections |
|
parseTargetInstall(target, js) |
|
parseTargetLink(target, js) |
|
parseTargetArchive(target, js) |
|
parseTargetDependencies(target, js) |
|
parseTargetSources(target, js) |
|
parseTargetSourceGroups(target, js) |
|
parseTargetCompileGroups(target, js) |
|
parseTargetBacktraceGraph(target, js) |
|
|
|
return target |
|
|
|
except OSError as e: |
|
log.err(f"Error loading {targetPath}: {str(e)}") |
|
return None |
|
except json.decoder.JSONDecodeError as e: |
|
log.err(f"Error parsing JSON in {targetPath}: {str(e)}") |
|
return None |
|
|
|
def parseTargetType(targetType): |
|
return { |
|
"EXECUTABLE": zspdx.cmakefileapi.TargetType.EXECUTABLE, |
|
"STATIC_LIBRARY": zspdx.cmakefileapi.TargetType.STATIC_LIBRARY, |
|
"SHARED_LIBRARY": zspdx.cmakefileapi.TargetType.SHARED_LIBRARY, |
|
"MODULE_LIBRARY": zspdx.cmakefileapi.TargetType.MODULE_LIBRARY, |
|
"OBJECT_LIBRARY": zspdx.cmakefileapi.TargetType.OBJECT_LIBRARY, |
|
"UTILITY": zspdx.cmakefileapi.TargetType.UTILITY, |
|
}.get(targetType, zspdx.cmakefileapi.TargetType.UNKNOWN) |
|
|
|
def parseTargetInstall(target, js): |
|
install_dict = js.get("install", {}) |
|
if install_dict == {}: |
|
return |
|
prefix_dict = install_dict.get("prefix", {}) |
|
target.install_prefix = prefix_dict.get("path", "") |
|
|
|
destinations_arr = install_dict.get("destinations", []) |
|
for destination_dict in destinations_arr: |
|
dest = zspdx.cmakefileapi.TargetInstallDestination() |
|
dest.path = destination_dict.get("path", "") |
|
dest.backtrace = destination_dict.get("backtrace", -1) |
|
target.install_destinations.append(dest) |
|
|
|
def parseTargetLink(target, js): |
|
link_dict = js.get("link", {}) |
|
if link_dict == {}: |
|
return |
|
target.link_language = link_dict.get("language", {}) |
|
target.link_lto = link_dict.get("lto", False) |
|
sysroot_dict = link_dict.get("sysroot", {}) |
|
target.link_sysroot = sysroot_dict.get("path", "") |
|
|
|
fragments_arr = link_dict.get("commandFragments", []) |
|
for fragment_dict in fragments_arr: |
|
fragment = zspdx.cmakefileapi.TargetCommandFragment() |
|
fragment.fragment = fragment_dict.get("fragment", "") |
|
fragment.role = fragment_dict.get("role", "") |
|
target.link_commandFragments.append(fragment) |
|
|
|
def parseTargetArchive(target, js): |
|
archive_dict = js.get("archive", {}) |
|
if archive_dict == {}: |
|
return |
|
target.archive_lto = archive_dict.get("lto", False) |
|
|
|
fragments_arr = archive_dict.get("commandFragments", []) |
|
for fragment_dict in fragments_arr: |
|
fragment = zspdx.cmakefileapi.TargetCommandFragment() |
|
fragment.fragment = fragment_dict.get("fragment", "") |
|
fragment.role = fragment_dict.get("role", "") |
|
target.archive_commandFragments.append(fragment) |
|
|
|
def parseTargetDependencies(target, js): |
|
dependencies_arr = js.get("dependencies", []) |
|
for dependency_dict in dependencies_arr: |
|
dep = zspdx.cmakefileapi.TargetDependency() |
|
dep.id = dependency_dict.get("id", "") |
|
dep.backtrace = dependency_dict.get("backtrace", -1) |
|
target.dependencies.append(dep) |
|
|
|
def parseTargetSources(target, js): |
|
sources_arr = js.get("sources", []) |
|
for source_dict in sources_arr: |
|
src = zspdx.cmakefileapi.TargetSource() |
|
src.path = source_dict.get("path", "") |
|
src.compileGroupIndex = source_dict.get("compileGroupIndex", -1) |
|
src.sourceGroupIndex = source_dict.get("sourceGroupIndex", -1) |
|
src.isGenerated = source_dict.get("isGenerated", False) |
|
src.backtrace = source_dict.get("backtrace", -1) |
|
target.sources.append(src) |
|
|
|
def parseTargetSourceGroups(target, js): |
|
sourceGroups_arr = js.get("sourceGroups", []) |
|
for sourceGroup_dict in sourceGroups_arr: |
|
srcgrp = zspdx.cmakefileapi.TargetSourceGroup() |
|
srcgrp.name = sourceGroup_dict.get("name", "") |
|
srcgrp.sourceIndexes = sourceGroup_dict.get("sourceIndexes", []) |
|
target.sourceGroups.append(srcgrp) |
|
|
|
def parseTargetCompileGroups(target, js): |
|
compileGroups_arr = js.get("compileGroups", []) |
|
for compileGroup_dict in compileGroups_arr: |
|
cmpgrp = zspdx.cmakefileapi.TargetCompileGroup() |
|
cmpgrp.sourceIndexes = compileGroup_dict.get("sourceIndexes", []) |
|
cmpgrp.language = compileGroup_dict.get("language", "") |
|
cmpgrp.sysroot = compileGroup_dict.get("sysroot", "") |
|
|
|
commandFragments_arr = compileGroup_dict.get("compileCommandFragments", []) |
|
for commandFragment_dict in commandFragments_arr: |
|
fragment = commandFragment_dict.get("fragment", "") |
|
if fragment != "": |
|
cmpgrp.compileCommandFragments.append(fragment) |
|
|
|
includes_arr = compileGroup_dict.get("includes", []) |
|
for include_dict in includes_arr: |
|
grpInclude = zspdx.cmakefileapi.TargetCompileGroupInclude() |
|
grpInclude.path = include_dict.get("path", "") |
|
grpInclude.isSystem = include_dict.get("isSystem", False) |
|
grpInclude.backtrace = include_dict.get("backtrace", -1) |
|
cmpgrp.includes.append(grpInclude) |
|
|
|
precompileHeaders_arr = compileGroup_dict.get("precompileHeaders", []) |
|
for precompileHeader_dict in precompileHeaders_arr: |
|
grpHeader = zspdx.cmakefileapi.TargetCompileGroupPrecompileHeader() |
|
grpHeader.header = precompileHeader_dict.get("header", "") |
|
grpHeader.backtrace = precompileHeader_dict.get("backtrace", -1) |
|
cmpgrp.precompileHeaders.append(grpHeader) |
|
|
|
defines_arr = compileGroup_dict.get("defines", []) |
|
for define_dict in defines_arr: |
|
grpDefine = zspdx.cmakefileapi.TargetCompileGroupDefine() |
|
grpDefine.define = define_dict.get("define", "") |
|
grpDefine.backtrace = define_dict.get("backtrace", -1) |
|
cmpgrp.defines.append(grpDefine) |
|
|
|
target.compileGroups.append(cmpgrp) |
|
|
|
def parseTargetBacktraceGraph(target, js): |
|
backtraceGraph_dict = js.get("backtraceGraph", {}) |
|
if backtraceGraph_dict == {}: |
|
return |
|
target.backtraceGraph_commands = backtraceGraph_dict.get("commands", []) |
|
target.backtraceGraph_files = backtraceGraph_dict.get("files", []) |
|
|
|
nodes_arr = backtraceGraph_dict.get("nodes", []) |
|
for node_dict in nodes_arr: |
|
node = zspdx.cmakefileapi.TargetBacktraceGraphNode() |
|
node.file = node_dict.get("file", -1) |
|
node.line = node_dict.get("line", -1) |
|
node.command = node_dict.get("command", -1) |
|
node.parent = node_dict.get("parent", -1) |
|
target.backtraceGraph_nodes.append(node) |
|
|
|
# Create direct pointers for all Configs in Codemodel |
|
# takes: Codemodel |
|
def linkCodemodel(cm): |
|
for cfg in cm.configurations: |
|
linkConfig(cfg) |
|
|
|
# Create direct pointers for all contents of Config |
|
# takes: Config |
|
def linkConfig(cfg): |
|
for cfgDir in cfg.directories: |
|
linkConfigDir(cfg, cfgDir) |
|
for cfgPrj in cfg.projects: |
|
linkConfigProject(cfg, cfgPrj) |
|
for cfgTarget in cfg.configTargets: |
|
linkConfigTarget(cfg, cfgTarget) |
|
|
|
# Create direct pointers for ConfigDir indices |
|
# takes: Config and ConfigDir |
|
def linkConfigDir(cfg, cfgDir): |
|
if cfgDir.parentIndex == -1: |
|
cfgDir.parent = None |
|
else: |
|
cfgDir.parent = cfg.directories[cfgDir.parentIndex] |
|
|
|
if cfgDir.projectIndex == -1: |
|
cfgDir.project = None |
|
else: |
|
cfgDir.project = cfg.projects[cfgDir.projectIndex] |
|
|
|
cfgDir.children = [] |
|
for childIndex in cfgDir.childIndexes: |
|
cfgDir.children.append(cfg.directories[childIndex]) |
|
|
|
cfgDir.targets = [] |
|
for targetIndex in cfgDir.targetIndexes: |
|
cfgDir.targets.append(cfg.configTargets[targetIndex]) |
|
|
|
# Create direct pointers for ConfigProject indices |
|
# takes: Config and ConfigProject |
|
def linkConfigProject(cfg, cfgPrj): |
|
if cfgPrj.parentIndex == -1: |
|
cfgPrj.parent = None |
|
else: |
|
cfgPrj.parent = cfg.projects[cfgPrj.parentIndex] |
|
|
|
cfgPrj.children = [] |
|
for childIndex in cfgPrj.childIndexes: |
|
cfgPrj.children.append(cfg.projects[childIndex]) |
|
|
|
cfgPrj.directories = [] |
|
for dirIndex in cfgPrj.directoryIndexes: |
|
cfgPrj.directories.append(cfg.directories[dirIndex]) |
|
|
|
cfgPrj.targets = [] |
|
for targetIndex in cfgPrj.targetIndexes: |
|
cfgPrj.targets.append(cfg.configTargets[targetIndex]) |
|
|
|
# Create direct pointers for ConfigTarget indices |
|
# takes: Config and ConfigTarget |
|
def linkConfigTarget(cfg, cfgTarget): |
|
if cfgTarget.directoryIndex == -1: |
|
cfgTarget.directory = None |
|
else: |
|
cfgTarget.directory = cfg.directories[cfgTarget.directoryIndex] |
|
|
|
if cfgTarget.projectIndex == -1: |
|
cfgTarget.project = None |
|
else: |
|
cfgTarget.project = cfg.projects[cfgTarget.projectIndex] |
|
|
|
# and link target's sources and source groups |
|
for ts in cfgTarget.target.sources: |
|
linkTargetSource(cfgTarget.target, ts) |
|
for tsg in cfgTarget.target.sourceGroups: |
|
linkTargetSourceGroup(cfgTarget.target, tsg) |
|
for tcg in cfgTarget.target.compileGroups: |
|
linkTargetCompileGroup(cfgTarget.target, tcg) |
|
|
|
# Create direct pointers for TargetSource indices |
|
# takes: Target and TargetSource |
|
def linkTargetSource(target, targetSrc): |
|
if targetSrc.compileGroupIndex == -1: |
|
targetSrc.compileGroup = None |
|
else: |
|
targetSrc.compileGroup = target.compileGroups[targetSrc.compileGroupIndex] |
|
|
|
if targetSrc.sourceGroupIndex == -1: |
|
targetSrc.sourceGroup = None |
|
else: |
|
targetSrc.sourceGroup = target.sourceGroups[targetSrc.sourceGroupIndex] |
|
|
|
# Create direct pointers for TargetSourceGroup indices |
|
# takes: Target and TargetSourceGroup |
|
def linkTargetSourceGroup(target, targetSrcGrp): |
|
targetSrcGrp.sources = [] |
|
for srcIndex in targetSrcGrp.sourceIndexes: |
|
targetSrcGrp.sources.append(target.sources[srcIndex]) |
|
|
|
# Create direct pointers for TargetCompileGroup indices |
|
# takes: Target and TargetCompileGroup |
|
def linkTargetCompileGroup(target, targetCmpGrp): |
|
targetCmpGrp.sources = [] |
|
for srcIndex in targetCmpGrp.sourceIndexes: |
|
targetCmpGrp.sources.append(target.sources[srcIndex])
|
|
|