#============================================================================
#
# CBSP Builders build rules
#
# GENERAL DESCRIPTION
#    Contains builder(s) to build a Secure App 
#
# Copyright 2015 by Qualcomm Technologies, Incorporated.
# All Rights Reserved.
# QUALCOMM Proprietary/GTDR
#
#----------------------------------------------------------------------------
#
#  $Header: //components/rel/core.tz/1.0.5/bsp/build/scripts/secure_app_builder.py#2 $
#  $DateTime: 2016/04/13 01:37:02 $
#  $Author: pwbldsvc $
#  $Change: 10255826 $
#
#                      EDIT HISTORY FOR FILE
#
#  This section contains comments describing changes made to the module.
#  Notice that changes are listed in reverse chronological order.
#
# when       who       what, where, why
# --------   ---       ------------------------------------------------------
# 
#============================================================================
import os

#----------------------------------------------------------------------------
# Global values
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# Hooks for Scons
#----------------------------------------------------------------------------
def exists(env):
  
  return env.Detect('secure_app_builder')

def generate(env):
  secure_app_generate(env)

#============================================================================
# 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.AddMethod(secure_app_builder, "SecureAppBuilder")
  env.AddMethod(get_object_file_name, "GetObjectFileName")
  env.AddMethod(secure_app_lib_builder, "SecureAppLibBuilder")

  # load the llvm builder
  env.LoadToolScript('llvm', toolpath = [env.subst('${BUILD_SCRIPTS_ROOT}')]) 
  # load the builder to add cust_h and other ASM_DFLAGS and CC_DFLAGS
  env.LoadToolScript('apps_defs', toolpath = [env.subst('${BUILD_SCRIPTS_ROOT}')])
  # the builder to help sign the secure app image
  env.LoadToolScript('sectools_builder', toolpath = [env.subst('${BUILD_ROOT}/tools/build/scons/sectools')])
  # the builder to generate mbn image
  env.LoadToolScript('mbn_builder',toolpath=[env.subst('${BUILD_ROOT}/core/bsp/build/scripts/')])
  # the builder to get the objects and libraries the app depends on
  env.LoadToolScript('get_dependency_builder',toolpath=[env.subst('${BUILD_ROOT}/core/bsp/build/scripts/')])
  # the builder that generate the scatter loader file to generate an image map to be used by linker
  env.LoadToolScript('scl_builder',toolpath=[env.subst('${BUILD_ROOT}/core/bsp/build/scripts/')])
  # the builder that creates multiple files from the image file 
  env.LoadToolScript('pil_splitter_builder', toolpath = [env.subst('${BUILD_ROOT}/core/bsp/build/scripts')])
  # the builder that generates the metadata.c file and add privileges as defined in metadata dictionary
  env.LoadToolScript('secure_apps', toolpath=[env.subst('${BUILD_ROOT}/core/bsp/build/scripts/')])
  # the builder that affects formatting and also has buildspec rules for klocwork
  env.LoadToolScript('buildspec_builder', toolpath=[env.subst('${BUILD_ROOT}/tools/build/scons/scripts/')])

