tools/write_sbom: extract SBOM into its own separate utility library
diff --git a/tools/BUILD b/tools/BUILD
index 4422ca0..d6baeed 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -40,7 +40,7 @@
 
 py_binary(
     name = "write_sbom",
-    srcs = ["write_sbom.py"],
+    srcs = ["write_sbom.py", "sbom.py"],
     python_version = "PY3",
     visibility = ["//visibility:public"],
 )
diff --git a/tools/sbom.py b/tools/sbom.py
new file mode 100644
index 0000000..b48ffe4
--- /dev/null
+++ b/tools/sbom.py
@@ -0,0 +1,55 @@
+import datetime
+import getpass
+import json
+
+
+class SBOMWriter:
+    def __init__(self, tool, out):
+        self.out = out
+        self.tool = tool
+
+    def write_header(self, package):
+        header = [
+            'SPDXVersion: SPDX-2.2',
+            'DataLicense: CC0-1.0',
+            'SPDXID: SPDXRef-DOCUMENT',
+            'DocumentName: %s' % package,
+            # TBD
+            # 'DocumentNamespace: https://swinslow.net/spdx-examples/example1/hello-v3
+            'Creator: Person: %s' % getpass.getuser(),
+            'Creator: Tool: %s' % self.tool,
+            datetime.datetime.utcnow().strftime('Created: %Y-%m-%d-%H:%M:%SZ'),
+            '',
+            '##### Package: %s' % package,
+        ]
+        self.out.write('\n'.join(header))
+    
+    def write_packages(self, packages):
+        """Produce a basic SBOM
+        Args:
+            out: file object to write to
+            packages: package metadata. A big blob of JSON.
+        """
+        for p in packages:
+            name = p.get('package_name') or '<unknown>'
+            self.out.write('\n')
+            self.out.write('SPDXID: "%s"\n' % name)
+            self.out.write('  name: "%s"\n' % name)
+            if p.get('package_version'):
+                self.out.write('  versionInfo: "%s"\n' % p['package_version'])
+            
+            # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
+            cn = p.get('copyright_notice')
+            if cn:
+                self.out.write('  copyrightText: "%s"\n' % cn)
+            
+            kinds = p.get('license_kinds')
+            if kinds:
+                self.out.write('  licenseDeclared: "%s"\n' %
+                    ','.join([k['name'] for k in kinds]))
+            
+            url = p.get('package_url')
+            if url:
+                self.out.write('  downloadLocation: %s\n' % url)
+
+
diff --git a/tools/write_sbom.py b/tools/write_sbom.py
index 1505d4e..84ce430 100644
--- a/tools/write_sbom.py
+++ b/tools/write_sbom.py
@@ -20,10 +20,8 @@
 
 import argparse
 import codecs
-import datetime
-import getpass
 import json
-
+import sbom
 
 TOOL = 'https//github.com/bazelbuild/rules_license/tools:write_sbom'
 
@@ -31,51 +29,6 @@
   with codecs.open(package_info, encoding='utf-8') as inp:
     return json.loads(inp.read())
 
-def _write_sbom_header(out, package):
-  header = [
-    'SPDXVersion: SPDX-2.2',
-    'DataLicense: CC0-1.0',
-    'SPDXID: SPDXRef-DOCUMENT',
-    'DocumentName: %s' % package,
-    # TBD
-    # 'DocumentNamespace: https://swinslow.net/spdx-examples/example1/hello-v3
-    'Creator: Person: %s' % getpass.getuser(),
-    'Creator: Tool: %s' % TOOL,
-    datetime.datetime.utcnow().strftime('Created: %Y-%m-%d-%H:%M:%SZ'),
-    '',
-    '##### Package: %s' % package,
-  ]
-  out.write('\n'.join(header))
-
-
-
-def _write_sbom(out, packages):
-  """Produce a basic SBOM
-
-  Args:
-    out: file object to write to
-    packages: package metadata. A big blob of JSON.
-  """
-  for p in packages:
-    name = p.get('package_name') or '<unknown>'
-    out.write('\n')
-    out.write('SPDXID: "%s"\n' % name)
-    out.write('  name: "%s"\n' % name)
-    if p.get('package_version'):
-      out.write('  versionInfo: "%s"\n' % p['package_version'])
-    # IGNORE_COPYRIGHT: Not a copyright notice. It is a variable holding one.
-    cn = p.get('copyright_notice')
-    if cn:
-      out.write('  copyrightText: "%s"\n' % cn)
-    kinds = p.get('license_kinds')
-    if kinds:
-      out.write('  licenseDeclared: "%s"\n' %
-                ','.join([k['name'] for k in kinds]))
-    url = p.get('package_url')
-    if url:
-      out.write('  downloadLocation: %s\n' % url)
-
-
 def main():
   parser = argparse.ArgumentParser(
       description='Demonstraton license compliance checker')
@@ -106,11 +59,10 @@
     else:
       all[pi['bazel_package']] = pi
 
-  err = 0
   with codecs.open(args.out, mode='w', encoding='utf-8') as out:
-    _write_sbom_header(out, package=top_level_target)
-    _write_sbom(out, all.values())
-  return err
+    sbom_writer = sbom.SBOMWriter(TOOL, out)
+    sbom_writer.write_header(package=top_level_target)
+    sbom_writer.write_packages(packages=all.values())
 
 
 if __name__ == '__main__':