makepandacore.py 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688
  1. ########################################################################
  2. ##
  3. ## Caution: there are two separate, independent build systems:
  4. ## 'makepanda', and 'ppremake'. Use one or the other, do not attempt
  5. ## to use both. This file is part of the 'makepanda' system.
  6. ##
  7. ## This file, makepandacore, contains all the global state and
  8. ## global functions for the makepanda system.
  9. ##
  10. ########################################################################
  11. import sys,os,time,stat,string,re,getopt,cPickle,fnmatch,threading,Queue,signal,shutil,platform,glob,getpass
  12. from distutils import sysconfig
  13. SUFFIX_INC=[".cxx",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r",".plist"]
  14. SUFFIX_DLL=[".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd"]
  15. SUFFIX_LIB=[".lib",".ilb"]
  16. STARTTIME=time.time()
  17. MAINTHREAD=threading.currentThread()
  18. OUTPUTDIR="built"
  19. CUSTOM_OUTPUTDIR=False
  20. OPTIMIZE="3"
  21. VERBOSE=False
  22. ########################################################################
  23. ##
  24. ## Maya and Max Version List (with registry keys)
  25. ##
  26. ########################################################################
  27. MAYAVERSIONINFO=[("MAYA6", "6.0"),
  28. ("MAYA65", "6.5"),
  29. ("MAYA7", "7.0"),
  30. ("MAYA8", "8.0"),
  31. ("MAYA85", "8.5"),
  32. ("MAYA2008","2008"),
  33. ("MAYA2009","2009"),
  34. ]
  35. MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
  36. ("MAX7", "SOFTWARE\\Autodesk\\3DSMAX\\7.0", "Installdir", "maxsdk\\include\\CS"),
  37. ("MAX8", "SOFTWARE\\Autodesk\\3DSMAX\\8.0", "Installdir", "maxsdk\\include\\CS"),
  38. ("MAX9", "SOFTWARE\\Autodesk\\3DSMAX\\9.0", "Installdir", "maxsdk\\include\\CS"),
  39. ("MAX2009", "SOFTWARE\\Autodesk\\3DSMAX\\11.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  40. ("MAX2010", "SOFTWARE\\Autodesk\\3DSMAX\\12.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"),
  41. ]
  42. MAYAVERSIONS=[]
  43. MAXVERSIONS=[]
  44. DXVERSIONS=["DX8","DX9"]
  45. for (ver,key) in MAYAVERSIONINFO:
  46. MAYAVERSIONS.append(ver)
  47. for (ver,key1,key2,subdir) in MAXVERSIONINFO:
  48. MAXVERSIONS.append(ver)
  49. ########################################################################
  50. ##
  51. ## The exit routine will normally
  52. ##
  53. ## - print a message
  54. ## - save the dependency cache
  55. ## - exit
  56. ##
  57. ## However, if it is invoked inside a thread, it instead:
  58. ##
  59. ## - prints a message
  60. ## - raises the "initiate-exit" exception
  61. ##
  62. ## If you create a thread, you must be prepared to catch this
  63. ## exception, save the dependency cache, and exit.
  64. ##
  65. ########################################################################
  66. WARNINGS=[]
  67. THREADS={}
  68. HAVE_COLORS=False
  69. SETF=""
  70. try:
  71. import curses
  72. curses.setupterm()
  73. SETF=curses.tigetstr("setf")
  74. if (SETF == None):
  75. SETF=curses.tigetstr("setaf")
  76. assert SETF != None
  77. HAVE_COLORS=sys.stdout.isatty()
  78. except: pass
  79. def GetColor(color = None):
  80. if not HAVE_COLORS: return ""
  81. if color != None: color = color.lower()
  82. if (color == "blue"):
  83. return curses.tparm(SETF, 1)
  84. elif (color == "green"):
  85. return curses.tparm(SETF, 2)
  86. elif (color == "cyan"):
  87. return curses.tparm(SETF, 3)
  88. elif (color == "red"):
  89. return curses.tparm(SETF, 4)
  90. elif (color == "magenta"):
  91. return curses.tparm(SETF, 5)
  92. elif (color == "yellow"):
  93. return curses.tparm(SETF, 6)
  94. else:
  95. return curses.tparm(curses.tigetstr("sgr0"))
  96. def PrettyTime(t):
  97. t = int(t)
  98. hours = t/3600
  99. t -= hours*3600
  100. minutes = t/60
  101. t -= minutes*60
  102. seconds = t
  103. if (hours): return str(hours)+" hours "+str(minutes)+" min"
  104. if (minutes): return str(minutes)+" min "+str(seconds)+" sec"
  105. return str(seconds)+" sec"
  106. def ProgressOutput(progress, msg, target = None):
  107. if (threading.currentThread() == MAINTHREAD):
  108. if (progress == None):
  109. print msg
  110. elif (progress >= 100.0):
  111. print "%s[%s%d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
  112. elif (progress < 10.0):
  113. print "%s[%s %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
  114. else:
  115. print "%s[%s %d%%%s] %s" % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"), msg),
  116. else:
  117. global THREADS
  118. ident = threading.currentThread().ident
  119. if (ident not in THREADS):
  120. THREADS[ident] = len(THREADS) + 1
  121. print "%s[%sT%d%s] %s" % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow"), msg),
  122. if (target == None):
  123. print GetColor()
  124. else:
  125. print "%s%s%s" % (GetColor("green"), target, GetColor())
  126. def exit(msg = ""):
  127. if (threading.currentThread() == MAINTHREAD):
  128. SaveDependencyCache()
  129. # Move any files we've moved away back.
  130. if os.path.isfile("dtool/src/dtoolutil/pandaVersion.h.moved"):
  131. os.rename("dtool/src/dtoolutil/pandaVersion.h.moved", "dtool/src/dtoolutil/pandaVersion.h")
  132. if os.path.isfile("dtool/src/dtoolutil/checkPandaVersion.h.moved"):
  133. os.rename("dtool/src/dtoolutil/checkPandaVersion.h.moved", "dtool/src/dtoolutil/checkPandaVersion.h")
  134. if os.path.isfile("dtool/src/dtoolutil/checkPandaVersion.cxx.moved"):
  135. os.rename("dtool/src/dtoolutil/checkPandaVersion.cxx.moved", "dtool/src/dtoolutil/checkPandaVersion.cxx")
  136. if os.path.isfile("dtool/src/prc/prc_parameters.h.moved"):
  137. os.rename("dtool/src/prc/prc_parameters.h.moved", "dtool/src/prc/prc_parameters.h")
  138. if os.path.isfile("direct/src/plugin/p3d_plugin_config.h.moved"):
  139. os.rename("direct/src/plugin/p3d_plugin_config.h.moved", "direct/src/plugin/p3d_plugin_config.h")
  140. print "Elapsed Time: "+PrettyTime(time.time() - STARTTIME)
  141. print msg
  142. print GetColor("red") + "Build terminated." + GetColor()
  143. sys.stdout.flush()
  144. sys.stderr.flush()
  145. os._exit(1)
  146. else:
  147. print msg
  148. raise "initiate-exit"
  149. ########################################################################
  150. ##
  151. ## Run a command.
  152. ##
  153. ########################################################################
  154. def oscmd(cmd, ignoreError = False):
  155. if VERBOSE:
  156. print GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor()
  157. sys.stdout.flush()
  158. if sys.platform == "win32":
  159. exe = cmd.split()[0]
  160. if not (len(exe) > 4 and exe[-4:] == ".exe"):
  161. exe += ".exe"
  162. if os.path.isfile(exe)==0:
  163. for i in os.environ["PATH"].split(";"):
  164. if os.path.isfile(os.path.join(i, exe)):
  165. exe = os.path.join(i, exe)
  166. break
  167. if os.path.isfile(exe)==0:
  168. exit("Cannot find "+exe+" on search path")
  169. res = os.spawnl(os.P_WAIT, exe, cmd)
  170. else:
  171. res = os.system(cmd)
  172. if (res == 11):
  173. if (LocateBinary("gdb") and GetVerbose()):
  174. print GetColor("red") + "Received SIGSEGV, retrieving traceback..." + GetColor()
  175. 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)
  176. else:
  177. print GetColor("red") + "Received SIGSEGV" + GetColor()
  178. if res != 0 and not ignoreError:
  179. if "interrogate" in cmd.split(" ", 1)[0] and GetVerbose():
  180. print GetColor("red") + "Interrogate failed, retrieving debug output..." + GetColor()
  181. os.system(cmd.split(" ", 1)[0] + " -v " + cmd.split(" ", 1)[1])
  182. exit("")
  183. ########################################################################
  184. ##
  185. ## GetDirectoryContents
  186. ##
  187. ## At times, makepanda will use a function like "os.listdir" to process
  188. ## all the files in a directory. Unfortunately, that means that any
  189. ## accidental addition of a file to a directory could cause makepanda
  190. ## to misbehave without warning.
  191. ##
  192. ## To alleviate this weakness, we created GetDirectoryContents. This
  193. ## uses "os.listdir" to fetch the directory contents, but then it
  194. ## compares the results to the appropriate CVS/Entries to see if
  195. ## they match. If not, it prints a big warning message.
  196. ##
  197. ########################################################################
  198. def GetDirectoryContents(dir, filters="*", skip=[]):
  199. if (type(filters)==str):
  200. filters = [filters]
  201. actual = {}
  202. files = os.listdir(dir)
  203. for filter in filters:
  204. for file in fnmatch.filter(files, filter):
  205. if (skip.count(file)==0) and (os.path.isfile(dir + "/" + file)):
  206. actual[file] = 1
  207. if (os.path.isfile(dir + "/CVS/Entries")):
  208. cvs = {}
  209. srchandle = open(dir+"/CVS/Entries", "r")
  210. files = []
  211. for line in srchandle:
  212. if (line[0]=="/"):
  213. s = line.split("/",2)
  214. if (len(s)==3):
  215. files.append(s[1])
  216. srchandle.close()
  217. for filter in filters:
  218. for file in fnmatch.filter(files, filter):
  219. if (skip.count(file)==0):
  220. cvs[file] = 1
  221. for file in actual.keys():
  222. if (file not in cvs and VERBOSE):
  223. msg = "%sWARNING: %s is in %s, but not in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
  224. print msg
  225. WARNINGS.append(msg)
  226. for file in cvs.keys():
  227. if (file not in actual and VERBOSE):
  228. msg = "%sWARNING: %s is not in %s, but is in CVS%s" % (GetColor("red"), GetColor("green") + file + GetColor(), GetColor("green") + dir + GetColor(), GetColor())
  229. print msg
  230. WARNINGS.append(msg)
  231. results = actual.keys()
  232. results.sort()
  233. return results
  234. ########################################################################
  235. ##
  236. ## LocateBinary
  237. ##
  238. ## This function searches the system PATH for the binary. Returns its
  239. ## full path when it is found, or None when it was not found.
  240. ##
  241. ########################################################################
  242. def LocateBinary(binary):
  243. if "PATH" not in os.environ or os.environ["PATH"] == "":
  244. p = os.defpath
  245. else:
  246. p = os.environ["PATH"]
  247. for path in p.split(os.pathsep):
  248. if os.access(os.path.join(path, binary), os.X_OK):
  249. return os.path.abspath(os.path.realpath(os.path.join(path, binary)))
  250. return None
  251. ########################################################################
  252. ##
  253. ## The Timestamp Cache
  254. ##
  255. ## The make utility is constantly fetching the timestamps of files.
  256. ## This can represent the bulk of the file accesses during the make
  257. ## process. The timestamp cache eliminates redundant checks.
  258. ##
  259. ########################################################################
  260. TIMESTAMPCACHE = {}
  261. def GetTimestamp(path):
  262. if path in TIMESTAMPCACHE:
  263. return TIMESTAMPCACHE[path]
  264. try: date = os.path.getmtime(path)
  265. except: date = 0
  266. TIMESTAMPCACHE[path] = date
  267. return date
  268. def ClearTimestamp(path):
  269. del TIMESTAMPCACHE[path]
  270. ########################################################################
  271. ##
  272. ## The Dependency cache.
  273. ##
  274. ## Makepanda's strategy for file dependencies is different from most
  275. ## make-utilities. Whenever a file is built, makepanda records
  276. ## that the file was built, and it records what the input files were,
  277. ## and what their dates were. Whenever a file is about to be built,
  278. ## panda compares the current list of input files and their dates,
  279. ## to the previous list of input files and their dates. If they match,
  280. ## there is no need to build the file.
  281. ##
  282. ########################################################################
  283. BUILTFROMCACHE = {}
  284. def JustBuilt(files,others):
  285. dates = []
  286. for file in files:
  287. del TIMESTAMPCACHE[file]
  288. dates.append(GetTimestamp(file))
  289. for file in others:
  290. dates.append(GetTimestamp(file))
  291. key = tuple(files)
  292. BUILTFROMCACHE[key] = [others,dates]
  293. def NeedsBuild(files,others):
  294. dates = []
  295. for file in files:
  296. dates.append(GetTimestamp(file))
  297. if (not os.path.exists(file)): return 1
  298. for file in others:
  299. dates.append(GetTimestamp(file))
  300. key = tuple(files)
  301. if (key in BUILTFROMCACHE):
  302. if (BUILTFROMCACHE[key] == [others,dates]):
  303. return 0
  304. else:
  305. oldothers = BUILTFROMCACHE[key][0]
  306. if (oldothers != others and VERBOSE):
  307. print "%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), str(files), GetColor())
  308. return 1
  309. ########################################################################
  310. ##
  311. ## The CXX include cache:
  312. ##
  313. ## The following routine scans a CXX file and returns a list of
  314. ## the include-directives inside that file. It's not recursive:
  315. ## it just returns the includes that are textually inside the
  316. ## file. If you need recursive dependencies, you need the higher-level
  317. ## routine CxxCalcDependencies, defined elsewhere.
  318. ##
  319. ## Since scanning a CXX file is slow, we cache the result. It records
  320. ## the date of the source file and the list of includes that it
  321. ## contains. It assumes that if the file date hasn't changed, that
  322. ## the list of include-statements inside the file has not changed
  323. ## either. Once again, this particular routine does not return
  324. ## recursive dependencies --- it only returns an explicit list of
  325. ## include statements that are textually inside the file. That
  326. ## is what the cache stores, as well.
  327. ##
  328. ########################################################################
  329. CXXINCLUDECACHE = {}
  330. global CxxIncludeRegex
  331. CxxIncludeRegex = re.compile('^[ \t]*[#][ \t]*include[ \t]+"([^"]+)"[ \t\r\n]*$')
  332. def CxxGetIncludes(path):
  333. date = GetTimestamp(path)
  334. if (path in CXXINCLUDECACHE):
  335. cached = CXXINCLUDECACHE[path]
  336. if (cached[0]==date): return cached[1]
  337. try: sfile = open(path, 'rb')
  338. except:
  339. exit("Cannot open source file \""+path+"\" for reading.")
  340. include = []
  341. for line in sfile:
  342. match = CxxIncludeRegex.match(line,0)
  343. if (match):
  344. incname = match.group(1)
  345. include.append(incname)
  346. sfile.close()
  347. CXXINCLUDECACHE[path] = [date, include]
  348. return include
  349. ########################################################################
  350. ##
  351. ## SaveDependencyCache / LoadDependencyCache
  352. ##
  353. ## This actually saves both the dependency and cxx-include caches.
  354. ##
  355. ########################################################################
  356. DCACHE_BACKED_UP = False
  357. def SaveDependencyCache():
  358. global DCACHE_BACKED_UP
  359. if not DCACHE_BACKED_UP:
  360. try:
  361. if (os.path.exists(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"))):
  362. os.rename(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),
  363. os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache-backup"))
  364. except: pass
  365. DCACHE_BACKED_UP = True
  366. try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb')
  367. except: icache = 0
  368. if (icache!=0):
  369. print "Storing dependency cache."
  370. cPickle.dump(CXXINCLUDECACHE, icache, 1)
  371. cPickle.dump(BUILTFROMCACHE, icache, 1)
  372. icache.close()
  373. def LoadDependencyCache():
  374. global CXXINCLUDECACHE
  375. global BUILTFROMCACHE
  376. try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'rb')
  377. except: icache = 0
  378. if (icache!=0):
  379. CXXINCLUDECACHE = cPickle.load(icache)
  380. BUILTFROMCACHE = cPickle.load(icache)
  381. icache.close()
  382. ########################################################################
  383. ##
  384. ## CxxFindSource: given a source file name and a directory list,
  385. ## searches the directory list for the given source file. Returns
  386. ## the full pathname of the located file.
  387. ##
  388. ## CxxFindHeader: given a source file, an include directive, and a
  389. ## directory list, searches the directory list for the given header
  390. ## file. Returns the full pathname of the located file.
  391. ##
  392. ## Of course, CxxFindSource and CxxFindHeader cannot find a source
  393. ## file that has not been created yet. This can cause dependency
  394. ## problems. So the function CreateStubHeader can be used to create
  395. ## a file that CxxFindSource or CxxFindHeader can subsequently find.
  396. ##
  397. ########################################################################
  398. def CxxFindSource(name, ipath):
  399. for dir in ipath:
  400. if (dir == "."): full = name
  401. else: full = dir + "/" + name
  402. if GetTimestamp(full) > 0: return full
  403. exit("Could not find source file: "+name)
  404. def CxxFindHeader(srcfile, incfile, ipath):
  405. if (incfile.startswith(".")):
  406. last = srcfile.rfind("/")
  407. if (last < 0): exit("CxxFindHeader cannot handle this case #1")
  408. srcdir = srcfile[:last+1]
  409. while (incfile[:1]=="."):
  410. if (incfile[:2]=="./"):
  411. incfile = incfile[2:]
  412. elif (incfile[:3]=="../"):
  413. incfile = incfile[3:]
  414. last = srcdir[:-1].rfind("/")
  415. if (last < 0): exit("CxxFindHeader cannot handle this case #2")
  416. srcdir = srcdir[:last+1]
  417. else: exit("CxxFindHeader cannot handle this case #3")
  418. full = srcdir + incfile
  419. if GetTimestamp(full) > 0: return full
  420. return 0
  421. else:
  422. for dir in ipath:
  423. full = dir + "/" + incfile
  424. if GetTimestamp(full) > 0: return full
  425. return 0
  426. ########################################################################
  427. ##
  428. ## CxxCalcDependencies(srcfile, ipath, ignore)
  429. ##
  430. ## Calculate the dependencies of a source file given a
  431. ## particular include-path. Any file in the list of files to
  432. ## ignore is not considered.
  433. ##
  434. ########################################################################
  435. global CxxIgnoreHeader
  436. global CxxDependencyCache
  437. CxxIgnoreHeader = {}
  438. CxxDependencyCache = {}
  439. def CxxCalcDependencies(srcfile, ipath, ignore):
  440. if (srcfile in CxxDependencyCache):
  441. return CxxDependencyCache[srcfile]
  442. if (ignore.count(srcfile)): return []
  443. dep = {}
  444. dep[srcfile] = 1
  445. includes = CxxGetIncludes(srcfile)
  446. for include in includes:
  447. header = CxxFindHeader(srcfile, include, ipath)
  448. if (header!=0):
  449. if (ignore.count(header)==0):
  450. hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore)
  451. for x in hdeps: dep[x] = 1
  452. result = dep.keys()
  453. CxxDependencyCache[srcfile] = result
  454. return result
  455. ########################################################################
  456. ##
  457. ## Registry Key Handling
  458. ##
  459. ## Of course, these routines will fail if you call them on a
  460. ## non win32 platform. If you use them on a win64 platform, they
  461. ## will look in the win32 private hive first, then look in the
  462. ## win64 hive.
  463. ##
  464. ########################################################################
  465. if sys.platform == "win32":
  466. import _winreg
  467. def TryRegistryKey(path):
  468. try:
  469. key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ)
  470. return key
  471. except: pass
  472. try:
  473. key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ | 256)
  474. return key
  475. except: pass
  476. return 0
  477. def ListRegistryKeys(path):
  478. result=[]
  479. index=0
  480. key = TryRegistryKey(path)
  481. if (key != 0):
  482. try:
  483. while (1):
  484. result.append(_winreg.EnumKey(key, index))
  485. index = index + 1
  486. except: pass
  487. _winreg.CloseKey(key)
  488. return result
  489. def GetRegistryKey(path, subkey):
  490. if (platform.architecture()[0]=="64bit"):
  491. path = path.replace("SOFTWARE\\", "SOFTWARE\\Wow6432Node\\")
  492. k1=0
  493. key = TryRegistryKey(path)
  494. if (key != 0):
  495. try:
  496. k1, k2 = _winreg.QueryValueEx(key, subkey)
  497. except: pass
  498. _winreg.CloseKey(key)
  499. return k1
  500. def GetProgramFiles():
  501. if ("PROGRAMFILES" in os.environ):
  502. return os.environ["PROGRAMFILES"]
  503. elif (os.path.isdir("C:\\Program Files")):
  504. return "C:\\Program Files"
  505. elif (os.path.isdir("D:\\Program Files")):
  506. return "D:\\Program Files"
  507. elif (os.path.isdir("E:\\Program Files")):
  508. return "E:\\Program Files"
  509. return 0
  510. ########################################################################
  511. ##
  512. ## Parsing Compiler Option Lists
  513. ##
  514. ########################################################################
  515. def GetListOption(opts, prefix):
  516. res=[]
  517. for x in opts:
  518. if (x.startswith(prefix)):
  519. res.append(x[len(prefix):])
  520. return res
  521. def GetValueOption(opts, prefix):
  522. for x in opts:
  523. if (x.startswith(prefix)):
  524. return x[len(prefix):]
  525. return 0
  526. def GetOptimizeOption(opts):
  527. val = GetValueOption(opts, "OPT:")
  528. if (val == 0):
  529. return OPTIMIZE
  530. return val
  531. ########################################################################
  532. ##
  533. ## General File Manipulation
  534. ##
  535. ########################################################################
  536. def MakeDirectory(path):
  537. if os.path.isdir(path): return 0
  538. os.mkdir(path)
  539. def ReadFile(wfile):
  540. try:
  541. srchandle = open(wfile, "rb")
  542. data = srchandle.read()
  543. srchandle.close()
  544. return data
  545. except: exit("Cannot read "+wfile)
  546. def WriteFile(wfile,data):
  547. try:
  548. dsthandle = open(wfile, "wb")
  549. dsthandle.write(data)
  550. dsthandle.close()
  551. except: exit("Cannot write "+wfile)
  552. def ConditionalWriteFile(dest,desiredcontents):
  553. try:
  554. rfile = open(dest, 'rb')
  555. contents = rfile.read(-1)
  556. rfile.close()
  557. except:
  558. contents=0
  559. if contents != desiredcontents:
  560. sys.stdout.flush()
  561. WriteFile(dest,desiredcontents)
  562. def DeleteCVS(dir):
  563. if dir == "": dir = "."
  564. for entry in os.listdir(dir):
  565. if (entry != ".") and (entry != ".."):
  566. subdir = dir + "/" + entry
  567. if (os.path.isdir(subdir)):
  568. if (entry == "CVS"):
  569. shutil.rmtree(subdir)
  570. else:
  571. DeleteCVS(subdir)
  572. elif (os.path.isfile(subdir) and (entry == ".cvsignore" or entry.startswith(".#"))):
  573. os.remove(subdir)
  574. def DeleteBuildFiles(dir):
  575. for entry in os.listdir(dir):
  576. if (entry != ".") and (entry != ".."):
  577. subdir = dir + "/" + entry
  578. if (os.path.isfile(subdir) and os.path.splitext(subdir)[-1] in SUFFIX_INC+[".pp", ".moved"]):
  579. os.remove(subdir)
  580. elif (os.path.isdir(subdir)):
  581. DeleteBuildFiles(subdir)
  582. def CreateFile(file):
  583. if (os.path.exists(file)==0):
  584. WriteFile(file,"")
  585. ########################################################################
  586. ##
  587. ## Visual Studio Manifest Manipulation.
  588. ##
  589. ########################################################################
  590. VC90CRTVERSIONRE=re.compile("name=['\"]Microsoft.VC90.CRT['\"]\\s+version=['\"]([0-9.]+)['\"]")
  591. def GetVC90CRTVersion(fn):
  592. manifest = ReadFile(fn)
  593. version = VC90CRTVERSIONRE.search(manifest)
  594. if (version == None):
  595. exit("Cannot locate version number in "+fn)
  596. return version.group(1)
  597. def SetVC90CRTVersion(fn, ver):
  598. manifest = ReadFile(fn)
  599. subst = " name='Microsoft.VC90.CRT' version='"+ver+"' "
  600. manifest = VC90CRTVERSIONRE.sub(subst, manifest)
  601. WriteFile(fn, manifest)
  602. ########################################################################
  603. ##
  604. ## Thirdparty libraries paths
  605. ##
  606. ########################################################################
  607. if (sys.platform == "win32"):
  608. if (platform.architecture()[0] == "64bit"):
  609. THIRDPARTYLIBS="thirdparty/win-libs-vc9-x64/"
  610. else:
  611. THIRDPARTYLIBS="thirdparty/win-libs-vc9/"
  612. if not os.path.isdir(THIRDPARTYLIBS):
  613. THIRDPARTYLIBS="thirdparty/win-libs-vc9/"
  614. VC90CRTVERSION = GetVC90CRTVersion(THIRDPARTYLIBS+"extras/bin/Microsoft.VC90.CRT.manifest")
  615. else:
  616. if (sys.platform == "darwin"):
  617. THIRDPARTYLIBS="thirdparty/darwin-libs-a/"
  618. elif (sys.platform.startswith("linux")):
  619. if (platform.architecture()[0] == "64bit"):
  620. THIRDPARTYLIBS="thirdparty/linux-libs-x64/"
  621. else:
  622. THIRDPARTYLIBS="thirdparty/linux-libs-a/"
  623. elif (sys.platform.startswith("freebsd")):
  624. if (platform.architecture()[0] == "64bit"):
  625. THIRDPARTYLIBS="thirdparty/freebsd-libs-x64/"
  626. else:
  627. THIRDPARTYLIBS="thirdparty/freebsd-libs-a/"
  628. else:
  629. exit("Unknown platform: %s" % sys.platform)
  630. VC90CRTVERSION = 0
  631. ########################################################################
  632. #
  633. # Create the panda build tree.
  634. #
  635. ########################################################################
  636. def MakeBuildTree():
  637. MakeDirectory(OUTPUTDIR)
  638. MakeDirectory(OUTPUTDIR+"/bin")
  639. MakeDirectory(OUTPUTDIR+"/lib")
  640. MakeDirectory(OUTPUTDIR+"/tmp")
  641. MakeDirectory(OUTPUTDIR+"/etc")
  642. MakeDirectory(OUTPUTDIR+"/plugins")
  643. MakeDirectory(OUTPUTDIR+"/include")
  644. MakeDirectory(OUTPUTDIR+"/models")
  645. MakeDirectory(OUTPUTDIR+"/models/audio")
  646. MakeDirectory(OUTPUTDIR+"/models/audio/sfx")
  647. MakeDirectory(OUTPUTDIR+"/models/icons")
  648. MakeDirectory(OUTPUTDIR+"/models/maps")
  649. MakeDirectory(OUTPUTDIR+"/models/misc")
  650. MakeDirectory(OUTPUTDIR+"/models/gui")
  651. MakeDirectory(OUTPUTDIR+"/direct")
  652. MakeDirectory(OUTPUTDIR+"/pandac")
  653. MakeDirectory(OUTPUTDIR+"/pandac/input")
  654. ########################################################################
  655. #
  656. # Make sure that you are in the root of the panda tree.
  657. #
  658. ########################################################################
  659. def CheckPandaSourceTree():
  660. dir = os.getcwd()
  661. if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or
  662. (os.path.exists(os.path.join(dir, "dtool","src","dtoolbase","dtoolbase.h"))==0) or
  663. (os.path.exists(os.path.join(dir, "panda","src","pandabase","pandabase.h"))==0)):
  664. exit("Current directory is not the root of the panda tree.")
  665. ########################################################################
  666. ##
  667. ## Gets or sets the output directory, by default "built".
  668. ## Gets or sets the optimize level.
  669. ## Gets or sets the verbose flag.
  670. ##
  671. ########################################################################
  672. def GetOutputDir():
  673. return OUTPUTDIR
  674. def SetOutputDir(outputdir):
  675. global OUTPUTDIR, CUSTOM_OUTPUTDIR
  676. OUTPUTDIR=outputdir
  677. CUSTOM_OUTPUTDIR=True
  678. def GetOptimize():
  679. return int(OPTIMIZE)
  680. def SetOptimize(optimize):
  681. global OPTIMIZE
  682. OPTIMIZE=optimize
  683. def GetVerbose():
  684. return VERBOSE
  685. def SetVerbose(verbose):
  686. global VERBOSE
  687. VERBOSE=verbose
  688. ########################################################################
  689. ##
  690. ## Package Selection
  691. ##
  692. ## This facility enables makepanda to keep a list of packages selected
  693. ## by the user for inclusion or omission.
  694. ##
  695. ########################################################################
  696. PKG_LIST_ALL=[]
  697. PKG_LIST_OMIT={}
  698. def PkgListSet(pkgs):
  699. global PKG_LIST_ALL
  700. global PKG_LIST_OMIT
  701. PKG_LIST_ALL=pkgs
  702. PKG_LIST_OMIT={}
  703. PkgEnableAll()
  704. def PkgListGet():
  705. return PKG_LIST_ALL
  706. def PkgEnableAll():
  707. for x in PKG_LIST_ALL:
  708. PKG_LIST_OMIT[x] = 0
  709. def PkgDisableAll():
  710. for x in PKG_LIST_ALL:
  711. PKG_LIST_OMIT[x] = 1
  712. def PkgEnable(pkg):
  713. PKG_LIST_OMIT[pkg] = 0
  714. def PkgDisable(pkg):
  715. PKG_LIST_OMIT[pkg] = 1
  716. def PkgSkip(pkg):
  717. return PKG_LIST_OMIT[pkg]
  718. def PkgSelected(pkglist, pkg):
  719. if (pkglist.count(pkg)==0): return 0
  720. if (PKG_LIST_OMIT[pkg]): return 0
  721. return 1
  722. ########################################################################
  723. ##
  724. ## These functions are for libraries which use pkg-config.
  725. ##
  726. ########################################################################
  727. def PkgConfigHavePkg(pkgname, tool = "pkg-config"):
  728. """Returns a bool whether the pkg-config package is installed."""
  729. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  730. return False
  731. if (tool == "pkg-config"):
  732. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
  733. else:
  734. return bool(LocateBinary(tool) != None)
  735. result = handle.read().strip()
  736. handle.close()
  737. return bool(len(result) > 0)
  738. def PkgConfigGetLibs(pkgname, tool = "pkg-config"):
  739. """Returns a list of libs for the package, prefixed by -l."""
  740. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  741. return []
  742. if (tool == "pkg-config"):
  743. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
  744. else:
  745. handle = os.popen(LocateBinary(tool) + " --libs")
  746. result = handle.read().strip()
  747. handle.close()
  748. libs = []
  749. for l in result.split(" "):
  750. libs.append(l)
  751. return libs
  752. def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
  753. """Returns a list of includes for the package, NOT prefixed by -I."""
  754. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  755. return []
  756. if (tool == "pkg-config"):
  757. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
  758. else:
  759. handle = os.popen(LocateBinary(tool) + " --cflags")
  760. result = handle.read().strip()
  761. if len(result) == 0: return []
  762. handle.close()
  763. libs = []
  764. for l in result.split(" "):
  765. if (l.startswith("-I")):
  766. libs.append(l.replace("-I", "").replace("\"", "").strip())
  767. return libs
  768. def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"):
  769. """Returns a list of library paths for the package, NOT prefixed by -L."""
  770. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  771. return []
  772. if (tool == "pkg-config"):
  773. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
  774. elif (tool == "wx-config"):
  775. return []
  776. else:
  777. handle = os.popen(LocateBinary(tool) + " --ldflags")
  778. result = handle.read().strip()
  779. handle.close()
  780. if len(result) == 0: return []
  781. libs = []
  782. for l in result.split(" "):
  783. if (l.startswith("-L")):
  784. libs.append(l.replace("-L", "").replace("\"", "").strip())
  785. return libs
  786. def PkgConfigGetDefSymbols(pkgname, tool = "pkg-config"):
  787. """Returns a dictionary of preprocessor definitions."""
  788. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  789. return []
  790. if (tool == "pkg-config"):
  791. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags " + pkgname)
  792. else:
  793. handle = os.popen(LocateBinary(tool) + " --cflags")
  794. result = handle.read().strip()
  795. handle.close()
  796. if len(result) == 0: return {}
  797. defs = {}
  798. for l in result.split(" "):
  799. if (l.startswith("-D")):
  800. d = l.replace("-D", "").replace("\"", "").strip().split("=")
  801. if (len(d) == 1):
  802. defs[d[0]] = ""
  803. else:
  804. defs[d[0]] = d[1]
  805. return defs
  806. def PkgConfigEnable(opt, pkgname, tool = "pkg-config"):
  807. """Adds the libraries and includes to IncDirectory, LibName and LibDirectory."""
  808. for i in PkgConfigGetIncDirs(pkgname, tool):
  809. IncDirectory(opt, i)
  810. for i in PkgConfigGetLibDirs(pkgname, tool):
  811. LibDirectory(opt, i)
  812. for i in PkgConfigGetLibs(pkgname, tool):
  813. LibName(opt, i)
  814. for i, j in PkgConfigGetDefSymbols(pkgname, tool).items():
  815. DefSymbol(opt, i, j)
  816. LD_CACHE = None
  817. STATIC_CACHE = None
  818. def GetLibCache():
  819. # Returns a list of cached libraries, not prefixed by lib and not suffixed by .so* or .a!
  820. global LD_CACHE
  821. if (LD_CACHE == None):
  822. LD_CACHE = []
  823. if (LocateBinary("ldconfig") != None):
  824. handle = os.popen(LocateBinary("ldconfig") + " -NXp")
  825. result = handle.read().strip().split("\n")
  826. for line in result:
  827. lib = line.strip().split(" ", 1)[0]
  828. lib = lib.split(".so", 1)[0][3:]
  829. LD_CACHE.append(lib)
  830. else:
  831. libs = glob.glob("/lib/*.so*") + glob.glob("/usr/lib/*.so*") + glob.glob("/usr/local/lib/*.so*") + glob.glob("/usr/PCBSD/local/lib/*.so*")
  832. if (sys.platform == "darwin"):
  833. libs += glob.glob("/lib/*.dylib*") + glob.glob("/usr/lib/*.dylib*") + glob.glob("/usr/local/lib/*.dylib*")
  834. libs += glob.glob("/usr/X11/lib/*.dylib*") + glob.glob("/usr/X11R6/lib/*.dylib*")
  835. for l in libs:
  836. lib = os.path.basename(l).split(".so", 1)[0].split(".dylib", 1)[0][3:]
  837. LD_CACHE.append(lib)
  838. # Now check for static libraries - they aren't found by ldconfig.
  839. for l in glob.glob("/lib/*.a") + glob.glob("/usr/lib/*.a") + glob.glob("/usr/local/lib/*.a") + glob.glob("/usr/PCBSD/local/lib/*.a"):
  840. lib = os.path.basename(l).split(".a", 1)[0][3:]
  841. LD_CACHE.append(lib)
  842. return LD_CACHE
  843. def ChooseLib(*libs):
  844. # Chooses a library from the parameters, in order of preference. Returns the first if none of them were found.
  845. for l in libs:
  846. libname = l
  847. if (l.startswith("lib")):
  848. libname = l[3:]
  849. if (libname in GetLibCache()):
  850. return libname
  851. else:
  852. print GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()
  853. if (len(libs) > 0):
  854. return libs[0]
  855. def PkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, framework = None, tool = "pkg-config"):
  856. global PKG_LIST_ALL
  857. if (pkg in PkgListGet() and PkgSkip(pkg)):
  858. return
  859. if (pkgconfig == ""):
  860. pkgconfig = None
  861. if (framework == ""):
  862. framework = None
  863. if (libs == None or libs == ""):
  864. libs = ()
  865. elif (isinstance(libs, str)):
  866. libs = (libs, )
  867. if (incs == None or incs == ""):
  868. incs = ()
  869. elif (isinstance(incs, str)):
  870. incs = (incs, )
  871. if (defs == None or defs == "" or len(defs) == 0):
  872. defs = {}
  873. elif (isinstance(incs, str)):
  874. defs = {defs : ""}
  875. elif (isinstance(incs, list) or isinstance(incs, tuple) or isinstance(incs, set)):
  876. olddefs = defs
  877. defs = {}
  878. for d in olddefs:
  879. defs[d] = ""
  880. if (os.path.isdir(THIRDPARTYLIBS + pkg.lower())):
  881. IncDirectory(pkg, THIRDPARTYLIBS + pkg.lower() + "/include")
  882. LibDirectory(pkg, THIRDPARTYLIBS + pkg.lower() + "/lib")
  883. for l in libs:
  884. libname = l
  885. if (l.startswith("lib")):
  886. libname = l[3:]
  887. # This is for backward compatibility - in the thirdparty dir, we kept some libs with "panda" prefix, like libpandatiff.
  888. if (len(glob.glob(THIRDPARTYLIBS + pkg.lower() + "/lib/libpanda%s.*" % libname)) > 0 and
  889. len(glob.glob(THIRDPARTYLIBS + pkg.lower() + "/lib/lib%s.*" % libname)) == 0):
  890. libname = "panda" + libname
  891. LibName(pkg, "-l" + libname)
  892. for d, v in defs.values():
  893. DefSymbol(pkg, d, v)
  894. elif (sys.platform == "darwin" and framework != None):
  895. if (os.path.isdir("/Library/Frameworks/%s.framework" % framework) or
  896. os.path.isdir("/System/Library/Frameworks/%s.framework" % framework) or
  897. os.path.isdir("/Developer/Library/Frameworks/%s.framework" % framework) or
  898. os.path.isdir("/Users/%s/System/Library/Frameworks/%s.framework" % (getpass.getuser(), framework))):
  899. LibName(pkg, "-framework " + framework)
  900. for d, v in defs.values():
  901. DefSymbol(pkg, d, v)
  902. elif (pkg in PkgListGet()):
  903. print "%sWARNING:%s Could not locate framework %s, excluding from build" % (GetColor("red"), GetColor(), framework)
  904. PkgDisable(pkg)
  905. else:
  906. print "%sERROR:%s Could not locate framework %s, aborting build" % (GetColor("red"), GetColor(), framework)
  907. exit()
  908. elif (LocateBinary(tool) != None and (tool != "pkg-config" or pkgconfig != None)):
  909. if (isinstance(pkgconfig, str) or tool != "pkg-config"):
  910. if (PkgConfigHavePkg(pkgconfig, tool)):
  911. return PkgConfigEnable(pkg, pkgconfig, tool)
  912. else:
  913. have_all_pkgs = True
  914. for pc in pkgconfig:
  915. if (PkgConfigHavePkg(pc, tool)):
  916. PkgConfigEnable(pkg, pc, tool)
  917. else:
  918. have_all_pkgs = False
  919. if (have_all_pkgs):
  920. return
  921. elif (pkgconfig != None and libs == None):
  922. if (pkg in PkgListGet()):
  923. print "%sWARNING:%s Could not locate package %s, excluding from build" % (GetColor("red"), GetColor(), pkgconfig)
  924. PkgDisable(pkg)
  925. else:
  926. print "%sERROR:%s Could not locate package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig)
  927. exit()
  928. else:
  929. # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves.
  930. have_pkg = True
  931. for l in libs:
  932. libname = l
  933. if (l.startswith("lib")):
  934. libname = l[3:]
  935. if (libname in GetLibCache()):
  936. LibName(pkg, "-l" + libname)
  937. else:
  938. if (VERBOSE):
  939. print GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()
  940. have_pkg = False
  941. for i in incs:
  942. incdir = None
  943. if (len(glob.glob("/usr/include/" + i)) > 0):
  944. incdir = sorted(glob.glob("/usr/include/" + i))[-1]
  945. elif (len(glob.glob("/usr/local/include/" + i)) > 0):
  946. incdir = sorted(glob.glob("/usr/local/include/" + i))[-1]
  947. elif (platform.uname()[1]=="pcbsd" and len(glob.glob("/usr/PCBSD/local/include/" + i)) > 0):
  948. incdir = sorted(glob.glob("/usr/PCBSD/local/include/" + i))[-1]
  949. else:
  950. have_pkg = False
  951. # Try searching in the package's IncDirectories.
  952. for ppkg, pdir in INCDIRECTORIES:
  953. if (pkg == ppkg and len(glob.glob(os.path.join(pdir, i))) > 0):
  954. incdir = sorted(glob.glob(os.path.join(pdir, i)))[-1]
  955. have_pkg = True
  956. if (incdir == None and VERBOSE and i.endswith(".h")):
  957. print GetColor("cyan") + "Couldn't find header file " + i + GetColor()
  958. # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists.
  959. if (incdir != None and os.path.isdir(incdir)):
  960. IncDirectory(pkg, incdir)
  961. if (not have_pkg):
  962. if (pkg in PkgListGet()):
  963. print "%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), pkg.lower())
  964. PkgDisable(pkg)
  965. else:
  966. print "%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), pkg.lower())
  967. exit()
  968. ########################################################################
  969. ##
  970. ## SDK Location
  971. ##
  972. ## This section is concerned with locating the install directories
  973. ## for various third-party packages. The results are stored in the
  974. ## SDK table.
  975. ##
  976. ## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK.
  977. ## The only way to reliably find it is to search through the installer's
  978. ## uninstall-directories, look in each one, and see if it contains the
  979. ## relevant files.
  980. ##
  981. ########################################################################
  982. SDK = {}
  983. def GetSdkDir(sdkname, sdkkey = None):
  984. # Returns the default SDK directory. If it exists,
  985. # and sdkkey is not None, it is put in SDK[sdkkey].
  986. # Note: return value may not be an existing path.
  987. sdir = "sdks"
  988. if (sys.platform.startswith("win")):
  989. sdir += "/win"
  990. sdir += platform.architecture()[0][:2]
  991. elif (sys.platform.startswith("linux")):
  992. sdir += "/linux"
  993. sdir += platform.architecture()[0][:2]
  994. elif (sys.platform == "darwin"):
  995. sdir += "/macosx"
  996. sdir += "/" + sdkname
  997. # If it does not exist, try the old location.
  998. if (sdkkey and not os.path.isdir(sdir)):
  999. sdir = "sdks/" + sdir
  1000. if (sys.platform.startswith("linux")):
  1001. sdir += "-linux"
  1002. sdir += platform.architecture()[0][:2]
  1003. elif (sys.platform == "darwin"):
  1004. sdir += "-osx"
  1005. if (os.path.isdir(sdir)):
  1006. SDK[sdkkey] = sdir
  1007. return sdir
  1008. def SdkLocateDirectX():
  1009. if (sys.platform != "win32"): return
  1010. GetSdkDir("directx8", "DX8")
  1011. GetSdkDir("directx9", "DX9")
  1012. if ("DX9" not in SDK):
  1013. ## Try to locate the key within the "new" March 2009 location in the registry (yecch):
  1014. dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath")
  1015. if (dir != 0):
  1016. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1017. if ("DX9" not in SDK) or ("DX8" not in SDK):
  1018. uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  1019. for subdir in ListRegistryKeys(uninstaller):
  1020. if (subdir[0]=="{"):
  1021. dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
  1022. if (dir != 0):
  1023. if (("DX8" not in SDK) and
  1024. (os.path.isfile(dir+"\\Include\\d3d8.h")) and
  1025. (os.path.isfile(dir+"\\Include\\d3dx8.h")) and
  1026. (os.path.isfile(dir+"\\Lib\\d3d8.lib")) and
  1027. (os.path.isfile(dir+"\\Lib\\d3dx8.lib"))):
  1028. SDK["DX8"] = dir.replace("\\", "/").rstrip("/")
  1029. if (("DX9" not in SDK) and
  1030. (os.path.isfile(dir+"\\Include\\d3d9.h")) and
  1031. (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
  1032. (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
  1033. (os.path.isfile(dir+"\\Lib\\x86\\d3d9.lib")) and
  1034. (os.path.isfile(dir+"\\Lib\\x86\\d3dx9.lib"))):
  1035. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  1036. if ("DX9" in SDK):
  1037. SDK["DIRECTCAM"] = SDK["DX9"]
  1038. def SdkLocateMaya():
  1039. for (ver,key) in MAYAVERSIONINFO:
  1040. if (PkgSkip(ver)==0 and ver not in SDK):
  1041. GetSdkDir(ver.lower().replace("x",""), ver)
  1042. if (not ver in SDK):
  1043. if (sys.platform == "win32"):
  1044. for dev in ["Alias|Wavefront","Alias","Autodesk"]:
  1045. fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath"
  1046. res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION")
  1047. if (res != 0):
  1048. res = res.replace("\\", "/").rstrip("/")
  1049. SDK[ver] = res
  1050. elif (sys.platform == "darwin"):
  1051. ddir = "/Applications/Autodesk/maya"+key+"/Maya.app/Contents"
  1052. if (os.path.isdir(ddir)): SDK[ver] = ddir
  1053. else:
  1054. if (platform.architecture()[0] == "64bit"):
  1055. ddir1 = "/usr/autodesk/maya"+key+"-x64"
  1056. ddir2 = "/usr/aw/maya"+key+"-x64"
  1057. else:
  1058. ddir1 = "/usr/autodesk/maya"+key
  1059. ddir2 = "/usr/aw/maya"+key
  1060. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  1061. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  1062. def SdkLocateMax():
  1063. if (sys.platform != "win32"): return
  1064. for version,key1,key2,subdir in MAXVERSIONINFO:
  1065. if (PkgSkip(version)==0):
  1066. if (version not in SDK):
  1067. GetSdkDir("maxsdk"+version.lower()[3:], version)
  1068. GetSdkDir("maxsdk"+version.lower()[3:], version+"CS")
  1069. if (not version in SDK):
  1070. top = GetRegistryKey(key1,key2)
  1071. if (top != 0):
  1072. SDK[version] = top + "maxsdk"
  1073. if (os.path.isdir(top + "\\" + subdir)!=0):
  1074. SDK[version+"CS"] = top + subdir
  1075. def SdkLocatePython():
  1076. if (PkgSkip("PYTHON")==0):
  1077. if (sys.platform == "win32"):
  1078. SDK["PYTHON"] = "thirdparty/win-python"
  1079. if (GetOptimize() <= 2):
  1080. SDK["PYTHON"] += "-dbg"
  1081. if (platform.architecture()[0] == "64bit" and os.path.isdir(SDK["PYTHON"] + "-x64")):
  1082. SDK["PYTHON"] += "-x64"
  1083. SDK["PYTHONEXEC"] = SDK["PYTHON"] + "/python"
  1084. if (GetOptimize() <= 2): SDK["PYTHONEXEC"] += "_d.exe"
  1085. else: SDK["PYTHONEXEC"] += ".exe"
  1086. if (not os.path.isfile(SDK["PYTHONEXEC"])):
  1087. exit("Could not find %s!" % SDK["PYTHONEXEC"])
  1088. os.system(SDK["PYTHONEXEC"].replace("/", "\\") + " -V > "+OUTPUTDIR+"/tmp/pythonversion 2>&1")
  1089. pv=ReadFile(OUTPUTDIR+"/tmp/pythonversion")
  1090. if (pv.startswith("Python ")==0):
  1091. exit("python -V did not produce the expected output")
  1092. pv = pv[7:10]
  1093. SDK["PYTHONVERSION"]="python"+pv
  1094. elif (sys.platform == "darwin"):
  1095. if "MACOSX" not in SDK: SdkLocateMacOSX()
  1096. if (os.path.isdir("%s/System/Library/Frameworks/Python.framework" % SDK["MACOSX"])):
  1097. pv = os.readlink("%s/System/Library/Frameworks/Python.framework/Versions/Current" % SDK["MACOSX"])
  1098. SDK["PYTHON"] = SDK["MACOSX"] + "/System/Library/Frameworks/Python.framework/Headers"
  1099. SDK["PYTHONVERSION"] = "python " +pv
  1100. SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python"
  1101. else:
  1102. exit("Could not find the python framework!")
  1103. else:
  1104. SDK["PYTHON"] = sysconfig.get_python_inc()
  1105. SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
  1106. SDK["PYTHONEXEC"] = sys.executable
  1107. def SdkLocateVisualStudio():
  1108. if (sys.platform != "win32"): return
  1109. vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "9.0")
  1110. if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
  1111. vcdir = vcdir[:-3]
  1112. SDK["VISUALSTUDIO"] = vcdir
  1113. elif "VCINSTALLDIR" in os.environ:
  1114. vcdir = os.environ["VCINSTALLDIR"]
  1115. if (vcdir[-3:] == "\\VC"):
  1116. vcdir = vcdir[:-2]
  1117. elif (vcdir[-4:] == "\\VC\\"):
  1118. vcdir = vcdir[:-3]
  1119. SDK["VISUALSTUDIO"] = vcdir
  1120. def SdkLocateMSPlatform():
  1121. if (sys.platform != "win32"): return
  1122. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", "Install Dir")
  1123. if (platsdk and not os.path.isdir(platsdk)): platsdk = 0
  1124. if (platsdk == 0):
  1125. platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.1","InstallationFolder")
  1126. if (platsdk and not os.path.isdir(platsdk)): platsdk = 0
  1127. if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2"))):
  1128. if (platform.architecture()[0]!="64bit" or os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2", "Lib", "AMD64"))):
  1129. platsdk = os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2")
  1130. if (not os.path.isdir(platsdk)): platsdk = 0
  1131. # Doesn't work with the Express versions, so we're checking for the "atlmfc" dir, which is not in the Express
  1132. if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\atlmfc"))
  1133. and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\PlatformSDK"))):
  1134. platsdk = os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\PlatformSDK")
  1135. if (not os.path.isdir(platsdk)): platsdk = 0
  1136. # This may not be the best idea but it does give a warning
  1137. if (platsdk == 0):
  1138. if ("WindowsSdkDir" in os.environ):
  1139. WARNINGS.append("Windows SDK directory not found in registry, found in Environment variables instead")
  1140. platsdk = os.environ["WindowsSdkDir"]
  1141. if (platsdk != 0):
  1142. if (not platsdk.endswith("\\")):
  1143. platsdk += "\\"
  1144. SDK["MSPLATFORM"] = platsdk
  1145. def SdkLocateMacOSX():
  1146. if (sys.platform != "darwin"): return
  1147. if (os.path.exists("/Developer/SDKs/MacOSX10.6.sdk")):
  1148. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.6.sdk"
  1149. elif (os.path.exists("/Developer/SDKs/MacOSX10.5.sdk")):
  1150. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.5.sdk"
  1151. elif (os.path.exists("/Developer/SDKs/MacOSX10.4u.sdk")):
  1152. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4u.sdk"
  1153. elif (os.path.exists("/Developer/SDKs/MacOSX10.4.0.sdk")):
  1154. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4.0.sdk"
  1155. else:
  1156. exit("Could not find any MacOSX SDK")
  1157. ########################################################################
  1158. ##
  1159. ## SDK Auto-Disables
  1160. ##
  1161. ## Disable packages whose SDKs could not be found.
  1162. ##
  1163. ########################################################################
  1164. def SdkAutoDisableDirectX():
  1165. for ver in ["DX8","DX9","DIRECTCAM"]:
  1166. if (PkgSkip(ver)==0):
  1167. if (ver not in SDK):
  1168. if (sys.platform.startswith("win")):
  1169. WARNINGS.append("I cannot locate SDK for "+ver)
  1170. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  1171. PkgDisable(ver)
  1172. else:
  1173. WARNINGS.append("Using "+ver+" sdk: "+SDK[ver])
  1174. def SdkAutoDisableMaya():
  1175. for (ver,key) in MAYAVERSIONINFO:
  1176. if (ver not in SDK) and (PkgSkip(ver)==0):
  1177. if (sys.platform == "win32"):
  1178. WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.")
  1179. else:
  1180. WARNINGS.append("I cannot locate SDK for "+ver)
  1181. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  1182. PkgDisable(ver)
  1183. def SdkAutoDisableMax():
  1184. for version,key1,key2,subdir in MAXVERSIONINFO:
  1185. if (PkgSkip(version)==0) and ((version not in SDK) or (version+"CS" not in SDK)):
  1186. if (sys.platform.startswith("win")):
  1187. if (version in SDK):
  1188. WARNINGS.append("Your copy of "+version+" does not include the character studio SDK")
  1189. else:
  1190. WARNINGS.append("The registry does not appear to contain a pointer to "+version)
  1191. WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower())
  1192. PkgDisable(version)
  1193. ########################################################################
  1194. ##
  1195. ## Visual Studio comes with a script called VSVARS32.BAT, which
  1196. ## you need to run before using visual studio command-line tools.
  1197. ## The following python subroutine serves the same purpose.
  1198. ##
  1199. ########################################################################
  1200. def AddToPathEnv(path,add):
  1201. if (path in os.environ):
  1202. if (sys.platform.startswith("win")):
  1203. os.environ[path] = add + ";" + os.environ[path]
  1204. else:
  1205. os.environ[path] = add + ":" + os.environ[path]
  1206. else:
  1207. os.environ[path] = add
  1208. def SetupVisualStudioEnviron():
  1209. if ("VISUALSTUDIO" not in SDK):
  1210. exit("Could not find Visual Studio install directory")
  1211. if ("MSPLATFORM" not in SDK):
  1212. exit("Could not find the Microsoft Platform SDK")
  1213. os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + "VC"
  1214. os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"]
  1215. suffix=""
  1216. if (platform.architecture()[0]=="64bit"): suffix = "\\amd64"
  1217. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "VC\\bin"+suffix)
  1218. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE")
  1219. AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include")
  1220. AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include")
  1221. AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\lib"+suffix)
  1222. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
  1223. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl")
  1224. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\mfc")
  1225. if (platform.architecture()[0]=="32bit"):
  1226. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib")
  1227. elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\x64")):
  1228. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\x64")
  1229. elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\amd64")):
  1230. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\amd64")
  1231. else:
  1232. exit("Could not locate 64-bits libraries in Platform SDK!")
  1233. ########################################################################
  1234. #
  1235. # Include and Lib directories.
  1236. #
  1237. # These allow you to add include and lib directories to the
  1238. # compiler search paths. These methods accept a "package"
  1239. # parameter, which specifies which package the directory is
  1240. # associated with. The include/lib directory is not used
  1241. # if the package is not selected. The package can be 'ALWAYS'.
  1242. #
  1243. ########################################################################
  1244. INCDIRECTORIES = []
  1245. LIBDIRECTORIES = []
  1246. LIBNAMES = []
  1247. DEFSYMBOLS = []
  1248. def IncDirectory(opt, dir):
  1249. INCDIRECTORIES.append((opt, dir))
  1250. def LibDirectory(opt, dir):
  1251. LIBDIRECTORIES.append((opt, dir))
  1252. def LibName(opt, name):
  1253. #check to see if the lib file actually exists for the thrid party library given
  1254. #are we a thrid party library?
  1255. if name.startswith("thirdparty"):
  1256. #does this lib exists
  1257. if not os.path.exists(name):
  1258. PkgDisable(opt)
  1259. WARNINGS.append(name + " not found. Skipping Package " + opt)
  1260. return
  1261. LIBNAMES.append((opt, name))
  1262. def DefSymbol(opt, sym, val):
  1263. DEFSYMBOLS.append((opt, sym, val))
  1264. ########################################################################
  1265. #
  1266. # On Linux/OSX, to run panda, the dynamic linker needs to know how to
  1267. # find the shared libraries. This subroutine verifies that the dynamic
  1268. # linker is properly configured. If not, it sets it up on a temporary
  1269. # basis and issues a warning.
  1270. #
  1271. ########################################################################
  1272. def CheckLinkerLibraryPath():
  1273. if (sys.platform == "win32"): return
  1274. builtlib = os.path.abspath(os.path.join(OUTPUTDIR,"lib"))
  1275. dyldpath = []
  1276. try:
  1277. ldpath = []
  1278. f = file("/etc/ld.so.conf","r")
  1279. for line in f: ldpath.append(line.rstrip())
  1280. f.close()
  1281. except: ldpath = []
  1282. # Get the current
  1283. if ("LD_LIBRARY_PATH" in os.environ):
  1284. ldpath = ldpath + os.environ["LD_LIBRARY_PATH"].split(":")
  1285. if (sys.platform == "darwin" and "DYLD_LIBRARY_PATH" in os.environ):
  1286. dyldpath = os.environ["DYLD_LIBRARY_PATH"].split(":")
  1287. # Remove any potential current Panda installation lib dirs
  1288. for i in ldpath:
  1289. if i.startswith("/usr/lib/panda"): ldpath.remove(i)
  1290. for i in ldpath:
  1291. if i.startswith("/usr/local/panda"): ldpath.remove(i)
  1292. for i in dyldpath:
  1293. if i.startswith("/Applications/Panda3D"): dyldpath.remove(i)
  1294. # Add built/lib/ to (DY)LD_LIBRARY_PATH if it's not already there
  1295. if (ldpath.count(builtlib)==0):
  1296. if ("LD_LIBRARY_PATH" in os.environ):
  1297. os.environ["LD_LIBRARY_PATH"] = builtlib + ":" + os.environ["LD_LIBRARY_PATH"]
  1298. else:
  1299. os.environ["LD_LIBRARY_PATH"] = builtlib
  1300. if (sys.platform == "darwin" and dyldpath.count(builtlib)==0):
  1301. if ("DYLD_LIBRARY_PATH" in os.environ):
  1302. os.environ["DYLD_LIBRARY_PATH"] = builtlib + ":" + os.environ["DYLD_LIBRARY_PATH"]
  1303. else:
  1304. os.environ["DYLD_LIBRARY_PATH"] = builtlib
  1305. # Workaround around compile issue on PCBSD
  1306. if (platform.uname()[1]=="pcbsd"):
  1307. os.environ["LD_LIBRARY_PATH"] += ":/usr/PCBSD/local/lib"
  1308. ########################################################################
  1309. ##
  1310. ## Routines to copy files into the build tree
  1311. ##
  1312. ########################################################################
  1313. def CopyFile(dstfile,srcfile):
  1314. if (dstfile[-1]=='/'):
  1315. dstdir = dstfile
  1316. fnl = srcfile.rfind("/")
  1317. if (fnl < 0): fn = srcfile
  1318. else: fn = srcfile[fnl+1:]
  1319. dstfile = dstdir + fn
  1320. if (NeedsBuild([dstfile],[srcfile])):
  1321. WriteFile(dstfile,ReadFile(srcfile))
  1322. JustBuilt([dstfile], [srcfile])
  1323. def CopyAllFiles(dstdir, srcdir, suffix=""):
  1324. for x in GetDirectoryContents(srcdir, ["*"+suffix]):
  1325. CopyFile(dstdir+x, srcdir+x)
  1326. def CopyAllHeaders(dir, skip=[]):
  1327. for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
  1328. srcfile = dir + "/" + filename
  1329. dstfile = OUTPUTDIR+"/include/" + filename
  1330. if (NeedsBuild([dstfile],[srcfile])):
  1331. WriteFile(dstfile,ReadFile(srcfile))
  1332. JustBuilt([dstfile],[srcfile])
  1333. def CopyTree(dstdir,srcdir):
  1334. if (os.path.isdir(dstdir)): return 0
  1335. if (sys.platform == "win32"):
  1336. cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"'
  1337. else:
  1338. cmd = 'cp -R -f ' + srcdir + ' ' + dstdir
  1339. oscmd(cmd)
  1340. ########################################################################
  1341. ##
  1342. ## Parse PandaVersion.pp to extract the version number.
  1343. ##
  1344. ########################################################################
  1345. def ParsePandaVersion(fn):
  1346. try:
  1347. f = file(fn, "r")
  1348. pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
  1349. for line in f:
  1350. match = pattern.match(line,0)
  1351. if (match):
  1352. version = match.group(1)+"."+match.group(2)+"."+match.group(3)
  1353. break
  1354. f.close()
  1355. except: version="0.0.0"
  1356. return version
  1357. def ParsePluginVersion(fn):
  1358. try:
  1359. f = file(fn, "r")
  1360. pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_PLUGIN_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
  1361. for line in f:
  1362. match = pattern.match(line,0)
  1363. if (match):
  1364. version = match.group(1)+"."+match.group(2)+"."+match.group(3)
  1365. break
  1366. f.close()
  1367. except: version="0.0.0"
  1368. return version
  1369. ########################################################################
  1370. ##
  1371. ## FindLocation
  1372. ##
  1373. ########################################################################
  1374. ORIG_EXT={}
  1375. def GetOrigExt(x):
  1376. return ORIG_EXT[x]
  1377. def SetOrigExt(x, v):
  1378. ORIG_EXT[x] = v
  1379. def CalcLocation(fn, ipath):
  1380. if (fn.count("/")): return fn
  1381. dllext = ""
  1382. if (GetOptimize() <= 2): dllext = "_d"
  1383. if (fn == "PandaModules.py"): return "pandac/" + fn
  1384. if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath)
  1385. if (fn.endswith(".I")): return CxxFindSource(fn, ipath)
  1386. if (fn.endswith(".h")): return CxxFindSource(fn, ipath)
  1387. if (fn.endswith(".c")): return CxxFindSource(fn, ipath)
  1388. if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath)
  1389. if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
  1390. if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath)
  1391. if (sys.platform.startswith("win")):
  1392. if (fn.endswith(".def")): return CxxFindSource(fn, ipath)
  1393. if (fn.endswith(".rc")): return CxxFindSource(fn, ipath)
  1394. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn
  1395. if (fn.endswith(".res")): return OUTPUTDIR+"/tmp/"+fn
  1396. if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".dll"
  1397. if (fn.endswith(".pyd")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".pyd"
  1398. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".mll"
  1399. if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dlo"
  1400. if (fn.endswith(".dli")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dli"
  1401. if (fn.endswith(".dle")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle"
  1402. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn
  1403. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib"
  1404. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib"
  1405. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  1406. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  1407. elif (sys.platform == "darwin"):
  1408. if (fn.endswith(".mm")): return CxxFindSource(fn, ipath)
  1409. if (fn.endswith(".r")): return CxxFindSource(fn, ipath)
  1410. if (fn.endswith(".plist")): return CxxFindSource(fn, ipath)
  1411. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  1412. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
  1413. if (fn.endswith(".pyd")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
  1414. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  1415. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  1416. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  1417. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  1418. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  1419. if (fn.endswith(".rsrc")): return OUTPUTDIR+"/tmp/"+fn
  1420. if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn
  1421. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  1422. else:
  1423. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  1424. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
  1425. if (fn.endswith(".pyd")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
  1426. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  1427. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  1428. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  1429. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  1430. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  1431. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  1432. return fn
  1433. def FindLocation(fn, ipath):
  1434. loc = CalcLocation(fn, ipath)
  1435. (base,ext) = os.path.splitext(fn)
  1436. ORIG_EXT[loc] = ext
  1437. return loc
  1438. ########################################################################
  1439. ##
  1440. ## TargetAdd
  1441. ##
  1442. ## Makepanda maintains a list of make-targets. Each target has
  1443. ## these attributes:
  1444. ##
  1445. ## name - the name of the file being created.
  1446. ## ext - the original file extension, prior to OS-specific translation
  1447. ## inputs - the names of the input files to the compiler
  1448. ## deps - other input files that the target also depends on
  1449. ## opts - compiler options, a catch-all category
  1450. ##
  1451. ## TargetAdd will create the target if it does not exist. Then,
  1452. ## depending on what options you pass, it will push data onto these
  1453. ## various target attributes. This is cumulative: for example, if
  1454. ## you use TargetAdd to add compiler options, then use TargetAdd
  1455. ## again with more compiler options, both sets of options will be
  1456. ## included.
  1457. ##
  1458. ## TargetAdd does some automatic dependency generation on C++ files.
  1459. ## It will scan these files for include-files and automatically push
  1460. ## the include files onto the list of dependencies. In order to do
  1461. ## this, it needs an include-file search path. So if you supply
  1462. ## any C++ input, you also need to supply compiler options containing
  1463. ## include-directories, or alternately, a separate ipath parameter.
  1464. ##
  1465. ## The main body of 'makepanda' is a long list of TargetAdd
  1466. ## directives building up a giant list of make targets. Then,
  1467. ## finally, the targets are run and panda is built.
  1468. ##
  1469. ## Makepanda's dependency system does not understand multiple
  1470. ## outputs from a single build step. When a build step generates
  1471. ## a primary output file and a secondary output file, it is
  1472. ## necessary to trick the dependency system. Insert a dummy
  1473. ## build step that "generates" the secondary output file, using
  1474. ## the primary output file as an input. There is a special
  1475. ## compiler option DEPENDENCYONLY that creates such a dummy
  1476. ## build-step. There are two cases where dummy build steps must
  1477. ## be inserted: bison generates an OBJ and a secondary header
  1478. ## file, interrogate generates an IN and a secondary IGATE.OBJ.
  1479. ##
  1480. ########################################################################
  1481. class Target:
  1482. pass
  1483. TARGET_LIST=[]
  1484. TARGET_TABLE={}
  1485. def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0):
  1486. if (dummy != 0):
  1487. exit("Syntax error in TargetAdd "+target)
  1488. if (ipath == 0): ipath = opts
  1489. if (ipath == 0): ipath = []
  1490. if (type(input) == str): input = [input]
  1491. if (type(dep) == str): dep = [dep]
  1492. full = FindLocation(target,[OUTPUTDIR+"/include"])
  1493. if (full not in TARGET_TABLE):
  1494. t = Target()
  1495. t.name = full
  1496. t.inputs = []
  1497. t.deps = {}
  1498. t.opts = []
  1499. TARGET_TABLE[full] = t
  1500. TARGET_LIST.append(t)
  1501. else:
  1502. t = TARGET_TABLE[full]
  1503. ipath = [OUTPUTDIR+"/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"]
  1504. if (opts != 0):
  1505. for x in opts:
  1506. if (t.opts.count(x)==0):
  1507. t.opts.append(x)
  1508. if (input != 0):
  1509. for x in input:
  1510. fullinput = FindLocation(x, ipath)
  1511. t.inputs.append(fullinput)
  1512. t.deps[fullinput] = 1
  1513. (base,suffix) = os.path.splitext(x)
  1514. if (SUFFIX_INC.count(suffix)):
  1515. for d in CxxCalcDependencies(fullinput, ipath, []):
  1516. t.deps[d] = 1
  1517. if (dep != 0):
  1518. for x in dep:
  1519. fulldep = FindLocation(x, ipath)
  1520. t.deps[fulldep] = 1
  1521. if (target.endswith(".in")):
  1522. t.deps[FindLocation("interrogate.exe",[])] = 1
  1523. t.deps[FindLocation("dtool_have_python.dat",[])] = 1