cmakefileapijson.py 17 KB

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