FreezeTool.py 86 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267
  1. """ This module contains code to freeze a number of Python modules
  2. into a single (mostly) standalone DLL or EXE. """
  3. import modulefinder
  4. import sys
  5. import os
  6. import marshal
  7. import imp
  8. import platform
  9. import struct
  10. from io import StringIO, BytesIO, TextIOWrapper
  11. import distutils.sysconfig as sysconf
  12. import zipfile
  13. from . import pefile
  14. # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
  15. try:
  16. import p3extend_frozen
  17. except ImportError:
  18. p3extend_frozen = None
  19. from panda3d.core import *
  20. # Check to see if we are running python_d, which implies we have a
  21. # debug build, and we have to build the module with debug options.
  22. # This is only relevant on Windows.
  23. # I wonder if there's a better way to determine this?
  24. python = os.path.splitext(os.path.split(sys.executable)[1])[0]
  25. isDebugBuild = (python.lower().endswith('_d'))
  26. # These are modules that Python always tries to import up-front. They
  27. # must be frozen in any main.exe.
  28. # NB. if encodings are removed, be sure to remove them from the shortcut in
  29. # deploy-stub.c.
  30. startupModules = [
  31. 'encodings', 'encodings.aliases', 'encodings.undefined', 'encodings.ascii',
  32. 'encodings.cp1252', 'encodings.latin_1', 'encodings.utf_8',
  33. 'encodings.mbcs', 'encodings.cp850', 'encodings.cp437', 'imp',
  34. ]
  35. if sys.version_info >= (3, 0):
  36. # Modules specific to Python 3
  37. startupModules += ['io', 'marshal', 'importlib.machinery', 'importlib.util']
  38. else:
  39. # Modules specific to Python 2
  40. startupModules += ['encodings.string_escape']
  41. # These are some special init functions for some built-in Python modules that
  42. # deviate from the standard naming convention. A value of None means that a
  43. # dummy entry should be written to the inittab.
  44. builtinInitFuncs = {
  45. 'builtins': None,
  46. '__builtin__': None,
  47. 'sys': None,
  48. 'exceptions': None,
  49. '_imp': 'PyInit_imp',
  50. '_warnings': '_PyWarnings_Init',
  51. 'marshal': 'PyMarshal_Init',
  52. }
  53. # These are modules that are not found normally for these modules. Add them
  54. # to an include list so users do not have to do this manually.
  55. try:
  56. from pytest import freeze_includes as pytest_imports
  57. except ImportError:
  58. def pytest_imports():
  59. return []
  60. hiddenImports = {
  61. 'pytest': pytest_imports(),
  62. 'pkg_resources': [
  63. 'pkg_resources.*.*',
  64. # TODO why does the above not get these modules too?
  65. 'pkg_resources._vendor.packaging.*',
  66. 'pkg_resources._vendor.packaging.version',
  67. 'pkg_resources._vendor.packaging.specifiers',
  68. 'pkg_resources._vendor.packaging.requirements',
  69. ],
  70. 'xml.etree.cElementTree': ['xml.etree.ElementTree'],
  71. 'datetime': ['_strptime'],
  72. }
  73. # These are missing modules that we've reported already this session.
  74. reportedMissing = {}
  75. class CompilationEnvironment:
  76. """ Create an instance of this class to record the commands to
  77. invoke the compiler on a given platform. If needed, the caller
  78. can create a custom instance of this class (or simply set the
  79. compile strings directly) to customize the build environment. """
  80. def __init__(self, platform):
  81. self.platform = platform
  82. # The command to compile a c to an object file. Replace %(basename)s
  83. # with the basename of the source file, and an implicit .c extension.
  84. self.compileObj = 'error'
  85. # The command to link a single object file into an executable. As
  86. # above, replace $(basename)s with the basename of the original source
  87. # file, and of the target executable.
  88. self.linkExe = 'error'
  89. # The command to link a single object file into a shared library.
  90. self.linkDll = 'error'
  91. # Paths to Python stuff.
  92. self.Python = None
  93. self.PythonIPath = sysconf.get_python_inc()
  94. self.PythonVersion = sysconf.get_config_var("LDVERSION") or sysconf.get_python_version()
  95. # The VC directory of Microsoft Visual Studio (if relevant)
  96. self.MSVC = None
  97. # Directory to Windows Platform SDK (if relevant)
  98. self.PSDK = None
  99. # The setting to control release vs. debug builds. Only relevant on
  100. # Windows.
  101. self.MD = None
  102. # Added to the path to the MSVC bin and lib directories on 64-bits Windows.
  103. self.suffix64 = ''
  104. # The _d extension to add to dll filenames on Windows in debug builds.
  105. self.dllext = ''
  106. # Any architecture-specific string.
  107. self.arch = ''
  108. self.determineStandardSetup()
  109. def determineStandardSetup(self):
  110. if self.platform.startswith('win'):
  111. self.Python = sysconf.PREFIX
  112. if ('VCINSTALLDIR' in os.environ):
  113. self.MSVC = os.environ['VCINSTALLDIR']
  114. elif (Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').exists()):
  115. self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio 9.0/VC').toOsSpecific()
  116. elif (Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').exists()):
  117. self.MSVC = Filename('/c/Program Files (x86)/Microsoft Visual Studio 9.0/VC').toOsSpecific()
  118. elif (Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').exists()):
  119. self.MSVC = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').toOsSpecific()
  120. else:
  121. print('Could not locate Microsoft Visual C++ Compiler! Try running from the Visual Studio Command Prompt.')
  122. sys.exit(1)
  123. if ('WindowsSdkDir' in os.environ):
  124. self.PSDK = os.environ['WindowsSdkDir']
  125. elif (platform.architecture()[0] == '32bit' and Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').exists()):
  126. self.PSDK = Filename('/c/Program Files/Microsoft Platform SDK for Windows Server 2003 R2').toOsSpecific()
  127. elif (os.path.exists(os.path.join(self.MSVC, 'PlatformSDK'))):
  128. self.PSDK = os.path.join(self.MSVC, 'PlatformSDK')
  129. else:
  130. print('Could not locate the Microsoft Windows Platform SDK! Try running from the Visual Studio Command Prompt.')
  131. sys.exit(1)
  132. # We need to use the correct compiler setting for debug vs. release builds.
  133. self.MD = '/MD'
  134. if isDebugBuild:
  135. self.MD = '/MDd'
  136. self.dllext = '_d'
  137. # MSVC/bin and /lib directories have a different location
  138. # for win64.
  139. if self.platform == 'win_amd64':
  140. self.suffix64 = '\\amd64'
  141. # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
  142. if ('MAKEPANDA' in os.environ):
  143. self.compileObjExe = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c %(MD)s /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(pythonIPath)s" %(filename)s'
  144. self.compileObjDll = self.compileObjExe
  145. 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'
  146. 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'
  147. else:
  148. os.environ['PATH'] += ';' + self.MSVC + '\\bin' + self.suffix64 + ';' + self.MSVC + '\\Common7\\IDE;' + self.PSDK + '\\bin'
  149. 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'
  150. self.compileObjDll = self.compileObjExe
  151. 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'
  152. 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'
  153. elif self.platform.startswith('osx_'):
  154. # OSX
  155. proc = self.platform.split('_', 1)[1]
  156. if proc == 'i386':
  157. self.arch = '-arch i386'
  158. elif proc == 'ppc':
  159. self.arch = '-arch ppc'
  160. elif proc == 'amd64':
  161. self.arch = '-arch x86_64'
  162. self.compileObjExe = "gcc -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
  163. self.compileObjDll = "gcc -fPIC -c %(arch)s -o %(basename)s.o -O2 -I%(pythonIPath)s %(filename)s"
  164. self.linkExe = "gcc %(arch)s -o %(basename)s %(basename)s.o -framework Python"
  165. self.linkDll = "gcc %(arch)s -undefined dynamic_lookup -bundle -o %(basename)s.so %(basename)s.o"
  166. else:
  167. # Unix
  168. lib_dir = sysconf.get_python_lib(plat_specific=1, standard_lib=1)
  169. #python_a = os.path.join(lib_dir, "config", "libpython%(pythonVersion)s.a")
  170. self.compileObjExe = "%(CC)s %(CFLAGS)s -c -o %(basename)s.o -pthread -O2 %(filename)s -I%(pythonIPath)s"
  171. self.compileObjDll = "%(CC)s %(CFLAGS)s %(CCSHARED)s -c -o %(basename)s.o -O2 %(filename)s -I%(pythonIPath)s"
  172. self.linkExe = "%(CC)s -o %(basename)s %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
  173. self.linkDll = "%(LDSHARED)s -o %(basename)s.so %(basename)s.o -L/usr/local/lib -lpython%(pythonVersion)s"
  174. if (os.path.isdir("/usr/PCBSD/local/lib")):
  175. self.linkExe += " -L/usr/PCBSD/local/lib"
  176. self.linkDll += " -L/usr/PCBSD/local/lib"
  177. def compileExe(self, filename, basename, extraLink=[]):
  178. compile = self.compileObjExe % dict({
  179. 'python' : self.Python,
  180. 'MSVC' : self.MSVC,
  181. 'PSDK' : self.PSDK,
  182. 'suffix64' : self.suffix64,
  183. 'MD' : self.MD,
  184. 'pythonIPath' : self.PythonIPath,
  185. 'pythonVersion' : self.PythonVersion,
  186. 'arch' : self.arch,
  187. 'filename' : filename,
  188. 'basename' : basename,
  189. }, **sysconf.get_config_vars())
  190. sys.stderr.write(compile + '\n')
  191. if os.system(compile) != 0:
  192. raise Exception('failed to compile %s.' % basename)
  193. link = self.linkExe % dict({
  194. 'python' : self.Python,
  195. 'MSVC' : self.MSVC,
  196. 'PSDK' : self.PSDK,
  197. 'suffix64' : self.suffix64,
  198. 'pythonIPath' : self.PythonIPath,
  199. 'pythonVersion' : self.PythonVersion,
  200. 'arch' : self.arch,
  201. 'filename' : filename,
  202. 'basename' : basename,
  203. }, **sysconf.get_config_vars())
  204. link += ' ' + ' '.join(extraLink)
  205. sys.stderr.write(link + '\n')
  206. if os.system(link) != 0:
  207. raise Exception('failed to link %s.' % basename)
  208. def compileDll(self, filename, basename, extraLink=[]):
  209. compile = self.compileObjDll % dict({
  210. 'python' : self.Python,
  211. 'MSVC' : self.MSVC,
  212. 'PSDK' : self.PSDK,
  213. 'suffix64' : self.suffix64,
  214. 'MD' : self.MD,
  215. 'pythonIPath' : self.PythonIPath,
  216. 'pythonVersion' : self.PythonVersion,
  217. 'arch' : self.arch,
  218. 'filename' : filename,
  219. 'basename' : basename,
  220. }, **sysconf.get_config_vars())
  221. sys.stderr.write(compile + '\n')
  222. if os.system(compile) != 0:
  223. raise Exception('failed to compile %s.' % basename)
  224. link = self.linkDll % dict({
  225. 'python' : self.Python,
  226. 'MSVC' : self.MSVC,
  227. 'PSDK' : self.PSDK,
  228. 'suffix64' : self.suffix64,
  229. 'pythonIPath' : self.PythonIPath,
  230. 'pythonVersion' : self.PythonVersion,
  231. 'arch' : self.arch,
  232. 'filename' : filename,
  233. 'basename' : basename,
  234. 'dllext' : self.dllext,
  235. }, **sysconf.get_config_vars())
  236. link += ' ' + ' '.join(extraLink)
  237. sys.stderr.write(link + '\n')
  238. if os.system(link) != 0:
  239. raise Exception('failed to link %s.' % basename)
  240. # The code from frozenmain.c in the Python source repository.
  241. frozenMainCode = """
  242. /* Python interpreter main program for frozen scripts */
  243. #include "Python.h"
  244. #if PY_MAJOR_VERSION >= 3
  245. #include <locale.h>
  246. #if PY_MINOR_VERSION < 5
  247. #define Py_DecodeLocale _Py_char2wchar
  248. #endif
  249. #endif
  250. #ifdef MS_WINDOWS
  251. extern void PyWinFreeze_ExeInit(void);
  252. extern void PyWinFreeze_ExeTerm(void);
  253. extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
  254. #endif
  255. /* Main program */
  256. int
  257. Py_FrozenMain(int argc, char **argv)
  258. {
  259. char *p;
  260. int n, sts = 1;
  261. int inspect = 0;
  262. int unbuffered = 0;
  263. #if PY_MAJOR_VERSION >= 3
  264. int i;
  265. char *oldloc;
  266. wchar_t **argv_copy = NULL;
  267. /* We need a second copies, as Python might modify the first one. */
  268. wchar_t **argv_copy2 = NULL;
  269. if (argc > 0) {
  270. argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
  271. argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
  272. }
  273. #endif
  274. Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
  275. Py_NoSiteFlag = 1;
  276. Py_NoUserSiteDirectory = 1;
  277. if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\\0')
  278. inspect = 1;
  279. if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\\0')
  280. unbuffered = 1;
  281. if (unbuffered) {
  282. setbuf(stdin, (char *)NULL);
  283. setbuf(stdout, (char *)NULL);
  284. setbuf(stderr, (char *)NULL);
  285. }
  286. #if PY_MAJOR_VERSION >= 3
  287. oldloc = setlocale(LC_ALL, NULL);
  288. setlocale(LC_ALL, \"\");
  289. for (i = 0; i < argc; i++) {
  290. argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
  291. argv_copy2[i] = argv_copy[i];
  292. if (!argv_copy[i]) {
  293. fprintf(stderr, \"Unable to decode the command line argument #%i\\n\",
  294. i + 1);
  295. argc = i;
  296. goto error;
  297. }
  298. }
  299. setlocale(LC_ALL, oldloc);
  300. #endif
  301. #ifdef MS_WINDOWS
  302. PyImport_ExtendInittab(extensions);
  303. #endif /* MS_WINDOWS */
  304. if (argc >= 1) {
  305. #if PY_MAJOR_VERSION >= 3
  306. Py_SetProgramName(argv_copy[0]);
  307. #else
  308. Py_SetProgramName(argv[0]);
  309. #endif
  310. }
  311. Py_Initialize();
  312. #ifdef MS_WINDOWS
  313. PyWinFreeze_ExeInit();
  314. #endif
  315. if (Py_VerboseFlag)
  316. fprintf(stderr, "Python %s\\n%s\\n",
  317. Py_GetVersion(), Py_GetCopyright());
  318. #if PY_MAJOR_VERSION >= 3
  319. PySys_SetArgv(argc, argv_copy);
  320. #else
  321. PySys_SetArgv(argc, argv);
  322. #endif
  323. n = PyImport_ImportFrozenModule("__main__");
  324. if (n == 0)
  325. Py_FatalError("__main__ not frozen");
  326. if (n < 0) {
  327. PyErr_Print();
  328. sts = 1;
  329. }
  330. else
  331. sts = 0;
  332. if (inspect && isatty((int)fileno(stdin)))
  333. sts = PyRun_AnyFile(stdin, "<stdin>") != 0;
  334. #ifdef MS_WINDOWS
  335. PyWinFreeze_ExeTerm();
  336. #endif
  337. Py_Finalize();
  338. #if PY_MAJOR_VERSION >= 3
  339. error:
  340. if (argv_copy2) {
  341. for (i = 0; i < argc; i++) {
  342. #if PY_MINOR_VERSION >= 4
  343. PyMem_RawFree(argv_copy2[i]);
  344. #else
  345. PyMem_Free(argv_copy2[i]);
  346. #endif
  347. }
  348. }
  349. #endif
  350. return sts;
  351. }
  352. """
  353. # The code from frozen_dllmain.c in the Python source repository.
  354. # Windows only.
  355. frozenDllMainCode = """
  356. #include "windows.h"
  357. static char *possibleModules[] = {
  358. "pywintypes",
  359. "pythoncom",
  360. "win32ui",
  361. NULL,
  362. };
  363. BOOL CallModuleDllMain(char *modName, DWORD dwReason);
  364. /*
  365. Called by a frozen .EXE only, so that built-in extension
  366. modules are initialized correctly
  367. */
  368. void PyWinFreeze_ExeInit(void)
  369. {
  370. char **modName;
  371. for (modName = possibleModules;*modName;*modName++) {
  372. /* printf("Initialising '%s'\\n", *modName); */
  373. CallModuleDllMain(*modName, DLL_PROCESS_ATTACH);
  374. }
  375. }
  376. /*
  377. Called by a frozen .EXE only, so that built-in extension
  378. modules are cleaned up
  379. */
  380. void PyWinFreeze_ExeTerm(void)
  381. {
  382. // Must go backwards
  383. char **modName;
  384. for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
  385. modName >= possibleModules;
  386. *modName--) {
  387. /* printf("Terminating '%s'\\n", *modName);*/
  388. CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
  389. }
  390. }
  391. BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
  392. {
  393. BOOL ret = TRUE;
  394. switch (dwReason) {
  395. case DLL_PROCESS_ATTACH:
  396. {
  397. char **modName;
  398. for (modName = possibleModules;*modName;*modName++) {
  399. BOOL ok = CallModuleDllMain(*modName, dwReason);
  400. if (!ok)
  401. ret = FALSE;
  402. }
  403. break;
  404. }
  405. case DLL_PROCESS_DETACH:
  406. {
  407. // Must go backwards
  408. char **modName;
  409. for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
  410. modName >= possibleModules;
  411. *modName--)
  412. CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
  413. break;
  414. }
  415. }
  416. return ret;
  417. }
  418. BOOL CallModuleDllMain(char *modName, DWORD dwReason)
  419. {
  420. BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID);
  421. char funcName[255];
  422. HMODULE hmod = GetModuleHandle(NULL);
  423. strcpy(funcName, "_DllMain");
  424. strcat(funcName, modName);
  425. strcat(funcName, "@12"); // stdcall convention.
  426. pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName);
  427. if (pfndllmain==NULL) {
  428. /* No function by that name exported - then that module does
  429. not appear in our frozen program - return OK
  430. */
  431. return TRUE;
  432. }
  433. return (*pfndllmain)(hmod, dwReason, NULL);
  434. }
  435. """
  436. # Our own glue code to start up a Python executable.
  437. mainInitCode = """
  438. %(frozenMainCode)s
  439. int
  440. main(int argc, char *argv[]) {
  441. PyImport_FrozenModules = _PyImport_FrozenModules;
  442. return Py_FrozenMain(argc, argv);
  443. }
  444. """
  445. # Our own glue code to start up a Python shared library.
  446. dllInitCode = """
  447. /*
  448. * Call this function to extend the frozen modules array with a new
  449. * array of frozen modules, provided in a C-style array, at runtime.
  450. * Returns the total number of frozen modules.
  451. */
  452. static int
  453. extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
  454. int orig_count;
  455. struct _frozen *realloc_FrozenModules;
  456. /* First, count the number of frozen modules we had originally. */
  457. orig_count = 0;
  458. while (PyImport_FrozenModules[orig_count].name != NULL) {
  459. ++orig_count;
  460. }
  461. if (new_count == 0) {
  462. /* Trivial no-op. */
  463. return orig_count;
  464. }
  465. /* Reallocate the PyImport_FrozenModules array bigger to make room
  466. for the additional frozen modules. We just leak the original
  467. array; it's too risky to try to free it. */
  468. realloc_FrozenModules = (struct _frozen *)malloc((orig_count + new_count + 1) * sizeof(struct _frozen));
  469. /* The new frozen modules go at the front of the list. */
  470. memcpy(realloc_FrozenModules, new_modules, new_count * sizeof(struct _frozen));
  471. /* Then the original set of frozen modules. */
  472. memcpy(realloc_FrozenModules + new_count, PyImport_FrozenModules, orig_count * sizeof(struct _frozen));
  473. /* Finally, a single 0-valued entry marks the end of the array. */
  474. memset(realloc_FrozenModules + orig_count + new_count, 0, sizeof(struct _frozen));
  475. /* Assign the new pointer. */
  476. PyImport_FrozenModules = realloc_FrozenModules;
  477. return orig_count + new_count;
  478. }
  479. #if PY_MAJOR_VERSION >= 3
  480. static PyModuleDef mdef = {
  481. PyModuleDef_HEAD_INIT,
  482. "%(moduleName)s",
  483. "",
  484. -1,
  485. NULL, NULL, NULL, NULL, NULL
  486. };
  487. %(dllexport)sPyObject *PyInit_%(moduleName)s(void) {
  488. extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
  489. return PyModule_Create(&mdef);
  490. }
  491. #else
  492. static PyMethodDef nullMethods[] = {
  493. {NULL, NULL}
  494. };
  495. %(dllexport)svoid init%(moduleName)s(void) {
  496. extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
  497. Py_InitModule("%(moduleName)s", nullMethods);
  498. }
  499. #endif
  500. """
  501. programFile = """
  502. #include "Python.h"
  503. #ifdef _WIN32
  504. #include "malloc.h"
  505. #endif
  506. %(moduleDefs)s
  507. struct _frozen _PyImport_FrozenModules[] = {
  508. %(moduleList)s
  509. {NULL, NULL, 0}
  510. };
  511. """
  512. okMissing = [
  513. '__main__', '_dummy_threading', 'Carbon', 'Carbon.Files',
  514. 'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
  515. 'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
  516. 'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
  517. 'riscospath', 'dbm', 'fcntl', 'win32api', 'win32pipe', 'usercustomize',
  518. '_winreg', 'winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
  519. 'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios', 'vms_lib',
  520. 'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
  521. 'email.Iterators', '_subprocess', 'gestalt', 'java.lang',
  522. 'direct.extensions_native.extensions_darwin',
  523. ]
  524. class Freezer:
  525. class ModuleDef:
  526. def __init__(self, moduleName, filename = None,
  527. implicit = False, guess = False,
  528. exclude = False, forbid = False,
  529. allowChildren = False, fromSource = None,
  530. text = None):
  531. # The Python module name.
  532. self.moduleName = moduleName
  533. # The file on disk it was loaded from, if any.
  534. self.filename = filename
  535. if filename is not None and not isinstance(filename, Filename):
  536. self.filename = Filename(filename)
  537. # True if the module was found via the modulefinder.
  538. self.implicit = implicit
  539. # True if the moduleName might refer to some Python object
  540. # other than a module, in which case the module should be
  541. # ignored.
  542. self.guess = guess
  543. # True if the module should *not* be included in the
  544. # generated output.
  545. self.exclude = exclude
  546. # True if the module should never be allowed, even if it
  547. # exists at runtime.
  548. self.forbid = forbid
  549. # True if excluding the module still allows its children
  550. # to be included. This only makes sense if the module
  551. # will exist at runtime through some other means
  552. # (e.g. from another package).
  553. self.allowChildren = allowChildren
  554. # Additional black-box information about where this module
  555. # record came from, supplied by the caller.
  556. self.fromSource = fromSource
  557. # If this is set, it contains Python code of the module.
  558. self.text = text
  559. # Some sanity checks.
  560. if not self.exclude:
  561. self.allowChildren = True
  562. if self.forbid:
  563. self.exclude = True
  564. self.allowChildren = False
  565. def __repr__(self):
  566. args = [repr(self.moduleName), repr(self.filename)]
  567. if self.implicit:
  568. args.append('implicit = True')
  569. if self.guess:
  570. args.append('guess = True')
  571. if self.exclude:
  572. args.append('exclude = True')
  573. if self.forbid:
  574. args.append('forbid = True')
  575. if self.allowChildren:
  576. args.append('allowChildren = True')
  577. return 'ModuleDef(%s)' % (', '.join(args))
  578. def __init__(self, previous = None, debugLevel = 0,
  579. platform = None, path=None):
  580. # Normally, we are freezing for our own platform. Change this
  581. # if untrue.
  582. self.platform = platform or PandaSystem.getPlatform()
  583. # This is the compilation environment. Fill in your own
  584. # object here if you have custom needs (for instance, for a
  585. # cross-compiler or something). If this is None, then a
  586. # default object will be created when it is needed.
  587. self.cenv = None
  588. # This is the search path to use for Python modules. Leave it
  589. # to the default value of None to use sys.path.
  590. self.path = path
  591. # The filename extension to append to the source file before
  592. # compiling.
  593. self.sourceExtension = '.c'
  594. # The filename extension to append to the object file.
  595. self.objectExtension = '.o'
  596. if self.platform.startswith('win'):
  597. self.objectExtension = '.obj'
  598. self.keepTemporaryFiles = False
  599. # Change any of these to change the generated startup and glue
  600. # code.
  601. self.frozenMainCode = frozenMainCode
  602. self.frozenDllMainCode = frozenDllMainCode
  603. self.mainInitCode = mainInitCode
  604. # Set this true to encode Python files in a Multifile as their
  605. # original source if possible, or false to encode them as
  606. # compiled pyc or pyo files. This has no effect on frozen exe
  607. # or dll's; those are always stored with compiled code.
  608. self.storePythonSource = False
  609. # This list will be filled in by generateCode() or
  610. # addToMultifile(). It contains a list of all the extension
  611. # modules that were discovered, which have not been added to
  612. # the output. The list is a list of tuples of the form
  613. # (moduleName, filename). filename will be None for built-in
  614. # modules.
  615. self.extras = []
  616. # Set this to true if extension modules should be linked in to
  617. # the resulting executable.
  618. self.linkExtensionModules = False
  619. # End of public interface. These remaining members should not
  620. # be directly manipulated by callers.
  621. self.previousModules = {}
  622. self.modules = {}
  623. if previous:
  624. self.previousModules = dict(previous.modules)
  625. self.modules = dict(previous.modules)
  626. # Exclude doctest by default; it is not very useful in production
  627. # builds. It can be explicitly included if desired.
  628. self.modules['doctest'] = self.ModuleDef('doctest', exclude = True)
  629. self.mf = None
  630. # Actually, make sure we know how to find all of the
  631. # already-imported modules. (Some of them might do their own
  632. # special path mangling.)
  633. for moduleName, module in list(sys.modules.items()):
  634. if module and hasattr(module, '__path__'):
  635. path = list(getattr(module, '__path__'))
  636. if path:
  637. modulefinder.AddPackagePath(moduleName, path[0])
  638. # Suffix/extension for Python C extension modules
  639. if self.platform == PandaSystem.getPlatform():
  640. self.moduleSuffixes = imp.get_suffixes()
  641. else:
  642. self.moduleSuffixes = [('.py', 'r', 1), ('.pyc', 'rb', 2)]
  643. if 'linux' in self.platform:
  644. self.moduleSuffixes += [
  645. ('.cpython-{0}{1}m-x86_64-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
  646. ('.cpython-{0}{1}m-i686-linux-gnu.so'.format(*sys.version_info), 'rb', 3),
  647. ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
  648. ('.so', 'rb', 3),
  649. ]
  650. elif 'win' in self.platform:
  651. self.moduleSuffixes += [
  652. ('.cp{0}{1}-win_amd64.pyd'.format(*sys.version_info), 'rb', 3),
  653. ('.cp{0}{1}-win32.pyd'.format(*sys.version_info), 'rb', 3),
  654. ('.pyd', 'rb', 3),
  655. ]
  656. elif 'mac' in self.platform:
  657. self.moduleSuffixes += [
  658. ('.cpython-{0}{1}m-darwin.so'.format(*sys.version_info), 'rb', 3),
  659. ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
  660. ('.so', 'rb', 3),
  661. ]
  662. else: # FreeBSD et al.
  663. self.moduleSuffixes += [
  664. ('.cpython-{0}{1}m.so'.format(*sys.version_info), 'rb', 3),
  665. ('.abi{0}.so'.format(*sys.version_info), 'rb', 3),
  666. ('.so', 'rb', 3),
  667. ]
  668. def excludeFrom(self, freezer):
  669. """ Excludes all modules that have already been processed by
  670. the indicated FreezeTool. This is equivalent to passing the
  671. indicated FreezeTool object as previous to this object's
  672. constructor, but it may be called at any point during
  673. processing. """
  674. for key, value in list(freezer.modules.items()):
  675. self.previousModules[key] = value
  676. self.modules[key] = value
  677. def excludeModule(self, moduleName, forbid = False, allowChildren = False,
  678. fromSource = None):
  679. """ Adds a module to the list of modules not to be exported by
  680. this tool. If forbid is true, the module is furthermore
  681. forbidden to be imported, even if it exists on disk. If
  682. allowChildren is true, the children of the indicated module
  683. may still be included."""
  684. assert self.mf == None
  685. self.modules[moduleName] = self.ModuleDef(
  686. moduleName, exclude = True,
  687. forbid = forbid, allowChildren = allowChildren,
  688. fromSource = fromSource)
  689. def handleCustomPath(self, moduleName):
  690. """ Indicates a module that may perform runtime manipulation
  691. of its __path__ variable, and which must therefore be actually
  692. imported at runtime in order to determine the true value of
  693. __path__. """
  694. str = 'import %s' % (moduleName)
  695. exec(str)
  696. module = sys.modules[moduleName]
  697. for path in module.__path__:
  698. modulefinder.AddPackagePath(moduleName, path)
  699. def getModulePath(self, moduleName):
  700. """ Looks for the indicated directory module and returns the
  701. __path__ member: the list of directories in which its python
  702. files can be found. If the module is a .py file and not a
  703. directory, returns None. """
  704. # First, try to import the module directly. That's the most
  705. # reliable answer, if it works.
  706. try:
  707. module = __import__(moduleName)
  708. except:
  709. print("couldn't import %s" % (moduleName))
  710. module = None
  711. if module != None:
  712. for symbol in moduleName.split('.')[1:]:
  713. module = getattr(module, symbol)
  714. if hasattr(module, '__path__'):
  715. return module.__path__
  716. # If it didn't work--maybe the module is unimportable because
  717. # it makes certain assumptions about the builtins, or
  718. # whatever--then just look for file on disk. That's usually
  719. # good enough.
  720. path = None
  721. baseName = moduleName
  722. if '.' in baseName:
  723. parentName, baseName = moduleName.rsplit('.', 1)
  724. path = self.getModulePath(parentName)
  725. if path == None:
  726. return None
  727. try:
  728. file, pathname, description = imp.find_module(baseName, path)
  729. except ImportError:
  730. return None
  731. if not os.path.isdir(pathname):
  732. return None
  733. return [pathname]
  734. def getModuleStar(self, moduleName):
  735. """ Looks for the indicated directory module and returns the
  736. __all__ member: the list of symbols within the module. """
  737. # First, try to import the module directly. That's the most
  738. # reliable answer, if it works.
  739. try:
  740. module = __import__(moduleName)
  741. except:
  742. print("couldn't import %s" % (moduleName))
  743. module = None
  744. if module != None:
  745. for symbol in moduleName.split('.')[1:]:
  746. module = getattr(module, symbol)
  747. if hasattr(module, '__all__'):
  748. return module.__all__
  749. # If it didn't work, just open the directory and scan for *.py
  750. # files.
  751. path = None
  752. baseName = moduleName
  753. if '.' in baseName:
  754. parentName, baseName = moduleName.rsplit('.', 1)
  755. path = self.getModulePath(parentName)
  756. if path == None:
  757. return None
  758. try:
  759. file, pathname, description = imp.find_module(baseName, path)
  760. except ImportError:
  761. return None
  762. if not os.path.isdir(pathname):
  763. return None
  764. # Scan the directory, looking for .py files.
  765. modules = []
  766. for basename in os.listdir(pathname):
  767. if basename.endswith('.py') and basename != '__init__.py':
  768. modules.append(basename[:-3])
  769. return modules
  770. def addModule(self, moduleName, implicit = False, newName = None,
  771. filename = None, guess = False, fromSource = None,
  772. text = None):
  773. """ Adds a module to the list of modules to be exported by
  774. this tool. If implicit is true, it is OK if the module does
  775. not actually exist.
  776. newName is the name to call the module when it appears in the
  777. output. The default is the same name it had in the original.
  778. Use caution when renaming a module; if another module imports
  779. this module by its original name, you will also need to
  780. explicitly add the module under its original name, duplicating
  781. the module twice in the output.
  782. The module name may end in ".*", which means to add all of the
  783. .py files (other than __init__.py) in a particular directory.
  784. It may also end in ".*.*", which means to cycle through all
  785. directories within a particular directory.
  786. """
  787. assert self.mf == None
  788. if not newName:
  789. newName = moduleName
  790. if moduleName.endswith('.*'):
  791. assert(newName.endswith('.*'))
  792. # Find the parent module, so we can get its directory.
  793. parentName = moduleName[:-2]
  794. newParentName = newName[:-2]
  795. parentNames = [(parentName, newParentName)]
  796. if parentName.endswith('.*'):
  797. assert(newParentName.endswith('.*'))
  798. # Another special case. The parent name "*" means to
  799. # return all possible directories within a particular
  800. # directory.
  801. topName = parentName[:-2]
  802. newTopName = newParentName[:-2]
  803. parentNames = []
  804. modulePath = self.getModulePath(topName)
  805. if modulePath:
  806. for dirname in modulePath:
  807. for basename in os.listdir(dirname):
  808. if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
  809. parentName = '%s.%s' % (topName, basename)
  810. newParentName = '%s.%s' % (newTopName, basename)
  811. if self.getModulePath(parentName):
  812. parentNames.append((parentName, newParentName))
  813. for parentName, newParentName in parentNames:
  814. modules = self.getModuleStar(parentName)
  815. if modules == None:
  816. # It's actually a regular module.
  817. self.modules[newParentName] = self.ModuleDef(
  818. parentName, implicit = implicit, guess = guess,
  819. fromSource = fromSource, text = text)
  820. else:
  821. # Now get all the py files in the parent directory.
  822. for basename in modules:
  823. moduleName = '%s.%s' % (parentName, basename)
  824. newName = '%s.%s' % (newParentName, basename)
  825. mdef = self.ModuleDef(
  826. moduleName, implicit = implicit, guess = True,
  827. fromSource = fromSource)
  828. self.modules[newName] = mdef
  829. else:
  830. # A normal, explicit module name.
  831. self.modules[newName] = self.ModuleDef(
  832. moduleName, filename = filename, implicit = implicit,
  833. guess = guess, fromSource = fromSource, text = text)
  834. def done(self, addStartupModules = False):
  835. """ Call this method after you have added all modules with
  836. addModule(). You may then call generateCode() or
  837. writeMultifile() to dump the resulting output. After a call
  838. to done(), you may not add any more modules until you call
  839. reset(). """
  840. assert self.mf == None
  841. # If we are building an exe, we also need to implicitly
  842. # bring in Python's startup modules.
  843. if addStartupModules:
  844. self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
  845. self.modules['_frozen_importlib_external'] = self.ModuleDef('importlib._bootstrap_external', implicit = True)
  846. for moduleName in startupModules:
  847. if moduleName not in self.modules:
  848. self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True)
  849. # Excluding a parent module also excludes all its
  850. # (non-explicit) children, unless the parent has allowChildren
  851. # set.
  852. # Walk through the list in sorted order, so we reach parents
  853. # before children.
  854. names = list(self.modules.items())
  855. names.sort()
  856. excludeDict = {}
  857. implicitParentDict = {}
  858. includes = []
  859. autoIncludes = []
  860. origToNewName = {}
  861. for newName, mdef in names:
  862. moduleName = mdef.moduleName
  863. origToNewName[moduleName] = newName
  864. if mdef.implicit and '.' in newName:
  865. # For implicit modules, check if the parent is excluded.
  866. parentName, baseName = newName.rsplit('.', 1)
  867. if parentName in excludeDict :
  868. mdef = excludeDict[parentName]
  869. if mdef.exclude:
  870. if not mdef.allowChildren:
  871. excludeDict[moduleName] = mdef
  872. elif mdef.implicit or mdef.guess:
  873. autoIncludes.append(mdef)
  874. else:
  875. includes.append(mdef)
  876. self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path)
  877. # Attempt to import the explicit modules into the modulefinder.
  878. # First, ensure the includes are sorted in order so that
  879. # packages appear before the modules they contain. This
  880. # resolves potential ordering issues, especially with modules
  881. # that are discovered by filename rather than through import
  882. # statements.
  883. includes.sort(key = self.__sortModuleKey)
  884. # Now walk through the list and import them all.
  885. for mdef in includes:
  886. try:
  887. self.__loadModule(mdef)
  888. except ImportError as ex:
  889. message = "Unknown module: %s" % (mdef.moduleName)
  890. if str(ex) != "No module named " + str(mdef.moduleName):
  891. message += " (%s)" % (ex)
  892. print(message)
  893. # Also attempt to import any implicit modules. If any of
  894. # these fail to import, we don't really care.
  895. for mdef in autoIncludes:
  896. try:
  897. self.__loadModule(mdef)
  898. # Since it successfully loaded, it's no longer a guess.
  899. mdef.guess = False
  900. except:
  901. # Something went wrong, guess it's not an importable
  902. # module.
  903. pass
  904. # Check if any new modules we found have "hidden" imports
  905. for origName in list(self.mf.modules.keys()):
  906. hidden = hiddenImports.get(origName, [])
  907. for modname in hidden:
  908. self.__loadModule(self.ModuleDef(modname, implicit = True))
  909. # Now, any new modules we found get added to the export list.
  910. for origName in list(self.mf.modules.keys()):
  911. if origName not in origToNewName:
  912. self.modules[origName] = self.ModuleDef(origName, implicit = True)
  913. missing = []
  914. for origName in self.mf.any_missing_maybe()[0]:
  915. if origName in startupModules:
  916. continue
  917. if origName in self.previousModules:
  918. continue
  919. if origName in self.modules:
  920. continue
  921. # This module is missing. Let it be missing in the
  922. # runtime also.
  923. self.modules[origName] = self.ModuleDef(origName, exclude = True,
  924. implicit = True)
  925. if origName in okMissing:
  926. # If it's listed in okMissing, don't even report it.
  927. continue
  928. prefix = origName.split('.')[0]
  929. if origName not in reportedMissing:
  930. missing.append(origName)
  931. reportedMissing[origName] = True
  932. if missing:
  933. missing.sort()
  934. print("There are some missing modules: %r" % missing)
  935. def __sortModuleKey(self, mdef):
  936. """ A sort key function to sort a list of mdef's into order,
  937. primarily to ensure that packages proceed their modules. """
  938. if mdef.moduleName:
  939. # If we have a moduleName, the key consists of the split
  940. # tuple of packages names. That way, parents always sort
  941. # before children.
  942. return ('a', mdef.moduleName.split('.'))
  943. else:
  944. # If we don't have a moduleName, the key doesn't really
  945. # matter--we use filename--but we start with 'b' to ensure
  946. # that all of non-named modules appear following all of
  947. # the named modules.
  948. return ('b', mdef.filename)
  949. def __loadModule(self, mdef):
  950. """ Adds the indicated module to the modulefinder. """
  951. if mdef.filename:
  952. # If it has a filename, then we found it as a file on
  953. # disk. In this case, the moduleName may not be accurate
  954. # and useful, so load it as a file instead.
  955. tempPath = None
  956. if '.' not in mdef.moduleName:
  957. # If we loaded a python file from the root, we need to
  958. # temporarily add its directory to the module search
  959. # path, so the modulefinder can find any sibling
  960. # python files it imports as well.
  961. tempPath = Filename(mdef.filename.getDirname()).toOsSpecific()
  962. self.mf.path.append(tempPath)
  963. pathname = mdef.filename.toOsSpecific()
  964. ext = mdef.filename.getExtension()
  965. if ext == 'pyc' or ext == 'pyo':
  966. fp = open(pathname, 'rb')
  967. stuff = ("", "rb", imp.PY_COMPILED)
  968. self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
  969. else:
  970. if mdef.text:
  971. fp = StringIO(mdef.text)
  972. else:
  973. fp = open(pathname, 'U')
  974. stuff = ("", "r", imp.PY_SOURCE)
  975. self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
  976. if tempPath:
  977. del self.mf.path[-1]
  978. else:
  979. # Otherwise, we can just import it normally.
  980. self.mf.import_hook(mdef.moduleName)
  981. def reset(self):
  982. """ After a previous call to done(), this resets the
  983. FreezeTool object for a new pass. More modules may be added
  984. and dumped to a new target. Previously-added modules are
  985. remembered and will not be dumped again. """
  986. self.mf = None
  987. self.previousModules = dict(self.modules)
  988. def mangleName(self, moduleName):
  989. return 'M_' + moduleName.replace('.', '__').replace('-', '_')
  990. def getAllModuleNames(self):
  991. """ Return a list of all module names that have been included
  992. or forbidden, either in this current pass or in a previous
  993. pass. Module names that have been excluded are not included
  994. in this list. """
  995. moduleNames = []
  996. for newName, mdef in list(self.modules.items()):
  997. if mdef.guess:
  998. # Not really a module.
  999. pass
  1000. elif mdef.exclude and not mdef.forbid:
  1001. # An excluded (but not forbidden) file.
  1002. pass
  1003. else:
  1004. moduleNames.append(newName)
  1005. moduleNames.sort()
  1006. return moduleNames
  1007. def getModuleDefs(self):
  1008. """ Return a list of all of the modules we will be explicitly
  1009. or implicitly including. The return value is actually a list
  1010. of tuples: (moduleName, moduleDef)."""
  1011. moduleDefs = []
  1012. for newName, mdef in list(self.modules.items()):
  1013. prev = self.previousModules.get(newName, None)
  1014. if not mdef.exclude:
  1015. # Include this module (even if a previous pass
  1016. # excluded it). But don't bother if we exported it
  1017. # previously.
  1018. if prev and not prev.exclude:
  1019. # Previously exported.
  1020. pass
  1021. elif mdef.moduleName in self.mf.modules or \
  1022. mdef.moduleName in startupModules or \
  1023. mdef.filename:
  1024. moduleDefs.append((newName, mdef))
  1025. elif mdef.forbid:
  1026. if not prev or not prev.forbid:
  1027. moduleDefs.append((newName, mdef))
  1028. moduleDefs.sort()
  1029. return moduleDefs
  1030. def __replacePaths(self):
  1031. # Build up the replacement pathname table, so we can eliminate
  1032. # the personal information in the frozen pathnames. The
  1033. # actual filename we put in there is meaningful only for stack
  1034. # traces, so we'll just use the module name.
  1035. replace_paths = []
  1036. for moduleName, module in list(self.mf.modules.items()):
  1037. if module.__code__:
  1038. origPathname = module.__code__.co_filename
  1039. replace_paths.append((origPathname, moduleName))
  1040. self.mf.replace_paths = replace_paths
  1041. # Now that we have built up the replacement mapping, go back
  1042. # through and actually replace the paths.
  1043. for moduleName, module in list(self.mf.modules.items()):
  1044. if module.__code__:
  1045. co = self.mf.replace_paths_in_code(module.__code__)
  1046. module.__code__ = co;
  1047. def __addPyc(self, multifile, filename, code, compressionLevel):
  1048. if code:
  1049. data = imp.get_magic() + b'\0\0\0\0'
  1050. if sys.version_info >= (3, 0):
  1051. data += b'\0\0\0\0'
  1052. data += marshal.dumps(code)
  1053. stream = StringStream(data)
  1054. multifile.addSubfile(filename, stream, compressionLevel)
  1055. multifile.flush()
  1056. def __addPythonDirs(self, multifile, moduleDirs, dirnames, compressionLevel):
  1057. """ Adds all of the names on dirnames as a module directory. """
  1058. if not dirnames:
  1059. return
  1060. str = '.'.join(dirnames)
  1061. if str not in moduleDirs:
  1062. # Add an implicit __init__.py file (but only if there's
  1063. # not already a legitimate __init__.py file).
  1064. moduleName = '.'.join(dirnames)
  1065. filename = '/'.join(dirnames) + '/__init__'
  1066. if self.storePythonSource:
  1067. filename += '.py'
  1068. stream = StringStream(b'')
  1069. if multifile.findSubfile(filename) < 0:
  1070. multifile.addSubfile(filename, stream, 0)
  1071. multifile.flush()
  1072. else:
  1073. if __debug__:
  1074. filename += '.pyc'
  1075. else:
  1076. filename += '.pyo'
  1077. if multifile.findSubfile(filename) < 0:
  1078. code = compile('', moduleName, 'exec')
  1079. self.__addPyc(multifile, filename, code, compressionLevel)
  1080. moduleDirs[str] = True
  1081. self.__addPythonDirs(multifile, moduleDirs, dirnames[:-1], compressionLevel)
  1082. def __addPythonFile(self, multifile, moduleDirs, moduleName, mdef,
  1083. compressionLevel):
  1084. """ Adds the named module to the multifile as a .pyc file. """
  1085. # First, split the module into its subdirectory names.
  1086. dirnames = moduleName.split('.')
  1087. if len(dirnames) > 1 and dirnames[-1] == '__init__':
  1088. # The "module" may end in __init__, but that really means
  1089. # the parent directory.
  1090. dirnames = dirnames[:-1]
  1091. self.__addPythonDirs(multifile, moduleDirs, dirnames[:-1], compressionLevel)
  1092. filename = '/'.join(dirnames)
  1093. module = self.mf.modules.get(mdef.moduleName, None)
  1094. if getattr(module, '__path__', None) is not None or \
  1095. (getattr(module, '__file__', None) is not None and getattr(module, '__file__').endswith('/__init__.py')):
  1096. # It's actually a package. In this case, we really write
  1097. # the file moduleName/__init__.py.
  1098. filename += '/__init__'
  1099. moduleDirs[moduleName] = True
  1100. # Ensure we don't have an implicit filename from above.
  1101. multifile.removeSubfile(filename + '.py')
  1102. if __debug__:
  1103. multifile.removeSubfile(filename + '.pyc')
  1104. else:
  1105. multifile.removeSubfile(filename + '.pyo')
  1106. # Attempt to add the original source file if we can.
  1107. sourceFilename = None
  1108. if mdef.filename and mdef.filename.getExtension() == "py":
  1109. sourceFilename = mdef.filename
  1110. elif getattr(module, '__file__', None):
  1111. sourceFilename = Filename.fromOsSpecific(module.__file__)
  1112. sourceFilename.setExtension("py")
  1113. sourceFilename.setText()
  1114. if self.storePythonSource:
  1115. if sourceFilename and sourceFilename.exists():
  1116. filename += '.py'
  1117. multifile.addSubfile(filename, sourceFilename, compressionLevel)
  1118. return
  1119. # If we can't find the source file, add the compiled pyc instead.
  1120. if __debug__:
  1121. filename += '.pyc'
  1122. else:
  1123. filename += '.pyo'
  1124. code = None
  1125. if module:
  1126. # Get the compiled code directly from the module object.
  1127. code = getattr(module, "__code__", None)
  1128. if not code:
  1129. # This is a module with no associated Python
  1130. # code. It must be an extension module. Get the
  1131. # filename.
  1132. extensionFilename = getattr(module, '__file__', None)
  1133. if extensionFilename:
  1134. self.extras.append((moduleName, extensionFilename))
  1135. else:
  1136. # It doesn't even have a filename; it must
  1137. # be a built-in module. No worries about
  1138. # this one, then.
  1139. pass
  1140. else:
  1141. # Read the code from the source file and compile it on-the-fly.
  1142. if sourceFilename and sourceFilename.exists():
  1143. source = open(sourceFilename.toOsSpecific(), 'r').read()
  1144. if source and source[-1] != '\n':
  1145. source = source + '\n'
  1146. code = compile(source, str(sourceFilename), 'exec')
  1147. self.__addPyc(multifile, filename, code, compressionLevel)
  1148. def addToMultifile(self, multifile, compressionLevel = 0):
  1149. """ After a call to done(), this stores all of the accumulated
  1150. python code into the indicated Multifile. Additional
  1151. extension modules are listed in self.extras. """
  1152. moduleDirs = {}
  1153. for moduleName, mdef in self.getModuleDefs():
  1154. if not mdef.exclude:
  1155. self.__addPythonFile(multifile, moduleDirs, moduleName, mdef,
  1156. compressionLevel)
  1157. def writeMultifile(self, mfname):
  1158. """ After a call to done(), this stores all of the accumulated
  1159. python code into a Multifile with the indicated filename,
  1160. including the extension. Additional extension modules are
  1161. listed in self.extras."""
  1162. self.__replacePaths()
  1163. Filename(mfname).unlink()
  1164. multifile = Multifile()
  1165. if not multifile.openReadWrite(mfname):
  1166. raise Exception
  1167. self.addToMultifile(multifile)
  1168. multifile.flush()
  1169. multifile.repack()
  1170. def writeCode(self, filename, initCode = ""):
  1171. """ After a call to done(), this freezes all of the accumulated
  1172. Python code into a C source file. """
  1173. self.__replacePaths()
  1174. # Now generate the actual export table.
  1175. moduleDefs = []
  1176. moduleList = []
  1177. for moduleName, mdef in self.getModuleDefs():
  1178. origName = mdef.moduleName
  1179. if mdef.forbid:
  1180. # Explicitly disallow importing this module.
  1181. moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
  1182. continue
  1183. assert not mdef.exclude
  1184. # Allow importing this module.
  1185. module = self.mf.modules.get(origName, None)
  1186. code = getattr(module, "__code__", None)
  1187. if code:
  1188. code = marshal.dumps(code)
  1189. mangledName = self.mangleName(moduleName)
  1190. moduleDefs.append(self.makeModuleDef(mangledName, code))
  1191. moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
  1192. continue
  1193. #if moduleName in startupModules:
  1194. # # Forbid the loading of this startup module.
  1195. # moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
  1196. # continue
  1197. # This is a module with no associated Python code. It is either
  1198. # an extension module or a builtin module. Get the filename, if
  1199. # it is the former.
  1200. extensionFilename = getattr(module, '__file__', None)
  1201. if extensionFilename or self.linkExtensionModules:
  1202. self.extras.append((moduleName, extensionFilename))
  1203. # If it is a submodule of a frozen module, Python will have
  1204. # trouble importing it as a builtin module. Synthesize a frozen
  1205. # module that loads it as builtin.
  1206. if '.' in moduleName and self.linkExtensionModules:
  1207. code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec')
  1208. code = marshal.dumps(code)
  1209. mangledName = self.mangleName(moduleName)
  1210. moduleDefs.append(self.makeModuleDef(mangledName, code))
  1211. moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, None))
  1212. elif '.' in moduleName:
  1213. # Nothing we can do about this case except warn the user they
  1214. # are in for some trouble.
  1215. print('WARNING: Python cannot import extension modules under '
  1216. 'frozen Python packages; %s will be inaccessible. '
  1217. 'passing either -l to link in extension modules or use '
  1218. '-x %s to exclude the entire package.' % (moduleName, moduleName.split('.')[0]))
  1219. text = programFile % {
  1220. 'moduleDefs': '\n'.join(moduleDefs),
  1221. 'moduleList': '\n'.join(moduleList),
  1222. }
  1223. if self.linkExtensionModules and self.extras:
  1224. # Should we link in extension modules? If so, we write out a new
  1225. # built-in module table that directly hooks up with the init
  1226. # functions. On Linux, we completely override Python's own
  1227. # built-in module table; on Windows, we can't do this, so we
  1228. # instead use PyImport_ExtendInittab to add to it.
  1229. # Python 3 case.
  1230. text += '#if PY_MAJOR_VERSION >= 3\n'
  1231. for module, fn in self.extras:
  1232. if sys.platform != "win32" or fn:
  1233. libName = module.split('.')[-1]
  1234. initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName)
  1235. if initFunc:
  1236. text += 'extern DL_IMPORT(PyObject) *%s(void);\n' % (initFunc)
  1237. text += '\n'
  1238. if sys.platform == "win32":
  1239. text += 'static struct _inittab extensions[] = {\n'
  1240. else:
  1241. text += 'struct _inittab _PyImport_Inittab[] = {\n'
  1242. for module, fn in self.extras:
  1243. if sys.platform != "win32" or fn:
  1244. libName = module.split('.')[-1]
  1245. initFunc = builtinInitFuncs.get(module, 'PyInit_' + libName) or 'NULL'
  1246. text += ' {"%s", %s},\n' % (module, initFunc)
  1247. text += ' {0, 0},\n'
  1248. text += '};\n\n'
  1249. # Python 2 case.
  1250. text += '#else\n'
  1251. for module, fn in self.extras:
  1252. if sys.platform != "win32" or fn:
  1253. libName = module.split('.')[-1]
  1254. initFunc = builtinInitFuncs.get(module, 'init' + libName)
  1255. if initFunc:
  1256. text += 'extern DL_IMPORT(void) %s(void);\n' % (initFunc)
  1257. text += '\n'
  1258. if sys.platform == "win32":
  1259. text += 'static struct _inittab extensions[] = {\n'
  1260. else:
  1261. text += 'struct _inittab _PyImport_Inittab[] = {\n'
  1262. for module, fn in self.extras:
  1263. if sys.platform != "win32" or fn:
  1264. libName = module.split('.')[-1]
  1265. initFunc = builtinInitFuncs.get(module, 'init' + libName) or 'NULL'
  1266. text += ' {"%s", %s},\n' % (module, initFunc)
  1267. text += ' {0, 0},\n'
  1268. text += '};\n'
  1269. text += '#endif\n\n'
  1270. elif sys.platform == "win32":
  1271. text += 'static struct _inittab extensions[] = {\n'
  1272. text += ' {0, 0},\n'
  1273. text += '};\n\n'
  1274. text += initCode
  1275. if filename is not None:
  1276. file = open(filename, 'w')
  1277. file.write(text)
  1278. file.close()
  1279. def generateCode(self, basename, compileToExe = False):
  1280. """ After a call to done(), this freezes all of the
  1281. accumulated python code into either an executable program (if
  1282. compileToExe is true) or a dynamic library (if compileToExe is
  1283. false). The basename is the name of the file to write,
  1284. without the extension.
  1285. The return value is the newly-generated filename, including
  1286. the filename extension. Additional extension modules are
  1287. listed in self.extras. """
  1288. if compileToExe:
  1289. # We must have a __main__ module to make an exe file.
  1290. if not self.__writingModule('__main__'):
  1291. message = "Can't generate an executable without a __main__ module."
  1292. raise Exception(message)
  1293. filename = basename + self.sourceExtension
  1294. dllexport = ''
  1295. dllimport = ''
  1296. if self.platform.startswith('win'):
  1297. dllexport = '__declspec(dllexport) '
  1298. dllimport = '__declspec(dllimport) '
  1299. if not self.cenv:
  1300. self.cenv = CompilationEnvironment(platform = self.platform)
  1301. if compileToExe:
  1302. code = self.frozenMainCode
  1303. if self.platform.startswith('win'):
  1304. code += self.frozenDllMainCode
  1305. initCode = self.mainInitCode % {
  1306. 'frozenMainCode' : code,
  1307. 'programName' : os.path.basename(basename),
  1308. 'dllexport' : dllexport,
  1309. 'dllimport' : dllimport,
  1310. }
  1311. if self.platform.startswith('win'):
  1312. target = basename + '.exe'
  1313. else:
  1314. target = basename
  1315. compileFunc = self.cenv.compileExe
  1316. else:
  1317. if self.platform.startswith('win'):
  1318. target = basename + self.cenv.dllext + '.pyd'
  1319. else:
  1320. target = basename + '.so'
  1321. initCode = dllInitCode % {
  1322. 'moduleName' : os.path.basename(basename),
  1323. 'dllexport' : dllexport,
  1324. 'dllimport' : dllimport,
  1325. }
  1326. compileFunc = self.cenv.compileDll
  1327. self.writeCode(filename, initCode=initCode)
  1328. # Keep track of the files we should clean up after use.
  1329. cleanFiles = [filename, basename + self.objectExtension]
  1330. extraLink = []
  1331. if self.linkExtensionModules:
  1332. for mod, fn in self.extras:
  1333. if not fn:
  1334. continue
  1335. if sys.platform == 'win32':
  1336. # We can't link with a .pyd directly on Windows. Check
  1337. # if there is a corresponding .lib file in the Python libs
  1338. # directory.
  1339. libsdir = os.path.join(sys.exec_prefix, 'libs')
  1340. libfile = os.path.join(libsdir, mod + '.lib')
  1341. if os.path.isfile(libfile):
  1342. extraLink.append(mod + '.lib')
  1343. continue
  1344. # No, so we have to generate a .lib file. This is pretty
  1345. # easy given that we know the only symbol we need is a
  1346. # initmodule or PyInit_module function.
  1347. modname = mod.split('.')[-1]
  1348. libfile = modname + '.lib'
  1349. if sys.version_info >= (3, 0):
  1350. symbolName = 'PyInit_' + modname
  1351. else:
  1352. symbolName = 'init' + modname
  1353. os.system('lib /nologo /def /export:%s /name:%s.pyd /out:%s' % (symbolName, modname, libfile))
  1354. extraLink.append(libfile)
  1355. cleanFiles += [libfile, modname + '.exp']
  1356. else:
  1357. extraLink.append(fn)
  1358. try:
  1359. compileFunc(filename, basename, extraLink=extraLink)
  1360. finally:
  1361. if not self.keepTemporaryFiles:
  1362. for file in cleanFiles:
  1363. if os.path.exists(file):
  1364. os.unlink(file)
  1365. return target
  1366. def generateRuntimeFromStub(self, target, stub_file, fields={}):
  1367. # We must have a __main__ module to make an exe file.
  1368. if not self.__writingModule('__main__'):
  1369. message = "Can't generate an executable without a __main__ module."
  1370. raise Exception(message)
  1371. if self.platform.startswith('win'):
  1372. modext = '.pyd'
  1373. else:
  1374. modext = '.so'
  1375. # First gather up the strings and code for all the module names, and
  1376. # put those in a string pool.
  1377. pool = b""
  1378. strings = set()
  1379. for moduleName, mdef in self.getModuleDefs():
  1380. strings.add(moduleName.encode('ascii'))
  1381. for value in fields.values():
  1382. if value is not None:
  1383. strings.add(value.encode('utf-8'))
  1384. # Sort by length descending, allowing reuse of partial strings.
  1385. strings = sorted(strings, key=lambda str:-len(str))
  1386. string_offsets = {}
  1387. # Now add the strings to the pool, and collect the offsets relative to
  1388. # the beginning of the pool.
  1389. for string in strings:
  1390. # First check whether it's already in there; it could be part of
  1391. # a longer string.
  1392. offset = pool.find(string + b'\0')
  1393. if offset < 0:
  1394. offset = len(pool)
  1395. pool += string + b'\0'
  1396. string_offsets[string] = offset
  1397. # Now go through the modules and add them to the pool as well. These
  1398. # are not 0-terminated, but we later record their sizes and names in
  1399. # a table after the blob header.
  1400. moduleList = []
  1401. for moduleName, mdef in self.getModuleDefs():
  1402. origName = mdef.moduleName
  1403. if mdef.forbid:
  1404. # Explicitly disallow importing this module.
  1405. moduleList.append((moduleName, 0, 0))
  1406. continue
  1407. # For whatever it's worth, align the code blocks.
  1408. if len(pool) & 3 != 0:
  1409. pad = (4 - (len(pool) & 3))
  1410. pool += b'\0' * pad
  1411. assert not mdef.exclude
  1412. # Allow importing this module.
  1413. module = self.mf.modules.get(origName, None)
  1414. code = getattr(module, "__code__", None)
  1415. if code:
  1416. code = marshal.dumps(code)
  1417. size = len(code)
  1418. if getattr(module, "__path__", None):
  1419. # Indicate package by negative size
  1420. size = -size
  1421. moduleList.append((moduleName, len(pool), size))
  1422. pool += code
  1423. continue
  1424. # This is a module with no associated Python code. It is either
  1425. # an extension module or a builtin module. Get the filename, if
  1426. # it is the former.
  1427. extensionFilename = getattr(module, '__file__', None)
  1428. if extensionFilename:
  1429. self.extras.append((moduleName, extensionFilename))
  1430. # If it is a submodule of a frozen module, Python will have
  1431. # trouble importing it as a builtin module. Synthesize a frozen
  1432. # module that loads it dynamically.
  1433. if '.' in moduleName:
  1434. 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)
  1435. code = compile(code, moduleName, 'exec')
  1436. code = marshal.dumps(code)
  1437. moduleList.append((moduleName, len(pool), len(code)))
  1438. pool += code
  1439. # Determine the format of the header and module list entries depending
  1440. # on the platform.
  1441. num_pointers = 11
  1442. stub_data = bytearray(stub_file.read())
  1443. bitnesses = self._get_executable_bitnesses(stub_data)
  1444. header_layouts = {
  1445. 32: '<QQHHHH8x%dII' % num_pointers,
  1446. 64: '<QQHHHH8x%dQQ' % num_pointers,
  1447. }
  1448. entry_layouts = {
  1449. 32: '<IIi',
  1450. 64: '<QQixxxx',
  1451. }
  1452. # Calculate the size of the module tables, so that we can determine
  1453. # the proper offset for the string pointers. There can be more than
  1454. # one module table for macOS executables. Sort the bitnesses so that
  1455. # the alignment is correct.
  1456. bitnesses = sorted(bitnesses, reverse=True)
  1457. pool_offset = 0
  1458. for bitness in bitnesses:
  1459. pool_offset += (len(moduleList) + 1) * struct.calcsize(entry_layouts[bitness])
  1460. # Now we can determine the offset of the blob.
  1461. if self.platform.startswith('win'):
  1462. # We don't use mmap on Windows. Align just for good measure.
  1463. blob_align = 32
  1464. else:
  1465. # Align to page size, so that it can be mmapped.
  1466. blob_align = 4096
  1467. # Add padding before the blob if necessary.
  1468. blob_offset = len(stub_data)
  1469. if (blob_offset & (blob_align - 1)) != 0:
  1470. pad = (blob_align - (blob_offset & (blob_align - 1)))
  1471. stub_data += (b'\0' * pad)
  1472. blob_offset += pad
  1473. assert (blob_offset % blob_align) == 0
  1474. assert blob_offset == len(stub_data)
  1475. # Also determine the total blob size now. Add padding to the end.
  1476. blob_size = pool_offset + len(pool)
  1477. if blob_size & 31 != 0:
  1478. pad = (32 - (blob_size & 31))
  1479. blob_size += pad
  1480. # Calculate the offsets for the variables. These are pointers,
  1481. # relative to the beginning of the blob.
  1482. field_offsets = {}
  1483. for key, value in fields.items():
  1484. if value is not None:
  1485. encoded = value.encode('utf-8')
  1486. field_offsets[key] = pool_offset + string_offsets[encoded]
  1487. # OK, now go and write the blob. This consists of the module table
  1488. # (there may be two in the case of a macOS universal (fat) binary).
  1489. blob = b""
  1490. append_offset = False
  1491. for bitness in bitnesses:
  1492. entry_layout = entry_layouts[bitness]
  1493. header_layout = header_layouts[bitness]
  1494. table_offset = len(blob)
  1495. for moduleName, offset, size in moduleList:
  1496. encoded = moduleName.encode('ascii')
  1497. string_offset = pool_offset + string_offsets[encoded]
  1498. if size != 0:
  1499. offset += pool_offset
  1500. blob += struct.pack(entry_layout, string_offset, offset, size)
  1501. # A null entry marks the end of the module table.
  1502. blob += struct.pack(entry_layout, 0, 0, 0)
  1503. # Compose the header we will be writing to the stub, to tell it
  1504. # where to find the module data blob, as well as other variables.
  1505. header = struct.pack(header_layout,
  1506. blob_offset,
  1507. blob_size,
  1508. 1, # Version number
  1509. num_pointers, # Number of pointers that follow
  1510. 0, # Codepage, not yet used
  1511. 0, # Flags, not yet used
  1512. table_offset, # Module table pointer.
  1513. # The following variables need to be set before static init
  1514. # time. See configPageManager.cxx, where they are read.
  1515. field_offsets.get('prc_data', 0),
  1516. field_offsets.get('default_prc_dir', 0),
  1517. field_offsets.get('prc_dir_envvars', 0),
  1518. field_offsets.get('prc_path_envvars', 0),
  1519. field_offsets.get('prc_patterns', 0),
  1520. field_offsets.get('prc_encrypted_patterns', 0),
  1521. field_offsets.get('prc_encryption_key', 0),
  1522. field_offsets.get('prc_executable_patterns', 0),
  1523. field_offsets.get('prc_executable_args_envvar', 0),
  1524. field_offsets.get('main_dir', 0),
  1525. 0)
  1526. # Now, find the location of the 'blobinfo' symbol in the binary,
  1527. # to which we will write our header.
  1528. if not self._replace_symbol(stub_data, b'blobinfo', header, bitness=bitness):
  1529. # This must be a legacy deploy-stub, which requires the offset to
  1530. # be appended to the end.
  1531. append_offset = True
  1532. # Add the string/code pool.
  1533. assert len(blob) == pool_offset
  1534. blob += pool
  1535. del pool
  1536. # Now pad out the blob to the calculated blob size.
  1537. if len(blob) < blob_size:
  1538. blob += b'\0' * (blob_size - len(blob))
  1539. assert len(blob) == blob_size
  1540. if append_offset:
  1541. # This is for legacy deploy-stub.
  1542. print("WARNING: Could not find blob header. Is deploy-stub outdated?")
  1543. blob += struct.pack('<Q', blob_offset)
  1544. with open(target, 'wb') as f:
  1545. f.write(stub_data)
  1546. assert f.tell() == blob_offset
  1547. f.write(blob)
  1548. os.chmod(target, 0o755)
  1549. return target
  1550. def _get_executable_bitnesses(self, data):
  1551. """Returns the bitnesses (32 or 64) of the given executable data.
  1552. This will contain 1 element for non-fat executables."""
  1553. if data.startswith(b'MZ'):
  1554. # A Windows PE file.
  1555. offset, = struct.unpack_from('<I', data, 0x3c)
  1556. assert data[offset:offset+4] == b'PE\0\0'
  1557. magic, = struct.unpack_from('<H', data, offset + 24)
  1558. assert magic in (0x010b, 0x020b)
  1559. if magic == 0x020b:
  1560. return (64,)
  1561. else:
  1562. return (32,)
  1563. elif data.startswith(b"\177ELF"):
  1564. # A Linux/FreeBSD ELF executable.
  1565. elfclass = ord(data[4:5])
  1566. assert elfclass in (1, 2)
  1567. return (elfclass * 32,)
  1568. elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE'):
  1569. # 32-bit Mach-O file, as used on macOS.
  1570. return (32,)
  1571. elif data[:4] in (b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
  1572. # 64-bit Mach-O file, as used on macOS.
  1573. return (64,)
  1574. elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
  1575. # Universal binary with 32-bit offsets.
  1576. num_fat, = struct.unpack_from('>I', data, 4)
  1577. bitnesses = set()
  1578. ptr = 8
  1579. for i in range(num_fat):
  1580. cputype, cpusubtype, offset, size, align = \
  1581. struct.unpack_from('>IIIII', data, ptr)
  1582. ptr += 20
  1583. if (cputype & 0x1000000) != 0:
  1584. bitnesses.add(64)
  1585. else:
  1586. bitnesses.add(32)
  1587. return tuple(bitnesses)
  1588. elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
  1589. # Universal binary with 64-bit offsets.
  1590. num_fat, = struct.unpack_from('>I', data, 4)
  1591. bitnesses = set()
  1592. ptr = 8
  1593. for i in range(num_fat):
  1594. cputype, cpusubtype, offset, size, align = \
  1595. struct.unpack_from('>QQQQQ', data, ptr)
  1596. ptr += 40
  1597. if (cputype & 0x1000000) != 0:
  1598. bitnesses.add(64)
  1599. else:
  1600. bitnesses.add(32)
  1601. return tuple(bitnesses)
  1602. def _replace_symbol(self, data, symbol_name, replacement, bitness=None):
  1603. """We store a custom section in the binary file containing a header
  1604. containing offsets to the binary data.
  1605. If bitness is set, and the binary in question is a macOS universal
  1606. binary, it only replaces for binaries with the given bitness. """
  1607. if data.startswith(b'MZ'):
  1608. # A Windows PE file.
  1609. pe = pefile.PEFile()
  1610. pe.read(BytesIO(data))
  1611. addr = pe.get_export_address(symbol_name)
  1612. if addr is not None:
  1613. # We found it, return its offset in the file.
  1614. offset = pe.get_address_offset(addr)
  1615. if offset is not None:
  1616. data[offset:offset+len(replacement)] = replacement
  1617. return True
  1618. elif data.startswith(b"\177ELF"):
  1619. return self._replace_symbol_elf(data, symbol_name, replacement)
  1620. elif data[:4] in (b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
  1621. b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE'):
  1622. off = self._find_symbol_macho(data, symbol_name)
  1623. if off is not None:
  1624. data[off:off+len(replacement)] = replacement
  1625. return True
  1626. return False
  1627. elif data[:4] in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA'):
  1628. # Universal binary with 32-bit offsets.
  1629. num_fat, = struct.unpack_from('>I', data, 4)
  1630. replaced = False
  1631. ptr = 8
  1632. for i in range(num_fat):
  1633. cputype, cpusubtype, offset, size, align = \
  1634. struct.unpack_from('>IIIII', data, ptr)
  1635. ptr += 20
  1636. # Does this match the requested bitness?
  1637. if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64):
  1638. continue
  1639. macho_data = data[offset:offset+size]
  1640. off = self._find_symbol_macho(macho_data, symbol_name)
  1641. if off is not None:
  1642. off += offset
  1643. data[off:off+len(replacement)] = replacement
  1644. replaced = True
  1645. return replaced
  1646. elif data[:4] in (b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA'):
  1647. # Universal binary with 64-bit offsets.
  1648. num_fat, = struct.unpack_from('>I', data, 4)
  1649. replaced = False
  1650. ptr = 8
  1651. for i in range(num_fat):
  1652. cputype, cpusubtype, offset, size, align = \
  1653. struct.unpack_from('>QQQQQ', data, ptr)
  1654. ptr += 40
  1655. # Does this match the requested bitness?
  1656. if bitness is not None and ((cputype & 0x1000000) != 0) != (bitness == 64):
  1657. continue
  1658. macho_data = data[offset:offset+size]
  1659. off = self._find_symbol_macho(macho_data, symbol_name)
  1660. if off is not None:
  1661. off += offset
  1662. data[off:off+len(replacement)] = replacement
  1663. replaced = True
  1664. return replaced
  1665. # We don't know what kind of file this is.
  1666. return False
  1667. def _replace_symbol_elf(self, elf_data, symbol_name, replacement):
  1668. """ The Linux/FreeBSD implementation of _replace_symbol. """
  1669. replaced = False
  1670. # Make sure we read in the correct endianness and integer size
  1671. endian = "<>"[ord(elf_data[5:6]) - 1]
  1672. is_64bit = ord(elf_data[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
  1673. header_struct = endian + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[is_64bit]
  1674. section_struct = endian + ("4xI4xIIII8xI", "4xI8xQQQI12xQ")[is_64bit]
  1675. symbol_struct = endian + ("IIIBBH", "IBBHQQ")[is_64bit]
  1676. header_size = struct.calcsize(header_struct)
  1677. type, machine, version, entry, phoff, shoff, flags, ehsize, phentsize, phnum, shentsize, shnum, shstrndx \
  1678. = struct.unpack_from(header_struct, elf_data, 16)
  1679. section_offsets = []
  1680. symbol_tables = []
  1681. string_tables = {}
  1682. # Seek to the section header table and find the symbol tables.
  1683. ptr = shoff
  1684. for i in range(shnum):
  1685. type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
  1686. ptr += shentsize
  1687. section_offsets.append(offset - addr)
  1688. if type == 0x0B and link != 0: # SHT_DYNSYM, links to string table
  1689. symbol_tables.append((offset, size, link, entsize))
  1690. string_tables[link] = None
  1691. # Read the relevant string tables.
  1692. for idx in list(string_tables.keys()):
  1693. ptr = shoff + idx * shentsize
  1694. type, addr, offset, size, link, entsize = struct.unpack_from(section_struct, elf_data[ptr:ptr+shentsize])
  1695. if type == 3:
  1696. string_tables[idx] = elf_data[offset:offset+size]
  1697. # Loop through to find the offset of the "blobinfo" symbol.
  1698. for offset, size, link, entsize in symbol_tables:
  1699. entries = size // entsize
  1700. for i in range(entries):
  1701. ptr = offset + i * entsize
  1702. fields = struct.unpack_from(symbol_struct, elf_data[ptr:ptr+entsize])
  1703. if is_64bit:
  1704. name, info, other, shndx, value, size = fields
  1705. else:
  1706. name, value, size, info, other, shndx = fields
  1707. if not name:
  1708. continue
  1709. name = string_tables[link][name : string_tables[link].find(b'\0', name)]
  1710. if name == symbol_name:
  1711. if shndx == 0: # SHN_UNDEF
  1712. continue
  1713. elif shndx >= 0xff00 and shndx <= 0xffff:
  1714. assert False
  1715. else:
  1716. # Got it. Make the replacement.
  1717. off = section_offsets[shndx] + value
  1718. elf_data[off:off+len(replacement)] = replacement
  1719. replaced = True
  1720. return replaced
  1721. def _find_symbol_macho(self, macho_data, symbol_name):
  1722. """ Returns the offset of the given symbol in the binary file. """
  1723. if macho_data[:4] in (b'\xCE\xFA\xED\xFE', b'\xCF\xFA\xED\xFE'):
  1724. endian = '<'
  1725. else:
  1726. endian = '>'
  1727. cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
  1728. struct.unpack_from(endian + 'IIIIII', macho_data, 4)
  1729. is_64bit = (cputype & 0x1000000) != 0
  1730. segments = []
  1731. cmd_ptr = 28
  1732. nlist_struct = endian + 'IBBHI'
  1733. if is_64bit:
  1734. nlist_struct = endian + 'IBBHQ'
  1735. cmd_ptr += 4
  1736. nlist_size = struct.calcsize(nlist_struct)
  1737. for i in range(ncmds):
  1738. cmd, cmd_size = struct.unpack_from(endian + 'II', macho_data, cmd_ptr)
  1739. cmd_data = macho_data[cmd_ptr+8:cmd_ptr+cmd_size]
  1740. cmd_ptr += cmd_size
  1741. cmd &= ~0x80000000
  1742. if cmd == 0x01: # LC_SEGMENT
  1743. segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
  1744. struct.unpack_from(endian + '16sIIIIIIII', cmd_data)
  1745. segments.append((vmaddr, vmsize, fileoff))
  1746. elif cmd == 0x19: # LC_SEGMENT_64
  1747. segname, vmaddr, vmsize, fileoff, filesize, maxprot, initprot, nsects, flags = \
  1748. struct.unpack_from(endian + '16sQQQQIIII', cmd_data)
  1749. segments.append((vmaddr, vmsize, fileoff))
  1750. elif cmd == 0x2: # LC_SYMTAB
  1751. symoff, nsyms, stroff, strsize = \
  1752. struct.unpack_from(endian + 'IIII', cmd_data)
  1753. strings = macho_data[stroff:stroff+strsize]
  1754. for i in range(nsyms):
  1755. strx, type, sect, desc, value = struct.unpack_from(nlist_struct, macho_data, symoff)
  1756. symoff += nlist_size
  1757. name = strings[strx : strings.find(b'\0', strx)]
  1758. if name == b'_' + symbol_name:
  1759. # Find out in which segment this is.
  1760. for vmaddr, vmsize, fileoff in segments:
  1761. # Is it defined in this segment?
  1762. rel = value - vmaddr
  1763. if rel >= 0 and rel < vmsize:
  1764. # Yes, so return the symbol offset.
  1765. return fileoff + rel
  1766. print("Could not find memory address for symbol %s" % (symbol_name))
  1767. def makeModuleDef(self, mangledName, code):
  1768. result = ''
  1769. result += 'static unsigned char %s[] = {' % (mangledName)
  1770. for i in range(0, len(code), 16):
  1771. result += '\n '
  1772. for c in code[i:i+16]:
  1773. if isinstance(c, int): # Python 3
  1774. result += ('%d,' % c)
  1775. else: # Python 2
  1776. result += ('%d,' % ord(c))
  1777. result += '\n};\n'
  1778. return result
  1779. def makeModuleListEntry(self, mangledName, code, moduleName, module):
  1780. size = len(code)
  1781. if getattr(module, "__path__", None):
  1782. # Indicate package by negative size
  1783. size = -size
  1784. return ' {"%s", %s, %s},' % (moduleName, mangledName, size)
  1785. def makeForbiddenModuleListEntry(self, moduleName):
  1786. return ' {"%s", NULL, 0},' % (moduleName)
  1787. def __writingModule(self, moduleName):
  1788. """ Returns true if we are outputting the named module in this
  1789. pass, false if we have already output in a previous pass, or
  1790. if it is not yet on the output table. """
  1791. mdef = self.modules.get(moduleName, (None, None))
  1792. if mdef.exclude:
  1793. return False
  1794. if moduleName in self.previousModules:
  1795. return False
  1796. return True
  1797. class PandaModuleFinder(modulefinder.ModuleFinder):
  1798. def __init__(self, *args, **kw):
  1799. """
  1800. :param path: search path to look on, defaults to sys.path
  1801. :param suffixes: defaults to imp.get_suffixes()
  1802. :param excludes: a list of modules to exclude
  1803. :param debug: an integer indicating the level of verbosity
  1804. """
  1805. self.suffixes = kw.pop('suffixes', imp.get_suffixes())
  1806. modulefinder.ModuleFinder.__init__(self, *args, **kw)
  1807. # Make sure we don't open a .whl/.zip file more than once.
  1808. self._zip_files = {}
  1809. def _open_file(self, path, mode):
  1810. """ Opens a module at the given path, which may contain a zip file.
  1811. Returns None if the module could not be found. """
  1812. if os.path.isfile(path):
  1813. if sys.version_info >= (3, 0) and 'b' not in mode:
  1814. return open(path, mode, encoding='utf8')
  1815. else:
  1816. return open(path, mode)
  1817. # Is there a zip file along the path?
  1818. dir, dirname = os.path.split(path)
  1819. fn = dirname
  1820. while dirname:
  1821. if os.path.isfile(dir):
  1822. # Okay, this is actually a file. Is it a zip file?
  1823. if dir in self._zip_files:
  1824. # Yes, and we've previously opened this.
  1825. zip = self._zip_files[dir]
  1826. elif zipfile.is_zipfile(dir):
  1827. zip = zipfile.ZipFile(dir)
  1828. self._zip_files[dir] = zip
  1829. else:
  1830. # It's a different kind of file. Stop looking.
  1831. return None
  1832. try:
  1833. fp = zip.open(fn.replace(os.path.sep, '/'), 'r')
  1834. except KeyError:
  1835. return None
  1836. if sys.version_info >= (3, 0) and 'b' not in mode:
  1837. return TextIOWrapper(fp, encoding='utf8')
  1838. return fp
  1839. # Look at the parent directory.
  1840. dir, dirname = os.path.split(dir)
  1841. fn = os.path.join(dirname, fn)
  1842. return None
  1843. def find_module(self, name, path=None, parent=None):
  1844. """ Finds a module with the indicated name on the given search path
  1845. (or self.path if None). Returns a tuple like (fp, path, stuff), where
  1846. stuff is a tuple like (suffix, mode, type). """
  1847. if imp.is_frozen(name):
  1848. # Don't pick up modules that are frozen into p3dpython.
  1849. raise ImportError("'%s' is a frozen module" % (name))
  1850. if parent is not None:
  1851. fullname = parent.__name__+'.'+name
  1852. else:
  1853. fullname = name
  1854. if fullname in self.excludes:
  1855. raise ImportError(name)
  1856. # If no search path is given, look for a built-in module.
  1857. if path is None:
  1858. if name in sys.builtin_module_names:
  1859. return (None, None, ('', '', imp.C_BUILTIN))
  1860. path = self.path
  1861. # Look for the module on the search path.
  1862. for dir_path in path:
  1863. basename = os.path.join(dir_path, name.split('.')[-1])
  1864. # Look for recognized extensions.
  1865. for stuff in self.suffixes:
  1866. suffix, mode, _ = stuff
  1867. fp = self._open_file(basename + suffix, mode)
  1868. if fp:
  1869. return (fp, basename + suffix, stuff)
  1870. # Consider a package, i.e. a directory containing __init__.py.
  1871. for suffix, mode, _ in self.suffixes:
  1872. init = os.path.join(basename, '__init__' + suffix)
  1873. if self._open_file(init, mode):
  1874. return (None, basename, ('', '', imp.PKG_DIRECTORY))
  1875. # It wasn't found through the normal channels. Maybe it's one of
  1876. # ours, or maybe it's frozen?
  1877. if not path:
  1878. # Only if we're not looking on a particular path, though.
  1879. if p3extend_frozen and p3extend_frozen.is_frozen_module(name):
  1880. # It's a frozen module.
  1881. return (None, name, ('', '', imp.PY_FROZEN))
  1882. raise ImportError(name)
  1883. def find_all_submodules(self, m):
  1884. # Overridden so that we can define our own suffixes.
  1885. if not m.__path__:
  1886. return
  1887. modules = {}
  1888. for dir in m.__path__:
  1889. try:
  1890. names = os.listdir(dir)
  1891. except OSError:
  1892. self.msg(2, "can't list directory", dir)
  1893. continue
  1894. for name in names:
  1895. mod = None
  1896. for suff in self.suffixes:
  1897. n = len(suff)
  1898. if name[-n:] == suff:
  1899. mod = name[:-n]
  1900. break
  1901. if mod and mod != "__init__":
  1902. modules[mod] = mod
  1903. return modules.keys()