makepandacore.py 67 KB

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