#
# CBSP Builders build rules
#
# GENERAL DESCRIPTION
#    Contains builder(s) to build a Secure App
#
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
# All Rights Reserved.
# Confidential and Proprietary - Qualcomm Technologies, Inc.


import os
import sys
import platform
import fnmatch
import hashlib
import pickle
import SCons.Script
import codecs
import base64

#----------------------------------------------------------------------------
# Global values
#----------------------------------------------------------------------------
DEFAULT_APP_SPLITBIN_COUNT = 9
DEFAULT_APP_SPLITBIN_COUNT_WITHOUT_RELRO = 8
THIS_DIR = os.path.dirname(os.path.realpath(__file__))

STANDARD_INCLUDE_PATHS = [
  '${BUILD_ROOT}/core/api/kernel/libstd',
  '${BUILD_ROOT}/core/api/kernel/libstd/stringl/',
  '${BUILD_ROOT}/ssg/api/securemsm/trustzone/gp/',
  '${BUILD_ROOT}/ssg/api/securemsm/trustzone/qsee/',
  '${BUILD_ROOT}/ssg/api/securemsm/util',
  '${BUILD_ROOT}/ssg/securemsm/trustzone/qsee/include/',
  '${BUILD_ROOT}/ssg/securemsm/trustzone/qsee/mink/include/',
]

#TODO: kick out most of these and go fix each TA to pull from the correct directories
SDK_INC_PATHS = [
  '${SDK_ROOT}/inc',
  '${SDK_ROOT}/inc/gp',
  '${SDK_ROOT}/inc/qsee',
  '${SDK_ROOT}/inc/idl',
  '${SDK_ROOT}/inc/idl/private',
  '${SDK_ROOT}/inc/private',
  '${SDK_ROOT}/inc/util',
]

#----------------------------------------------------------------------------
# Hooks for Scons
#----------------------------------------------------------------------------
def exists(env):
  return True

def generate(env):
  secure_app_generate(env)

#---------
#Build helpers
#--------
def get_out_dir(env):
  # The app scons environment is expected to define the OUT_DIR path
  return '${OUT_DIR}/'

def get_lib_out_dir(env):
  if env.get('LIB_OUT_DIR'):
    return '${LIB_OUT_DIR}/'
  else:
    return '${OUT_DIR}/'

def is_flavour2_allowed(env, sources):
  if env['PROC'] == 'RV64' and env['TZ_EXEC_MODE'] == 'riscv64':
    return   env['PROC'] == 'RV64'
  else:
    return env['PROC'] == 'A53_64'

# Don't invoke secure_app_lib_builder_helper function armv9 version
# for cmnlib/applib
def skip_list_armv9(env):
 return (not env.IsKeyEnable(['CTZL_IMAGE', 'CTZL64_IMAGE', 'APPLIB32_IMAGE', 'APPLIB64_IMAGE']))

def skip_list_riscv64(env):
  return (not env.IsKeyEnable(['CTZL_IMAGE', 'CTZL64_IMAGE', 'APPLIB32_IMAGE', 'APPLIB64_IMAGE']))

# Check isarmv9 status in environment for library.
def is_armv9_lib_env_stat(env):
  return (env.get('isarmv9'))

def is_riscv64_lib_env_stat(env):
  return (env.get('isriscv64'))

#============================================================================
# Add the Secure App Builder to the environment. Also Load other builders
# that are needed for secure app compilation via LoadToolScript helper.
#============================================================================
def secure_app_generate(env):
  env.Tool("sectools_glue", toolpath = [THIS_DIR])
  if not env.UseV2Signer():
    if env.StandaloneSdk():
      env.Tool('sectools_builder', toolpath = ['${SDK_ROOT}/scripts/sectools'])
    else:
      env.Tool('sectools_builder', toolpath = ['${SECTOOLS_DIR}'])

  if 'SECURE_APP_BUILDER_ARCHIVE_OBJS' not in env:
    env.Replace(SECURE_APP_BUILDER_ARCHIVE_OBJS=True)

  env.AddMethod(secure_app_builder, "SecureAppBuilder")
  env.AddMethod(get_object_file_name, "GetObjectFileName")
  env.AddMethod(secure_app_lib_builder, "SecureAppLibBuilder")
  env.AddMethod(offtarget_client_app_builder, "OfftargetClientAppBuilder")
  env.AddMethod(offtarget_client_app_lib_builder, "OfftargetClientAppLibBuilder")
  env.AddMethod(validate_cmnlib_symbols_external, "ValidateCmnlibSymbolsExternal")

  if env.StandaloneSdk():
    stub_elfimage=True
    if env.OfftargetTesting():
      toolchain = 'offtarget_toolchain.py'
    else:
      toolchain = 'llvm.py'
    tools = [
      os.path.join(THIS_DIR, toolchain),
      os.path.join(THIS_DIR, 'mbn_builder.py'),
      os.path.join(THIS_DIR, 'scl_builder.py'),
      os.path.join(THIS_DIR, 'get_dependency_builder.py'),
      os.path.join(THIS_DIR, 'image_environment.py'),
      os.path.join(THIS_DIR, 'secure_apps.py'),
      os.path.join(THIS_DIR, 'unittesting_apps.py'),
      os.path.join(THIS_DIR, 'pil_splitter_builder.py'),
      os.path.join(THIS_DIR, 'gen_linktime_paths_file.py'),
    ]
  else:
    stub_elfimage = 'USES_CLEAN_PACK' in env

    if env.OfftargetTesting():
      toolchain = '${BUILD_ROOT}/ssg/bsp/build/scripts/offtarget_toolchain.py'
    else:
      toolchain = '${BUILD_ROOT}/tools/build/scons/scripts/llvm.py'
    tools = [
      toolchain,
      '${BUILD_ROOT}/ssg/bsp/build/scripts/mbn_builder.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/scl_builder.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/get_dependency_builder.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/image_environment.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/pil_splitter_builder.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/secure_apps.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/unittesting_apps.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/gen_linktime_paths_file.py',
      '${BUILD_ROOT}/ssg/bsp/build/scripts/embedded_tz_dict_builder.py',
    ]


  #elfimage.py is required only for embedding internal TAs
  #Adding this script only in internal cases
  if stub_elfimage:
    env.AddMethod(dummy_method,'AddEmbeddedImage')
    env.AddMethod(dummy_method,'RemoveEmbeddedImage')
    env.AddMethod(dummy_method,'EmbeddedImagesAsBuilderSource')
    env.AddMethod(dummy_method, 'BuilderSourceAsEmbeddedImages')
  else:
    tools.append('${BUILD_ROOT}/core/bsp/build/scripts/elfimage.py')

  for tool in tools:
    env.LoadToolScript(tool)

