| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Create the package information file in JSON format. |
| |
| To get the package information, use the library at |
| https://cos.googlesource.com/cos/tools/+/refs/heads/master/src/pkg/cos/pkg_info.go |
| instead of accessing the JSON file directly. |
| |
| For information on the JSON format, see http://go/cos-package-list-design . |
| """ |
| |
| from __future__ import print_function |
| |
| import collections |
| import getopt |
| import json |
| import re |
| import sys |
| |
| |
| # The following regular expresssions are used for extracting the category name, |
| # the package name, the package version without the revision, and the package |
| # revision from a string that looks like |
| # |
| # category_name/package_name-1.2.3.4_beta1_rc2-r1 |
| # |
| # These regular expressions follow the Gentoo package manager specification at |
| # https://www.gentoo.org/proj/en/qa/pms.xml . They are similar to what SplitPV |
| # function (in chromite/lib/portage_util.py) uses. |
| |
| CATEGORY_RE = r'(?P<category>\w[\w\+\.\-]*)' |
| PACKAGE_RE = r'(?P<package>\w[\w+-]*)' |
| VERSION_NUMLETTER_RE = r'\d+(\.\d+)*[a-z]?' |
| VERSION_SUFFIX_RE = r'_(pre|p|beta|alpha|rc)\d*' |
| VERSION_NO_REV_RE = r'(?P<version_no_rev>%s(%s)*)' % (VERSION_NUMLETTER_RE, |
| VERSION_SUFFIX_RE) |
| REVISION_RE = r'-r(?P<revision>\d+)' |
| CPV_RE = r'^%s\/%s-%s(%s)?$' % (CATEGORY_RE, PACKAGE_RE, |
| VERSION_NO_REV_RE, REVISION_RE) |
| |
| OVERLAY_RE = r'(?P<overlay>overlay:\w[\w+-]*)' |
| USE_FLAGS_RE = r'(?P<use_flags>use_flags:[(-|+)\w+-_,]*)' |
| DEBUG_CPV_RE = r'^%s\/%s-%s(%s)?(\s)?(%s)?(\s)?(\s)?(%s)?$' % (CATEGORY_RE, PACKAGE_RE, |
| VERSION_NO_REV_RE, REVISION_RE, |
| OVERLAY_RE, USE_FLAGS_RE) |
| |
| |
| def PrintHelp(): |
| print('usage: create_pkg_list.py --input=<comma separated list ' |
| 'of pkg_type:pkg_info_file> --build-id=<build id> ' |
| '--output=<output file> --debug') |
| |
| |
| def CreateList(input_lines, debug): |
| if debug: |
| cpv_re = re.compile(DEBUG_CPV_RE, re.VERBOSE) |
| else: |
| cpv_re = re.compile(CPV_RE, re.VERBOSE) |
| package_list = [] |
| for line in input_lines: |
| match = cpv_re.match(line.strip()) |
| if match is not None: |
| package_list.append(match.groupdict()) |
| return package_list |
| |
| |
| def WriteJson(pkg_info, build_id, output_file, debug): |
| result = {} |
| for pkg_type, pkg_list in pkg_info.items(): |
| packages = [] |
| for p in pkg_list: |
| package_info = collections.OrderedDict( |
| [('category', p['category']), |
| ('name', p['package']), |
| ('version', build_id), |
| ('ebuild_version', p['version_no_rev']) |
| ] |
| ) |
| if 'revision' in p and p['revision'] is not None: |
| package_info['ebuild_version'] += '-r' + p['revision'] |
| |
| if debug: |
| if 'overlay' in p and p['overlay'] is not None: |
| package_info['overlay'] = p['overlay'].replace('overlay:', '') |
| if 'use_flags' in p and p['use_flags'] is not None: |
| package_info['use_flags'] = p['use_flags'].replace('use_flags:', '') |
| |
| packages.append(package_info) |
| |
| result[pkg_type] = packages |
| |
| json.dump(result, output_file, indent=4) |
| return 0 |
| |
| |
| def main(argv): |
| input_fn = '' |
| output_fn = '' |
| build_id = '' |
| debug = False |
| |
| try: |
| opts, args_left = getopt.getopt(argv, '', ['help', 'input=', 'output=', 'build-id=', 'debug']) |
| except getopt.GetoptError: |
| PrintHelp() |
| return -1 |
| if len(args_left) != 0: |
| PrintHelp() |
| return -1 |
| |
| for opt, arg in opts: |
| if opt == '--help': |
| PrintHelp() |
| elif opt == '--input': |
| input_fn = arg |
| elif opt == '--output': |
| output_fn = arg |
| elif opt == '--build-id': |
| build_id = arg |
| elif opt == '--debug': |
| debug = True |
| |
| if input_fn == '' or output_fn == '': |
| PrintHelp() |
| return -1 |
| |
| inputs = input_fn.split(',') |
| pkg_info = {} |
| for i in inputs: |
| # <pkg_type>:<pkg_info_file> |
| pkg_type, pkg_info_file = i.split(':', 1) |
| input_lines = [] |
| try: |
| with open(pkg_info_file) as input_file: |
| input_lines = input_file.readlines() |
| except OSError: |
| print('error: Failed to open input file: %s' % pkg_info_file) |
| return -1 |
| if len(input_lines) == 0: |
| print('warning: No input lines') |
| continue |
| |
| package_list = CreateList(input_lines, debug) |
| if len(package_list) == 0: |
| print('warning: Empty package list') |
| continue |
| pkg_info[pkg_type] = package_list |
| |
| try: |
| with open(output_fn, 'w') as output_file: |
| ret = WriteJson(pkg_info, build_id, output_file, debug) |
| if ret != 0: |
| print('error: Failed to write package info') |
| return ret |
| except OSError: |
| print('error: Failed to open output file: %s' % output_fn) |
| return -1 |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |