blob: 44cf197984f92dc673e2220022a226b65ded3256 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2021 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""gen_sdk is a command line tool for managing the sdk extension proto db.
Example usages:
# Print a binary representation of the proto database.
$ gen_sdk --action print_binary
# Validate the database
$ gen_sdk --action validate
# Create a new SDK
$ gen_sdk --action new_sdk --sdk 1 --modules=IPSEC,SDK_EXTENSIONS
"""
import argparse
import google.protobuf.text_format
import pathlib
import sys
from sdk_pb2 import ExtensionVersion
from sdk_pb2 import ExtensionDatabase
from sdk_pb2 import SdkModule
from sdk_pb2 import SdkVersion
def ParseArgs(argv):
parser = argparse.ArgumentParser('Manage the extension SDK database')
parser.add_argument(
'--database',
type=pathlib.Path,
metavar='PATH',
default='extensions_db.textpb',
help='The existing text-proto database to use. (default: extensions_db.textpb)'
)
parser.add_argument(
'--action',
choices=['print_binary', 'new_sdk', 'validate'],
metavar='ACTION',
required=True,
help='Which action to take (print_binary|new_sdk|validate).'
)
parser.add_argument(
'--sdk',
type=int,
metavar='SDK',
help='The extension SDK level to deal with (int)'
)
parser.add_argument(
'--modules',
metavar='MODULES',
help='Comma-separated list of modules providing new APIs. Used for action=new_sdk to create a '
'new SDK that only requires new versions of some modules.'
)
return parser.parse_args(argv)
"""Print the binary representation of the db proto to stdout."""
def PrintBinary(database):
sys.stdout.buffer.write(database.SerializeToString())
def ValidateDatabase(database, dbname):
def find_duplicate(l):
s = set()
for i in l:
if i in s:
return i
s.add(i)
return None
def find_bug():
dupe = find_duplicate([v.version for v in database.versions])
if dupe:
return 'Found duplicate extension version: %d' % dupe
for version in database.versions:
dupe = find_duplicate([r.module for r in version.requirements])
if dupe:
return 'Found duplicate module requirement for %s in single version %s' % (dupe, version)
prev_requirements = {}
for version in sorted(database.versions, key=lambda v: v.version):
for requirement in version.requirements:
if requirement.module in prev_requirements:
prev = prev_requirements[requirement.module]
if prev.version > requirement.version.version:
return 'Found module requirement moving backwards: %s in %s' % (requirement, version)
prev_requirements[requirement.module] = requirement.version
for version in database.versions:
required_modules = [r.module for r in version.requirements]
if SdkModule.UNKNOWN in required_modules:
return 'SDK %d has a requirement on the UNKNOWN module' % version.version
if not all([m in SdkModule.values() for m in required_modules]):
return 'SDK %d has a requirement on an undefined module value' % version.version
return None
err = find_bug()
if err is not None:
print('%s not valid, aborting:\n %s' % (dbname, err))
sys.exit(1)
def NewSdk(database, args):
if not args.sdk:
print('Missing required argument --sdk for action new_sdk')
sys.exit(1)
new_version = args.sdk
if args.modules:
module_names = args.modules.split(',')
else:
# Default: require all modules
module_names = [m for m in SdkModule.keys() if not m == 'UNKNOWN']
module_values = [SdkModule.Value(m) for m in module_names]
new_requirements = {}
# Gather the previous highest requirement of each module
for prev_version in sorted(database.versions, key=lambda v: v.version):
for prev_requirement in prev_version.requirements:
new_requirements[prev_requirement.module] = prev_requirement.version
# Add new requirements of this version
for module in module_values:
new_requirements[module] = SdkVersion(version=new_version)
to_proto = lambda m : ExtensionVersion.ModuleRequirement(module=m, version=new_requirements[m])
module_requirements = [to_proto(m) for m in new_requirements]
extension_version = ExtensionVersion(version=new_version, requirements=module_requirements)
database.versions.append(extension_version)
print('Created a new extension SDK level %d with modules %s' % (new_version, ','.join(module_names)))
def main(argv):
args = ParseArgs(argv)
with args.database.open('r') as f:
database = google.protobuf.text_format.Parse(f.read(), ExtensionDatabase())
ValidateDatabase(database, 'Input database')
{
'validate': lambda : print('Validated database'),
'print_binary': lambda : PrintBinary(database),
'new_sdk': lambda : NewSdk(database, args)
}[args.action]()
if args.action in ['new_sdk']:
ValidateDatabase(database, 'Post-modification database')
with args.database.open('w') as f:
f.write(google.protobuf.text_format.MessageToString(database))
if __name__ == '__main__':
main(sys.argv[1:])