2014-07-02 01:57:19 +08:00
|
|
|
""" Python SWIG wrapper creation script Windows/LINUX/OSX platform
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
File: buildSwigPython.py
|
|
|
|
|
|
|
|
Overview: Creates SWIG Python C++ Script Bridge wrapper code. This
|
|
|
|
script is called by build-swig-wrapper-classes.py in turn.
|
|
|
|
|
|
|
|
Environment: OS: Windows Vista or newer, LINUX, OSX.
|
|
|
|
IDE: Visual Studio 2013 Plugin Python Tools (PTVS)
|
|
|
|
Script: Python 2.6/2.7.5 x64
|
|
|
|
Other: SWIG 2.0.11
|
|
|
|
|
|
|
|
Gotchas: Python debug complied pythonXX_d.lib is required for SWIG
|
|
|
|
to build correct LLDBWrapperPython.cpp in order for Visual
|
|
|
|
Studio to compile successfully. The release version of the
|
|
|
|
Python lib will not work.
|
|
|
|
LLDB (dir) CMakeLists.txt uses windows environmental
|
|
|
|
variables $PYTHON_INCLUDE and $PYTHON_LIB to locate
|
|
|
|
Python files required for the build.
|
|
|
|
|
|
|
|
Copyright: None.
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Python modules:
|
|
|
|
import os # Provide directory and file handling, determine OS information
|
2014-08-02 15:11:22 +08:00
|
|
|
import sys # sys.executable
|
2014-07-02 01:57:19 +08:00
|
|
|
import time # Time access and conversions
|
|
|
|
import subprocess # Call external programs
|
|
|
|
import shutil # File handling
|
|
|
|
|
|
|
|
# Third party modules:
|
|
|
|
|
|
|
|
# In-house modules:
|
|
|
|
import utilsOsType # Determine the OS type this script is running on
|
|
|
|
import utilsDebug # Debug Python scripts
|
|
|
|
|
|
|
|
# User facing text:
|
|
|
|
strMsgLldbDisablePythonEnv = "Python build aborted as LLDB_DISABLE_PYTHON \
|
|
|
|
environmental variable is defined";
|
|
|
|
strMsgLldbDisableGccEnv = "Python build aborted as GCC_PREPROCESSOR_DEFINITIONS \
|
|
|
|
environmental variable is defined";
|
|
|
|
strMsgHdrFiles = "Header files are:";
|
|
|
|
strMsgIFaceFiles = "SWIG interface files are";
|
|
|
|
strErrMsgProgFail = "Program failure: ";
|
|
|
|
strMsgFileNewrSwigOpFile = "\'%s\' is newer than \'%s\'\nSWIG file will need to be re-built";
|
|
|
|
strMsgFileNotExist = "\'%s\' could not be found\nSWIG file will need to be re-built";
|
|
|
|
strErrMsgOsTypeUnknown = "Unable to determine current OS type";
|
|
|
|
strMsgNotNeedUpdate = "Everything is up-to-date";
|
|
|
|
strMsgSwigNeedRebuild = "SWIG needs to be re-run";
|
|
|
|
strErrMsgSwigParamsMissing = "This script was not passed either '--swigExePath \
|
|
|
|
or the '--swigExeName' argument. Both are required.";
|
|
|
|
strMsgSwigExecute = "SWIG executing the following:\n\'%s'";
|
|
|
|
strErrMsgSwigExecute = "SWIG failed: %s";
|
|
|
|
strErrMsgPythonExecute = "Python script '%s' failed: %s";
|
|
|
|
strMsgSwigNoGenDep = "SWIG ran with no generated dependencies, script exit early";
|
|
|
|
strMsgSwigGenDep = "SWIG ran and generated dependencies, script exit early, deleted '%s'";
|
|
|
|
strErrMsgFrameWkPyDirNotExist = "Unable to find the LLDB. Framework directory is '%s'";
|
|
|
|
strMsgFoundLldbFrameWkDir = "Found '%s'";
|
|
|
|
strErrMsgModifyPythonLldbPyFileNotFound = "Unable to find '%s' in '%s'";
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the list of hard coded lldb header file names and
|
|
|
|
# put in the program's dictArgs map container. Make paths compatible
|
|
|
|
# with the current OS.
|
|
|
|
# Note this does not necessarily match the content of those
|
|
|
|
# directories. The resultant header string is inserted into the
|
|
|
|
# dictionary vDictArgs key "--headerFiles".
|
|
|
|
# Args: vDictArgs - (RW) Program input parameters.
|
|
|
|
# Returns: Bool - True = success, False = failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_header_files( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_header_files()" );
|
|
|
|
listHeaderFiles = [ "/include/lldb/API/lldb.h", # .sh has /include/lldb/lldb.h 21/11/2013
|
|
|
|
"/include/lldb/lldb-defines.h",
|
|
|
|
"/include/lldb/lldb-enumerations.h",
|
|
|
|
"/include/lldb/lldb-forward.h",
|
|
|
|
"/include/lldb/lldb-forward-rtti.h",
|
|
|
|
"/include/lldb/lldb-types.h",
|
|
|
|
"/include/lldb/API/SBAddress.h",
|
|
|
|
"/include/lldb/API/SBBlock.h",
|
|
|
|
"/include/lldb/API/SBBreakpoint.h",
|
|
|
|
"/include/lldb/API/SBBreakpointLocation.h",
|
|
|
|
"/include/lldb/API/SBBroadcaster.h",
|
|
|
|
"/include/lldb/API/SBCommandInterpreter.h",
|
|
|
|
"/include/lldb/API/SBCommandReturnObject.h",
|
|
|
|
"/include/lldb/API/SBCommunication.h",
|
|
|
|
"/include/lldb/API/SBCompileUnit.h",
|
|
|
|
"/include/lldb/API/SBData.h",
|
|
|
|
"/include/lldb/API/SBDebugger.h",
|
|
|
|
"/include/lldb/API/SBError.h",
|
|
|
|
"/include/lldb/API/SBEvent.h",
|
|
|
|
"/include/lldb/API/SBExpressionOptions.h",
|
|
|
|
"/include/lldb/API/SBFileSpec.h",
|
|
|
|
"/include/lldb/API/SBFrame.h",
|
|
|
|
"/include/lldb/API/SBFunction.h",
|
|
|
|
"/include/lldb/API/SBHostOS.h",
|
|
|
|
"/include/lldb/API/SBInputReader.h",
|
|
|
|
"/include/lldb/API/SBInstruction.h",
|
|
|
|
"/include/lldb/API/SBInstructionList.h",
|
|
|
|
"/include/lldb/API/SBLineEntry.h",
|
|
|
|
"/include/lldb/API/SBListener.h",
|
|
|
|
"/include/lldb/API/SBModule.h",
|
|
|
|
"/include/lldb/API/SBModuleSpec.h",
|
|
|
|
"/include/lldb/API/SBProcess.h",
|
|
|
|
"/include/lldb/API/SBSourceManager.h",
|
|
|
|
"/include/lldb/API/SBStream.h",
|
|
|
|
"/include/lldb/API/SBStringList.h",
|
|
|
|
"/include/lldb/API/SBSymbol.h",
|
|
|
|
"/include/lldb/API/SBSymbolContext.h",
|
|
|
|
"/include/lldb/API/SBSymbolContextList.h",
|
|
|
|
"/include/lldb/API/SBTarget.h",
|
|
|
|
"/include/lldb/API/SBThread.h",
|
2014-09-06 09:21:19 +08:00
|
|
|
"/include/lldb/API/SBThreadCollection.h",
|
2014-07-02 01:57:19 +08:00
|
|
|
"/include/lldb/API/SBType.h",
|
|
|
|
"/include/lldb/API/SBTypeCategory.h",
|
|
|
|
"/include/lldb/API/SBTypeFilter.h",
|
|
|
|
"/include/lldb/API/SBTypeFormat.h",
|
|
|
|
"/include/lldb/API/SBTypeNameSpecifier.h",
|
|
|
|
"/include/lldb/API/SBTypeSummary.h",
|
|
|
|
"/include/lldb/API/SBTypeSynthetic.h",
|
|
|
|
"/include/lldb/API/SBValue.h",
|
|
|
|
"/include/lldb/API/SBValueList.h",
|
|
|
|
"/include/lldb/API/SBWatchpoint.h" ];
|
|
|
|
bDebug = vDictArgs.has_key( "-d" );
|
|
|
|
strRt = vDictArgs[ "--srcRoot" ];
|
|
|
|
strRt = os.path.normcase( strRt );
|
|
|
|
|
|
|
|
strHeaderFiles = "";
|
|
|
|
for strHdr in listHeaderFiles[ 0: len( listHeaderFiles ) ]:
|
|
|
|
strHdr = os.path.normcase( strHdr );
|
|
|
|
strHeaderFiles += " %s%s" % (strRt, strHdr);
|
|
|
|
|
|
|
|
if bDebug:
|
|
|
|
print strMsgHdrFiles;
|
|
|
|
print strHeaderFiles;
|
|
|
|
|
|
|
|
vDictArgs[ "--headerFiles" ] = strHeaderFiles;
|
|
|
|
|
|
|
|
return True;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the list of hard coded lldb SWIG interface file names and
|
|
|
|
# put in the program's dictArgs map container. Make paths compatible
|
|
|
|
# with the current OS.
|
|
|
|
# Note this does not necessarily match the content of those
|
|
|
|
# directories. The resultant interface string is inserted into the
|
|
|
|
# dictionary vDictArgs key "--ifaceFiles".
|
|
|
|
# Args: vDictArgs - (RW) Program input parameters.
|
|
|
|
# Returns: Bool - True = success, False = failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_interface_files( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_interface_files()" );
|
|
|
|
listIFaceFiles = [ "/scripts/Python/interface/SBAddress.i",
|
|
|
|
"/scripts/Python/interface/SBBlock.i",
|
|
|
|
"/scripts/Python/interface/SBBreakpoint.i",
|
|
|
|
"/scripts/Python/interface/SBBreakpointLocation.i",
|
|
|
|
"/scripts/Python/interface/SBBroadcaster.i",
|
|
|
|
"/scripts/Python/interface/SBCommandInterpreter.i",
|
|
|
|
"/scripts/Python/interface/SBCommandReturnObject.i",
|
|
|
|
"/scripts/Python/interface/SBCommunication.i",
|
|
|
|
"/scripts/Python/interface/SBCompileUnit.i",
|
|
|
|
"/scripts/Python/interface/SBData.i",
|
|
|
|
"/scripts/Python/interface/SBDebugger.i",
|
|
|
|
"/scripts/Python/interface/SBDeclaration.i",
|
|
|
|
"/scripts/Python/interface/SBError.i",
|
|
|
|
"/scripts/Python/interface/SBEvent.i",
|
|
|
|
"/scripts/Python/interface/SBExpressionOptions.i",
|
|
|
|
"/scripts/Python/interface/SBFileSpec.i",
|
|
|
|
"/scripts/Python/interface/SBFrame.i",
|
|
|
|
"/scripts/Python/interface/SBFunction.i",
|
|
|
|
"/scripts/Python/interface/SBHostOS.i",
|
|
|
|
"/scripts/Python/interface/SBInputReader.i",
|
|
|
|
"/scripts/Python/interface/SBInstruction.i",
|
|
|
|
"/scripts/Python/interface/SBInstructionList.i",
|
|
|
|
"/scripts/Python/interface/SBLineEntry.i",
|
|
|
|
"/scripts/Python/interface/SBListener.i",
|
|
|
|
"/scripts/Python/interface/SBModule.i",
|
|
|
|
"/scripts/Python/interface/SBModuleSpec.i",
|
|
|
|
"/scripts/Python/interface/SBProcess.i",
|
|
|
|
"/scripts/Python/interface/SBSourceManager.i",
|
|
|
|
"/scripts/Python/interface/SBStream.i",
|
|
|
|
"/scripts/Python/interface/SBStringList.i",
|
|
|
|
"/scripts/Python/interface/SBSymbol.i",
|
|
|
|
"/scripts/Python/interface/SBSymbolContext.i",
|
|
|
|
"/scripts/Python/interface/SBTarget.i",
|
|
|
|
"/scripts/Python/interface/SBThread.i",
|
2014-09-06 09:21:19 +08:00
|
|
|
"/scripts/Python/interface/SBThreadCollection.i",
|
2014-07-02 01:57:19 +08:00
|
|
|
"/scripts/Python/interface/SBType.i",
|
|
|
|
"/scripts/Python/interface/SBTypeCategory.i",
|
|
|
|
"/scripts/Python/interface/SBTypeFilter.i",
|
|
|
|
"/scripts/Python/interface/SBTypeFormat.i",
|
|
|
|
"/scripts/Python/interface/SBTypeNameSpecifier.i",
|
|
|
|
"/scripts/Python/interface/SBTypeSummary.i",
|
|
|
|
"/scripts/Python/interface/SBTypeSynthetic.i",
|
|
|
|
"/scripts/Python/interface/SBValue.i",
|
|
|
|
"/scripts/Python/interface/SBValueList.i",
|
|
|
|
"/scripts/Python/interface/SBWatchpoint.i" ];
|
|
|
|
bDebug = vDictArgs.has_key( "-d" );
|
|
|
|
strRt = vDictArgs[ "--srcRoot" ];
|
|
|
|
strRt = os.path.normcase( strRt );
|
|
|
|
|
|
|
|
strInterfaceFiles = "";
|
|
|
|
for strIFace in listIFaceFiles[ 0: len( listIFaceFiles ) ]:
|
|
|
|
strIFace = os.path.normcase( strIFace );
|
|
|
|
strInterfaceFiles += " %s%s" % (strRt, strIFace);
|
|
|
|
|
|
|
|
if bDebug:
|
|
|
|
print strMsgIFaceFiles;
|
|
|
|
print strInterfaceFiles;
|
|
|
|
|
|
|
|
vDictArgs[ "--ifaceFiles" ] = strInterfaceFiles;
|
|
|
|
|
|
|
|
return True;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Compare which file is newer.
|
|
|
|
# Args: vFile1 - (R) File name path.
|
|
|
|
# vFile2 - (R) File name path.
|
|
|
|
# Returns: Int - 0 = both not exist, 1 = file 1 newer, 2 = file 2 newer,
|
|
|
|
# 3 = file 1 not exist.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def which_file_is_newer( vFile1, vFile2 ):
|
|
|
|
bF1 = os.path.exists( vFile1 );
|
|
|
|
bF2 = os.path.exists( vFile2 );
|
|
|
|
if bF1 == False and bF2 == False:
|
|
|
|
return 0; # Both files not exist
|
|
|
|
if bF1 == False:
|
|
|
|
return 3; # File 1 not exist
|
|
|
|
if bF2 == False:
|
|
|
|
return 1; # File 1 is newer / file 2 not exist
|
|
|
|
f1Stamp = os.path.getmtime( vFile1 );
|
|
|
|
f2Stamp = os.path.getmtime( vFile2 );
|
|
|
|
if f1Stamp > f2Stamp:
|
|
|
|
return 1; # File 1 is newer
|
|
|
|
|
|
|
|
return 2; # File 2 is newer than file 1
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Determine whether the specified file exists.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrFileNamePath - (R) Check this file exists.
|
|
|
|
# Returns: Bool - True = Files exists, false = not found.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def check_file_exists( vDictArgs, vstrFileNamePath ):
|
|
|
|
bExists = False;
|
|
|
|
bDebug = vDictArgs.has_key( "-d" );
|
|
|
|
|
|
|
|
if os.path.exists( vstrFileNamePath ):
|
|
|
|
bExists = True;
|
|
|
|
elif bDebug:
|
|
|
|
print strMsgFileNotExist % vstrFileNamePath;
|
|
|
|
|
|
|
|
return bExists;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Determine whether the specified file is newer than the
|
|
|
|
# LLDBWrapPython.cpp file.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrSwigOpFileNamePath - (R) LLDBWrapPython.cpp file.
|
|
|
|
# vstrFileNamePath - (R) Specific file.
|
|
|
|
# Returns: Bool - True = SWIG update required, false = no update required.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def check_newer_file( vDictArgs, vstrSwigOpFileNamePath, vstrFileNamePath ):
|
|
|
|
bNeedUpdate = False;
|
|
|
|
bDebug = vDictArgs.has_key( "-d" );
|
|
|
|
|
|
|
|
strMsg = "";
|
|
|
|
nResult = which_file_is_newer( vstrFileNamePath, vstrSwigOpFileNamePath );
|
|
|
|
if nResult == 1:
|
|
|
|
strMsg = strMsgFileNewrSwigOpFile % (vstrFileNamePath,
|
|
|
|
vstrSwigOpFileNamePath);
|
|
|
|
bNeedUpdate = True;
|
|
|
|
elif nResult == 3:
|
|
|
|
strMsg = strMsgFileNotExist % vstrFileNamePath;
|
|
|
|
bNeedUpdate = True;
|
|
|
|
|
|
|
|
if bNeedUpdate and bDebug:
|
|
|
|
print strMsg;
|
|
|
|
|
|
|
|
return bNeedUpdate;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Determine whether the any files in the list are newer than the
|
|
|
|
# LLDBWrapPython.cpp file.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrSwigOpFileNamePath - (R) LLDBWrapPython.cpp file.
|
|
|
|
# vstrFiles - (R) Multi string file names ' ' delimiter.
|
|
|
|
# Returns: Bool - True = SWIG update required, false = no update required.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def check_newer_files( vDictArgs, vstrSwigOpFileNamePath, vstrFiles ):
|
|
|
|
bNeedUpdate = False;
|
|
|
|
|
|
|
|
listFiles = vstrFiles.split();
|
|
|
|
for strFile in listFiles:
|
|
|
|
if check_newer_file( vDictArgs, vstrSwigOpFileNamePath, strFile ):
|
|
|
|
bNeedUpdate = True;
|
|
|
|
break;
|
|
|
|
|
|
|
|
return bNeedUpdate;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the directory path for Python's dist_packages/
|
|
|
|
# site_package folder on a Windows platform.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# Str - Python Framework directory path.
|
|
|
|
# strErrMsg - Error description on task failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_framework_python_dir_windows( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_framework_python_dir_windows()" );
|
|
|
|
bOk = True;
|
|
|
|
strWkDir = "";
|
|
|
|
strErrMsg = "";
|
|
|
|
|
|
|
|
# We are being built by LLVM, so use the PYTHON_INSTALL_DIR argument,
|
|
|
|
# and append the python version directory to the end of it. Depending
|
|
|
|
# on the system other stuff may need to be put here as well.
|
|
|
|
from distutils.sysconfig import get_python_lib;
|
|
|
|
strPythonInstallDir = "";
|
|
|
|
bHaveArgPrefix = vDictArgs.has_key( "--prefix" );
|
|
|
|
if bHaveArgPrefix:
|
|
|
|
strPythonInstallDir = vDictArgs[ "--prefix" ];
|
|
|
|
if strPythonInstallDir.__len__() != 0:
|
|
|
|
strWkDir = get_python_lib( True, False, strPythonInstallDir );
|
|
|
|
else:
|
|
|
|
strWkDir = get_python_lib( True, False );
|
|
|
|
strWkDir += "/lldb";
|
|
|
|
strWkDir = os.path.normcase( strWkDir );
|
|
|
|
|
|
|
|
return (bOk, strWkDir, strErrMsg);
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the directory path for Python's dist_packages/
|
|
|
|
# site_package folder on a UNIX style platform.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# Str - Python Framework directory path.
|
|
|
|
# strErrMsg - Error description on task failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_framework_python_dir_other_platforms( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_framework_python_dir_other_platform()" );
|
|
|
|
bOk = True;
|
|
|
|
strWkDir = "";
|
|
|
|
strErrMsg = "";
|
|
|
|
bDbg = vDictArgs.has_key( "-d" );
|
|
|
|
|
|
|
|
bMakeFileCalled = vDictArgs.has_key( "-m" );
|
|
|
|
if bMakeFileCalled:
|
|
|
|
dbg.dump_text( "Built by LLVM" );
|
|
|
|
return get_framework_python_dir_windows( vDictArgs );
|
|
|
|
else:
|
|
|
|
dbg.dump_text( "Built by XCode" );
|
|
|
|
# We are being built by XCode, so all the lldb Python files can go
|
|
|
|
# into the LLDB.framework/Resources/Python subdirectory.
|
|
|
|
strWkDir = vDictArgs[ "--targetDir" ];
|
|
|
|
strWkDir += "/LLDB.framework";
|
|
|
|
if os.path.exists( strWkDir ):
|
|
|
|
if bDbg:
|
|
|
|
print strMsgFoundLldbFrameWkDir % strWkDir;
|
|
|
|
strWkDir += "/Resources/Python/lldb";
|
|
|
|
strWkDir = os.path.normcase( strWkDir );
|
|
|
|
else:
|
|
|
|
bOk = False;
|
|
|
|
strErrMsg = strErrMsgFrameWkPyDirNotExist % strWkDir;
|
|
|
|
|
|
|
|
return (bOk, strWkDir, strErrMsg);
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the directory path for Python's dist_packages/
|
|
|
|
# site_package folder depending on the type of OS platform being
|
|
|
|
# used.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# Str - Python Framework directory path.
|
|
|
|
# strErrMsg - Error description on task failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_framework_python_dir( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_framework_python_dir()" );
|
|
|
|
bOk = True;
|
|
|
|
strWkDir = "";
|
|
|
|
strErrMsg = "";
|
|
|
|
|
|
|
|
eOSType = utilsOsType.determine_os_type();
|
|
|
|
if eOSType == utilsOsType.EnumOsType.Unknown:
|
|
|
|
bOk = False;
|
|
|
|
strErrMsg = strErrMsgOsTypeUnknown;
|
|
|
|
elif eOSType == utilsOsType.EnumOsType.Windows:
|
|
|
|
bOk, strWkDir, strErrMsg = get_framework_python_dir_windows( vDictArgs );
|
|
|
|
else:
|
|
|
|
bOk, strWkDir, strErrMsg = get_framework_python_dir_other_platforms( vDictArgs );
|
|
|
|
|
|
|
|
return (bOk, strWkDir, strErrMsg);
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Retrieve the configuration build path if present and valid (using
|
|
|
|
# parameter --cfgBlddir or copy the Python Framework directory.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrFrameworkPythonDir - (R) Python framework directory.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# Str - Config directory path.
|
|
|
|
# strErrMsg - Error description on task failure.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def get_config_build_dir( vDictArgs, vstrFrameworkPythonDir ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script get_config_build_dir()" );
|
|
|
|
bOk = True;
|
|
|
|
strErrMsg = "";
|
|
|
|
|
|
|
|
strConfigBldDir = "";
|
|
|
|
bHaveConfigBldDir = vDictArgs.has_key( "--cfgBldDir" );
|
|
|
|
if bHaveConfigBldDir:
|
|
|
|
strConfigBldDir = vDictArgs[ "--cfgBldDir" ];
|
|
|
|
if (bHaveConfigBldDir == False) or (strConfigBldDir.__len__() == 0):
|
|
|
|
strConfigBldDir = vstrFrameworkPythonDir;
|
|
|
|
|
|
|
|
return (bOk, strConfigBldDir, strErrMsg);
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Do a SWIG code rebuild. Any number returned by SWIG which is not
|
|
|
|
# zero is treated as an error. The generate dependencies flag decides
|
|
|
|
# how SWIG is rebuilt and if set false will cause the script to exit
|
|
|
|
# immediately with the exit status + 200 if status is not zero.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrSwigDepFile - (R) SWIG dependency file.
|
|
|
|
# vstrCfgBldDir - (R) Configuration build directory.
|
|
|
|
# vstrSwigOpFile - (R) SWIG output file.
|
|
|
|
# vstrSwigIpFile - (R) SWIG input file.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# strMsg - Error or status message.
|
|
|
|
# nExitResult - Exit result of SWIG executable.
|
|
|
|
# - 0 = Success.
|
|
|
|
# - 1 = Success, exit this script and parent script.
|
|
|
|
# - +200 = A SWIG error status result.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def do_swig_rebuild( vDictArgs, vstrSwigDepFile, vstrCfgBldDir,
|
|
|
|
vstrSwigOpFile, vstrSwigIpFile ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script do_swig_rebuild()" );
|
|
|
|
bOk = True;
|
|
|
|
strMsg = "";
|
|
|
|
bDbg = vDictArgs.has_key( "-d" );
|
|
|
|
bGenDependencies = vDictArgs.has_key( "-M" );
|
|
|
|
strSwigExePath = vDictArgs[ "--swigExePath" ];
|
|
|
|
strSwigExeName = vDictArgs[ "--swigExeName" ];
|
|
|
|
strSrcRoot = vDictArgs[ "--srcRoot" ];
|
|
|
|
|
|
|
|
# Build SWIG path to executable
|
|
|
|
if strSwigExePath != "":
|
|
|
|
strSwig = "%s/%s" % (strSwigExePath, strSwigExeName);
|
|
|
|
strSwig = os.path.normcase( strSwig );
|
|
|
|
else:
|
|
|
|
strSwig = strSwigExeName;
|
|
|
|
|
|
|
|
strCfg = vstrCfgBldDir;
|
|
|
|
strOp = vstrSwigOpFile;
|
|
|
|
strIp = vstrSwigIpFile;
|
|
|
|
strSi = os.path.normcase( "./." );
|
|
|
|
strRoot = strSrcRoot + "/include";
|
|
|
|
strRoot = os.path.normcase( strRoot );
|
|
|
|
strDep = "";
|
|
|
|
if bGenDependencies:
|
|
|
|
strDep = vstrSwigDepFile + ".tmp";
|
|
|
|
|
|
|
|
# Build the SWIG args list
|
|
|
|
strCmd = "%s " % strSwig;
|
|
|
|
strCmd += "-c++ ";
|
|
|
|
strCmd += "-shadow ";
|
|
|
|
strCmd += "-python ";
|
|
|
|
strCmd += "-threads ";
|
|
|
|
strCmd += "-I\"%s\" " % strRoot;
|
|
|
|
strCmd += "-I\"%s\" " % strSi;
|
|
|
|
strCmd += "-D__STDC_LIMIT_MACROS ";
|
|
|
|
strCmd += "-D__STDC_CONSTANT_MACROS ";
|
|
|
|
if bGenDependencies:
|
|
|
|
strCmd += "-MMD -MF \"%s\" " % strDep;
|
|
|
|
strCmd += "-outdir \"%s\" " % strCfg;
|
|
|
|
strCmd += "-o \"%s\" " % strOp;
|
|
|
|
strCmd += "\"%s\" " % strIp;
|
|
|
|
if bDbg:
|
|
|
|
print strMsgSwigExecute % strCmd;
|
|
|
|
|
|
|
|
# Execute SWIG
|
|
|
|
process = subprocess.Popen( strCmd, stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.PIPE, shell=True );
|
|
|
|
# Wait for SWIG process to terminate
|
|
|
|
strStdOut, strStdErr = process.communicate();
|
|
|
|
nResult = process.returncode;
|
|
|
|
if nResult != 0:
|
|
|
|
bOk = False;
|
|
|
|
nResult += 200;
|
|
|
|
strMsg = strErrMsgSwigExecute % strStdErr;
|
|
|
|
else:
|
|
|
|
if bDbg and (strStdOut.__len__() != 0):
|
|
|
|
strMsg = strStdOut;
|
|
|
|
|
|
|
|
if bGenDependencies:
|
|
|
|
if bOk:
|
|
|
|
if os.path.exists( strDep ):
|
|
|
|
shutil.move( strDep, vstrSwigDepFile );
|
|
|
|
else:
|
|
|
|
os.remove( strDep );
|
|
|
|
nResult = 1; # Exit this script and parent script
|
|
|
|
if bDbg:
|
|
|
|
strMsg = strMsgSwigGenDep % strDep;
|
|
|
|
else:
|
|
|
|
strMsg = strMsgSwigNoGenDep + strMsg;
|
|
|
|
|
|
|
|
return bOk, strMsg, nResult;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Execute another Python script from this script in a separate
|
|
|
|
# process. No data is passed back to the caller script. It is
|
|
|
|
# assumed should any exit result be returned that -ve numbers are
|
|
|
|
# error conditions. A zero or +ve numbers mean ok/warning/status.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrArgs - (R) Space separated parameters passed to python.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# strMsg - Error or status message.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def run_python_script( vDictArgs, vstrArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script run_python_script()" );
|
|
|
|
bOk = True;
|
|
|
|
strMsg = "";
|
|
|
|
bDbg = vDictArgs.has_key( "-d" );
|
|
|
|
|
2014-08-02 15:11:22 +08:00
|
|
|
strPy = "%s %s" % (sys.executable, vstrArgs);
|
2014-07-02 01:57:19 +08:00
|
|
|
process = subprocess.Popen( strPy, shell=True );
|
|
|
|
strStdOut, strStdErr = process.communicate();
|
|
|
|
nResult = process.returncode;
|
|
|
|
if nResult < 0:
|
|
|
|
bOk = False;
|
|
|
|
strErr = strStdErr;
|
|
|
|
if strErr == None:
|
|
|
|
strErr = "No error given";
|
|
|
|
strMsg = strErrMsgPythonExecute % (vstrArgs, strErr);
|
|
|
|
else:
|
|
|
|
if bDbg:
|
|
|
|
strOut = strStdOut;
|
|
|
|
if strOut == None:
|
|
|
|
strOut = "No status given";
|
|
|
|
strMsg = strOut;
|
|
|
|
|
|
|
|
return bOk, strMsg;
|
|
|
|
|
|
|
|
#++---------------------------------------------------------------------------
|
|
|
|
# Details: Implement the iterator protocol and/or eq/ne operators for some
|
|
|
|
# lldb objects.
|
|
|
|
# Append global variable to lldb Python module.
|
|
|
|
# And initialize the lldb debugger subsystem.
|
|
|
|
# Args: vDictArgs - (R) Program input parameters.
|
|
|
|
# vstrCfgBldDir - (R) Configuration build directory.
|
|
|
|
# Returns: Bool - True = function success, False = failure.
|
|
|
|
# strMsg - Error or status message.
|
|
|
|
# Throws: None.
|
|
|
|
#--
|
|
|
|
def do_modify_python_lldb( vDictArgs, vstrCfgBldDir ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script do_modify_python_lldb()" );
|
|
|
|
bOk = True;
|
|
|
|
strMsg = "";
|
|
|
|
bDbg = vDictArgs.has_key( "-d" );
|
|
|
|
strCwd = vDictArgs[ "--srcRoot" ]; # /llvm/tools/lldb
|
|
|
|
strCwd += "/scripts/Python";
|
|
|
|
strPyScript = "modify-python-lldb.py";
|
|
|
|
strPath = "%s/%s" % (strCwd, strPyScript);
|
|
|
|
strPath = os.path.normcase( strPath );
|
|
|
|
|
|
|
|
bOk = os.path.exists( strPath );
|
|
|
|
if not bOk:
|
|
|
|
strMsg = strErrMsgModifyPythonLldbPyFileNotFound % (strPyScript, strPath);
|
|
|
|
return bOk, strMsg;
|
|
|
|
|
|
|
|
strPyArgs = "%s %s" % (strPath, vstrCfgBldDir);
|
|
|
|
bOk, strMsg = run_python_script( vDictArgs, strPyArgs );
|
|
|
|
|
|
|
|
return bOk, strMsg;
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
""" Details: Program main entry point fn. Called by another Python script.
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
Details: This script is to be called by another Python script. It is not
|
|
|
|
intended to be called directly i.e from the command line.
|
|
|
|
If environmental variable "LLDB_DISABLE_PYTHON" is defined/exists
|
|
|
|
it will cause the script to end early creating nothing.
|
|
|
|
If environmental variable "GCC_PREPROCESSOR_DEFINITIONS" is
|
|
|
|
defined/exists it will cause the script to end early creating
|
|
|
|
nothing.
|
|
|
|
Args: vDictArgs - (R) Map of parameter names to values. Used to for the
|
|
|
|
the SWIG required parameters to create code. Note
|
|
|
|
this container does get amended with more data.
|
|
|
|
-d (optional) Determines whether or not this script
|
|
|
|
outputs additional information when running.
|
|
|
|
-m (optional) Specify called from Makefile system. If given locate
|
|
|
|
the LLDBWrapPython.cpp in --srcRoot/source folder
|
|
|
|
else in the --targetDir folder.
|
|
|
|
-M (optional) Specify want SWIG to generate a dependency file.
|
|
|
|
--srcRoot The root of the lldb source tree.
|
|
|
|
--targetDir Where the lldb framework/shared library gets put.
|
|
|
|
--cfgBldDir Where the buildSwigPythonLLDB.py program will
|
|
|
|
(optional) put the lldb.py file it generated from running
|
|
|
|
SWIG.
|
|
|
|
--prefix Is the root directory used to determine where
|
|
|
|
(optional) third-party modules for scripting languages should
|
|
|
|
be installed. Where non-Darwin systems want to put
|
|
|
|
the .py and .so files so that Python can find them
|
|
|
|
automatically. Python install directory.
|
|
|
|
--swigExePath File path the SWIG executable. (Determined and
|
|
|
|
passed by buildSwigWrapperClasses.py to here)
|
|
|
|
--swigExeName The file name of the SWIG executable. (Determined
|
|
|
|
and passed by buildSwigWrapperClasses.py to
|
|
|
|
here)
|
|
|
|
Results: 0 Success
|
|
|
|
1 Success, generated dependencies removed
|
|
|
|
LLDBWrapPython.cpp.d.
|
|
|
|
-100+ Error from this script to the caller script.
|
|
|
|
-100 Error program failure with optional message.
|
|
|
|
-200+ - 200 +- the SWIG exit result.
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------
|
|
|
|
|
|
|
|
"""
|
|
|
|
def main( vDictArgs ):
|
|
|
|
dbg = utilsDebug.CDebugFnVerbose( "Python script main()" );
|
|
|
|
bOk = True;
|
|
|
|
strMsg = "";
|
|
|
|
strErrMsgProgFail = "";
|
|
|
|
|
|
|
|
if not( vDictArgs.has_key( "--swigExePath" ) and vDictArgs.has_key( "--swigExeName" ) ):
|
|
|
|
strErrMsgProgFail += strErrMsgSwigParamsMissing;
|
|
|
|
return (-100, strErrMsgProgFail );
|
|
|
|
|
|
|
|
bDebug = vDictArgs.has_key( "-d" );
|
|
|
|
|
|
|
|
strSwigDepFile = "";
|
|
|
|
strSwigDepOptions = "";
|
|
|
|
bGenDependencies = vDictArgs.has_key( "-M" );
|
|
|
|
if bGenDependencies:
|
|
|
|
strSwigDepFile = vDictArgs[ "--targetDir" ] + "/LLDBWrapPython.cpp.d";
|
|
|
|
strSwigDepOptions = "-MMD -MF \"%s.tmp\"" % strSwigDepFile;
|
|
|
|
strSwigDepFile = os.path.normcase( strSwigDepFile );
|
|
|
|
strSwigDepOptions = os.path.normcase( strSwigDepOptions );
|
|
|
|
|
|
|
|
bMakeFileCalled = vDictArgs.has_key( "-m" );
|
|
|
|
strSwigOutputFile = ""
|
|
|
|
if bMakeFileCalled:
|
|
|
|
strSwigOutputFile = vDictArgs[ "--targetDir" ] + "/LLDBWrapPython.cpp";
|
|
|
|
else:
|
|
|
|
strSwigOutputFile = vDictArgs[ "--srcRoot" ] + "/source/LLDBWrapPython.cpp";
|
|
|
|
strSwigOutputFile = os.path.normcase( strSwigOutputFile );
|
|
|
|
|
|
|
|
strRt = vDictArgs[ "--srcRoot" ];
|
|
|
|
strSwigInputFile = strRt + "/scripts/lldb.swig";
|
|
|
|
strSwigPythonExtensions = strRt + "/scripts/Python/python-extensions.swig";
|
|
|
|
strSwigPythonWrapper = strRt + "/scripts/Python/python-wrapper.swig";
|
|
|
|
strSwigPythonTypemaps = strRt + "/scripts/Python/python-typemaps.swig";
|
|
|
|
strSwigPythonSwigsafecast = strRt + "/scripts/Python/python-swigsafecast.swig";
|
|
|
|
strSwigInputFile = os.path.normcase( strSwigInputFile );
|
|
|
|
strSwigPythonExtensions = os.path.normcase( strSwigPythonExtensions );
|
|
|
|
strSwigPythonWrapper = os.path.normcase( strSwigPythonWrapper );
|
|
|
|
strSwigPythonTypemaps = os.path.normcase( strSwigPythonTypemaps );
|
|
|
|
strSwigPythonSwigsafecast = os.path.normcase( strSwigPythonSwigsafecast );
|
|
|
|
|
|
|
|
strEnvVarLLDBDisablePython = os.getenv( "LLDB_DISABLE_PYTHON", None );
|
|
|
|
# We don't want Python for this build, but touch the output file so we
|
|
|
|
# don't have to conditionalize the build on this as well.
|
|
|
|
# Note, at present iOS doesn't have Python, so if you're building for
|
|
|
|
# iOS be sure to set LLDB_DISABLE_PYTHON to 1.
|
|
|
|
if (strEnvVarLLDBDisablePython != None) and \
|
|
|
|
(strEnvVarLLDBDisablePython == "1"):
|
|
|
|
os.remove( strSwigOutputFile );
|
|
|
|
open( strSwigOutputFile, 'w' ).close(); # Touch the file
|
|
|
|
if bDebug:
|
|
|
|
strMsg = strMsgLldbDisablePython;
|
|
|
|
return (0, strMsg );
|
|
|
|
|
|
|
|
# If this project is being built with LLDB_DISABLE_PYTHON defined,
|
|
|
|
# don't bother generating Python swig bindings -- we don't have
|
|
|
|
# Python available.
|
|
|
|
strEnvVarGccPreprocessDefs = os.getenv( "GCC_PREPROCESSOR_DEFINITIONS",
|
|
|
|
None );
|
|
|
|
if (strEnvVarGccPreprocessDefs != None) or \
|
|
|
|
(strEnvVarLLDBDisablePython != None):
|
|
|
|
os.remove( strSwigOutputFile );
|
|
|
|
open( strSwigOutputFile, 'w' ).close(); # Touch the file
|
|
|
|
if bDebug:
|
|
|
|
strMsg = strMsgLldbDisableGccEnv;
|
|
|
|
return (0, strMsg);
|
|
|
|
|
|
|
|
bOk = bOk and get_header_files( vDictArgs );
|
|
|
|
bOk = bOk and get_interface_files( vDictArgs );
|
|
|
|
|
|
|
|
strFrameworkPythonDir = "";
|
|
|
|
if bOk:
|
|
|
|
bNeedUpdate = (check_file_exists( vDictArgs, strSwigOutputFile ) == False);
|
|
|
|
dbg.dump_object( "check_file_exists strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_files( vDictArgs, strSwigOutputFile, vDictArgs[ "--headerFiles" ] );
|
|
|
|
dbg.dump_object( "check_newer_files header files than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_files( vDictArgs, strSwigOutputFile, vDictArgs[ "--ifaceFiles" ] );
|
|
|
|
dbg.dump_object( "check_newer_files iface files than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_file( vDictArgs, strSwigOutputFile, strSwigInputFile );
|
|
|
|
dbg.dump_object( "check_newer_files strSwigInputFile than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_file( vDictArgs, strSwigOutputFile, strSwigPythonExtensions );
|
|
|
|
dbg.dump_object( "check_newer_files strSwigPythonExtensions than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_file( vDictArgs, strSwigOutputFile, strSwigPythonWrapper );
|
|
|
|
dbg.dump_object( "check_newer_files strSwigPythonWrapper than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_file( vDictArgs, strSwigOutputFile, strSwigPythonTypemaps );
|
|
|
|
dbg.dump_object( "check_newer_files strSwigPythonTypemaps than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
if bNeedUpdate == False:
|
|
|
|
bNeedUpdate = check_newer_file( vDictArgs, strSwigOutputFile, strSwigPythonSwigsafecast );
|
|
|
|
dbg.dump_object( "check_newer_files strSwigPythonSwigsafecast than strSwigOutputFile, bNeedUpdate =", bNeedUpdate);
|
|
|
|
|
|
|
|
# Determine where to put the files
|
|
|
|
bOk, strFrameworkPythonDir, strMsg = get_framework_python_dir( vDictArgs );
|
|
|
|
|
|
|
|
if bOk:
|
|
|
|
bOk, strCfgBldDir, strMsg = get_config_build_dir( vDictArgs, strFrameworkPythonDir );
|
|
|
|
|
|
|
|
if bOk and (bNeedUpdate == False):
|
|
|
|
strDllPath = strFrameworkPythonDir + "/_lldb.so";
|
|
|
|
strDllPath = os.path.normcase( strDllPath );
|
|
|
|
bSymbolicLink = check_file_exists( vDictArgs, strDllPath ) and os.path.islink( strDllPath );
|
|
|
|
bNeedUpdate = not bSymbolicLink;
|
|
|
|
dbg.dump_object( "check_file_exists( vDictArgs, strDllPath ) and os.path.islink( strDllPath ), bNeedUpdate =", bNeedUpdate);
|
|
|
|
|
|
|
|
if bOk and (bNeedUpdate == False):
|
|
|
|
strInitPiPath = strFrameworkPythonDir + "/__init__.py";
|
|
|
|
strInitPiPath = os.path.normcase( strInitPiPath );
|
|
|
|
print strInitPiPath
|
|
|
|
bNeedUpdate = not check_file_exists( vDictArgs, strInitPiPath );
|
|
|
|
dbg.dump_object( "check_file_exists( vDictArgs, strInitPiPath ), bNeedUpdate =", bNeedUpdate);
|
|
|
|
|
|
|
|
if bOk:
|
|
|
|
if (bNeedUpdate == False):
|
|
|
|
strMsg = strMsgNotNeedUpdate;
|
|
|
|
return (0, strMsg );
|
|
|
|
else:
|
|
|
|
print strMsgSwigNeedRebuild;
|
|
|
|
bOk, strMsg, nExitResult = do_swig_rebuild( vDictArgs, strSwigDepFile,
|
|
|
|
strCfgBldDir,
|
|
|
|
strSwigOutputFile,
|
|
|
|
strSwigInputFile );
|
|
|
|
bGenDependencies = vDictArgs.has_key( "-M" );
|
|
|
|
if bGenDependencies == True:
|
|
|
|
return (nExitResult, strMsg);
|
|
|
|
|
|
|
|
if bOk:
|
|
|
|
bOk, strMsg = do_modify_python_lldb( vDictArgs, strCfgBldDir );
|
|
|
|
|
|
|
|
if bOk:
|
|
|
|
return (0, strMsg );
|
|
|
|
else:
|
|
|
|
strErrMsgProgFail += strMsg;
|
|
|
|
return (-100, strErrMsgProgFail );
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# This script can be called by another Python script by calling the main()
|
|
|
|
# function directly
|
|
|
|
if __name__ == "__main__":
|
|
|
|
print "Script cannot be called directly, called by buildSwigWrapperClasses.py";
|
|
|
|
|