1*b7893ccfSSadaf Ebrahimi#!/usr/bin/env python 2*b7893ccfSSadaf Ebrahimi# 3*b7893ccfSSadaf Ebrahimi# Copyright (C) 2015 The Android Open Source Project 4*b7893ccfSSadaf Ebrahimi# 5*b7893ccfSSadaf Ebrahimi# Licensed under the Apache License, Version 2.0 (the "License"); 6*b7893ccfSSadaf Ebrahimi# you may not use this file except in compliance with the License. 7*b7893ccfSSadaf Ebrahimi# You may obtain a copy of the License at 8*b7893ccfSSadaf Ebrahimi# 9*b7893ccfSSadaf Ebrahimi# http://www.apache.org/licenses/LICENSE-2.0 10*b7893ccfSSadaf Ebrahimi# 11*b7893ccfSSadaf Ebrahimi# Unless required by applicable law or agreed to in writing, software 12*b7893ccfSSadaf Ebrahimi# distributed under the License is distributed on an "AS IS" BASIS, 13*b7893ccfSSadaf Ebrahimi# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*b7893ccfSSadaf Ebrahimi# See the License for the specific language governing permissions and 15*b7893ccfSSadaf Ebrahimi# limitations under the License. 16*b7893ccfSSadaf Ebrahimi# 17*b7893ccfSSadaf Ebrahimi 18*b7893ccfSSadaf Ebrahimiimport argparse 19*b7893ccfSSadaf Ebrahimiimport multiprocessing 20*b7893ccfSSadaf Ebrahimiimport os 21*b7893ccfSSadaf Ebrahimiimport shutil 22*b7893ccfSSadaf Ebrahimiimport subprocess 23*b7893ccfSSadaf Ebrahimiimport sys 24*b7893ccfSSadaf Ebrahimiimport time 25*b7893ccfSSadaf Ebrahimi 26*b7893ccfSSadaf Ebrahimifrom subprocess import PIPE, STDOUT 27*b7893ccfSSadaf Ebrahimi 28*b7893ccfSSadaf Ebrahimidef install_file(file_name, src_dir, dst_dir): 29*b7893ccfSSadaf Ebrahimi src_file = os.path.join(src_dir, file_name) 30*b7893ccfSSadaf Ebrahimi dst_file = os.path.join(dst_dir, file_name) 31*b7893ccfSSadaf Ebrahimi 32*b7893ccfSSadaf Ebrahimi print('Copying {} to {}...'.format(src_file, dst_file)) 33*b7893ccfSSadaf Ebrahimi if os.path.isdir(src_file): 34*b7893ccfSSadaf Ebrahimi _install_dir(src_file, dst_file) 35*b7893ccfSSadaf Ebrahimi elif os.path.islink(src_file): 36*b7893ccfSSadaf Ebrahimi _install_symlink(src_file, dst_file) 37*b7893ccfSSadaf Ebrahimi else: 38*b7893ccfSSadaf Ebrahimi _install_file(src_file, dst_file) 39*b7893ccfSSadaf Ebrahimi 40*b7893ccfSSadaf Ebrahimi 41*b7893ccfSSadaf Ebrahimidef _install_dir(src_dir, dst_dir): 42*b7893ccfSSadaf Ebrahimi parent_dir = os.path.normpath(os.path.join(dst_dir, '..')) 43*b7893ccfSSadaf Ebrahimi if not os.path.exists(parent_dir): 44*b7893ccfSSadaf Ebrahimi os.makedirs(parent_dir) 45*b7893ccfSSadaf Ebrahimi shutil.copytree(src_dir, dst_dir, symlinks=True) 46*b7893ccfSSadaf Ebrahimi 47*b7893ccfSSadaf Ebrahimi 48*b7893ccfSSadaf Ebrahimidef _install_symlink(src_file, dst_file): 49*b7893ccfSSadaf Ebrahimi dirname = os.path.dirname(dst_file) 50*b7893ccfSSadaf Ebrahimi if not os.path.exists(dirname): 51*b7893ccfSSadaf Ebrahimi os.makedirs(dirname) 52*b7893ccfSSadaf Ebrahimi link_target = os.readlink(src_file) 53*b7893ccfSSadaf Ebrahimi os.symlink(link_target, dst_file) 54*b7893ccfSSadaf Ebrahimi 55*b7893ccfSSadaf Ebrahimi 56*b7893ccfSSadaf Ebrahimidef _install_file(src_file, dst_file): 57*b7893ccfSSadaf Ebrahimi dirname = os.path.dirname(dst_file) 58*b7893ccfSSadaf Ebrahimi if not os.path.exists(dirname): 59*b7893ccfSSadaf Ebrahimi os.makedirs(dirname) 60*b7893ccfSSadaf Ebrahimi # copy2 is just copy followed by copystat (preserves file metadata). 61*b7893ccfSSadaf Ebrahimi shutil.copy2(src_file, dst_file) 62*b7893ccfSSadaf Ebrahimi 63*b7893ccfSSadaf EbrahimiTHIS_DIR = os.path.realpath(os.path.dirname(__file__)) 64*b7893ccfSSadaf Ebrahimi 65*b7893ccfSSadaf EbrahimiALL_ARCHITECTURES = ( 66*b7893ccfSSadaf Ebrahimi 'arm', 67*b7893ccfSSadaf Ebrahimi 'arm64', 68*b7893ccfSSadaf Ebrahimi 'x86', 69*b7893ccfSSadaf Ebrahimi 'x86_64', 70*b7893ccfSSadaf Ebrahimi) 71*b7893ccfSSadaf Ebrahimi 72*b7893ccfSSadaf Ebrahimi# According to vk_platform.h, armeabi is not supported for Vulkan 73*b7893ccfSSadaf Ebrahimi# so remove it from the abis list. 74*b7893ccfSSadaf EbrahimiALL_ABIS = ( 75*b7893ccfSSadaf Ebrahimi 'armeabi-v7a', 76*b7893ccfSSadaf Ebrahimi 'arm64-v8a', 77*b7893ccfSSadaf Ebrahimi 'x86', 78*b7893ccfSSadaf Ebrahimi 'x86_64', 79*b7893ccfSSadaf Ebrahimi) 80*b7893ccfSSadaf Ebrahimi 81*b7893ccfSSadaf Ebrahimidef jobs_arg(): 82*b7893ccfSSadaf Ebrahimi return '-j{}'.format(multiprocessing.cpu_count() * 2) 83*b7893ccfSSadaf Ebrahimi 84*b7893ccfSSadaf Ebrahimidef arch_to_abis(arch): 85*b7893ccfSSadaf Ebrahimi return { 86*b7893ccfSSadaf Ebrahimi 'arm': ['armeabi-v7a'], 87*b7893ccfSSadaf Ebrahimi 'arm64': ['arm64-v8a'], 88*b7893ccfSSadaf Ebrahimi 'x86': ['x86'], 89*b7893ccfSSadaf Ebrahimi 'x86_64': ['x86_64'], 90*b7893ccfSSadaf Ebrahimi }[arch] 91*b7893ccfSSadaf Ebrahimi 92*b7893ccfSSadaf Ebrahimiclass ArgParser(argparse.ArgumentParser): 93*b7893ccfSSadaf Ebrahimi def __init__(self): 94*b7893ccfSSadaf Ebrahimi super(ArgParser, self).__init__() 95*b7893ccfSSadaf Ebrahimi 96*b7893ccfSSadaf Ebrahimi self.add_argument( 97*b7893ccfSSadaf Ebrahimi '--out-dir', help='Directory to place temporary build files.', 98*b7893ccfSSadaf Ebrahimi type=os.path.realpath, default=os.path.join(THIS_DIR, 'out')) 99*b7893ccfSSadaf Ebrahimi 100*b7893ccfSSadaf Ebrahimi self.add_argument( 101*b7893ccfSSadaf Ebrahimi '--arch', choices=ALL_ARCHITECTURES, 102*b7893ccfSSadaf Ebrahimi help='Architectures to build. Builds all if not present.') 103*b7893ccfSSadaf Ebrahimi 104*b7893ccfSSadaf Ebrahimi self.add_argument('--installdir', dest='installdir', required=True, 105*b7893ccfSSadaf Ebrahimi help='Installation directory. Required.') 106*b7893ccfSSadaf Ebrahimi 107*b7893ccfSSadaf Ebrahimi # The default for --dist-dir has to be handled after parsing all 108*b7893ccfSSadaf Ebrahimi # arguments because the default is derived from --out-dir. This is 109*b7893ccfSSadaf Ebrahimi # handled in run(). 110*b7893ccfSSadaf Ebrahimi self.add_argument( 111*b7893ccfSSadaf Ebrahimi '--dist-dir', help='Directory to place the packaged artifact.', 112*b7893ccfSSadaf Ebrahimi type=os.path.realpath) 113*b7893ccfSSadaf Ebrahimi 114*b7893ccfSSadaf Ebrahimi 115*b7893ccfSSadaf Ebrahimidef main(): 116*b7893ccfSSadaf Ebrahimi print('THIS_DIR: %s' % THIS_DIR) 117*b7893ccfSSadaf Ebrahimi parser = ArgParser() 118*b7893ccfSSadaf Ebrahimi args = parser.parse_args() 119*b7893ccfSSadaf Ebrahimi 120*b7893ccfSSadaf Ebrahimi arches = ALL_ARCHITECTURES 121*b7893ccfSSadaf Ebrahimi if args.arch is not None: 122*b7893ccfSSadaf Ebrahimi arches = [args.arch] 123*b7893ccfSSadaf Ebrahimi 124*b7893ccfSSadaf Ebrahimi # Make paths absolute, and ensure directories exist. 125*b7893ccfSSadaf Ebrahimi installdir = os.path.abspath(args.installdir) 126*b7893ccfSSadaf Ebrahimi 127*b7893ccfSSadaf Ebrahimi abis = [] 128*b7893ccfSSadaf Ebrahimi for arch in arches: 129*b7893ccfSSadaf Ebrahimi abis.extend(arch_to_abis(arch)) 130*b7893ccfSSadaf Ebrahimi 131*b7893ccfSSadaf Ebrahimi shaderc_path = installdir + '/shaderc/android_test' 132*b7893ccfSSadaf Ebrahimi print('shaderc_path = %s' % shaderc_path) 133*b7893ccfSSadaf Ebrahimi 134*b7893ccfSSadaf Ebrahimi ndk_dir = os.path.join(THIS_DIR, '../../../prebuilts/toolchain') 135*b7893ccfSSadaf Ebrahimi 136*b7893ccfSSadaf Ebrahimi ndk_build = os.path.join(ndk_dir, 'ndk-build') 137*b7893ccfSSadaf Ebrahimi platforms_root = os.path.join(ndk_dir, 'platforms') 138*b7893ccfSSadaf Ebrahimi toolchains_root = os.path.join(ndk_dir, 'toolchains') 139*b7893ccfSSadaf Ebrahimi build_dir = installdir 140*b7893ccfSSadaf Ebrahimi 141*b7893ccfSSadaf Ebrahimi print('installdir: %s' % installdir) 142*b7893ccfSSadaf Ebrahimi print('ndk_dir: %s' % ndk_dir) 143*b7893ccfSSadaf Ebrahimi print('ndk_build: %s' % ndk_build) 144*b7893ccfSSadaf Ebrahimi print('platforms_root: %s' % platforms_root) 145*b7893ccfSSadaf Ebrahimi 146*b7893ccfSSadaf Ebrahimi compiler = 'clang' 147*b7893ccfSSadaf Ebrahimi stl = 'c++_static' 148*b7893ccfSSadaf Ebrahimi obj_out = os.path.join(THIS_DIR, stl, 'obj') 149*b7893ccfSSadaf Ebrahimi lib_out = os.path.join(THIS_DIR, 'jniLibs') 150*b7893ccfSSadaf Ebrahimi 151*b7893ccfSSadaf Ebrahimi print('obj_out: %s' % obj_out) 152*b7893ccfSSadaf Ebrahimi print('lib_out: %s' % lib_out) 153*b7893ccfSSadaf Ebrahimi 154*b7893ccfSSadaf Ebrahimi print('Constructing shaderc build tree...') 155*b7893ccfSSadaf Ebrahimi shaderc_root_dir = os.path.join(THIS_DIR, '../../shaderc') 156*b7893ccfSSadaf Ebrahimi 157*b7893ccfSSadaf Ebrahimi print('Pulling in vulkan headers and layers...') 158*b7893ccfSSadaf Ebrahimi vulkan_root_dir = os.path.join(THIS_DIR, '../../vulkan-validation-layers') 159*b7893ccfSSadaf Ebrahimi vulkan_headers_root_dir = os.path.join(THIS_DIR, '../../vulkan-headers') 160*b7893ccfSSadaf Ebrahimi 161*b7893ccfSSadaf Ebrahimi copies = [ 162*b7893ccfSSadaf Ebrahimi { 163*b7893ccfSSadaf Ebrahimi 'source_dir': os.path.join(shaderc_root_dir, 'shaderc'), 164*b7893ccfSSadaf Ebrahimi 'dest_dir': 'third_party/shaderc', 165*b7893ccfSSadaf Ebrahimi 'files': [ 166*b7893ccfSSadaf Ebrahimi 'Android.mk', 'libshaderc/Android.mk', 167*b7893ccfSSadaf Ebrahimi 'libshaderc_util/Android.mk', 168*b7893ccfSSadaf Ebrahimi 'third_party/Android.mk', 169*b7893ccfSSadaf Ebrahimi 'utils/update_build_version.py', 170*b7893ccfSSadaf Ebrahimi 'CHANGES', 171*b7893ccfSSadaf Ebrahimi ], 172*b7893ccfSSadaf Ebrahimi 'dirs': [ 173*b7893ccfSSadaf Ebrahimi 'libshaderc/include', 'libshaderc/src', 174*b7893ccfSSadaf Ebrahimi 'libshaderc_util/include', 'libshaderc_util/src', 175*b7893ccfSSadaf Ebrahimi 'android_test' 176*b7893ccfSSadaf Ebrahimi ], 177*b7893ccfSSadaf Ebrahimi }, 178*b7893ccfSSadaf Ebrahimi { 179*b7893ccfSSadaf Ebrahimi 'source_dir': os.path.join(shaderc_root_dir, 'spirv-tools'), 180*b7893ccfSSadaf Ebrahimi 'dest_dir': 'third_party/shaderc/third_party/spirv-tools', 181*b7893ccfSSadaf Ebrahimi 'files': [ 182*b7893ccfSSadaf Ebrahimi 'utils/generate_grammar_tables.py', 183*b7893ccfSSadaf Ebrahimi 'utils/generate_language_headers.py', 184*b7893ccfSSadaf Ebrahimi 'utils/generate_registry_tables.py', 185*b7893ccfSSadaf Ebrahimi 'utils/update_build_version.py', 186*b7893ccfSSadaf Ebrahimi 'Android.mk', 187*b7893ccfSSadaf Ebrahimi 'CHANGES', 188*b7893ccfSSadaf Ebrahimi ], 189*b7893ccfSSadaf Ebrahimi 'dirs': ['include', 'source'], 190*b7893ccfSSadaf Ebrahimi }, 191*b7893ccfSSadaf Ebrahimi { 192*b7893ccfSSadaf Ebrahimi 'source_dir': os.path.join(shaderc_root_dir, 'spirv-headers'), 193*b7893ccfSSadaf Ebrahimi 'dest_dir': 194*b7893ccfSSadaf Ebrahimi 'third_party/shaderc/third_party/spirv-tools/external/spirv-headers', 195*b7893ccfSSadaf Ebrahimi 'dirs': ['include',], 196*b7893ccfSSadaf Ebrahimi 'files': [ 197*b7893ccfSSadaf Ebrahimi 'include/spirv/1.0/spirv.py', 198*b7893ccfSSadaf Ebrahimi 'include/spirv/1.1/spirv.py', 199*b7893ccfSSadaf Ebrahimi 'include/spirv/1.2/spirv.py', 200*b7893ccfSSadaf Ebrahimi ], 201*b7893ccfSSadaf Ebrahimi }, 202*b7893ccfSSadaf Ebrahimi { 203*b7893ccfSSadaf Ebrahimi 'source_dir': os.path.join(shaderc_root_dir, 'glslang'), 204*b7893ccfSSadaf Ebrahimi 'dest_dir': 'third_party/shaderc/third_party/glslang', 205*b7893ccfSSadaf Ebrahimi 'files': ['glslang/OSDependent/osinclude.h', 206*b7893ccfSSadaf Ebrahimi 'Android.mk', 207*b7893ccfSSadaf Ebrahimi # Build version info is generated frmo the CHANGES.md file. 208*b7893ccfSSadaf Ebrahimi 'CHANGES.md', 209*b7893ccfSSadaf Ebrahimi 'build_info.h.tmpl', 210*b7893ccfSSadaf Ebrahimi 'build_info.py', 211*b7893ccfSSadaf Ebrahimi ], 212*b7893ccfSSadaf Ebrahimi 'dirs': [ 213*b7893ccfSSadaf Ebrahimi 'SPIRV', 214*b7893ccfSSadaf Ebrahimi 'OGLCompilersDLL', 215*b7893ccfSSadaf Ebrahimi 'glslang/GenericCodeGen', 216*b7893ccfSSadaf Ebrahimi 'hlsl', 217*b7893ccfSSadaf Ebrahimi 'glslang/HLSL', 218*b7893ccfSSadaf Ebrahimi 'glslang/Include', 219*b7893ccfSSadaf Ebrahimi 'glslang/MachineIndependent', 220*b7893ccfSSadaf Ebrahimi 'glslang/OSDependent/Unix', 221*b7893ccfSSadaf Ebrahimi 'glslang/Public', 222*b7893ccfSSadaf Ebrahimi ], 223*b7893ccfSSadaf Ebrahimi }, 224*b7893ccfSSadaf Ebrahimi { 225*b7893ccfSSadaf Ebrahimi 'source_dir': vulkan_root_dir, 226*b7893ccfSSadaf Ebrahimi 'dest_dir': 'vulkan/src', 227*b7893ccfSSadaf Ebrahimi 'files': [ 228*b7893ccfSSadaf Ebrahimi ], 229*b7893ccfSSadaf Ebrahimi 'dirs': [ 230*b7893ccfSSadaf Ebrahimi 'layers', 'scripts', 'build-android' 231*b7893ccfSSadaf Ebrahimi ], 232*b7893ccfSSadaf Ebrahimi }, 233*b7893ccfSSadaf Ebrahimi 234*b7893ccfSSadaf Ebrahimi { 235*b7893ccfSSadaf Ebrahimi 'source_dir': vulkan_headers_root_dir, 236*b7893ccfSSadaf Ebrahimi 'dest_dir': 'vulkan/src', 237*b7893ccfSSadaf Ebrahimi 'files': [ 238*b7893ccfSSadaf Ebrahimi ], 239*b7893ccfSSadaf Ebrahimi 'dirs': [ 240*b7893ccfSSadaf Ebrahimi 'include', 'registry' 241*b7893ccfSSadaf Ebrahimi ], 242*b7893ccfSSadaf Ebrahimi }, 243*b7893ccfSSadaf Ebrahimi ] 244*b7893ccfSSadaf Ebrahimi 245*b7893ccfSSadaf Ebrahimi default_ignore_patterns = shutil.ignore_patterns( 246*b7893ccfSSadaf Ebrahimi "*CMakeLists.txt", 247*b7893ccfSSadaf Ebrahimi "*test.h", 248*b7893ccfSSadaf Ebrahimi "*test.cc") 249*b7893ccfSSadaf Ebrahimi 250*b7893ccfSSadaf Ebrahimi for properties in copies: 251*b7893ccfSSadaf Ebrahimi source_dir = properties['source_dir'] 252*b7893ccfSSadaf Ebrahimi dest_dir = os.path.join(installdir, properties['dest_dir']) 253*b7893ccfSSadaf Ebrahimi for d in properties['dirs']: 254*b7893ccfSSadaf Ebrahimi src = os.path.join(source_dir, d) 255*b7893ccfSSadaf Ebrahimi dst = os.path.join(dest_dir, d) 256*b7893ccfSSadaf Ebrahimi print(src, " -> ", dst) 257*b7893ccfSSadaf Ebrahimi shutil.copytree(src, dst, 258*b7893ccfSSadaf Ebrahimi ignore=default_ignore_patterns) 259*b7893ccfSSadaf Ebrahimi for f in properties['files']: 260*b7893ccfSSadaf Ebrahimi print(source_dir, ':', dest_dir, ":", f) 261*b7893ccfSSadaf Ebrahimi # Only copy if the source file exists. That way 262*b7893ccfSSadaf Ebrahimi # we can update this script in anticipation of 263*b7893ccfSSadaf Ebrahimi # source files yet-to-come. 264*b7893ccfSSadaf Ebrahimi if os.path.exists(os.path.join(source_dir, f)): 265*b7893ccfSSadaf Ebrahimi install_file(f, source_dir, dest_dir) 266*b7893ccfSSadaf Ebrahimi else: 267*b7893ccfSSadaf Ebrahimi print(source_dir, ':', dest_dir, ":", f, "SKIPPED") 268*b7893ccfSSadaf Ebrahimi 269*b7893ccfSSadaf Ebrahimi print('Constructing Vulkan validation layer source...') 270*b7893ccfSSadaf Ebrahimi 271*b7893ccfSSadaf Ebrahimi build_cmd = [ 272*b7893ccfSSadaf Ebrahimi 'bash', ndk_build, '-C', build_dir + '/vulkan/src/build-android', 273*b7893ccfSSadaf Ebrahimi jobs_arg(), 274*b7893ccfSSadaf Ebrahimi 'APP_ABI=' + ' '.join(abis), 275*b7893ccfSSadaf Ebrahimi # Use the prebuilt platforms and toolchains. 276*b7893ccfSSadaf Ebrahimi 'NDK_PLATFORMS_ROOT=' + platforms_root, 277*b7893ccfSSadaf Ebrahimi 'NDK_TOOLCHAINS_ROOT=' + toolchains_root, 278*b7893ccfSSadaf Ebrahimi 'NDK_MODULE_PATH=' + installdir, 279*b7893ccfSSadaf Ebrahimi 'GNUSTL_PREFIX=', 280*b7893ccfSSadaf Ebrahimi 'APP_STL=' + stl, 281*b7893ccfSSadaf Ebrahimi 'NDK_TOOLCHAIN_VERSION=' + compiler, 282*b7893ccfSSadaf Ebrahimi 283*b7893ccfSSadaf Ebrahimi # Tell ndk-build where to put the results 284*b7893ccfSSadaf Ebrahimi 'NDK_OUT=' + obj_out, 285*b7893ccfSSadaf Ebrahimi 'NDK_LIBS_OUT=' + lib_out, 286*b7893ccfSSadaf Ebrahimi ] 287*b7893ccfSSadaf Ebrahimi 288*b7893ccfSSadaf Ebrahimi print('Building Vulkan validation layers for ABIs:' + 289*b7893ccfSSadaf Ebrahimi ' {}'.format(', '.join(abis)) + "...") 290*b7893ccfSSadaf Ebrahimi print(' '.join(build_cmd)) 291*b7893ccfSSadaf Ebrahimi 292*b7893ccfSSadaf Ebrahimi subprocess.check_call(build_cmd) 293*b7893ccfSSadaf Ebrahimi 294*b7893ccfSSadaf Ebrahimi print('Finished building Vulkan validation layers') 295*b7893ccfSSadaf Ebrahimi out_package = os.path.join(installdir, 'vulkan_validation_layers.zip') 296*b7893ccfSSadaf Ebrahimi os.chdir(lib_out) 297*b7893ccfSSadaf Ebrahimi build_cmd = [ 298*b7893ccfSSadaf Ebrahimi 'zip', '-9qr', out_package, "." 299*b7893ccfSSadaf Ebrahimi ] 300*b7893ccfSSadaf Ebrahimi 301*b7893ccfSSadaf Ebrahimi print('Packaging Vulkan validation layers') 302*b7893ccfSSadaf Ebrahimi subprocess.check_call(build_cmd) 303*b7893ccfSSadaf Ebrahimi print('Finished Packaging Vulkan validation layers') 304*b7893ccfSSadaf Ebrahimi 305*b7893ccfSSadaf Ebrahimi for properties in copies: 306*b7893ccfSSadaf Ebrahimi dest_dir = os.path.join(installdir, properties['dest_dir']) 307*b7893ccfSSadaf Ebrahimi for d in properties['dirs']: 308*b7893ccfSSadaf Ebrahimi dst = os.path.join(dest_dir, d) 309*b7893ccfSSadaf Ebrahimi print('Remove: %s' % dst) 310*b7893ccfSSadaf Ebrahimi shutil.rmtree(dst) 311*b7893ccfSSadaf Ebrahimi 312*b7893ccfSSadaf Ebrahimi return 0 313*b7893ccfSSadaf Ebrahimi 314*b7893ccfSSadaf Ebrahimi 315*b7893ccfSSadaf Ebrahimiif __name__ == '__main__': 316*b7893ccfSSadaf Ebrahimi main() 317