writer.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. # Copyright (c) 2020, 2021 The Linux Foundation
  2. #
  3. # SPDX-License-Identifier: Apache-2.0
  4. from datetime import datetime
  5. from west import log
  6. from zspdx.util import getHashes
  7. # Output tag-value SPDX 2.2 content for the given Relationship object.
  8. # Arguments:
  9. # 1) f: file handle for SPDX document
  10. # 2) rln: Relationship object being described
  11. def writeRelationshipSPDX(f, rln):
  12. f.write(f"Relationship: {rln.refA} {rln.rlnType} {rln.refB}\n")
  13. # Output tag-value SPDX 2.2 content for the given File object.
  14. # Arguments:
  15. # 1) f: file handle for SPDX document
  16. # 2) bf: File object being described
  17. def writeFileSPDX(f, bf):
  18. f.write(f"""FileName: ./{bf.relpath}
  19. SPDXID: {bf.spdxID}
  20. FileChecksum: SHA1: {bf.sha1}
  21. """)
  22. if bf.sha256 != "":
  23. f.write(f"FileChecksum: SHA256: {bf.sha256}\n")
  24. if bf.md5 != "":
  25. f.write(f"FileChecksum: MD5: {bf.md5}\n")
  26. f.write(f"LicenseConcluded: {bf.concludedLicense}\n")
  27. if len(bf.licenseInfoInFile) == 0:
  28. f.write(f"LicenseInfoInFile: NONE\n")
  29. else:
  30. for licInfoInFile in bf.licenseInfoInFile:
  31. f.write(f"LicenseInfoInFile: {licInfoInFile}\n")
  32. f.write(f"FileCopyrightText: {bf.copyrightText}\n\n")
  33. # write file relationships
  34. if len(bf.rlns) > 0:
  35. for rln in bf.rlns:
  36. writeRelationshipSPDX(f, rln)
  37. f.write("\n")
  38. # Output tag-value SPDX 2.2 content for the given Package object.
  39. # Arguments:
  40. # 1) f: file handle for SPDX document
  41. # 2) pkg: Package object being described
  42. def writePackageSPDX(f, pkg):
  43. f.write(f"""##### Package: {pkg.cfg.name}
  44. PackageName: {pkg.cfg.name}
  45. SPDXID: {pkg.cfg.spdxID}
  46. PackageDownloadLocation: NOASSERTION
  47. PackageLicenseConcluded: {pkg.concludedLicense}
  48. """)
  49. for licFromFiles in pkg.licenseInfoFromFiles:
  50. f.write(f"PackageLicenseInfoFromFiles: {licFromFiles}\n")
  51. f.write(f"""PackageLicenseDeclared: {pkg.cfg.declaredLicense}
  52. PackageCopyrightText: {pkg.cfg.copyrightText}
  53. """)
  54. # flag whether files analyzed / any files present
  55. if len(pkg.files) > 0:
  56. f.write(f"FilesAnalyzed: true\nPackageVerificationCode: {pkg.verificationCode}\n\n")
  57. else:
  58. f.write(f"FilesAnalyzed: false\nPackageComment: Utility target; no files\n\n")
  59. # write package relationships
  60. if len(pkg.rlns) > 0:
  61. for rln in pkg.rlns:
  62. writeRelationshipSPDX(f, rln)
  63. f.write("\n")
  64. # write package files, if any
  65. if len(pkg.files) > 0:
  66. bfs = list(pkg.files.values())
  67. bfs.sort(key = lambda x: x.relpath)
  68. for bf in bfs:
  69. writeFileSPDX(f, bf)
  70. # Output tag-value SPDX 2.2 content for a custom license.
  71. # Arguments:
  72. # 1) f: file handle for SPDX document
  73. # 2) lic: custom license ID being described
  74. def writeOtherLicenseSPDX(f, lic):
  75. f.write(f"""LicenseID: {lic}
  76. ExtractedText: {lic}
  77. LicenseName: {lic}
  78. LicenseComment: Corresponds to the license ID `{lic}` detected in an SPDX-License-Identifier: tag.
  79. """)
  80. # Output tag-value SPDX 2.2 content for the given Document object.
  81. # Arguments:
  82. # 1) f: file handle for SPDX document
  83. # 2) doc: Document object being described
  84. def writeDocumentSPDX(f, doc):
  85. f.write(f"""SPDXVersion: SPDX-2.2
  86. DataLicense: CC0-1.0
  87. SPDXID: SPDXRef-DOCUMENT
  88. DocumentName: {doc.cfg.name}
  89. DocumentNamespace: {doc.cfg.namespace}
  90. Creator: Tool: Zephyr SPDX builder
  91. Created: {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")}
  92. """)
  93. # write any external document references
  94. if len(doc.externalDocuments) > 0:
  95. extDocs = list(doc.externalDocuments)
  96. extDocs.sort(key = lambda x: x.cfg.docRefID)
  97. for extDoc in extDocs:
  98. f.write(f"ExternalDocumentRef: {extDoc.cfg.docRefID} {extDoc.cfg.namespace} SHA1: {extDoc.myDocSHA1}\n")
  99. f.write(f"\n")
  100. # write relationships owned by this Document (not by its Packages, etc.), if any
  101. if len(doc.relationships) > 0:
  102. for rln in doc.relationships:
  103. writeRelationshipSPDX(f, rln)
  104. f.write(f"\n")
  105. # write packages
  106. for pkg in doc.pkgs.values():
  107. writePackageSPDX(f, pkg)
  108. # write other license info, if any
  109. if len(doc.customLicenseIDs) > 0:
  110. for lic in list(doc.customLicenseIDs).sort():
  111. writeOtherLicenseSPDX(f, lic)
  112. # Open SPDX document file for writing, write the document, and calculate
  113. # its hash for other referring documents to use.
  114. # Arguments:
  115. # 1) spdxPath: path to write SPDX document
  116. # 2) doc: SPDX Document object to write
  117. def writeSPDX(spdxPath, doc):
  118. # create and write document to disk
  119. try:
  120. log.inf(f"Writing SPDX document {doc.cfg.name} to {spdxPath}")
  121. with open(spdxPath, "w") as f:
  122. writeDocumentSPDX(f, doc)
  123. except OSError as e:
  124. log.err(f"Error: Unable to write to {spdxPath}: {str(e)}")
  125. return False
  126. # calculate hash of the document we just wrote
  127. hashes = getHashes(spdxPath)
  128. if not hashes:
  129. log.err(f"Error: created document but unable to calculate hash values")
  130. return False
  131. doc.myDocSHA1 = hashes[0]
  132. return True