makepandacore.py 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199
  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
  12. SUFFIX_INC=[".cxx",".c",".h",".I",".yxx",".lxx",".mm"]
  13. SUFFIX_DLL=[".dll",".dlo",".dle",".dli",".dlm",".mll",".exe"]
  14. SUFFIX_LIB=[".lib",".ilb"]
  15. STARTTIME=time.time()
  16. MAINTHREAD=threading.currentThread()
  17. OUTPUTDIR="built"
  18. ########################################################################
  19. ##
  20. ## Maya and Max Version List (with registry keys)
  21. ##
  22. ########################################################################
  23. MAYAVERSIONINFO=[("MAYA6", "6.0"),
  24. ("MAYA65", "6.5"),
  25. ("MAYA7", "7.0"),
  26. ("MAYA8", "8.0"),
  27. ("MAYA85", "8.5"),
  28. ("MAYA2008","2008"),
  29. ("MAYA2009","2009"),
  30. ]
  31. MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"),
  32. ("MAX7", "SOFTWARE\\Autodesk\\3DSMAX\\7.0", "Installdir", "maxsdk\\include\\CS"),
  33. ("MAX8", "SOFTWARE\\Autodesk\\3DSMAX\\8.0", "Installdir", "maxsdk\\include\\CS"),
  34. ("MAX9", "SOFTWARE\\Autodesk\\3DSMAX\\9.0", "Installdir", "maxsdk\\include\\CS"),
  35. ("MAX2009", "SOFTWARE\\Autodesk\\3DSMAX\\11.0", "Installdir", "maxsdk\\include\\CS"),
  36. ]
  37. MAYAVERSIONS=[]
  38. MAXVERSIONS=[]
  39. DXVERSIONS=["DX8","DX9"]
  40. for (ver,key) in MAYAVERSIONINFO:
  41. MAYAVERSIONS.append(ver)
  42. for (ver,key1,key2,subdir) in MAXVERSIONINFO:
  43. MAXVERSIONS.append(ver)
  44. ########################################################################
  45. ##
  46. ## The exit routine will normally
  47. ##
  48. ## - print a message
  49. ## - save the dependency cache
  50. ## - exit
  51. ##
  52. ## However, if it is invoked inside a thread, it instead:
  53. ##
  54. ## - prints a message
  55. ## - raises the "initiate-exit" exception
  56. ##
  57. ## If you create a thread, you must be prepared to catch this
  58. ## exception, save the dependency cache, and exit.
  59. ##
  60. ########################################################################
  61. WARNINGS=[]
  62. def PrettyTime(t):
  63. t = int(t)
  64. hours = t/3600
  65. t -= hours*3600
  66. minutes = t/60
  67. t -= minutes*60
  68. seconds = t
  69. if (hours): return str(hours)+" hours "+str(minutes)+" min"
  70. if (minutes): return str(minutes)+" min "+str(seconds)+" sec"
  71. return str(seconds)+" sec"
  72. def exit(msg):
  73. if (threading.currentThread() == MAINTHREAD):
  74. SaveDependencyCache()
  75. # Move any files we've moved away back.
  76. if os.path.isfile("dtool/src/dtoolutil/pandaVersion.h.moved"):
  77. os.rename("dtool/src/dtoolutil/pandaVersion.h.moved", "dtool/src/dtoolutil/pandaVersion.h")
  78. if os.path.isfile("dtool/src/dtoolutil/checkPandaVersion.h.moved"):
  79. os.rename("dtool/src/dtoolutil/checkPandaVersion.h.moved", "dtool/src/dtoolutil/checkPandaVersion.h")
  80. if os.path.isfile("dtool/src/dtoolutil/checkPandaVersion.cxx.moved"):
  81. os.rename("dtool/src/dtoolutil/checkPandaVersion.cxx.moved", "dtool/src/dtoolutil/checkPandaVersion.cxx")
  82. print "Elapsed Time: "+PrettyTime(time.time() - STARTTIME)
  83. print msg
  84. sys.stdout.flush()
  85. sys.stderr.flush()
  86. os._exit(1)
  87. else:
  88. print msg
  89. raise "initiate-exit"
  90. ########################################################################
  91. ##
  92. ## Run a command.
  93. ##
  94. ########################################################################
  95. def oscmd(cmd, ignoreError = False):
  96. print cmd
  97. sys.stdout.flush()
  98. if sys.platform == "win32":
  99. exe = cmd.split()[0]+".exe"
  100. if os.path.isfile(exe)==0:
  101. for i in os.environ["PATH"].split(";"):
  102. if os.path.isfile(os.path.join(i, exe)):
  103. exe = os.path.join(i, exe)
  104. break
  105. if os.path.isfile(exe)==0:
  106. exit("Cannot find "+exe+" on search path")
  107. res = os.spawnl(os.P_WAIT, exe, cmd)
  108. else:
  109. res = os.system(cmd)
  110. if res != 0 and not ignoreError:
  111. exit("")
  112. ########################################################################
  113. ##
  114. ## GetDirectoryContents
  115. ##
  116. ## At times, makepanda will use a function like "os.listdir" to process
  117. ## all the files in a directory. Unfortunately, that means that any
  118. ## accidental addition of a file to a directory could cause makepanda
  119. ## to misbehave without warning.
  120. ##
  121. ## To alleviate this weakness, we created GetDirectoryContents. This
  122. ## uses "os.listdir" to fetch the directory contents, but then it
  123. ## compares the results to the appropriate CVS/Entries to see if
  124. ## they match. If not, it prints a big warning message.
  125. ##
  126. ########################################################################
  127. def GetDirectoryContents(dir, filters="*", skip=[]):
  128. if (type(filters)==str):
  129. filters = [filters]
  130. actual = {}
  131. files = os.listdir(dir)
  132. for filter in filters:
  133. for file in fnmatch.filter(files, filter):
  134. if (skip.count(file)==0) and (os.path.isfile(dir + "/" + file)):
  135. actual[file] = 1
  136. if (os.path.isfile(dir + "/CVS/Entries")):
  137. cvs = {}
  138. srchandle = open(dir+"/CVS/Entries", "r")
  139. files = []
  140. for line in srchandle:
  141. if (line[0]=="/"):
  142. s = line.split("/",2)
  143. if (len(s)==3):
  144. files.append(s[1])
  145. srchandle.close()
  146. for filter in filters:
  147. for file in fnmatch.filter(files, filter):
  148. if (skip.count(file)==0):
  149. cvs[file] = 1
  150. for file in actual.keys():
  151. if (cvs.has_key(file)==0):
  152. msg = "WARNING: %s is in %s, but not in CVS"%(file, dir)
  153. print msg
  154. WARNINGS.append(msg)
  155. for file in cvs.keys():
  156. if (actual.has_key(file)==0):
  157. msg = "WARNING: %s is not in %s, but is in CVS"%(file, dir)
  158. print msg
  159. WARNINGS.append(msg)
  160. results = actual.keys()
  161. results.sort()
  162. return results
  163. ########################################################################
  164. ##
  165. ## LocateBinary
  166. ##
  167. ## This function searches the system PATH for the binary. Returns its
  168. ## full path when it is found, or None when it was not found.
  169. ##
  170. ########################################################################
  171. def LocateBinary(binary):
  172. if not os.environ.has_key("PATH") or os.environ["PATH"] == "":
  173. p = os.defpath
  174. else:
  175. p = os.environ["PATH"]
  176. for path in p.split(os.pathsep):
  177. if os.access(os.path.join(path, binary), os.X_OK):
  178. return os.path.abspath(os.path.realpath(os.path.join(path, binary)))
  179. return None
  180. ########################################################################
  181. ##
  182. ## The Timestamp Cache
  183. ##
  184. ## The make utility is constantly fetching the timestamps of files.
  185. ## This can represent the bulk of the file accesses during the make
  186. ## process. The timestamp cache eliminates redundant checks.
  187. ##
  188. ########################################################################
  189. TIMESTAMPCACHE = {}
  190. def GetTimestamp(path):
  191. if TIMESTAMPCACHE.has_key(path):
  192. return TIMESTAMPCACHE[path]
  193. try: date = os.path.getmtime(path)
  194. except: date = 0
  195. TIMESTAMPCACHE[path] = date
  196. return date
  197. def ClearTimestamp(path):
  198. del TIMESTAMPCACHE[path]
  199. ########################################################################
  200. ##
  201. ## The Dependency cache.
  202. ##
  203. ## Makepanda's strategy for file dependencies is different from most
  204. ## make-utilities. Whenever a file is built, makepanda records
  205. ## that the file was built, and it records what the input files were,
  206. ## and what their dates were. Whenever a file is about to be built,
  207. ## panda compares the current list of input files and their dates,
  208. ## to the previous list of input files and their dates. If they match,
  209. ## there is no need to build the file.
  210. ##
  211. ########################################################################
  212. BUILTFROMCACHE = {}
  213. def JustBuilt(files,others):
  214. dates = []
  215. for file in files:
  216. del TIMESTAMPCACHE[file]
  217. dates.append(GetTimestamp(file))
  218. for file in others:
  219. dates.append(GetTimestamp(file))
  220. key = tuple(files)
  221. BUILTFROMCACHE[key] = [others,dates]
  222. def NeedsBuild(files,others):
  223. dates = []
  224. for file in files:
  225. dates.append(GetTimestamp(file))
  226. for file in others:
  227. dates.append(GetTimestamp(file))
  228. key = tuple(files)
  229. if (BUILTFROMCACHE.has_key(key)):
  230. if (BUILTFROMCACHE[key] == [others,dates]):
  231. return 0
  232. else:
  233. oldothers = BUILTFROMCACHE[key][0]
  234. if (oldothers != others):
  235. print "CAUTION: file dependencies changed: "+str(files)
  236. return 1
  237. ########################################################################
  238. ##
  239. ## The CXX include cache:
  240. ##
  241. ## The following routine scans a CXX file and returns a list of
  242. ## the include-directives inside that file. It's not recursive:
  243. ## it just returns the includes that are textually inside the
  244. ## file. If you need recursive dependencies, you need the higher-level
  245. ## routine CxxCalcDependencies, defined elsewhere.
  246. ##
  247. ## Since scanning a CXX file is slow, we cache the result. It records
  248. ## the date of the source file and the list of includes that it
  249. ## contains. It assumes that if the file date hasn't changed, that
  250. ## the list of include-statements inside the file has not changed
  251. ## either. Once again, this particular routine does not return
  252. ## recursive dependencies --- it only returns an explicit list of
  253. ## include statements that are textually inside the file. That
  254. ## is what the cache stores, as well.
  255. ##
  256. ########################################################################
  257. CXXINCLUDECACHE = {}
  258. global CxxIncludeRegex
  259. CxxIncludeRegex = re.compile('^[ \t]*[#][ \t]*include[ \t]+"([^"]+)"[ \t\r\n]*$')
  260. def CxxGetIncludes(path):
  261. date = GetTimestamp(path)
  262. if (CXXINCLUDECACHE.has_key(path)):
  263. cached = CXXINCLUDECACHE[path]
  264. if (cached[0]==date): return cached[1]
  265. try: sfile = open(path, 'rb')
  266. except:
  267. exit("Cannot open source file \""+path+"\" for reading.")
  268. include = []
  269. for line in sfile:
  270. match = CxxIncludeRegex.match(line,0)
  271. if (match):
  272. incname = match.group(1)
  273. include.append(incname)
  274. sfile.close()
  275. CXXINCLUDECACHE[path] = [date, include]
  276. return include
  277. ########################################################################
  278. ##
  279. ## SaveDependencyCache / LoadDependencyCache
  280. ##
  281. ## This actually saves both the dependency and cxx-include caches.
  282. ##
  283. ########################################################################
  284. def SaveDependencyCache():
  285. try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb')
  286. except: icache = 0
  287. if (icache!=0):
  288. print "Storing dependency cache."
  289. cPickle.dump(CXXINCLUDECACHE, icache, 1)
  290. cPickle.dump(BUILTFROMCACHE, icache, 1)
  291. icache.close()
  292. def LoadDependencyCache():
  293. global CXXINCLUDECACHE
  294. global BUILTFROMCACHE
  295. try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'rb')
  296. except: icache = 0
  297. if (icache!=0):
  298. CXXINCLUDECACHE = cPickle.load(icache)
  299. BUILTFROMCACHE = cPickle.load(icache)
  300. icache.close()
  301. ########################################################################
  302. ##
  303. ## CxxFindSource: given a source file name and a directory list,
  304. ## searches the directory list for the given source file. Returns
  305. ## the full pathname of the located file.
  306. ##
  307. ## CxxFindHeader: given a source file, an include directive, and a
  308. ## directory list, searches the directory list for the given header
  309. ## file. Returns the full pathname of the located file.
  310. ##
  311. ## Of course, CxxFindSource and CxxFindHeader cannot find a source
  312. ## file that has not been created yet. This can cause dependency
  313. ## problems. So the function CreateStubHeader can be used to create
  314. ## a file that CxxFindSource or CxxFindHeader can subsequently find.
  315. ##
  316. ########################################################################
  317. def CxxFindSource(name, ipath):
  318. for dir in ipath:
  319. if (dir == "."): full = name
  320. else: full = dir + "/" + name
  321. if GetTimestamp(full) > 0: return full
  322. exit("Could not find source file: "+name)
  323. def CxxFindHeader(srcfile, incfile, ipath):
  324. if (incfile.startswith(".")):
  325. last = srcfile.rfind("/")
  326. if (last < 0): exit("CxxFindHeader cannot handle this case #1")
  327. srcdir = srcfile[:last+1]
  328. while (incfile[:1]=="."):
  329. if (incfile[:2]=="./"):
  330. incfile = incfile[2:]
  331. elif (incfile[:3]=="../"):
  332. incfile = incfile[3:]
  333. last = srcdir[:-1].rfind("/")
  334. if (last < 0): exit("CxxFindHeader cannot handle this case #2")
  335. srcdir = srcdir[:last+1]
  336. else: exit("CxxFindHeader cannot handle this case #3")
  337. full = srcdir + incfile
  338. if GetTimestamp(full) > 0: return full
  339. return 0
  340. else:
  341. for dir in ipath:
  342. full = dir + "/" + incfile
  343. if GetTimestamp(full) > 0: return full
  344. return 0
  345. ########################################################################
  346. ##
  347. ## CxxCalcDependencies(srcfile, ipath, ignore)
  348. ##
  349. ## Calculate the dependencies of a source file given a
  350. ## particular include-path. Any file in the list of files to
  351. ## ignore is not considered.
  352. ##
  353. ########################################################################
  354. global CxxIgnoreHeader
  355. global CxxDependencyCache
  356. CxxIgnoreHeader = {}
  357. CxxDependencyCache = {}
  358. def CxxCalcDependencies(srcfile, ipath, ignore):
  359. if (CxxDependencyCache.has_key(srcfile)):
  360. return CxxDependencyCache[srcfile]
  361. if (ignore.count(srcfile)): return []
  362. dep = {}
  363. dep[srcfile] = 1
  364. includes = CxxGetIncludes(srcfile)
  365. for include in includes:
  366. header = CxxFindHeader(srcfile, include, ipath)
  367. if (header!=0):
  368. if (ignore.count(header)==0):
  369. hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore)
  370. for x in hdeps: dep[x] = 1
  371. result = dep.keys()
  372. CxxDependencyCache[srcfile] = result
  373. return result
  374. ########################################################################
  375. ##
  376. ## Registry Key Handling
  377. ##
  378. ## Of course, these routines will fail if you call them on a
  379. ## non win32 platform. If you use them on a win64 platform, they
  380. ## will look in the win32 private hive first, then look in the
  381. ## win64 hive.
  382. ##
  383. ########################################################################
  384. if sys.platform == "win32":
  385. import _winreg
  386. def TryRegistryKey(path):
  387. try:
  388. key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ)
  389. return key
  390. except: pass
  391. try:
  392. key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, path, 0, _winreg.KEY_READ | 256)
  393. return key
  394. except: pass
  395. return 0
  396. def ListRegistryKeys(path):
  397. result=[]
  398. index=0
  399. key = TryRegistryKey(path)
  400. if (key != 0):
  401. try:
  402. while (1):
  403. result.append(_winreg.EnumKey(key, index))
  404. index = index + 1
  405. except: pass
  406. _winreg.CloseKey(key)
  407. return result
  408. def GetRegistryKey(path, subkey):
  409. k1=0
  410. key = TryRegistryKey(path)
  411. if (key != 0):
  412. try:
  413. k1, k2 = _winreg.QueryValueEx(key, subkey)
  414. except: pass
  415. _winreg.CloseKey(key)
  416. return k1
  417. ########################################################################
  418. ##
  419. ## Parsing Compiler Option Lists
  420. ##
  421. ########################################################################
  422. def GetListOption(opts, prefix):
  423. res=[]
  424. for x in opts:
  425. if (x.startswith(prefix)):
  426. res.append(x[len(prefix):])
  427. return res
  428. def GetValueOption(opts, prefix):
  429. for x in opts:
  430. if (x.startswith(prefix)):
  431. return x[len(prefix):]
  432. return 0
  433. def GetOptimizeOption(opts,defval):
  434. val = GetValueOption(opts, "OPT:")
  435. if (val == 0):
  436. return defval
  437. return val
  438. ########################################################################
  439. ##
  440. ## General File Manipulation
  441. ##
  442. ########################################################################
  443. def MakeDirectory(path):
  444. if os.path.isdir(path): return 0
  445. os.mkdir(path)
  446. def ReadFile(wfile):
  447. try:
  448. srchandle = open(wfile, "rb")
  449. data = srchandle.read()
  450. srchandle.close()
  451. return data
  452. except: exit("Cannot read "+wfile)
  453. def WriteFile(wfile,data):
  454. try:
  455. dsthandle = open(wfile, "wb")
  456. dsthandle.write(data)
  457. dsthandle.close()
  458. except: exit("Cannot write "+wfile)
  459. def ConditionalWriteFile(dest,desiredcontents):
  460. try:
  461. rfile = open(dest, 'rb')
  462. contents = rfile.read(-1)
  463. rfile.close()
  464. except:
  465. contents=0
  466. if contents != desiredcontents:
  467. sys.stdout.flush()
  468. WriteFile(dest,desiredcontents)
  469. def DeleteCVS(dir):
  470. for entry in os.listdir(dir):
  471. if (entry != ".") and (entry != ".."):
  472. subdir = dir + "/" + entry
  473. if (os.path.isdir(subdir)):
  474. if (entry == "CVS"):
  475. shutil.rmtree(subdir)
  476. else:
  477. DeleteCVS(subdir)
  478. def CreateFile(file):
  479. if (os.path.exists(file)==0):
  480. WriteFile(file,"")
  481. ########################################################################
  482. #
  483. # Create the panda build tree.
  484. #
  485. ########################################################################
  486. def MakeBuildTree():
  487. MakeDirectory(OUTPUTDIR)
  488. MakeDirectory(OUTPUTDIR+"/bin")
  489. MakeDirectory(OUTPUTDIR+"/lib")
  490. MakeDirectory(OUTPUTDIR+"/tmp")
  491. MakeDirectory(OUTPUTDIR+"/etc")
  492. MakeDirectory(OUTPUTDIR+"/plugins")
  493. MakeDirectory(OUTPUTDIR+"/modelcache")
  494. MakeDirectory(OUTPUTDIR+"/include")
  495. MakeDirectory(OUTPUTDIR+"/include/parser-inc")
  496. MakeDirectory(OUTPUTDIR+"/include/parser-inc/openssl")
  497. MakeDirectory(OUTPUTDIR+"/include/parser-inc/netinet")
  498. MakeDirectory(OUTPUTDIR+"/include/parser-inc/Cg")
  499. MakeDirectory(OUTPUTDIR+"/include/openssl")
  500. MakeDirectory(OUTPUTDIR+"/models")
  501. MakeDirectory(OUTPUTDIR+"/models/audio")
  502. MakeDirectory(OUTPUTDIR+"/models/audio/sfx")
  503. MakeDirectory(OUTPUTDIR+"/models/icons")
  504. MakeDirectory(OUTPUTDIR+"/models/maps")
  505. MakeDirectory(OUTPUTDIR+"/models/misc")
  506. MakeDirectory(OUTPUTDIR+"/models/gui")
  507. MakeDirectory(OUTPUTDIR+"/pandac")
  508. MakeDirectory(OUTPUTDIR+"/pandac/input")
  509. ########################################################################
  510. #
  511. # Make sure that you are in the root of the panda tree.
  512. #
  513. ########################################################################
  514. def CheckPandaSourceTree():
  515. dir = os.getcwd()
  516. if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or
  517. (os.path.exists(os.path.join(dir, "dtool","src","dtoolbase","dtoolbase.h"))==0) or
  518. (os.path.exists(os.path.join(dir, "panda","src","pandabase","pandabase.h"))==0)):
  519. exit("Current directory is not the root of the panda tree.")
  520. ########################################################################
  521. ##
  522. ## Visual Studio Manifest Manipulation.
  523. ##
  524. ########################################################################
  525. VC90CRTVERSIONRE=re.compile("name=['\"]Microsoft.VC90.CRT['\"]\\s+version=['\"]([0-9.]+)['\"]")
  526. def GetVC90CRTVersion(fn):
  527. manifest = ReadFile(fn)
  528. version = VC90CRTVERSIONRE.search(manifest)
  529. if (version == None):
  530. exit("Cannot locate version number in "+fn)
  531. return version.group(1)
  532. def SetVC90CRTVersion(fn, ver):
  533. manifest = ReadFile(fn)
  534. subst = " name='Microsoft.VC90.CRT' version='"+ver+"' "
  535. manifest = VC90CRTVERSIONRE.sub(subst, manifest)
  536. WriteFile(fn, manifest)
  537. ########################################################################
  538. ##
  539. ## Gets or sets the output directory, by default "built".
  540. ##
  541. ########################################################################
  542. def GetOutputDir():
  543. return OUTPUTDIR
  544. def SetOutputDir(outputdir):
  545. global OUTPUTDIR
  546. OUTPUTDIR=outputdir
  547. ########################################################################
  548. ##
  549. ## Package Selection
  550. ##
  551. ## This facility enables makepanda to keep a list of packages selected
  552. ## by the user for inclusion or omission.
  553. ##
  554. ########################################################################
  555. PKG_LIST_ALL=0
  556. PKG_LIST_OMIT=0
  557. def PkgListSet(pkgs):
  558. global PKG_LIST_ALL
  559. global PKG_LIST_OMIT
  560. PKG_LIST_ALL=pkgs
  561. PKG_LIST_OMIT={}
  562. PkgDisableAll()
  563. def PkgListGet():
  564. return PKG_LIST_ALL
  565. def PkgEnableAll():
  566. for x in PKG_LIST_ALL:
  567. PKG_LIST_OMIT[x] = 0
  568. def PkgDisableAll():
  569. for x in PKG_LIST_ALL:
  570. PKG_LIST_OMIT[x] = 1
  571. def PkgEnable(pkg):
  572. PKG_LIST_OMIT[pkg] = 0
  573. def PkgDisable(pkg):
  574. PKG_LIST_OMIT[pkg] = 1
  575. def PkgSkip(pkg):
  576. return PKG_LIST_OMIT[pkg]
  577. def PkgSelected(pkglist, pkg):
  578. if (pkglist.count(pkg)==0): return 0
  579. if (PKG_LIST_OMIT[pkg]): return 0
  580. return 1
  581. ########################################################################
  582. ##
  583. ## These functions are for libraries which use pkg-config.
  584. ##
  585. ########################################################################
  586. def PkgConfigHavePkg(pkgname):
  587. """Returns a bool whether the pkg-config package is installed."""
  588. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  589. return False
  590. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
  591. result = handle.read().strip()
  592. handle.close()
  593. return bool(len(result) > 0)
  594. def PkgConfigGetLibs(pkgname):
  595. """Returns a list of libs for the package, prefixed by -l."""
  596. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  597. return []
  598. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
  599. result = handle.read().strip()
  600. handle.close()
  601. libs = []
  602. for l in result.split(" "):
  603. libs.append(l)
  604. return libs
  605. def PkgConfigGetIncDirs(pkgname):
  606. """Returns a list of includes for the package, NOT prefixed by -I."""
  607. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  608. return []
  609. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
  610. result = handle.read().strip()
  611. handle.close()
  612. if len(result) == 0: return []
  613. libs = []
  614. for l in result.split(" "):
  615. libs.append(l.replace("-I", "").replace("\"", "").strip())
  616. return libs
  617. def PkgConfigGetLibDirs(pkgname):
  618. """Returns a list of library paths for the package, NOT prefixed by -L."""
  619. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  620. return []
  621. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
  622. result = handle.read().strip()
  623. handle.close()
  624. if len(result) == 0: return []
  625. libs = []
  626. for l in result.split(" "):
  627. libs.append(l.replace("-L", "").replace("\"", "").strip())
  628. return libs
  629. def PkgConfigEnable(opt, pkgname):
  630. """Adds the libraries and includes to IncDirectory, LibName and LibDirectory."""
  631. for i in PkgConfigGetIncDirs(pkgname):
  632. IncDirectory(opt, i)
  633. for i in PkgConfigGetLibDirs(pkgname):
  634. LibDirectory(opt, i)
  635. for i in PkgConfigGetLibs(pkgname):
  636. LibName(opt, i)
  637. ########################################################################
  638. ##
  639. ## SDK Location
  640. ##
  641. ## This section is concerned with locating the install directories
  642. ## for various third-party packages. The results are stored in the
  643. ## SDK table.
  644. ##
  645. ## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK.
  646. ## The only way to reliably find it is to search through the installer's
  647. ## uninstall-directories, look in each one, and see if it contains the
  648. ## relevant files.
  649. ##
  650. ########################################################################
  651. SDK = {}
  652. def SdkLocateDirectX():
  653. if (sys.platform != "win32"): return
  654. if (os.path.isdir("sdks/directx8")): SDK["DX8"]="sdks/directx8"
  655. if (os.path.isdir("sdks/directx9")): SDK["DX9"]="sdks/directx9"
  656. uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  657. for subdir in ListRegistryKeys(uninstaller):
  658. if (subdir[0]=="{"):
  659. dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
  660. if (dir != 0):
  661. if ((SDK.has_key("DX8")==0) and
  662. (os.path.isfile(dir+"\\Include\\d3d8.h")) and
  663. (os.path.isfile(dir+"\\Include\\d3dx8.h")) and
  664. (os.path.isfile(dir+"\\Lib\\d3d8.lib")) and
  665. (os.path.isfile(dir+"\\Lib\\d3dx8.lib"))):
  666. SDK["DX8"] = dir.replace("\\", "/").rstrip("/")
  667. if ((SDK.has_key("DX9")==0) and
  668. (os.path.isfile(dir+"\\Include\\d3d9.h")) and
  669. (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
  670. (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
  671. (os.path.isfile(dir+"\\Lib\\x86\\d3d9.lib")) and
  672. (os.path.isfile(dir+"\\Lib\\x86\\d3dx9.lib"))):
  673. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  674. if (SDK.has_key("DX9")):
  675. SDK["DIRECTCAM"] = SDK["DX9"]
  676. def SdkLocateMaya():
  677. for (ver,key) in MAYAVERSIONINFO:
  678. if (PkgSkip(ver)==0 and SDK.has_key(ver)==0):
  679. if (sys.platform == "win32"):
  680. ddir = "sdks/"+ver.lower().replace("x","")
  681. if (os.path.isdir(ddir)):
  682. SDK[ver] = ddir
  683. else:
  684. for dev in ["Alias|Wavefront","Alias","Autodesk"]:
  685. fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath"
  686. res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION")
  687. if (res != 0):
  688. res = res.replace("\\", "/").rstrip("/")
  689. SDK[ver] = res
  690. elif (sys.platform == "darwin"):
  691. ddir1 = "sdks/"+ver.lower().replace("x","")+"-osx"
  692. ddir2 = "/Applications/Autodesk/maya"+key+"/Maya.app/Contents"
  693. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  694. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  695. else:
  696. ddir1 = "sdks/"+ver.lower().replace("x","")+"-linux"+platform.architecture()[0].replace("bit","")
  697. if (platform.architecture()[0] == "64bit"):
  698. ddir2 = "/usr/autodesk/maya"+key+"-x64"
  699. ddir3 = "/usr/aw/maya"+key+"-x64"
  700. else:
  701. ddir2 = "/usr/autodesk/maya"+key
  702. ddir3 = "/usr/aw/maya"+key
  703. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  704. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  705. elif (os.path.isdir(ddir3)): SDK[ver] = ddir3
  706. def SdkLocateMax():
  707. if (sys.platform != "win32"): return
  708. for version,key1,key2,subdir in MAXVERSIONINFO:
  709. if (PkgSkip(version)==0):
  710. if (SDK.has_key(version)==0):
  711. ddir = "sdks/maxsdk"+version.lower()[3:]
  712. if (os.path.isdir(ddir)):
  713. SDK[version] = ddir
  714. SDK[version+"CS"] = ddir
  715. else:
  716. top = GetRegistryKey(key1,key2)
  717. if (top != 0):
  718. SDK[version] = top + "maxsdk"
  719. if (os.path.isdir(top + "\\" + subdir)!=0):
  720. SDK[version+"CS"] = top + subdir
  721. def SdkLocatePython():
  722. if (PkgSkip("PYTHON")==0):
  723. if (sys.platform == "win32"):
  724. SDK["PYTHON"]="thirdparty/win-python"
  725. SDK["PYTHONVERSION"]="python2.5"
  726. elif (sys.platform == "darwin"):
  727. if not SDK.has_key("MACOSX"): SdkLocateMacOSX()
  728. if (os.path.isdir("%s/System/Library/Frameworks/Python.framework" % SDK["MACOSX"])):
  729. os.system("readlink %s/System/Library/Frameworks/Python.framework/Versions/Current > %s/tmp/pythonversion 2>&1" % (SDK["MACOSX"], OUTPUTDIR))
  730. pv = ReadFile(OUTPUTDIR+"/tmp/pythonversion")
  731. SDK["PYTHON"] = SDK["MACOSX"]+"/System/Library/Frameworks/Python.framework/Headers"
  732. SDK["PYTHONVERSION"] = "python"+pv
  733. else:
  734. exit("Could not find the python framework!")
  735. else:
  736. os.system("python -V > "+OUTPUTDIR+"/tmp/pythonversion 2>&1")
  737. pv=ReadFile(OUTPUTDIR+"/tmp/pythonversion")
  738. if (pv.startswith("Python ")==0):
  739. exit("python -V did not produce the expected output")
  740. pv = pv[7:10]
  741. if (os.path.isdir("/usr/include/python"+pv)==0):
  742. exit("Python reports version "+pv+" but /usr/include/python"+pv+" is not installed.")
  743. SDK["PYTHON"]="/usr/include/python"+pv
  744. SDK["PYTHONVERSION"]="python"+pv
  745. def SdkLocateVisualStudio():
  746. if (sys.platform != "win32"): return
  747. vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "9.0")
  748. if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
  749. vcdir = vcdir[:-3]
  750. SDK["VISUALSTUDIO"] = vcdir
  751. def SdkLocateMSPlatform():
  752. platsdk=GetRegistryKey("SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", "Install Dir")
  753. if (platsdk == 0):
  754. platsdk=GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.1","InstallationFolder")
  755. if (platsdk == 0 and os.path.isdir("C:\\Program Files\\Microsoft Visual Studio 9\\VC\\PlatformSDK")):
  756. platsdk = "C:\\Program Files\\Microsoft Visual Studio 9\\VC\\PlatformSDK\\"
  757. if (platsdk != 0):
  758. SDK["MSPLATFORM"] = platsdk
  759. def SdkLocateMacOSX():
  760. if (sys.platform != "darwin"): return
  761. if (os.path.exists("/Developer/SDKs/MacOSX10.5.sdk")):
  762. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.5.sdk"
  763. elif (os.path.exists("/Developer/SDKs/MacOSX10.4u.sdk")):
  764. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4u.sdk"
  765. elif (os.path.exists("/Developer/SDKs/MacOSX10.4.0.sdk")):
  766. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4.0.sdk"
  767. else:
  768. exit("Could not find any MacOSX SDK")
  769. ########################################################################
  770. ##
  771. ## SDK Auto-Disables
  772. ##
  773. ## Disable packages whose SDKs could not be found.
  774. ##
  775. ########################################################################
  776. def SdkAutoDisableDirectX():
  777. for ver in ["DX8","DX9","DIRECTCAM"]:
  778. if (PkgSkip(ver)==0):
  779. if (SDK.has_key(ver)==0):
  780. if (sys.platform == "win32"):
  781. WARNINGS.append("I cannot locate SDK for "+ver)
  782. else:
  783. WARNINGS.append(ver+" only supported on windows yet")
  784. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  785. PkgDisable(ver)
  786. else:
  787. WARNINGS.append("Using "+ver+" sdk: "+SDK[ver])
  788. def SdkAutoDisableMaya():
  789. for (ver,key) in MAYAVERSIONINFO:
  790. if (SDK.has_key(ver)==0) and (PkgSkip(ver)==0):
  791. if (sys.platform == "win32"):
  792. WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.")
  793. else:
  794. WARNINGS.append("I cannot locate SDK for "+ver)
  795. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  796. PkgDisable(ver)
  797. def SdkAutoDisableMax():
  798. for version,key1,key2,subdir in MAXVERSIONINFO:
  799. if (PkgSkip(version)==0) and ((SDK.has_key(version)==0) or (SDK.has_key(version+"CS")==0)):
  800. if (sys.platform == "win32"):
  801. if (SDK.has_key(version)):
  802. WARNINGS.append("Your copy of "+version+" does not include the character studio SDK")
  803. else:
  804. WARNINGS.append("The registry does not appear to contain a pointer to "+version)
  805. else:
  806. WARNINGS.append(version+" only supported on windows yet")
  807. WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower())
  808. PkgDisable(version)
  809. ########################################################################
  810. ##
  811. ## Visual Studio comes with a script called VSVARS32.BAT, which
  812. ## you need to run before using visual studio command-line tools.
  813. ## The following python subroutine serves the same purpose.
  814. ##
  815. ########################################################################
  816. def AddToPathEnv(path,add):
  817. if (os.environ.has_key(path)):
  818. os.environ[path] = add + ";" + os.environ[path]
  819. else:
  820. os.environ[path] = add
  821. def SetupVisualStudioEnviron():
  822. if (SDK.has_key("VISUALSTUDIO")==0):
  823. exit("Could not find Visual Studio install directory")
  824. if (SDK.has_key("MSPLATFORM")==0):
  825. exit("Could not find the Microsoft Platform SDK")
  826. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "VC\\bin")
  827. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE")
  828. AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include")
  829. AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\lib")
  830. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
  831. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl")
  832. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib")
  833. ########################################################################
  834. #
  835. # Include and Lib directories.
  836. #
  837. # These allow you to add include and lib directories to the
  838. # compiler search paths. These methods accept a "package"
  839. # parameter, which specifies which package the directory is
  840. # associated with. The include/lib directory is not used
  841. # if the package is not selected. The package can be 'ALWAYS'.
  842. #
  843. ########################################################################
  844. INCDIRECTORIES = []
  845. LIBDIRECTORIES = []
  846. LIBNAMES = []
  847. DEFSYMBOLS = []
  848. def IncDirectory(opt, dir):
  849. INCDIRECTORIES.append((opt, dir))
  850. def LibDirectory(opt, dir):
  851. LIBDIRECTORIES.append((opt, dir))
  852. def LibName(opt, name):
  853. LIBNAMES.append((opt, name))
  854. def DefSymbol(opt, sym, val):
  855. DEFSYMBOLS.append((opt, sym, val))
  856. ########################################################################
  857. #
  858. # On Linux, to run panda, the dynamic linker needs to know how to find
  859. # the shared libraries. This subroutine verifies that the dynamic
  860. # linker is properly configured. If not, it sets it up on a temporary
  861. # basis and issues a warning.
  862. #
  863. ########################################################################
  864. def CheckLinkerLibraryPath():
  865. if (sys.platform == "win32"): return
  866. builtlib = os.path.abspath(os.path.join(OUTPUTDIR,"lib"))
  867. try:
  868. ldpath = []
  869. f = file("/etc/ld.so.conf","r")
  870. for line in f: ldpath.append(line.rstrip())
  871. f.close()
  872. except: ldpath = []
  873. if (os.environ.has_key("LD_LIBRARY_PATH")):
  874. ldpath = ldpath + os.environ["LD_LIBRARY_PATH"].split(":")
  875. if (ldpath.count(builtlib)==0):
  876. WARNINGS.append("Caution: the "+os.path.join(OUTPUTDIR,"lib")+" directory is not in LD_LIBRARY_PATH")
  877. WARNINGS.append("or /etc/ld.so.conf. You must add it before using panda.")
  878. if (os.environ.has_key("LD_LIBRARY_PATH")):
  879. os.environ["LD_LIBRARY_PATH"] = builtlib + ":" + os.environ["LD_LIBRARY_PATH"]
  880. else:
  881. os.environ["LD_LIBRARY_PATH"] = builtlib
  882. ########################################################################
  883. ##
  884. ## Routines to copy files into the build tree
  885. ##
  886. ########################################################################
  887. def CopyFile(dstfile,srcfile):
  888. if (dstfile[-1]=='/'):
  889. dstdir = dstfile
  890. fnl = srcfile.rfind("/")
  891. if (fnl < 0): fn = srcfile
  892. else: fn = srcfile[fnl+1:]
  893. dstfile = dstdir + fn
  894. if (NeedsBuild([dstfile],[srcfile])):
  895. WriteFile(dstfile,ReadFile(srcfile))
  896. JustBuilt([dstfile], [srcfile])
  897. def CopyAllFiles(dstdir, srcdir, suffix=""):
  898. for x in GetDirectoryContents(srcdir, ["*"+suffix]):
  899. CopyFile(dstdir+x, srcdir+x)
  900. def CopyAllHeaders(dir, skip=[]):
  901. for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
  902. srcfile = dir + "/" + filename
  903. dstfile = OUTPUTDIR+"/include/" + filename
  904. if (NeedsBuild([dstfile],[srcfile])):
  905. WriteFile(dstfile,ReadFile(srcfile))
  906. JustBuilt([dstfile],[srcfile])
  907. def CopyTree(dstdir,srcdir):
  908. if (os.path.isdir(dstdir)): return 0
  909. if (sys.platform == "win32"):
  910. cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"'
  911. else:
  912. cmd = 'cp -R -f ' + srcdir + ' ' + dstdir
  913. oscmd(cmd)
  914. ########################################################################
  915. ##
  916. ## Parse PandaVersion.pp to extract the version number.
  917. ##
  918. ########################################################################
  919. def ParsePandaVersion(fn):
  920. try:
  921. f = file(fn, "r")
  922. pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
  923. for line in f:
  924. match = pattern.match(line,0)
  925. if (match):
  926. version = match.group(1)+"."+match.group(2)+"."+match.group(3)
  927. break
  928. f.close()
  929. except: version="0.0.0"
  930. return version
  931. ########################################################################
  932. ##
  933. ## FindLocation
  934. ##
  935. ########################################################################
  936. ORIG_EXT={}
  937. def GetOrigExt(x):
  938. return ORIG_EXT[x]
  939. def CalcLocation(fn, ipath):
  940. if (fn.count("/")): return fn
  941. if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath)
  942. if (fn.endswith(".I")): return CxxFindSource(fn, ipath)
  943. if (fn.endswith(".h")): return CxxFindSource(fn, ipath)
  944. if (fn.endswith(".c")): return CxxFindSource(fn, ipath)
  945. if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath)
  946. if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
  947. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  948. if (sys.platform == "win32"):
  949. if (fn.endswith(".def")): return CxxFindSource(fn, ipath)
  950. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn
  951. if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn
  952. if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn
  953. if (fn.endswith(".dli")): return OUTPUTDIR+"/plugins/"+fn
  954. if (fn.endswith(".dle")): return OUTPUTDIR+"/plugins/"+fn
  955. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn
  956. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn
  957. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".lib"
  958. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  959. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  960. elif (sys.platform == "darwin"):
  961. if (fn.endswith(".mm")): return CxxFindSource(fn, ipath)
  962. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  963. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
  964. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  965. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  966. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  967. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  968. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  969. else:
  970. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  971. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
  972. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  973. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  974. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  975. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  976. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  977. return fn
  978. def FindLocation(fn, ipath):
  979. loc = CalcLocation(fn, ipath)
  980. (base,ext) = os.path.splitext(fn)
  981. ORIG_EXT[loc] = ext
  982. return loc
  983. ########################################################################
  984. ##
  985. ## TargetAdd
  986. ##
  987. ## Makepanda maintains a list of make-targets. Each target has
  988. ## these attributes:
  989. ##
  990. ## name - the name of the file being created.
  991. ## ext - the original file extension, prior to OS-specific translation
  992. ## inputs - the names of the input files to the compiler
  993. ## deps - other input files that the target also depends on
  994. ## opts - compiler options, a catch-all category
  995. ##
  996. ## TargetAdd will create the target if it does not exist. Then,
  997. ## depending on what options you pass, it will push data onto these
  998. ## various target attributes. This is cumulative: for example, if
  999. ## you use TargetAdd to add compiler options, then use TargetAdd
  1000. ## again with more compiler options, both sets of options will be
  1001. ## included.
  1002. ##
  1003. ## TargetAdd does some automatic dependency generation on C++ files.
  1004. ## It will scan these files for include-files and automatically push
  1005. ## the include files onto the list of dependencies. In order to do
  1006. ## this, it needs an include-file search path. So if you supply
  1007. ## any C++ input, you also need to supply compiler options containing
  1008. ## include-directories, or alternately, a separate ipath parameter.
  1009. ##
  1010. ## The main body of 'makepanda' is a long list of TargetAdd
  1011. ## directives building up a giant list of make targets. Then,
  1012. ## finally, the targets are run and panda is built.
  1013. ##
  1014. ## Makepanda's dependency system does not understand multiple
  1015. ## outputs from a single build step. When a build step generates
  1016. ## a primary output file and a secondary output file, it is
  1017. ## necessary to trick the dependency system. Insert a dummy
  1018. ## build step that "generates" the secondary output file, using
  1019. ## the primary output file as an input. There is a special
  1020. ## compiler option DEPENDENCYONLY that creates such a dummy
  1021. ## build-step. There are two cases where dummy build steps must
  1022. ## be inserted: bison generates an OBJ and a secondary header
  1023. ## file, interrogate generates an IN and a secondary IGATE.OBJ.
  1024. ##
  1025. ########################################################################
  1026. class Target:
  1027. pass
  1028. TARGET_LIST=[]
  1029. TARGET_TABLE={}
  1030. def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0):
  1031. if (dummy != 0):
  1032. exit("Syntax error in TargetAdd "+target)
  1033. if (ipath == 0): ipath = opts
  1034. if (ipath == 0): ipath = []
  1035. if (type(input) == str): input = [input]
  1036. if (type(dep) == str): dep = [dep]
  1037. full = FindLocation(target,[OUTPUTDIR+"/include"])
  1038. if (TARGET_TABLE.has_key(full) == 0):
  1039. t = Target()
  1040. t.name = full
  1041. t.inputs = []
  1042. t.deps = {}
  1043. t.opts = []
  1044. TARGET_TABLE[full] = t
  1045. TARGET_LIST.append(t)
  1046. else:
  1047. t = TARGET_TABLE[full]
  1048. ipath = [OUTPUTDIR+"/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"]
  1049. if (opts != 0):
  1050. for x in opts:
  1051. if (t.opts.count(x)==0):
  1052. t.opts.append(x)
  1053. if (input != 0):
  1054. for x in input:
  1055. fullinput = FindLocation(x, ipath)
  1056. t.inputs.append(fullinput)
  1057. t.deps[fullinput] = 1
  1058. (base,suffix) = os.path.splitext(x)
  1059. if (SUFFIX_INC.count(suffix)):
  1060. for d in CxxCalcDependencies(fullinput, ipath, []):
  1061. t.deps[d] = 1
  1062. if (dep != 0):
  1063. for x in dep:
  1064. fulldep = FindLocation(x, ipath)
  1065. t.deps[fulldep] = 1
  1066. if (target.endswith(".in")):
  1067. t.deps[FindLocation("interrogate.exe",[])] = 1
  1068. t.deps[FindLocation("dtool_have_python.dat",[])] = 1