#============================================================================
# validate_cmnlib_symbols_external:
# Purpose of the script:
# 1. This is to detect cmnlib APIs not supported scenario at compile 
#    time if particular SDK version doesn't support that API.
# 2. For unified QTEE SDKs, mainline SDK will behave like some branched out SDK.
# cmnlib used to build TAs would be present from latest SDK
# (i.e. some extra support in CMNLIB might be present).
# But on older branches, all APIs of mainline cmnlib might not be 
# present. If any unsupported API is used, then this script will fail the build.

# This  function which is called while building TA in external build.
# This is called after building TA ELF and before building TA MBN. This marks
# dependency on a dummy TA ELF file which is copy of original ELF file. This
# dummy ELF file is not used any further.
# This function invokes the script check_cmnlb_supported_apis.py to check if the
# cmnlib APIs used in TA ELF are available in env['CMNLIB_SUPPORTED_APIS'] or not.

#============================================================================

def validate_cmnlib_symbols_external(env, elf):
  # This validation is only required for ontarget TA build.
  # In offtarget scenario, we don't have dynamic cmnlib linking,
  # hence no gap in TA using cmnlib APIs and actual cmnlib APIs supported.
  if env.OfftargetTesting():
    return

  if not env.StandaloneSdk():
    return
  sdk_root_dir = str(env.subst(env.get('SDK_ROOT')))
  if "external" != sdk_root_dir.split(os.sep)[-1]:
    return
  sdk_version = SCons.Script.ARGUMENTS.get('qtee_sdk_version')
  if ((sdk_version is None) or (sdk_version.lower() in ["tz.xf.5.0", "latest"])):
    return
  if 'CMNLIB_SUPPORTED_APIS' not in env:
    raise Exception("CMNLIB_SUPPORTED_APIS list not present in env")
  cmnlib_symbol_check_tool_path = os.path.join(env.subst(env.get("SDK_ROOT")), "scripts", "check_cmnlb_supported_apis.py")
  input_elf_path = elf[0].get_abspath()
  output_elf_path = input_elf_path+'1'
  cmnlib_symbol_file_path = os.path.join(env.subst(env.get("SDK_ROOT")), "scripts", "supported_cmnlib_apis.txt")
  cmnlib_symbol_file_data = ""

  for i in env['CMNLIB_SUPPORTED_APIS']:
    cmnlib_symbol_file_data = cmnlib_symbol_file_data + i + "\n"
  cmnlib_symbol_file_handle = open(cmnlib_symbol_file_path, 'w')
  cmnlib_symbol_file_handle.write(cmnlib_symbol_file_data)
  cmnlib_symbol_file_handle.close()

  llvm_readelf_path = os.path.join(env.subst(env.get('LLVMROOT')), "bin", "llvm-readelf")

  cmd = "python " + cmnlib_symbol_check_tool_path
  cmd = cmd + " -i " + input_elf_path
  cmd = cmd + " -c " + cmnlib_symbol_file_path
  cmd = cmd + " -o " + output_elf_path
  cmd = cmd + " -s " + sdk_version
  cmd = cmd + " -l " + llvm_readelf_path

  dummy_elf = env.Command(output_elf_path, input_elf_path, cmd)