#============================================================================
# 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()

  if 'stack_size' in kwargs and kwargs['stack_size']:
    env['APP_STACK_SIZE'] = kwargs['stack_size']

  if 'heap_size' in kwargs and kwargs['heap_size']:
    env['APP_HEAP_SIZE'] = kwargs['heap_size']

  if 'sec_xml_config' in kwargs and kwargs['sec_xml_config']:
    env['SECIMAGE_XML_CONFIG'] = kwargs['sec_xml_config']

  env.Append(CPPPATH = includes)
  env.Append(CPPPATH = ['#../../core/api/services/',
                        '#../../core/securemsm/trustzone/qsee/mink/include/',
                        '#../../core/securemsm/secrsa/shared/inc',
                        '#../../core/api/securemsm/trustzone/qsee/',
                        '#../../core/securemsm/accesscontrol/api/',
                        '#../../core/securemsm/trustzone/qsee/include/',
                        '#../../core/securemsm/trustzone/qsapps/libs/applib/common_applib/inc',]
            )

  env.Replace(APP_IMAGE_NAME = image)
  env.Append(CFLAGS = '-fPIC')
  env.Append(CFLAGS = '-Werror')
  env.Append(LINKFLAGS = "-shared -Bsymbolic")

  #The app scons environment is expected to define the OUT_DIR path
  buildPath = env.subst('${OUT_DIR}/' + '${SHORT_BUILDPATH}/' + image)

  #Generate the metadata.c file 
  metadataObject = env.AddSecureAppMetadata(metadata)

  #compile sources to objects first, so we can control where the .o files end up at
  sobjects = []
  for s in sources:
    if isinstance(s, list):
      sflatten = []
      flatten_list(s, sflatten)
      for p in sflatten:
        objname = get_object_file_name(env,p)
        o = env.Object(buildPath + '/objects/' + objname, p)
        sobjects.append(o)
    else:
      objname = get_object_file_name(env, s)
      o = env.Object(buildPath + '/objects/' + objname , s)
      sobjects.append(o)

  # for ob in objects:
    # objname = get_object_file_name(env, ob)
    # sobjects.append(ob)

  #Get Dependencies
  libs, objs = env.GetDependency()
  sobjects.extend(objs)

  gp_app_list = ['gptest', 'gptest2', 'gpsample', 'ttaari1', 'ttacapi1', 'ttacapi2', 'ttacapi3', 'ttacapi4', 'ttacapi5',
                 'ttacrp1', 'ttads1', 'ttatcf1', 'ttatcf2', 'ttatcf3', 'ttatcf4', 'ttatcf5', 'ttatime1' ]
  #Add SCL file
  if image in gp_app_list:
    scl =  env.SclBuilder(buildPath,
                         '${BUILD_ROOT}/core/bsp/build/scripts/gpapp.ld')
  elif image == 'tbase':
    scl = env.SclBuilder(buildPath,
                         '${BUILD_ROOT}/core/securemsm/trustzone/qsapps/tbase/tbase310a/tbase/build/tbase.ld')
  elif image == 'teetest':
    scl = env.SclBuilder(buildPath, 
                         '${BUILD_ROOT}/core/securemsm/trustzone/qsapps/teetest/teetest/build/teetest.ld')
  else:
    if env['PROC'] == 'scorpion':
      scl = env.SclBuilder(buildPath,
                           '${BUILD_ROOT}/core/bsp/build/scripts/secureapp.ld')
    else:
      scl = env.SclBuilder(buildPath,
                           '${BUILD_ROOT}/core/bsp/build/scripts/secureapp64.ld')

  # get the libs for 32 bit or 64 bit based on PROC. scorpion => 32 bit
  if env['PROC'] == 'scorpion':
    libca = env.SubstRealPath('${MUSL32PATH}/lib/libc.a')
  else:
    libca= env.SubstRealPath('${MUSLPATH}/lib/libc.a')
  # only libc.a has a different lib for 32 bit vs 64 bit. The libclang_rt.builtins-arm.a
  # is common for both architectures.
  arm_libs = [
    env.File(libca),
    env.File(env.SubstRealPath('${LLVMLIB}/libclang_rt.builtins-arm.a')),
  ]

  libs.extend(arm_libs)
  if 'user_libs' in kwargs and kwargs['user_libs']:
    libs.extend(kwargs['user_libs']) 

  libs_path = env['INSTALL_LIBPATH']
  # ELF
  elf = env.Program( buildPath,
                     source=[sobjects, metadataObject],
                     LIBS=[libs], LIBPATH=libs_path
                   )
  env.Depends(elf, scl)

  image_map = env.subst(buildPath + '.map')
  image_sym = env.subst(buildPath + '.sym')

  env.Clean(elf, image_map)
  env.Clean(elf, image_sym)
  env.Clean(elf, metadataObject)
  
  pbn = env.InstallAs(buildPath + ".pbn", elf)
  env.Depends(pbn,elf)

  install_root = env.subst('${MBN_ROOT}')
  install_unsigned_root = env.SectoolGetUnsignedInstallPath(install_base_dir = install_root)
  env.Replace(MBN_FILE = os.path.join(install_unsigned_root, image))

  mbn = env.MbnBuilder( buildPath,
                        pbn, IMAGE_TYPE=image, MBN_TYPE="elf",
                        IMAGE_ID=4, FLASH_TYPE="sdcc")

  env.Depends(mbn, pbn)
  #----------------------------------------------------------------------------
  # Sectools signing
  #----------------------------------------------------------------------------
  default_config = env.subst('${BUILD_ROOT}/apps/bsp/trustzone/qsapps/build/secimage.xml')
  secimage_xml_config = env.get('SECIMAGE_XML_CONFIG', default_config)
  
  pil_base_dir = '${BUILD_ROOT}/build/ms/bin/PIL_IMAGES/SPLITBINS_${QC_SHORT_BUILDPATH}'
