makepandacore.py 138 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846
  1. ########################################################################
  2. ##
  3. ## This file, makepandacore, contains all the global state and
  4. ## global functions for the makepanda system.
  5. ##
  6. ########################################################################
  7. import configparser
  8. import fnmatch
  9. import getpass
  10. import glob
  11. import os
  12. import pickle
  13. import platform
  14. import re
  15. import shutil
  16. import signal
  17. import subprocess
  18. import sys
  19. import threading
  20. import _thread as thread
  21. import time
  22. import locations
  23. SUFFIX_INC = [".cxx",".cpp",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"]
  24. SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"]
  25. SUFFIX_LIB = [".lib",".ilb"]
  26. VCS_DIRS = set(["CVS", "CVSROOT", ".git", ".hg", "__pycache__"])
  27. VCS_FILES = set([".cvsignore", ".gitignore", ".gitmodules", ".hgignore"])
  28. STARTTIME = time.time()
  29. MAINTHREAD = threading.current_thread()
  30. OUTPUTDIR = "built"
  31. CUSTOM_OUTPUTDIR = False
  32. THIRDPARTYBASE = None
  33. THIRDPARTYDIR = None
  34. OPTIMIZE = "3"
  35. VERBOSE = False
  36. LINK_ALL_STATIC = False
  37. TARGET = None
  38. TARGET_ARCH = None
  39. HAS_TARGET_ARCH = False
  40. TOOLCHAIN_PREFIX = ""
  41. ANDROID_ABI = None
  42. ANDROID_TRIPLE = None
  43. ANDROID_API = None
  44. SYS_LIB_DIRS = []
  45. SYS_INC_DIRS = []
  46. DEBUG_DEPENDENCIES = False
  47. if sys.platform == "darwin" or sys.platform.startswith("freebsd"):
  48. DEFAULT_CC = "clang"
  49. DEFAULT_CXX = "clang++"
  50. else:
  51. DEFAULT_CC = "gcc"
  52. DEFAULT_CXX = "g++"
  53. DEFAULT_AR = "ar"
  54. DEFAULT_RANLIB = "ranlib"
  55. # Is the current Python a 32-bit or 64-bit build? There doesn't
  56. # appear to be a universal test for this.
  57. if sys.platform == 'darwin':
  58. # On macOS, platform.architecture reports '64bit' even if it is
  59. # currently running in 32-bit mode. But sys.maxint is a reliable
  60. # indicator.
  61. host_64 = (sys.maxsize > 0x100000000)
  62. else:
  63. # On Windows (and Linux?) sys.maxint reports 0x7fffffff even on a
  64. # 64-bit build. So we stick with platform.architecture in that
  65. # case.
  66. host_64 = (platform.architecture()[0] == '64bit')
  67. # On Android, get a list of all the public system libraries.
  68. ANDROID_SYS_LIBS = []
  69. if os.path.exists("/etc/public.libraries.txt"):
  70. for line in open("/etc/public.libraries.txt", "r"):
  71. line = line.strip()
  72. ANDROID_SYS_LIBS.append(line)
  73. ########################################################################
  74. ##
  75. ## Visual C++ Version (MSVC) and Visual Studio Information Map
  76. ##
  77. ########################################################################
  78. MSVCVERSIONINFO = {
  79. (10,0): {"vsversion":(10,0), "vsname":"Visual Studio 2010"},
  80. (11,0): {"vsversion":(11,0), "vsname":"Visual Studio 2012"},
  81. (12,0): {"vsversion":(12,0), "vsname":"Visual Studio 2013"},
  82. (14,0): {"vsversion":(14,0), "vsname":"Visual Studio 2015"},
  83. (14,1): {"vsversion":(15,0), "vsname":"Visual Studio 2017"},
  84. (14,2): {"vsversion":(16,0), "vsname":"Visual Studio 2019"},
  85. (14,3): {"vsversion":(17,0), "vsname":"Visual Studio 2022"},
  86. }
  87. ########################################################################
  88. ##
  89. ## Maya and Max Version List (with registry keys)
  90. ##
  91. ########################################################################
  92. MAYAVERSIONINFO = [("MAYA6", "6.0"),
  93. ("MAYA65", "6.5"),
  94. ("MAYA7", "7.0"),
  95. ("MAYA8", "8.0"),
  96. ("MAYA85", "8.5"),
  97. ("MAYA2008","2008"),
  98. ("MAYA2009","2009"),
  99. ("MAYA2010","2010"),
  100. ("MAYA2011","2011"),
  101. ("MAYA2012","2012"),
  102. ("MAYA2013","2013"),
  103. ("MAYA20135","2013.5"),
  104. ("MAYA2014","2014"),
  105. ("MAYA2015","2015"),
  106. ("MAYA2016","2016"),
  107. ("MAYA20165","2016.5"),
  108. ("MAYA2017","2017"),
  109. ("MAYA2018","2018"),
  110. ("MAYA2019","2019"),
  111. ("MAYA2020","2020"),
  112. ("MAYA2022","2022"),
  113. ]
  114. MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
  115. ("MAX7", "SOFTWARE\\Autodesk\\3DSMAX\\7.0", "Installdir", "maxsdk\\include\\CS"),
  116. ("MAX8", "SOFTWARE\\Autodesk\\3DSMAX\\8.0", "Installdir", "maxsdk\\include\\CS"),
  117. ("MAX9", "SOFTWARE\\Autodesk\\3DSMAX\\9.0", "Installdir", "maxsdk\\include\\CS"),
  118. ("MAX2009", "SOFTWARE\\Autodesk\\3DSMAX\\11.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  119. ("MAX2010", "SOFTWARE\\Autodesk\\3DSMAX\\12.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  120. ("MAX2011", "SOFTWARE\\Autodesk\\3DSMAX\\13.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  121. ("MAX2012", "SOFTWARE\\Autodesk\\3DSMAX\\14.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  122. ("MAX2013", "SOFTWARE\\Autodesk\\3DSMAX\\15.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  123. ("MAX2014", "SOFTWARE\\Autodesk\\3DSMAX\\16.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  124. ]
  125. MAYAVERSIONS = []
  126. MAXVERSIONS = []
  127. DXVERSIONS = ["DX9"]
  128. for (ver,key) in MAYAVERSIONINFO:
  129. MAYAVERSIONS.append(ver)
  130. for (ver,key1,key2,subdir) in MAXVERSIONINFO:
  131. MAXVERSIONS.append(ver)
  132. ########################################################################
  133. ##
  134. ## Potentially Conflicting Files
  135. ##
  136. ## The next function can warn about the existence of files that are
  137. ## commonly generated by ppremake that may conflict with the build.
  138. ##
  139. ########################################################################
  140. CONFLICTING_FILES=["dtool/src/dtoolutil/pandaVersion.h",
  141. "dtool/src/dtoolutil/checkPandaVersion.h",
  142. "dtool/src/dtoolutil/checkPandaVersion.cxx",
  143. "dtool/src/prc/prc_parameters.h",
  144. "contrib/src/speedtree/speedtree_parameters.h",
  145. "direct/src/plugin/p3d_plugin_config.h",
  146. "direct/src/plugin_activex/P3DActiveX.rc",
  147. "direct/src/plugin_npapi/nppanda3d.rc",
  148. "direct/src/plugin_standalone/panda3d.rc"]
  149. def WarnConflictingFiles(delete = False):
  150. for cfile in CONFLICTING_FILES:
  151. if os.path.exists(cfile):
  152. Warn("file may conflict with build:", cfile)
  153. if delete:
  154. os.unlink(cfile)
  155. print("Deleted.")
  156. ########################################################################
  157. ##
  158. ## The exit routine will normally
  159. ##
  160. ## - print a message
  161. ## - save the dependency cache
  162. ## - exit
  163. ##
  164. ## However, if it is invoked inside a thread, it instead:
  165. ##
  166. ## - prints a message
  167. ## - raises the "initiate-exit" exception
  168. ##
  169. ## If you create a thread, you must be prepared to catch this
  170. ## exception, save the dependency cache, and exit.
  171. ##
  172. ########################################################################
  173. WARNINGS = []
  174. THREADS = {}
  175. HAVE_COLORS = False
  176. SETF = ""
  177. try:
  178. import curses
  179. curses.setupterm()
  180. SETF = curses.tigetstr("setf")
  181. if SETF is None:
  182. SETF = curses.tigetstr("setaf")
  183. assert SETF is not None
  184. HAVE_COLORS = sys.stdout.isatty()
  185. except:
  186. pass
  187. def DisableColors():
  188. global HAVE_COLORS
  189. HAVE_COLORS = False
  190. def GetColor(color = None):
  191. if not HAVE_COLORS:
  192. return ""
  193. if color is not None:
  194. color = color.lower()
  195. if color == "blue":
  196. token = curses.tparm(SETF, 1)
  197. elif color == "green":
  198. token = curses.tparm(SETF, 2)
  199. elif color == "cyan":
  200. token = curses.tparm(SETF, 3)
  201. elif color == "red":
  202. token = curses.tparm(SETF, 4)
  203. elif color == "magenta":
  204. token = curses.tparm(SETF, 5)
  205. elif color == "yellow":
  206. token = curses.tparm(SETF, 6)
  207. else:
  208. token = curses.tparm(curses.tigetstr("sgr0"))
  209. return token.decode('ascii')
  210. def ColorText(color, text, reset=True):
  211. if reset is True:
  212. return ''.join((GetColor(color), text, GetColor()))
  213. else:
  214. return ''.join((GetColor(color), text))
  215. def PrettyTime(t):
  216. t = int(t)
  217. hours = t // 3600
  218. t -= hours * 3600
  219. minutes = t // 60
  220. t -= minutes * 60
  221. seconds = t
  222. if hours:
  223. return "%d hours %d min" % (hours, minutes)
  224. if minutes:
  225. return "%d min %d sec" % (minutes, seconds)
  226. return "%d sec" % (seconds)
  227. def ProgressOutput(progress, msg, target = None):
  228. sys.stdout.flush()
  229. sys.stderr.flush()
  230. prefix = ""
  231. thisthread = threading.current_thread()
  232. if thisthread is MAINTHREAD:
  233. if progress is None:
  234. prefix = ""
  235. elif progress >= 100.0:
  236. prefix = "%s[%s%d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
  237. elif progress < 10.0:
  238. prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
  239. else:
  240. prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
  241. else:
  242. global THREADS
  243. ident = thread.get_ident()
  244. if ident not in THREADS:
  245. THREADS[ident] = len(THREADS) + 1
  246. prefix = "%s[%sT%d%s] " % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow"))
  247. if target is not None:
  248. suffix = ' ' + ColorText("green", target)
  249. else:
  250. suffix = GetColor()
  251. print(''.join((prefix, msg, suffix)))
  252. sys.stdout.flush()
  253. sys.stderr.flush()
  254. def exit(msg = ""):
  255. sys.stdout.flush()
  256. sys.stderr.flush()
  257. if threading.current_thread() == MAINTHREAD:
  258. SaveDependencyCache()
  259. print("Elapsed Time: " + PrettyTime(time.time() - STARTTIME))
  260. print(msg)
  261. print(ColorText("red", "Build terminated."))
  262. sys.stdout.flush()
  263. sys.stderr.flush()
  264. ##Don't quit the interperter if I'm running this file directly (debugging)
  265. if __name__ != '__main__':
  266. os._exit(1)
  267. else:
  268. print(msg)
  269. raise "initiate-exit"
  270. def Warn(msg, extra=None):
  271. if extra is not None:
  272. print("%sWARNING:%s %s %s%s%s" % (GetColor("red"), GetColor(), msg, GetColor("green"), extra, GetColor()))
  273. else:
  274. print("%sWARNING:%s %s" % (GetColor("red"), GetColor(), msg))
  275. sys.stdout.flush()
  276. def Error(msg, extra=None):
  277. if extra is not None:
  278. print("%sERROR:%s %s %s%s%s" % (GetColor("red"), GetColor(), msg, GetColor("green"), extra, GetColor()))
  279. else:
  280. print("%sERROR:%s %s" % (GetColor("red"), GetColor(), msg))
  281. exit()
  282. ########################################################################
  283. ##
  284. ## SetTarget, GetTarget, GetHost
  285. ##
  286. ## These functions control cross-compilation.
  287. ##
  288. ########################################################################
  289. def GetHost():
  290. """Returns the host platform, ie. the one we're compiling on."""
  291. if sys.platform == 'win32' or sys.platform == 'cygwin':
  292. # sys.platform is win32 on 64-bits Windows as well.
  293. return 'windows'
  294. elif sys.platform == 'darwin':
  295. return 'darwin'
  296. elif sys.platform.startswith('linux'):
  297. try:
  298. # Python seems to offer no built-in way to check this.
  299. osname = subprocess.check_output(["uname", "-o"])
  300. if osname.strip().lower() == b'android':
  301. return 'android'
  302. else:
  303. return 'linux'
  304. except:
  305. return 'linux'
  306. elif sys.platform.startswith('freebsd'):
  307. return 'freebsd'
  308. else:
  309. exit('Unrecognized sys.platform: %s' % (sys.platform))
  310. def GetHostArch():
  311. """Returns the architecture we're compiling on.
  312. Its value is also platform-dependent, as different platforms
  313. have different architecture naming."""
  314. target = GetTarget()
  315. if target == 'windows':
  316. return 'x64' if host_64 else 'x86'
  317. machine = platform.machine()
  318. if machine.startswith('armv7'):
  319. return 'armv7a'
  320. else:
  321. return machine
  322. def SetTarget(target, arch=None):
  323. """Sets the target platform; the one we're compiling for. Also
  324. sets the target architecture (None for default, if any). Should
  325. be called *before* any calls are made to GetOutputDir, GetCC, etc."""
  326. global TARGET, TARGET_ARCH, HAS_TARGET_ARCH
  327. global TOOLCHAIN_PREFIX
  328. global DEFAULT_CC, DEFAULT_CXX, DEFAULT_AR, DEFAULT_RANLIB
  329. host = GetHost()
  330. host_arch = GetHostArch()
  331. if target is None:
  332. target = host
  333. else:
  334. target = target.lower()
  335. if arch is not None:
  336. HAS_TARGET_ARCH = True
  337. TOOLCHAIN_PREFIX = ''
  338. if target == 'windows':
  339. if arch == 'i386':
  340. arch = 'x86'
  341. elif arch == 'amd64':
  342. arch = 'x64'
  343. if arch is not None and arch != 'x86' and arch != 'x64':
  344. exit("Windows architecture must be x86 or x64")
  345. elif target == 'darwin':
  346. DEFAULT_CC = "clang"
  347. DEFAULT_CXX = "clang++"
  348. if arch == 'amd64':
  349. arch = 'x86_64'
  350. if arch == 'aarch64':
  351. arch = 'arm64'
  352. if arch is not None:
  353. choices = ('i386', 'x86_64', 'ppc', 'ppc64', 'arm64')
  354. if arch not in choices:
  355. exit('macOS architecture must be one of %s' % (', '.join(choices)))
  356. elif target == 'android' or target.startswith('android-'):
  357. if arch is None:
  358. # If compiling on Android, default to same architecture. Otherwise, arm.
  359. if host == 'android':
  360. arch = host_arch
  361. else:
  362. arch = 'armv7a'
  363. if arch == 'aarch64':
  364. arch = 'arm64'
  365. # Did we specify an API level?
  366. global ANDROID_API
  367. target, _, api = target.partition('-')
  368. if api:
  369. ANDROID_API = int(api)
  370. elif arch in ('mips64', 'arm64', 'x86_64'):
  371. # 64-bit platforms were introduced in Android 21.
  372. ANDROID_API = 21
  373. else:
  374. # Default to the lowest API level still supported by Google.
  375. ANDROID_API = 19
  376. # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc
  377. global ANDROID_ABI, ANDROID_TRIPLE
  378. if arch == 'armv7a':
  379. ANDROID_ABI = 'armeabi-v7a'
  380. ANDROID_TRIPLE = 'armv7a-linux-androideabi'
  381. elif arch == 'arm':
  382. ANDROID_ABI = 'armeabi'
  383. ANDROID_TRIPLE = 'arm-linux-androideabi'
  384. elif arch == 'arm64':
  385. ANDROID_ABI = 'arm64-v8a'
  386. ANDROID_TRIPLE = 'aarch64-linux-android'
  387. elif arch == 'mips':
  388. ANDROID_ABI = 'mips'
  389. ANDROID_TRIPLE = 'mipsel-linux-android'
  390. elif arch == 'mips64':
  391. ANDROID_ABI = 'mips64'
  392. ANDROID_TRIPLE = 'mips64el-linux-android'
  393. elif arch == 'x86':
  394. ANDROID_ABI = 'x86'
  395. ANDROID_TRIPLE = 'i686-linux-android'
  396. elif arch == 'x86_64':
  397. ANDROID_ABI = 'x86_64'
  398. ANDROID_TRIPLE = 'x86_64-linux-android'
  399. else:
  400. exit('Android architecture must be arm, armv7a, arm64, mips, mips64, x86 or x86_64, use --arch to specify')
  401. ANDROID_TRIPLE += str(ANDROID_API)
  402. TOOLCHAIN_PREFIX = ANDROID_TRIPLE + '-'
  403. DEFAULT_CC = "clang"
  404. DEFAULT_CXX = "clang++"
  405. elif target == 'linux':
  406. if arch is not None:
  407. TOOLCHAIN_PREFIX = '%s-linux-gnu-' % arch
  408. elif host != 'linux':
  409. exit('Should specify an architecture when building for Linux')
  410. elif target == 'emscripten':
  411. DEFAULT_CC = "emcc"
  412. DEFAULT_CXX = "em++"
  413. DEFAULT_AR = "emar"
  414. DEFAULT_RANLIB = "emranlib"
  415. arch = "wasm32"
  416. elif target == host:
  417. if target == 'freebsd':
  418. DEFAULT_CC = "clang"
  419. DEFAULT_CXX = "clang++"
  420. if arch is None or arch == host_arch:
  421. # Not a cross build.
  422. pass
  423. else:
  424. exit('Cannot cross-compile for %s-%s from %s-%s' % (target, arch, host, host_arch))
  425. else:
  426. exit('Cannot cross-compile for %s from %s' % (target, host))
  427. if arch is None:
  428. arch = host_arch
  429. TARGET = target
  430. TARGET_ARCH = arch
  431. def GetTarget():
  432. """Returns the platform we're compiling for. Defaults to GetHost()."""
  433. global TARGET
  434. if TARGET is None:
  435. TARGET = GetHost()
  436. return TARGET
  437. def HasTargetArch():
  438. """Returns True if the user specified an architecture to compile for."""
  439. return HAS_TARGET_ARCH
  440. def GetTargetArch():
  441. """Returns the architecture we're compiling for. Defaults to GetHostArch().
  442. Its value is also dependent on that of GetTarget(), as different platforms
  443. use a different architecture naming."""
  444. global TARGET_ARCH
  445. if TARGET_ARCH is None:
  446. TARGET_ARCH = GetHostArch()
  447. return TARGET_ARCH
  448. def CrossCompiling():
  449. """Returns True if we're cross-compiling."""
  450. return GetTarget() != GetHost()
  451. def GetCC():
  452. return os.environ.get('CC', TOOLCHAIN_PREFIX + DEFAULT_CC)
  453. def GetCXX():
  454. return os.environ.get('CXX', TOOLCHAIN_PREFIX + DEFAULT_CXX)
  455. def GetStrip():
  456. # Hack
  457. if TARGET == 'android':
  458. return 'llvm-strip'
  459. else:
  460. return 'strip'
  461. def GetAR():
  462. # Hack
  463. if TARGET == 'android':
  464. return TOOLCHAIN_PREFIX + DEFAULT_AR
  465. else:
  466. return DEFAULT_AR
  467. def GetRanlib():
  468. # Hack
  469. if TARGET == 'android':
  470. return TOOLCHAIN_PREFIX + DEFAULT_RANLIB
  471. else:
  472. return DEFAULT_RANLIB
  473. BISON = None
  474. def GetBison():
  475. global BISON
  476. if BISON is not None:
  477. return BISON
  478. # We now require a newer version of Bison than the one we previously
  479. # shipped in the win-util dir. The new version has a 'data'
  480. # subdirectory, so check for that.
  481. win_util_data = os.path.join(GetThirdpartyBase(), 'win-util', 'data')
  482. if GetHost() == 'windows' and os.path.isdir(win_util_data):
  483. BISON = os.path.join(GetThirdpartyBase(), 'win-util', 'bison.exe')
  484. elif LocateBinary('bison'):
  485. BISON = 'bison'
  486. else:
  487. # We don't strictly need it, so don't give an error
  488. return None
  489. return BISON
  490. FLEX = None
  491. def GetFlex():
  492. global FLEX
  493. if FLEX is not None:
  494. return FLEX
  495. win_util = os.path.join(GetThirdpartyBase(), 'win-util')
  496. if GetHost() == 'windows' and os.path.isdir(win_util):
  497. FLEX = GetThirdpartyBase() + "/win-util/flex.exe"
  498. elif LocateBinary('flex'):
  499. FLEX = 'flex'
  500. else:
  501. # We don't strictly need it, so don't give an error
  502. return None
  503. return FLEX
  504. def GetFlexVersion():
  505. flex = GetFlex()
  506. if not flex:
  507. return (0, 0, 0)
  508. try:
  509. handle = subprocess.Popen(["flex", "--version"], executable=flex, stdout=subprocess.PIPE)
  510. words = handle.communicate()[0].strip().splitlines()[0].split(b' ')
  511. if words[1] != "version":
  512. version = words[1]
  513. else:
  514. version = words[2]
  515. return tuple(map(int, version.split(b'.')))
  516. except:
  517. Warn("Unable to detect flex version")
  518. return (0, 0, 0)
  519. SEVENZIP = None
  520. def GetSevenZip():
  521. global SEVENZIP
  522. if SEVENZIP is not None:
  523. return SEVENZIP
  524. win_util = os.path.join(GetThirdpartyBase(), 'win-util')
  525. if GetHost() == 'windows' and os.path.isdir(win_util):
  526. SEVENZIP = GetThirdpartyBase() + "/win-util/7za.exe"
  527. elif LocateBinary('7z'):
  528. SEVENZIP = '7z'
  529. else:
  530. # We don't strictly need it, so don't give an error
  531. return None
  532. return SEVENZIP
  533. def HasSevenZip():
  534. return GetSevenZip() is not None
  535. ########################################################################
  536. ##
  537. ## GetInterrogate[Module]
  538. ##
  539. ## Installs and locates the interrogate tool.
  540. ##
  541. ########################################################################
  542. INTERROGATE_DIR = None
  543. INTERROGATE_LOCK = threading.Lock()
  544. def GetInterrogateDir():
  545. global INTERROGATE_DIR
  546. if INTERROGATE_DIR:
  547. return INTERROGATE_DIR
  548. with INTERROGATE_LOCK:
  549. if INTERROGATE_DIR:
  550. return INTERROGATE_DIR
  551. dir = os.path.join(GetOutputDir(), "tmp", "interrogate")
  552. if not os.path.isdir(os.path.join(dir, "panda3d_interrogate-0.1.0.dist-info")):
  553. oscmd("\"%s\" -m pip install --force-reinstall -t \"%s\" panda3d-interrogate==0.1.0" % (sys.executable, dir))
  554. INTERROGATE_DIR = dir
  555. return dir
  556. def GetInterrogate():
  557. path = GetInterrogateDir() + '/interrogate/interrogate'
  558. if sys.platform == "win32":
  559. path += ".exe"
  560. return path
  561. def GetInterrogateModule():
  562. path = GetInterrogateDir() + '/interrogate_module/interrogate_module'
  563. if sys.platform == "win32":
  564. path += ".exe"
  565. return path
  566. ########################################################################
  567. ##
  568. ## LocateBinary
  569. ##
  570. ## This function searches the system PATH for the binary. Returns its
  571. ## full path when it is found, or None when it was not found.
  572. ##
  573. ########################################################################
  574. def LocateBinary(binary):
  575. if os.path.isfile(binary):
  576. return binary
  577. if "PATH" not in os.environ or os.environ["PATH"] == "":
  578. p = os.defpath
  579. else:
  580. p = os.environ["PATH"]
  581. pathList = p.split(os.pathsep)
  582. suffixes = ['']
  583. if GetHost() == 'windows':
  584. if not binary.lower().endswith('.exe') and not binary.lower().endswith('.bat'):
  585. # Append .exe if necessary
  586. suffixes = ['.exe', '.bat']
  587. # On Windows the current directory is always implicitly
  588. # searched before anything else on PATH.
  589. pathList = ['.'] + pathList
  590. for path in pathList:
  591. binpath = os.path.join(os.path.expanduser(path), binary)
  592. for suffix in suffixes:
  593. if os.access(binpath + suffix, os.X_OK):
  594. return os.path.abspath(os.path.realpath(binpath + suffix))
  595. return None
  596. ########################################################################
  597. ##
  598. ## Run a command.
  599. ##
  600. ########################################################################
  601. def oscmd(cmd, ignoreError = False, cwd=None):
  602. if VERBOSE:
  603. print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor())
  604. sys.stdout.flush()
  605. if sys.platform == "win32":
  606. if cmd[0] == '"':
  607. exe = cmd[1 : cmd.index('"', 1)]
  608. else:
  609. exe = cmd.split()[0]
  610. exe_path = LocateBinary(exe)
  611. if exe_path is None:
  612. exit("Cannot find "+exe+" on search path")
  613. if cwd is not None:
  614. pwd = os.getcwd()
  615. os.chdir(cwd)
  616. res = os.spawnl(os.P_WAIT, exe_path, cmd)
  617. if res == -1073741510: # 0xc000013a
  618. exit("keyboard interrupt")
  619. if cwd is not None:
  620. os.chdir(pwd)
  621. else:
  622. cmd = cmd.replace(';', '\\;')
  623. cmd = cmd.replace('$', '\\$')
  624. res = subprocess.call(cmd, cwd=cwd, shell=True)
  625. sig = res & 0x7F
  626. if (GetVerbose() and res != 0):
  627. print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig)))
  628. if (sig == signal.SIGINT):
  629. exit("keyboard interrupt")
  630. # Don't ask me where the 35584 or 34304 come from...
  631. if (sig == signal.SIGSEGV or res == 35584 or res == 34304):
  632. if (LocateBinary("gdb") and GetVerbose() and GetHost() != "windows"):
  633. print(ColorText("red", "Received SIGSEGV, retrieving traceback..."))
  634. os.system("gdb -batch -ex 'handle SIG33 pass nostop noprint' -ex 'set pagination 0' -ex 'run' -ex 'bt full' -ex 'info registers' -ex 'thread apply all backtrace' -ex 'quit' --args %s < /dev/null" % cmd)
  635. else:
  636. print(ColorText("red", "Received SIGSEGV"))
  637. exit("")
  638. if res != 0 and not ignoreError:
  639. if "interrogate" in cmd.split(" ", 1)[0] and GetVerbose():
  640. print(ColorText("red", "Interrogate failed, retrieving debug output..."))
  641. sys.stdout.flush()
  642. verbose_cmd = cmd.split(" ", 1)[0] + " -vv " + cmd.split(" ", 1)[1]
  643. if sys.platform == "win32":
  644. os.spawnl(os.P_WAIT, exe_path, verbose_cmd)
  645. else:
  646. subprocess.call(verbose_cmd, shell=True)
  647. exit("The following command returned a non-zero value: " + str(cmd))
  648. return res
  649. ########################################################################
  650. ##
  651. ## GetDirectoryContents
  652. ##
  653. ########################################################################
  654. def GetDirectoryContents(dir, filters="*", skip=[]):
  655. if isinstance(filters, str):
  656. filters = [filters]
  657. actual = {}
  658. files = os.listdir(dir)
  659. for filter in filters:
  660. for file in fnmatch.filter(files, filter):
  661. if (skip.count(file)==0) and (os.path.isfile(dir + "/" + file)):
  662. actual[file] = 1
  663. results = list(actual.keys())
  664. results.sort()
  665. return results
  666. def GetDirectorySize(dir):
  667. if not os.path.isdir(dir):
  668. return 0
  669. size = 0
  670. for (path, dirs, files) in os.walk(dir):
  671. for file in files:
  672. try:
  673. size += os.path.getsize(os.path.join(path, file))
  674. except:
  675. pass
  676. return size
  677. ########################################################################
  678. ##
  679. ## The Timestamp Cache
  680. ##
  681. ## The make utility is constantly fetching the timestamps of files.
  682. ## This can represent the bulk of the file accesses during the make
  683. ## process. The timestamp cache eliminates redundant checks.
  684. ##
  685. ########################################################################
  686. TIMESTAMPCACHE = {}
  687. def GetTimestamp(path):
  688. if path in TIMESTAMPCACHE:
  689. return TIMESTAMPCACHE[path]
  690. try:
  691. date = int(os.path.getmtime(path))
  692. except:
  693. date = 0
  694. TIMESTAMPCACHE[path] = date
  695. return date
  696. def ClearTimestamp(path):
  697. del TIMESTAMPCACHE[path]
  698. ########################################################################
  699. ##
  700. ## The Dependency cache.
  701. ##
  702. ## Makepanda's strategy for file dependencies is different from most
  703. ## make-utilities. Whenever a file is built, makepanda records
  704. ## that the file was built, and it records what the input files were,
  705. ## and what their dates were. Whenever a file is about to be built,
  706. ## panda compares the current list of input files and their dates,
  707. ## to the previous list of input files and their dates. If they match,
  708. ## there is no need to build the file.
  709. ##
  710. ########################################################################
  711. BUILTFROMCACHE = {}
  712. def JustBuilt(files, others):
  713. dates = {}
  714. for file in files:
  715. del TIMESTAMPCACHE[file]
  716. dates[file] = GetTimestamp(file)
  717. for file in others:
  718. dates[file] = GetTimestamp(file)
  719. key = tuple(files)
  720. BUILTFROMCACHE[key] = dates
  721. def NeedsBuild(files, others):
  722. dates = {}
  723. for file in files:
  724. dates[file] = GetTimestamp(file)
  725. if not os.path.exists(file):
  726. if DEBUG_DEPENDENCIES:
  727. print("rebuilding %s because it does not exist" % (file))
  728. return True
  729. for file in others:
  730. dates[file] = GetTimestamp(file)
  731. key = tuple(files)
  732. if key in BUILTFROMCACHE:
  733. cached = BUILTFROMCACHE[key]
  734. if cached == dates:
  735. return False
  736. elif DEBUG_DEPENDENCIES:
  737. print("rebuilding %s because:" % (key))
  738. for key in frozenset(cached.keys()) | frozenset(dates.keys()):
  739. if key not in cached:
  740. print(" new dependency: %s" % (key))
  741. elif key not in dates:
  742. print(" removed dependency: %s" % (key))
  743. elif cached[key] != dates[key]:
  744. print(" dependency changed: %s" % (key))
  745. if VERBOSE and frozenset(cached) != frozenset(dates):
  746. Warn("file dependencies changed:", files)
  747. return True
  748. ########################################################################
  749. ##
  750. ## The CXX include cache:
  751. ##
  752. ## The following routine scans a CXX file and returns a list of
  753. ## the include-directives inside that file. It's not recursive:
  754. ## it just returns the includes that are textually inside the
  755. ## file. If you need recursive dependencies, you need the higher-level
  756. ## routine CxxCalcDependencies, defined elsewhere.
  757. ##
  758. ## Since scanning a CXX file is slow, we cache the result. It records
  759. ## the date of the source file and the list of includes that it
  760. ## contains. It assumes that if the file date hasn't changed, that
  761. ## the list of include-statements inside the file has not changed
  762. ## either. Once again, this particular routine does not return
  763. ## recursive dependencies --- it only returns an explicit list of
  764. ## include statements that are textually inside the file. That
  765. ## is what the cache stores, as well.
  766. ##
  767. ########################################################################
  768. CXXINCLUDECACHE = {}
  769. CxxIncludeRegex = re.compile('^[ \t]*[#][ \t]*include[ \t]+"([^"]+)"[ \t\r\n]*$')
  770. def CxxGetIncludes(path):
  771. date = GetTimestamp(path)
  772. if path in CXXINCLUDECACHE:
  773. cached = CXXINCLUDECACHE[path]
  774. if cached[0] == date:
  775. return cached[1]
  776. try:
  777. sfile = open(path, 'r')
  778. except:
  779. exit("Cannot open source file \""+path+"\" for reading.")
  780. include = []
  781. try:
  782. for line in sfile:
  783. match = CxxIncludeRegex.match(line,0)
  784. if match:
  785. incname = match.group(1)
  786. include.append(incname)
  787. except:
  788. print("Failed to determine dependencies of \""+path+"\".")
  789. raise
  790. sfile.close()
  791. CXXINCLUDECACHE[path] = [date, include]
  792. return include
  793. JAVAIMPORTCACHE = {}
  794. JavaImportRegex = re.compile('[ \t\r\n;]import[ \t]+([a-zA-Z][^;]+)[ \t\r\n]*;')
  795. def JavaGetImports(path):
  796. date = GetTimestamp(path)
  797. if path in JAVAIMPORTCACHE:
  798. cached = JAVAIMPORTCACHE[path]
  799. if cached[0] == date:
  800. return cached[1]
  801. try:
  802. source = open(path, 'r').read()
  803. except:
  804. exit("Cannot open source file \"" + path + "\" for reading.")
  805. imports = []
  806. try:
  807. for match in JavaImportRegex.finditer(source, 0):
  808. impname = match.group(1).strip()
  809. if not impname.startswith('java.') and \
  810. not impname.startswith('dalvik.') and \
  811. not impname.startswith('android.'):
  812. imports.append(impname.strip())
  813. except:
  814. print("Failed to determine dependencies of \"" + path +"\".")
  815. raise
  816. JAVAIMPORTCACHE[path] = [date, imports]
  817. return imports
  818. ########################################################################
  819. ##
  820. ## SaveDependencyCache / LoadDependencyCache
  821. ##
  822. ## This actually saves both the dependency and cxx-include caches.
  823. ##
  824. ########################################################################
  825. DCACHE_VERSION = 3
  826. DCACHE_BACKED_UP = False
  827. def SaveDependencyCache():
  828. global DCACHE_BACKED_UP
  829. if not DCACHE_BACKED_UP:
  830. try:
  831. if os.path.exists(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache")):
  832. os.rename(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),
  833. os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache-backup"))
  834. except:
  835. pass
  836. DCACHE_BACKED_UP = True
  837. try:
  838. icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb')
  839. except:
  840. icache = None
  841. if icache is not None:
  842. print("Storing dependency cache.")
  843. pickle.dump(DCACHE_VERSION, icache, 0)
  844. pickle.dump(CXXINCLUDECACHE, icache, 2)
  845. pickle.dump(BUILTFROMCACHE, icache, 2)
  846. icache.close()
  847. def LoadDependencyCache():
  848. global CXXINCLUDECACHE
  849. global BUILTFROMCACHE
  850. try:
  851. icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"), 'rb')
  852. except:
  853. icache = None
  854. if icache is not None:
  855. ver = pickle.load(icache)
  856. if ver == DCACHE_VERSION:
  857. CXXINCLUDECACHE = pickle.load(icache)
  858. BUILTFROMCACHE = pickle.load(icache)
  859. icache.close()
  860. else:
  861. print("Cannot load dependency cache, version is too old!")
  862. ########################################################################
  863. ##
  864. ## CxxFindSource: given a source file name and a directory list,
  865. ## searches the directory list for the given source file. Returns
  866. ## the full pathname of the located file.
  867. ##
  868. ## CxxFindHeader: given a source file, an include directive, and a
  869. ## directory list, searches the directory list for the given header
  870. ## file. Returns the full pathname of the located file.
  871. ##
  872. ## Of course, CxxFindSource and CxxFindHeader cannot find a source
  873. ## file that has not been created yet. This can cause dependency
  874. ## problems. So the function CreateStubHeader can be used to create
  875. ## a file that CxxFindSource or CxxFindHeader can subsequently find.
  876. ##
  877. ########################################################################
  878. def CxxFindSource(name, ipath):
  879. for dir in ipath:
  880. if (dir == "."): full = name
  881. else: full = dir + "/" + name
  882. if GetTimestamp(full) > 0: return full
  883. exit("Could not find source file: "+name)
  884. def CxxFindHeader(srcfile, incfile, ipath):
  885. if (incfile.startswith(".")):
  886. last = srcfile.rfind("/")
  887. if (last < 0): exit("CxxFindHeader cannot handle this case #1")
  888. srcdir = srcfile[:last+1]
  889. while (incfile[:1]=="."):
  890. if (incfile[:2]=="./"):
  891. incfile = incfile[2:]
  892. elif (incfile[:3]=="../"):
  893. incfile = incfile[3:]
  894. last = srcdir[:-1].rfind("/")
  895. if (last < 0): exit("CxxFindHeader cannot handle this case #2")
  896. srcdir = srcdir[:last+1]
  897. else: exit("CxxFindHeader cannot handle this case #3")
  898. full = srcdir + incfile
  899. if GetTimestamp(full) > 0: return full
  900. return 0
  901. else:
  902. for dir in ipath:
  903. full = dir + "/" + incfile
  904. if GetTimestamp(full) > 0: return full
  905. return 0
  906. def JavaFindClasses(impspec, clspath):
  907. path = clspath + '/' + impspec.replace('.', '/') + '.class'
  908. if '*' in path:
  909. return glob.glob(path)
  910. else:
  911. return [path]
  912. ########################################################################
  913. ##
  914. ## CxxCalcDependencies(srcfile, ipath, ignore)
  915. ##
  916. ## Calculate the dependencies of a source file given a
  917. ## particular include-path. Any file in the list of files to
  918. ## ignore is not considered.
  919. ##
  920. ########################################################################
  921. CxxIgnoreHeader = {}
  922. CxxDependencyCache = {}
  923. def CxxCalcDependencies(srcfile, ipath, ignore):
  924. if srcfile in CxxDependencyCache:
  925. return CxxDependencyCache[srcfile]
  926. if ignore.count(srcfile):
  927. return []
  928. dep = {}
  929. dep[srcfile] = 1
  930. includes = CxxGetIncludes(srcfile)
  931. for include in includes:
  932. header = CxxFindHeader(srcfile, include, ipath)
  933. if header != 0:
  934. if ignore.count(header) == 0:
  935. hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore)
  936. for x in hdeps:
  937. dep[x] = 1
  938. result = list(dep.keys())
  939. CxxDependencyCache[srcfile] = result
  940. return result
  941. global JavaDependencyCache
  942. JavaDependencyCache = {}
  943. def JavaCalcDependencies(srcfile, clspath):
  944. if srcfile in JavaDependencyCache:
  945. return JavaDependencyCache[srcfile]
  946. deps = set((srcfile,))
  947. JavaDependencyCache[srcfile] = deps
  948. imports = JavaGetImports(srcfile)
  949. for impspec in imports:
  950. for cls in JavaFindClasses(impspec, clspath):
  951. deps.add(cls)
  952. return deps
  953. ########################################################################
  954. ##
  955. ## Registry Key Handling
  956. ##
  957. ## Of course, these routines will fail if you call them on a
  958. ## non win32 platform. If you use them on a win64 platform, they
  959. ## will look in the win32 private hive first, then look in the
  960. ## win64 hive.
  961. ##
  962. ########################################################################
  963. if sys.platform == "win32":
  964. # Note: not supported on cygwin.
  965. import winreg
  966. def TryRegistryKey(path):
  967. try:
  968. key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ)
  969. return key
  970. except:
  971. pass
  972. try:
  973. key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ | 256)
  974. return key
  975. except:
  976. pass
  977. return 0
  978. def ListRegistryKeys(path):
  979. result=[]
  980. index=0
  981. key = TryRegistryKey(path)
  982. if (key != 0):
  983. try:
  984. while (1):
  985. result.append(winreg.EnumKey(key, index))
  986. index = index + 1
  987. except:
  988. pass
  989. winreg.CloseKey(key)
  990. return result
  991. def ListRegistryValues(path):
  992. result = []
  993. index = 0
  994. key = TryRegistryKey(path)
  995. if (key != 0):
  996. try:
  997. while (1):
  998. result.append(winreg.EnumValue(key, index)[0])
  999. index = index + 1
  1000. except:
  1001. pass
  1002. winreg.CloseKey(key)
  1003. return result
  1004. def GetRegistryKey(path, subkey, override64=True):
  1005. if (host_64 and override64):
  1006. path = path.replace("SOFTWARE\\", "SOFTWARE\\Wow6432Node\\")
  1007. k1=0
  1008. key = TryRegistryKey(path)
  1009. if (key != 0):
  1010. try:
  1011. k1, k2 = winreg.QueryValueEx(key, subkey)
  1012. except:
  1013. pass
  1014. winreg.CloseKey(key)
  1015. return k1
  1016. def GetProgramFiles():
  1017. if ("PROGRAMFILES" in os.environ):
  1018. return os.environ["PROGRAMFILES"]
  1019. elif (os.path.isdir("C:\\Program Files")):
  1020. return "C:\\Program Files"
  1021. elif (os.path.isdir("D:\\Program Files")):
  1022. return "D:\\Program Files"
  1023. elif (os.path.isdir("E:\\Program Files")):
  1024. return "E:\\Program Files"
  1025. return 0
  1026. def GetProgramFiles_x86():
  1027. if ("ProgramFiles(x86)" in os.environ):
  1028. return os.environ["ProgramFiles(x86)"]
  1029. elif (os.path.isdir("C:\\Program Files (x86)")):
  1030. return "C:\\Program Files (x86)"
  1031. elif (os.path.isdir("D:\\Program Files (x86)")):
  1032. return "D:\\Program Files (x86)"
  1033. elif (os.path.isdir("E:\\Program Files (x86)")):
  1034. return "E:\\Program Files (x86)"
  1035. return GetProgramFiles()
  1036. ########################################################################
  1037. ##
  1038. ## Parsing Compiler Option Lists
  1039. ##
  1040. ########################################################################
  1041. def GetListOption(opts, prefix):
  1042. res=[]
  1043. for x in opts:
  1044. if (x.startswith(prefix)):
  1045. res.append(x[len(prefix):])
  1046. return res
  1047. def GetValueOption(opts, prefix):
  1048. for x in opts:
  1049. if (x.startswith(prefix)):
  1050. return x[len(prefix):]
  1051. return 0
  1052. def GetOptimizeOption(opts):
  1053. val = GetValueOption(opts, "OPT:")
  1054. if (val == 0):
  1055. return OPTIMIZE
  1056. return val
  1057. ########################################################################
  1058. ##
  1059. ## General File Manipulation
  1060. ##
  1061. ########################################################################
  1062. def MakeDirectory(path, mode=None, recursive=False):
  1063. if os.path.isdir(path):
  1064. return
  1065. if recursive:
  1066. parent = os.path.dirname(path)
  1067. if parent and not os.path.isdir(parent):
  1068. MakeDirectory(parent, mode=mode, recursive=True)
  1069. if mode is not None:
  1070. os.mkdir(path, mode)
  1071. else:
  1072. os.mkdir(path)
  1073. def ReadFile(wfile):
  1074. try:
  1075. srchandle = open(wfile, "r")
  1076. data = srchandle.read()
  1077. srchandle.close()
  1078. return data
  1079. except:
  1080. ex = sys.exc_info()[1]
  1081. exit("Cannot read %s: %s" % (wfile, ex))
  1082. def ReadBinaryFile(wfile):
  1083. try:
  1084. srchandle = open(wfile, "rb")
  1085. data = srchandle.read()
  1086. srchandle.close()
  1087. return data
  1088. except:
  1089. ex = sys.exc_info()[1]
  1090. exit("Cannot read %s: %s" % (wfile, ex))
  1091. def WriteFile(wfile, data, newline=None):
  1092. if newline is not None:
  1093. data = data.replace('\r\n', '\n')
  1094. data = data.replace('\r', '\n')
  1095. data = data.replace('\n', newline)
  1096. try:
  1097. dsthandle = open(wfile, "w", newline='')
  1098. dsthandle.write(data)
  1099. dsthandle.close()
  1100. except:
  1101. ex = sys.exc_info()[1]
  1102. exit("Cannot write to %s: %s" % (wfile, ex))
  1103. def WriteBinaryFile(wfile, data):
  1104. try:
  1105. dsthandle = open(wfile, "wb")
  1106. dsthandle.write(data)
  1107. dsthandle.close()
  1108. except:
  1109. ex = sys.exc_info()[1]
  1110. exit("Cannot write to %s: %s" % (wfile, ex))
  1111. def ConditionalWriteFile(dest, data, newline=None):
  1112. if newline is not None:
  1113. data = data.replace('\r\n', '\n')
  1114. data = data.replace('\r', '\n')
  1115. data = data.replace('\n', newline)
  1116. try:
  1117. rfile = open(dest, 'r')
  1118. contents = rfile.read(-1)
  1119. rfile.close()
  1120. except:
  1121. contents = 0
  1122. if contents != data:
  1123. if VERBOSE:
  1124. print("Writing %s" % (dest))
  1125. sys.stdout.flush()
  1126. WriteFile(dest, data)
  1127. def DeleteVCS(dir):
  1128. if dir == "": dir = "."
  1129. for entry in os.listdir(dir):
  1130. subdir = os.path.join(dir, entry)
  1131. if (os.path.isdir(subdir)):
  1132. if entry in VCS_DIRS:
  1133. shutil.rmtree(subdir)
  1134. else:
  1135. DeleteVCS(subdir)
  1136. elif (os.path.isfile(subdir) and (entry in VCS_FILES or entry.startswith(".#"))):
  1137. os.remove(subdir)
  1138. def DeleteBuildFiles(dir):
  1139. if dir == "": dir = "."
  1140. for entry in os.listdir(dir):
  1141. subdir = os.path.join(dir, entry)
  1142. if (os.path.isfile(subdir) and os.path.splitext(subdir)[-1] in [".pp", ".moved"]):
  1143. os.remove(subdir)
  1144. elif (os.path.isdir(subdir)):
  1145. if (os.path.basename(subdir)[:3] == "Opt" and os.path.basename(subdir)[4] == "-"):
  1146. shutil.rmtree(subdir)
  1147. else:
  1148. DeleteBuildFiles(subdir)
  1149. def DeleteEmptyDirs(dir):
  1150. if dir == "": dir = "."
  1151. entries = os.listdir(dir)
  1152. if not entries:
  1153. os.rmdir(dir)
  1154. return
  1155. for entry in entries:
  1156. subdir = os.path.join(dir, entry)
  1157. if (os.path.isdir(subdir)):
  1158. if (not os.listdir(subdir)):
  1159. os.rmdir(subdir)
  1160. else:
  1161. DeleteEmptyDirs(subdir)
  1162. def CreateFile(file):
  1163. if (os.path.exists(file)==0):
  1164. WriteFile(file, "")
  1165. ########################################################################
  1166. #
  1167. # Create the panda build tree.
  1168. #
  1169. ########################################################################
  1170. def MakeBuildTree():
  1171. MakeDirectory(OUTPUTDIR)
  1172. MakeDirectory(OUTPUTDIR + "/bin")
  1173. MakeDirectory(OUTPUTDIR + "/lib")
  1174. MakeDirectory(OUTPUTDIR + "/tmp")
  1175. MakeDirectory(OUTPUTDIR + "/etc")
  1176. MakeDirectory(OUTPUTDIR + "/plugins")
  1177. MakeDirectory(OUTPUTDIR + "/include")
  1178. MakeDirectory(OUTPUTDIR + "/models")
  1179. MakeDirectory(OUTPUTDIR + "/models/audio")
  1180. MakeDirectory(OUTPUTDIR + "/models/audio/sfx")
  1181. MakeDirectory(OUTPUTDIR + "/models/icons")
  1182. MakeDirectory(OUTPUTDIR + "/models/maps")
  1183. MakeDirectory(OUTPUTDIR + "/models/misc")
  1184. MakeDirectory(OUTPUTDIR + "/models/gui")
  1185. MakeDirectory(OUTPUTDIR + "/pandac")
  1186. MakeDirectory(OUTPUTDIR + "/pandac/input")
  1187. MakeDirectory(OUTPUTDIR + "/panda3d")
  1188. if GetTarget() == 'android':
  1189. MakeDirectory(OUTPUTDIR + "/classes")
  1190. ########################################################################
  1191. #
  1192. # Make sure that you are in the root of the panda tree.
  1193. #
  1194. ########################################################################
  1195. def CheckPandaSourceTree():
  1196. dir = os.getcwd()
  1197. if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or
  1198. (os.path.exists(os.path.join(dir, "dtool", "src", "dtoolbase", "dtoolbase.h"))==0) or
  1199. (os.path.exists(os.path.join(dir, "panda", "src", "pandabase", "pandabase.h"))==0)):
  1200. exit("Current directory is not the root of the panda tree.")
  1201. ########################################################################
  1202. ##
  1203. ## Thirdparty libraries paths
  1204. ##
  1205. ########################################################################
  1206. def GetThirdpartyBase():
  1207. """Returns the location of the 'thirdparty' directory.
  1208. Normally, this is simply the thirdparty directory relative
  1209. to the root of the source root, but if a MAKEPANDA_THIRDPARTY
  1210. environment variable was set, it is used as the location of the
  1211. thirdparty directory. This is useful when wanting to use a single
  1212. system-wide thirdparty directory, for instance on a build machine."""
  1213. global THIRDPARTYBASE
  1214. if (THIRDPARTYBASE is not None):
  1215. return THIRDPARTYBASE
  1216. THIRDPARTYBASE = "thirdparty"
  1217. if "MAKEPANDA_THIRDPARTY" in os.environ:
  1218. THIRDPARTYBASE = os.environ["MAKEPANDA_THIRDPARTY"]
  1219. return THIRDPARTYBASE
  1220. def GetThirdpartyDir():
  1221. """Returns the thirdparty directory for the target platform,
  1222. ie. thirdparty/win-libs-vc10/. May return None in the future."""
  1223. global THIRDPARTYDIR
  1224. if THIRDPARTYDIR is not None:
  1225. return THIRDPARTYDIR
  1226. base = GetThirdpartyBase()
  1227. target = GetTarget()
  1228. target_arch = GetTargetArch()
  1229. if (target == 'windows'):
  1230. vc = str(SDK["MSVC_VERSION"][0])
  1231. if target_arch == 'x64':
  1232. THIRDPARTYDIR = base + "/win-libs-vc" + vc + "-x64/"
  1233. else:
  1234. THIRDPARTYDIR = base + "/win-libs-vc" + vc + "/"
  1235. elif (target == 'darwin'):
  1236. # macOS thirdparty binaries are universal, where possible.
  1237. THIRDPARTYDIR = base + "/darwin-libs-a/"
  1238. elif (target == 'linux'):
  1239. if target_arch in ("aarch64", "arm64"):
  1240. THIRDPARTYDIR = base + "/linux-libs-arm64/"
  1241. elif target_arch.startswith("arm"):
  1242. THIRDPARTYDIR = base + "/linux-libs-arm/"
  1243. elif (target_arch in ("x86_64", "amd64")):
  1244. THIRDPARTYDIR = base + "/linux-libs-x64/"
  1245. else:
  1246. THIRDPARTYDIR = base + "/linux-libs-a/"
  1247. elif (target == 'freebsd'):
  1248. if target_arch in ("aarch64", "arm64"):
  1249. THIRDPARTYDIR = base + "/freebsd-libs-arm64/"
  1250. elif target_arch.startswith("arm"):
  1251. THIRDPARTYDIR = base + "/freebsd-libs-arm/"
  1252. elif (target_arch in ("x86_64", "amd64")):
  1253. THIRDPARTYDIR = base + "/freebsd-libs-x64/"
  1254. else:
  1255. THIRDPARTYDIR = base + "/freebsd-libs-a/"
  1256. elif (target == 'android'):
  1257. THIRDPARTYDIR = base + "/android-libs-%s/" % (target_arch)
  1258. elif (target == 'emscripten'):
  1259. THIRDPARTYDIR = base + "/emscripten-libs/"
  1260. else:
  1261. Warn("Unsupported platform:", target)
  1262. return
  1263. if (GetVerbose()):
  1264. print("Using thirdparty directory: %s" % THIRDPARTYDIR)
  1265. return THIRDPARTYDIR
  1266. ########################################################################
  1267. ##
  1268. ## Gets or sets the output directory, by default "built".
  1269. ## Gets or sets the optimize level.
  1270. ## Gets or sets the verbose flag.
  1271. ##
  1272. ########################################################################
  1273. def GetOutputDir():
  1274. return OUTPUTDIR
  1275. def IsCustomOutputDir():
  1276. return CUSTOM_OUTPUTDIR
  1277. def SetOutputDir(outputdir):
  1278. global OUTPUTDIR, CUSTOM_OUTPUTDIR
  1279. OUTPUTDIR = outputdir
  1280. CUSTOM_OUTPUTDIR = True
  1281. def GetOptimize():
  1282. return int(OPTIMIZE)
  1283. def SetOptimize(optimize):
  1284. global OPTIMIZE
  1285. OPTIMIZE = optimize
  1286. def GetVerbose():
  1287. return VERBOSE
  1288. def SetVerbose(verbose):
  1289. global VERBOSE
  1290. VERBOSE = verbose
  1291. def SetDebugDependencies(dd = True):
  1292. global DEBUG_DEPENDENCIES
  1293. DEBUG_DEPENDENCIES = dd
  1294. def GetLinkAllStatic():
  1295. return LINK_ALL_STATIC
  1296. def SetLinkAllStatic(val = True):
  1297. global LINK_ALL_STATIC
  1298. LINK_ALL_STATIC = val
  1299. def UnsetLinkAllStatic():
  1300. global LINK_ALL_STATIC
  1301. LINK_ALL_STATIC = False
  1302. ########################################################################
  1303. ##
  1304. ## Package Selection
  1305. ##
  1306. ## This facility enables makepanda to keep a list of packages selected
  1307. ## by the user for inclusion or omission.
  1308. ##
  1309. ########################################################################
  1310. PKG_LIST_ALL = []
  1311. PKG_LIST_OMIT = {}
  1312. PKG_LIST_CUSTOM = set()
  1313. def PkgListSet(pkgs):
  1314. global PKG_LIST_ALL
  1315. global PKG_LIST_OMIT
  1316. PKG_LIST_ALL=pkgs
  1317. PKG_LIST_OMIT={}
  1318. PkgEnableAll()
  1319. def PkgListGet():
  1320. return PKG_LIST_ALL
  1321. def PkgEnableAll():
  1322. for x in PKG_LIST_ALL:
  1323. PKG_LIST_OMIT[x] = 0
  1324. def PkgDisableAll():
  1325. for x in PKG_LIST_ALL:
  1326. PKG_LIST_OMIT[x] = 1
  1327. def PkgEnable(pkg):
  1328. PKG_LIST_OMIT[pkg] = 0
  1329. def PkgDisable(pkg):
  1330. PKG_LIST_OMIT[pkg] = 1
  1331. def PkgSetCustomLocation(pkg):
  1332. PKG_LIST_CUSTOM.add(pkg)
  1333. def PkgHasCustomLocation(pkg):
  1334. return pkg in PKG_LIST_CUSTOM
  1335. def PkgSkip(pkg):
  1336. return PKG_LIST_OMIT[pkg]
  1337. def PkgSelected(pkglist, pkg):
  1338. if (pkglist.count(pkg)==0): return 0
  1339. if (PKG_LIST_OMIT[pkg]): return 0
  1340. return 1
  1341. ########################################################################
  1342. ##
  1343. ## DTOOL/PRC Option value override
  1344. ##
  1345. ## This facility enables makepanda to keep a list of parameters
  1346. ## overriden by the command line.
  1347. ##
  1348. ########################################################################
  1349. OVERRIDES_LIST={}
  1350. def AddOverride(spec):
  1351. if (spec.find("=")==-1):return
  1352. pair = spec.split("=",1)
  1353. OVERRIDES_LIST[pair[0]] = pair[1]
  1354. def OverrideValue(parameter, value):
  1355. if parameter in OVERRIDES_LIST:
  1356. print("Overriding value of key \"" + parameter + "\" with value \"" + OVERRIDES_LIST[parameter] + "\"")
  1357. return OVERRIDES_LIST[parameter]
  1358. else:
  1359. return value
  1360. ########################################################################
  1361. ##
  1362. ## These functions are for libraries which use pkg-config.
  1363. ##
  1364. ########################################################################
  1365. def PkgConfigHavePkg(pkgname, tool = "pkg-config"):
  1366. """Returns a bool whether the pkg-config package is installed."""
  1367. if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
  1368. return False
  1369. if (tool == "pkg-config"):
  1370. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
  1371. else:
  1372. return bool(LocateBinary(tool) is not None)
  1373. result = handle.read().strip()
  1374. returnval = handle.close()
  1375. if returnval is not None and returnval != 0:
  1376. return False
  1377. return bool(len(result) > 0)
  1378. def PkgConfigGetLibs(pkgname, tool = "pkg-config"):
  1379. """Returns a list of libs for the package, prefixed by -l."""
  1380. if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
  1381. return []
  1382. if (tool == "pkg-config"):
  1383. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
  1384. elif (tool == "fltk-config"):
  1385. handle = os.popen(LocateBinary("fltk-config") + " --ldstaticflags")
  1386. else:
  1387. handle = os.popen(LocateBinary(tool) + " --libs")
  1388. result = handle.read().strip()
  1389. handle.close()
  1390. libs = []
  1391. # Walk through the result arguments carefully. Look for -lname as
  1392. # well as -framework name.
  1393. r = result.split(' ')
  1394. ri = 0
  1395. while ri < len(r):
  1396. l = r[ri]
  1397. if l.startswith("-l") or l.startswith("/"):
  1398. libs.append(l)
  1399. elif l == '-framework':
  1400. libs.append(l)
  1401. ri += 1
  1402. libs.append(r[ri])
  1403. ri += 1
  1404. return libs
  1405. def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
  1406. """Returns a list of includes for the package, NOT prefixed by -I."""
  1407. if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
  1408. return []
  1409. if (tool == "pkg-config"):
  1410. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
  1411. else:
  1412. handle = os.popen(LocateBinary(tool) + " --cflags")
  1413. result = handle.read().strip()
  1414. handle.close()
  1415. if len(result) == 0: return []
  1416. dirs = []
  1417. for opt in result.split(" "):
  1418. if (opt.startswith("-I")):
  1419. inc_dir = opt.replace("-I", "").replace("\"", "").strip()
  1420. # Hack for ODE, otherwise -S/usr/include gets added to interrogate
  1421. if inc_dir != '/usr/include' and inc_dir != '/usr/include/':
  1422. dirs.append(inc_dir)
  1423. return dirs
  1424. def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"):
  1425. """Returns a list of library paths for the package, NOT prefixed by -L."""
  1426. if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
  1427. return []
  1428. if (tool == "pkg-config"):
  1429. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
  1430. elif (tool == "wx-config" or tool == "ode-config"):
  1431. return []
  1432. else:
  1433. handle = os.popen(LocateBinary(tool) + " --ldflags")
  1434. result = handle.read().strip()
  1435. handle.close()
  1436. if len(result) == 0: return []
  1437. libs = []
  1438. for l in result.split(" "):
  1439. if (l.startswith("-L")):
  1440. libs.append(l.replace("-L", "").replace("\"", "").strip())
  1441. return libs
  1442. def PkgConfigGetDefSymbols(pkgname, tool = "pkg-config"):
  1443. """Returns a dictionary of preprocessor definitions."""
  1444. if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
  1445. return []
  1446. if (tool == "pkg-config"):
  1447. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags " + pkgname)
  1448. else:
  1449. handle = os.popen(LocateBinary(tool) + " --cflags")
  1450. result = handle.read().strip()
  1451. handle.close()
  1452. if len(result) == 0: return {}
  1453. defs = {}
  1454. for l in result.split(" "):
  1455. if (l.startswith("-D")):
  1456. d = l.replace("-D", "").replace("\"", "").strip().split("=")
  1457. if d[0] in ('NDEBUG', '_DEBUG'):
  1458. # Setting one of these flags by accident could cause serious harm.
  1459. if GetVerbose():
  1460. print("Ignoring %s flag provided by %s" % (l, tool))
  1461. elif len(d) == 1:
  1462. defs[d[0]] = ""
  1463. else:
  1464. defs[d[0]] = d[1]
  1465. return defs
  1466. def PkgConfigEnable(opt, pkgname, tool = "pkg-config"):
  1467. """Adds the libraries and includes to IncDirectory, LibName and LibDirectory."""
  1468. for i in PkgConfigGetIncDirs(pkgname, tool):
  1469. IncDirectory(opt, i)
  1470. for i in PkgConfigGetLibDirs(pkgname, tool):
  1471. LibDirectory(opt, i)
  1472. for i in PkgConfigGetLibs(pkgname, tool):
  1473. LibName(opt, i)
  1474. for i, j in PkgConfigGetDefSymbols(pkgname, tool).items():
  1475. DefSymbol(opt, i, j)
  1476. def LocateLibrary(lib, lpath=[], prefer_static=False):
  1477. """Searches for the library in the search path, returning its path if found,
  1478. or None if it was not found."""
  1479. target = GetTarget()
  1480. if prefer_static and target != 'windows':
  1481. for dir in lpath:
  1482. if os.path.isfile(os.path.join(dir, 'lib%s.a' % lib)):
  1483. return os.path.join(dir, 'lib%s.a' % lib)
  1484. for dir in lpath:
  1485. if target == 'windows':
  1486. if os.path.isfile(os.path.join(dir, lib + '.lib')):
  1487. return os.path.join(dir, lib + '.lib')
  1488. elif target == 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.dylib' % lib)):
  1489. return os.path.join(dir, 'lib%s.dylib' % lib)
  1490. elif target != 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.so' % lib)):
  1491. return os.path.join(dir, 'lib%s.so' % lib)
  1492. elif os.path.isfile(os.path.join(dir, 'lib%s.a' % lib)):
  1493. return os.path.join(dir, 'lib%s.a' % lib)
  1494. return None
  1495. def SystemLibraryExists(lib):
  1496. result = LocateLibrary(lib, SYS_LIB_DIRS)
  1497. if result is not None:
  1498. return True
  1499. if GetHost() == "android" and GetTarget() == "android":
  1500. return ('lib%s.so' % lib) in ANDROID_SYS_LIBS
  1501. return False
  1502. def ChooseLib(libs, thirdparty=None):
  1503. """ Chooses a library from the parameters, in order of preference. Returns the first if none of them were found. """
  1504. lpath = []
  1505. if thirdparty is not None:
  1506. lpath.append(os.path.join(GetThirdpartyDir(), thirdparty.lower(), "lib"))
  1507. lpath += SYS_LIB_DIRS
  1508. for l in libs:
  1509. libname = l
  1510. if l.startswith("lib"):
  1511. libname = l[3:]
  1512. if LocateLibrary(libname, lpath):
  1513. return libname
  1514. if len(libs) > 0:
  1515. if VERBOSE:
  1516. print(ColorText("cyan", "Couldn't find any of the libraries " + ", ".join(libs)))
  1517. return libs[0]
  1518. def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, framework = None, target_pkg = None, tool = "pkg-config", thirdparty_dir = None):
  1519. global PKG_LIST_ALL
  1520. if (pkg in PkgListGet() and PkgSkip(pkg)):
  1521. return
  1522. if (target_pkg == "" or target_pkg is None):
  1523. target_pkg = pkg
  1524. if (pkgconfig == ""):
  1525. pkgconfig = None
  1526. if (framework == ""):
  1527. framework = None
  1528. if (libs is None or libs == ""):
  1529. libs = ()
  1530. elif (isinstance(libs, str)):
  1531. libs = (libs, )
  1532. if (incs is None or incs == ""):
  1533. incs = ()
  1534. elif (isinstance(incs, str)):
  1535. incs = (incs, )
  1536. if (defs is None or defs == "" or len(defs) == 0):
  1537. defs = {}
  1538. elif (isinstance(incs, str)):
  1539. defs = {defs : ""}
  1540. elif (isinstance(incs, list) or isinstance(incs, tuple) or isinstance(incs, set)):
  1541. olddefs = defs
  1542. defs = {}
  1543. for d in olddefs:
  1544. defs[d] = ""
  1545. custom_loc = PkgHasCustomLocation(pkg)
  1546. # Determine the location of the thirdparty directory.
  1547. if not thirdparty_dir:
  1548. thirdparty_dir = pkg.lower()
  1549. pkg_dir = os.path.join(GetThirdpartyDir(), thirdparty_dir)
  1550. # First check if the library can be found in the thirdparty directory.
  1551. if not custom_loc and os.path.isdir(pkg_dir):
  1552. if framework and os.path.isdir(os.path.join(pkg_dir, framework + ".framework")):
  1553. FrameworkDirectory(target_pkg, pkg_dir)
  1554. LibName(target_pkg, "-framework " + framework)
  1555. return
  1556. inc_dir = os.path.join(pkg_dir, "include")
  1557. if os.path.isdir(inc_dir):
  1558. IncDirectory(target_pkg, inc_dir)
  1559. # Handle cases like freetype2 where the include dir is a subdir under "include"
  1560. for i in incs:
  1561. if os.path.isdir(os.path.join(inc_dir, i)):
  1562. IncDirectory(target_pkg, os.path.join(inc_dir, i))
  1563. lib_dir = os.path.join(pkg_dir, "lib")
  1564. lpath = [lib_dir]
  1565. if not PkgSkip("PYTHON"):
  1566. py_lib_dir = os.path.join(lib_dir, SDK["PYTHONVERSION"])
  1567. if os.path.isdir(py_lib_dir):
  1568. lpath.append(py_lib_dir)
  1569. # TODO: check for a .pc file in the lib/pkgconfig/ dir
  1570. if (tool is not None and os.path.isfile(os.path.join(pkg_dir, "bin", tool))):
  1571. tool = os.path.join(pkg_dir, "bin", tool)
  1572. for i in PkgConfigGetLibs(None, tool):
  1573. if i.startswith('-l'):
  1574. # To make sure we don't pick up the system copy, write out
  1575. # the full path instead.
  1576. libname = i[2:]
  1577. location = LocateLibrary(libname, lpath, prefer_static=True)
  1578. if location is not None:
  1579. LibName(target_pkg, location)
  1580. else:
  1581. print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + pkg.lower() + GetColor())
  1582. LibName(target_pkg, i)
  1583. else:
  1584. LibName(target_pkg, i)
  1585. for i, j in PkgConfigGetDefSymbols(None, tool).items():
  1586. DefSymbol(target_pkg, i, j)
  1587. return
  1588. # Now search for the libraries in the package's lib directories.
  1589. for l in libs:
  1590. libname = l
  1591. if l.startswith("lib"):
  1592. libname = l[3:]
  1593. location = LocateLibrary(libname, lpath, prefer_static=True)
  1594. if location is not None:
  1595. # If it's a .so or .dylib we may have changed it and copied it to the built/lib dir.
  1596. if location.endswith('.so') or location.endswith('.dylib'):
  1597. location = os.path.join(GetOutputDir(), "lib", os.path.basename(location))
  1598. LibName(target_pkg, location)
  1599. else:
  1600. # This is for backward compatibility - in the thirdparty dir,
  1601. # we kept some libs with "panda" prefix, like libpandatiff.
  1602. location = LocateLibrary("panda" + libname, lpath, prefer_static=True)
  1603. if location is not None:
  1604. if location.endswith('.so') or location.endswith('.dylib'):
  1605. location = os.path.join(GetOutputDir(), "lib", os.path.basename(location))
  1606. LibName(target_pkg, location)
  1607. else:
  1608. print(GetColor("cyan") + "Couldn't find library lib" + libname + " in thirdparty directory " + thirdparty_dir + GetColor())
  1609. for d, v in defs.values():
  1610. DefSymbol(target_pkg, d, v)
  1611. return
  1612. elif not custom_loc and GetHost() == "darwin" and framework is not None:
  1613. prefix = SDK["MACOSX"]
  1614. if (os.path.isdir(prefix + "/Library/Frameworks/%s.framework" % framework) or
  1615. os.path.isdir(prefix + "/System/Library/Frameworks/%s.framework" % framework) or
  1616. os.path.isdir(prefix + "/Developer/Library/Frameworks/%s.framework" % framework) or
  1617. os.path.isdir(prefix + "/Users/%s/System/Library/Frameworks/%s.framework" % (getpass.getuser(), framework))):
  1618. LibName(target_pkg, "-framework " + framework)
  1619. for d, v in defs.values():
  1620. DefSymbol(target_pkg, d, v)
  1621. return
  1622. elif VERBOSE:
  1623. print(ColorText("cyan", "Couldn't find the framework %s" % (framework)))
  1624. elif not custom_loc and LocateBinary(tool) is not None and (tool != "pkg-config" or pkgconfig is not None):
  1625. if (isinstance(pkgconfig, str) or tool != "pkg-config"):
  1626. if (PkgConfigHavePkg(pkgconfig, tool)):
  1627. return PkgConfigEnable(target_pkg, pkgconfig, tool)
  1628. else:
  1629. have_all_pkgs = True
  1630. for pc in pkgconfig:
  1631. if (PkgConfigHavePkg(pc, tool)):
  1632. PkgConfigEnable(target_pkg, pc, tool)
  1633. else:
  1634. have_all_pkgs = False
  1635. if (have_all_pkgs):
  1636. return
  1637. if not custom_loc and pkgconfig is not None and not libs:
  1638. # pkg-config is all we can do, abort if it wasn't found.
  1639. if pkg in PkgListGet():
  1640. Warn("Could not locate pkg-config package %s, excluding from build" % (pkgconfig))
  1641. PkgDisable(pkg)
  1642. else:
  1643. Error("Could not locate pkg-config package %s, aborting build" % (pkgconfig))
  1644. else:
  1645. # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves.
  1646. have_pkg = True
  1647. for l in libs:
  1648. libname = l
  1649. if l.startswith("lib"):
  1650. libname = l[3:]
  1651. if custom_loc:
  1652. # Try searching in the package's LibDirectories.
  1653. lpath = [dir for ppkg, dir in LIBDIRECTORIES if pkg == ppkg]
  1654. location = LocateLibrary(libname, lpath)
  1655. if location is not None:
  1656. LibName(target_pkg, location)
  1657. else:
  1658. have_pkg = False
  1659. print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor())
  1660. elif SystemLibraryExists(libname):
  1661. # It exists in a system library directory.
  1662. LibName(target_pkg, "-l" + libname)
  1663. else:
  1664. # Try searching in the package's LibDirectories.
  1665. lpath = [dir for ppkg, dir in LIBDIRECTORIES if pkg == ppkg or ppkg == "ALWAYS"]
  1666. location = LocateLibrary(libname, lpath)
  1667. if location is not None:
  1668. LibName(target_pkg, "-l" + libname)
  1669. else:
  1670. have_pkg = False
  1671. if VERBOSE or custom_loc:
  1672. print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor())
  1673. # Determine which include directories to look in.
  1674. incdirs = []
  1675. if not custom_loc:
  1676. incdirs += list(SYS_INC_DIRS)
  1677. for ppkg, pdir in INCDIRECTORIES[:]:
  1678. if pkg == ppkg or (ppkg == "ALWAYS" and not custom_loc):
  1679. incdirs.append(pdir)
  1680. if custom_loc and pkg != target_pkg:
  1681. IncDirectory(target_pkg, pdir)
  1682. # The incs list contains both subdirectories to explicitly add to
  1683. # the include path and header files to check the existence of.
  1684. for i in incs:
  1685. incdir = None
  1686. for dir in incdirs:
  1687. if len(glob.glob(os.path.join(dir, i))) > 0:
  1688. incdir = sorted(glob.glob(os.path.join(dir, i)))[-1]
  1689. # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists.
  1690. if incdir is None and (i.endswith('/Dense') or i.endswith(".h")):
  1691. have_pkg = False
  1692. if VERBOSE or custom_loc:
  1693. print(GetColor("cyan") + "Couldn't find header file " + i + GetColor())
  1694. if incdir is not None and os.path.isdir(incdir):
  1695. IncDirectory(target_pkg, incdir)
  1696. if not have_pkg:
  1697. if custom_loc:
  1698. Error("Could not locate thirdparty package %s in specified directory, aborting build" % (pkg.lower()))
  1699. elif pkg in PkgListGet():
  1700. Warn("Could not locate thirdparty package %s, excluding from build" % (pkg.lower()))
  1701. PkgDisable(pkg)
  1702. else:
  1703. Error("Could not locate thirdparty package %s, aborting build" % (pkg.lower()))
  1704. ########################################################################
  1705. ##
  1706. ## SDK Location
  1707. ##
  1708. ## This section is concerned with locating the install directories
  1709. ## for various third-party packages. The results are stored in the
  1710. ## SDK table.
  1711. ##
  1712. ## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK.
  1713. ## The only way to reliably find it is to search through the installer's
  1714. ## uninstall-directories, look in each one, and see if it contains the
  1715. ## relevant files.
  1716. ##
  1717. ########################################################################
  1718. SDK = {}
  1719. def GetSdkDir(sdkname, sdkkey = None):
  1720. # Returns the default SDK directory. If it exists,
  1721. # and sdkkey is not None, it is put in SDK[sdkkey].
  1722. # Note: return value may not be an existing path.
  1723. sdkbase = "sdks"
  1724. if "MAKEPANDA_SDKS" in os.environ:
  1725. sdkbase = os.environ["MAKEPANDA_SDKS"]
  1726. sdir = sdkbase[:]
  1727. target = GetTarget()
  1728. target_arch = GetTargetArch()
  1729. if target == 'windows':
  1730. if target_arch == 'x64':
  1731. sdir += "/win64"
  1732. else:
  1733. sdir += "/win32"
  1734. elif target == 'linux':
  1735. sdir += "/linux"
  1736. sdir += platform.architecture()[0][:2]
  1737. elif target == 'darwin':
  1738. sdir += "/macosx"
  1739. sdir += "/" + sdkname
  1740. # If it does not exist, try the old location.
  1741. if (not os.path.isdir(sdir)):
  1742. sdir = sdkbase + "/" + sdir
  1743. if (target == 'linux'):
  1744. sdir += "-linux"
  1745. sdir += platform.architecture()[0][:2]
  1746. elif (target == "darwin"):
  1747. sdir += "-osx"
  1748. if (sdkkey and os.path.isdir(sdir)):
  1749. SDK[sdkkey] = sdir
  1750. return sdir
  1751. def SdkLocateDirectX( strMode = 'default' ):
  1752. if (GetHost() != "windows"): return
  1753. if strMode == 'default':
  1754. GetSdkDir("directx9", "DX9")
  1755. if ("DX9" not in SDK):
  1756. strMode = 'latest'
  1757. if strMode == 'latest':
  1758. ## We first try to locate the August SDK in 64 bits, then 32.
  1759. if ("DX9" not in SDK):
  1760. dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
  1761. if (dir != 0):
  1762. print("Using DirectX SDK June 2010")
  1763. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1764. if ("DX9" not in SDK):
  1765. dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
  1766. if (dir != 0):
  1767. print("Using DirectX SDK June 2010")
  1768. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1769. if ("DX9" not in SDK):
  1770. dir = "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)"
  1771. if os.path.isdir(dir):
  1772. print("Using DirectX SDK June 2010")
  1773. SDK["DX9"] = dir
  1774. if ("DX9" not in SDK):
  1775. dir = "C:/Program Files/Microsoft DirectX SDK (June 2010)"
  1776. if os.path.isdir(dir):
  1777. print("Using DirectX SDK June 2010")
  1778. SDK["DX9"] = dir
  1779. if ("DX9" not in SDK):
  1780. dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
  1781. if (dir != 0):
  1782. print("Using DirectX SDK Aug 2009")
  1783. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1784. if ("DX9" not in SDK):
  1785. dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
  1786. if (dir != 0):
  1787. print("Using DirectX SDK Aug 2009")
  1788. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1789. archStr = GetTargetArch()
  1790. if ("DX9" not in SDK):
  1791. uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
  1792. for subdir in ListRegistryKeys(uninstaller):
  1793. if (subdir[0]=="{"):
  1794. dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
  1795. if (dir != 0):
  1796. if (("DX9" not in SDK) and
  1797. (os.path.isfile(dir+"\\Include\\d3d9.h")) and
  1798. (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
  1799. (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
  1800. (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3d9.lib")) and
  1801. (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3dx9.lib"))):
  1802. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1803. if ("DX9" not in SDK):
  1804. return
  1805. elif strMode == 'jun2010':
  1806. if ("DX9" not in SDK):
  1807. dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
  1808. if (dir != 0):
  1809. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1810. if ("DX9" not in SDK):
  1811. dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath")
  1812. if (dir != 0):
  1813. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1814. if ("DX9" not in SDK):
  1815. dir = "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)"
  1816. if os.path.isdir(dir):
  1817. SDK["DX9"] = dir
  1818. if ("DX9" not in SDK):
  1819. dir = "C:/Program Files/Microsoft DirectX SDK (June 2010)"
  1820. if os.path.isdir(dir):
  1821. SDK["DX9"] = dir
  1822. if ("DX9" not in SDK):
  1823. exit("Couldn't find DirectX June2010 SDK")
  1824. else:
  1825. print("Found DirectX SDK June 2010")
  1826. elif strMode == 'aug2009':
  1827. if ("DX9" not in SDK):
  1828. dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
  1829. if (dir != 0):
  1830. print("Found DirectX SDK Aug 2009")
  1831. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1832. if ("DX9" not in SDK):
  1833. dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath")
  1834. if (dir != 0):
  1835. print("Found DirectX SDK Aug 2009")
  1836. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1837. if ("DX9" not in SDK):
  1838. exit("Couldn't find DirectX Aug 2009 SDK")
  1839. if ("DX9" in SDK):
  1840. SDK["DIRECTCAM"] = SDK["DX9"]
  1841. def SdkLocateMaya():
  1842. for (ver, key) in MAYAVERSIONINFO:
  1843. if (PkgSkip(ver)==0 and ver not in SDK):
  1844. GetSdkDir(ver.lower().replace("x",""), ver)
  1845. if (not ver in SDK):
  1846. if (GetHost() == "windows"):
  1847. for dev in ["Alias|Wavefront","Alias","Autodesk"]:
  1848. fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath"
  1849. res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION", override64=False)
  1850. if (res != 0):
  1851. res = res.replace("\\", "/").rstrip("/")
  1852. SDK[ver] = res
  1853. elif (GetHost() == "darwin"):
  1854. ddir = "/Applications/Autodesk/maya"+key
  1855. if (os.path.isdir(ddir)): SDK[ver] = ddir
  1856. else:
  1857. if (GetTargetArch() in ("x86_64", "amd64")):
  1858. ddir1 = "/usr/autodesk/maya"+key+"-x64"
  1859. ddir2 = "/usr/aw/maya"+key+"-x64"
  1860. else:
  1861. ddir1 = "/usr/autodesk/maya"+key
  1862. ddir2 = "/usr/aw/maya"+key
  1863. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  1864. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  1865. def SdkLocateMax():
  1866. if (GetHost() != "windows"): return
  1867. for version,key1,key2,subdir in MAXVERSIONINFO:
  1868. if (PkgSkip(version)==0):
  1869. if (version not in SDK):
  1870. GetSdkDir("maxsdk"+version.lower()[3:], version)
  1871. GetSdkDir("maxsdk"+version.lower()[3:], version+"CS")
  1872. if (not version in SDK):
  1873. top = GetRegistryKey(key1,key2)
  1874. if (top != 0):
  1875. SDK[version] = top + "maxsdk"
  1876. if (os.path.isdir(top + "\\" + subdir)!=0):
  1877. SDK[version+"CS"] = top + subdir
  1878. def SdkLocatePython(prefer_thirdparty_python=False):
  1879. if PkgSkip("PYTHON"):
  1880. # We're not compiling with Python support. We still need to set this
  1881. # in case we want to run any scripts that use Python, though.
  1882. SDK["PYTHONEXEC"] = os.path.realpath(sys.executable)
  1883. return
  1884. abiflags = getattr(sys, 'abiflags', '')
  1885. if GetTarget() == 'windows':
  1886. if PkgHasCustomLocation("PYTHON"):
  1887. # Check our custom location instead (--python-libdir, --python-incdir)
  1888. sdkdir = FindOptDirectory("PYTHON")
  1889. if sdkdir is None:
  1890. exit("Could not find a Python installation using these command line options.")
  1891. else:
  1892. sdkdir = GetThirdpartyBase() + "/win-python"
  1893. sdkdir += "%d.%d" % sys.version_info[:2]
  1894. if GetOptimize() <= 2:
  1895. sdkdir += "-dbg"
  1896. if GetTargetArch() == 'x64':
  1897. sdkdir += "-x64"
  1898. sdkdir = sdkdir.replace('\\', '/')
  1899. SDK["PYTHON"] = sdkdir
  1900. SDK["PYTHONEXEC"] = SDK["PYTHON"] + "/python"
  1901. if (GetOptimize() <= 2):
  1902. SDK["PYTHONEXEC"] += "_d.exe"
  1903. else:
  1904. SDK["PYTHONEXEC"] += ".exe"
  1905. if (not os.path.isfile(SDK["PYTHONEXEC"])):
  1906. exit("Could not find %s!" % SDK["PYTHONEXEC"])
  1907. # Determine which version it is by checking which dll is in the directory.
  1908. if (GetOptimize() <= 2):
  1909. py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll") + \
  1910. glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9]_d.dll")
  1911. else:
  1912. py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll") + \
  1913. glob.glob(SDK["PYTHON"] + "/python[0-9][0-9][0-9].dll")
  1914. if len(py_dlls) == 0:
  1915. exit("Could not find the Python dll in %s." % (SDK["PYTHON"]))
  1916. elif len(py_dlls) > 1:
  1917. exit("Found multiple Python dlls in %s." % (SDK["PYTHON"]))
  1918. py_dll = os.path.basename(py_dlls[0])
  1919. py_dllver = py_dll.strip(".DHLNOPTY_dhlnopty")
  1920. ver = py_dllver[0] + '.' + py_dllver[1:]
  1921. SDK["PYTHONVERSION"] = "python" + ver
  1922. os.environ["PYTHONHOME"] = SDK["PYTHON"]
  1923. running_ver = '%d.%d' % sys.version_info[:2]
  1924. if ver != running_ver:
  1925. Warn("running makepanda with Python %s, but building Panda3D with Python %s." % (running_ver, ver))
  1926. elif CrossCompiling() or (prefer_thirdparty_python and os.path.isdir(os.path.join(GetThirdpartyDir(), "python"))):
  1927. tp_python = os.path.join(GetThirdpartyDir(), "python")
  1928. if GetTarget() == 'darwin':
  1929. py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].dylib") + \
  1930. glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].dylib")
  1931. else:
  1932. py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].so") + \
  1933. glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].so")
  1934. if len(py_libs) == 0:
  1935. py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].a") + \
  1936. glob.glob(tp_python + "/lib/libpython[0-9].[0-9][0-9].a")
  1937. if len(py_libs) == 0:
  1938. exit("Could not find the Python library in %s." % (tp_python))
  1939. elif len(py_libs) > 1:
  1940. exit("Found multiple Python libraries in %s." % (tp_python))
  1941. py_lib = os.path.basename(py_libs[0])
  1942. py_libver = py_lib.strip('.abdhilnopsty')
  1943. SDK["PYTHONVERSION"] = "python" + py_libver
  1944. SDK["PYTHONEXEC"] = tp_python + "/bin/" + SDK["PYTHONVERSION"]
  1945. SDK["PYTHON"] = tp_python + "/include/" + SDK["PYTHONVERSION"]
  1946. elif GetTarget() == 'darwin' and not PkgHasCustomLocation("PYTHON"):
  1947. # On macOS, search for the Python framework directory matching the
  1948. # version number of our current Python version.
  1949. sysroot = SDK.get("MACOSX", "")
  1950. version = locations.get_python_version()
  1951. py_fwx = "{0}/System/Library/Frameworks/Python.framework/Versions/{1}".format(sysroot, version)
  1952. if not os.path.exists(py_fwx):
  1953. # Fall back to looking on the system.
  1954. py_fwx = "/Library/Frameworks/Python.framework/Versions/" + version
  1955. if not os.path.exists(py_fwx):
  1956. # Newer macOS versions use this scheme.
  1957. py_fwx = "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/" + version
  1958. if not os.path.exists(py_fwx):
  1959. exit("Could not locate Python installation at %s" % (py_fwx))
  1960. SDK["PYTHON"] = py_fwx + "/Headers"
  1961. SDK["PYTHONVERSION"] = "python" + version + abiflags
  1962. SDK["PYTHONEXEC"] = py_fwx + "/bin/python" + version
  1963. # Avoid choosing the one in the thirdparty package dir.
  1964. PkgSetCustomLocation("PYTHON")
  1965. IncDirectory("PYTHON", py_fwx + "/include")
  1966. LibDirectory("PYTHON", py_fwx + "/lib")
  1967. #elif GetTarget() == 'windows':
  1968. # SDK["PYTHON"] = os.path.dirname(locations.get_python_inc())
  1969. # SDK["PYTHONVERSION"] = "python" + locations.get_python_version()
  1970. # SDK["PYTHONEXEC"] = sys.executable
  1971. else:
  1972. SDK["PYTHON"] = locations.get_python_inc()
  1973. SDK["PYTHONVERSION"] = "python" + locations.get_python_version() + abiflags
  1974. SDK["PYTHONEXEC"] = os.path.realpath(sys.executable)
  1975. if CrossCompiling():
  1976. # We need a version of Python we can run.
  1977. SDK["PYTHONEXEC"] = sys.executable
  1978. host_version = "python" + locations.get_python_version() + abiflags
  1979. if SDK["PYTHONVERSION"] != host_version:
  1980. exit("Host Python version (%s) must be the same as target Python version (%s)!" % (host_version, SDK["PYTHONVERSION"]))
  1981. if GetVerbose():
  1982. print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:], SDK["PYTHON"]))
  1983. else:
  1984. print("Using Python %s" % (SDK["PYTHONVERSION"][6:]))
  1985. def SdkLocateVisualStudio(version=(10,0)):
  1986. if (GetHost() != "windows"): return
  1987. try:
  1988. msvcinfo = MSVCVERSIONINFO[version]
  1989. except:
  1990. exit("Couldn't get Visual Studio infomation with MSVC %s.%s version." % version)
  1991. vsversion = msvcinfo["vsversion"]
  1992. vsversion_str = "%s.%s" % vsversion
  1993. version_str = "%s.%s" % version
  1994. # try to use vswhere.exe
  1995. vswhere_path = LocateBinary("vswhere.exe")
  1996. if not vswhere_path:
  1997. if sys.platform == 'cygwin':
  1998. vswhere_path = "/cygdrive/c/Program Files/Microsoft Visual Studio/Installer/vswhere.exe"
  1999. else:
  2000. vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles()
  2001. if not os.path.isfile(vswhere_path):
  2002. vswhere_path = None
  2003. if not vswhere_path:
  2004. if sys.platform == 'cygwin':
  2005. vswhere_path = "/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe"
  2006. else:
  2007. vswhere_path = "%s\\Microsoft Visual Studio\\Installer\\vswhere.exe" % GetProgramFiles_x86()
  2008. if not os.path.isfile(vswhere_path):
  2009. vswhere_path = None
  2010. vsdir = 0
  2011. if vswhere_path:
  2012. min_vsversion = vsversion_str
  2013. max_vsversion = "%s.%s" % (vsversion[0]+1, 0)
  2014. vswhere_cmd = ["vswhere.exe", "-legacy", "-property", "installationPath",
  2015. "-version", "[{},{})".format(min_vsversion, max_vsversion)]
  2016. handle = subprocess.Popen(vswhere_cmd, executable=vswhere_path, stdout=subprocess.PIPE)
  2017. found_paths = handle.communicate()[0].splitlines()
  2018. if found_paths:
  2019. vsdir = found_paths[0].decode("utf-8") + "\\"
  2020. # try to use registry
  2021. if (vsdir == 0):
  2022. vsdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", vsversion_str)
  2023. vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", version_str)
  2024. if (vsdir != 0):
  2025. SDK["VISUALSTUDIO"] = vsdir
  2026. elif (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
  2027. vcdir = vcdir[:-3]
  2028. SDK["VISUALSTUDIO"] = vcdir
  2029. elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))):
  2030. SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio %s\\" % (vsversion_str)
  2031. elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio %s\\VC\\bin\\cl.exe" % (vsversion_str))):
  2032. SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio %s\\" % (vsversion_str)
  2033. elif "VCINSTALLDIR" in os.environ:
  2034. vcdir = os.environ["VCINSTALLDIR"]
  2035. if (vcdir[-3:] == "\\VC"):
  2036. vcdir = vcdir[:-2]
  2037. elif (vcdir[-4:] == "\\VC\\"):
  2038. vcdir = vcdir[:-3]
  2039. SDK["VISUALSTUDIO"] = vcdir
  2040. else:
  2041. exit("Couldn't find %s. To use a different version, use the --msvc-version option." % msvcinfo["vsname"])
  2042. SDK["MSVC_VERSION"] = version
  2043. SDK["VISUALSTUDIO_VERSION"] = vsversion
  2044. if GetVerbose():
  2045. print("Using %s located at %s" % (msvcinfo["vsname"], SDK["VISUALSTUDIO"]))
  2046. else:
  2047. print("Using %s" % (msvcinfo["vsname"]))
  2048. print("Using MSVC %s" % version_str)
  2049. def SdkLocateWindows(version=None):
  2050. if GetTarget() != "windows" or GetHost() != "windows":
  2051. return
  2052. if version:
  2053. version = version.upper()
  2054. if version == '10':
  2055. version = '10.0'
  2056. if (version and version.startswith('10.') and version.count('.') == 1) or version == '11':
  2057. # Choose the latest version of the Windows 10 SDK.
  2058. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
  2059. # Fallback in case we can't read the registry.
  2060. if not platsdk or not os.path.isdir(platsdk):
  2061. platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
  2062. if platsdk and os.path.isdir(platsdk):
  2063. min_version = (10, 0, 0)
  2064. if version == '11':
  2065. version = '10.0'
  2066. min_version = (10, 0, 22000)
  2067. incdirs = glob.glob(os.path.join(platsdk, 'Include', version + '.*.*'))
  2068. max_version = ()
  2069. for dir in incdirs:
  2070. verstring = os.path.basename(dir)
  2071. # Check that the important include directories exist.
  2072. if not os.path.isdir(os.path.join(dir, 'ucrt')):
  2073. continue
  2074. if not os.path.isdir(os.path.join(dir, 'shared')):
  2075. continue
  2076. if not os.path.isdir(os.path.join(dir, 'um')):
  2077. continue
  2078. if not os.path.isdir(os.path.join(platsdk, 'Lib', verstring, 'ucrt')):
  2079. continue
  2080. if not os.path.isdir(os.path.join(platsdk, 'Lib', verstring, 'um')):
  2081. continue
  2082. vertuple = tuple(map(int, verstring.split('.')))
  2083. if vertuple > max_version and vertuple > min_version:
  2084. version = verstring
  2085. max_version = vertuple
  2086. if not max_version:
  2087. # No suitable version found.
  2088. platsdk = None
  2089. elif version and version.startswith('10.'):
  2090. # We chose a specific version of the Windows 10 SDK. Verify it exists.
  2091. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
  2092. # Fallback in case we can't read the registry.
  2093. if not platsdk or not os.path.isdir(platsdk):
  2094. platsdk = "C:\\Program Files (x86)\\Windows Kits\\10\\"
  2095. if version.count('.') == 2:
  2096. version += '.0'
  2097. if platsdk and not os.path.isdir(os.path.join(platsdk, 'Include', version)):
  2098. platsdk = None
  2099. elif version == '8.1' or not version:
  2100. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot81")
  2101. # Fallback in case we can't read the registry.
  2102. if not platsdk or not os.path.isdir(platsdk):
  2103. platsdk = "C:\\Program Files (x86)\\Windows Kits\\8.1\\"
  2104. if not version:
  2105. if not os.path.isdir(platsdk):
  2106. # Fall back to 7.1 SDK.
  2107. return SdkLocateWindows("7.1")
  2108. version = '8.1'
  2109. elif version == '8.0':
  2110. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot")
  2111. else:
  2112. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v" + version, "InstallationFolder")
  2113. DefSymbol("ALWAYS", "_USING_V110_SDK71_")
  2114. if not platsdk or not os.path.isdir(platsdk):
  2115. # Most common location. Worth a try.
  2116. platsdk = GetProgramFiles() + "\\Microsoft SDKs\\Windows\\v" + version
  2117. if not os.path.isdir(platsdk):
  2118. if not version.endswith('A'):
  2119. # Try the stripped-down version that is bundled with Visual Studio.
  2120. return SdkLocateWindows(version + 'A')
  2121. platsdk = None
  2122. if not platsdk or not os.path.isdir(platsdk):
  2123. exit("Couldn't find Windows SDK version %s. To use a different version, use the --windows-sdk option." % (version))
  2124. if not platsdk.endswith("\\"):
  2125. platsdk += "\\"
  2126. SDK["MSPLATFORM"] = platsdk
  2127. SDK["MSPLATFORM_VERSION"] = version
  2128. if GetVerbose():
  2129. print("Using Windows SDK %s located at %s" % (version, platsdk))
  2130. else:
  2131. print("Using Windows SDK %s" % (version))
  2132. def SdkLocateMacOSX(archs = []):
  2133. if (GetHost() != "darwin"): return
  2134. handle = os.popen("xcode-select -print-path")
  2135. xcode_dir = handle.read().strip().rstrip('/')
  2136. handle.close()
  2137. # Make a list of SDK versions that will work for us, then grab the latest.
  2138. sdk_versions = []
  2139. if 'arm64' not in archs:
  2140. # Prefer pre-10.14 for now so that we can keep building FMOD.
  2141. sdk_versions += ["10.13", "10.12"]
  2142. sdk_versions += ["14.0", "13.3", "13.1", "13.0", "12.3", "11.3", "11.1", "11.0"]
  2143. if 'arm64' not in archs:
  2144. sdk_versions += ["10.15", "10.14"]
  2145. for version in sdk_versions:
  2146. sdkname = "MacOSX" + version
  2147. if os.path.exists("/Library/Developer/CommandLineTools/SDKs/%s.sdk" % sdkname):
  2148. SDK["MACOSX"] = "/Library/Developer/CommandLineTools/SDKs/%s.sdk" % sdkname
  2149. return
  2150. elif os.path.exists("/Developer/SDKs/%s.sdk" % sdkname):
  2151. SDK["MACOSX"] = "/Developer/SDKs/%s.sdk" % sdkname
  2152. return
  2153. elif os.path.exists("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % sdkname):
  2154. SDK["MACOSX"] = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % sdkname
  2155. return
  2156. elif xcode_dir and os.path.exists("%s/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % (xcode_dir, sdkname)):
  2157. SDK["MACOSX"] = "%s/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % (xcode_dir, sdkname)
  2158. return
  2159. exit("Couldn't find any suitable MacOSX SDK!")
  2160. def SdkLocateSpeedTree():
  2161. # Look for all of the SpeedTree SDK directories within the
  2162. # sdks/win32/speedtree dir, and pick the highest-numbered one.
  2163. dir = GetSdkDir("speedtree")
  2164. if not os.path.exists(dir):
  2165. return
  2166. speedtrees = []
  2167. for dirname in os.listdir(dir):
  2168. if dirname.startswith('SpeedTree SDK v'):
  2169. version = dirname[15:].split()[0]
  2170. version = tuple(map(int, version.split('.')))
  2171. speedtrees.append((version, dirname))
  2172. if not speedtrees:
  2173. # No installed SpeedTree SDK.
  2174. return
  2175. speedtrees.sort()
  2176. version, dirname = speedtrees[-1]
  2177. SDK["SPEEDTREE"] = os.path.join(dir, dirname)
  2178. SDK["SPEEDTREEAPI"] = "OpenGL"
  2179. SDK["SPEEDTREEVERSION"] = '%s.%s' % (version[0], version[1])
  2180. def SdkLocateAndroid():
  2181. """This actually locates the Android NDK, not the Android SDK.
  2182. NDK_ROOT must be set to its root directory."""
  2183. global TOOLCHAIN_PREFIX
  2184. if GetTarget() != 'android':
  2185. return
  2186. # Allow ANDROID_API/ANDROID_ABI to be used in makepanda.py.
  2187. if ANDROID_API is None:
  2188. SetTarget('android')
  2189. api = ANDROID_API
  2190. SDK["ANDROID_API"] = api
  2191. abi = ANDROID_ABI
  2192. SDK["ANDROID_ABI"] = abi
  2193. SDK["ANDROID_TRIPLE"] = ANDROID_TRIPLE
  2194. if GetHost() == 'android':
  2195. # Assume we're compiling from termux.
  2196. prefix = os.environ.get("PREFIX", "/data/data/com.termux/files/usr")
  2197. SDK["ANDROID_JAR"] = prefix + "/share/aapt/android.jar"
  2198. return
  2199. # Find the location of the Android SDK.
  2200. sdk_root = os.environ.get('ANDROID_HOME')
  2201. if not sdk_root or not os.path.isdir(sdk_root):
  2202. sdk_root = os.environ.get('ANDROID_SDK_ROOT')
  2203. # Try the default installation location on Windows.
  2204. if not sdk_root and GetHost() == 'windows':
  2205. sdk_root = os.path.expanduser(os.path.join('~', 'AppData', 'Local', 'Android', 'Sdk'))
  2206. if not sdk_root:
  2207. exit('ANDROID_SDK_ROOT must be set when compiling for Android!')
  2208. elif not os.path.isdir(sdk_root):
  2209. exit('Cannot find %s. Please install Android SDK and set ANDROID_SDK_ROOT or ANDROID_HOME.' % (sdk_root))
  2210. # Determine the NDK installation directory.
  2211. if os.environ.get('NDK_ROOT') or os.environ.get('ANDROID_NDK_ROOT'):
  2212. # We have an explicit setting from an environment variable.
  2213. ndk_root = os.environ.get('ANDROID_NDK_ROOT')
  2214. if not ndk_root or not os.path.isdir(ndk_root):
  2215. ndk_root = os.environ.get('NDK_ROOT')
  2216. if not ndk_root or not os.path.isdir(ndk_root):
  2217. exit("Cannot find %s. Please install Android NDK and set ANDROID_NDK_ROOT." % (ndk_root))
  2218. else:
  2219. # Often, it's installed in the ndk-bundle subdirectory of the SDK.
  2220. ndk_root = os.path.join(sdk_root, 'ndk-bundle')
  2221. if not os.path.isdir(os.path.join(ndk_root, 'toolchains')):
  2222. exit('Cannot find the Android NDK. Install it via the SDK manager or set the ANDROID_NDK_ROOT variable if you have installed it in a different location.')
  2223. SDK["ANDROID_NDK"] = ndk_root
  2224. # Determine the toolchain location.
  2225. prebuilt_dir = os.path.join(ndk_root, 'toolchains', 'llvm', 'prebuilt')
  2226. if not os.path.isdir(prebuilt_dir):
  2227. exit('Not found: %s (is the Android NDK installed?)' % (prebuilt_dir))
  2228. host_tag = GetHost() + '-x86'
  2229. if host_64:
  2230. host_tag += '_64'
  2231. elif host_tag == 'windows-x86':
  2232. host_tag = 'windows'
  2233. prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
  2234. if host_tag == 'windows-x86_64' and not os.path.isdir(prebuilt_dir):
  2235. # Try the 32-bits toolchain instead.
  2236. host_tag = 'windows'
  2237. prebuilt_dir = os.path.join(prebuilt_dir, host_tag)
  2238. SDK["ANDROID_TOOLCHAIN"] = prebuilt_dir
  2239. # And locate the GCC toolchain, which is needed for some tools (eg. as/ld)
  2240. arch = GetTargetArch()
  2241. for opt in (TOOLCHAIN_PREFIX + '4.9', arch + '-4.9', TOOLCHAIN_PREFIX + '4.8', arch + '-4.8'):
  2242. if os.path.isdir(os.path.join(ndk_root, 'toolchains', opt)):
  2243. SDK["ANDROID_GCC_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', opt, 'prebuilt', host_tag)
  2244. break
  2245. # The prebuilt binaries have no toolchain prefix.
  2246. TOOLCHAIN_PREFIX = ''
  2247. # Determine the sysroot directory.
  2248. if arch == 'armv7a':
  2249. arch_dir = 'arch-arm'
  2250. elif arch == 'aarch64':
  2251. arch_dir = 'arch-arm64'
  2252. else:
  2253. arch_dir = 'arch-' + arch
  2254. SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-%s' % (api), arch_dir).replace('\\', '/')
  2255. #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include'))
  2256. # We need to redistribute the C++ standard library.
  2257. stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'llvm-libc++')
  2258. stl_lib = os.path.join(stdlibc, 'libs', abi, 'libc++_shared.so')
  2259. CopyFile(os.path.join(GetOutputDir(), 'lib', 'libc++_shared.so'), stl_lib)
  2260. # The Android support library polyfills C++ features not available in the
  2261. # STL that ships with Android.
  2262. #support = os.path.join(ndk_root, 'sources', 'android', 'support', 'include')
  2263. #IncDirectory("ALWAYS", support.replace('\\', '/'))
  2264. if api < 21:
  2265. LibName("ALWAYS", "-landroid_support")
  2266. # Determine the location of android.jar.
  2267. SDK["ANDROID_JAR"] = os.path.join(sdk_root, 'platforms', 'android-%s' % (api), 'android.jar')
  2268. if not os.path.isfile(SDK["ANDROID_JAR"]):
  2269. exit("Cannot find %s. Install platform API level %s via the SDK manager or change the targeted API level with --target=android-#" % (SDK["ANDROID_JAR"], api))
  2270. # Which build tools versions do we have? Pick the latest.
  2271. versions = []
  2272. for version in os.listdir(os.path.join(sdk_root, "build-tools")):
  2273. match = re.match('([0-9]+)\\.([0-9]+)\\.([0-9]+)', version)
  2274. if match:
  2275. version_tuple = int(match.group(1)), int(match.group(2)), int(match.group(3))
  2276. versions.append(version_tuple)
  2277. versions.sort()
  2278. if versions:
  2279. version = versions[-1]
  2280. SDK["ANDROID_BUILD_TOOLS"] = os.path.join(sdk_root, "build-tools", "{0}.{1}.{2}".format(*version))
  2281. # And find the location of the Java compiler.
  2282. if GetHost() == "windows":
  2283. jdk_home = os.environ.get("JDK_HOME") or os.environ.get("JAVA_HOME")
  2284. if not jdk_home:
  2285. # Try to use the Java shipped with Android Studio.
  2286. studio_path = GetRegistryKey("SOFTWARE\\Android Studio", "Path", override64=False)
  2287. if studio_path and os.path.isdir(studio_path):
  2288. jdk_home = os.path.join(studio_path, "jre")
  2289. if not jdk_home or not os.path.isdir(jdk_home):
  2290. exit("Cannot find JDK. Please set JDK_HOME or JAVA_HOME.")
  2291. javac = os.path.join(jdk_home, "bin", "javac.exe")
  2292. if not os.path.isfile(javac):
  2293. exit("Cannot find %s. Install the JDK and set JDK_HOME or JAVA_HOME." % (javac))
  2294. SDK["JDK"] = jdk_home
  2295. ########################################################################
  2296. ##
  2297. ## SDK Auto-Disables
  2298. ##
  2299. ## Disable packages whose SDKs could not be found.
  2300. ##
  2301. ########################################################################
  2302. def SdkAutoDisableDirectX():
  2303. for ver in DXVERSIONS + ["DIRECTCAM"]:
  2304. if (PkgSkip(ver)==0):
  2305. if (ver not in SDK):
  2306. if (GetHost() == "windows"):
  2307. WARNINGS.append("I cannot locate SDK for "+ver)
  2308. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  2309. PkgDisable(ver)
  2310. else:
  2311. WARNINGS.append("Using "+ver+" sdk: "+SDK[ver])
  2312. def SdkAutoDisableMaya():
  2313. for (ver,key) in MAYAVERSIONINFO:
  2314. if (ver not in SDK) and (PkgSkip(ver)==0):
  2315. if (GetHost() == "windows"):
  2316. WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.")
  2317. else:
  2318. WARNINGS.append("I cannot locate SDK for "+ver)
  2319. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  2320. PkgDisable(ver)
  2321. def SdkAutoDisableMax():
  2322. for version,key1,key2,subdir in MAXVERSIONINFO:
  2323. if (PkgSkip(version)==0) and ((version not in SDK) or (version+"CS" not in SDK)):
  2324. if (GetHost() == "windows"):
  2325. if (version in SDK):
  2326. WARNINGS.append("Your copy of "+version+" does not include the character studio SDK")
  2327. else:
  2328. WARNINGS.append("The registry does not appear to contain a pointer to "+version)
  2329. WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower())
  2330. PkgDisable(version)
  2331. def SdkAutoDisableSpeedTree():
  2332. if ("SPEEDTREE" not in SDK) and (PkgSkip("SPEEDTREE")==0):
  2333. PkgDisable("SPEEDTREE")
  2334. WARNINGS.append("I cannot locate SDK for SpeedTree")
  2335. WARNINGS.append("I have automatically added this command-line option: --no-speedtree")
  2336. ########################################################################
  2337. ##
  2338. ## Visual Studio comes with a script called VSVARS32.BAT, which
  2339. ## you need to run before using visual studio command-line tools.
  2340. ## The following python subroutine serves the same purpose.
  2341. ##
  2342. ########################################################################
  2343. def AddToPathEnv(path,add):
  2344. if path in os.environ:
  2345. if sys.platform == 'cygwin' and path != "PATH":
  2346. # INCLUDE, LIB, etc. must remain in Windows-style in cygwin.
  2347. os.environ[path] = add + ';' + os.environ[path]
  2348. else:
  2349. os.environ[path] = add + os.pathsep + os.environ[path]
  2350. else:
  2351. os.environ[path] = add
  2352. def SetupVisualStudioEnviron():
  2353. if ("VISUALSTUDIO" not in SDK):
  2354. exit("Could not find Visual Studio install directory")
  2355. if ("MSPLATFORM" not in SDK):
  2356. exit("Could not find the Microsoft Platform SDK")
  2357. if (SDK["VISUALSTUDIO_VERSION"] >= (15,0)):
  2358. try:
  2359. vsver_file = open(os.path.join(SDK["VISUALSTUDIO"],
  2360. "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"), "r")
  2361. SDK["VCTOOLSVERSION"] = vsver_file.readline().strip()
  2362. vcdir_suffix = "VC\\Tools\\MSVC\\%s\\" % SDK["VCTOOLSVERSION"]
  2363. except:
  2364. exit("Couldn't find tool version of %s." % MSVCVERSIONINFO[SDK["MSVC_VERSION"]]["vsname"])
  2365. else:
  2366. vcdir_suffix = "VC\\"
  2367. os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + vcdir_suffix
  2368. os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"]
  2369. winsdk_ver = SDK["MSPLATFORM_VERSION"]
  2370. # Determine the directories to look in based on the architecture.
  2371. arch = GetTargetArch()
  2372. bindir = ""
  2373. libdir = ""
  2374. if ("VCTOOLSVERSION" in SDK):
  2375. bindir = "Host" + GetHostArch().upper() + "\\" + arch
  2376. libdir = arch
  2377. else:
  2378. if (arch == 'x64'):
  2379. bindir = 'amd64'
  2380. libdir = 'amd64'
  2381. elif (arch != 'x86'):
  2382. bindir = arch
  2383. libdir = arch
  2384. if (arch != 'x86' and GetHostArch() == 'x86'):
  2385. # Special version of the tools that run on x86.
  2386. bindir = 'x86_' + bindir
  2387. vc_binpath = SDK["VISUALSTUDIO"] + vcdir_suffix + "bin"
  2388. binpath = os.path.join(vc_binpath, bindir)
  2389. if not os.path.isfile(binpath + "\\cl.exe"):
  2390. # Try the x86 tools, those should work just as well.
  2391. if arch == 'x64' and os.path.isfile(vc_binpath + "\\x86_amd64\\cl.exe"):
  2392. binpath = "{0}\\x86_amd64;{0}".format(vc_binpath)
  2393. elif winsdk_ver.startswith('10.'):
  2394. exit("Couldn't find compilers in %s. You may need to install the Windows SDK 7.1 and the Visual C++ 2010 SP1 Compiler Update for Windows SDK 7.1." % binpath)
  2395. else:
  2396. exit("Couldn't find compilers in %s." % binpath)
  2397. AddToPathEnv("PATH", binpath)
  2398. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE")
  2399. AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "include")
  2400. AddToPathEnv("INCLUDE", os.environ["VCINSTALLDIR"] + "atlmfc\\include")
  2401. AddToPathEnv("LIB", os.environ["VCINSTALLDIR"] + "lib\\" + libdir)
  2402. AddToPathEnv("LIB", os.environ["VCINSTALLDIR"] + "atlmfc\\lib\\" + libdir)
  2403. winsdk_ver = SDK["MSPLATFORM_VERSION"]
  2404. if winsdk_ver.startswith('10.'):
  2405. AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + arch)
  2406. AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + winsdk_ver + "\\" + arch)
  2407. # Windows Kit 10 introduces the "universal CRT".
  2408. inc_dir = SDK["MSPLATFORM"] + "Include\\" + winsdk_ver + "\\"
  2409. lib_dir = SDK["MSPLATFORM"] + "Lib\\" + winsdk_ver + "\\"
  2410. AddToPathEnv("INCLUDE", inc_dir + "shared")
  2411. AddToPathEnv("INCLUDE", inc_dir + "ucrt")
  2412. AddToPathEnv("INCLUDE", inc_dir + "um")
  2413. AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch)
  2414. AddToPathEnv("LIB", lib_dir + "um\\" + arch)
  2415. elif winsdk_ver == '8.1':
  2416. AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin\\" + arch)
  2417. inc_dir = SDK["MSPLATFORM"] + "Include\\"
  2418. lib_dir = SDK["MSPLATFORM"] + "Lib\\winv6.3\\"
  2419. AddToPathEnv("INCLUDE", inc_dir + "shared")
  2420. AddToPathEnv("INCLUDE", inc_dir + "ucrt")
  2421. AddToPathEnv("INCLUDE", inc_dir + "um")
  2422. AddToPathEnv("LIB", lib_dir + "ucrt\\" + arch)
  2423. AddToPathEnv("LIB", lib_dir + "um\\" + arch)
  2424. else:
  2425. AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin")
  2426. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
  2427. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl")
  2428. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\mfc")
  2429. if arch != 'x64':
  2430. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib")
  2431. AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC100.CRT")
  2432. AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC100.MFC")
  2433. elif os.path.isdir(SDK["MSPLATFORM"] + "lib\\x64"):
  2434. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\x64")
  2435. elif os.path.isdir(SDK["MSPLATFORM"] + "lib\\amd64"):
  2436. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\amd64")
  2437. else:
  2438. exit("Could not locate 64-bits libraries in Windows SDK directory!\nUsing directory: %s" % SDK["MSPLATFORM"])
  2439. # Targeting the 7.1 SDK (which is the only way to have Windows XP support)
  2440. # with Visual Studio 2015+ requires use of the Universal CRT.
  2441. if winsdk_ver in ('7.1', '7.1A', '8.0', '8.1') and SDK["VISUALSTUDIO_VERSION"] >= (14,0):
  2442. win_kit = GetRegistryKey("SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", "KitsRoot10")
  2443. # Fallback in case we can't read the registry.
  2444. if not win_kit or not os.path.isdir(win_kit):
  2445. win_kit = "C:\\Program Files (x86)\\Windows Kits\\10\\"
  2446. elif not win_kit.endswith('\\'):
  2447. win_kit += '\\'
  2448. for vnum in 10150, 10240, 10586, 14393, 15063, 16299, 17134, 17763, 18362, 19041, 20348, 22000:
  2449. version = "10.0.{0}.0".format(vnum)
  2450. if os.path.isfile(win_kit + "Include\\" + version + "\\ucrt\\assert.h"):
  2451. print("Using Universal CRT %s" % (version))
  2452. break
  2453. AddToPathEnv("LIB", "%s\\Lib\\%s\\ucrt\\%s" % (win_kit, version, arch))
  2454. AddToPathEnv("INCLUDE", "%s\\Include\\%s\\ucrt" % (win_kit, version))
  2455. # Copy the DLLs to the bin directory.
  2456. CopyAllFiles(GetOutputDir() + "/bin/", win_kit + "Redist\\ucrt\\DLLs\\" + arch + "\\")
  2457. ########################################################################
  2458. #
  2459. # Include and Lib directories.
  2460. #
  2461. # These allow you to add include and lib directories to the
  2462. # compiler search paths. These methods accept a "package"
  2463. # parameter, which specifies which package the directory is
  2464. # associated with. The include/lib directory is not used
  2465. # if the package is not selected. The package can be 'ALWAYS'.
  2466. #
  2467. ########################################################################
  2468. INCDIRECTORIES = []
  2469. LIBDIRECTORIES = []
  2470. FRAMEWORKDIRECTORIES = []
  2471. LIBNAMES = []
  2472. DEFSYMBOLS = []
  2473. COMPILEFLAGS = []
  2474. LINKFLAGS = []
  2475. def IncDirectory(opt, dir):
  2476. INCDIRECTORIES.append((opt, dir))
  2477. def LibDirectory(opt, dir):
  2478. LIBDIRECTORIES.append((opt, dir))
  2479. def FrameworkDirectory(opt, dir):
  2480. FRAMEWORKDIRECTORIES.append((opt, dir))
  2481. def FindIncDirectory(opt):
  2482. # Find the include directory associated with this module
  2483. for mod, dir in INCDIRECTORIES:
  2484. if mod == opt:
  2485. return os.path.abspath(dir)
  2486. def FindLibDirectory(opt):
  2487. # Find the library directory associated with this module
  2488. for mod, dir in LIBDIRECTORIES:
  2489. if mod == opt:
  2490. return os.path.abspath(dir)
  2491. def FindOptDirectory(opt):
  2492. # Find the common directory associated with this module
  2493. # using the include and library directories as a guide
  2494. include_dir = FindIncDirectory(opt)
  2495. lib_dir = FindLibDirectory(opt)
  2496. if include_dir and lib_dir:
  2497. # The module's common directory is the common prefix of
  2498. # its include and library directory
  2499. common_dir = os.path.commonprefix([include_dir, lib_dir])
  2500. if common_dir:
  2501. return os.path.abspath(common_dir)
  2502. elif include_dir:
  2503. # The module's common directory is the parent of the include
  2504. # directory
  2505. return os.path.abspath(os.path.join(include_dir, os.pardir))
  2506. elif lib_dir:
  2507. # The module's common directory is the parent of the library
  2508. # directory
  2509. return os.path.abspath(os.path.join(lib_dir, os.pardir))
  2510. def LibName(opt, name):
  2511. # Check to see if the lib file actually exists for the thirdparty library given
  2512. # Are we a thirdparty library?
  2513. if name.startswith(GetThirdpartyDir()):
  2514. # Does this lib exist?
  2515. if not os.path.exists(name):
  2516. WARNINGS.append(name + " not found. Skipping Package " + opt)
  2517. if (opt in PkgListGet()):
  2518. if not PkgSkip(opt):
  2519. Warn("Could not locate thirdparty package %s, excluding from build" % (opt.lower()))
  2520. PkgDisable(opt)
  2521. return
  2522. else:
  2523. Error("Could not locate thirdparty package %s, aborting build" % (opt.lower()))
  2524. LIBNAMES.append((opt, name))
  2525. def DefSymbol(opt, sym, val=""):
  2526. DEFSYMBOLS.append((opt, sym, val))
  2527. def CompileFlag(opt, flag):
  2528. COMPILEFLAGS.append((opt, flag))
  2529. def LinkFlag(opt, flag):
  2530. LINKFLAGS.append((opt, flag))
  2531. ########################################################################
  2532. #
  2533. # This subroutine prepares the environment for the build.
  2534. #
  2535. ########################################################################
  2536. def SetupBuildEnvironment(compiler):
  2537. if GetVerbose():
  2538. print("Using compiler: %s" % compiler)
  2539. print("Host OS: %s" % GetHost())
  2540. print("Host arch: %s" % GetHostArch())
  2541. target = GetTarget()
  2542. if target != 'android':
  2543. print("Target OS: %s" % GetTarget())
  2544. else:
  2545. print("Target OS: %s (API level %d)" % (GetTarget(), ANDROID_API))
  2546. print("Target arch: %s" % GetTargetArch())
  2547. # Set to English so we can safely parse the result of gcc commands.
  2548. # Setting it to UTF-8 is necessary for Python 3 modules to import
  2549. # correctly.
  2550. os.environ["LC_ALL"] = "en_US.UTF-8"
  2551. os.environ["LANGUAGE"] = "en"
  2552. # In the case of Android, we have to put the toolchain on the PATH in order to use it.
  2553. if GetTarget() == 'android' and GetHost() != 'android':
  2554. AddToPathEnv("PATH", os.path.join(SDK["ANDROID_TOOLCHAIN"], "bin"))
  2555. if "ANDROID_BUILD_TOOLS" in SDK:
  2556. AddToPathEnv("PATH", SDK["ANDROID_BUILD_TOOLS"])
  2557. if "JDK" in SDK:
  2558. AddToPathEnv("PATH", os.path.join(SDK["JDK"], "bin"))
  2559. os.environ["JAVA_HOME"] = SDK["JDK"]
  2560. if compiler == "MSVC":
  2561. # Add the visual studio tools to PATH et al.
  2562. SetupVisualStudioEnviron()
  2563. if compiler == "GCC":
  2564. # Invoke gcc to determine the system library directories.
  2565. global SYS_LIB_DIRS, SYS_INC_DIRS
  2566. if sys.platform == "darwin":
  2567. # We need to add this one explicitly for some reason.
  2568. SYS_LIB_DIRS.append(SDK["MACOSX"] + "/usr/lib")
  2569. if not SDK.get("MACOSX"):
  2570. # gcc doesn't add this one, but we do want it:
  2571. local_lib = SDK.get("SYSROOT", "") + "/usr/local/lib"
  2572. if os.path.isdir(local_lib):
  2573. SYS_LIB_DIRS.append(local_lib)
  2574. sysroot_flag = ""
  2575. if SDK.get("MACOSX"):
  2576. # The default compiler in Leopard does not respect --sysroot correctly.
  2577. sysroot_flag = " -isysroot " + SDK["MACOSX"]
  2578. #if SDK.get("SYSROOT"):
  2579. # sysroot_flag = ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"])
  2580. if GetTarget() == "android":
  2581. sysroot_flag = " -target " + ANDROID_TRIPLE
  2582. # Extract the dirs from the line that starts with 'libraries: ='.
  2583. # The -E is mostly to keep emscripten happy by preventing it from
  2584. # running the compiler and complaining about the lack of input files.
  2585. cmd = GetCXX() + " -E -print-search-dirs" + sysroot_flag
  2586. handle = os.popen(cmd)
  2587. for line in handle:
  2588. if not line.startswith('libraries: ='):
  2589. continue
  2590. line = line[12:].strip()
  2591. libdirs = line.split(':')
  2592. while libdirs:
  2593. libdir = os.path.normpath(libdirs.pop(0))
  2594. if os.path.isdir(libdir):
  2595. if libdir not in SYS_LIB_DIRS:
  2596. SYS_LIB_DIRS.append(libdir)
  2597. elif len(libdir) == 1:
  2598. # Oops, is this a drive letter? Prepend it to the next.
  2599. libdirs[0] = libdir + ':' + libdirs[0]
  2600. elif GetVerbose():
  2601. print("Ignoring non-existent library directory %s" % (libdir))
  2602. returnval = handle.close()
  2603. if returnval is not None and returnval != 0:
  2604. Warn("%s failed" % (cmd))
  2605. SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
  2606. # The Android toolchain on Windows doesn't actually add this one.
  2607. if target == 'android' and GetHost() == 'windows':
  2608. libdir = SDK.get("SYSROOT", "") + "/usr/lib"
  2609. if GetTargetArch() == 'x86_64':
  2610. libdir += '64'
  2611. SYS_LIB_DIRS += [libdir]
  2612. # Now extract the preprocessor's include directories.
  2613. cmd = GetCXX() + " -x c++ -v -E " + os.devnull
  2614. cmd += sysroot_flag
  2615. null = open(os.devnull, 'w')
  2616. handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True)
  2617. scanning = False
  2618. for line in handle.communicate()[1].splitlines():
  2619. line = line.decode('utf-8', 'replace')
  2620. # Start looking at a line that says: #include "..." search starts here
  2621. if not scanning:
  2622. if line.startswith('#include'):
  2623. scanning = True
  2624. continue
  2625. if sys.platform == "win32":
  2626. if not line.startswith(' '):
  2627. continue
  2628. else:
  2629. if not line.startswith(' /'):
  2630. continue
  2631. line = line.strip()
  2632. if line.endswith(" (framework directory)"):
  2633. pass
  2634. elif os.path.isdir(line):
  2635. SYS_INC_DIRS.append(os.path.normpath(line))
  2636. elif GetVerbose():
  2637. print("Ignoring non-existent include directory %s" % (line))
  2638. if handle.returncode != 0 or not SYS_INC_DIRS:
  2639. Warn("%s failed or did not produce the expected result" % (cmd))
  2640. sysroot = SDK.get("SYSROOT", "")
  2641. # Add some sensible directories as a fallback.
  2642. SYS_INC_DIRS = [
  2643. sysroot + "/usr/include",
  2644. sysroot + "/usr/local/include"
  2645. ]
  2646. pcbsd_inc = sysroot + "/usr/PCBSD/local/include"
  2647. if os.path.isdir(pcbsd_inc):
  2648. SYS_INC_DIRS.append(pcbsd_inc)
  2649. null.close()
  2650. # Print out the search paths
  2651. if GetVerbose():
  2652. print("System library search path:")
  2653. for dir in SYS_LIB_DIRS:
  2654. print(" " + dir)
  2655. print("System include search path:")
  2656. for dir in SYS_INC_DIRS:
  2657. print(" " + dir)
  2658. # If we're cross-compiling, no point in putting our output dirs on the path.
  2659. if CrossCompiling():
  2660. return
  2661. # Add our output directories to the environment.
  2662. builtdir = GetOutputDir()
  2663. AddToPathEnv("PYTHONPATH", builtdir)
  2664. AddToPathEnv("PANDA_PRC_DIR", os.path.join(builtdir, "etc"))
  2665. AddToPathEnv("PATH", os.path.join(builtdir, "bin"))
  2666. if GetHost() == 'windows':
  2667. # extension_native_helpers.py currently expects to find libpandaexpress on sys.path.
  2668. AddToPathEnv("PYTHONPATH", os.path.join(builtdir, "bin"))
  2669. AddToPathEnv("PATH", os.path.join(builtdir, "plugins"))
  2670. # Now for the special (DY)LD_LIBRARY_PATH on Unix-esque systems.
  2671. if GetHost() != 'windows':
  2672. # Get the current
  2673. ldpath = os.environ.get("LD_LIBRARY_PATH", "").split(os.pathsep)
  2674. if GetHost() == 'darwin':
  2675. dyldpath = os.environ.get("DYLD_LIBRARY_PATH", "").split(os.pathsep)
  2676. # Remove any potential current Panda installation lib dirs
  2677. for i in ldpath[:]:
  2678. if i.startswith("/usr/lib/panda3d") or \
  2679. i.startswith("/usr/local/panda"):
  2680. ldpath.remove(i)
  2681. if GetHost() == 'darwin':
  2682. for i in dyldpath[:]:
  2683. if i.startswith("/Applications/Panda3D") or \
  2684. i.startswith("/Developer/Panda3D"):
  2685. dyldpath.remove(i)
  2686. # Add built/lib/ to (DY)LD_LIBRARY_PATH
  2687. ldpath.insert(0, os.path.join(builtdir, 'lib'))
  2688. os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(ldpath)
  2689. if GetHost() == 'darwin':
  2690. dyldpath.insert(0, os.path.join(builtdir, 'lib'))
  2691. os.environ["DYLD_LIBRARY_PATH"] = os.pathsep.join(dyldpath)
  2692. # macOS 10.11 removed DYLD_LIBRARY_PATH, but we still need to pass
  2693. # on our lib directory to ppackage, so add it to PATH instead.
  2694. os.environ["PATH"] = os.path.join(builtdir, 'lib') + ':' + os.environ.get("PATH", "")
  2695. # Workaround around compile issue on PCBSD
  2696. if (os.path.exists("/usr/PCBSD")):
  2697. os.environ["LD_LIBRARY_PATH"] += os.pathsep + "/usr/PCBSD/local/lib"
  2698. ########################################################################
  2699. ##
  2700. ## Routines to copy files into the build tree
  2701. ##
  2702. ########################################################################
  2703. def CopyFile(dstfile, srcfile):
  2704. if dstfile[-1] == '/':
  2705. dstfile += os.path.basename(srcfile)
  2706. if NeedsBuild([dstfile], [srcfile]):
  2707. if os.path.islink(srcfile):
  2708. # Preserve symlinks
  2709. if os.path.isfile(dstfile) or os.path.islink(dstfile):
  2710. print("Removing file %s" % (dstfile))
  2711. os.unlink(dstfile)
  2712. elif os.path.isdir(dstfile):
  2713. print("Removing directory %s" % (dstfile))
  2714. shutil.rmtree(dstfile)
  2715. os.symlink(os.readlink(srcfile), dstfile)
  2716. else:
  2717. WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
  2718. if sys.platform == 'cygwin' and os.path.splitext(dstfile)[1].lower() in ('.dll', '.exe'):
  2719. os.chmod(dstfile, 0o755)
  2720. JustBuilt([dstfile], [srcfile])
  2721. def CopyAllFiles(dstdir, srcdir, suffix=""):
  2722. for x in GetDirectoryContents(srcdir, ["*"+suffix]):
  2723. CopyFile(dstdir + x, srcdir + x)
  2724. def CopyAllHeaders(dir, skip=[]):
  2725. for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
  2726. srcfile = dir + "/" + filename
  2727. dstfile = OUTPUTDIR + "/include/" + filename
  2728. if (NeedsBuild([dstfile], [srcfile])):
  2729. WriteBinaryFile(dstfile, ReadBinaryFile(srcfile))
  2730. JustBuilt([dstfile], [srcfile])
  2731. def CopyTree(dstdir, srcdir, omitVCS=True, exclude=()):
  2732. if os.path.isdir(dstdir):
  2733. source_entries = os.listdir(srcdir)
  2734. for entry in source_entries:
  2735. srcpth = os.path.join(srcdir, entry)
  2736. dstpth = os.path.join(dstdir, entry)
  2737. if entry in exclude:
  2738. continue
  2739. if os.path.islink(srcpth) or os.path.isfile(srcpth):
  2740. if not omitVCS or entry not in VCS_FILES:
  2741. CopyFile(dstpth, srcpth)
  2742. else:
  2743. if not omitVCS or entry not in VCS_DIRS:
  2744. CopyTree(dstpth, srcpth)
  2745. # Delete files in dstdir that are not in srcdir.
  2746. for entry in os.listdir(dstdir):
  2747. if entry not in source_entries or entry in exclude:
  2748. path = os.path.join(dstdir, entry)
  2749. if os.path.islink(path) or os.path.isfile(path):
  2750. os.remove(path)
  2751. elif os.path.isdir(path):
  2752. shutil.rmtree(path)
  2753. else:
  2754. if GetHost() == 'windows':
  2755. srcdir = srcdir.replace('/', '\\')
  2756. dstdir = dstdir.replace('/', '\\')
  2757. cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"'
  2758. oscmd(cmd)
  2759. else:
  2760. if subprocess.call(['cp', '-R', '-f', srcdir, dstdir]) != 0:
  2761. exit("Copy failed.")
  2762. for entry in exclude:
  2763. path = os.path.join(dstdir, entry)
  2764. if os.path.islink(path) or os.path.isfile(path):
  2765. os.remove(path)
  2766. elif os.path.isdir(path):
  2767. shutil.rmtree(path)
  2768. if omitVCS:
  2769. DeleteVCS(dstdir)
  2770. def CopyPythonTree(dstdir, srcdir, threads=0):
  2771. if (not os.path.isdir(dstdir)):
  2772. os.mkdir(dstdir)
  2773. exclude_files = set(VCS_FILES)
  2774. exclude_files.add('panda3d.py')
  2775. for entry in os.listdir(srcdir):
  2776. srcpth = os.path.join(srcdir, entry)
  2777. dstpth = os.path.join(dstdir, entry)
  2778. if os.path.isfile(srcpth):
  2779. base, ext = os.path.splitext(entry)
  2780. if entry not in exclude_files and ext not in SUFFIX_INC + ['.pyc', '.pyo']:
  2781. if (NeedsBuild([dstpth], [srcpth])):
  2782. WriteBinaryFile(dstpth, ReadBinaryFile(srcpth))
  2783. JustBuilt([dstpth], [srcpth])
  2784. elif entry not in VCS_DIRS:
  2785. CopyPythonTree(dstpth, srcpth, threads=threads)
  2786. ########################################################################
  2787. ##
  2788. ## Parse setup.cfg to extract the version number.
  2789. ##
  2790. ########################################################################
  2791. cfg_parser = None
  2792. def GetMetadataValue(key):
  2793. global cfg_parser
  2794. if not cfg_parser:
  2795. # Parse the metadata from the setup.cfg file.
  2796. cfg_parser = configparser.ConfigParser()
  2797. path = os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')
  2798. assert cfg_parser.read(path), "Could not read setup.cfg file."
  2799. value = cfg_parser.get('metadata', key)
  2800. if key == 'classifiers':
  2801. value = value.strip().split('\n')
  2802. return value
  2803. # This function is being phased out.
  2804. def ParsePandaVersion(fn):
  2805. try:
  2806. f = open(fn, "r")
  2807. pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
  2808. for line in f:
  2809. match = pattern.match(line, 0)
  2810. if (match):
  2811. f.close()
  2812. return match.group(1) + "." + match.group(2) + "." + match.group(3)
  2813. f.close()
  2814. except:
  2815. pass
  2816. return "0.0.0"
  2817. ##########################################################################################
  2818. #
  2819. # Utility function to generate a resource file
  2820. #
  2821. ##########################################################################################
  2822. RESOURCE_FILE_TEMPLATE = """VS_VERSION_INFO VERSIONINFO
  2823. FILEVERSION %(commaversion)s
  2824. PRODUCTVERSION %(commaversion)s
  2825. FILEFLAGSMASK 0x3fL
  2826. FILEFLAGS %(debugflag)s
  2827. FILEOS 0x40004L
  2828. FILETYPE 0x2L
  2829. FILESUBTYPE 0x0L
  2830. BEGIN
  2831. BLOCK "StringFileInfo"
  2832. BEGIN
  2833. BLOCK "040904e4"
  2834. BEGIN
  2835. VALUE "FileDescription", "%(description)s\\0"
  2836. VALUE "FileVersion", "%(dotversion)s"
  2837. VALUE "LegalTrademarks", "\\0"
  2838. VALUE "MIMEType", "%(mimetype)s\\0"
  2839. VALUE "FileExtents", "%(extension)s\\0"
  2840. VALUE "FileOpenName", "%(filedesc)s\\0"
  2841. VALUE "OLESelfRegister", "\\0"
  2842. VALUE "OriginalFilename", "%(filename)s\\0"
  2843. VALUE "ProductName", "%(name)s %(version)s\\0"
  2844. VALUE "ProductVersion", "%(dotversion)s"
  2845. END
  2846. END
  2847. BLOCK "VarFileInfo"
  2848. BEGIN
  2849. VALUE "Translation", 0x409, 1252
  2850. END
  2851. END
  2852. """
  2853. def GenerateResourceFile(**kwargs):
  2854. if "debugflag" not in kwargs:
  2855. if GetOptimize() <= 2:
  2856. kwargs["debugflag"] = "0x1L"
  2857. else:
  2858. kwargs["debugflag"] = "0x0L"
  2859. kwargs["dotversion"] = kwargs["version"]
  2860. if len(kwargs["dotversion"].split(".")) == 3:
  2861. kwargs["dotversion"] += ".0"
  2862. if "commaversion" not in kwargs:
  2863. kwargs["commaversion"] = kwargs["dotversion"].replace(".", ",")
  2864. rcdata = ""
  2865. if "noinclude" not in kwargs:
  2866. rcdata += "#define APSTUDIO_READONLY_SYMBOLS\n"
  2867. rcdata += "#include \"winresrc.h\"\n"
  2868. rcdata += "#undef APSTUDIO_READONLY_SYMBOLS\n"
  2869. rcdata += RESOURCE_FILE_TEMPLATE % kwargs
  2870. if "icon" in kwargs:
  2871. rcdata += "\nICON_FILE ICON \"%s\"\n" % kwargs["icon"]
  2872. return rcdata
  2873. def WriteResourceFile(basename, **kwargs):
  2874. if not basename.endswith(".rc"):
  2875. basename += ".rc"
  2876. basename = GetOutputDir() + "/include/" + basename
  2877. ConditionalWriteFile(basename, GenerateResourceFile(**kwargs))
  2878. return basename
  2879. def GenerateEmbeddedStringFile(string_name, data):
  2880. yield 'extern const char %s[] = {\n' % (string_name)
  2881. i = 0
  2882. for byte in data:
  2883. if i == 0:
  2884. yield ' '
  2885. yield ' 0x%02x,' % (byte)
  2886. i += 1
  2887. if i >= 12:
  2888. yield '\n'
  2889. i = 0
  2890. yield '\n};\n'
  2891. def WriteEmbeddedStringFile(basename, inputs, string_name=None):
  2892. if os.path.splitext(basename)[1] not in SUFFIX_INC:
  2893. basename += '.cxx'
  2894. target = GetOutputDir() + "/tmp/" + basename
  2895. if string_name is None:
  2896. string_name = os.path.basename(os.path.splitext(target)[0])
  2897. string_name = string_name.replace('-', '_')
  2898. data = bytearray()
  2899. for input in inputs:
  2900. fp = open(input, 'rb')
  2901. # Insert a #line so that we get meaningful compile/assert errors when
  2902. # the result is inserted by interrogate_module into generated code.
  2903. if os.path.splitext(input)[1] in SUFFIX_INC:
  2904. line = '#line 1 "%s"\n' % (input)
  2905. data += bytearray(line.encode('ascii', 'replace'))
  2906. data += bytearray(fp.read())
  2907. fp.close()
  2908. data.append(0)
  2909. output = ''.join(GenerateEmbeddedStringFile(string_name, data))
  2910. ConditionalWriteFile(target, output)
  2911. return target
  2912. ########################################################################
  2913. ##
  2914. ## FindLocation
  2915. ##
  2916. ########################################################################
  2917. ORIG_EXT = {}
  2918. PYABI_SPECIFIC = set()
  2919. WARNED_FILES = set()
  2920. def GetOrigExt(x):
  2921. return ORIG_EXT[x]
  2922. def SetOrigExt(x, v):
  2923. ORIG_EXT[x] = v
  2924. def GetExtensionSuffix():
  2925. target = GetTarget()
  2926. if target == 'windows':
  2927. if GetOptimize() <= 2:
  2928. dllext = '_d'
  2929. else:
  2930. dllext = ''
  2931. if GetTargetArch() == 'x64':
  2932. return dllext + '.cp%d%d-win_amd64.pyd' % (sys.version_info[:2])
  2933. else:
  2934. return dllext + '.cp%d%d-win32.pyd' % (sys.version_info[:2])
  2935. elif target == 'emscripten':
  2936. return '.so'
  2937. elif CrossCompiling():
  2938. return '.{0}.so'.format(GetPythonABI())
  2939. else:
  2940. import _imp
  2941. return _imp.extension_suffixes()[0]
  2942. def GetPythonABI():
  2943. if not CrossCompiling():
  2944. soabi = locations.get_config_var('SOABI')
  2945. if soabi:
  2946. return soabi
  2947. return 'cpython-%d%d' % (sys.version_info[:2])
  2948. def CalcLocation(fn, ipath):
  2949. if fn.startswith("panda3d/") and fn.endswith(".py"):
  2950. return OUTPUTDIR + "/" + fn
  2951. if (fn.endswith(".class")):return OUTPUTDIR+"/classes/"+fn
  2952. if (fn.count("/")): return fn
  2953. dllext = ""
  2954. target = GetTarget()
  2955. if (GetOptimize() <= 2 and target == 'windows'): dllext = "_d"
  2956. if (fn == "AndroidManifest.xml"): return OUTPUTDIR+"/"+fn
  2957. if (fn == "classes.dex"): return OUTPUTDIR+"/"+fn
  2958. if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath)
  2959. if (fn.endswith(".I")): return CxxFindSource(fn, ipath)
  2960. if (fn.endswith(".h")): return CxxFindSource(fn, ipath)
  2961. if (fn.endswith(".c")): return CxxFindSource(fn, ipath)
  2962. if (fn.endswith(".py")): return CxxFindSource(fn, ipath)
  2963. if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath)
  2964. if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
  2965. if (fn.endswith(".xml")): return CxxFindSource(fn, ipath)
  2966. if (fn.endswith(".java")):return CxxFindSource(fn, ipath)
  2967. if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn
  2968. if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn
  2969. if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+GetExtensionSuffix()
  2970. if (target == 'windows'):
  2971. if (fn.endswith(".def")): return CxxFindSource(fn, ipath)
  2972. if (fn.endswith(".rc")): return CxxFindSource(fn, ipath)
  2973. if (fn.endswith(".idl")): return CxxFindSource(fn, ipath)
  2974. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn
  2975. if (fn.endswith(".res")): return OUTPUTDIR+"/tmp/"+fn
  2976. if (fn.endswith(".tlb")): return OUTPUTDIR+"/tmp/"+fn
  2977. if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".dll"
  2978. if (fn.endswith(".ocx")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".ocx"
  2979. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".mll"
  2980. if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dlo"
  2981. if (fn.endswith(".dli")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dli"
  2982. if (fn.endswith(".dle")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle"
  2983. if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".dll"
  2984. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn
  2985. if (fn.endswith(".p3d")): return OUTPUTDIR+"/bin/"+fn
  2986. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib"
  2987. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib"
  2988. elif (target == 'darwin'):
  2989. if (fn.endswith(".mm")): return CxxFindSource(fn, ipath)
  2990. if (fn.endswith(".r")): return CxxFindSource(fn, ipath)
  2991. if (fn.endswith(".plist")): return CxxFindSource(fn, ipath)
  2992. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  2993. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
  2994. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  2995. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  2996. if (fn.endswith(".p3d")): return OUTPUTDIR+"/bin/"+fn[:-4]
  2997. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  2998. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  2999. if (fn.endswith(".rsrc")): return OUTPUTDIR+"/tmp/"+fn
  3000. if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn
  3001. if (fn.endswith(".app")): return OUTPUTDIR+"/bin/"+fn
  3002. elif (target == 'emscripten'):
  3003. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  3004. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".o"
  3005. if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+".o"
  3006. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  3007. if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".js"
  3008. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]+".js"
  3009. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  3010. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  3011. else:
  3012. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  3013. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
  3014. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  3015. if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so"
  3016. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  3017. if (fn.endswith(".p3d")): return OUTPUTDIR+"/bin/"+fn[:-4]
  3018. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  3019. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  3020. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  3021. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  3022. return fn
  3023. def FindLocation(fn, ipath, pyabi=None):
  3024. if GetLinkAllStatic():
  3025. if fn.endswith(".dll"):
  3026. fn = fn[:-4] + ".lib"
  3027. elif fn.endswith(".pyd"):
  3028. fn = "libpy.panda3d." \
  3029. + os.path.splitext(fn[:-4] + GetExtensionSuffix())[0] + ".lib"
  3030. loc = CalcLocation(fn, ipath)
  3031. base, ext = os.path.splitext(fn)
  3032. # If this is a target created with PyTargetAdd, we need to make sure it
  3033. # it put in a Python-version-specific directory.
  3034. if loc in PYABI_SPECIFIC:
  3035. if loc.startswith(OUTPUTDIR + "/tmp"):
  3036. if pyabi is not None:
  3037. loc = OUTPUTDIR + "/tmp/" + pyabi + loc[len(OUTPUTDIR) + 4:]
  3038. else:
  3039. raise RuntimeError("%s is a Python-specific target, use PyTargetAdd instead of TargetAdd" % (fn))
  3040. elif ext != ".pyd" and loc not in WARNED_FILES:
  3041. WARNED_FILES.add(loc)
  3042. Warn("file depends on Python but is not in an ABI-specific directory:", loc)
  3043. ORIG_EXT[loc] = ext
  3044. return loc
  3045. ########################################################################
  3046. ##
  3047. ## These files maintain a python_versions.json file in the built/tmp
  3048. ## directory that can be used by the other scripts in this directory.
  3049. ##
  3050. ########################################################################
  3051. def GetCurrentPythonVersionInfo():
  3052. if PkgSkip("PYTHON"):
  3053. return
  3054. return {
  3055. "version": SDK["PYTHONVERSION"][6:].rstrip('dmu'),
  3056. "soabi": GetPythonABI(),
  3057. "ext_suffix": GetExtensionSuffix(),
  3058. "executable": sys.executable,
  3059. "purelib": locations.get_python_lib(False),
  3060. "platlib": locations.get_python_lib(True),
  3061. }
  3062. def UpdatePythonVersionInfoFile(new_info):
  3063. import json
  3064. json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json")
  3065. json_data = []
  3066. if os.path.isfile(json_file) and not PkgSkip("PYTHON"):
  3067. try:
  3068. with open(json_file, 'r') as fh:
  3069. json_data = json.load(fh)
  3070. except:
  3071. json_data = []
  3072. # Prune the list by removing the entries that conflict with our build,
  3073. # plus the entries that no longer exist, and the EOL Python versions
  3074. for version_info in json_data[:]:
  3075. core_pyd = os.path.join(GetOutputDir(), "panda3d", "core" + version_info["ext_suffix"])
  3076. if version_info["ext_suffix"] == new_info["ext_suffix"] or \
  3077. version_info["soabi"] == new_info["soabi"] or \
  3078. not os.path.isfile(core_pyd) or \
  3079. version_info["version"].split(".", 1)[0] == "2" or \
  3080. version_info["version"] in ("3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7"):
  3081. json_data.remove(version_info)
  3082. if not PkgSkip("PYTHON"):
  3083. json_data.append(new_info)
  3084. if VERBOSE:
  3085. print("Writing %s" % (json_file))
  3086. with open(json_file, 'w') as fh:
  3087. json.dump(json_data, fh, indent=4)
  3088. def ReadPythonVersionInfoFile():
  3089. import json
  3090. json_file = os.path.join(GetOutputDir(), "tmp", "python_versions.json")
  3091. if os.path.isfile(json_file):
  3092. try:
  3093. json_data = json.load(open(json_file, 'r'))
  3094. except:
  3095. pass
  3096. # Don't include unsupported versions of Python.
  3097. for version_info in json_data[:]:
  3098. if version_info["version"] in ("2.6", "2.7", "3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7"):
  3099. json_data.remove(version_info)
  3100. return json_data
  3101. return []
  3102. ########################################################################
  3103. ##
  3104. ## TargetAdd
  3105. ##
  3106. ## Makepanda maintains a list of make-targets. Each target has
  3107. ## these attributes:
  3108. ##
  3109. ## name - the name of the file being created.
  3110. ## ext - the original file extension, prior to OS-specific translation
  3111. ## inputs - the names of the input files to the compiler
  3112. ## deps - other input files that the target also depends on
  3113. ## opts - compiler options, a catch-all category
  3114. ##
  3115. ## TargetAdd will create the target if it does not exist. Then,
  3116. ## depending on what options you pass, it will push data onto these
  3117. ## various target attributes. This is cumulative: for example, if
  3118. ## you use TargetAdd to add compiler options, then use TargetAdd
  3119. ## again with more compiler options, both sets of options will be
  3120. ## included.
  3121. ##
  3122. ## TargetAdd does some automatic dependency generation on C++ files.
  3123. ## It will scan these files for include-files and automatically push
  3124. ## the include files onto the list of dependencies. In order to do
  3125. ## this, it needs an include-file search path. So if you supply
  3126. ## any C++ input, you also need to supply compiler options containing
  3127. ## include-directories, or alternately, a separate ipath parameter.
  3128. ##
  3129. ## The main body of 'makepanda' is a long list of TargetAdd
  3130. ## directives building up a giant list of make targets. Then,
  3131. ## finally, the targets are run and panda is built.
  3132. ##
  3133. ## Makepanda's dependency system does not understand multiple
  3134. ## outputs from a single build step. When a build step generates
  3135. ## a primary output file and a secondary output file, it is
  3136. ## necessary to trick the dependency system. Insert a dummy
  3137. ## build step that "generates" the secondary output file, using
  3138. ## the primary output file as an input. There is a special
  3139. ## compiler option DEPENDENCYONLY that creates such a dummy
  3140. ## build-step. There are two cases where dummy build steps must
  3141. ## be inserted: bison generates an OBJ and a secondary header
  3142. ## file, interrogate generates an IN and a secondary IGATE.OBJ.
  3143. ##
  3144. ## PyTargetAdd is a special version for targets that depend on Python.
  3145. ## It will create a target for each Python version we are building with,
  3146. ## ensuring that builds with different Python versions won't conflict
  3147. ## when we build for multiple Python ABIs side-by-side.
  3148. ##
  3149. ########################################################################
  3150. class Target:
  3151. pass
  3152. TARGET_LIST = []
  3153. TARGET_TABLE = {}
  3154. def TargetAdd(target, dummy=0, opts=[], input=[], dep=[], ipath=None, winrc=None, pyabi=None):
  3155. if dummy != 0:
  3156. exit("Syntax error in TargetAdd " + target)
  3157. if ipath is None:
  3158. ipath = opts
  3159. if not ipath:
  3160. ipath = []
  3161. if isinstance(input, str):
  3162. input = [input]
  3163. if isinstance(dep, str):
  3164. dep = [dep]
  3165. if target.endswith(".pyd") and not pyabi:
  3166. raise RuntimeError("Use PyTargetAdd to build .pyd targets")
  3167. full = FindLocation(target, [OUTPUTDIR + "/include"], pyabi=pyabi)
  3168. if full not in TARGET_TABLE:
  3169. t = Target()
  3170. t.name = full
  3171. t.inputs = []
  3172. t.deps = {}
  3173. t.opts = []
  3174. TARGET_TABLE[full] = t
  3175. TARGET_LIST.append(t)
  3176. else:
  3177. t = TARGET_TABLE[full]
  3178. for x in opts:
  3179. if x not in t.opts:
  3180. t.opts.append(x)
  3181. ipath = [OUTPUTDIR + "/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"]
  3182. for x in input:
  3183. fullinput = FindLocation(x, ipath, pyabi=pyabi)
  3184. t.inputs.append(fullinput)
  3185. # Don't re-link a library or binary if just its dependency dlls have been altered.
  3186. # This should work out fine in most cases, and often reduces recompilation time.
  3187. if os.path.splitext(x)[-1] not in SUFFIX_DLL:
  3188. t.deps[fullinput] = 1
  3189. (base,suffix) = os.path.splitext(x)
  3190. if SUFFIX_INC.count(suffix):
  3191. for d in CxxCalcDependencies(fullinput, ipath, []):
  3192. t.deps[d] = 1
  3193. elif suffix == '.java':
  3194. for d in JavaCalcDependencies(fullinput, OUTPUTDIR + "/classes"):
  3195. t.deps[d] = 1
  3196. # If we are linking statically, add the source DLL's dynamic dependencies.
  3197. if GetLinkAllStatic() and ORIG_EXT[fullinput] == '.lib' and fullinput in TARGET_TABLE:
  3198. tdep = TARGET_TABLE[fullinput]
  3199. for y in tdep.inputs:
  3200. if ORIG_EXT[y] == '.lib' and y not in t.inputs:
  3201. t.inputs.append(y)
  3202. for opt, _ in LIBNAMES + LIBDIRECTORIES + FRAMEWORKDIRECTORIES + LINKFLAGS + COMPILEFLAGS:
  3203. if opt in tdep.opts and opt not in t.opts:
  3204. t.opts.append(opt)
  3205. elif GetTarget() == 'emscripten' and ORIG_EXT[fullinput] == '.dll' and fullinput in TARGET_TABLE:
  3206. # Transfer over flags like -s USE_LIBPNG=1
  3207. tdep = TARGET_TABLE[fullinput]
  3208. for opt, _ in LINKFLAGS:
  3209. if opt in tdep.opts and opt not in t.opts:
  3210. t.opts.append(opt)
  3211. if x.endswith(".in"):
  3212. # Mark the _igate.cxx file as a dependency also.
  3213. outbase = os.path.basename(x)[:-3]
  3214. woutc = GetOutputDir()+"/tmp/"+outbase+"_igate.cxx"
  3215. t.deps[woutc] = 1
  3216. if target.endswith(".in"):
  3217. # Add any .N files.
  3218. base, ext = os.path.splitext(fullinput)
  3219. fulln = base + ".N"
  3220. if os.path.isfile(fulln):
  3221. t.deps[fulln] = 1
  3222. for x in dep:
  3223. fulldep = FindLocation(x, ipath, pyabi=pyabi)
  3224. t.deps[fulldep] = 1
  3225. if winrc and GetTarget() == 'windows':
  3226. TargetAdd(target, input=WriteResourceFile(target.split("/")[-1].split(".")[0], **winrc))
  3227. ext = os.path.splitext(target)[1]
  3228. if ext == ".in":
  3229. if not CrossCompiling():
  3230. t.deps[FindLocation("interrogate.exe", [])] = 1
  3231. t.deps[FindLocation("dtool_have_python.dat", [])] = 1
  3232. if ext in (".obj", ".tlb", ".res", ".plugin", ".app") or ext in SUFFIX_DLL or ext in SUFFIX_LIB:
  3233. t.deps[FindLocation("platform.dat", [])] = 1
  3234. if target.endswith(".obj") and any(x.endswith(".in") for x in input):
  3235. if not CrossCompiling():
  3236. t.deps[FindLocation("interrogate_module.exe", [])] = 1
  3237. if target.endswith(".pz") and not CrossCompiling():
  3238. t.deps[FindLocation("pzip.exe", [])] = 1
  3239. if target.endswith(".in"):
  3240. # Also add a target to compile the _igate.cxx file into an _igate.obj.
  3241. outbase = os.path.basename(target)[:-3]
  3242. woutc = OUTPUTDIR + "/tmp/" + outbase + "_igate.cxx"
  3243. CxxDependencyCache[woutc] = []
  3244. PyTargetAdd(outbase + "_igate.obj", opts=opts+['PYTHON','BIGOBJ'], input=woutc, dep=target)
  3245. def PyTargetAdd(target, opts=[], **kwargs):
  3246. if PkgSkip("PYTHON"):
  3247. return
  3248. if 'PYTHON' not in opts:
  3249. opts = opts + ['PYTHON']
  3250. abi = GetPythonABI()
  3251. MakeDirectory(OUTPUTDIR + "/tmp/" + abi)
  3252. # Mark this target as being a Python-specific target.
  3253. orig = CalcLocation(target, [OUTPUTDIR + "/include"])
  3254. PYABI_SPECIFIC.add(orig)
  3255. if orig.startswith(OUTPUTDIR + "/tmp/") and os.path.exists(orig):
  3256. print("Removing file %s" % (orig))
  3257. os.unlink(orig)
  3258. TargetAdd(target, opts=opts, pyabi=abi, **kwargs)