#============================================================================
# secure_app_builder:
# this builder takes in the list of source files, include file paths,
# the metadata dictionary and the desired image name to build the
# secure application. The builder takes key word arguments as optional
# parameters. The key word arguments currently processed are:
# stack_size, heap_size, sec_xml_config, user_libs
# The Node object of the final builder is returned
# back so that the caller can add it as a dependency if needed.
#============================================================================
def secure_app_builder(env, sources, includes, metadata, image, **kwargs):
  env = env.Clone()
  return_list = []

  # Unsigned MBNs should not be regenerated for one chipset
  # if they have already been generated for another chipset
  # provided TA is chipset independent and the sources, 
  # headers and libs remain the same.
  if not env.get('CHIPSET_INDEPENDENT'):
    env['MBN_ROOT_UNSIGNED'] = env['MBN_ROOT']
  else:
    env['MBN_ROOT_UNSIGNED'] = '${BUILD_ROOT}/build/ms/bin'

  deploy_variants = kwargs.get('deploy_variants', ['DO_NOT_SHIP'])
  try:
    del kwargs['deploy_variants']
  except:
    pass

  if 'stack_size' in kwargs:
    print('Error: Declaring stack size via SecureAppBuilder is no longer supported.')
    print('       Please declare stack size in the app metadata.')
    sys.exit(1)
  if 'heap_size' in kwargs:
    print('Error: Declaring heap size via SecureAppBuilder is no longer supported. ')
    print('       Please declare heap size in the app metadata.')
    sys.exit(1)

  env.AppendUnique(CPPPATH = includes)
  env.AppendUnique(CPPPATH=SDK_INC_PATHS)
  env.AppendUnique(CPPPATH = STANDARD_INCLUDE_PATHS)

  # Added -ffunction-sections to reduce the size of TAs by culling unused
  # sections at the function level rather than the file level when used with
  # the linker flag --gc-sections
  env.Append(CCFLAGS = ' -fPIC  -ffunction-sections ')

  if env.OfftargetTesting():
    if env.StandaloneSdk():
      env.Append(CPPPATH=['$SDK_ROOT/inc/offtarget','$SDK_ROOT/inc/offtarget/private'])
    env.Append(CCFLAGS= ' -g ')
    env.Append(LINKFLAGS = ' -shared -Bsymbolic -Bdynamic -Wl,-gc-sections -Wl,-e,CElfFile_invoke ')
    # When OFFTARGET_DEBUG_FLAG flag enabled. linker will pick wrap qsee_malloc, qsee_realloc,
    # qsee_free and qsee_query_heap_info calls for all TAs instead of using internal implementation.
    if env.get('OFFTARGET_DEBUG_LEAK'):
      env.Append(LINKFLAGS = " -Wl,-wrap,qsee_malloc -Wl,-wrap,qsee_free -Wl,-wrap,qsee_realloc -Wl,-wrap,qsee_query_heap_info -Wl,-wrap,mem_get_buffer_size ")
    else:
      # Wrap malloc and free calls for all TAs (except TA with nowrap_malloc_free,
      # which uses its own internal implementation of malloc and free)
      if kwargs.get('nowrap_malloc_free','0') == '0':
        env.Append(LINKFLAGS = " -Wl,-wrap,malloc -Wl,-wrap,free ")

    # wrap commonlib functions
    wrap_funcs = ['close', 'creat', 'fcntl', 'fstat',
            'fsync', 'lseek', 'lstat', 'mkdir', 'open',
            'openat', 'opendir', 'read', 'readdir', 'remove',
            'rmdir', 'telldir', 'unlink', 'unlinkat',
            'write', 'closedir',
    ]
    wrap_funcs_64 = [
            # unaligned
            '_Znwm', '_Znam', #new 64bit
            '_ZdlPv', '_ZdaPv', #delete
            '_ZnwmRKSt9nothrow_t', '_ZnamRKSt9nothrow_t', #new64bit nothrow
            '_ZdlPvRKSt9nothrow_t', '_ZdaPvRKSt9nothrow_t', #delete nothrow
            # aligned
            '_ZnwmSt11align_val_t', '_ZnamSt11align_val_t', #new 64bit
            '_ZdlPvSt11align_val_t', '_ZdaPvSt11align_val_t', #delete
            '_ZnwmSt11align_val_tRKSt9nothrow_t', '_ZnamSt11align_val_tRKSt9nothrow_t', #new64bit nothrow
            '_ZdlPvSt11align_val_tRKSt9nothrow_t', '_ZdaPvSt11align_val_tRKSt9nothrow_t', #delete nothrow
    ]
    wrap_funcs_32 = [
            # unaligned
            '_Znwj', '_Znaj', #new 32bit
            '_ZdlPv', '_ZdaPv', #delete
            '_ZnwjRKSt9nothrow_t', '_ZnajRKSt9nothrow_t', #new32bit nothrow
            '_ZdlPvRKSt9nothrow_t', '_ZdaPvRKSt9nothrow_t', #delete nothrow
            # aligned
            '_ZnwjSt11align_val_t', '_ZnajSt11align_val_t', #new 32bit
            '_ZdlPvSt11align_val_t', '_ZdaPvSt11align_val_t', #delete
            '_ZnwjSt11align_val_tRKSt9nothrow_t', '_ZnajSt11align_val_tRKSt9nothrow_t', #new32bit nothrow
            '_ZdlPvSt11align_val_tRKSt9nothrow_t', '_ZdaPvSt11align_val_tRKSt9nothrow_t', #delete nothrow
    ]
    if env.Is64BitImage():
      wrap_funcs.extend(wrap_funcs_64)
    else:
      wrap_funcs.extend(wrap_funcs_32)

    env.Append(LINKFLAGS=''.join([' -Wl,-wrap,{} '.format(f) for f in wrap_funcs]))

    if env.get('OFFTARGET_WEVERYTHING'):
      env.Append(CCFLAGS=' -Wno-error ')

  else:
    # Wrap malloc and free calls for all TAs (except TA with nowrap_malloc_free,
    # which uses its own internal implementation of malloc and free)
    if kwargs.get('nowrap_malloc_free','0') == '0':
      env.Append(LINKFLAGS = " --wrap malloc --wrap free ")

    # Added -gc-sections to garbage collect unused sections which when used with
    # compiler flag -ffunction-sections is at the function level.
    env.Append(LINKFLAGS = " -shared -Bsymbolic -Bdynamic -gc-sections -e TZAPPENTRY ")

    # pac-ret/bti support has been provided by default to 64-bit C
    # implementation based TA, based on below condition.
    if env.IsArmv9AppEnvStat() and is_flavour2_allowed(env, sources):
      if 'APP_NAME' in env and env['APP_NAME'] in ['fingerprint64', 'fingerprint64dbg']:
        env.Append(CCFLAGS    = " -march=armv8.5-a+dotprod ")
      else:
        env.Append(CCFLAGS    = " -march=armv8.5-a ")
      env.Append(CCFLAGS    = " -mbranch-protection=pac-ret+b-key+bti ")
      env.Append(ASFLAGS    = " -mmark-bti-property ")
      metadata['taGpBit']   = 'true'
      if env.IsCplusplus(sources) :
        env.Replace(LLVMCPPINC = " $LLVMROOT/aarch64-pacret-b-key-bti-none-elf/include/c++/v1")
        env.Append(LINKFLAGS  = " -L $LLVMROOT/aarch64-pacret-b-key-bti-none-elf/lib ")

    if env.IsRiscVAppEnvStat() and is_flavour2_allowed(env, sources):
      if 'APP_NAME' in env and env['APP_NAME'] in ['fingerprint64', 'fingerprint64dbg']:
        env.Append(CCFLAGS    = " -march=rv64imac ")
      metadata['taGpBit']   = 'true'    
  if env.StandaloneSdk() and env.get('OVERRIDE_TA_METADATA') and SCons.Script.ARGUMENTS.get('sdk_version') and SCons.Script.ARGUMENTS.get('sdk_version') not in ['TZ.XF.5.0', 'latest']:
    for md_key in env['OVERRIDE_TA_METADATA']:
      metadata[md_key] = env['OVERRIDE_TA_METADATA'][md_key]

  if kwargs.get('strip'):
    env.Append(CCFLAGS=' -g0')
    env.Append(LINKFLAGS=' -s')

  # Normally this would be a linker flag, but because we are using a custom linker script, we need
  # to pass this to the linker script preprocessor, which uses CCFLAGS
  if env.CheckLinkerRelroSupport() and not kwargs.get('norelro', False):
    env.Append(CCFLAGS=' -DTA_LINK_RELRO')

  default_deployables = []
 
  shared_objs = []

  image_list = [image]

  #Check if UUID is present in TA metadata. If present, generate binaries for both
  #uuid.mbn and appName.mbn
  #UUID needs to be in upper case because distNameFromUUID generated UUID in upper case.
  for key in metadata:
    if key == "UUID":
      image_list.append(metadata[key].upper())

  build_path = get_out_dir(env)

  # Check for the existence of built TA flag in SCons ARGUMENTS array to check if the
  # TA objects have already been built for another chipset provided the TA is chipset independent
  # and directly read the built objects if present.
  built_flag = env.get('APP_NAME', '') + '_BUILT'

  # compile sources to objects first, so we can control where the .o
  # files end up at
  if not SCons.Script.ARGUMENTS.get(built_flag):
    objects = compile_sources(env, build_path, sources, deploy_variants, kwargs.get('deploy_objects'), **kwargs)
    SCons.Script.ARGUMENTS[built_flag + '_COMPILE_SOURCES'] = objects
  else:
    objects = SCons.Script.ARGUMENTS[built_flag + '_COMPILE_SOURCES']

  for image in image_list:
    build_name = build_path + image

    metadata_c_file = os.path.join(build_path, image +'_metadata', 'metadata.c')

    # We don't want the metadata.c file to be built again if it already exists and 
    # the content remain the same. So, preventing the auto removal of existing metadata.c
    # by SCons.
    env.Precious(env.subst(metadata_c_file))

    # Encoding the metadata dict as pickle.dumps() requires a byte object.
    pickled = codecs.encode(pickle.dumps(metadata), "base64").decode()

    if not SCons.Script.ARGUMENTS.get(built_flag):
      md = env.AddSecureAppMetadata(source=env.Value(pickled),target=metadata_c_file)
      md = env.Object(source=md)
      SCons.Script.ARGUMENTS[built_flag + '_METADATA'] = md
    else:
      md = SCons.Script.ARGUMENTS[built_flag + '_METADATA']

    sobjects = []
    sobjects.append(objects)
    sobjects.append(md)

    libs, objs = env.GetTADependencies()

    sobjects.extend(objs)

    if not env.OfftargetTesting():

      legacy_libc = kwargs.get('legacy_libc',
                               image in ['secure_ui_sample',
                                         'secure_ui_sample64',
                                         'fingpd32', 'fingpr32',
                                         'fingerpd', 'fingerpr'])
	
      if not legacy_libc:
        libcpath_llvm = {
        'aarch64' : '${LLVMROOT}/aarch64-none-elf/libc/lib',
        'aarch32' : '${LLVMROOT}/armv7-none-eabi/libc/lib',
        'riscv64' : '${LLVMROOT}/riscv64-unknown-elf/libc/lib',
        'riscv32' : '${LLVMROOT}/riscv32-unknown-elf/libc/lib'}
        
	    
        libcpath_pacret_bkey_bti = {
        'aarch64' : '${LLVMROOT}/aarch64-pacret-b-key-bti-none-elf/libc/lib',
        'riscv64' : '${LLVMROOT}/riscv64-unknown-elf/libc/lib',
        'riscv32' : '${LLVMROOT}/riscv32-unknown-elf/libc/lib'}
	    
        libcfname = 'libc.a'
        if  env.IsArmv9AppEnvStat():
          libcpath = libcpath_pacret_bkey_bti[env['APP_EXEC_MODE']]
        elif  env.IsRiscVAppEnvStat():
          libcpath = libcpath_pacret_bkey_bti[env['APP_EXEC_MODE']]
        else:
          libcpath = libcpath_llvm[env['APP_EXEC_MODE']]
      else:
        libcfname = 'minklibc.a'
        libcpath = '${BUILD_ROOT}/ssg/securemsm/trustzone/qsee/mink/libstd/${APP_EXEC_MODE}'
	  
        if env.StandaloneSdk():
          libcpath = '${SDK_ROOT}/libs/${APP_EXEC_MODE}'
  
      libs.append(os.path.join(libcpath, libcfname))

      if env.Is64BitImage():
        if  env.IsArmv9AppEnvStat():
          libs.insert(0, env.File('${LLVMLIB}/libclang_rt.builtins-pacret-b-key-bti-aarch64.a'))
        elif  env.IsRiscVAppEnvStat(): #ToDo: define the IsRiscVAppEnvStat func in the env; default is 64 bit
          libs.insert(0, env.File('${LLVMLIB}/libclang_rt.builtins-riscv64.a'))
        else:
          libs.insert(0, env.File('${LLVMLIB}/libclang_rt.builtins-aarch64.a'))
      elif env['PROC'] == 'RV32':
        libs.insert(0, env.File('${LLVMLIB}/libclang_rt.builtins-riscv32.a'))        
      else:
        libs.insert(0, env.File('${LLVMLIB}/libclang_rt.builtins-arm.a'))

    libs.extend(kwargs.get('user_libs',[]))
    libs = env.Flatten(libs)
    libs = [env.File(l) for l in libs]

    if env.OfftargetTesting():
      if len(image_list) > 1 and image != metadata['UUID'].upper():
        continue
      if env.StandaloneSdk():
        cmnlib_path = "${SDK_ROOT}/libs/${APP_EXEC_MODE}/"
      else:
        cmnlib_path = "${BUILD_ROOT}/ssg/bsp/trustzone/qsapps/commonlib/build/${SHORT_BUILDPATH}/${PROC}/"

      env.Append(LINKFLAGS = '-L' + cmnlib_path + " ")
      if env['OFFTARGET_ANDROID']:
        env.Append(LINKFLAGS = ' -Wl,-rpath=/data/QTEEEmu/libs ')
      else:
        env.Append(LINKFLAGS = ' -Wl,-rpath=' + cmnlib_path + " ")

      libs.append('cmnlib')


    # For offtarget testing, we don't have special linker scripts, mbns,
    # pil splitting, sectools signing, or deploying, so we can just build
    # the TA as a shared object and return it.
    if env.OfftargetTesting():
      so = env.Program(get_out_dir(env) + image + env['SHLIBSUFFIX'], source=sobjects, LIBS=libs)

      #We must explicitly register a dependency on cmnlib because we do not link with the full path.
      if not env.StandaloneSdk():
        if env.Is64BitImage():
          env.Depends(so, env.Alias('cmnlib64offtarget'))
        else:
          env.Depends(so, env.Alias('cmnlibofftarget'))
       
      return so 
    # When building on-target, we just build the elf as usual and add it to the list of
    # artifacts to be returned.
    if not SCons.Script.ARGUMENTS.get(built_flag):
      elf = env.Program(build_name, source=sobjects, LIBS=libs)
      SCons.Script.ARGUMENTS[built_flag + '_ELF'] = elf
    else:
      elf = SCons.Script.ARGUMENTS[built_flag + '_ELF'] 
    return_list.extend(elf)

    if not SCons.Script.ARGUMENTS.get(built_flag):
      if not env.StandaloneSdk():
        env.IsImageEmbedded(image)
      scl = env.SclBuilder(build_name, env.GetTALinkerScript())
      SCons.Script.ARGUMENTS[built_flag + '_SCL'] = scl
    else:
      scl = SCons.Script.ARGUMENTS[built_flag + '_SCL']

    env.ValidateCmnlibSymbolsExternal(elf)
    env.Depends(elf, scl)
    env.Depends(elf, env.GetCommonlibMBN())

    image_map = env.SideEffect(build_name + '.map', elf)
    env.Clean(elf, image_map)

    if not SCons.Script.ARGUMENTS.get(built_flag):
      pbn = env.InstallAs(build_name + ".pbn", elf)
      SCons.Script.ARGUMENTS[built_flag + '_PBN'] = pbn
    else:
      pbn = SCons.Script.ARGUMENTS[built_flag + '_PBN']
    return_list.extend(pbn)

    alt_sign = kwargs.get('alt_sign')
    if not alt_sign:
      soc_hw_version = env.get("SOC_HW_VERSION")
      soc_vers = env.get("SOC_VERS")
    else:
      soc_hw_version = None
      soc_vers = None

    compression_allowed = True
    if env.StandaloneSdk():
      if 'COMPRESS_TA_IMAGE' in env:
        compression_allowed = env.get('COMPRESS_TA_IMAGE')

    if compression_allowed:
      # TA's SConscript will pass argument whether to compress that TA or not
      compress_ta_image = kwargs.get('compress_ta_image', False)
      env['TA_PROP_CONFIG'] = metadata['appName'].replace('.','_') + "_config"
      if env['TA_PROP_CONFIG'] not in env:
        env[env['TA_PROP_CONFIG']] = dict()
      env[env['TA_PROP_CONFIG']]['compress_ta_image'] = compress_ta_image

    qti_doublesign = kwargs.get('qti_doublesign')
    app_id = kwargs.get('user_app_id', env.GetAppIDByImageName(image))

    if env.UseV2Signer():
      # Set sectools v2 container path for SDK environment.
      if env.StandaloneSdk():
        # Select sectools v2 location to use
        # order of preference
        #   1. os.environ["SECTOOLS"] if it's set
        #   2. appropriate server if server select script exists

        sectools = os.environ.get('SECTOOLS')
        if sectools:
          if not os.path.isfile(sectools):
            raise RuntimeError(
              "Sectools location not accessible at: '{}'".format(sectools))
        else:
          sectools = env.get('SECTOOLS_VERSION')
          if sectools:
            if not os.path.isfile(sectools):
              raise RuntimeError(
                "Sectools location not accessible at: '{}'".format(sectools))
          else:
            sys.path.insert(0, env.subst('${SDK_ROOT}/scripts'))
            if os.path.isfile(os.path.join(env.subst('${SDK_ROOT}'), 'scripts', 'sectools_finder.py')):
              import sectools_finder
              sectools = sectools_finder.find("v2", "1.38")
              print("sectools = {}".format(sectools))
              if sectools is None or not os.path.isfile(sectools):
                raise RuntimeError(
                  "Sectools location not accessible at: '{}'".format(sectools))
            else:
              print("SECTOOLS environment variable not set")

        # Set sectools path in environment
        if sectools is not None:
            sectools = os.path.abspath(sectools)
            print("sectools = {}".format(sectools))
            env.Replace(SECTOOLS = sectools)

      if qti_doublesign:
        signed_mbn = env.DoubleSign_v2(elf, image + ".mbn", "TZ-APP-QTI",
                                       secondary_software_id=app_id)
      else:
        oem_id = kwargs.get('oem_id', None)
        oem_product_id = kwargs.get('oem_product_id', None)
        root_key_path = kwargs.get('root_key_path', None)
        root_cer_path = kwargs.get('root_cer_path', None)
        signing_mode = kwargs.get('signing_mode', None)
        platform_binding = kwargs.get('platform_binding', 'INDEPENDENT')
        signed_mbn = env.OEMExclusiveSign_v2(
            elf, image + ".mbn", "TZ-APP-OEM", secondary_software_id=app_id,
            oem_id=oem_id, oem_product_id=oem_product_id,
            root_key=root_key_path, root_certificate=root_cer_path,
            signing_mode=signing_mode, platform_binding=platform_binding)
    else:  # V1 signing
      # Sectools signing
      default_config = env.subst('${SECIMAGE_CONFIG}')
      if default_config == 'secimage.xml':
        default_config = env.SectoolConfigV1()
      elif default_config == 'secimagev2.xml':
        default_config = env.SectoolConfigV2()
      elif default_config == 'secimagev3.xml':
        default_config = env.SectoolConfigV3()
      elif default_config == 'secimage_ecc.xml':
        default_config = env.SectoolConfigECC()
      elif default_config == 'secimage_eccv3.xml':
        default_config = env.SectoolConfigECCV3()
      else:
        raise ValueError('{} is not a valid secimage*.xml file'.format(default_config))

      secimage_xml_config = kwargs.get('sec_xml_config', default_config)

      #if the user passes his/her own secimage_xml_config file, the expectation here is that the
      #sign_id match the image name.
      if default_config != secimage_xml_config:
        sign_id = image
      else:
        sign_id = 'tzapps_dbl' if qti_doublesign else 'tzapps'

      if qti_doublesign:
        if env.StandaloneSdk():
          secimage_xml_config_qti = os.path.join(os.path.dirname(default_config), '${SECIMAGE_CONFIG_QTI}')
        else:
          secimage_xml_config_qti = '${SECTOOLS_DIR}/config/integration/${SECIMAGE_CONFIG_QTI}'

        signed_mbn = env.DoubleSign(elf, image + ".mbn", sign_id, app_id=app_id,
                                    config=env.RealPath(secimage_xml_config),
                                    config_qti=secimage_xml_config_qti)
      else:
        signed_mbn = env.OEMExclusiveSign(elf, image + ".mbn", sign_id, app_id=app_id, config=env.RealPath(secimage_xml_config))

    return_list.extend(signed_mbn)

    default_deployables.append(signed_mbn)

    # These two lines are intended to be temporary.  The world is not
    # ready to migrate over from split bins to MBN files.  When everyone
    # is ready, we can remove these two lines and split bins will be the
    # exception.
    if 'force_splitbins_on_sdk' not in kwargs:
      kwargs['force_splitbins_on_sdk'] = True

    pil_base_dir = '${BUILD_ROOT}/build/ms/bin/PIL_IMAGES/SPLITBINS_${SHORT_BUILDPATH}'
    
    if env.get('LINKER_RELRO_SUPPORT'):
      default_app_splitbin_count = DEFAULT_APP_SPLITBIN_COUNT
    else:
      default_app_splitbin_count = DEFAULT_APP_SPLITBIN_COUNT_WITHOUT_RELRO

    if env.StandaloneSdk() and not kwargs.get('force_splitbins_on_sdk'):
      # pil splitting not supported in standalone sdk unless
      # specifically asked for
      pass
    else:
      # Build files for PIL driver
      split_bin_count = kwargs.get('split_bin_count',default_app_splitbin_count)
      #if mbn is compressed, splitbins are not created for that mbn
      pil_split_mbn = [x for x in signed_mbn if "compressed" != str(x).split(os.path.sep)[-2]]
      pil_bin_list = env.PilSplit(pil_split_mbn,
                                  pil_base_dir, split_bin_count)
      return_list.extend(pil_bin_list)
      default_deployables.append(pil_bin_list)

    # deploy_sources: ex. sources passed from TA Sconscript(if given), None(default).
    # deploy_variants: ex. HY11_1(if given), 'DO_NOT_SHIP'(default).
    # mbn, pil images and deploy_sources(if given) are deployed to given
    # deploy variants.

    deployables = default_deployables
    if kwargs.get('deploy_sources'):
      deployables += kwargs.get('deploy_sources')

    # elfs & maps are always saved to DO_NOT_SHIP. If they are desired
    # in external dirs, they must be passed in by the caller. sym files
    # are not explicitly created by this builder & thus must be deployed
    # from the app's SConscript.
    env.DeployInternal([elf, image_map])

  env.Deploy(deployables, deploy_variants=deploy_variants, skip_check=True)

  # Capture the TA related info getting built currently.
  # This is used later for generating per chipset TA info report.
  env.CaptureTaData(metadata_c_file, elf, signed_mbn, deploy_variants)

  return return_list

