Skip to content

Commit 63e159e

Browse files
committed
Refactor generate_sdks.py
1 parent dd82801 commit 63e159e

File tree

3 files changed

+160
-115
lines changed

3 files changed

+160
-115
lines changed

CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,14 @@ else ()
8484
if (REGENERATE_CLIENTS)
8585
string(CONCAT CONFIG_FAIL_MSG
8686
"The REGENERATE_CLIENTS option previously available is removed from new build scripts. "
87-
"To regenerate service client code from models use the tools/script/generate_sdks.py script. "
87+
"To generate service client code from models use the tools/script/generate_sdks.py script. "
88+
"Example call: \n"
89+
"tools/scripts/generate_sdks.py\n"
90+
" --service-name dynamodb\n"
91+
" --api-version 2012-08-10\n"
92+
" --enable-virtual-operations\n"
93+
" --output-path generated\n"
94+
" --prepare-tools\n"
8895
"Alternatively, -DLEGACY_BUILD=ON can be passed to cmake to use the old build scripts until 1.11.")
8996
message(FATAL_ERROR "${CONFIG_FAIL_MSG}")
9097
endif ()

cmake_legacy/sdks.cmake

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ endif()
7676
# SDK_BUILD_LIST is now a list of present SDKs that can be processed unconditionally
7777
if(ADD_CUSTOM_CLIENTS OR REGENERATE_CLIENTS OR REGENERATE_DEFAULTS)
7878
execute_process(
79-
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --prepareTools
79+
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --prepare-tools
8080
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
8181
)
8282
endif()
@@ -103,7 +103,7 @@ if(REGENERATE_CLIENTS)
103103
file(REMOVE_RECURSE "${CMAKE_CURRENT_SOURCE_DIR}/aws-cpp-sdk-${SDK}")
104104

105105
execute_process(
106-
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --serviceName ${SDK} --apiVersion ${C2J_DATE} ${ENABLE_VIRTUAL_OPERATIONS_ARG} --outputLocation ./generated/
106+
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --service-name ${SDK} --api-version ${C2J_DATE} ${ENABLE_VIRTUAL_OPERATIONS_ARG} --output-path ../generated/
107107
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
108108
)
109109
message(STATUS "Generated service: ${SDK}, version: ${C2J_DATE}")
@@ -118,7 +118,7 @@ if(REGENERATE_DEFAULTS)
118118

119119
if(TRUE)#EXISTS ${SDK_C2J_FILE})
120120
execute_process(
121-
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --clientConfigDefaults "${CMAKE_CURRENT_SOURCE_DIR}/code-generation/defaults/sdk-default-configuration.json" --outputLocation ./generated/
121+
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --generate-config-defaults --config-defaults-path "${CMAKE_CURRENT_SOURCE_DIR}/code-generation/defaults/sdk-default-configuration.json" --outputLocation ./generated/
122122
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
123123
)
124124
message(STATUS "Generated defaults into ${CMAKE_CURRENT_SOURCE_DIR}")
@@ -175,7 +175,7 @@ foreach(custom_client ${ADD_CUSTOM_CLIENTS})
175175
file(REMOVE_RECURSE "${CMAKE_CURRENT_SOURCE_DIR}/aws-cpp-sdk-${C_SERVICE_NAME}")
176176
message(STATUS "generating client for ${C_SERVICE_NAME} version ${C_VERSION}")
177177
execute_process(
178-
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --serviceName ${C_SERVICE_NAME} --apiVersion ${C_VERSION} ${ENABLE_VIRTUAL_OPERATIONS_ARG} --outputLocation ./generated/
178+
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --service-name ${C_SERVICE_NAME} --api-version ${C_VERSION} ${ENABLE_VIRTUAL_OPERATIONS_ARG} --output-path ./generated/
179179
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
180180
)
181181
LIST(APPEND SDK_BUILD_LIST ${C_SERVICE_NAME})
@@ -249,7 +249,7 @@ function(add_sdks)
249249

