# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. # import argparse import re import subprocess import os import zipfile import codecs from subprocess import PIPE, Popen from os import listdir from os.path import isfile, join def parse_arguments(): arg_map = {} parser = argparse.ArgumentParser(description="Generates an SDK given an sdk name and version") parser.add_argument("--outputLocation", action="store") parser.add_argument("--serviceName", action="store") parser.add_argument("--clientConfigDefaults", action="store") parser.add_argument("--apiVersion", action="store") parser.add_argument("--namespace", action="store") parser.add_argument("--licenseText", action="store") parser.add_argument("--pathToApiDefinitions", action="store") parser.add_argument("--pathToGenerator", action="store") parser.add_argument("--prepareTools", help="Makes sure generation environment is setup.", action="store_true") parser.add_argument("--standalone", help="Build custom client as a separate package, with prebuilt C++ SDK as dependency", action="store_true") parser.add_argument("--listAll", help="Lists all available SDKs for generation.", action="store_true") parser.add_argument("--enableVirtualOperations", help="Mark operation functions in service client as virtual functions.", action="store_true") args = vars(parser.parse_args()) arg_map["outputLocation"] = args["outputLocation"] or "./" arg_map["serviceName"] = args["serviceName"] or None arg_map["clientConfigDefaults"] = args["clientConfigDefaults"] or None arg_map["apiVersion"] = args["apiVersion"] or "" arg_map["namespace"] = args["namespace"] or "" arg_map["licenseText"] = args["licenseText"] or "" arg_map["pathToApiDefinitions"] = args["pathToApiDefinitions"] or "./code-generation/api-descriptions" arg_map["pathToGenerator"] = args["pathToGenerator"] or "./code-generation/generator" arg_map["prepareTools"] = args["prepareTools"] arg_map["standalone"] = args["standalone"] arg_map["listAll"] = args["listAll"] arg_map["enableVirtualOperations"] = args["enableVirtualOperations"] return arg_map serviceNameRemaps = { "runtime.lex": "lex", "runtime.lex.v2": "lexv2-runtime", "models.lex.v2": "lexv2-models", "entitlement.marketplace": "marketplace-entitlement", "runtime.sagemaker": "sagemaker-runtime", "transfer": "awstransfer", "transcribe-streaming": "transcribestreaming", "streams.dynamodb": "dynamodbstreams" } def discover_all_available_models(discovery_path): sdks = {} files_in_dir = [f for f in listdir(discovery_path) if isfile(join(discovery_path, f))] for file in files_in_dir: match = re.search(r'([\w.-]+)-(\d{4}-\d{2}-\d{2}).normal.json', file) if match: service_name = match.group(1) if service_name in serviceNameRemaps: service_name = serviceNameRemaps[service_name] sdk = {'service_name': service_name, 'apiVersion': match.group(2), 'filePath': join(discovery_path, file)} sdks['{}-{}'.format(sdk['service_name'], sdk['apiVersion'])] = sdk if service_name == "s3": s3crt = {'service_name': "s3-crt", 'apiVersion': sdk['apiVersion'], 'filePath': sdk['filePath']} sdks['s3-crt-{}'.format(s3crt['apiVersion'])] = s3crt return sdks def prepare_generator(generator_path): current_dir = os.getcwd() os.chdir(generator_path) subprocess.call('mvn package', shell=True) os.chdir(current_dir) def generate_sdk(generator_path, sdk, output_dir, namespace, license_text, standalone, enable_virtual_operations): try: with codecs.open(sdk['filePath'], 'rb', 'utf-8') as api_definition: api_content = api_definition.read() jar_path = join(generator_path, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar') process = Popen(['java', '-jar', jar_path, '--service', sdk['serviceName'], '--version', sdk['apiVersion'], '--namespace', namespace, '--license-text', license_text, '--language-binding', 'cpp', '--arbitrary', '--standalone' if standalone else '', '--enable-virtual-operations' if enable_virtual_operations else ''], stdout=PIPE, stdin=PIPE) writer = codecs.getwriter('utf-8') std_in_writer = writer(process.stdin) std_in_writer.write(api_content) process.stdin.close() output = process.stdout.read() if output: with zipfile.ZipFile(output.strip().decode('utf-8'), 'r') as zipped: zipped.extractall(output_dir) except EnvironmentError as ex: print('Error generating sdk {} with error {}'.format(sdk, ex)) def generate_defaults(generator_path, defaults_definition_path, output_dir, namespace, license_text): try: with codecs.open(defaults_definition_path, 'rb', 'utf-8') as defaults_definition: defaults_content = defaults_definition.read() jar_path = join(generator_path, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar') process = Popen( ['java', '-jar', jar_path, '--defaults', 'global', '--namespace', namespace, '--license-text', license_text, '--language-binding', 'cpp', '--arbitrary'], stdout=PIPE, stdin=PIPE) writer = codecs.getwriter('utf-8') std_in_writer = writer(process.stdin) std_in_writer.write(defaults_content) process.stdin.close() output = process.stdout.read() if output: with zipfile.ZipFile(output.strip().decode('utf-8'), 'r') as zipped: zipped.extractall(output_dir) except EnvironmentError as ex: print('Error generating {} defaults with error {}'.format('global', ex)) except Exception as ex: print('Error generating {} defaults with error {}'.format('global', ex)) def main(): arguments = parse_arguments() if arguments['prepareTools']: prepare_generator(arguments['pathToGenerator']) sdks = discover_all_available_models(arguments['pathToApiDefinitions']) if arguments['listAll']: for key, value in sdks.items(): print(value) if arguments['serviceName']: print('Generating {} api version {}.'.format(arguments['serviceName'], arguments['apiVersion'])) key = '{}-{}'.format(arguments['serviceName'], arguments['apiVersion']) generate_sdk(arguments['pathToGenerator'], sdks[key], arguments['outputLocation'], arguments['namespace'], arguments['licenseText'], arguments['standalone'], arguments['enableVirtualOperations']) if arguments['clientConfigDefaults']: print('Generating {} defaults api version.'.format(arguments['clientConfigDefaults'])) defaults_definition_path = './code-generation/defaults/sdk-default-configuration.json' generate_defaults(arguments['pathToGenerator'], defaults_definition_path, arguments['outputLocation'], arguments['namespace'], arguments['licenseText']) if __name__ == "__main__": main()