#============================================================================
# Create the offtarget client app
# based on secure_app_builder w/o. metadata and env.GetTADependencies()
#============================================================================
def offtarget_client_app_builder(env, sources, includes, image, **kwargs):
  env = env.Clone()

  if not env.OfftargetTesting():
    raise ValueError('Error: offtarget_client_app_builder only supported for offtarget compilation')
    sys.exit(1)

  use_qteeemu_release = kwargs.get('use_qteeemu_release')
  if use_qteeemu_release and env.StandaloneSdk():
    raise ValueError('Error: Argument \'use_qteeemu_release\' not supported in the QTEE SDK.')
    sys.exit(1)

  test_svc_enabled = kwargs.get('test_svc_enabled')
  if test_svc_enabled and env.StandaloneSdk():
    raise ValueError('Error: QTEEEmu test variant not available in the QTEE SDK.')
    sys.exit(1)

  if kwargs.get('strip'):
    env.Append(CCFLAGS=' -g0')
    env.Append(LINKFLAGS=' -s')

  if env.get('OFFTARGET_WEVERYTHING'):
    env.Append(CCFLAGS=' -Wno-error ')

  # By default the offtarget toolchain has exceptions and RTTI disabled, and uses C++11 (aligned
  # with on-target settings).
  # Enable these and use C++17 in CAs.
  env.Append(CXXFLAGS= ' -fexceptions -frtti -std=c++17 ')

  deploy_variants = kwargs.get('deploy_variants', ['DO_NOT_SHIP'])


  env.AppendUnique(CPPPATH = includes)
  env.AppendUnique(CPPPATH=SDK_INC_PATHS)
  env.AppendUnique(CPPPATH='${BUILD_ROOT}/ssg/securemsm/sdk/CA/inc')
  env.AppendUnique(CPPPATH = STANDARD_INCLUDE_PATHS)
  if env.StandaloneSdk():
    env.Append(CPPPATH=['$SDK_ROOT/inc/offtarget','$SDK_ROOT/inc/offtarget/private'])
  else:
    env.AppendUnique(CPPPATH='${BUILD_ROOT}/ssg/securemsm/sdk/QTEEEmu/client_apis/inc')

  if not env['OFFTARGET_ANDROID']:
    env.Append(LINKFLAGS= ' -lpthread -ldl ')

  build_path = os.path.join(get_out_dir(env))
  build_name = os.path.join(build_path, image)

  if kwargs.get('no_emulation_libs'):
    libs = []
    linktime_paths_object = ""
  else:
    libs, cmnlib_path, config_path = env.GetQteeEmuLibs(use_qteeemu_release, test_svc_enabled)

    if env['OFFTARGET_ANDROID']:
      env.Append(CPPDEFINES = 'TA_PATH_DEFINE=\\"/data/QTEEEmu/TAs/\\"')
      cmnlib_path = "/data/QTEEEmu/libs/"
      config_path = "/data/QTEEEmu/config/QTEEEmu_default_config.json"

    linktime_paths = [('CMNLIB_PATH', cmnlib_path), ('CONFIG_PATH', config_path)]
    linktime_paths_as_source = env.LinktimePathsListToSource(linktime_paths)

    linktime_paths_c = os.path.join(build_path, 'linktime_paths_{}.c'.format(image))

    linktime_paths_file = env.GenerateLinktimePathsFile(source=linktime_paths_as_source, target=linktime_paths_c)
    linktime_paths_object = env.Object(source=linktime_paths_file)

  # compile sources to objects first, so we can control where the .o files end up at
  sobjects = compile_sources(env, build_path, sources, deploy_variants, kwargs.get('deploy_objects'), **kwargs)

  if linktime_paths_object:
    sobjects.append(linktime_paths_object)

  # User libs must come first in the list of libraries so that QTEEEmu symbols are correctly
  # resolved.
  #
  # If user libs come at the end of the list, and the CA objects themselves don't contain any
  # reference to QTEEEmu symbols, i.e. they rely on a library for all QTEEEmu function calls,
  # the symbols might be resolved incorrectly because the dependency on an undefined symbol is
  # not registered when the QTEEEmu libraries are linked.
  libs = kwargs.get('user_libs',[]) + libs
  libs = env.Flatten(libs)
  libs = [env.File(l) for l in libs]

  # For libc++_shared.so, until we bundle it with an APK.
  if env['OFFTARGET_ANDROID']:
    env.Append(LINKFLAGS = ' -Wl,-rpath=/data/QTEEEmu/libs ')

  elf = env.Program(build_name, source=sobjects, LIBS=libs)
  return elf

