1# Copyright (c) 2020 The Linux Foundation 2# 3# SPDX-License-Identifier: Apache-2.0 4 5import json 6import os 7 8from west import log 9 10import zspdx.cmakefileapi 11 12 13def parseReply(replyIndexPath): 14 replyDir, _ = os.path.split(replyIndexPath) 15 16 # first we need to find the codemodel reply file 17 try: 18 with open(replyIndexPath) as indexFile: 19 js = json.load(indexFile) 20 21 # get reply object 22 reply_dict = js.get("reply", {}) 23 if reply_dict == {}: 24 log.err('no "reply" field found in index file') 25 return None 26 # get codemodel object 27 cm_dict = reply_dict.get("codemodel-v2", {}) 28 if cm_dict == {}: 29 log.err('no "codemodel-v2" field found in "reply" object in index file') 30 return None 31 # and get codemodel filename 32 jsonFile = cm_dict.get("jsonFile", "") 33 if jsonFile == "": 34 log.err('no "jsonFile" field found in "codemodel-v2" object in index file') 35 return None 36 37 return parseCodemodel(replyDir, jsonFile) 38 39 except OSError as e: 40 log.err(f"Error loading {replyIndexPath}: {str(e)}") 41 return None 42 except json.decoder.JSONDecodeError as e: 43 log.err(f"Error parsing JSON in {replyIndexPath}: {str(e)}") 44 return None 45 46def parseCodemodel(replyDir, codemodelFile): 47 codemodelPath = os.path.join(replyDir, codemodelFile) 48 49 try: 50 with open(codemodelPath) as cmFile: 51 js = json.load(cmFile) 52 53 cm = zspdx.cmakefileapi.Codemodel() 54 55 # for correctness, check kind and version 56 kind = js.get("kind", "") 57 if kind != "codemodel": 58 log.err('Error loading CMake API reply: expected "kind":"codemodel" ' 59 f'in {codemodelPath}, got {kind}') 60 return None 61 version = js.get("version", {}) 62 versionMajor = version.get("major", -1) 63 if versionMajor != 2: 64 if versionMajor == -1: 65 log.err("Error loading CMake API reply: expected major version 2 " 66 f"in {codemodelPath}, no version found") 67 return None 68 log.err("Error loading CMake API reply: expected major version 2 " 69 f"in {codemodelPath}, got {versionMajor}") 70 return None 71 72 # get paths 73 paths_dict = js.get("paths", {}) 74 cm.paths_source = paths_dict.get("source", "") 75 cm.paths_build = paths_dict.get("build", "") 76 77 # get configurations 78 configs_arr = js.get("configurations", []) 79 for cfg_dict in configs_arr: 80 cfg = parseConfig(cfg_dict, replyDir) 81 if cfg: 82 cm.configurations.append(cfg) 83 84 # and after parsing is done, link all the indices 85 linkCodemodel(cm) 86 87 return cm 88 89 except OSError as e: 90 log.err(f"Error loading {codemodelPath}: {str(e)}") 91 return None 92 except json.decoder.JSONDecodeError as e: 93 log.err(f"Error parsing JSON in {codemodelPath}: {str(e)}") 94 return None 95 96def parseConfig(cfg_dict, replyDir): 97 cfg = zspdx.cmakefileapi.Config() 98 cfg.name = cfg_dict.get("name", "") 99 100 # parse and add each directory 101 dirs_arr = cfg_dict.get("directories", []) 102 for dir_dict in dirs_arr: 103 if dir_dict != {}: 104 cfgdir = zspdx.cmakefileapi.ConfigDir() 105 cfgdir.source = dir_dict.get("source", "") 106 cfgdir.build = dir_dict.get("build", "") 107 cfgdir.parentIndex = dir_dict.get("parentIndex", -1) 108 cfgdir.childIndexes = dir_dict.get("childIndexes", []) 109 cfgdir.projectIndex = dir_dict.get("projectIndex", -1) 110 cfgdir.targetIndexes = dir_dict.get("targetIndexes", []) 111 minCMakeVer_dict = dir_dict.get("minimumCMakeVersion", {}) 112 cfgdir.minimumCMakeVersion = minCMakeVer_dict.get("string", "") 113 cfgdir.hasInstallRule = dir_dict.get("hasInstallRule", False) 114 cfg.directories.append(cfgdir) 115 116 # parse and add each project 117 projects_arr = cfg_dict.get("projects", []) 118 for prj_dict in projects_arr: 119 if prj_dict != {}: 120 prj = zspdx.cmakefileapi.ConfigProject() 121 prj.name = prj_dict.get("name", "") 122 prj.parentIndex = prj_dict.get("parentIndex", -1) 123 prj.childIndexes = prj_dict.get("childIndexes", []) 124 prj.directoryIndexes = prj_dict.get("directoryIndexes", []) 125 prj.targetIndexes = prj_dict.get("targetIndexes", []) 126 cfg.projects.append(prj) 127 128 # parse and add each target 129 cfgTargets_arr = cfg_dict.get("targets", []) 130 for cfgTarget_dict in cfgTargets_arr: 131 if cfgTarget_dict != {}: 132 cfgTarget = zspdx.cmakefileapi.ConfigTarget() 133 cfgTarget.name = cfgTarget_dict.get("name", "") 134 cfgTarget.id = cfgTarget_dict.get("id", "") 135 cfgTarget.directoryIndex = cfgTarget_dict.get("directoryIndex", -1) 136 cfgTarget.projectIndex = cfgTarget_dict.get("projectIndex", -1) 137 cfgTarget.jsonFile = cfgTarget_dict.get("jsonFile", "") 138 139 if cfgTarget.jsonFile != "": 140 cfgTarget.target = parseTarget(os.path.join(replyDir, cfgTarget.jsonFile)) 141 else: 142 cfgTarget.target = None 143 144 cfg.configTargets.append(cfgTarget) 145 146 return cfg 147 148def parseTarget(targetPath): 149 try: 150 with open(targetPath) as targetFile: 151 js = json.load(targetFile) 152 153 target = zspdx.cmakefileapi.Target() 154 155 target.name = js.get("name", "") 156 target.id = js.get("id", "") 157 target.type = parseTargetType(js.get("type", "UNKNOWN")) 158 target.backtrace = js.get("backtrace", -1) 159 target.folder = js.get("folder", "") 160 161 # get paths 162 paths_dict = js.get("paths", {}) 163 target.paths_source = paths_dict.get("source", "") 164 target.paths_build = paths_dict.get("build", "") 165 166 target.nameOnDisk = js.get("nameOnDisk", "") 167 168 # parse artifacts if present 169 artifacts_arr = js.get("artifacts", []) 170 target.artifacts = [] 171 for artifact_dict in artifacts_arr: 172 artifact_path = artifact_dict.get("path", "") 173 if artifact_path != "": 174 target.artifacts.append(artifact_path) 175 176 target.isGeneratorProvided = js.get("isGeneratorProvided", False) 177 178 # call separate functions to parse subsections 179 parseTargetInstall(target, js) 180 parseTargetLink(target, js) 181 parseTargetArchive(target, js) 182 parseTargetDependencies(target, js) 183 parseTargetSources(target, js) 184 parseTargetSourceGroups(target, js) 185 parseTargetCompileGroups(target, js) 186 parseTargetBacktraceGraph(target, js) 187 188 return target 189 190 except OSError as e: 191 log.err(f"Error loading {targetPath}: {str(e)}") 192 return None 193 except json.decoder.JSONDecodeError as e: 194 log.err(f"Error parsing JSON in {targetPath}: {str(e)}") 195 return None 196 197def parseTargetType(targetType): 198 return { 199 "EXECUTABLE": zspdx.cmakefileapi.TargetType.EXECUTABLE, 200 "STATIC_LIBRARY": zspdx.cmakefileapi.TargetType.STATIC_LIBRARY, 201 "SHARED_LIBRARY": zspdx.cmakefileapi.TargetType.SHARED_LIBRARY, 202 "MODULE_LIBRARY": zspdx.cmakefileapi.TargetType.MODULE_LIBRARY, 203 "OBJECT_LIBRARY": zspdx.cmakefileapi.TargetType.OBJECT_LIBRARY, 204 "UTILITY": zspdx.cmakefileapi.TargetType.UTILITY, 205 }.get(targetType, zspdx.cmakefileapi.TargetType.UNKNOWN) 206 207def parseTargetInstall(target, js): 208 install_dict = js.get("install", {}) 209 if install_dict == {}: 210 return 211 prefix_dict = install_dict.get("prefix", {}) 212 target.install_prefix = prefix_dict.get("path", "") 213 214 destinations_arr = install_dict.get("destinations", []) 215 for destination_dict in destinations_arr: 216 dest = zspdx.cmakefileapi.TargetInstallDestination() 217 dest.path = destination_dict.get("path", "") 218 dest.backtrace = destination_dict.get("backtrace", -1) 219 target.install_destinations.append(dest) 220 221def parseTargetLink(target, js): 222 link_dict = js.get("link", {}) 223 if link_dict == {}: 224 return 225 target.link_language = link_dict.get("language", {}) 226 target.link_lto = link_dict.get("lto", False) 227 sysroot_dict = link_dict.get("sysroot", {}) 228 target.link_sysroot = sysroot_dict.get("path", "") 229 230 fragments_arr = link_dict.get("commandFragments", []) 231 for fragment_dict in fragments_arr: 232 fragment = zspdx.cmakefileapi.TargetCommandFragment() 233 fragment.fragment = fragment_dict.get("fragment", "") 234 fragment.role = fragment_dict.get("role", "") 235 target.link_commandFragments.append(fragment) 236 237def parseTargetArchive(target, js): 238 archive_dict = js.get("archive", {}) 239 if archive_dict == {}: 240 return 241 target.archive_lto = archive_dict.get("lto", False) 242 243 fragments_arr = archive_dict.get("commandFragments", []) 244 for fragment_dict in fragments_arr: 245 fragment = zspdx.cmakefileapi.TargetCommandFragment() 246 fragment.fragment = fragment_dict.get("fragment", "") 247 fragment.role = fragment_dict.get("role", "") 248 target.archive_commandFragments.append(fragment) 249 250def parseTargetDependencies(target, js): 251 dependencies_arr = js.get("dependencies", []) 252 for dependency_dict in dependencies_arr: 253 dep = zspdx.cmakefileapi.TargetDependency() 254 dep.id = dependency_dict.get("id", "") 255 dep.backtrace = dependency_dict.get("backtrace", -1) 256 target.dependencies.append(dep) 257 258def parseTargetSources(target, js): 259 sources_arr = js.get("sources", []) 260 for source_dict in sources_arr: 261 src = zspdx.cmakefileapi.TargetSource() 262 src.path = source_dict.get("path", "") 263 src.compileGroupIndex = source_dict.get("compileGroupIndex", -1) 264 src.sourceGroupIndex = source_dict.get("sourceGroupIndex", -1) 265 src.isGenerated = source_dict.get("isGenerated", False) 266 src.backtrace = source_dict.get("backtrace", -1) 267 target.sources.append(src) 268 269def parseTargetSourceGroups(target, js): 270 sourceGroups_arr = js.get("sourceGroups", []) 271 for sourceGroup_dict in sourceGroups_arr: 272 srcgrp = zspdx.cmakefileapi.TargetSourceGroup() 273 srcgrp.name = sourceGroup_dict.get("name", "") 274 srcgrp.sourceIndexes = sourceGroup_dict.get("sourceIndexes", []) 275 target.sourceGroups.append(srcgrp) 276 277def parseTargetCompileGroups(target, js): 278 compileGroups_arr = js.get("compileGroups", []) 279 for compileGroup_dict in compileGroups_arr: 280 cmpgrp = zspdx.cmakefileapi.TargetCompileGroup() 281 cmpgrp.sourceIndexes = compileGroup_dict.get("sourceIndexes", []) 282 cmpgrp.language = compileGroup_dict.get("language", "") 283 cmpgrp.sysroot = compileGroup_dict.get("sysroot", "") 284 285 commandFragments_arr = compileGroup_dict.get("compileCommandFragments", []) 286 for commandFragment_dict in commandFragments_arr: 287 fragment = commandFragment_dict.get("fragment", "") 288 if fragment != "": 289 cmpgrp.compileCommandFragments.append(fragment) 290 291 includes_arr = compileGroup_dict.get("includes", []) 292 for include_dict in includes_arr: 293 grpInclude = zspdx.cmakefileapi.TargetCompileGroupInclude() 294 grpInclude.path = include_dict.get("path", "") 295 grpInclude.isSystem = include_dict.get("isSystem", False) 296 grpInclude.backtrace = include_dict.get("backtrace", -1) 297 cmpgrp.includes.append(grpInclude) 298 299 precompileHeaders_arr = compileGroup_dict.get("precompileHeaders", []) 300 for precompileHeader_dict in precompileHeaders_arr: 301 grpHeader = zspdx.cmakefileapi.TargetCompileGroupPrecompileHeader() 302 grpHeader.header = precompileHeader_dict.get("header", "") 303 grpHeader.backtrace = precompileHeader_dict.get("backtrace", -1) 304 cmpgrp.precompileHeaders.append(grpHeader) 305 306 defines_arr = compileGroup_dict.get("defines", []) 307 for define_dict in defines_arr: 308 grpDefine = zspdx.cmakefileapi.TargetCompileGroupDefine() 309 grpDefine.define = define_dict.get("define", "") 310 grpDefine.backtrace = define_dict.get("backtrace", -1) 311 cmpgrp.defines.append(grpDefine) 312 313 target.compileGroups.append(cmpgrp) 314 315def parseTargetBacktraceGraph(target, js): 316 backtraceGraph_dict = js.get("backtraceGraph", {}) 317 if backtraceGraph_dict == {}: 318 return 319 target.backtraceGraph_commands = backtraceGraph_dict.get("commands", []) 320 target.backtraceGraph_files = backtraceGraph_dict.get("files", []) 321 322 nodes_arr = backtraceGraph_dict.get("nodes", []) 323 for node_dict in nodes_arr: 324 node = zspdx.cmakefileapi.TargetBacktraceGraphNode() 325 node.file = node_dict.get("file", -1) 326 node.line = node_dict.get("line", -1) 327 node.command = node_dict.get("command", -1) 328 node.parent = node_dict.get("parent", -1) 329 target.backtraceGraph_nodes.append(node) 330 331# Create direct pointers for all Configs in Codemodel 332# takes: Codemodel 333def linkCodemodel(cm): 334 for cfg in cm.configurations: 335 linkConfig(cfg) 336 337# Create direct pointers for all contents of Config 338# takes: Config 339def linkConfig(cfg): 340 for cfgDir in cfg.directories: 341 linkConfigDir(cfg, cfgDir) 342 for cfgPrj in cfg.projects: 343 linkConfigProject(cfg, cfgPrj) 344 for cfgTarget in cfg.configTargets: 345 linkConfigTarget(cfg, cfgTarget) 346 347# Create direct pointers for ConfigDir indices 348# takes: Config and ConfigDir 349def linkConfigDir(cfg, cfgDir): 350 if cfgDir.parentIndex == -1: 351 cfgDir.parent = None 352 else: 353 cfgDir.parent = cfg.directories[cfgDir.parentIndex] 354 355 if cfgDir.projectIndex == -1: 356 cfgDir.project = None 357 else: 358 cfgDir.project = cfg.projects[cfgDir.projectIndex] 359 360 cfgDir.children = [] 361 for childIndex in cfgDir.childIndexes: 362 cfgDir.children.append(cfg.directories[childIndex]) 363 364 cfgDir.targets = [] 365 for targetIndex in cfgDir.targetIndexes: 366 cfgDir.targets.append(cfg.configTargets[targetIndex]) 367 368# Create direct pointers for ConfigProject indices 369# takes: Config and ConfigProject 370def linkConfigProject(cfg, cfgPrj): 371 if cfgPrj.parentIndex == -1: 372 cfgPrj.parent = None 373 else: 374 cfgPrj.parent = cfg.projects[cfgPrj.parentIndex] 375 376 cfgPrj.children = [] 377 for childIndex in cfgPrj.childIndexes: 378 cfgPrj.children.append(cfg.projects[childIndex]) 379 380 cfgPrj.directories = [] 381 for dirIndex in cfgPrj.directoryIndexes: 382 cfgPrj.directories.append(cfg.directories[dirIndex]) 383 384 cfgPrj.targets = [] 385 for targetIndex in cfgPrj.targetIndexes: 386 cfgPrj.targets.append(cfg.configTargets[targetIndex]) 387 388# Create direct pointers for ConfigTarget indices 389# takes: Config and ConfigTarget 390def linkConfigTarget(cfg, cfgTarget): 391 if cfgTarget.directoryIndex == -1: 392 cfgTarget.directory = None 393 else: 394 cfgTarget.directory = cfg.directories[cfgTarget.directoryIndex] 395 396 if cfgTarget.projectIndex == -1: 397 cfgTarget.project = None 398 else: 399 cfgTarget.project = cfg.projects[cfgTarget.projectIndex] 400 401 # and link target's sources and source groups 402 for ts in cfgTarget.target.sources: 403 linkTargetSource(cfgTarget.target, ts) 404 for tsg in cfgTarget.target.sourceGroups: 405 linkTargetSourceGroup(cfgTarget.target, tsg) 406 for tcg in cfgTarget.target.compileGroups: 407 linkTargetCompileGroup(cfgTarget.target, tcg) 408 409# Create direct pointers for TargetSource indices 410# takes: Target and TargetSource 411def linkTargetSource(target, targetSrc): 412 if targetSrc.compileGroupIndex == -1: 413 targetSrc.compileGroup = None 414 else: 415 targetSrc.compileGroup = target.compileGroups[targetSrc.compileGroupIndex] 416 417 if targetSrc.sourceGroupIndex == -1: 418 targetSrc.sourceGroup = None 419 else: 420 targetSrc.sourceGroup = target.sourceGroups[targetSrc.sourceGroupIndex] 421 422# Create direct pointers for TargetSourceGroup indices 423# takes: Target and TargetSourceGroup 424def linkTargetSourceGroup(target, targetSrcGrp): 425 targetSrcGrp.sources = [] 426 for srcIndex in targetSrcGrp.sourceIndexes: 427 targetSrcGrp.sources.append(target.sources[srcIndex]) 428 429# Create direct pointers for TargetCompileGroup indices 430# takes: Target and TargetCompileGroup 431def linkTargetCompileGroup(target, targetCmpGrp): 432 targetCmpGrp.sources = [] 433 for srcIndex in targetCmpGrp.sourceIndexes: 434 targetCmpGrp.sources.append(target.sources[srcIndex]) 435