2
0

FreezeTool.py 110 KB

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