#this helper function helps return 3 parameters about the object file that is
#getting compiled. It determines the name and relative path of the object file
#from the path and name of source file being compiled. It also returns the
#extension of the source file name. Some apps include '.o' files in the list
#of sources. We want to add these object files directly to list of objects
#since SCons Object builder does not take an object file. Moreover, we also
#return back the status of presence of the source file, to determine if
#the file is stripped or not.
def get_object_file_name(env, spath):
  #source files can be of the form a.c, ../x/a.c, b/a.c.
  #in each of above cases, we want objects to be stored as
  #a.o, x/a.o, b/a.o
  if isinstance(spath, str):
    sstr = spath
    sourceNodeRepr = env.File(spath)
  elif isinstance(spath, list):
    print('...Error: get_object_file_name does not process list!! %s' % (str(spath)))
    exit(1)
  else:
    sourceNodeRepr = spath

  spath_str = sourceNodeRepr.path
  split_pattern = env.subst('${BUILD_ROOT}/')
  if platform.system() == 'Windows':
    split_pattern = split_pattern.replace('/', '&').split(':')[-1]
    spath_str = spath_str.split(':')[-1].replace(os.sep, '&')
    sstr = spath_str.split(split_pattern)[-1]
    sstr = sstr.replace('&', os.sep)
  else:
    sstr = spath_str.split(split_pattern)[-1]

  #split at qsapps, otherwise path may become too long for windows to create object file
  if 'qsapps' in sstr:
    sstr = sstr.split('qsapps' + os.sep)[-1]
  elif 'trustzone_images' in sstr:
    sstr = sstr.split('trustzone_images' + os.sep)[-1]
  elif 'qtee_tas' in sstr:
    sstr = sstr.split('qtee_tas' + os.sep)[-1]
  name, extension = os.path.splitext(sstr)

  #print 'Returning name and extension = %s %s from get_object_file_name' % (name, extension)
  #also return if the source file exists or we need to look for precompiled object file
  return (name, extension, sourceNodeRepr.exists())

