| # 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, 'r') as indexFile: |
| js = json.load(indexFile) |
| |
| # get reply object |
| reply_dict = js.get("reply", {}) |
| if reply_dict == {}: |
| log.err(f"no \"reply\" field found in index file") |
| return None |
| # get codemodel object |
| cm_dict = reply_dict.get("codemodel-v2", {}) |
| if cm_dict == {}: |
| log.err(f"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(f"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, 'r') 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(f"Error loading CMake API reply: expected \"kind\":\"codemodel\" in {codemodelPath}, got {kind}") |
| return None |
| version = js.get("version", {}) |
| versionMajor = version.get("major", -1) |
| if versionMajor != 2: |
| if versionMajor == -1: |
| log.err(f"Error loading CMake API reply: expected major version 2 in {codemodelPath}, no version found") |
| return None |
| log.err(f"Error loading CMake API reply: expected major version 2 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, 'r') 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): |
| if targetType == "EXECUTABLE": |
| return zspdx.cmakefileapi.TargetType.EXECUTABLE |
| elif targetType == "STATIC_LIBRARY": |
| return zspdx.cmakefileapi.TargetType.STATIC_LIBRARY |
| elif targetType == "SHARED_LIBRARY": |
| return zspdx.cmakefileapi.TargetType.SHARED_LIBRARY |
| elif targetType == "MODULE_LIBRARY": |
| return zspdx.cmakefileapi.TargetType.MODULE_LIBRARY |
| elif targetType == "OBJECT_LIBRARY": |
| return zspdx.cmakefileapi.TargetType.OBJECT_LIBRARY |
| elif targetType == "UTILITY": |
| return zspdx.cmakefileapi.TargetType.UTILITY |
| else: |
| return 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]) |