makepandacore.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  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. def GetProgramFiles():
  418. if (os.environ.has_key("PROGRAMFILES")):
  419. return os.environ["PROGRAMFILES"]
  420. elif (os.path.isdir("C:\\Program Files")):
  421. return "C:\\Program Files"
  422. elif (os.path.isdir("D:\\Program Files")):
  423. return "D:\\Program Files"
  424. elif (os.path.isdir("E:\\Program Files")):
  425. return "E:\\Program Files"
  426. return 0
  427. ########################################################################
  428. ##
  429. ## Parsing Compiler Option Lists
  430. ##
  431. ########################################################################
  432. def GetListOption(opts, prefix):
  433. res=[]
  434. for x in opts:
  435. if (x.startswith(prefix)):
  436. res.append(x[len(prefix):])
  437. return res
  438. def GetValueOption(opts, prefix):
  439. for x in opts:
  440. if (x.startswith(prefix)):
  441. return x[len(prefix):]
  442. return 0
  443. def GetOptimizeOption(opts,defval):
  444. val = GetValueOption(opts, "OPT:")
  445. if (val == 0):
  446. return defval
  447. return val
  448. ########################################################################
  449. ##
  450. ## General File Manipulation
  451. ##
  452. ########################################################################
  453. def MakeDirectory(path):
  454. if os.path.isdir(path): return 0
  455. os.mkdir(path)
  456. def ReadFile(wfile):
  457. try:
  458. srchandle = open(wfile, "rb")
  459. data = srchandle.read()
  460. srchandle.close()
  461. return data
  462. except: exit("Cannot read "+wfile)
  463. def WriteFile(wfile,data):
  464. try:
  465. dsthandle = open(wfile, "wb")
  466. dsthandle.write(data)
  467. dsthandle.close()
  468. except: exit("Cannot write "+wfile)
  469. def ConditionalWriteFile(dest,desiredcontents):
  470. try:
  471. rfile = open(dest, 'rb')
  472. contents = rfile.read(-1)
  473. rfile.close()
  474. except:
  475. contents=0
  476. if contents != desiredcontents:
  477. sys.stdout.flush()
  478. WriteFile(dest,desiredcontents)
  479. def DeleteCVS(dir):
  480. for entry in os.listdir(dir):
  481. if (entry != ".") and (entry != ".."):
  482. subdir = dir + "/" + entry
  483. if (os.path.isdir(subdir)):
  484. if (entry == "CVS"):
  485. shutil.rmtree(subdir)
  486. else:
  487. DeleteCVS(subdir)
  488. def CreateFile(file):
  489. if (os.path.exists(file)==0):
  490. WriteFile(file,"")
  491. ########################################################################
  492. #
  493. # Create the panda build tree.
  494. #
  495. ########################################################################
  496. def MakeBuildTree():
  497. MakeDirectory(OUTPUTDIR)
  498. MakeDirectory(OUTPUTDIR+"/bin")
  499. MakeDirectory(OUTPUTDIR+"/lib")
  500. MakeDirectory(OUTPUTDIR+"/tmp")
  501. MakeDirectory(OUTPUTDIR+"/etc")
  502. MakeDirectory(OUTPUTDIR+"/plugins")
  503. MakeDirectory(OUTPUTDIR+"/modelcache")
  504. MakeDirectory(OUTPUTDIR+"/include")
  505. MakeDirectory(OUTPUTDIR+"/include/parser-inc")
  506. MakeDirectory(OUTPUTDIR+"/include/parser-inc/openssl")
  507. MakeDirectory(OUTPUTDIR+"/include/parser-inc/netinet")
  508. MakeDirectory(OUTPUTDIR+"/include/parser-inc/Cg")
  509. MakeDirectory(OUTPUTDIR+"/include/openssl")
  510. MakeDirectory(OUTPUTDIR+"/models")
  511. MakeDirectory(OUTPUTDIR+"/models/audio")
  512. MakeDirectory(OUTPUTDIR+"/models/audio/sfx")
  513. MakeDirectory(OUTPUTDIR+"/models/icons")
  514. MakeDirectory(OUTPUTDIR+"/models/maps")
  515. MakeDirectory(OUTPUTDIR+"/models/misc")
  516. MakeDirectory(OUTPUTDIR+"/models/gui")
  517. MakeDirectory(OUTPUTDIR+"/direct")
  518. MakeDirectory(OUTPUTDIR+"/pandac")
  519. MakeDirectory(OUTPUTDIR+"/pandac/input")
  520. ########################################################################
  521. #
  522. # Make sure that you are in the root of the panda tree.
  523. #
  524. ########################################################################
  525. def CheckPandaSourceTree():
  526. dir = os.getcwd()
  527. if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or
  528. (os.path.exists(os.path.join(dir, "dtool","src","dtoolbase","dtoolbase.h"))==0) or
  529. (os.path.exists(os.path.join(dir, "panda","src","pandabase","pandabase.h"))==0)):
  530. exit("Current directory is not the root of the panda tree.")
  531. ########################################################################
  532. ##
  533. ## Visual Studio Manifest Manipulation.
  534. ##
  535. ########################################################################
  536. VC90CRTVERSIONRE=re.compile("name=['\"]Microsoft.VC90.CRT['\"]\\s+version=['\"]([0-9.]+)['\"]")
  537. def GetVC90CRTVersion(fn):
  538. manifest = ReadFile(fn)
  539. version = VC90CRTVERSIONRE.search(manifest)
  540. if (version == None):
  541. exit("Cannot locate version number in "+fn)
  542. return version.group(1)
  543. def SetVC90CRTVersion(fn, ver):
  544. manifest = ReadFile(fn)
  545. subst = " name='Microsoft.VC90.CRT' version='"+ver+"' "
  546. manifest = VC90CRTVERSIONRE.sub(subst, manifest)
  547. WriteFile(fn, manifest)
  548. ########################################################################
  549. ##
  550. ## Gets or sets the output directory, by default "built".
  551. ##
  552. ########################################################################
  553. def GetOutputDir():
  554. return OUTPUTDIR
  555. def SetOutputDir(outputdir):
  556. global OUTPUTDIR
  557. OUTPUTDIR=outputdir
  558. ########################################################################
  559. ##
  560. ## Package Selection
  561. ##
  562. ## This facility enables makepanda to keep a list of packages selected
  563. ## by the user for inclusion or omission.
  564. ##
  565. ########################################################################
  566. PKG_LIST_ALL=0
  567. PKG_LIST_OMIT=0
  568. def PkgListSet(pkgs):
  569. global PKG_LIST_ALL
  570. global PKG_LIST_OMIT
  571. PKG_LIST_ALL=pkgs
  572. PKG_LIST_OMIT={}
  573. PkgDisableAll()
  574. def PkgListGet():
  575. return PKG_LIST_ALL
  576. def PkgEnableAll():
  577. for x in PKG_LIST_ALL:
  578. PKG_LIST_OMIT[x] = 0
  579. def PkgDisableAll():
  580. for x in PKG_LIST_ALL:
  581. PKG_LIST_OMIT[x] = 1
  582. def PkgEnable(pkg):
  583. PKG_LIST_OMIT[pkg] = 0
  584. def PkgDisable(pkg):
  585. PKG_LIST_OMIT[pkg] = 1
  586. def PkgSkip(pkg):
  587. return PKG_LIST_OMIT[pkg]
  588. def PkgSelected(pkglist, pkg):
  589. if (pkglist.count(pkg)==0): return 0
  590. if (PKG_LIST_OMIT[pkg]): return 0
  591. return 1
  592. ########################################################################
  593. ##
  594. ## These functions are for libraries which use pkg-config.
  595. ##
  596. ########################################################################
  597. def PkgConfigHavePkg(pkgname):
  598. """Returns a bool whether the pkg-config package is installed."""
  599. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  600. return False
  601. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
  602. result = handle.read().strip()
  603. handle.close()
  604. return bool(len(result) > 0)
  605. def PkgConfigGetLibs(pkgname):
  606. """Returns a list of libs for the package, prefixed by -l."""
  607. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  608. return []
  609. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
  610. result = handle.read().strip()
  611. handle.close()
  612. libs = []
  613. for l in result.split(" "):
  614. libs.append(l)
  615. return libs
  616. def PkgConfigGetIncDirs(pkgname):
  617. """Returns a list of includes for the package, NOT prefixed by -I."""
  618. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  619. return []
  620. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
  621. result = handle.read().strip()
  622. handle.close()
  623. if len(result) == 0: return []
  624. libs = []
  625. for l in result.split(" "):
  626. libs.append(l.replace("-I", "").replace("\"", "").strip())
  627. return libs
  628. def PkgConfigGetLibDirs(pkgname):
  629. """Returns a list of library paths for the package, NOT prefixed by -L."""
  630. if (sys.platform == "win32" or not LocateBinary("pkg-config")):
  631. return []
  632. handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
  633. result = handle.read().strip()
  634. handle.close()
  635. if len(result) == 0: return []
  636. libs = []
  637. for l in result.split(" "):
  638. libs.append(l.replace("-L", "").replace("\"", "").strip())
  639. return libs
  640. def PkgConfigEnable(opt, pkgname):
  641. """Adds the libraries and includes to IncDirectory, LibName and LibDirectory."""
  642. for i in PkgConfigGetIncDirs(pkgname):
  643. IncDirectory(opt, i)
  644. for i in PkgConfigGetLibDirs(pkgname):
  645. LibDirectory(opt, i)
  646. for i in PkgConfigGetLibs(pkgname):
  647. LibName(opt, i)
  648. ########################################################################
  649. ##
  650. ## SDK Location
  651. ##
  652. ## This section is concerned with locating the install directories
  653. ## for various third-party packages. The results are stored in the
  654. ## SDK table.
  655. ##
  656. ## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK.
  657. ## The only way to reliably find it is to search through the installer's
  658. ## uninstall-directories, look in each one, and see if it contains the
  659. ## relevant files.
  660. ##
  661. ########################################################################
  662. SDK = {}
  663. def SdkLocateDirectX():
  664. if (sys.platform != "win32"): return
  665. if (os.path.isdir("sdks/directx8")): SDK["DX8"]="sdks/directx8"
  666. if (os.path.isdir("sdks/directx9")): SDK["DX9"]="sdks/directx9"
  667. uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  668. for subdir in ListRegistryKeys(uninstaller):
  669. if (subdir[0]=="{"):
  670. dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation")
  671. if (dir != 0):
  672. if ((SDK.has_key("DX8")==0) and
  673. (os.path.isfile(dir+"\\Include\\d3d8.h")) and
  674. (os.path.isfile(dir+"\\Include\\d3dx8.h")) and
  675. (os.path.isfile(dir+"\\Lib\\d3d8.lib")) and
  676. (os.path.isfile(dir+"\\Lib\\d3dx8.lib"))):
  677. SDK["DX8"] = dir.replace("\\", "/").rstrip("/")
  678. if ((SDK.has_key("DX9")==0) and
  679. (os.path.isfile(dir+"\\Include\\d3d9.h")) and
  680. (os.path.isfile(dir+"\\Include\\d3dx9.h")) and
  681. (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and
  682. (os.path.isfile(dir+"\\Lib\\x86\\d3d9.lib")) and
  683. (os.path.isfile(dir+"\\Lib\\x86\\d3dx9.lib"))):
  684. SDK["DX9"] = dir.replace("\\", "/").rstrip("/")
  685. if (SDK.has_key("DX9")):
  686. SDK["DIRECTCAM"] = SDK["DX9"]
  687. def SdkLocateMaya():
  688. for (ver,key) in MAYAVERSIONINFO:
  689. if (PkgSkip(ver)==0 and SDK.has_key(ver)==0):
  690. if (sys.platform == "win32"):
  691. ddir = "sdks/"+ver.lower().replace("x","")
  692. if (os.path.isdir(ddir)):
  693. SDK[ver] = ddir
  694. else:
  695. for dev in ["Alias|Wavefront","Alias","Autodesk"]:
  696. fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath"
  697. res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION")
  698. if (res != 0):
  699. res = res.replace("\\", "/").rstrip("/")
  700. SDK[ver] = res
  701. elif (sys.platform == "darwin"):
  702. ddir1 = "sdks/"+ver.lower().replace("x","")+"-osx"
  703. ddir2 = "/Applications/Autodesk/maya"+key+"/Maya.app/Contents"
  704. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  705. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  706. else:
  707. ddir1 = "sdks/"+ver.lower().replace("x","")+"-linux"+platform.architecture()[0].replace("bit","")
  708. if (platform.architecture()[0] == "64bit"):
  709. ddir2 = "/usr/autodesk/maya"+key+"-x64"
  710. ddir3 = "/usr/aw/maya"+key+"-x64"
  711. else:
  712. ddir2 = "/usr/autodesk/maya"+key
  713. ddir3 = "/usr/aw/maya"+key
  714. if (os.path.isdir(ddir1)): SDK[ver] = ddir1
  715. elif (os.path.isdir(ddir2)): SDK[ver] = ddir2
  716. elif (os.path.isdir(ddir3)): SDK[ver] = ddir3
  717. def SdkLocateMax():
  718. if (sys.platform != "win32"): return
  719. for version,key1,key2,subdir in MAXVERSIONINFO:
  720. if (PkgSkip(version)==0):
  721. if (SDK.has_key(version)==0):
  722. ddir = "sdks/maxsdk"+version.lower()[3:]
  723. if (os.path.isdir(ddir)):
  724. SDK[version] = ddir
  725. SDK[version+"CS"] = ddir
  726. else:
  727. top = GetRegistryKey(key1,key2)
  728. if (top != 0):
  729. SDK[version] = top + "maxsdk"
  730. if (os.path.isdir(top + "\\" + subdir)!=0):
  731. SDK[version+"CS"] = top + subdir
  732. def SdkLocatePython():
  733. if (PkgSkip("PYTHON")==0):
  734. if (sys.platform == "win32"):
  735. SDK["PYTHON"]="thirdparty/win-python"
  736. SDK["PYTHONVERSION"]="python2.5"
  737. elif (sys.platform == "darwin"):
  738. if not SDK.has_key("MACOSX"): SdkLocateMacOSX()
  739. if (os.path.isdir("%s/System/Library/Frameworks/Python.framework" % SDK["MACOSX"])):
  740. os.system("readlink %s/System/Library/Frameworks/Python.framework/Versions/Current > %s/tmp/pythonversion 2>&1" % (SDK["MACOSX"], OUTPUTDIR))
  741. pv = ReadFile(OUTPUTDIR+"/tmp/pythonversion")
  742. SDK["PYTHON"] = SDK["MACOSX"]+"/System/Library/Frameworks/Python.framework/Headers"
  743. SDK["PYTHONVERSION"] = "python"+pv
  744. else:
  745. exit("Could not find the python framework!")
  746. else:
  747. os.system("python -V > "+OUTPUTDIR+"/tmp/pythonversion 2>&1")
  748. pv=ReadFile(OUTPUTDIR+"/tmp/pythonversion")
  749. if (pv.startswith("Python ")==0):
  750. exit("python -V did not produce the expected output")
  751. pv = pv[7:10]
  752. if (os.path.isdir("/usr/include/python"+pv)==0):
  753. exit("Python reports version "+pv+" but /usr/include/python"+pv+" is not installed.")
  754. SDK["PYTHON"]="/usr/include/python"+pv
  755. SDK["PYTHONVERSION"]="python"+pv
  756. def SdkLocateVisualStudio():
  757. if (sys.platform != "win32"): return
  758. vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "9.0")
  759. if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"):
  760. vcdir = vcdir[:-3]
  761. SDK["VISUALSTUDIO"] = vcdir
  762. def SdkLocateMSPlatform():
  763. platsdk=GetRegistryKey("SOFTWARE\\Microsoft\\MicrosoftSDK\\InstalledSDKs\\D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1", "Install Dir")
  764. if (platsdk == 0):
  765. platsdk=GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v6.1","InstallationFolder")
  766. if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2"))):
  767. platsdk = os.path.join(GetProgramFiles(), "Microsoft Platform SDK for Windows Server 2003 R2")
  768. # Doesn't work with the Express versions, so we're checking for the "atlmfc" dir, which is not in the Express
  769. if (platsdk == 0 and os.path.isdir(os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\atlmfc"))):
  770. platsdk = os.path.join(GetProgramFiles(), "Microsoft Visual Studio 9\\VC\\PlatformSDK")
  771. if (platsdk != 0):
  772. if (not platsdk.endswith("//")):
  773. platsdk += "//"
  774. SDK["MSPLATFORM"] = platsdk
  775. def SdkLocateMacOSX():
  776. if (sys.platform != "darwin"): return
  777. if (os.path.exists("/Developer/SDKs/MacOSX10.5.sdk")):
  778. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.5.sdk"
  779. elif (os.path.exists("/Developer/SDKs/MacOSX10.4u.sdk")):
  780. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4u.sdk"
  781. elif (os.path.exists("/Developer/SDKs/MacOSX10.4.0.sdk")):
  782. SDK["MACOSX"] = "/Developer/SDKs/MacOSX10.4.0.sdk"
  783. else:
  784. exit("Could not find any MacOSX SDK")
  785. ########################################################################
  786. ##
  787. ## SDK Auto-Disables
  788. ##
  789. ## Disable packages whose SDKs could not be found.
  790. ##
  791. ########################################################################
  792. def SdkAutoDisableDirectX():
  793. for ver in ["DX8","DX9","DIRECTCAM"]:
  794. if (PkgSkip(ver)==0):
  795. if (SDK.has_key(ver)==0):
  796. if (sys.platform == "win32"):
  797. WARNINGS.append("I cannot locate SDK for "+ver)
  798. else:
  799. WARNINGS.append(ver+" only supported on windows yet")
  800. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  801. PkgDisable(ver)
  802. else:
  803. WARNINGS.append("Using "+ver+" sdk: "+SDK[ver])
  804. def SdkAutoDisableMaya():
  805. for (ver,key) in MAYAVERSIONINFO:
  806. if (SDK.has_key(ver)==0) and (PkgSkip(ver)==0):
  807. if (sys.platform == "win32"):
  808. WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.")
  809. else:
  810. WARNINGS.append("I cannot locate SDK for "+ver)
  811. WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower())
  812. PkgDisable(ver)
  813. def SdkAutoDisableMax():
  814. for version,key1,key2,subdir in MAXVERSIONINFO:
  815. if (PkgSkip(version)==0) and ((SDK.has_key(version)==0) or (SDK.has_key(version+"CS")==0)):
  816. if (sys.platform == "win32"):
  817. if (SDK.has_key(version)):
  818. WARNINGS.append("Your copy of "+version+" does not include the character studio SDK")
  819. else:
  820. WARNINGS.append("The registry does not appear to contain a pointer to "+version)
  821. else:
  822. WARNINGS.append(version+" only supported on windows yet")
  823. WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower())
  824. PkgDisable(version)
  825. ########################################################################
  826. ##
  827. ## Visual Studio comes with a script called VSVARS32.BAT, which
  828. ## you need to run before using visual studio command-line tools.
  829. ## The following python subroutine serves the same purpose.
  830. ##
  831. ########################################################################
  832. def AddToPathEnv(path,add):
  833. if (os.environ.has_key(path)):
  834. os.environ[path] = add + ";" + os.environ[path]
  835. else:
  836. os.environ[path] = add
  837. def SetupVisualStudioEnviron():
  838. if (SDK.has_key("VISUALSTUDIO")==0):
  839. exit("Could not find Visual Studio install directory")
  840. if (SDK.has_key("MSPLATFORM")==0):
  841. exit("Could not find the Microsoft Platform SDK")
  842. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "VC\\bin")
  843. AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE")
  844. AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include")
  845. AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include")
  846. AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\lib")
  847. AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin")
  848. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include")
  849. AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl")
  850. AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib")
  851. ########################################################################
  852. #
  853. # Include and Lib directories.
  854. #
  855. # These allow you to add include and lib directories to the
  856. # compiler search paths. These methods accept a "package"
  857. # parameter, which specifies which package the directory is
  858. # associated with. The include/lib directory is not used
  859. # if the package is not selected. The package can be 'ALWAYS'.
  860. #
  861. ########################################################################
  862. INCDIRECTORIES = []
  863. LIBDIRECTORIES = []
  864. LIBNAMES = []
  865. DEFSYMBOLS = []
  866. def IncDirectory(opt, dir):
  867. INCDIRECTORIES.append((opt, dir))
  868. def LibDirectory(opt, dir):
  869. LIBDIRECTORIES.append((opt, dir))
  870. def LibName(opt, name):
  871. LIBNAMES.append((opt, name))
  872. def DefSymbol(opt, sym, val):
  873. DEFSYMBOLS.append((opt, sym, val))
  874. ########################################################################
  875. #
  876. # On Linux, to run panda, the dynamic linker needs to know how to find
  877. # the shared libraries. This subroutine verifies that the dynamic
  878. # linker is properly configured. If not, it sets it up on a temporary
  879. # basis and issues a warning.
  880. #
  881. ########################################################################
  882. def CheckLinkerLibraryPath():
  883. if (sys.platform == "win32"): return
  884. builtlib = os.path.abspath(os.path.join(OUTPUTDIR,"lib"))
  885. try:
  886. ldpath = []
  887. f = file("/etc/ld.so.conf","r")
  888. for line in f: ldpath.append(line.rstrip())
  889. f.close()
  890. except: ldpath = []
  891. if (os.environ.has_key("LD_LIBRARY_PATH")):
  892. ldpath = ldpath + os.environ["LD_LIBRARY_PATH"].split(":")
  893. if (ldpath.count(builtlib)==0):
  894. WARNINGS.append("Caution: the "+os.path.join(OUTPUTDIR,"lib")+" directory is not in LD_LIBRARY_PATH")
  895. WARNINGS.append("or /etc/ld.so.conf. You must add it before using panda.")
  896. if (os.environ.has_key("LD_LIBRARY_PATH")):
  897. os.environ["LD_LIBRARY_PATH"] = builtlib + ":" + os.environ["LD_LIBRARY_PATH"]
  898. else:
  899. os.environ["LD_LIBRARY_PATH"] = builtlib
  900. ########################################################################
  901. ##
  902. ## Routines to copy files into the build tree
  903. ##
  904. ########################################################################
  905. def CopyFile(dstfile,srcfile):
  906. if (dstfile[-1]=='/'):
  907. dstdir = dstfile
  908. fnl = srcfile.rfind("/")
  909. if (fnl < 0): fn = srcfile
  910. else: fn = srcfile[fnl+1:]
  911. dstfile = dstdir + fn
  912. if (NeedsBuild([dstfile],[srcfile])):
  913. WriteFile(dstfile,ReadFile(srcfile))
  914. JustBuilt([dstfile], [srcfile])
  915. def CopyAllFiles(dstdir, srcdir, suffix=""):
  916. for x in GetDirectoryContents(srcdir, ["*"+suffix]):
  917. CopyFile(dstdir+x, srcdir+x)
  918. def CopyAllHeaders(dir, skip=[]):
  919. for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip):
  920. srcfile = dir + "/" + filename
  921. dstfile = OUTPUTDIR+"/include/" + filename
  922. if (NeedsBuild([dstfile],[srcfile])):
  923. WriteFile(dstfile,ReadFile(srcfile))
  924. JustBuilt([dstfile],[srcfile])
  925. def CopyTree(dstdir,srcdir):
  926. if (os.path.isdir(dstdir)): return 0
  927. if (sys.platform == "win32"):
  928. cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"'
  929. else:
  930. cmd = 'cp -R -f ' + srcdir + ' ' + dstdir
  931. oscmd(cmd)
  932. ########################################################################
  933. ##
  934. ## Parse PandaVersion.pp to extract the version number.
  935. ##
  936. ########################################################################
  937. def ParsePandaVersion(fn):
  938. try:
  939. f = file(fn, "r")
  940. pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)')
  941. for line in f:
  942. match = pattern.match(line,0)
  943. if (match):
  944. version = match.group(1)+"."+match.group(2)+"."+match.group(3)
  945. break
  946. f.close()
  947. except: version="0.0.0"
  948. return version
  949. ########################################################################
  950. ##
  951. ## FindLocation
  952. ##
  953. ########################################################################
  954. ORIG_EXT={}
  955. def GetOrigExt(x):
  956. return ORIG_EXT[x]
  957. def CalcLocation(fn, ipath):
  958. if (fn.count("/")): return fn
  959. if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath)
  960. if (fn.endswith(".I")): return CxxFindSource(fn, ipath)
  961. if (fn.endswith(".h")): return CxxFindSource(fn, ipath)
  962. if (fn.endswith(".c")): return CxxFindSource(fn, ipath)
  963. if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath)
  964. if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath)
  965. if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn
  966. if (sys.platform == "win32"):
  967. if (fn.endswith(".def")): return CxxFindSource(fn, ipath)
  968. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn
  969. if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn
  970. if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn
  971. if (fn.endswith(".dli")): return OUTPUTDIR+"/plugins/"+fn
  972. if (fn.endswith(".dle")): return OUTPUTDIR+"/plugins/"+fn
  973. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn
  974. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn
  975. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".lib"
  976. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  977. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  978. elif (sys.platform == "darwin"):
  979. if (fn.endswith(".mm")): return CxxFindSource(fn, ipath)
  980. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  981. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib"
  982. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  983. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  984. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  985. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  986. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  987. else:
  988. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o"
  989. if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so"
  990. if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4]
  991. if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a"
  992. if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a"
  993. if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn
  994. if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn
  995. return fn
  996. def FindLocation(fn, ipath):
  997. loc = CalcLocation(fn, ipath)
  998. (base,ext) = os.path.splitext(fn)
  999. ORIG_EXT[loc] = ext
  1000. return loc
  1001. ########################################################################
  1002. ##
  1003. ## TargetAdd
  1004. ##
  1005. ## Makepanda maintains a list of make-targets. Each target has
  1006. ## these attributes:
  1007. ##
  1008. ## name - the name of the file being created.
  1009. ## ext - the original file extension, prior to OS-specific translation
  1010. ## inputs - the names of the input files to the compiler
  1011. ## deps - other input files that the target also depends on
  1012. ## opts - compiler options, a catch-all category
  1013. ##
  1014. ## TargetAdd will create the target if it does not exist. Then,
  1015. ## depending on what options you pass, it will push data onto these
  1016. ## various target attributes. This is cumulative: for example, if
  1017. ## you use TargetAdd to add compiler options, then use TargetAdd
  1018. ## again with more compiler options, both sets of options will be
  1019. ## included.
  1020. ##
  1021. ## TargetAdd does some automatic dependency generation on C++ files.
  1022. ## It will scan these files for include-files and automatically push
  1023. ## the include files onto the list of dependencies. In order to do
  1024. ## this, it needs an include-file search path. So if you supply
  1025. ## any C++ input, you also need to supply compiler options containing
  1026. ## include-directories, or alternately, a separate ipath parameter.
  1027. ##
  1028. ## The main body of 'makepanda' is a long list of TargetAdd
  1029. ## directives building up a giant list of make targets. Then,
  1030. ## finally, the targets are run and panda is built.
  1031. ##
  1032. ## Makepanda's dependency system does not understand multiple
  1033. ## outputs from a single build step. When a build step generates
  1034. ## a primary output file and a secondary output file, it is
  1035. ## necessary to trick the dependency system. Insert a dummy
  1036. ## build step that "generates" the secondary output file, using
  1037. ## the primary output file as an input. There is a special
  1038. ## compiler option DEPENDENCYONLY that creates such a dummy
  1039. ## build-step. There are two cases where dummy build steps must
  1040. ## be inserted: bison generates an OBJ and a secondary header
  1041. ## file, interrogate generates an IN and a secondary IGATE.OBJ.
  1042. ##
  1043. ########################################################################
  1044. class Target:
  1045. pass
  1046. TARGET_LIST=[]
  1047. TARGET_TABLE={}
  1048. def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0):
  1049. if (dummy != 0):
  1050. exit("Syntax error in TargetAdd "+target)
  1051. if (ipath == 0): ipath = opts
  1052. if (ipath == 0): ipath = []
  1053. if (type(input) == str): input = [input]
  1054. if (type(dep) == str): dep = [dep]
  1055. full = FindLocation(target,[OUTPUTDIR+"/include"])
  1056. if (TARGET_TABLE.has_key(full) == 0):
  1057. t = Target()
  1058. t.name = full
  1059. t.inputs = []
  1060. t.deps = {}
  1061. t.opts = []
  1062. TARGET_TABLE[full] = t
  1063. TARGET_LIST.append(t)
  1064. else:
  1065. t = TARGET_TABLE[full]
  1066. ipath = [OUTPUTDIR+"/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"]
  1067. if (opts != 0):
  1068. for x in opts:
  1069. if (t.opts.count(x)==0):
  1070. t.opts.append(x)
  1071. if (input != 0):
  1072. for x in input:
  1073. fullinput = FindLocation(x, ipath)
  1074. t.inputs.append(fullinput)
  1075. t.deps[fullinput] = 1
  1076. (base,suffix) = os.path.splitext(x)
  1077. if (SUFFIX_INC.count(suffix)):
  1078. for d in CxxCalcDependencies(fullinput, ipath, []):
  1079. t.deps[d] = 1
  1080. if (dep != 0):
  1081. for x in dep:
  1082. fulldep = FindLocation(x, ipath)
  1083. t.deps[fulldep] = 1
  1084. if (target.endswith(".in")):
  1085. t.deps[FindLocation("interrogate.exe",[])] = 1
  1086. t.deps[FindLocation("dtool_have_python.dat",[])] = 1