FreezeTool.py 106 KB

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