#this is the helper function that helps build libraries that are used by
#apps to compose and link with their images. Using the secure app lib builder,
#we easily control the parameters like library name format, destination of libs and
#lib artifacts.
#archive_objs has special behavior regarding its behavior:
#  exists in kwargs -> use that value to decide to archive or not
#  doesn't exist in kwargs -> use env['SECURE_APP_BUILDER_ARCHIVE_OBJS']
def secure_app_lib_builder_helper(env, includes, sources, libname, deploy_sources=None, deploy_variants=None, deploy_lib=None, **kwargs):
  if deploy_variants is None:
    deploy_variants = ['DO_NOT_SHIP']
  env = env.Clone()

  #take include path and file path relative to BUILD_ROOT in .debug_line_str & .debug_str segment
  env.Append(CXXFLAGS = "-fdebug-prefix-map=${BUILD_ROOT}=.")
  env.Append(CFLAGS = '-fdebug-prefix-map=${BUILD_ROOT}=.')

  if kwargs.get('strip'):
    env.Append(CCFLAGS=' -g0')
    env.Append(LINKFLAGS=' -s')

  if env.get('OFFTARGET_WEVERYTHING'):
    env.Append(CCFLAGS=' -Wno-error ')

  env.Replace(LIBNAME = libname)
  env.Append(CCFLAGS = ' -fPIC -ffunction-sections ')
  env.AppendUnique(CPPPATH = includes)
  env.AppendUnique(CPPPATH=SDK_INC_PATHS)
  env.AppendUnique(CPPPATH = STANDARD_INCLUDE_PATHS)

  if is_armv9_lib_env_stat(env):
    if 'APP_NAME' in env and env['APP_NAME'] in ['fingerprint64', 'fingerprint64dbg']:
      env.Append(CCFLAGS    = " -march=armv8.5-a+dotprod ")
    elif env.IsKeyEnable(['CTZL64_IMAGE']):
      env.Append(CCFLAGS    = " -march=armv8.5-a+sm4 ")
      env.Append(ASFLAGS    = " -march=armv8.5-a+sm4 " )
    else: 
      env.Append(CCFLAGS    = " -march=armv8.5-a ")
    env.Append(CCFLAGS    = " -mbranch-protection=pac-ret+b-key+bti ")
    env.Append(ASFLAGS    = " -mmark-bti-property ")
    if env.IsCplusplus(sources) :
      env.Replace(LLVMCPPINC = " $LLVMROOT/aarch64-pacret-b-key-bti-none-elf/include/c++/v1")
      env.Append(LINKFLAGS  = " -L $LLVMROOT/aarch64-pacret-b-key-bti-none-elf/lib ")

  if is_riscv64_lib_env_stat(env):
    if 'APP_NAME' in env and env['APP_NAME'] in ['fingerprint64', 'fingerprint64dbg']:
      env.Append(CCFLAGS    = " -march=rv64imac ")
    elif env.IsKeyEnable(['CTZL64_IMAGE']):
      env.Append(CCFLAGS    = " -march=rv64imac ")
      env.Append(ASFLAGS    = " -march=rv64imac " )
    else: 
      env.Append(CCFLAGS    = " -march=rv64imac ")
    #env.Append(CCFLAGS    = " -mbranch-protection=pac-ret+b-key+bti ")
    env.Append(ASFLAGS    = " -mmark-bti-property ")

  # uses flag handling is odd and expects that env['USES_CLEAN_PACK']
  # = 'False' will evaluate to False, and that env['USES_CLEAN_PACK']
  # = 'True' evaluates to True, and also that if 'USES_CLEAN_PACK'
  # isn't in the env, it evaluates to False, so let's figure that out
  # here
  if env.StandaloneSdk():
    env.AddCommandlineArgument(env.BoolVariable('USES_CLEAN_PACK','USES_CLEAN_PACK',0))
    cleanpack = env.get('USES_CLEAN_PACK')
  else:
    cleanpack = 'USES_CLEAN_PACK' in env and (env.get('USES_CLEAN_PACK') == True or env.get('USES_CLEAN_PACK') == 'True')

  if cleanpack and not kwargs.get('force_source_compilation'):
    lib = env.File('${LIB_OUT_DIR}/' + libname + '${LIBSUFFIX}')
    if not lib.exists():
      print('...Error: lib %s does not exist' % (lib.rstr()))
      sys.exit(1)
  else:
    install_dir = '${LIB_OUT_DIR}'
    objlist = compile_sources(env, install_dir, sources, deploy_variants, **kwargs)
    archive_objs = kwargs.get('archive_objs',env.get('SECURE_APP_BUILDER_ARCHIVE_OBJS',True))

    if archive_objs:
      lib = env.Library(os.path.join(install_dir, libname), objlist)
    else:
      lib = objlist
    if env.GetOption('cleanpack'):
      env.NoClean(lib)

  if deploy_sources:
    env.Deploy(deploy_sources, deploy_variants=deploy_variants, skip_check=True)
  if deploy_lib:
    env.Deploy(lib, deploy_variants=deploy_variants, skip_check=True)
  if kwargs.get('deploy_sdk_lib'):
    env.DeploySdkLib(lib)
  return lib