250250
# Generates SDK client based on aws-cpp-sdk-core-tests/resources/api-descriptions/document-test-2021-06-28.normal.json for functional testing.
251251
execute_process(
252-
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --pathToApiDefinitions aws-cpp-sdk-core-tests/resources/api-descriptions --serviceName document-test --apiVersion 2021-06-28 --outputLocation ./generated/ --prepareTool
252+
COMMAND ${PYTHON_CMD} scripts/generate_sdks.py --api-definitions-path aws-cpp-sdk-core-tests/resources/api-descriptions --service-name document-test --api-version 2021-06-28 --output-path ./generated/ --prepare-tool
253253
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
254254
)
255255
message(STATUS "Generated service: document-test, version: 2021-06-28")

tools/scripts/generate_sdks.py

100644100755
Lines changed: 147 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,190 @@
1-
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2-
# SPDX-License-Identifier: Apache-2.0.
3-
#
1+
#!/usr/bin/env python3
2+
3+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4+
# SPDX-License-Identifier: Apache-2.0
5+
46
import argparse
5-
import shutil
6-
import subprocess
77
import re
88
import subprocess
99
import os
1010
import zipfile
11-
import io
1211
import codecs
13-
from subprocess import PIPE, STDOUT, Popen
12+
from subprocess import PIPE, Popen
1413
from os import listdir
1514
from os.path import isfile, join
15+
import sys
16+
17+
18+
def parse_arguments():
19+
path_to_exe = os.path.dirname(sys.argv[0])
20+
cwd_path = os.getcwd()
21+
parser = argparse.ArgumentParser(
22+
description="Generates an SDK Service Client given an service model name and version")
23+
parser.add_argument("--output-path",
24+
help="Path to where to generate the code for the client.",
25+
default=cwd_path,
26+
action="store")
27+
parser.add_argument("--service-name",
28+
help="Name of the service client that needs to be generated.",
29+
default=None,
30+
action="store")
31+
parser.add_argument("--generate-config-defaults",
32+
help="Generate config defaults from defaults.json.",
33+
action="store_true")
34+
parser.add_argument("--config-defaults-path",
35+
help="Path to the json file defining the default config values.",
36+
default=os.path.join(path_to_exe, "../code-generation/defaults/sdk-default-configuration.json"),
37+
action="store")
38+
parser.add_argument("--api-version",
39+
help="Declaration of API version to be used to select models for generation.",
40+
default="",
41+
action="store")
42+
parser.add_argument("--namespace",
43+
help="Namespace argument to be passed to the generator.",
44+
default="",
45+
action="store")
46+
parser.add_argument("--license",
47+
help="License text to be passed to the generator.",
48+
default="",
49+
action="store")
50+
parser.add_argument("--api-definitions-path",
51+
help="Path to the service api model specification files to read during generation.",
52+
default=os.path.join(path_to_exe, "../code-generation/api-descriptions"),
53+
action="store")
54+
parser.add_argument("--code-generator-path",
55+
help="Path to the code generation engine.",
56+
default=os.path.join(path_to_exe, "../code-generation/generator"),
57+
action="store")
58+
parser.add_argument("--prepare-tools",
59+
help="Makes sure generation environment is setup by maven.",
60+
action="store_true")
61+
parser.add_argument("--standalone",
62+
help="Pass to generator the requirement for service client to generate separate package "
63+
"with dependency on prebuilt C++ SDK.",
64+
action="store_true")
65+
parser.add_argument("--list-all",
66+
help="Lists all service clients available for generation.",
67+
action="store_true")
68+
parser.add_argument("--enable-virtual-operations",
69+
help="Mark operation functions in service client as virtual functions.",
70+
action="store_true")
71+
72+
return vars(parser.parse_args())
1673

