blob: 55099c7829502e20a3bbbde3abf58928abc06245 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2023 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates licenses SPDX from GN metadata."""
import argparse
import sys
import logging
import os
from file_access import FileAccess
from gn_label import GnLabel
from spdx_writer import SpdxWriter
from readme_fuchsia import ReadmesDB
from collector import Collector
from pathlib import Path
from gn_license_metadata import GnLicenseMetadataDB
from spdx_comparator import SpdxComparator
def main() -> int:
"""
Generates licenses SPDX json file from GN license metadata.
"""
parser = argparse.ArgumentParser()
parser.add_argument(
"--generated-license-metadata",
type=str,
required=True,
help="Path to the license GN metadata file, generated via the GN genereated_file action",
)
parser.add_argument(
"--fuchsia-source-path",
type=str,
required=True,
help="The path to the root of the Fuchsia source tree",
)
parser.add_argument(
"--spdx-root-package-name",
type=str,
required=True,
help="The name of the SPDX root package",
)
parser.add_argument(
"--spdx-output",
type=str,
required=True,
help="Path to the output SPDX file",
)
parser.add_argument(
"--dep-file",
type=str,
required=True,
help="Path to the generated dep file",
)
parser.add_argument(
"--ignore-collection-errors",
action="store_true",
default=False,
help="Tool will fail when encountering license collection errors",
)
parser.add_argument(
"--include-host-tools",
action="store_true",
default=False,
help="Whether to include licenses of targets that are host tools",
)
parser.add_argument(
"--debug-hints",
action="store_true",
default=False,
help="Embeds '_hint' keys in the output SPDX for debugging purposes",
)
# TODO(132725): Remove once migration completes.
parser.add_argument(
"--compare-with-legacy-spdx",
type=str,
default=None,
help="The tool will compare the contents of the actual spdx output with the given legacy file",
)
parser.add_argument(
"--ignore-comparison-errors",
action="store_true",
default=False,
help="Tool will fail when comparison with the legacy spdx finds missing licenses",
)
parser.add_argument(
"--log-level",
type=str,
default="INFO",
help="Python logging level",
)
args = parser.parse_args()
log_level = args.log_level.upper()
logging.basicConfig(
level=log_level, force=True, format="%(levelname)s:%(message)s"
)
fuchsia_source_path = Path(args.fuchsia_source_path).expanduser()
assert fuchsia_source_path.exists()
logging.debug("fuchsia_source_path=%s", fuchsia_source_path)
fuchsia_source_path_str = str(fuchsia_source_path)
file_access = FileAccess(fuchsia_source_path_str=fuchsia_source_path_str)
readmes_db = ReadmesDB(file_access=file_access)
metadata_db = GnLicenseMetadataDB.from_file(
file_path=args.generated_license_metadata,
fuchsia_source_path=fuchsia_source_path_str,
)
# Collect licenses information
collector = Collector(
file_access=file_access,
metadata_db=metadata_db,
readmes_db=readmes_db,
include_host_tools=args.include_host_tools,
default_license_file=GnLabel.from_str("//LICENSE"),
)
collector.collect()
if collector.errors:
if args.ignore_collection_errors:
collector.log_errors(log_level=logging.WARN, is_full_report=False)
logging.warning(
"Errors are ignored because --ignore_collection_errors is True. "
)
else:
collector.log_errors(log_level=logging.ERROR, is_full_report=True)
return -1
logging.info(f"Collection stats: {collector.stats}")
# Generate an SPDX file:
spdx_writer = SpdxWriter.create(
root_package_name=args.spdx_root_package_name,
file_access=file_access,
)
debug_hints = args.debug_hints
for collected_license in collector.unique_licenses:
spdx_writer.add_license(
public_package_name=collected_license.public_name,
license_labels=collected_license.license_files,
collection_hint=collected_license.debug_hint
if debug_hints
else None,
)
spdx_writer.save(args.spdx_output)
logging.info(
f"Wrote spdx {args.spdx_output} (licenses={len(collector.unique_licenses)} size={os.stat(args.spdx_output).st_size})"
)
if args.compare_with_legacy_spdx:
# Compare with legacy spdx file
comparator = SpdxComparator(
current_file=args.spdx_output,
legacy_file=Path(args.compare_with_legacy_spdx),
)
comparator.compare()
if comparator.found_differences():
if args.ignore_comparison_errors:
comparator.log_differences(logging.WARN, is_full_report=False)
logging.warning(
"Differences are ignored because --ignore_comparison_errors is True. "
)
else:
comparator.log_differences(logging.ERROR, is_full_report=True)
return -1
# Generate a GN depfile
logging.info(f"writing depfile {args.dep_file}")
file_access.write_depfile(args.dep_file, main_entry=args.spdx_output)
return 0
if __name__ == "__main__":
sys.exit(main())