| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267 |
- """ This module contains code to freeze a number of Python modules
- into a single (mostly) standalone DLL or EXE. """
- import modulefinder
- import sys
- import os
- import marshal
- import imp
- import platform
- import struct
- from io import StringIO, BytesIO, TextIOWrapper
- import distutils.sysconfig as sysconf
- import zipfile
- from . import pefile
- # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
- try:
- import p3extend_frozen
- except ImportError:
- p3extend_frozen = None
- from panda3d.core import *
- # Check to see if we are running python_d, which implies we have a
- # debug build, and we have to build the module with debug options.
- # This is only relevant on Windows.
- # I wonder if there's a better way to determine this?
- python = os.path.splitext(os.path.split(sys.executable)[1])[0]
- isDebugBuild = (python.lower().endswith('_d'))
- # These are modules that Python always tries to import up-front. They
- # must be frozen in any main.exe.
- # NB. if encodings are removed, be sure to remove them from the shortcut in
- # deploy-stub.c.
- startupModules = [
- 'encodings', 'encodings.aliases', 'encodings.undefined', 'encodings.ascii',
- 'encodings.cp1252', 'encodings.latin_1', 'encodings.utf_8',
- 'encodings.mbcs', 'encodings.cp850', 'encodings.cp437', 'imp',
- ]
- if sys.version_info >= (3, 0):
- # Modules specific to Python 3
- startupModules += ['io', 'marshal', 'importlib.machinery', 'importlib.util']
- else:
- # Modules specific to Python 2
- startupModules += ['encodings.string_escape']
- # These are some special init functions for some built-in Python modules that
- # deviate from the standard naming convention. A value of None means that a
- # dummy entry should be written to the inittab.
- builtinInitFuncs = {
- 'builtins': None,
- '__builtin__': None,
- 'sys': None,
- 'exceptions': None,
- '_imp': 'PyInit_imp',
- '_warnings': '_PyWarnings_Init',
- 'marshal': 'PyMarshal_Init',
- }
- # These are modules that are not found normally for these modules. Add them
- # to an include list so users do not have to do this manually.
- try:
- from pytest import freeze_includes as pytest_imports
- except ImportError:
- def pytest_imports():
- return []
- hiddenImports = {
- 'pytest': pytest_imports(),
- 'pkg_resources': [
- 'pkg_resources.*.*',
- # TODO why does the above not get these modules too?
- 'pkg_resources._vendor.packaging.*',
- 'pkg_resources._vendor.packaging.version',
- 'pkg_resources._vendor.packaging.specifiers',
- 'pkg_resources._vendor.packaging.requirements',
- ],
- 'xml.etree.cElementTree': ['xml.etree.ElementTree'],
- 'datetime': ['_strptime'],
- }
- # These are missing modules that we've reported already this session.
- reportedMissing = {}
- class CompilationEnvironment:
- """ Create an instance of this class to record the commands to
- invoke the compiler on a given platform. If needed, the caller
- can create a custom instance of this class (or simply set the
- compile strings directly) to customize the build environment. """
- def __init__(self, platform):
- self.platform = platform
- # The command to compile a c to an object file. Replace %(basename)s
- # with the basename of the source file, and an implicit .c extension.
- self.compileObj = 'error'
- # The command to link a single object file into an executable. As
- # above, replace $(basename)s with the basename of the original source
- # file, and of the target executable.
- self.linkExe = 'error'
- # The command to link a single object file into a shared library.
- self.linkDll = 'error'
- # Paths to Python stuff.
- self.Python = None
- self.PythonIPath = sysconf.get_python_inc()
- self.PythonVersion = sysconf.get_config_var("LDVERSION") or sysconf.get_python_version()
- # The VC directory of Microsoft Visual Studio (if relevant)
- self.MSVC = None
- # Directory to Windows Platform SDK (if relevant)
- self.PSDK = None
- # The setting to control release vs. debug builds. Only relevant on
- # Windows.
- self.MD = None
- # Added to the path to the MSVC bin and lib directories on 64-bits Windows.
- self.suffix64 = ''
- # The _d extension to add to dll filenames on Windows in debug builds.
- self.dllext = ''
- # Any architecture-specific string.
- self.arch = ''
- self.determineStandardSetup()
- def determineStandardSetup(self):
- if self.platform.startswith('win'):
- self.Python = sysconf.PREFIX
- if ('VCINSTALLDIR' in os.environ):
- self.MSVC = os.environ['VCINSTALLDIR']
- elif (Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').exists()):
- self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').toOsSpecific()
- elif (Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').exists()):
- self.MSVC = Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').toOsSpecific()
- elif (Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').exists()):
- self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').toOsSpecific()
- else:
- print('Could not locate Microsoft Visual C++ Compiler! Try running from the Visual Studio Command Prompt.')
- sys.exit(1)
- if ('WindowsSdkDir' in os.environ):
- self.PSDK = os.environ['WindowsSdkDir']
- elif (platform.architecture()[0] == '32bit' and Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').exists()):
- self.PSDK = Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').toOsSpecific()
- elif (os.path.exists(os.path.join(self.MSVC, 'PlatformSDK'))):
- self.PSDK = os.path.join(self.MSVC, 'PlatformSDK')
- else:
- print('Could not locate the Microsoft Windows Platform SDK! Try running from the Visual Studio Command Prompt.')
- sys.exit(1)
- # We need to use the correct compiler setting for debug vs. release builds.
- self.MD = '/MD'
- if isDebugBuild:
- self.MD = '/MDd'
- self.dllext = '_d'
- # MSVC/bin and /lib directories have a different location
- # for win64.
- if self.platform == 'win_amd64':
- self.suffix64 = '\\amd64'
- # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
- if ('MAKEPANDA' in os.environ):
- self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
- self.compileObjDll = self.compileObjExe
- self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(python)s\libs" /out:%(basename)s.exe %(basename)s.obj'
- self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(python)s\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
- else:
- os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
- self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" /I"%(PSDK)s\include" /I"%(MSVC)s\include" %(filename)s'
- self.compileObjDll = self.compileObjExe
- self.linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs" /out:%(basename)s.exe %(basename)s.obj'
- self.linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(PSDK)s\lib" /LIBPATH:"%(MSVC)s\\lib%(suffix64)s" /LIBPATH:"%(python)s\libs" /out:%(basename)s%(dllext)s.pyd %(basename)s.obj'
- elif self.platform.startswith('osx_'):
- # OSX
- proc = self.platform.split('_', 1)[1]
- if proc == 'i386':
- self.arch = '-arch i386'
- elif proc == 'ppc':
- self.arch = '-arch ppc'
- elif proc == 'amd64':
- self.arch = '-arch x86_64'
- self.compileObjExe = "gcc -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
- self.compileObjDll = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
- self.linkExe = "gcc %(arch)s -o %(basename)s %(basename)s.o -framework Python"
- self.linkDll = "gcc %(arch)s -undefined dynamic_lookup -bundle -o %(basename)s.so %(basename)s.o"
- else:
- # Unix
- lib_dir = sysconf.get_python_lib(plat_specific=1, standard_lib=1)
- #python_a = os.path.join(lib_dir, "config", "libpython%(pythonVersion)s.a")
- self.compileObjExe = "%(CC)s %(CFLAGS)s -c -o %(basename)s.o -pthread -O2 %(filename)s -I%(pythonIPath)s"
- self.compileObjDll = "%(CC)s %(CFLAGS)s %(CCSHARED)s -c -o %(basename)s.o -O2 %(filename)s -I%(pythonIPath)s"
- self.linkExe = "%(CC)s -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
- self.linkDll = "%(LDSHARED)s -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
- if (os.path.isdir("/usr/PCBSD/local/lib")):
- self.linkExe += " -L/usr/PCBSD/local/lib"
- self.linkDll += " -L/usr/PCBSD/local/lib"
- def compileExe(self, filename, basename, extraLink=[]):
- compile = self.compileObjExe % dict({
- 'python' : self.Python,
- 'MSVC' : self.MSVC,
- 'PSDK' : self.PSDK,
- 'suffix64' : self.suffix64,
- 'MD' : self.MD,
- 'pythonIPath' : self.PythonIPath,
- 'pythonVersion' : self.PythonVersion,
- 'arch' : self.arch,
- 'filename' : filename,
- 'basename' : basename,
- }, **sysconf.get_config_vars())
- sys.stderr.write(compile + '\n')
- if os.system(compile) != 0:
- raise Exception('failed to compile %s.' % basename)
- link = self.linkExe % dict({
- 'python' : self.Python,
- 'MSVC' : self.MSVC,
- 'PSDK' : self.PSDK,
- 'suffix64' : self.suffix64,
- 'pythonIPath' : self.PythonIPath,
- 'pythonVersion' : self.PythonVersion,
- 'arch' : self.arch,
- 'filename' : filename,
- 'basename' : basename,
- }, **sysconf.get_config_vars())
- link += ' ' + ' '.join(extraLink)
- sys.stderr.write(link + '\n')
- if os.system(link) != 0:
- raise Exception('failed to link %s.' % basename)
- def compileDll(self, filename, basename, extraLink=[]):
- compile = self.compileObjDll % dict({
- 'python' : self.Python,
- 'MSVC' : self.MSVC,
- 'PSDK' : self.PSDK,
- 'suffix64' : self.suffix64,
- 'MD' : self.MD,
- 'pythonIPath' : self.PythonIPath,
- 'pythonVersion' : self.PythonVersion,
- 'arch' : self.arch,
- 'filename' : filename,
- 'basename' : basename,
- }, **sysconf.get_config_vars())
- sys.stderr.write(compile + '\n')
- if os.system(compile) != 0:
- raise Exception('failed to compile %s.' % basename)
- link = self.linkDll % dict({
- 'python' : self.Python,
- 'MSVC' : self.MSVC,
- 'PSDK' : self.PSDK,
- 'suffix64' : self.suffix64,
- 'pythonIPath' : self.PythonIPath,
- 'pythonVersion' : self.PythonVersion,
- 'arch' : self.arch,
- 'filename' : filename,
- 'basename' : basename,
- 'dllext' : self.dllext,
- }, **sysconf.get_config_vars())
- link += ' ' + ' '.join(extraLink)
- sys.stderr.write(link + '\n')
- if os.system(link) != 0:
- raise Exception('failed to link %s.' % basename)
- # The code from frozenmain.c in the Python source repository.
- frozenMainCode = """
- /* Python interpreter main program for frozen scripts */
- #include "Python.h"
- #if PY_MAJOR_VERSION >= 3
- #include <locale.h>
- #if PY_MINOR_VERSION < 5
- #define Py_DecodeLocale _Py_char2wchar
- #endif
- #endif
- #ifdef MS_WINDOWS
- extern void PyWinFreeze_ExeInit(void);
- extern void PyWinFreeze_ExeTerm(void);
- extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
- #endif
- /* Main program */
- int
- Py_FrozenMain(int argc, char **argv)
- {
- char *p;
- int n, sts = 1;
- int inspect = 0;
- int unbuffered = 0;
- #if PY_MAJOR_VERSION >= 3
- int i;
- char *oldloc;
- wchar_t **argv_copy = NULL;
- /* We need a second copies, as Python might modify the first one. */
- wchar_t **argv_copy2 = NULL;
- if (argc > 0) {
- argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
- argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
- }
- #endif
- Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
- Py_NoSiteFlag = 1;
- Py_NoUserSiteDirectory = 1;
- if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\\0')
- inspect = 1;
- if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\\0')
- unbuffered = 1;
- if (unbuffered) {
- setbuf(stdin, (char *)NULL);
- setbuf(stdout, (char *)NULL);
- setbuf(stderr, (char *)NULL);
- }
- #if PY_MAJOR_VERSION >= 3
- oldloc = setlocale(LC_ALL, NULL);
- setlocale(LC_ALL, \"\");
- for (i = 0; i < argc; i++) {
- argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
- argv_copy2[i] = argv_copy[i];
- if (!argv_copy[i]) {
- fprintf(stderr, \"Unable to decode the command line argument #%i\\n\",
- i + 1);
- argc = i;
- goto error;
- }
- }
- setlocale(LC_ALL, oldloc);
- #endif
- #ifdef MS_WINDOWS
- PyImport_ExtendInittab(extensions);
- #endif /* MS_WINDOWS */
- if (argc >= 1) {
- #if PY_MAJOR_VERSION >= 3
- Py_SetProgramName(argv_copy[0]);
- #else
- Py_SetProgramName(argv[0]);
- #endif
- }
- Py_Initialize();
- #ifdef MS_WINDOWS
- PyWinFreeze_ExeInit();
- #endif
- if (Py_VerboseFlag)
- fprintf(stderr, "Python %s\\n%s\\n",
- Py_GetVersion(), Py_GetCopyright());
- #if PY_MAJOR_VERSION >= 3
- PySys_SetArgv(argc, argv_copy);
- #else
- PySys_SetArgv(argc, argv);
- #endif
- n = PyImport_ImportFrozenModule("__main__");
- if (n == 0)
- Py_FatalError("__main__ not frozen");
- if (n < 0) {
- PyErr_Print();
- sts = 1;
- }
- else
- sts = 0;
- if (inspect && isatty((int)fileno(stdin)))
- sts = PyRun_AnyFile(stdin, "<stdin>") != 0;
- #ifdef MS_WINDOWS
- PyWinFreeze_ExeTerm();
- #endif
- Py_Finalize();
- #if PY_MAJOR_VERSION >= 3
- error:
- if (argv_copy2) {
- for (i = 0; i < argc; i++) {
- #if PY_MINOR_VERSION >= 4
- PyMem_RawFree(argv_copy2[i]);
- #else
- PyMem_Free(argv_copy2[i]);
- #endif
- }
- }
- #endif
- return sts;
- }
- """
- # The code from frozen_dllmain.c in the Python source repository.
- # Windows only.
- frozenDllMainCode = """
- #include "windows.h"
- static char *possibleModules[] = {
- "pywintypes",
- "pythoncom",
- "win32ui",
- NULL,
- };
- BOOL CallModuleDllMain(char *modName, DWORD dwReason);
- /*
- Called by a frozen .EXE only, so that built-in extension
- modules are initialized correctly
- */
- void PyWinFreeze_ExeInit(void)
- {
- char **modName;
- for (modName = possibleModules;*modName;*modName++) {
- /* printf("Initialising '%s'\\n", *modName); */
- CallModuleDllMain(*modName, DLL_PROCESS_ATTACH);
- }
- }
- /*
- Called by a frozen .EXE only, so that built-in extension
- modules are cleaned up
- */
- void PyWinFreeze_ExeTerm(void)
- {
- // Must go backwards
- char **modName;
- for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
- modName >= possibleModules;
- *modName--) {
- /* printf("Terminating '%s'\\n", *modName);*/
- CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
- }
- }
- BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
- {
- BOOL ret = TRUE;
- switch (dwReason) {
- case DLL_PROCESS_ATTACH:
- {
- char **modName;
- for (modName = possibleModules;*modName;*modName++) {
- BOOL ok = CallModuleDllMain(*modName, dwReason);
- if (!ok)
- ret = FALSE;
- }
- break;
- }
- case DLL_PROCESS_DETACH:
- {
- // Must go backwards
- char **modName;
- for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
- modName >= possibleModules;
- *modName--)
- CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
- break;
- }
- }
- return ret;
- }
- BOOL CallModuleDllMain(char *modName, DWORD dwReason)
- {
- BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID);
- char funcName[255];
- HMODULE hmod = GetModuleHandle(NULL);
- strcpy(funcName, "_DllMain");
- strcat(funcName, modName);
- strcat(funcName, "@12"); // stdcall convention.
- pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName);
- if (pfndllmain==NULL) {
- /* No function by that name exported - then that module does
- not appear in our frozen program - return OK
- */
- return TRUE;
- }
- return (*pfndllmain)(hmod, dwReason, NULL);
- }
- """
- # Our own glue code to start up a Python executable.
- mainInitCode = """
- %(frozenMainCode)s
- int
- main(int argc, char *argv[]) {
- PyImport_FrozenModules = _PyImport_FrozenModules;
- return Py_FrozenMain(argc, argv);
- }
- """
- # Our own glue code to start up a Python shared library.
- dllInitCode = """
- /*
- * Call this function to extend the frozen modules array with a new
- * array of frozen modules, provided in a C-style array, at runtime.
- * Returns the total number of frozen modules.
- */
- static int
- extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
- int orig_count;
- struct _frozen *realloc_FrozenModules;
- /* First, count the number of frozen modules we had originally. */
- orig_count = 0;
- while (PyImport_FrozenModules[orig_count].name != NULL) {
- ++orig_count;
- }
- if (new_count == 0) {
- /* Trivial no-op. */
- return orig_count;
- }
- /* Reallocate the PyImport_FrozenModules array bigger to make room
- for the additional frozen modules. We just leak the original
- array; it's too risky to try to free it. */
- realloc_FrozenModules = (struct _frozen *)malloc((orig_count + new_count + 1) * sizeof(struct _frozen));
- /* The new frozen modules go at the front of the list. */
- memcpy(realloc_FrozenModules, new_modules, new_count * sizeof(struct _frozen));
- /* Then the original set of frozen modules. */
- memcpy(realloc_FrozenModules + new_count, PyImport_FrozenModules, orig_count * sizeof(struct _frozen));
- /* Finally, a single 0-valued entry marks the end of the array. */
- memset(realloc_FrozenModules + orig_count + new_count, 0, sizeof(struct _frozen));
- /* Assign the new pointer. */
- PyImport_FrozenModules = realloc_FrozenModules;
- return orig_count + new_count;
- }
- #if PY_MAJOR_VERSION >= 3
- static PyModuleDef mdef = {
- PyModuleDef_HEAD_INIT,
- "%(moduleName)s",
- "",
- -1,
- NULL, NULL, NULL, NULL, NULL
- };
- %(dllexport)sPyObject *PyInit_%(moduleName)s(void) {
- extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
- return PyModule_Create(&mdef);
- }
- #else
- static PyMethodDef nullMethods[] = {
- {NULL, NULL}
- };
- %(dllexport)svoid init%(moduleName)s(void) {
- extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
- Py_InitModule("%(moduleName)s", nullMethods);
- }
- #endif
- """
- programFile = """
- #include "Python.h"
- #ifdef _WIN32
- #include "malloc.h"
- #endif
- %(moduleDefs)s
- struct _frozen _PyImport_FrozenModules[] = {
- %(moduleList)s
- {NULL, NULL, 0}
- };
- """
- okMissing = [
- '__main__', '_dummy_threading', 'Carbon', 'Carbon.Files',
- 'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
- 'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
- 'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
- 'riscospath', 'dbm', 'fcntl', 'win32api', 'win32pipe', 'usercustomize',
- '_winreg', 'winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
- 'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios', 'vms_lib',
- 'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
- 'email.Iterators', '_subprocess', 'gestalt', 'java.lang',
- 'direct.extensions_native.extensions_darwin',
- ]
- class Freezer:
- class ModuleDef:
- def __init__(self, moduleName, filename = None,
- implicit = False, guess = False,
- exclude = False, forbid = False,
- allowChildren = False, fromSource = None,
- text = None):
- # The Python module name.
- self.moduleName = moduleName
- # The file on disk it was loaded from, if any.
- self.filename = filename
- if filename is not None and not isinstance(filename, Filename):
- self.filename = Filename(filename)
- # True if the module was found via the modulefinder.
- self.implicit = implicit
- # True if the moduleName might refer to some Python object
- # other than a module, in which case the module should be
- # ignored.
- self.guess = guess
- # True if the module should *not* be included in the
- # generated output.
- self.exclude = exclude
- # True if the module should never be allowed, even if it
- # exists at runtime.
- self.forbid = forbid
- # True if excluding the module still allows its children
- # to be included. This only makes sense if the module
- # will exist at runtime through some other means
- # (e.g. from another package).
- self.allowChildren = allowChildren
- # Additional black-box information about where this module
- # record came from, supplied by the caller.
- self.fromSource = fromSource
- # If this is set, it contains Python code of the module.
- self.text = text
- # Some sanity checks.
- if not self.exclude:
- self.allowChildren = True
- if self.forbid:
- self.exclude = True
- self.allowChildren = False
- def __repr__(self):
- args = [repr(self.moduleName), repr(self.filename)]
- if self.implicit:
- args.append('implicit = True')
- if self.guess:
- args.append('guess = True')
- if self.exclude:
- args.append('exclude = True')
- if self.forbid:
- args.append('forbid = True')
- if self.allowChildren:
- args.append('allowChildren = True')
- return 'ModuleDef(%s)' % (', '.join(args))
- def __init__(self, previous = None, debugLevel = 0,
- platform = None, path=None):
- # Normally, we are freezing for our own platform. Change this
- # if untrue.
- self.platform = platform or PandaSystem.getPlatform()
- # This is the compilation environment. Fill in your own
- # object here if you have custom needs (for instance, for a
- # cross-compiler or something). If this is None, then a
- # default object will be created when it is needed.
- self.cenv = None
- # This is the search path to use for Python modules. Leave it
- # to the default value of None to use sys.path.
- self.path = path
- # The filename extension to append to the source file before
- # compiling.
- self.sourceExtension = '.c'
- # The filename extension to append to the object file.
- self.objectExtension = '.o'
- if self.platform.startswith('win'):
- self.objectExtension = '.obj'
- self.keepTemporaryFiles = False
- # Change any of these to change the generated startup and glue
- # code.
- self.frozenMainCode = frozenMainCode
- self.frozenDllMainCode = frozenDllMainCode
- self.mainInitCode = mainInitCode
- # Set this true to encode Python files in a Multifile as their
- # original source if possible, or false to encode them as
- # compiled pyc or pyo files. This has no effect on frozen exe
- # or dll's; those are always stored with compiled code.
- self.storePythonSource = False
- # This list will be filled in by generateCode() or
- # addToMultifile(). It contains a list of all the extension
- # modules that were discovered, which have not been added to
- # the output. The list is a list of tuples of the form
- # (moduleName, filename). filename will be None for built-in
- # modules.
- self.extras = []
- # Set this to true if extension modules should be linked in to
- # the resulting executable.
- self.linkExtensionModules = False
- # End of public interface. These remaining members should not
- # be directly manipulated by callers.
- self.previousModules = {}
- self.modules = {}
- if previous:
- self.previousModules = dict(previous.modules)
- self.modules = dict(previous.modules)
- # Exclude doctest by default; it is not very useful in production
- # builds. It can be explicitly included if desired.
- self.modules['doctest'] = self.ModuleDef('doctest', exclude = True)
- self.mf = None
- # Actually, make sure we know how to find all of the
- # already-imported modules. (Some of them might do their own
- # special path mangling.)
- for moduleName, module in list(sys.modules.items()):
- if module and hasattr(module, '__path__'):
- path = list(getattr(module, '__path__'))
- if path:
- modulefinder.AddPackagePath(moduleName, path[0])
- # Suffix/extension for Python C extension modules
- if self.platform == PandaSystem.getPlatform():
- self.moduleSuffixes = imp.get_suffixes()
- else:
- self.moduleSuffixes = [('.py', 'r', 1), ('.pyc', 'rb', 2)]
- if 'linux' in self.platform:
- self.moduleSuffixes += [
- ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
- ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
- ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
- ('.so', 'rb', 3),
- ]
- elif 'win' in self.platform:
- self.moduleSuffixes += [
- ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3),
- ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3),
- ('.pyd', 'rb', 3),
- ]
- elif 'mac' in self.platform:
- self.moduleSuffixes += [
- ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3),
- ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
- ('.so', 'rb', 3),
- ]
- else: # FreeBSD et al.
- self.moduleSuffixes += [
- ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3),
- ('.abi{0}.so'.format(*sys.version_info), 'rb', 3),
- ('.so', 'rb', 3),
- ]
- def excludeFrom(self, freezer):
- """ Excludes all modules that have already been processed by
- the indicated FreezeTool. This is equivalent to passing the
- indicated FreezeTool object as previous to this object's
- constructor, but it may be called at any point during
- processing. """
- for key, value in list(freezer.modules.items()):
- self.previousModules[key] = value
- self.modules[key] = value
- def excludeModule(self, moduleName, forbid = False, allowChildren = False,
- fromSource = None):
- """ Adds a module to the list of modules not to be exported by
- this tool. If forbid is true, the module is furthermore
- forbidden to be imported, even if it exists on disk. If
- allowChildren is true, the children of the indicated module
- may still be included."""
- assert self.mf == None
- self.modules[moduleName] = self.ModuleDef(
- moduleName, exclude = True,
- forbid = forbid, allowChildren = allowChildren,
- fromSource = fromSource)
- def handleCustomPath(self, moduleName):
- """ Indicates a module that may perform runtime manipulation
- of its __path__ variable, and which must therefore be actually
- imported at runtime in order to determine the true value of
- __path__. """
- str = 'import %s' % (moduleName)
- exec(str)
- module = sys.modules[moduleName]
- for path in module.__path__:
- modulefinder.AddPackagePath(moduleName, path)
- def getModulePath(self, moduleName):
- """ Looks for the indicated directory module and returns the
- __path__ member: the list of directories in which its python
- files can be found. If the module is a .py file and not a
- directory, returns None. """
- # First, try to import the module directly. That's the most
- # reliable answer, if it works.
- try:
- module = __import__(moduleName)
- except:
- print("couldn't import %s" % (moduleName))
- module = None
- if module != None:
- for symbol in moduleName.split('.')[1:]:
- module = getattr(module, symbol)
- if hasattr(module, '__path__'):
- return module.__path__
- # If it didn't work--maybe the module is unimportable because
- # it makes certain assumptions about the builtins, or
- # whatever--then just look for file on disk. That's usually
- # good enough.
- path = None
- baseName = moduleName
- if '.' in baseName:
- parentName, baseName = moduleName.rsplit('.', 1)
- path = self.getModulePath(parentName)
- if path == None:
- return None
- try:
- file, pathname, description = imp.find_module(baseName, path)
- except ImportError:
- return None
- if not os.path.isdir(pathname):
- return None
- return [pathname]
- def getModuleStar(self, moduleName):
- """ Looks for the indicated directory module and returns the
- __all__ member: the list of symbols within the module. """
- # First, try to import the module directly. That's the most
- # reliable answer, if it works.
- try:
- module = __import__(moduleName)
- except:
- print("couldn't import %s" % (moduleName))
- module = None
- if module != None:
- for symbol in moduleName.split('.')[1:]:
- module = getattr(module, symbol)
- if hasattr(module, '__all__'):
- return module.__all__
- # If it didn't work, just open the directory and scan for *.py
- # files.
- path = None
- baseName = moduleName
- if '.' in baseName:
- parentName, baseName = moduleName.rsplit('.', 1)
- path = self.getModulePath(parentName)
- if path == None:
- return None
- try:
- file, pathname, description = imp.find_module(baseName, path)
- except ImportError:
- return None
- if not os.path.isdir(pathname):
- return None
- # Scan the directory, looking for .py files.
- modules = []
- for basename in os.listdir(pathname):
- if basename.endswith('.py') and basename != '__init__.py':
- modules.append(basename[:-3])
- return modules
- def addModule(self, moduleName, implicit = False, newName = None,
- filename = None, guess = False, fromSource = None,
- text = None):
- """ Adds a module to the list of modules to be exported by
- this tool. If implicit is true, it is OK if the module does
- not actually exist.
- newName is the name to call the module when it appears in the
- output. The default is the same name it had in the original.
- Use caution when renaming a module; if another module imports
- this module by its original name, you will also need to
- explicitly add the module under its original name, duplicating
- the module twice in the output.
- The module name may end in ".*", which means to add all of the
- .py files (other than __init__.py) in a particular directory.
- It may also end in ".*.*", which means to cycle through all
- directories within a particular directory.
- """
- assert self.mf == None
- if not newName:
- newName = moduleName
- if moduleName.endswith('.*'):
- assert(newName.endswith('.*'))
- # Find the parent module, so we can get its directory.
- parentName = moduleName[:-2]
- newParentName = newName[:-2]
- parentNames = [(parentName, newParentName)]
- if parentName.endswith('.*'):
- assert(newParentName.endswith('.*'))
- # Another special case. The parent name "*" means to
- # return all possible directories within a particular
- # directory.
- topName = parentName[:-2]
- newTopName = newParentName[:-2]
- parentNames = []
- modulePath = self.getModulePath(topName)
- if modulePath:
- for dirname in modulePath:
- for basename in os.listdir(dirname):
- if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
- parentName = '%s.%s' % (topName, basename)
- newParentName = '%s.%s' % (newTopName, basename)
- if self.getModulePath(parentName):
- parentNames.append((parentName, newParentName))
- for parentName, newParentName in parentNames:
- modules = self.getModuleStar(parentName)
- if modules == None:
- # It's actually a regular module.
- self.modules[newParentName] = self.ModuleDef(
- parentName, implicit = implicit, guess = guess,
- fromSource = fromSource, text = text)
- else:
- # Now get all the py files in the parent directory.
- for basename in modules:
- moduleName = '%s.%s' % (parentName, basename)
- newName = '%s.%s' % (newParentName, basename)
- mdef = self.ModuleDef(
- moduleName, implicit = implicit, guess = True,
- fromSource = fromSource)
- self.modules[newName] = mdef
- else:
- # A normal, explicit module name.
- self.modules[newName] = self.ModuleDef(
- moduleName, filename = filename, implicit = implicit,
- guess = guess, fromSource = fromSource, text = text)
- def done(self, addStartupModules = False):
- """ Call this method after you have added all modules with
- addModule(). You may then call generateCode() or
- writeMultifile() to dump the resulting output. After a call
- to done(), you may not add any more modules until you call
- reset(). """
- assert self.mf == None
- # If we are building an exe, we also need to implicitly
- # bring in Python's startup modules.
- if addStartupModules:
- self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
- self.modules['_frozen_importlib_external'] = self.ModuleDef('importlib._bootstrap_external', implicit = True)
- for moduleName in startupModules:
- if moduleName not in self.modules:
- self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True)
- # Excluding a parent module also excludes all its
- # (non-explicit) children, unless the parent has allowChildren
- # set.
- # Walk through the list in sorted order, so we reach parents
- # before children.
- names = list(self.modules.items())
- names.sort()
- excludeDict = {}
- implicitParentDict = {}
- includes = []
- autoIncludes = []
- origToNewName = {}
- for newName, mdef in names:
- moduleName = mdef.moduleName
- origToNewName[moduleName] = newName
- if mdef.implicit and '.' in newName:
- # For implicit modules, check if the parent is excluded.
- parentName, baseName = newName.rsplit('.', 1)
- if parentName in excludeDict :
- mdef = excludeDict[parentName]
- if mdef.exclude:
- if not mdef.allowChildren:
- excludeDict[moduleName] = mdef
- elif mdef.implicit or mdef.guess:
- autoIncludes.append(mdef)
- else:
- includes.append(mdef)
- self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path)
- # Attempt to import the explicit modules into the modulefinder.
- # First, ensure the includes are sorted in order so that
- # packages appear before the modules they contain. This
- # resolves potential ordering issues, especially with modules
- # that are discovered by filename rather than through import
- # statements.
- includes.sort(key = self.__sortModuleKey)
- # Now walk through the list and import them all.
- for mdef in includes:
- try:
- self.__loadModule(mdef)
- except ImportError as ex:
- message = "Unknown module: %s" % (mdef.moduleName)
- if str(ex) != "No module named " + str(mdef.moduleName):
- message += " (%s)" % (ex)
- print(message)
- # Also attempt to import any implicit modules. If any of
- # these fail to import, we don't really care.
- for mdef in autoIncludes:
- try:
- self.__loadModule(mdef)
- # Since it successfully loaded, it's no longer a guess.
- mdef.guess = False
- except:
- # Something went wrong, guess it's not an importable
- # module.
- pass
- # Check if any new modules we found have "hidden" imports
- for origName in list(self.mf.modules.keys()):
- hidden = hiddenImports.get(origName, [])
- for modname in hidden:
- self.__loadModule(self.ModuleDef(modname, implicit = True))
- # Now, any new modules we found get added to the export list.
- for origName in list(self.mf.modules.keys()):
- if origName not in origToNewName:
- self.modules[origName] = self.ModuleDef(origName, implicit = True)
- missing = []
- for origName in self.mf.any_missing_maybe()[0]:
- if origName in startupModules:
- continue
- if origName in self.previousModules:
- continue
- if origName in self.modules:
- continue
- # This module is missing. Let it be missing in the
- # runtime also.
- self.modules[origName] = self.ModuleDef(origName, exclude = True,
- implicit = True)
- if origName in okMissing:
- # If it's listed in okMissing, don't even report it.
- continue
- prefix = origName.split('.')[0]
- if origName not in reportedMissing:
- missing.append(origName)
- reportedMissing[origName] = True
- if missing:
- missing.sort()
- print("There are some missing modules: %r" % missing)
- def __sortModuleKey(self, mdef):
- """ A sort key function to sort a list of mdef's into order,
- primarily to ensure that packages proceed their modules. """
- if mdef.moduleName:
- # If we have a moduleName, the key consists of the split
- # tuple of packages names. That way, parents always sort
- # before children.
- return ('a', mdef.moduleName.split('.'))
- else:
- # If we don't have a moduleName, the key doesn't really
- # matter--we use filename--but we start with 'b' to ensure
- # that all of non-named modules appear following all of
- # the named modules.
- return ('b', mdef.filename)
- def __loadModule(self, mdef):
- """ Adds the indicated module to the modulefinder. """
- if mdef.filename:
- # If it has a filename, then we found it as a file on
- # disk. In this case, the moduleName may not be accurate
- # and useful, so load it as a file instead.
- tempPath = None
- if '.' not in mdef.moduleName:
- # If we loaded a python file from the root, we need to
- # temporarily add its directory to the module search
- # path, so the modulefinder can find any sibling
- # python files it imports as well.
- tempPath = Filename(mdef.filename.getDirname()).toOsSpecific()
- self.mf.path.append(tempPath)
- pathname = mdef.filename.toOsSpecific()
- ext = mdef.filename.getExtension()
- if ext == 'pyc' or ext == 'pyo':
- fp = open(pathname, 'rb')
- stuff = ("", "rb", imp.PY_COMPILED)
- self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
- else:
- if mdef.text:
- fp = StringIO(mdef.text)
- else:
- fp = open(pathname, 'U')
- stuff = ("", "r", imp.PY_SOURCE)
- self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
- if tempPath:
- del self.mf.path[-1]
- else:
- # Otherwise, we can just import it normally.
- self.mf.import_hook(mdef.moduleName)
- def reset(self):
- """ After a previous call to done(), this resets the
- FreezeTool object for a new pass. More modules may be added
- and dumped to a new target. Previously-added modules are
- remembered and will not be dumped again. """
- self.mf = None
- self.previousModules = dict(self.modules)
- def mangleName(self, moduleName):
- return 'M_' + moduleName.replace('.', '__').replace('-', '_')
- def getAllModuleNames(self):
- """ Return a list of all module names that have been included
- or forbidden, either in this current pass or in a previous
- pass. Module names that have been excluded are not included
- in this list. """
- moduleNames = []
- for newName, mdef in list(self.modules.items()):
- if mdef.guess:
- # Not really a module.
- pass
- elif mdef.exclude and not mdef.forbid:
- # An excluded (but not forbidden) file.
- pass
- else:
- moduleNames.append(newName)
- moduleNames.sort()
- return moduleNames
- def getModuleDefs(self):
- """ Return a list of all of the modules we will be explicitly
- or implicitly including. The return value is actually a list
- of tuples: (moduleName, moduleDef)."""
- moduleDefs = []
- for newName, mdef in list(self.modules.items()):
- prev = self.previousModules.get(newName, None)
- if not mdef.exclude:
- # Include this module (even if a previous pass
- # excluded it). But don't bother if we exported it
- # previously.
- if prev and not prev.exclude:
- # Previously exported.
- pass
- elif mdef.moduleName in self.mf.modules or \
- mdef.moduleName in startupModules or \
- mdef.filename:
- moduleDefs.append((newName, mdef))
- elif mdef.forbid:
- if not prev or not prev.forbid:
- moduleDefs.append((newName, mdef))
- moduleDefs.sort()
- return moduleDefs
- def __replacePaths(self):
- # Build up the replacement pathname table, so we can eliminate
- # the personal information in the frozen pathnames. The
- # actual filename we put in there is meaningful only for stack
- # traces, so we'll just use the module name.
- replace_paths = []
- for moduleName, module in list(self.mf.modules.items()):
- if module.__code__:
- origPathname = module.__code__.co_filename
- replace_paths.append((origPathname, moduleName))
- self.mf.replace_paths = replace_paths
- # Now that we have built up the replacement mapping, go back
- # through and actually replace the paths.
- for moduleName, module in list(self.mf.modules.items()):
- if module.__code__:
- co = self.mf.replace_paths_in_code(module.__code__)
- module.__code__ = co;
- def __addPyc(self, multifile, filename, code, compressionLevel):
- if code:
- data = imp.get_magic() + b'\0\0\0\0'
- if sys.version_info >= (3, 0):
- data += b'\0\0\0\0'
- data += marshal.dumps(code)
- stream = StringStream(data)
- multifile.addSubfile(filename, stream, compressionLevel)
- multifile.flush()
- def __addPythonDirs(self, multifile, moduleDirs, dirnames, compressionLevel):
- """ Adds all of the names on dirnames as a module directory. """
- if not dirnames:
- return
- str = '.'.join(dirnames)
- if str not in moduleDirs:
- # Add an implicit __init__.py file (but only if there's
- # not already a legitimate __init__.py file).
- moduleName = '.'.join(dirnames)
- filename = '/'.join(dirnames) + '/__init__'
- if self.storePythonSource:
- filename += '.py'
- stream = StringStream(b'')
- if multifile.findSubfile(filename) < 0:
- multifile.addSubfile(filename, stream, 0)
- multifile.flush()
- else:
- if __debug__:
- filename += '.pyc'
- else:
- filename += '.pyo'
- if multifile.findSubfile(filename) < 0:
- code = compile('', moduleName, 'exec')
- self.__addPyc(multifile, filename, code, compressionLevel)
- moduleDirs[str] = True
- self.__addPythonDirs(multifile, moduleDirs, dirnames[:-1], compressionLevel)
- def __addPythonFile(self, multifile, moduleDirs, moduleName, mdef,
- compressionLevel):
- """ Adds the named module to the multifile as a .pyc file. """
- # First, split the module into its subdirectory names.
- dirnames = moduleName.split('.')
- if len(dirnames) > 1 and dirnames[-1] == '__init__':
- # The "module" may end in __init__, but that really means
- # the parent directory.
- dirnames = dirnames[:-1]
- self.__addPythonDirs(multifile, moduleDirs, dirnames[:-1], compressionLevel)
- filename = '/'.join(dirnames)
- module = self.mf.modules.get(mdef.moduleName, None)
- if getattr(module, '__path__', None) is not None or \
- (getattr(module, '__file__', None) is not None and getattr(module, '__file__').endswith('/__init__.py')):
- # It's actually a package. In this case, we really write
- # the file moduleName/__init__.py.
- filename += '/__init__'
- moduleDirs[moduleName] = True
- # Ensure we don't have an implicit filename from above.
- multifile.removeSubfile(filename + '.py')
- if __debug__:
- multifile.removeSubfile(filename + '.pyc')
- else:
- multifile.removeSubfile(filename + '.pyo')
- # Attempt to add the original source file if we can.
- sourceFilename = None
- if mdef.filename and mdef.filename.getExtension() == "py":
- sourceFilename = mdef.filename
- elif getattr(module, '__file__', None):
- sourceFilename = Filename.fromOsSpecific(module.__file__)
- sourceFilename.setExtension("py")
- sourceFilename.setText()
- if self.storePythonSource:
- if sourceFilename and sourceFilename.exists():
- filename += '.py'
- multifile.addSubfile(filename, sourceFilename, compressionLevel)
- return
- # If we can't find the source file, add the compiled pyc instead.
- if __debug__:
- filename += '.pyc'
- else:
- filename += '.pyo'
- code = None
- if module:
- # Get the compiled code directly from the module object.
- code = getattr(module, "__code__", None)
- if not code:
- # This is a module with no associated Python
- # code. It must be an extension module. Get the
- # filename.
- extensionFilename = getattr(module, '__file__', None)
- if extensionFilename:
- self.extras.append((moduleName, extensionFilename))
- else:
- # It doesn't even have a filename; it must
- # be a built-in module. No worries about
- # this one, then.
- pass
- else:
- # Read the code from the source file and compile it on-the-fly.
- if sourceFilename and sourceFilename.exists():
- source = open(sourceFilename.toOsSpecific(), 'r').read()
- if source and source[-1] != '\n':
- source = source + '\n'
- code = compile(source, str(sourceFilename), 'exec')
- self.__addPyc(multifile, filename, code, compressionLevel)
- def addToMultifile(self, multifile, compressionLevel = 0):
- """ After a call to done(), this stores all of the accumulated
- python code into the indicated Multifile. Additional
- extension modules are listed in self.extras. """
- moduleDirs = {}
- for moduleName, mdef in self.getModuleDefs():
- if not mdef.exclude:
- self.__addPythonFile(multifile, moduleDirs, moduleName, mdef,
- compressionLevel)
- def writeMultifile(self, mfname):
- """ After a call to done(), this stores all of the accumulated
- python code into a Multifile with the indicated filename,
- including the extension. Additional extension modules are
- listed in self.extras."""
- self.__replacePaths()
- Filename(mfname).unlink()
- multifile = Multifile()
- if not multifile.openReadWrite(mfname):
- raise Exception
- self.addToMultifile(multifile)
- multifile.flush()
- multifile.repack()
- def writeCode(self, filename, initCode = ""):
- """ After a call to done(), this freezes all of the accumulated
- Python code into a C source file. """
- self.__replacePaths()
- # Now generate the actual export table.
- moduleDefs = []
- moduleList = []
- for moduleName, mdef in self.getModuleDefs():
- origName = mdef.moduleName
- if mdef.forbid:
- # Explicitly disallow importing this module.
- moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
- continue
- assert not mdef.exclude
- # Allow importing this module.
- module = self.mf.modules.get(origName, None)
- code = getattr(module, "__code__", None)
- if code:
- code = marshal.dumps(code)
- mangledName = self.mangleName(moduleName)
- moduleDefs.append(self.makeModuleDef(mangledName, code))
- moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
- continue
- #if moduleName in startupModules:
- # # Forbid the loading of this startup module.
- # moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
- # continue
- # This is a module with no associated Python code. It is either
- # an extension module or a builtin module. Get the filename, if
- # it is the former.
- extensionFilename = getattr(module, '__file__', None)
- if extensionFilename or self.linkExtensionModules:
- self.extras.append((moduleName, extensionFilename))
- # If it is a submodule of a frozen module, Python will have
- # trouble importing it as a builtin module. Synthesize a frozen
- # module that loads it as builtin.
- if '.' in moduleName and self.linkExtensionModules:
- code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec')
- code = marshal.dumps(code)
- mangledName = self.mangleName(moduleName)
- moduleDefs.append(self.makeModuleDef(mangledName, code))
- moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, None))
- elif '.' in moduleName:
- # Nothing we can do about this case except warn the user they
- # are in for some trouble.
- print('WARNING: Python cannot import extension modules under '
- 'frozen Python packages; %s will be inaccessible. '
- 'passing either -l to link in extension modules or use '
- '-x %s to exclude the entire package.' % (moduleName, moduleName.split('.')[0]))
- text = programFile % {
- 'moduleDefs': '\n'.join(moduleDefs),
- 'moduleList': '\n'.join(moduleList),
- }
- if self.linkExtensionModules and self.extras:
- # Should we link in extension modules? If so, we write out a new
- # built-in module table that directly hooks up with the init
- # functions. On Linux, we completely override Python's own
- # built-in module table; on Windows, we can't do this, so we
- # instead use PyImport_ExtendInittab to add to it.
- # Python 3 case.
- text += '#if PY_MAJOR_VERSION >= 3\n'
- for module, fn in self.extras:
- if sys.platform != "win32" or fn:
- libName = module.split('.')[-1]
- initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName)
- if initFunc:
- text += 'extern DL_IMPORT(PyObject) *%s(void);\n' % (initFunc)
- text += '\n'
- if sys.platform == "win32":
- text += 'static struct _inittab extensions[] = {\n'
- else:
- text += 'struct _inittab _PyImport_Inittab[] = {\n'
- for module, fn in self.extras:
- if sys.platform != "win32" or fn:
- libName = module.split('.')[-1]
- initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName) or 'NULL'
- text += ' {"%s", %s},\n' % (module, initFunc)
- text += ' {0, 0},\n'
- text += '};\n\n'
- # Python 2 case.
- text += '#else\n'
- for module, fn in self.extras:
- if sys.platform != "win32" or fn:
- libName = module.split('.')[-1]
- initFunc = builtinInitFuncs.get(module, 'init' + libName)
- if initFunc:
- text += 'extern DL_IMPORT(void) %s(void);\n' % (initFunc)
- text += '\n'
- if sys.platform == "win32":
- text += 'static struct _inittab extensions[] = {\n'
- else:
- text += 'struct _inittab _PyImport_Inittab[] = {\n'
- for module, fn in self.extras:
- if sys.platform != "win32" or fn:
- libName = module.split('.')[-1]
- initFunc = builtinInitFuncs.get(module, 'init' + libName) or 'NULL'
- text += ' {"%s", %s},\n' % (module, initFunc)
- text += ' {0, 0},\n'
- text += '};\n'
- text += '#endif\n\n'
- elif sys.platform == "win32":
- text += 'static struct _inittab extensions[] = {\n'
- text += ' {0, 0},\n'
- text += '};\n\n'
- text += initCode
- if filename is not None:
- file = open(filename, 'w')
- file.write(text)
- file.close()
- def generateCode(self, basename, compileToExe = False):
- """ After a call to done(), this freezes all of the
- accumulated python code into either an executable program (if
- compileToExe is true) or a dynamic library (if compileToExe is
- false). The basename is the name of the file to write,
- without the extension.
- The return value is the newly-generated filename, including
- the filename extension. Additional extension modules are
- listed in self.extras. """
- if compileToExe:
- # We must have a __main__ module to make an exe file.
- if not self.__writingModule('__main__'):
- message = "Can't generate an executable without a __main__ module."
- raise Exception(message)
- filename = basename + self.sourceExtension
- dllexport = ''
- dllimport = ''
- if self.platform.startswith('win'):
- dllexport = '__declspec(dllexport) '
- dllimport = '__declspec(dllimport) '
- if not self.cenv:
- self.cenv = CompilationEnvironment(platform = self.platform)
- if compileToExe:
- code = self.frozenMainCode
- if self.platform.startswith('win'):
- code += self.frozenDllMainCode
- initCode = self.mainInitCode % {
- 'frozenMainCode' : code,
- 'programName' : os.path.basename(basename),
- 'dllexport' : dllexport,
- 'dllimport' : dllimport,
- }
- if self.platform.startswith('win'):
- target = basename + '.exe'
- else:
- target = basename
- compileFunc = self.cenv.compileExe
- else:
- if self.platform.startswith('win'):
- target = basename + self.cenv.dllext + '.pyd'
- else:
- target = basename + '.so'
- initCode = dllInitCode % {
- 'moduleName' : os.path.basename(basename),
- 'dllexport' : dllexport,
- 'dllimport' : dllimport,
- }
- compileFunc = self.cenv.compileDll
- self.writeCode(filename, initCode=initCode)
- # Keep track of the files we should clean up after use.
- cleanFiles = [filename, basename + self.objectExtension]
- extraLink = []
- if self.linkExtensionModules:
- for mod, fn in self.extras:
- if not fn:
- continue
- if sys.platform == 'win32':
- # We can't link with a .pyd directly on Windows. Check
- # if there is a corresponding .lib file in the Python libs
- # directory.
- libsdir = os.path.join(sys.exec_prefix, 'libs')
- libfile = os.path.join(libsdir, mod + '.lib')
- if os.path.isfile(libfile):
- extraLink.append(mod + '.lib')
- continue
- # No, so we have to generate a .lib file. This is pretty
- # easy given that we know the only symbol we need is a
- # initmodule or PyInit_module function.
- modname = mod.split('.')[-1]
- libfile = modname + '.lib'
- if sys.version_info >= (3, 0):
- symbolName = 'PyInit_' + modname
- else:
- symbolName = 'init' + modname
- os.system('lib /nologo /def /export:%s /name:%s.pyd /out:%s' % (symbolName, modname, libfile))
- extraLink.append(libfile)
- cleanFiles += [libfile, modname + '.exp']
- else:
- extraLink.append(fn)
- try:
- compileFunc(filename, basename, extraLink=extraLink)
- finally:
- if not self.keepTemporaryFiles:
- for file in cleanFiles:
- if os.path.exists(file):
- os.unlink(file)
- return target
- def generateRuntimeFromStub(self, target, stub_file, fields={}):
- # We must have a __main__ module to make an exe file.
- if not self.__writingModule('__main__'):
- message = "Can't generate an executable without a __main__ module."
- raise Exception(message)
- if self.platform.startswith('win'):
- modext = '.pyd'
- else:
- modext = '.so'
- # First gather up the strings and code for all the module names, and
- # put those in a string pool.
- pool = b""
- strings = set()
- for moduleName, mdef in self.getModuleDefs():
- strings.add(moduleName.encode('ascii'))
- for value in fields.values():
- if value is not None:
- strings.add(value.encode('utf-8'))
- # Sort by length descending, allowing reuse of partial strings.
- strings = sorted(strings, key=lambda str:-len(str))
- string_offsets = {}
- # Now add the strings to the pool, and collect the offsets relative to
- # the beginning of the pool.
- for string in strings:
- # First check whether it's already in there; it could be part of
- # a longer string.
- offset = pool.find(string + b'\0')
- if offset < 0:
- offset = len(pool)
- pool += string + b'\0'
- string_offsets[string] = offset
- # Now go through the modules and add them to the pool as well. These
- # are not 0-terminated, but we later record their sizes and names in
- # a table after the blob header.
- moduleList = []
- for moduleName, mdef in self.getModuleDefs():
- origName = mdef.moduleName
- if mdef.forbid:
- # Explicitly disallow importing this module.
- moduleList.append((moduleName, 0, 0))
- continue
- # For whatever it's worth, align the code blocks.
- if len(pool) & 3 != 0:
- pad = (4 - (len(pool) & 3))
- pool += b'\0' * pad
- assert not mdef.exclude
- # Allow importing this module.
- module = self.mf.modules.get(origName, None)
- code = getattr(module, "__code__", None)
- if code:
- code = marshal.dumps(code)
- size = len(code)
- if getattr(module, "__path__", None):
- # Indicate package by negative size
- size = -size
- moduleList.append((moduleName, len(pool), size))
- pool += code
- continue
- # This is a module with no associated Python code. It is either
- # an extension module or a builtin module. Get the filename, if
- # it is the former.
- extensionFilename = getattr(module, '__file__', None)
- if extensionFilename:
- self.extras.append((moduleName, extensionFilename))
- # If it is a submodule of a frozen module, Python will have
- # trouble importing it as a builtin module. Synthesize a frozen
- # module that loads it dynamically.
- if '.' in moduleName:
- code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
- code = compile(code, moduleName, 'exec')
- code = marshal.dumps(code)
- moduleList.append((moduleName, len(pool), len(code)))
- pool += code
- # Determine the format of the header and module list entries depending
- # on the platform.
- num_pointers = 11
- stub_data = bytearray(stub_file.read())
- bitnesses = self._get_executable_bitnesses(stub_data)
- header_layouts = {
- 32: '<QQHHHH8x%dII' % num_pointers,
- 64: '<QQHHHH8x%dQQ' % num_pointers,
- }
- entry_layouts = {
- 32: '<IIi',
- 64: '<QQixxxx',
- }
- # Calculate the size of the module tables, so that we can determine
- # the proper offset for the string pointers. There can be more than
- # one module table for macOS executables. Sort the bitnesses so that
- # the alignment is correct.
- bitnesses = sorted(bitnesses, reverse=True)
- pool_offset = 0
- for bitness in bitnesses:
- pool_offset += (len(moduleList) + 1) * struct.calcsize(entry_layouts[bitness])
- # Now we can determine the offset of the blob.
- if self.platform.startswith('win'):
- # We don't use mmap on Windows. Align just for good measure.
- blob_align = 32
- else:
- # Align to page size, so that it can be mmapped.
- blob_align = 4096
- # Add padding before the blob if necessary.
- blob_offset = len(stub_data)
- if (blob_offset & (blob_align - 1)) != 0:
- pad = (blob_align - (blob_offset & (blob_align - 1)))
- stub_data += (b'\0' * pad)
- blob_offset += pad
- assert (blob_offset % blob_align) == 0
- assert blob_offset == len(stub_data)
- # Also determine the total blob size now. Add padding to the end.
- blob_size = pool_offset + len(pool)
- if blob_size & 31 != 0:
- pad = (32 - (blob_size & 31))
- blob_size += pad
- # Calculate the offsets for the variables. These are pointers,
- # relative to the beginning of the blob.
- field_offsets = {}
- for key, value in fields.items():
- if value is not None:
- encoded = value.encode('utf-8')
- field_offsets[key] = pool_offset + string_offsets[encoded]
- # OK, now go and write the blob. This consists of the module table
- # (there may be two in the case of a macOS universal (fat) binary).
- blob = b""
- append_offset = False
- for bitness in bitnesses:
- entry_layout = entry_layouts[bitness]
- header_layout = header_layouts[bitness]
- table_offset = len(blob)
- for moduleName, offset, size in moduleList:
- encoded = moduleName.encode('ascii')
- string_offset = pool_offset + string_offsets[encoded]
- if size != 0:
- offset += pool_offset
- blob += struct.pack(entry_layout, string_offset, offset, size)
- # A null entry marks the end of the module table.
- blob += struct.pack(entry_layout, 0, 0, 0)
- # Compose the header we will be writing to the stub, to tell it
- # where to find the module data blob, as well as other variables.
- header = struct.pack(header_layout,
- blob_offset,
- blob_size,
- 1, # Version number
- num_pointers, # Number of pointers that follow
- 0, # Codepage, not yet used
- 0, # Flags, not yet used
- table_offset, # Module table pointer.
- # The following variables need to be set before static init
- # time. See configPageManager.cxx, where they are read.
- field_offsets.get('prc_data', 0),
- field_offsets.get('default_prc_dir', 0),
- field_offsets.get('prc_dir_envvars', 0),
- field_offsets.get('prc_path_envvars', 0),
- field_offsets.get('prc_patterns', 0),
- field_offsets.get('prc_encrypted_patterns', 0),
- field_offsets.get('prc_encryption_key', 0),
- field_offsets.get('prc_executable_patterns', 0),
- field_offsets.get('prc_executable_args_envvar', 0),
- field_offsets.get('main_dir', 0),
- 0)
- # Now, find the location of the 'blobinfo' symbol in the binary,
- # to which we will write our header.
- if not self._replace_symbol(stub_data, b'blobinfo', header, bitness=bitness):
- # This must be a legacy deploy-stub, which requires the offset to
- # be appended to the end.
- append_offset = True
- # Add the string/code pool.
- assert len(blob) == pool_offset
- blob += pool
- del pool
- # Now pad out the blob to the calculated blob size.
- if len(blob) < blob_size:
- blob += b'\0' * (blob_size - len(blob))
- assert len(blob) == blob_size
- if append_offset:
- # This is for legacy deploy-stub.
- print("WARNING: Could not find blob header. Is deploy-stub outdated?")
- blob += struct.pack('<Q', blob_offset)
- with open(target, 'wb') as f:
- f.write(stub_data)
- assert f.tell() == blob_offset
- f.write(blob)
- os.chmod(target, 0o755)
- return target
- def _get_executable_bitnesses(self, data):
- """Returns the bitnesses (32 or 64) of the given executable data.
- This will contain 1 element for non-fat executables."""
- if data.startswith(b'MZ'):
- # A Windows PE file.
- offset, = struct.unpack_from('<I', data, 0x3c)
- assert data[offset:offset+4] == b'PE\0\0'
- magic, = struct.unpack_from('<H', data, offset + 24)
- assert magic in (0x010b, 0x020b)
- if magic == 0x020b:
- return (64,)
- else:
- return (32,)
- elif data.startswith(b"\177ELF"):
- # A Linux/FreeBSD ELF executable.
- elfclass = ord(data[4:5])
- assert elfclass in (1, 2)
- return (elfclass * 32,)
- elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE'):
- # 32-bit Mach-O file, as used on macOS.
- return (32,)
- elif data[:4] in (b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
- # 64-bit Mach-O file, as used on macOS.
- return (64,)
- elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
- # Universal binary with 32-bit offsets.
- num_fat, = struct.unpack_from('>I', data, 4)
- bitnesses = set()
- ptr = 8
- for i in range(num_fat):
- cputype, cpusubtype, offset, size, align = \
- struct.unpack_from('>IIIII', data, ptr)
- ptr += 20
- if (cputype & 0x1000000) != 0:
- bitnesses.add(64)
- else:
- bitnesses.add(32)
- return tuple(bitnesses)
- elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
- # Universal binary with 64-bit offsets.
- num_fat, = struct.unpack_from('>I', data, 4)
- bitnesses = set()
- ptr = 8
- for i in range(num_fat):
- cputype, cpusubtype, offset, size, align = \
- struct.unpack_from('>QQQQQ', data, ptr)
- ptr += 40
- if (cputype & 0x1000000) != 0:
- bitnesses.add(64)
- else:
- bitnesses.add(32)
- return tuple(bitnesses)
- def _replace_symbol(self, data, symbol_name, replacement, bitness=None):
- """We store a custom section in the binary file containing a header
- containing offsets to the binary data.
- If bitness is set, and the binary in question is a macOS universal
- binary, it only replaces for binaries with the given bitness. """
- if data.startswith(b'MZ'):
- # A Windows PE file.
- pe = pefile.PEFile()
- pe.read(BytesIO(data))
- addr = pe.get_export_address(symbol_name)
- if addr is not None:
- # We found it, return its offset in the file.
- offset = pe.get_address_offset(addr)
- if offset is not None:
- data[offset:offset+len(replacement)] = replacement
- return True
- elif data.startswith(b"\177ELF"):
- return self._replace_symbol_elf(data, symbol_name, replacement)
- elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
- b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
- off = self._find_symbol_macho(data, symbol_name)
- if off is not None:
- data[off:off+len(replacement)] = replacement
- return True
- return False
- elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
- # Universal binary with 32-bit offsets.
- num_fat, = struct.unpack_from('>I', data, 4)
- replaced = False
- ptr = 8
- for i in range(num_fat):
- cputype, cpusubtype, offset, size, align = \
- struct.unpack_from('>IIIII', data, ptr)
- ptr += 20
- # Does this match the requested bitness?
- if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64):
- continue
- macho_data = data[offset:offset+size]
- off = self._find_symbol_macho(macho_data, symbol_name)
- if off is not None:
- off += offset
- data[off:off+len(replacement)] = replacement
- replaced = True
- return replaced
- elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
- # Universal binary with 64-bit offsets.
- num_fat, = struct.unpack_from('>I', data, 4)
- replaced = False
- ptr = 8
- for i in range(num_fat):
- cputype, cpusubtype, offset, size, align = \
- struct.unpack_from('>QQQQQ', data, ptr)
- ptr += 40
- # Does this match the requested bitness?
- if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64):
- continue
- macho_data = data[offset:offset+size]
- off = self._find_symbol_macho(macho_data, symbol_name)
- if off is not None:
- off += offset
- data[off:off+len(replacement)] = replacement
- replaced = True
- return replaced
- # We don't know what kind of file this is.
- return False
- def _replace_symbol_elf(self, elf_data, symbol_name, replacement):
- """ The Linux/FreeBSD implementation of _replace_symbol. """
- replaced = False
- # Make sure we read in the correct endianness and integer size
- endian = "<>"[ord(elf_data[5:6]) - 1]
- is_64bit = ord(elf_data[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
- header_struct = endian + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[is_64bit]
- section_struct = endian + ("4xI4xIIII8xI", "4xI8xQQQI12xQ")[is_64bit]
- symbol_struct = endian + ("IIIBBH", "IBBHQQ")[is_64bit]
- header_size = struct.calcsize(header_struct)
- type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
- = struct.unpack_from(header_struct, elf_data, 16)
- section_offsets = []
- symbol_tables = []
- string_tables = {}
- # Seek to the section header table and find the symbol tables.
- ptr = shoff
- for i in range(shnum):
- type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
- ptr += shentsize
- section_offsets.append(offset - addr)
- if type == 0x0B and link != 0: # SHT_DYNSYM, links to string table
- symbol_tables.append((offset, size, link, entsize))
- string_tables[link] = None
- # Read the relevant string tables.
- for idx in list(string_tables.keys()):
- ptr = shoff + idx * shentsize
- type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
- if type == 3:
- string_tables[idx] = elf_data[offset:offset+size]
- # Loop through to find the offset of the "blobinfo" symbol.
- for offset, size, link, entsize in symbol_tables:
- entries = size // entsize
- for i in range(entries):
- ptr = offset + i * entsize
- fields = struct.unpack_from(symbol_struct, elf_data[ptr:ptr+entsize])
- if is_64bit:
- name, info, other, shndx, value, size = fields
- else:
- name, value, size, info, other, shndx = fields
- if not name:
- continue
- name = string_tables[link][name : string_tables[link].find(b'\0', name)]
- if name == symbol_name:
- if shndx == 0: # SHN_UNDEF
- continue
- elif shndx >= 0xff00 and shndx <= 0xffff:
- assert False
- else:
- # Got it. Make the replacement.
- off = section_offsets[shndx] + value
- elf_data[off:off+len(replacement)] = replacement
- replaced = True
- return replaced
- def _find_symbol_macho(self, macho_data, symbol_name):
- """ Returns the offset of the given symbol in the binary file. """
- if macho_data[:4] in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'):
- endian = '<'
- else:
- endian = '>'
- cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
- struct.unpack_from(endian + 'IIIIII', macho_data, 4)
- is_64bit = (cputype & 0x1000000) != 0
- segments = []
- cmd_ptr = 28
- nlist_struct = endian + 'IBBHI'
- if is_64bit:
- nlist_struct = endian + 'IBBHQ'
- cmd_ptr += 4
- nlist_size = struct.calcsize(nlist_struct)
- for i in range(ncmds):
- cmd, cmd_size = struct.unpack_from(endian + 'II', macho_data, cmd_ptr)
- cmd_data = macho_data[cmd_ptr+8:cmd_ptr+cmd_size]
- cmd_ptr += cmd_size
- cmd &= ~0x80000000
- if cmd == 0x01: # LC_SEGMENT
- segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
- struct.unpack_from(endian + '16sIIIIIIII', cmd_data)
- segments.append((vmaddr, vmsize, fileoff))
- elif cmd == 0x19: # LC_SEGMENT_64
- segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
- struct.unpack_from(endian + '16sQQQQIIII', cmd_data)
- segments.append((vmaddr, vmsize, fileoff))
- elif cmd == 0x2: # LC_SYMTAB
- symoff, nsyms, stroff, strsize = \
- struct.unpack_from(endian + 'IIII', cmd_data)
- strings = macho_data[stroff:stroff+strsize]
- for i in range(nsyms):
- strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff)
- symoff += nlist_size
- name = strings[strx : strings.find(b'\0', strx)]
- if name == b'_' + symbol_name:
- # Find out in which segment this is.
- for vmaddr, vmsize, fileoff in segments:
- # Is it defined in this segment?
- rel = value - vmaddr
- if rel >= 0 and rel < vmsize:
- # Yes, so return the symbol offset.
- return fileoff + rel
- print("Could not find memory address for symbol %s" % (symbol_name))
- def makeModuleDef(self, mangledName, code):
- result = ''
- result += 'static unsigned char %s[] = {' % (mangledName)
- for i in range(0, len(code), 16):
- result += '\n '
- for c in code[i:i+16]:
- if isinstance(c, int): # Python 3
- result += ('%d,' % c)
- else: # Python 2
- result += ('%d,' % ord(c))
- result += '\n};\n'
- return result
- def makeModuleListEntry(self, mangledName, code, moduleName, module):
- size = len(code)
- if getattr(module, "__path__", None):
- # Indicate package by negative size
- size = -size
- return ' {"%s", %s, %s},' % (moduleName, mangledName, size)
- def makeForbiddenModuleListEntry(self, moduleName):
- return ' {"%s", NULL, 0},' % (moduleName)
- def __writingModule(self, moduleName):
- """ Returns true if we are outputting the named module in this
- pass, false if we have already output in a previous pass, or
- if it is not yet on the output table. """
- mdef = self.modules.get(moduleName, (None, None))
- if mdef.exclude:
- return False
- if moduleName in self.previousModules:
- return False
- return True
- class PandaModuleFinder(modulefinder.ModuleFinder):
- def __init__(self, *args, **kw):
- """
- :param path: search path to look on, defaults to sys.path
- :param suffixes: defaults to imp.get_suffixes()
- :param excludes: a list of modules to exclude
- :param debug: an integer indicating the level of verbosity
- """
- self.suffixes = kw.pop('suffixes', imp.get_suffixes())
- modulefinder.ModuleFinder.__init__(self, *args, **kw)
- # Make sure we don't open a .whl/.zip file more than once.
- self._zip_files = {}
- def _open_file(self, path, mode):
- """ Opens a module at the given path, which may contain a zip file.
- Returns None if the module could not be found. """
- if os.path.isfile(path):
- if sys.version_info >= (3, 0) and 'b' not in mode:
- return open(path, mode, encoding='utf8')
- else:
- return open(path, mode)
- # Is there a zip file along the path?
- dir, dirname = os.path.split(path)
- fn = dirname
- while dirname:
- if os.path.isfile(dir):
- # Okay, this is actually a file. Is it a zip file?
- if dir in self._zip_files:
- # Yes, and we've previously opened this.
- zip = self._zip_files[dir]
- elif zipfile.is_zipfile(dir):
- zip = zipfile.ZipFile(dir)
- self._zip_files[dir] = zip
- else:
- # It's a different kind of file. Stop looking.
- return None
- try:
- fp = zip.open(fn.replace(os.path.sep, '/'), 'r')
- except KeyError:
- return None
- if sys.version_info >= (3, 0) and 'b' not in mode:
- return TextIOWrapper(fp, encoding='utf8')
- return fp
- # Look at the parent directory.
- dir, dirname = os.path.split(dir)
- fn = os.path.join(dirname, fn)
- return None
- def find_module(self, name, path=None, parent=None):
- """ Finds a module with the indicated name on the given search path
- (or self.path if None). Returns a tuple like (fp, path, stuff), where
- stuff is a tuple like (suffix, mode, type). """
- if imp.is_frozen(name):
- # Don't pick up modules that are frozen into p3dpython.
- raise ImportError("'%s' is a frozen module" % (name))
- if parent is not None:
- fullname = parent.__name__+'.'+name
- else:
- fullname = name
- if fullname in self.excludes:
- raise ImportError(name)
- # If no search path is given, look for a built-in module.
- if path is None:
- if name in sys.builtin_module_names:
- return (None, None, ('', '', imp.C_BUILTIN))
- path = self.path
- # Look for the module on the search path.
- for dir_path in path:
- basename = os.path.join(dir_path, name.split('.')[-1])
- # Look for recognized extensions.
- for stuff in self.suffixes:
- suffix, mode, _ = stuff
- fp = self._open_file(basename + suffix, mode)
- if fp:
- return (fp, basename + suffix, stuff)
- # Consider a package, i.e. a directory containing __init__.py.
- for suffix, mode, _ in self.suffixes:
- init = os.path.join(basename, '__init__' + suffix)
- if self._open_file(init, mode):
- return (None, basename, ('', '', imp.PKG_DIRECTORY))
- # It wasn't found through the normal channels. Maybe it's one of
- # ours, or maybe it's frozen?
- if not path:
- # Only if we're not looking on a particular path, though.
- if p3extend_frozen and p3extend_frozen.is_frozen_module(name):
- # It's a frozen module.
- return (None, name, ('', '', imp.PY_FROZEN))
- raise ImportError(name)
- def find_all_submodules(self, m):
- # Overridden so that we can define our own suffixes.
- if not m.__path__:
- return
- modules = {}
- for dir in m.__path__:
- try:
- names = os.listdir(dir)
- except OSError:
- self.msg(2, "can't list directory", dir)
- continue
- for name in names:
- mod = None
- for suff in self.suffixes:
- n = len(suff)
- if name[-n:] == suff:
- mod = name[:-n]
- break
- if mod and mod != "__init__":
- modules[mod] = mod
- return modules.keys()
|