#  sectools_signed_mbn = env.SectoolBuilder(
#                            target_base_dir = '${OUT_DIR}/' + '${SHORT_BUILDPATH}',
#                            source = mbn,
#                            sign_id = sign_id,
#                            app_id = app_id,
#                            msmid = env.subst('${MSM_ID}'),
#                            sectools_install_base_dir = install_root,
#                            install_file_name = image + ".mbn",
#                            config = secimage_xml_config,
#                            pilsplitter_target_base_dir = pil_base_dir,
#                            soc_hw_version = soc_hw_version,
#                            soc_vers = soc_vers
#                        )

  sectools_signed_mbn = env.Command('${BUILD_ROOT}/build/ms/bin/${QC_SHORT_BUILDPATH}/unsigned/signed_mldap.mbn','${BUILD_ROOT}/build/ms/bin/${QC_SHORT_BUILDPATH}/unsigned/mldap.mbn',\
                     "java -jar ${BUILD_ROOT}/../common/build/signclient.jar -model INPUT_MODEL_NAME -runtype qc_secimg30_mldap -input $SOURCE -output $TARGET")

  env.Depends(sectools_signed_mbn, mbn)
  #-------------------------------------------------------------------------
  # Build files for PIL driver
  #-------------------------------------------------------------------------
  unsigned_pil_dir = env.SectoolGetUnsignedInstallPath(install_base_dir = pil_base_dir)
  pil = env.PilSplitterBuilder(os.path.join(pil_base_dir, image + '.mdt'), sectools_signed_mbn)
  env.Depends(pil, sectools_signed_mbn)

  return pil

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
  elif isinstance(spath, list):
    print 'Error: get_object_file_name does not process list!! %s' % (str(spath))
    exit(1)
  else:
    spath_str = spath.path
    sstr = spath_str.split(env.subst('${SRC_SCONS_ROOT}'))[-1]
    if sstr == spath_str :
      # scons root is not in the path, so try to split at image
      # name if it is there in the path to get relative paths
      if 'APP_IMAGE_NAME' in env and \
           env['APP_IMAGE_NAME'] and \
           env.subst('${APP_IMAGE_NAME}') in spath_str:
        sstr = spath_str.split(env.subst('${APP_IMAGE_NAME}'))[-1]
      else:
        # no easy way to split the path to get relative source paths
        # break down at the BUILD_ROOT, so objects are stored with relative
        # paths from root under the objects directory in output
        sstr = spath_str.split(env.subst('${BUILD_ROOT}'))[-1]
  return sstr.split('../')[-1].split('.')[0]




def secure_app_lib_builder(env, includes, sources, libname):
  env.Replace(LIBNAME = libname.upper())
  env.Append(CFLAGS = '-fPIC')
  env.Append(CPPPATH = includes)

  install_dir = env.subst('${LIB_OUT_DIR}')
  objlist = []
  for s in sources:
    if isinstance(s, list):
      sflatten = []
      flatten_list(s, sflatten)
      for p in sflatten:
        objname = get_object_file_name(env,p)
        o = env.Object(install_dir + '/objects/' + objname, p)
        objlist.append(o)
    else:
      objname = get_object_file_name(env, s)
      o = env.Object(install_dir + '/objects/' + objname , s)
      objlist.append(o)

  lib = env.Library(install_dir + '/' + libname, objlist)
  return lib

def flatten_list (origlist, newlist):
  for i in origlist:
    if isinstance(i, str):
      newlist.append(i)
    else:
      flatten_list(i, newlist)
    
   
  