17-
def ParseArguments():
18-
argMap = {}
19-
20-
parser = argparse.ArgumentParser(description="Generates an SDK given an sdk name and version")
21-
parser.add_argument("--outputLocation", action="store")
22-
parser.add_argument("--serviceName", action="store")
23-
parser.add_argument("--clientConfigDefaults", action="store")
24-
parser.add_argument("--apiVersion", action="store")
25-
parser.add_argument("--namespace", action="store")
26-
parser.add_argument("--licenseText", action="store")
27-
parser.add_argument("--pathToApiDefinitions", action="store")
28-
parser.add_argument("--pathToGenerator", action="store")
29-
parser.add_argument("--prepareTools", help="Makes sure generation environment is setup.", action="store_true")
30-
parser.add_argument("--standalone", help="Build custom client as a separete package, with prebuilt C++ SDK as dependency", action="store_true")
31-
parser.add_argument("--listAll", help="Lists all available SDKs for generation.", action="store_true")
32-
parser.add_argument("--enableVirtualOperations", help ="Mark operation functions in service client as virtual functions.", action="store_true")
33-
34-
args = vars( parser.parse_args() )
35-
argMap[ "outputLocation" ] = args[ "outputLocation" ] or "./"
36-
argMap[ "serviceName" ] = args[ "serviceName" ] or None
37-
argMap[ "clientConfigDefaults" ] = args[ "clientConfigDefaults" ] or None
38-
argMap[ "apiVersion" ] = args[ "apiVersion" ] or ""
39-
argMap[ "namespace" ] = args[ "namespace" ] or ""
40-
argMap[ "licenseText" ] = args[ "licenseText" ] or ""
41-
argMap[ "pathToApiDefinitions" ] = args["pathToApiDefinitions"] or "./code-generation/api-descriptions"
42-
argMap[ "pathToGenerator" ] = args["pathToGenerator"] or "./code-generation/generator"
43-
argMap[ "prepareTools" ] = args["prepareTools"]
44-
argMap[ "standalone" ] = args["standalone"]
45-
argMap[ "listAll" ] = args["listAll"]
46-
argMap[ "enableVirtualOperations" ] = args["enableVirtualOperations"]
47-
48-
return argMap
4974

5075
serviceNameRemaps = {
51-
"runtime.lex" : "lex",
52-
"runtime.lex.v2" : "lexv2-runtime",
53-
"models.lex.v2" : "lexv2-models",
54-
"entitlement.marketplace" : "marketplace-entitlement",
55-
"runtime.sagemaker" : "sagemaker-runtime",
56-
"transfer" : "awstransfer",
57-
"transcribe-streaming" : "transcribestreaming",
58-
"streams.dynamodb" : "dynamodbstreams"
76+
"runtime.lex": "lex",
77+
"runtime.lex.v2": "lexv2-runtime",
78+
"models.lex.v2": "lexv2-models",
79+
"entitlement.marketplace": "marketplace-entitlement",
80+
"runtime.sagemaker": "sagemaker-runtime",
81+
"transfer": "awstransfer",
82+
"transcribe-streaming": "transcribestreaming",
83+
"streams.dynamodb": "dynamodbstreams"
5984
}
6085

61-
def DiscoverAllAvailableSDKs(discoveryPath):
86+
87+
def discover_all_available_service_models(discovery_path):
6288
sdks = {}
6389

64-
filesInDir = [f for f in listdir(discoveryPath) if isfile(join(discoveryPath, f))]
90+
files_in_dir = [f for f in listdir(discovery_path) if isfile(join(discovery_path, f))]
6591

66-
for file in filesInDir:
67-
match = re.search('([\w\d\.-]+)-(\d{4}-\d{2}-\d{2}).normal.json', file)
92+
for file in files_in_dir:
93+
match = re.search(r"([\w.-]+)-(\d{4}-\d{2}-\d{2}).normal.json", file)
6894
if match:
69-
serviceName = match.group(1)
70-
if serviceName in serviceNameRemaps:
71-
serviceName = serviceNameRemaps[serviceName]
72-
73-
sdk = {}
74-
sdk['serviceName'] = serviceName
75-
sdk['apiVersion'] = match.group(2)
76-
sdk['filePath'] = join(discoveryPath, file)
95+
service_name = match.group(1)
96+
if service_name in serviceNameRemaps:
97+
service_name = serviceNameRemaps[service_name]
98+
99+
sdk = {'serviceName': service_name, 'apiVersion': match.group(2), 'filePath': join(discovery_path, file)}
77100
sdks['{}-{}'.format(sdk['serviceName'], sdk['apiVersion'])] = sdk
78101

