8
8
# See https://aboutcode.org for more information about nexB OSS projects.
9
9
#
10
10
11
+ import os
11
12
import json
13
+ import logging
12
14
import uuid
13
15
from collections import defaultdict
14
16
from datetime import datetime
15
17
from enum import Enum
16
18
from typing import List
19
+ import warnings
17
20
18
21
import attr
19
22
from lxml import etree
26
29
from plugincode .output import output_impl
27
30
28
31
32
+ TRACE = os .environ .get ('SCANCODE_DEBUG_OUTPUTS' , False )
33
+
34
+
35
+ def logger_debug (* args ):
36
+ pass
37
+
38
+
39
+ logger = logging .getLogger (__name__ )
40
+
41
+ if TRACE :
42
+ import sys
43
+
44
+ logging .basicConfig (stream = sys .stdout )
45
+ logger .setLevel (logging .DEBUG )
46
+
47
+ def logger_debug (* args ):
48
+ return logger .debug (' ' .join (isinstance (a , str ) and a or repr (a ) for a in args ))
49
+
50
+
29
51
class ToDictMixin :
30
52
31
53
def to_dict (self ):
@@ -274,7 +296,7 @@ def from_package(cls, package):
274
296
275
297
purl = package .get ('purl' )
276
298
277
- return CycloneDxComponent (
299
+ return cls (
278
300
bom_ref = purl ,
279
301
purl = purl ,
280
302
name = name ,
@@ -301,7 +323,7 @@ def from_packages(cls, packages):
301
323
"""
302
324
components_by_purl = defaultdict (list )
303
325
for package in packages :
304
- comp = CycloneDxComponent .from_package (package )
326
+ comp = cls .from_package (package )
305
327
if not comp :
306
328
continue
307
329
components_by_purl [comp .purl ].append (comp )
@@ -497,7 +519,7 @@ def from_package(cls, package, components_by_purl):
497
519
warnings_by_dependent [purl ].append (msg )
498
520
499
521
for ref , dependsOn in dependencies_by_dependent .items ():
500
- yield CycloneDxDependency (
522
+ yield cls (
501
523
ref = ref ,
502
524
dependsOn = dependsOn ,
503
525
warnings = warnings_by_dependent .get (purl , [])
@@ -556,6 +578,11 @@ def from_headers(cls, headers):
556
578
headers = [h for h in headers if h .get ('tool_name' ) == 'scancode-toolkit' ]
557
579
scancode_header = headers [0 ] if headers else {}
558
580
581
+ if TRACE :
582
+ logger_debug ('CycloneDxMetadata: headers' )
583
+ from pprint import pformat
584
+ logger_debug (pformat (headers ))
585
+
559
586
try :
560
587
tool_header = {
561
588
'vendor' : 'AboutCode.org' ,
@@ -568,11 +595,17 @@ def from_headers(cls, headers):
568
595
props = dict (
569
596
notice = scancode_header .get ('notice' ),
570
597
errors = scancode_header .get ('errors' , []),
598
+ warnings = scancode_header .get ('warnings' , []),
571
599
message = scancode_header .get ('message' ),
572
600
)
573
601
props .update (scancode_header .get ('extra_data' , {}))
574
602
properties = [CycloneDxProperty (k , v ) for k , v in props .items ()]
575
603
604
+ if TRACE :
605
+ logger_debug ('CycloneDxMetadata: properties' )
606
+ from pprint import pformat
607
+ logger_debug (pformat (properties ))
608
+
576
609
return CycloneDxMetadata (
577
610
tools = [tool_header ],
578
611
properties = properties ,
@@ -596,6 +629,10 @@ def to_xml_element(self):
596
629
return xmetadata
597
630
598
631
632
+ class CycloneDxPluginNoPackagesWarning (DeprecationWarning ):
633
+ pass
634
+
635
+
599
636
@attr .s
600
637
class CycloneDxBom :
601
638
"""
@@ -629,10 +666,32 @@ def from_codebase(cls, codebase):
629
666
"""
630
667
Return a CycloneDxBom built from a ScanCode ``codebase``.
631
668
"""
632
- metadata = CycloneDxMetadata .from_headers (codebase .get_headers ())
633
- packages = codebase .attributes .packages
634
- components = list (CycloneDxComponent .from_packages (packages ))
635
- dependencies = list (CycloneDxDependency .from_packages (packages , components ))
669
+ components = []
670
+ dependencies = []
671
+
672
+ packages_not_found_message = (
673
+ "The --cyclonedx-xml option will not output any component/dependency data "
674
+ "as there are no package data in the present scan. To get package data "
675
+ "please rerun the scan with --package or --system-package CLI options enabled."
676
+ )
677
+ codebase .get_or_create_current_header ()
678
+
679
+ if hasattr (codebase .attributes , 'packages' ):
680
+ packages = codebase .attributes .packages
681
+ components = list (CycloneDxComponent .from_packages (packages ))
682
+ dependencies = list (CycloneDxDependency .from_packages (packages , components ))
683
+ else :
684
+ warnings .simplefilter ('always' , CycloneDxPluginNoPackagesWarning )
685
+ warnings .warn (
686
+ packages_not_found_message ,
687
+ CycloneDxPluginNoPackagesWarning ,
688
+ stacklevel = 2 ,
689
+ )
690
+ headers = codebase .get_or_create_current_header ()
691
+ headers .warnings .append (packages_not_found_message )
692
+
693
+ codebase_headers = codebase .get_headers ()
694
+ metadata = CycloneDxMetadata .from_headers (codebase_headers )
636
695
637
696
return CycloneDxBom (
638
697
metadata = metadata ,
0 commit comments