2
0

FreezeTool.py 104 KB

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