123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- # 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("projecttIndex", -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])
|