makepandacore.py 46 KB

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