79-
if serviceName == "s3":
80-
s3crt = {}
81-
s3crt['serviceName'] = "s3-crt"
82-
s3crt['apiVersion'] = sdk['apiVersion']
83-
s3crt['filePath'] = sdk['filePath']
102+
if service_name == "s3":
103+
s3crt = {'serviceName': "s3-crt", 'apiVersion': sdk['apiVersion'], 'filePath': sdk['filePath']}
84104
sdks['s3-crt-{}'.format(s3crt['apiVersion'])] = s3crt
85105

86106
return sdks
87107

88-
def PrepareGenerator(generatorPath):
89-
currentDir = os.getcwd()
90-
os.chdir(generatorPath)
91-
process = subprocess.call('mvn package', shell=True)
92-
os.chdir(currentDir)
93108

94-
def GenerateSdk(generatorPath, sdk, outputDir, namespace, licenseText, standalone, enableVirtualOperations):
109+
def prepare_generator(generator_path):
110+
current_dir = os.getcwd()
111+
os.chdir(generator_path)
112+
subprocess.call('mvn package', shell=True)
113+
os.chdir(current_dir)
114+
115+
116+
def generate_sdk_service_client(generator_path, sdk, output_dir, namespace, license_text, standalone,
117+
enable_virtual_operations):
95118
try:
96-
with codecs.open(sdk['filePath'], 'rb', 'utf-8') as api_definition:
119+
with codecs.open(sdk['filePath'], 'rb', 'utf-8') as api_definition:
97120
api_content = api_definition.read()
98-
jar_path = join(generatorPath, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar')
99-
process = Popen(['java', '-jar', jar_path, '--service', sdk['serviceName'], '--version', sdk['apiVersion'], '--namespace', namespace, '--license-text', licenseText, '--language-binding', 'cpp', '--arbitrary', '--standalone' if standalone else '', '--enable-virtual-operations' if enableVirtualOperations else '' ], stdout=PIPE, stdin=PIPE)
100-
writer = codecs.getwriter('utf-8')
101-
stdInWriter = writer(process.stdin)
102-
stdInWriter.write(api_content)
103-
process.stdin.close()
104-
output = process.stdout.read()
105-
if output:
106-
with zipfile.ZipFile(output.strip().decode('utf-8'), 'r') as zip:
107-
zip.extractall(outputDir)
121+
jar_path = join(generator_path, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar')
122+
process = Popen(['java', '-jar', jar_path, '--service', sdk['serviceName'], '--version', sdk['apiVersion'],
123+
'--namespace', namespace, '--license-text', license_text, '--language-binding', 'cpp',
124+
'--arbitrary', '--standalone' if standalone else '',
125+
'--enable-virtual-operations' if enable_virtual_operations else ''],
126+
stdout=PIPE, stdin=PIPE)
127+
generation_io_handling(api_content, output_dir, process)
108128
except EnvironmentError as ex:
109129
print('Error generating sdk {} with error {}'.format(sdk, ex))
110130

111-
def GenerateDefaults(generatorPath, defaultsDefinitionPath, outputDir, namespace, licenseText):
131+
132+
def generation_io_handling(api_content, output_dir, process):
133+
writer = codecs.getwriter('utf-8')
134+
std_in_writer = writer(process.stdin)
135+
std_in_writer.write(api_content)
136+
process.stdin.close()
137+
output = process.stdout.read()
138+
if output:
139+
with zipfile.ZipFile(output.strip().decode('utf-8'), 'r') as zip_item:
140+
zip_item.extractall(output_dir)
141+
142+
143+
def generate_defaults(generator_path, defaults_definition_path, output_dir, namespace, license_text):
112144
try:
113-
with codecs.open(defaultsDefinitionPath, 'rb', 'utf-8') as defaults_definition:
145+
with codecs.open(defaults_definition_path, 'rb', 'utf-8') as defaults_definition:
114146
defaults_content = defaults_definition.read()
115-
jar_path = join(generatorPath, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar')
116-
process = Popen(['java', '-jar', jar_path, '--defaults', 'global', '--namespace', namespace, '--license-text', licenseText, '--language-binding', 'cpp', '--arbitrary'], stdout=PIPE, stdin=PIPE)
117-
writer = codecs.getwriter('utf-8')
118-
stdInWriter = writer(process.stdin)
119-
stdInWriter.write(defaults_content)
120-
process.stdin.close()
121-
output = process.stdout.read()
122-
if output:
123-
with zipfile.ZipFile(output.strip().decode('utf-8'), 'r') as zip:
124-
zip.extractall(outputDir)
147+
jar_path = join(generator_path, 'target/aws-client-generator-1.0-SNAPSHOT-jar-with-dependencies.jar')
148+
process = Popen(
149+
['java', '-jar', jar_path, '--defaults', 'global', '--namespace', namespace, '--license-text',
150+
license_text, '--language-binding', 'cpp', '--arbitrary'], stdout=PIPE, stdin=PIPE)
151+
generation_io_handling(defaults_content, output_dir, process)
152+
125153
except EnvironmentError as ex:
126154
print('Error generating {} defaults with error {}'.format('global', ex))
127155
except Exception as ex:
128156
print('Error generating {} defaults with error {}'.format('global', ex))
129157

130-
def Main():
131-
arguments = ParseArguments()
132158

133-
if arguments['prepareTools']:
134-
PrepareGenerator(arguments['pathToGenerator'])
159+
def main():
160+
# Python before 3.9 doesn't exit on parsing errors.
161+
min_python = (3, 9)
162+
if sys.version_info < min_python:
163+
sys.exit("Python %s.%s or later is required.\n" % min_python)
135164

136-
sdks = DiscoverAllAvailableSDKs(arguments['pathToApiDefinitions'])
165+
arguments = parse_arguments()
137166

138-
if arguments['listAll']:
139-
for key, value in sdks.iteritems():
167+
if arguments['prepare_tools']:
168+
prepare_generator(arguments['code_generator_path'])
169+
170+
sdks = discover_all_available_service_models(arguments['api_definitions_path'])
171+
172+
if arguments['list_all']:
173+
for value in sdks.values():
140174
print(value)
141175

142-
if arguments['serviceName']:
143-
print('Generating {} api version {}.'.format(arguments['serviceName'], arguments['apiVersion']))
144-
key = '{}-{}'.format(arguments['serviceName'], arguments['apiVersion'])
145-
GenerateSdk(arguments['pathToGenerator'], sdks[key], arguments['outputLocation'], arguments['namespace'], arguments['licenseText'], arguments['standalone'], arguments['enableVirtualOperations'])
176+
if arguments['service_name']:
177+
print('Generating {} api version {}.'.format(arguments['service_name'], arguments['api_version']))
178+
key = '{}-{}'.format(arguments['service_name'], arguments['api_version'])
179+
generate_sdk_service_client(arguments['code_generator_path'], sdks[key], arguments['output_path'],
180+
arguments['namespace'], arguments['license'], arguments['standalone'],
181+
arguments['enable_virtual_operations'])
182+
183+
if arguments['generate_config_defaults']:
184+
print('Generating {} defaults api version.'.format(arguments['generate_config_defaults']))
185+
generate_defaults(arguments['code_generator_path'], arguments['config-defaults-path'],
186+
arguments['outputLocation'], arguments['namespace'], arguments['licenseText'])
146187

147-
if arguments['clientConfigDefaults']:
148-
print('Generating {} defaults api version.'.format(arguments['clientConfigDefaults']))
149-
defaultsDefinitionPath = './code-generation/defaults/sdk-default-configuration.json'
150-
GenerateDefaults(arguments['pathToGenerator'], defaultsDefinitionPath, arguments['outputLocation'], arguments['namespace'], arguments['licenseText'])
151188

152-
Main()
189+
if __name__ == '__main__':
190+
main()

0 commit comments

Comments
 (0)