makepandacore.py 70 KB

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