• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2025 Northeastern University
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import argparse
20import os
21import shutil
22import subprocess
23import sys
24from datetime import datetime
25from pathlib import Path
26
27sys.path.append(
28    os.path.dirname(os.path.dirname(os.path.dirname(
29        os.path.abspath(__file__)))))
30
31from ohos.sbom.common.utils import write_json
32from ohos.sbom.converters.api import SBOMConverter
33from ohos.sbom.converters.base import SBOMFormat
34from ohos.sbom.extraction.local_resource_loader import LocalResourceLoader
35from ohos.sbom.pipeline.sbom_generator import SBOMGenerator
36
37
38def generate_manifest(args):
39    """
40    Generate a release manifest using 'python .repo/repo/repo manifest -r -o' command.
41
42    The manifest is saved to:
43        <out_dir>/sbom/manifests/manifest_tag_YYYYMMDD_HHMMSS.xml
44
45    Args:
46        args: Parsed command-line arguments with output
47    """
48
49    source_root = Path(args.source_root_dir).resolve()
50    out_dir = Path(args.out_dir).resolve()
51
52    repo_script = source_root / ".repo" / "repo" / "repo"
53    if not repo_script.exists():
54        raise FileNotFoundError(f"Repo script not found: {repo_script}\n"
55                                f"Please make sure you are in a valid repo workspace.")
56
57    manifest_dir = out_dir / "sbom" / "manifests"
58    manifest_dir.mkdir(parents=True, exist_ok=True)
59
60    timestamp_str = datetime.now().strftime("%Y%m%d_%H%M%S")
61    output_path = manifest_dir / f"manifest_tag_{timestamp_str}.xml"
62
63    cmd = [
64        "python", str(repo_script),
65        "manifest", "-r", "-o", str(output_path)
66    ]
67
68    try:
69        print(f"[INFO] Generating manifest: {output_path}")
70        print(f"[DEBUG] Running command: {' '.join(cmd)}")
71
72        subprocess.run(cmd, check=True, cwd=source_root)
73        print(f"[INFO] Manifest generated successfully: {output_path}")
74        return output_path
75
76    except subprocess.CalledProcessError as e:
77        print(f"[ERROR] Failed to generate manifest (command exited with error): {e}")
78        raise
79    except FileNotFoundError:
80        print(f"[ERROR] Python interpreter not found or repo script missing.")
81        raise
82    except Exception as e:
83        print(f"[ERROR] Unexpected error during manifest generation: {e}")
84        raise
85
86
87def set_path(args):
88    LocalResourceLoader.set_source_root(args.source_root_dir)
89    LocalResourceLoader.set_out_root(args.out_dir)
90
91
92def generate_sbom(args):
93    """
94    Generate SBOM (Software Bill of Materials) and clean up temporary files afterward.
95    """
96    # Define the output directory for SBOM artifacts
97    sbom_dir = os.path.join(args.out_dir, "sbom")
98    os.makedirs(sbom_dir, exist_ok=True)
99
100    # Paths to temporary files/directories to be cleaned up
101    manifests_dir = os.path.join(sbom_dir, "manifests")
102    gn_gen_file = os.path.join(sbom_dir, "gn_gen.json")
103
104    try:
105        # Generate SBOM metadata using the provided arguments
106        sbom_meta_data = SBOMGenerator(args).build_sbom()
107
108        # Convert SBOM metadata to SPDX format
109        spdx_data = SBOMConverter(sbom_meta_data).convert(SBOMFormat.SPDX)
110
111        # Define output file paths
112        output_file_meta_data = os.path.join(sbom_dir, "sbom_meta_data.json")
113        output_file_spdx = os.path.join(sbom_dir, "spdx.json")
114
115        # Write SBOM metadata and SPDX data to JSON files
116        write_json(sbom_meta_data.to_dict(), output_file_meta_data)
117        write_json(spdx_data, output_file_spdx)
118
119    finally:
120        # Ensure cleanup runs regardless of success or failure
121
122        # Remove the 'manifest' directory if it exists
123        if os.path.exists(manifests_dir) and os.path.isdir(manifests_dir):
124            try:
125                shutil.rmtree(manifests_dir)
126                print(f"Cleaned up directory: {manifests_dir}")
127            except Exception as e:
128                print(f"Failed to delete directory {manifests_dir}: {e}")
129
130        # Remove the 'gn_gen.json' file if it exists
131        if os.path.exists(gn_gen_file) and os.path.isfile(gn_gen_file):
132            try:
133                os.remove(gn_gen_file)
134                print(f"Cleaned up file: {gn_gen_file}")
135            except Exception as e:
136                print(f"Failed to delete file {gn_gen_file}: {e}")
137
138
139def main():
140    parser = argparse.ArgumentParser()
141    parser.add_argument("--source-root-dir", type=str, required=True, help="project source root directory")
142    parser.add_argument("--out-dir", type=str, required=True, help="SBOM output directory")
143    parser.add_argument("--product", type=str, required=True, help="Product name")
144    parser.add_argument("--platform", type=str, required=True, help="Target platform")
145    args = parser.parse_args()
146    set_path(args)
147    generate_manifest(args)
148    generate_sbom(args)
149
150    return 0
151
152
153if __name__ == '__main__':
154    sys.exit(main())
155