make-index-json.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. #!/usr/bin/env python3
  2. """
  3. Parse the native package index files into a json file for use by downstream
  4. tools. See:
  5. https://github.com/openwrt/openwrt/commit/218ce40cd738f3373438aab82467807a8707fb9c
  6. The "version 1" index.json contained ABI-versioned package names, making the
  7. unusable by the ASU server. The version 2 format contains package names that
  8. have been stripped of their ABI version.
  9. """
  10. import email.parser
  11. import json
  12. def removesuffix(src, suffix):
  13. # For compatibility with Python < 3.9.
  14. suffix_length = len(suffix)
  15. return src[:-suffix_length] if suffix_length and src.endswith(suffix) else src
  16. def parse_args():
  17. from argparse import ArgumentParser
  18. source_format = "apk", "opkg"
  19. parser = ArgumentParser()
  20. # fmt: off
  21. parser.add_argument("-a", "--architecture", required=True,
  22. help="Required device architecture: like 'x86_64' or 'aarch64_generic'")
  23. parser.add_argument("-f", "--source-format", required=True, choices=source_format,
  24. help="Required source format of input: 'apk' or 'opkg'")
  25. parser.add_argument("-m", "--manifest", action="store_true", default=False,
  26. help="Print output in manifest format, as package:version pairs")
  27. parser.add_argument(dest="source",
  28. help="File name for input, '-' for stdin")
  29. # fmt: on
  30. args = parser.parse_args()
  31. return args
  32. def parse_apk(text: str) -> dict:
  33. packages: dict = {}
  34. data = json.loads(text)
  35. if isinstance(data, dict) and "packages" in data:
  36. # Extract 'apk adbdump' dict field to 'apk query' package list
  37. data = data["packages"]
  38. for package in data:
  39. package_name: str = package["name"]
  40. for tag in package.get("tags", []):
  41. if tag.startswith("openwrt:abiversion="):
  42. package_abi: str = tag.split("=")[-1]
  43. package_name = removesuffix(package_name, package_abi)
  44. break
  45. packages[package_name] = package["version"]
  46. return packages
  47. def parse_opkg(text: str) -> dict:
  48. packages: dict = {}
  49. parser: email.parser.Parser = email.parser.Parser()
  50. chunks: list[str] = text.strip().split("\n\n")
  51. for chunk in chunks:
  52. package: dict = parser.parsestr(chunk, headersonly=True)
  53. package_name: str = package["Package"]
  54. package_abi = package.get("ABIVersion")
  55. if package_abi:
  56. package_name = removesuffix(package_name, package_abi)
  57. packages[package_name] = package["Version"]
  58. return packages
  59. if __name__ == "__main__":
  60. import sys
  61. args = parse_args()
  62. input = sys.stdin if args.source == "-" else open(args.source, "r")
  63. with input:
  64. text: str = input.read()
  65. packages = parse_apk(text) if args.source_format == "apk" else parse_opkg(text)
  66. if args.manifest:
  67. for name, version in packages.items():
  68. print(name, version)
  69. else:
  70. index = {
  71. "version": 2,
  72. "architecture": args.architecture,
  73. "packages": packages,
  74. }
  75. print(json.dumps(index, indent=2))