# this function helps build below library versions.
# 32-bit targets build/return lib.
# 64-bit targets build lib and libv9 and return lib or libv9
# based on is_flavour2_allowed and skip_list_armv9.
def secure_app_lib_builder(env, includes, sources, libname, deploy_sources=None, deploy_variants=None, deploy_lib=None, **kwargs):
  env = env.Clone()
  isarmv9_stat =  env.IsArmv9AppEnvStat()
  isriscv_stat =  env.IsRiscVAppEnvStat()
  #if isarmv9 is already configured(cmnlib/applib/TA) in env then disable below condition.
  if not 'isarmv9' in env:
    env['isarmv9'] = False
  if not 'riscv64' in env:
    env['riscv64'] = False
  lib = secure_app_lib_builder_helper(env, includes, sources, libname, deploy_sources, deploy_variants, deploy_lib, **kwargs)
  if is_flavour2_allowed(env, sources) and skip_list_armv9(env):
    if '${PROC}' in env.get('LIB_OUT_DIR'):
      env.Replace(LIB_OUT_DIR = 'armv9/${PROC}'.join(env.get('LIB_OUT_DIR').split('${PROC}', 1)))
    else:
      env.Replace(LIB_OUT_DIR = 'armv9/$PROC'.join(env.get('LIB_OUT_DIR').split('$PROC', 1)))
    env['isarmv9'] = True
    libv9 = secure_app_lib_builder_helper(env, includes, sources, libname, deploy_sources, deploy_variants, deploy_lib, **kwargs)
    env.Depends(libv9, lib)
    if isarmv9_stat:
      lib = libv9

  elif is_flavour2_allowed(env, sources) and skip_list_riscv64(env):
    if '${PROC}' in env.get('LIB_OUT_DIR'):
      #env.Replace(LIB_OUT_DIR = 'armv9/${PROC}'.join(env.get('LIB_OUT_DIR').split('${PROC}', 1)))
      env.Replace(LIB_OUT_DIR = 'riscv64/${PROC}'.join(env.get('LIB_OUT_DIR').split('${PROC}', 1)))
    else:
      #env.Replace(LIB_OUT_DIR = 'armv9/$PROC'.join(env.get('LIB_OUT_DIR').split('$PROC', 1)))
      env.Replace(LIB_OUT_DIR = 'riscv64/$PROC'.join(env.get('LIB_OUT_DIR').split('$PROC', 1)))
    env['isriscv64'] = True
    libriscv64 = secure_app_lib_builder_helper(env, includes, sources, libname, deploy_sources, deploy_variants, deploy_lib, **kwargs)
    env.Depends(libriscv64, lib)
    if isriscv_stat:
      lib = libriscv64
  return lib

