"""
Generate reference_data.json from OpenHarmony README.OpenSource files.
This script scans the OpenHarmony repository for README.OpenSource files,
extracts relevant metadata (Name, License, Version Number, Upstream URL),
and generates a reference data JSON file for validation purposes.
"""
import argparse
import json
import logging
import os
from pathlib import Path
from typing import Dict, List, Optional
from spdx_converter import SPDXLicenseConverter
logging.basicConfig(
level=logging.INFO,
format='%(levelname)s: %(message)s'
)
logger = logging.getLogger(__name__)
REQUIRED_FIELDS = {"Name", "License", "Version Number", "Upstream URL"}
def find_readme_files(root_dir: str) -> List[str]:
"""
Find all README.OpenSource files recursively.
Args:
root_dir: Root directory to search for README.OpenSource files.
Returns:
List of absolute paths to README.OpenSource files.
"""
readme_files = []
root_path = Path(root_dir)
if not root_path.exists():
logger.error(f"Root directory does not exist: {root_dir}")
return readme_files
if not root_path.is_dir():
logger.error(f"Root path is not a directory: {root_dir}")
return readme_files
for file_path in root_path.rglob("README.OpenSource"):
readme_files.append(str(file_path))
logger.info(f"Found {len(readme_files)} README.OpenSource files")
return readme_files
def extract_reference_data(
readme_path: str,
converter: Optional[SPDXLicenseConverter] = None
) -> Optional[Dict[str, str]]:
"""
Extract Name, License, Version Number, Upstream URL from README.OpenSource.
Args:
readme_path: Path to the README.OpenSource file.
converter: Optional SPDX license converter for standardizing license names.
Returns:
Dictionary with extracted fields, or None if extraction fails.
"""
try:
with open(readme_path, "r", encoding="utf-8") as f:
data = json.load(f)
except json.JSONDecodeError as e:
logger.error(f"Malformed JSON in {readme_path}: {e}")
return None
except Exception as e:
logger.error(f"Error reading {readme_path}: {e}")
return None
if not isinstance(data, list):
logger.error(f"Expected JSON list in {readme_path}, got {type(data).__name__}")
return None
if len(data) == 0:
logger.warning(f"Empty list in {readme_path}")
return None
component = data[0]
missing_fields = REQUIRED_FIELDS - set(component.keys())
if missing_fields:
logger.warning(
f"Missing required fields {missing_fields} in {readme_path}"
)
return None
license_value = component["License"]
if converter:
original_license = license_value
license_value = converter.convert(license_value)
if original_license != license_value:
logger.debug(
f"License converted: '{original_license}' -> '{license_value}' "
f"in {readme_path}"
)
return {
"Name": component["Name"],
"License": license_value,
"Version Number": component["Version Number"],
"Upstream URL": component["Upstream URL"],
}
def generate_reference_data(
ohos_root: str,
output_path: str,
deduplicate: bool = True,
spdx_json_path: Optional[str] = None,
convert_spdx: bool = True
) -> None:
"""
Main function to generate reference data JSON file.
Args:
ohos_root: Path to OpenHarmony repository root.
output_path: Output path for reference_data.json.
deduplicate: If True, keep first occurrence of duplicate names.
spdx_json_path: Path to SPDX license mapping JSON file.
convert_spdx: If True, convert license names to SPDX standard.
"""
converter = None
if convert_spdx:
try:
converter = SPDXLicenseConverter(spdx_json_path)
logger.info("SPDX license conversion enabled")
except Exception as e:
logger.warning(f"Failed to initialize SPDX converter: {e}")
logger.warning("Continuing without SPDX conversion")
readme_files = find_readme_files(ohos_root)
if not readme_files:
logger.error("No README.OpenSource files found")
return
reference_data = []
seen_names = set()
for readme_path in readme_files:
data = extract_reference_data(readme_path, converter)
if data is None:
continue
name = data["Name"]
if deduplicate:
if name in seen_names:
logger.warning(f"Duplicate name '{name}' found in {readme_path}, skipping")
continue
seen_names.add(name)
reference_data.append(data)
reference_data.sort(key=lambda x: x["Name"])
output_dir = os.path.dirname(output_path)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
with open(output_path, "w", encoding="utf-8") as f:
json.dump(reference_data, f, indent=2, ensure_ascii=False)
logger.info(f"Generated {output_path} with {len(reference_data)} entries")
def main() -> None:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Generate reference_data.json from OpenHarmony repo"
)
parser.add_argument(
"--ohos-root",
default="/home/jinguang/opensource/ohos",
help="Path to OpenHarmony repository root"
)
parser.add_argument(
"--output",
default="test/reference_data.json",
help="Output path for reference_data.json"
)
parser.add_argument(
"--keep-duplicates",
action="store_true",
help="Keep all entries including duplicate names (default: remove duplicates)"
)
parser.add_argument(
"--spdx-json",
default=None,
help="Path to SPDX license mapping JSON file (default: data/spdx.json)"
)
parser.add_argument(
"--no-spdx-convert",
action="store_true",
help="Disable SPDX license standardization (default: enabled)"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose logging"
)
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
generate_reference_data(
args.ohos_root,
args.output,
deduplicate=not args.keep_duplicates,
spdx_json_path=args.spdx_json,
convert_spdx=not args.no_spdx_convert
)
if __name__ == "__main__":
main()