#============================================================================
# Create offtarget client app lib
#============================================================================
def offtarget_client_app_lib_builder(env, includes, sources, libname, **kwargs):
  env = env.Clone()

  if not env.OfftargetTesting():
    raise ValueError('Error: offtarget_client_app_lib_builder only supported for offtarget compilation')
    sys.exit(1)

  deploy_variants = kwargs.get('deploy_variants', ['DO_NOT_SHIP'])

  env.Replace(LIBNAME = libname)
  env.AppendUnique(CPPPATH = includes)
  env.AppendUnique(CPPPATH = SDK_INC_PATHS)
  env.AppendUnique(CPPPATH = STANDARD_INCLUDE_PATHS)

  # QTEEEmu client API headers
  if env.StandaloneSdk():
    env.Append(CPPPATH=['$SDK_ROOT/inc/offtarget','$SDK_ROOT/inc/offtarget/private'])
  else:
    env.Append(CPPPATH='${BUILD_ROOT}/ssg/securemsm/sdk/QTEEEmu/client_apis/inc')


  # By default the offtarget toolchain has exceptions and RTTI disabled, and uses C++11 (aligned
  # with on-target settings).
  # Enable these and use C++17 in CAs.
  env.Append(CXXFLAGS= ' -fexceptions -frtti -std=c++17 ')

  install_dir = '${LIB_OUT_DIR}'
  objlist = compile_sources(env, install_dir, sources, deploy_variants, **kwargs)
  lib = env.Library(os.path.join(install_dir, libname), objlist)
  if env.GetOption('cleanpack'):
    env.NoClean(lib)

  return lib

#this is the helper function that compiles the sources into object files.
#If the source files exist, they are always used for compilation.
#if the build is stripped of sources, the pre-built object files are
#used instead. To make the code and process easy, the list of prebuilt
#object files is gathered. If sources are present, then the the
#corresponding object file is removed from the list of prebuilt objects
#gathered earlier. The cummulative list of sources and objects is
#presented to the builder to use for compilation
def compile_sources(env, dest_dir, sources, deploy_variants, deploy_obj_list=None, **kwargs):
  sobjects = []

  #prepare the list of sources whose objects need to be deployed
  deploy_objects = get_deploy_object_list_sname(env, deploy_obj_list)

  for source in env.Flatten(sources):
    #print("trying to compile",env.RealPath(source))
    if isinstance(source,str):
      source = env.RealPath(source)
    objname, extension, exists = get_object_file_name(env,source)
    if not exists and kwargs.get('ignore_nonexistent_files', False):
      # This path can be hit for auto-generated source files, where proper
      # rules for creation are in place, but the file simply hasn't been built
      # yet. Setting 'ignore_nonexistent_files' will instead completely skip
      # valid auto-generated files if they haven't been created yet, potentially
      # leading to race conditions or other errors (e.g. undefined references).
      # Do not set ignore_nonexistent_files to True unless you know what you're
      # doing!
      print("compile_sources can't find file {}".format(env.RealPath(str(source))))
      #raise #TODO: what do we do now?
    else:
      if extension.lower() == '.o':
        obj = env.RealPath(source)
      else:
        obj = env.Object(os.path.join(env.subst(dest_dir),objname),
                         source,
                         **kwargs)
      sobjects.append(obj)
      if env.GetOption('cleanpack'):
        env.NoClean(obj)
      if deploy_objects:
        deploy_object(env, deploy_objects, source, obj, deploy_variants)

  return sobjects

#helper function to check if the source name exists in the list
#of sources whose objects need to be deployed.
def deploy_object(env, deploy_object_list, src_name, deploy_obj, deploy_variants):
  if isinstance(src_name, SCons.Node.Node):
    sname = src_name.get_abspath()
  else:
    sname = src_name
  if sname in deploy_object_list:
    env.Deploy(deploy_obj, deploy_variants=deploy_variants, skip_check=True)

#helper function to create an ascii named list of file names of
#sources whose objects need to be deploy
def get_deploy_object_list_sname(env, *deploy_obj_list):
  #create a list of file names whose objects need to be deployed
  #the list can be of type None, 'filename' i.e string,
  #['file1', 'file2'] list of file name strings.
  deploy_objects = []
  for dsrc in env.Flatten(deploy_obj_list):
    #the list may be empty, then kwargs call into this function
    #may just pass None..so this needs to be processed
    if dsrc is None:
      break
    dsrc = dsrc if isinstance(dsrc, list) else [dsrc]
    for fnode in dsrc:
      if isinstance(fnode, SCons.Node.Node):
        deploy_objects.append(fnode.get_abspath())
      else:
        deploy_objects.append(fnode)
  return deploy_objects

def dummy_method(env, path, name, arch, image_list):
  pass
