######################################################################## ## ## Caution: there are two separate, independent build systems: ## 'makepanda', and 'ppremake'. Use one or the other, do not attempt ## to use both. This file is part of the 'makepanda' system. ## ## This file, makepandacore, contains all the global state and ## global functions for the makepanda system. ## ######################################################################## import sys,os,time,stat,string,re,getopt,fnmatch,threading,signal,shutil,platform,glob,getpass,signal import subprocess from distutils import sysconfig if sys.version_info >= (3, 0): import pickle import _thread as thread else: import cPickle as pickle import thread SUFFIX_INC = [".cxx",".c",".h",".I",".yxx",".lxx",".mm",".rc",".r"] SUFFIX_DLL = [".dll",".dlo",".dle",".dli",".dlm",".mll",".exe",".pyd",".ocx"] SUFFIX_LIB = [".lib",".ilb"] VCS_DIRS = set(["CVS", "CVSROOT", ".git", ".hg"]) VCS_FILES = set([".cvsignore", ".gitignore", ".gitmodules", ".hgignore"]) STARTTIME = time.time() MAINTHREAD = threading.currentThread() OUTPUTDIR = "built" CUSTOM_OUTPUTDIR = False THIRDPARTYBASE = None THIRDPARTYDIR = None OPTIMIZE = "3" VERBOSE = False LINK_ALL_STATIC = False TARGET = None TARGET_ARCH = None HAS_TARGET_ARCH = False TOOLCHAIN_PREFIX = "" ANDROID_ABI = None SYS_LIB_DIRS = [] SYS_INC_DIRS = [] DEBUG_DEPENDENCIES = False # Is the current Python a 32-bit or 64-bit build? There doesn't # appear to be a universal test for this. if sys.platform == 'darwin': # On OSX, platform.architecture reports '64bit' even if it is # currently running in 32-bit mode. But sys.maxint is a reliable # indicator. if sys.version_info >= (3, 0): host_64 = (sys.maxsize > 0x100000000) else: host_64 = (sys.maxint > 0x100000000) else: # On Windows (and Linux?) sys.maxint reports 0x7fffffff even on a # 64-bit build. So we stick with platform.architecture in that # case. host_64 = (platform.architecture()[0] == '64bit') ######################################################################## ## ## Maya and Max Version List (with registry keys) ## ######################################################################## MAYAVERSIONINFO = [("MAYA6", "6.0"), ("MAYA65", "6.5"), ("MAYA7", "7.0"), ("MAYA8", "8.0"), ("MAYA85", "8.5"), ("MAYA2008","2008"), ("MAYA2009","2009"), ("MAYA2010","2010"), ("MAYA2011","2011"), ("MAYA2012","2012"), ("MAYA2013","2013"), ("MAYA20135","2013.5"), ("MAYA2014","2014"), ("MAYA2015","2015"), ("MAYA2016","2016"), ] MAXVERSIONINFO = [("MAX6", "SOFTWARE\\Autodesk\\3DSMAX\\6.0", "installdir", "maxsdk\\cssdk\\include"), ("MAX7", "SOFTWARE\\Autodesk\\3DSMAX\\7.0", "Installdir", "maxsdk\\include\\CS"), ("MAX8", "SOFTWARE\\Autodesk\\3DSMAX\\8.0", "Installdir", "maxsdk\\include\\CS"), ("MAX9", "SOFTWARE\\Autodesk\\3DSMAX\\9.0", "Installdir", "maxsdk\\include\\CS"), ("MAX2009", "SOFTWARE\\Autodesk\\3DSMAX\\11.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ("MAX2010", "SOFTWARE\\Autodesk\\3DSMAX\\12.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ("MAX2011", "SOFTWARE\\Autodesk\\3DSMAX\\13.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ("MAX2012", "SOFTWARE\\Autodesk\\3DSMAX\\14.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ("MAX2013", "SOFTWARE\\Autodesk\\3DSMAX\\15.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ("MAX2014", "SOFTWARE\\Autodesk\\3DSMAX\\16.0\\MAX-1:409", "Installdir", "maxsdk\\include\\CS"), ] MAYAVERSIONS = [] MAXVERSIONS = [] DXVERSIONS = ["DX9"] for (ver,key) in MAYAVERSIONINFO: MAYAVERSIONS.append(ver) for (ver,key1,key2,subdir) in MAXVERSIONINFO: MAXVERSIONS.append(ver) ######################################################################## ## ## Potentially Conflicting Files ## ## The next function can warn about the existence of files that are ## commonly generated by ppremake that may conflict with the build. ## ######################################################################## CONFLICTING_FILES=["dtool/src/dtoolutil/pandaVersion.h", "dtool/src/dtoolutil/checkPandaVersion.h", "dtool/src/dtoolutil/checkPandaVersion.cxx", "dtool/src/prc/prc_parameters.h", "panda/src/speedtree/speedtree_parameters.h", "direct/src/plugin/p3d_plugin_config.h", "direct/src/plugin_activex/P3DActiveX.rc", "direct/src/plugin_npapi/nppanda3d.rc", "direct/src/plugin_standalone/panda3d.rc"] def WarnConflictingFiles(delete = False): for cfile in CONFLICTING_FILES: if os.path.exists(cfile): print("%sWARNING:%s file may conflict with build: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), cfile, GetColor())) if delete: os.unlink(cfile) print("Deleted.") ######################################################################## ## ## The exit routine will normally ## ## - print a message ## - save the dependency cache ## - exit ## ## However, if it is invoked inside a thread, it instead: ## ## - prints a message ## - raises the "initiate-exit" exception ## ## If you create a thread, you must be prepared to catch this ## exception, save the dependency cache, and exit. ## ######################################################################## WARNINGS = [] THREADS = {} HAVE_COLORS = False SETF = "" try: import curses curses.setupterm() SETF = curses.tigetstr("setf") if (SETF == None): SETF = curses.tigetstr("setaf") assert SETF != None HAVE_COLORS = sys.stdout.isatty() except: pass def DisableColors(): global HAVE_COLORS HAVE_COLORS = False def GetColor(color = None): if not HAVE_COLORS: return "" if color != None: color = color.lower() if (color == "blue"): token = curses.tparm(SETF, 1) elif (color == "green"): token = curses.tparm(SETF, 2) elif (color == "cyan"): token = curses.tparm(SETF, 3) elif (color == "red"): token = curses.tparm(SETF, 4) elif (color == "magenta"): token = curses.tparm(SETF, 5) elif (color == "yellow"): token = curses.tparm(SETF, 6) else: token = curses.tparm(curses.tigetstr("sgr0")) if sys.version_info >= (3, 0): return token.decode('ascii') else: return token def ColorText(color, text, reset=True): if reset is True: return ''.join((GetColor(color), text, GetColor())) else: return ''.join((GetColor(color), text)) def PrettyTime(t): t = int(t) hours = t // 3600 t -= hours * 3600 minutes = t // 60 t -= minutes * 60 seconds = t if hours: return "%d hours %d min" % (hours, minutes) if minutes: return "%d min %d sec" % (minutes, seconds) return "%d sec" % (seconds) def ProgressOutput(progress, msg, target = None): sys.stdout.flush() sys.stderr.flush() prefix = "" thisthread = threading.currentThread() if thisthread is MAINTHREAD: if progress is None: prefix = "" elif (progress >= 100.0): prefix = "%s[%s%d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow")) elif (progress < 10.0): prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow")) else: prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow")) else: global THREADS ident = thread.get_ident() if (ident not in THREADS): THREADS[ident] = len(THREADS) + 1 prefix = "%s[%sT%d%s] " % (GetColor("yellow"), GetColor("cyan"), THREADS[ident], GetColor("yellow")) if target is not None: suffix = ' ' + ColorText("green", target) else: suffix = GetColor() print(''.join((prefix, msg, suffix))) sys.stdout.flush() sys.stderr.flush() def exit(msg = ""): sys.stdout.flush() sys.stderr.flush() if (threading.currentThread() == MAINTHREAD): SaveDependencyCache() print("Elapsed Time: " + PrettyTime(time.time() - STARTTIME)) print(msg) print(ColorText("red", "Build terminated.")) sys.stdout.flush() sys.stderr.flush() ##Don't quit the interperter if I'm running this file directly (debugging) if __name__ != '__main__': os._exit(1) else: print(msg) raise "initiate-exit" ######################################################################## ## ## SetTarget, GetTarget, GetHost ## ## These functions control cross-compilation. ## ######################################################################## def GetHost(): """Returns the host platform, ie. the one we're compiling on.""" if sys.platform == 'win32' or sys.platform == 'cygwin': # sys.platform is win32 on 64-bits Windows as well. return 'windows' elif sys.platform == 'darwin': return 'darwin' elif sys.platform.startswith('linux'): return 'linux' elif sys.platform.startswith('freebsd'): return 'freebsd' else: exit('Unrecognized sys.platform: %s' % (sys.platform)) def GetHostArch(): """Returns the architecture we're compiling on. Its value is also platform-dependent, as different platforms have different architecture naming.""" target = GetTarget() if target == 'windows': return 'x64' if host_64 else 'x86' else: #TODO return platform.machine() def SetTarget(target, arch=None): """Sets the target platform; the one we're compiling for. Also sets the target architecture (None for default, if any). Should be called *before* any calls are made to GetOutputDir, GetCC, etc.""" global TARGET, TARGET_ARCH, HAS_TARGET_ARCH global TOOLCHAIN_PREFIX host = GetHost() host_arch = GetHostArch() if target is None: target = host else: target = target.lower() if arch is not None: HAS_TARGET_ARCH = True TOOLCHAIN_PREFIX = '' if target == 'windows': if arch == 'i386': arch = 'x86' elif arch == 'amd64': arch = 'x64' if arch is not None and arch != 'x86' and arch != 'x64': exit("Windows architecture must be x86 or x64") elif target == 'darwin': if arch == 'amd64': arch = 'x86_64' if arch is not None: choices = ('i386', 'x86_64', 'ppc', 'ppc64') if arch not in choices: exit('Mac OS X architecture must be one of %s' % (', '.join(choices))) elif target == 'android': if arch is None: arch = 'arm' # Determine the prefix for our gcc tools, eg. arm-linux-androideabi-gcc global ANDROID_ABI if arch == 'armv7a': ANDROID_ABI = 'armeabi-v7a' TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' elif arch == 'arm': ANDROID_ABI = 'armeabi' TOOLCHAIN_PREFIX = 'arm-linux-androideabi-' elif arch == 'x86': ANDROID_ABI = 'x86' TOOLCHAIN_PREFIX = 'i686-linux-android-' elif arch == 'mips': ANDROID_ABI = 'mips' TOOLCHAIN_PREFIX = 'mipsel-linux-android-' else: exit('Android architecture must be arm, armv7a, x86 or mips') elif target == 'linux': if arch is not None: TOOLCHAIN_PREFIX = '%s-linux-gnu-' % arch elif host != 'linux': exit('Should specify an architecture when building for Linux') elif target == host: if arch is None or arch == host_arch: # Not a cross build. pass else: exit('Cannot cross-compile for %s-%s from %s-%s' % (target, arch, host, host_arch)) else: exit('Cannot cross-compile for %s from %s' % (target, host)) if arch is None: arch = host_arch TARGET = target TARGET_ARCH = arch def GetTarget(): """Returns the platform we're compiling for. Defaults to GetHost().""" global TARGET if TARGET is None: TARGET = GetHost() return TARGET def HasTargetArch(): """Returns True if the user specified an architecture to compile for.""" return HAS_TARGET_ARCH def GetTargetArch(): """Returns the architecture we're compiling for. Defaults to GetHostArch(). Its value is also dependent on that of GetTarget(), as different platforms use a different architecture naming.""" global TARGET_ARCH if TARGET_ARCH is None: TARGET_ARCH = GetHostArch() return TARGET_ARCH def CrossCompiling(): """Returns True if we're cross-compiling.""" return GetTarget() != GetHost() def GetCC(): return os.environ.get('CC', TOOLCHAIN_PREFIX + 'gcc') def GetCXX(): return os.environ.get('CXX', TOOLCHAIN_PREFIX + 'g++') def GetStrip(): # Hack if TARGET == 'android': return TOOLCHAIN_PREFIX + 'strip' else: return 'strip' def GetAR(): # Hack if TARGET == 'android': return TOOLCHAIN_PREFIX + 'ar' else: return 'ar' def GetRanlib(): # Hack if TARGET == 'android': return TOOLCHAIN_PREFIX + 'ranlib' else: return 'ranlib' BISON = None def GetBison(): global BISON if BISON is not None: return BISON win_util = os.path.join(GetThirdpartyBase(), 'win-util') if GetHost() == 'windows' and os.path.isdir(win_util): BISON = os.path.join(win_util, 'bison.exe') elif LocateBinary('bison'): BISON = 'bison' else: # We don't strictly need it, so don't give an error return None return BISON FLEX = None def GetFlex(): global FLEX if FLEX is not None: return FLEX win_util = os.path.join(GetThirdpartyBase(), 'win-util') if GetHost() == 'windows' and os.path.isdir(win_util): FLEX = GetThirdpartyBase() + "/win-util/flex.exe" elif LocateBinary('flex'): FLEX = 'flex' else: # We don't strictly need it, so don't give an error return None return FLEX ######################################################################## ## ## LocateBinary ## ## This function searches the system PATH for the binary. Returns its ## full path when it is found, or None when it was not found. ## ######################################################################## def LocateBinary(binary): if os.path.isfile(binary): return binary if "PATH" not in os.environ or os.environ["PATH"] == "": p = os.defpath else: p = os.environ["PATH"] pathList = p.split(os.pathsep) # Nirai hack if GetHost() == 'windows' and not binary.endswith('.exe'): binary += '.exe' basename = os.path.basename(binary) if os.path.isfile('prebuilt/' + basename): return 'prebuilt/' + basename if GetHost() == 'windows': if not binary.endswith('.exe'): # Append .exe if necessary binary += '.exe' # On Windows the current directory is always implicitly # searched before anything else on PATH. pathList = ['.'] + pathList for path in pathList: binpath = os.path.join(os.path.expanduser(path), binary) if os.access(binpath, os.X_OK): return os.path.abspath(os.path.realpath(binpath)) return None ######################################################################## ## ## Run a command. ## ######################################################################## def oscmd(cmd, ignoreError = False): if VERBOSE: print(GetColor("blue") + cmd.split(" ", 1)[0] + " " + GetColor("magenta") + cmd.split(" ", 1)[1] + GetColor()) sys.stdout.flush() if sys.platform in ("win32", "cygwin"): exe = cmd.split()[0] exe_path = LocateBinary(exe) if exe_path is None: exit("Cannot find "+exe+" on search path") res = os.spawnl(os.P_WAIT, exe_path, cmd) else: res = os.system(cmd) sig = res & 0x7F if (GetVerbose() and res != 0): print(ColorText("red", "Process exited with exit status %d and signal code %d" % ((res & 0xFF00) >> 8, sig))) if (sig == signal.SIGINT): exit("keyboard interrupt") # Don't ask me where the 35584 or 34304 come from... if (sig == signal.SIGSEGV or res == 35584 or res == 34304): if (LocateBinary("gdb") and GetVerbose() and GetHost() != "windows"): print(ColorText("red", "Received SIGSEGV, retrieving traceback...")) 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) else: print(ColorText("red", "Received SIGSEGV")) exit("") if res != 0 and not ignoreError: if "interrogate" in cmd.split(" ", 1)[0] and GetVerbose(): print(ColorText("red", "Interrogate failed, retrieving debug output...")) if sys.platform == "win32": os.spawnl(os.P_WAIT, exe, cmd.split(" ", 1)[0] + " -vv " + cmd.split(" ", 1)[1]) else: os.system(cmd.split(" ", 1)[0] + " -vv " + cmd.split(" ", 1)[1]) exit("The following command returned a non-zero value: " + str(cmd)) return res ######################################################################## ## ## GetDirectoryContents ## ######################################################################## def GetDirectoryContents(dir, filters="*", skip=[]): if (type(filters)==str): filters = [filters] actual = {} files = os.listdir(dir) for filter in filters: for file in fnmatch.filter(files, filter): if (skip.count(file)==0) and (os.path.isfile(dir + "/" + file)): actual[file] = 1 results = list(actual.keys()) results.sort() return results def GetDirectorySize(dir): if not os.path.isdir(dir): return 0 size = 0 for (path, dirs, files) in os.walk(dir): for file in files: try: size += os.path.getsize(os.path.join(path, file)) except: pass return size ######################################################################## ## ## The Timestamp Cache ## ## The make utility is constantly fetching the timestamps of files. ## This can represent the bulk of the file accesses during the make ## process. The timestamp cache eliminates redundant checks. ## ######################################################################## TIMESTAMPCACHE = {} def GetTimestamp(path): if path in TIMESTAMPCACHE: return TIMESTAMPCACHE[path] try: date = os.path.getmtime(path) except: date = 0 TIMESTAMPCACHE[path] = date return date def ClearTimestamp(path): del TIMESTAMPCACHE[path] ######################################################################## ## ## The Dependency cache. ## ## Makepanda's strategy for file dependencies is different from most ## make-utilities. Whenever a file is built, makepanda records ## that the file was built, and it records what the input files were, ## and what their dates were. Whenever a file is about to be built, ## panda compares the current list of input files and their dates, ## to the previous list of input files and their dates. If they match, ## there is no need to build the file. ## ######################################################################## BUILTFROMCACHE = {} def JustBuilt(files, others): dates = {} for file in files: del TIMESTAMPCACHE[file] dates[file] = GetTimestamp(file) for file in others: dates[file] = GetTimestamp(file) key = tuple(files) BUILTFROMCACHE[key] = dates def NeedsBuild(files, others): dates = {} for file in files: dates[file] = GetTimestamp(file) if not os.path.exists(file): if DEBUG_DEPENDENCIES: print("rebuilding %s because it does not exist" % (file)) return True for file in others: dates[file] = GetTimestamp(file) key = tuple(files) if key in BUILTFROMCACHE: cached = BUILTFROMCACHE[key] if cached == dates: return False elif DEBUG_DEPENDENCIES: print("rebuilding %s because:" % (key)) for key in frozenset(cached.keys()) | frozenset(dates.keys()): if key not in cached: print(" new dependency: %s" % (key)) elif key not in dates: print(" removed dependency: %s" % (key)) elif cached[key] != dates[key]: print(" dependency changed: %s" % (key)) if VERBOSE and frozenset(cached) != frozenset(dates): print("%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), files, GetColor())) return True ######################################################################## ## ## The CXX include cache: ## ## The following routine scans a CXX file and returns a list of ## the include-directives inside that file. It's not recursive: ## it just returns the includes that are textually inside the ## file. If you need recursive dependencies, you need the higher-level ## routine CxxCalcDependencies, defined elsewhere. ## ## Since scanning a CXX file is slow, we cache the result. It records ## the date of the source file and the list of includes that it ## contains. It assumes that if the file date hasn't changed, that ## the list of include-statements inside the file has not changed ## either. Once again, this particular routine does not return ## recursive dependencies --- it only returns an explicit list of ## include statements that are textually inside the file. That ## is what the cache stores, as well. ## ######################################################################## CXXINCLUDECACHE = {} global CxxIncludeRegex CxxIncludeRegex = re.compile('^[ \t]*[#][ \t]*include[ \t]+"([^"]+)"[ \t\r\n]*$') def CxxGetIncludes(path): date = GetTimestamp(path) if (path in CXXINCLUDECACHE): cached = CXXINCLUDECACHE[path] if (cached[0]==date): return cached[1] try: sfile = open(path, 'r') except: exit("Cannot open source file \""+path+"\" for reading.") include = [] try: for line in sfile: match = CxxIncludeRegex.match(line,0) if (match): incname = match.group(1) include.append(incname) except: print("Failed to determine dependencies of \""+path+"\".") raise sfile.close() CXXINCLUDECACHE[path] = [date, include] return include ######################################################################## ## ## SaveDependencyCache / LoadDependencyCache ## ## This actually saves both the dependency and cxx-include caches. ## ######################################################################## DCACHE_VERSION = 2 DCACHE_BACKED_UP = False def SaveDependencyCache(): global DCACHE_BACKED_UP if not DCACHE_BACKED_UP: try: if (os.path.exists(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"))): os.rename(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"), os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache-backup")) except: pass DCACHE_BACKED_UP = True try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"),'wb') except: icache = None if icache is not None: print("Storing dependency cache.") pickle.dump(DCACHE_VERSION, icache, 0) pickle.dump(CXXINCLUDECACHE, icache, 2) pickle.dump(BUILTFROMCACHE, icache, 2) icache.close() def LoadDependencyCache(): global CXXINCLUDECACHE global BUILTFROMCACHE try: icache = open(os.path.join(OUTPUTDIR, "tmp", "makepanda-dcache"), 'rb') except: icache = None if icache is not None: ver = pickle.load(icache) if ver == DCACHE_VERSION: CXXINCLUDECACHE = pickle.load(icache) BUILTFROMCACHE = pickle.load(icache) icache.close() else: print("Cannot load dependency cache, version is too old!") ######################################################################## ## ## CxxFindSource: given a source file name and a directory list, ## searches the directory list for the given source file. Returns ## the full pathname of the located file. ## ## CxxFindHeader: given a source file, an include directive, and a ## directory list, searches the directory list for the given header ## file. Returns the full pathname of the located file. ## ## Of course, CxxFindSource and CxxFindHeader cannot find a source ## file that has not been created yet. This can cause dependency ## problems. So the function CreateStubHeader can be used to create ## a file that CxxFindSource or CxxFindHeader can subsequently find. ## ######################################################################## def CxxFindSource(name, ipath): for dir in ipath: if (dir == "."): full = name else: full = dir + "/" + name if GetTimestamp(full) > 0: return full exit("Could not find source file: "+name) def CxxFindHeader(srcfile, incfile, ipath): if (incfile.startswith(".")): last = srcfile.rfind("/") if (last < 0): exit("CxxFindHeader cannot handle this case #1") srcdir = srcfile[:last+1] while (incfile[:1]=="."): if (incfile[:2]=="./"): incfile = incfile[2:] elif (incfile[:3]=="../"): incfile = incfile[3:] last = srcdir[:-1].rfind("/") if (last < 0): exit("CxxFindHeader cannot handle this case #2") srcdir = srcdir[:last+1] else: exit("CxxFindHeader cannot handle this case #3") full = srcdir + incfile if GetTimestamp(full) > 0: return full return 0 else: for dir in ipath: full = dir + "/" + incfile if GetTimestamp(full) > 0: return full return 0 ######################################################################## ## ## CxxCalcDependencies(srcfile, ipath, ignore) ## ## Calculate the dependencies of a source file given a ## particular include-path. Any file in the list of files to ## ignore is not considered. ## ######################################################################## global CxxIgnoreHeader global CxxDependencyCache CxxIgnoreHeader = {} CxxDependencyCache = {} def CxxCalcDependencies(srcfile, ipath, ignore): if (srcfile in CxxDependencyCache): return CxxDependencyCache[srcfile] if (ignore.count(srcfile)): return [] dep = {} dep[srcfile] = 1 includes = CxxGetIncludes(srcfile) for include in includes: header = CxxFindHeader(srcfile, include, ipath) if (header!=0): if (ignore.count(header)==0): hdeps = CxxCalcDependencies(header, ipath, [srcfile]+ignore) for x in hdeps: dep[x] = 1 result = list(dep.keys()) CxxDependencyCache[srcfile] = result return result ######################################################################## ## ## Registry Key Handling ## ## Of course, these routines will fail if you call them on a ## non win32 platform. If you use them on a win64 platform, they ## will look in the win32 private hive first, then look in the ## win64 hive. ## ######################################################################## if sys.platform == "win32": # Note: not supported on cygwin. if sys.version_info >= (3, 0): import winreg else: import _winreg as winreg def TryRegistryKey(path): try: key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ) return key except: pass try: key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ | 256) return key except: pass return 0 def ListRegistryKeys(path): result=[] index=0 key = TryRegistryKey(path) if (key != 0): try: while (1): result.append(winreg.EnumKey(key, index)) index = index + 1 except: pass winreg.CloseKey(key) return result def ListRegistryValues(path): result = [] index = 0 key = TryRegistryKey(path) if (key != 0): try: while (1): result.append(winreg.EnumValue(key, index)[0]) index = index + 1 except: pass winreg.CloseKey(key) return result def GetRegistryKey(path, subkey, override64=True): if (host_64 and override64): path = path.replace("SOFTWARE\\", "SOFTWARE\\Wow6432Node\\") k1=0 key = TryRegistryKey(path) if (key != 0): try: k1, k2 = winreg.QueryValueEx(key, subkey) except: pass winreg.CloseKey(key) return k1 def GetProgramFiles(): if ("PROGRAMFILES" in os.environ): return os.environ["PROGRAMFILES"] elif (os.path.isdir("C:\\Program Files")): return "C:\\Program Files" elif (os.path.isdir("D:\\Program Files")): return "D:\\Program Files" elif (os.path.isdir("E:\\Program Files")): return "E:\\Program Files" return 0 ######################################################################## ## ## Parsing Compiler Option Lists ## ######################################################################## def GetListOption(opts, prefix): res=[] for x in opts: if (x.startswith(prefix)): res.append(x[len(prefix):]) return res def GetValueOption(opts, prefix): for x in opts: if (x.startswith(prefix)): return x[len(prefix):] return 0 def GetOptimizeOption(opts): val = GetValueOption(opts, "OPT:") if (val == 0): return OPTIMIZE return val ######################################################################## ## ## General File Manipulation ## ######################################################################## def MakeDirectory(path): if os.path.isdir(path): return 0 os.mkdir(path) def ReadFile(wfile): try: srchandle = open(wfile, "r") data = srchandle.read() srchandle.close() return data except: ex = sys.exc_info()[1] exit("Cannot read %s: %s" % (wfile, ex)) def ReadBinaryFile(wfile): try: srchandle = open(wfile, "rb") data = srchandle.read() srchandle.close() return data except: ex = sys.exc_info()[1] exit("Cannot read %s: %s" % (wfile, ex)) def WriteFile(wfile, data): try: dsthandle = open(wfile, "w") dsthandle.write(data) dsthandle.close() except: ex = sys.exc_info()[1] exit("Cannot write to %s: %s" % (wfile, ex)) def WriteBinaryFile(wfile, data): try: dsthandle = open(wfile, "wb") dsthandle.write(data) dsthandle.close() except: ex = sys.exc_info()[1] exit("Cannot write to %s: %s" % (wfile, ex)) def ConditionalWriteFile(dest, desiredcontents): try: rfile = open(dest, 'r') contents = rfile.read(-1) rfile.close() except: contents = 0 if contents != desiredcontents: if VERBOSE: print("Writing %s" % (dest)) sys.stdout.flush() WriteFile(dest, desiredcontents) def DeleteVCS(dir): if dir == "": dir = "." for entry in os.listdir(dir): subdir = os.path.join(dir, entry) if (os.path.isdir(subdir)): if entry in VCS_DIRS: shutil.rmtree(subdir) else: DeleteVCS(subdir) elif (os.path.isfile(subdir) and (entry in VCS_FILES or entry.startswith(".#"))): os.remove(subdir) def DeleteBuildFiles(dir): if dir == "": dir = "." for entry in os.listdir(dir): subdir = os.path.join(dir, entry) if (os.path.isfile(subdir) and os.path.splitext(subdir)[-1] in [".pp", ".moved"]): os.remove(subdir) elif (os.path.isdir(subdir)): if (os.path.basename(subdir)[:3] == "Opt" and os.path.basename(subdir)[4] == "-"): shutil.rmtree(subdir) else: DeleteBuildFiles(subdir) def DeleteEmptyDirs(dir): if dir == "": dir = "." entries = os.listdir(dir) if not entries: os.rmdir(dir) return for entry in entries: subdir = os.path.join(dir, entry) if (os.path.isdir(subdir)): if (not os.listdir(subdir)): os.rmdir(subdir) else: DeleteEmptyDirs(subdir) def CreateFile(file): if (os.path.exists(file)==0): WriteFile(file, "") ######################################################################## # # Create the panda build tree. # ######################################################################## def MakeBuildTree(): MakeDirectory(OUTPUTDIR) MakeDirectory(OUTPUTDIR + "/bin") MakeDirectory(OUTPUTDIR + "/lib") MakeDirectory(OUTPUTDIR + "/tmp") MakeDirectory(OUTPUTDIR + "/etc") MakeDirectory(OUTPUTDIR + "/plugins") MakeDirectory(OUTPUTDIR + "/include") MakeDirectory(OUTPUTDIR + "/models") MakeDirectory(OUTPUTDIR + "/models/audio") MakeDirectory(OUTPUTDIR + "/models/audio/sfx") MakeDirectory(OUTPUTDIR + "/models/icons") MakeDirectory(OUTPUTDIR + "/models/maps") MakeDirectory(OUTPUTDIR + "/models/misc") MakeDirectory(OUTPUTDIR + "/models/gui") MakeDirectory(OUTPUTDIR + "/pandac") MakeDirectory(OUTPUTDIR + "/pandac/input") MakeDirectory(OUTPUTDIR + "/panda3d") if GetTarget() == 'darwin': MakeDirectory(OUTPUTDIR + "/Frameworks") elif GetTarget() == 'android': MakeDirectory(OUTPUTDIR + "/libs") MakeDirectory(OUTPUTDIR + "/libs/" + ANDROID_ABI) MakeDirectory(OUTPUTDIR + "/src") MakeDirectory(OUTPUTDIR + "/src/org") MakeDirectory(OUTPUTDIR + "/src/org/panda3d") MakeDirectory(OUTPUTDIR + "/src/org/panda3d/android") ######################################################################## # # Make sure that you are in the root of the panda tree. # ######################################################################## def CheckPandaSourceTree(): dir = os.getcwd() if ((os.path.exists(os.path.join(dir, "makepanda/makepanda.py"))==0) or (os.path.exists(os.path.join(dir, "dtool", "src", "dtoolbase", "dtoolbase.h"))==0) or (os.path.exists(os.path.join(dir, "panda", "src", "pandabase", "pandabase.h"))==0)): exit("Current directory is not the root of the panda tree.") ######################################################################## ## ## Thirdparty libraries paths ## ######################################################################## def GetThirdpartyBase(): """Returns the location of the 'thirdparty' directory. Normally, this is simply the thirdparty directory relative to the root of the source root, but if a MAKEPANDA_THIRDPARTY environment variable was set, it is used as the location of the thirdparty directory. This is useful when wanting to use a single system-wide thirdparty directory, for instance on a build machine.""" global THIRDPARTYBASE if (THIRDPARTYBASE != None): return THIRDPARTYBASE THIRDPARTYBASE = "thirdparty" if "MAKEPANDA_THIRDPARTY" in os.environ: THIRDPARTYBASE = os.environ["MAKEPANDA_THIRDPARTY"] return THIRDPARTYBASE def GetThirdpartyDir(): """Returns the thirdparty directory for the target platform, ie. thirdparty/win-libs-vc10/. May return None in the future.""" global THIRDPARTYDIR if THIRDPARTYDIR != None: return THIRDPARTYDIR base = GetThirdpartyBase() target = GetTarget() target_arch = GetTargetArch() if (target == 'windows'): if target_arch == 'x64': THIRDPARTYDIR = base + "/win-libs-vc10-x64/" if not os.path.isdir(THIRDPARTYDIR): THIRDPARTYDIR = base + "/win-libs-vc10/" else: THIRDPARTYDIR = base + "/win-libs-vc10/" elif (target == 'darwin'): # OSX thirdparty binaries are universal, where possible. THIRDPARTYDIR = base + "/darwin-libs-a/" elif (target == 'linux'): if (target_arch.startswith("arm")): THIRDPARTYDIR = base + "/linux-libs-arm/" elif (target_arch in ("x86_64", "amd64")): THIRDPARTYDIR = base + "/linux-libs-x64/" else: THIRDPARTYDIR = base + "/linux-libs-a/" elif (target == 'freebsd'): if (target_arch.startswith("arm")): THIRDPARTYDIR = base + "/freebsd-libs-arm/" elif (target_arch in ("x86_64", "amd64")): THIRDPARTYDIR = base + "/freebsd-libs-x64/" else: THIRDPARTYDIR = base + "/freebsd-libs-a/" elif (target == 'android'): THIRDPARTYDIR = GetThirdpartyBase()+"/android-libs-%s/" % (GetTargetArch()) else: print("%s Unsupported platform: %s" % (ColorText("red", "WARNING:"), target)) return if (GetVerbose()): print("Using thirdparty directory: %s" % THIRDPARTYDIR) return THIRDPARTYDIR ######################################################################## ## ## Gets or sets the output directory, by default "built". ## Gets or sets the optimize level. ## Gets or sets the verbose flag. ## ######################################################################## def GetOutputDir(): return OUTPUTDIR def IsCustomOutputDir(): return CUSTOM_OUTPUTDIR def SetOutputDir(outputdir): global OUTPUTDIR, CUSTOM_OUTPUTDIR OUTPUTDIR = outputdir CUSTOM_OUTPUTDIR = True def GetOptimize(): return int(OPTIMIZE) def SetOptimize(optimize): global OPTIMIZE OPTIMIZE = optimize def GetVerbose(): return VERBOSE def SetVerbose(verbose): global VERBOSE VERBOSE = verbose def SetDebugDependencies(dd = True): global DEBUG_DEPENDENCIES DEBUG_DEPENDENCIES = dd def GetLinkAllStatic(): return LINK_ALL_STATIC def SetLinkAllStatic(val = True): global LINK_ALL_STATIC LINK_ALL_STATIC = val def UnsetLinkAllStatic(): global LINK_ALL_STATIC LINK_ALL_STATIC = False ######################################################################## ## ## Package Selection ## ## This facility enables makepanda to keep a list of packages selected ## by the user for inclusion or omission. ## ######################################################################## PKG_LIST_ALL = [] PKG_LIST_OMIT = {} PKG_LIST_CUSTOM = set() def PkgListSet(pkgs): global PKG_LIST_ALL global PKG_LIST_OMIT PKG_LIST_ALL=pkgs PKG_LIST_OMIT={} PkgEnableAll() def PkgListGet(): return PKG_LIST_ALL def PkgEnableAll(): for x in PKG_LIST_ALL: PKG_LIST_OMIT[x] = 0 def PkgDisableAll(): for x in PKG_LIST_ALL: PKG_LIST_OMIT[x] = 1 def PkgEnable(pkg): PKG_LIST_OMIT[pkg] = 0 def PkgDisable(pkg): PKG_LIST_OMIT[pkg] = 1 def PkgSetCustomLocation(pkg): PKG_LIST_CUSTOM.add(pkg) def PkgHasCustomLocation(pkg): return pkg in PKG_LIST_CUSTOM def PkgSkip(pkg): return PKG_LIST_OMIT[pkg] def PkgSelected(pkglist, pkg): if (pkglist.count(pkg)==0): return 0 if (PKG_LIST_OMIT[pkg]): return 0 return 1 ######################################################################## ## ## DTOOL/PRC Option value override ## ## This facility enables makepanda to keep a list of parameters ## overriden by the command line. ## ######################################################################## OVERRIDES_LIST={} def AddOverride(spec): if (spec.find("=")==-1):return pair = spec.split("=",1) OVERRIDES_LIST[pair[0]] = pair[1] def OverrideValue(parameter, value): if parameter in OVERRIDES_LIST: print("Overriding value of key \"" + parameter + "\" with value \"" + OVERRIDES_LIST[parameter] + "\"") return OVERRIDES_LIST[parameter] else: return value ######################################################################## ## ## These functions are for libraries which use pkg-config. ## ######################################################################## def PkgConfigHavePkg(pkgname, tool = "pkg-config"): """Returns a bool whether the pkg-config package is installed.""" if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)): return False if (tool == "pkg-config"): handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname) else: return bool(LocateBinary(tool) != None) result = handle.read().strip() returnval = handle.close() if returnval != None and returnval != 0: return False return bool(len(result) > 0) def PkgConfigGetLibs(pkgname, tool = "pkg-config"): """Returns a list of libs for the package, prefixed by -l.""" if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)): return [] if (tool == "pkg-config"): handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname) elif (tool == "fltk-config"): handle = os.popen(LocateBinary("fltk-config") + " --ldstaticflags") else: handle = os.popen(LocateBinary(tool) + " --libs") result = handle.read().strip() handle.close() libs = [] # Walk through the result arguments carefully. Look for -lname as # well as -framework name. r = result.split(' ') ri = 0 while ri < len(r): l = r[ri] if l.startswith("-l") or l.startswith("/"): libs.append(l) elif l == '-framework': libs.append(l) ri += 1 libs.append(r[ri]) ri += 1 return libs def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"): """Returns a list of includes for the package, NOT prefixed by -I.""" if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)): return [] if (tool == "pkg-config"): handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname) else: handle = os.popen(LocateBinary(tool) + " --cflags") result = handle.read().strip() if len(result) == 0: return [] handle.close() dirs = [] for opt in result.split(" "): if (opt.startswith("-I")): inc_dir = opt.replace("-I", "").replace("\"", "").strip() # Hack for ODE, otherwise -S/usr/include gets added to interrogate if inc_dir != '/usr/include' and inc_dir != '/usr/include/': dirs.append(inc_dir) return dirs def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"): """Returns a list of library paths for the package, NOT prefixed by -L.""" if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)): return [] if (tool == "pkg-config"): handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname) elif (tool == "wx-config" or tool == "ode-config"): return [] else: handle = os.popen(LocateBinary(tool) + " --ldflags") result = handle.read().strip() handle.close() if len(result) == 0: return [] libs = [] for l in result.split(" "): if (l.startswith("-L")): libs.append(l.replace("-L", "").replace("\"", "").strip()) return libs def PkgConfigGetDefSymbols(pkgname, tool = "pkg-config"): """Returns a dictionary of preprocessor definitions.""" if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)): return [] if (tool == "pkg-config"): handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags " + pkgname) else: handle = os.popen(LocateBinary(tool) + " --cflags") result = handle.read().strip() handle.close() if len(result) == 0: return {} defs = {} for l in result.split(" "): if (l.startswith("-D")): d = l.replace("-D", "").replace("\"", "").strip().split("=") if (len(d) == 1): defs[d[0]] = "" else: defs[d[0]] = d[1] return defs def PkgConfigEnable(opt, pkgname, tool = "pkg-config"): """Adds the libraries and includes to IncDirectory, LibName and LibDirectory.""" for i in PkgConfigGetIncDirs(pkgname, tool): IncDirectory(opt, i) for i in PkgConfigGetLibDirs(pkgname, tool): LibDirectory(opt, i) for i in PkgConfigGetLibs(pkgname, tool): LibName(opt, i) for i, j in PkgConfigGetDefSymbols(pkgname, tool).items(): DefSymbol(opt, i, j) def LocateLibrary(lib, lpath=[]): """ Returns True if this library was found in the given search path, False otherwise. """ target = GetTarget() for dir in lpath: if target == 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.dylib' % lib)): return os.path.join(dir, 'lib%s.dylib' % lib) elif target != 'darwin' and os.path.isfile(os.path.join(dir, 'lib%s.so' % lib)): return os.path.join(dir, 'lib%s.so' % lib) elif os.path.isfile(os.path.join(dir, 'lib%s.a' % lib)): return os.path.join(dir, 'lib%s.a' % lib) return None def SystemLibraryExists(lib): return LocateLibrary(lib, SYS_LIB_DIRS) is not None def ChooseLib(libs, thirdparty=None): """ Chooses a library from the parameters, in order of preference. Returns the first if none of them were found. """ lpath = [] if thirdparty is not None: lpath.append(os.path.join(GetThirdpartyDir(), thirdparty.lower(), "lib")) lpath += SYS_LIB_DIRS for l in libs: libname = l if l.startswith("lib"): libname = l[3:] if LocateLibrary(libname, lpath): return libname if len(libs) > 0: if VERBOSE: print(ColorText("cyan", "Couldn't find any of the libraries " + ", ".join(libs))) return libs[0] def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None, framework = None, target_pkg = None, tool = "pkg-config"): global PKG_LIST_ALL if (pkg in PkgListGet() and PkgSkip(pkg)): return if (target_pkg == "" or target_pkg == None): target_pkg = pkg if (pkgconfig == ""): pkgconfig = None if (framework == ""): framework = None if (libs == None or libs == ""): libs = () elif (isinstance(libs, str)): libs = (libs, ) if (incs == None or incs == ""): incs = () elif (isinstance(incs, str)): incs = (incs, ) if (defs == None or defs == "" or len(defs) == 0): defs = {} elif (isinstance(incs, str)): defs = {defs : ""} elif (isinstance(incs, list) or isinstance(incs, tuple) or isinstance(incs, set)): olddefs = defs defs = {} for d in olddefs: defs[d] = "" custom_loc = PkgHasCustomLocation(pkg) if pkg.lower() == "swscale" and os.path.isfile(GetThirdpartyDir() + "ffmpeg/include/libswscale/swscale.h"): # Let it be handled by the ffmpeg package LibName(target_pkg, "-lswscale") return if pkg.lower() == "swresample" and os.path.isfile(GetThirdpartyDir() + "ffmpeg/include/libswresample/swresample.h"): LibName(target_pkg, "-lswresample") return pkg_dir = os.path.join(GetThirdpartyDir(), pkg.lower()) if not custom_loc and os.path.isdir(pkg_dir): if framework and os.path.isdir(os.path.join(pkg_dir, framework + ".framework")): FrameworkDirectory(target_pkg, pkg_dir) LibName(target_pkg, "-framework " + framework) return if os.path.isdir(os.path.join(pkg_dir, "include")): IncDirectory(target_pkg, os.path.join(pkg_dir, "include")) if os.path.isdir(os.path.join(pkg_dir, "lib")): LibDirectory(target_pkg, os.path.join(pkg_dir, "lib")) if (PkgSkip("PYTHON") == 0): py_lib_dir = os.path.join(pkg_dir, "lib", SDK["PYTHONVERSION"]) if os.path.isdir(py_lib_dir): LibDirectory(target_pkg, py_lib_dir) # TODO: check for a .pc file in the lib/pkg-config/ dir if (tool != None and os.path.isfile(os.path.join(pkg_dir, "bin", tool))): tool = os.path.join(pkg_dir, "bin", tool) for i in PkgConfigGetLibs(None, tool): LibName(target_pkg, i) for i, j in PkgConfigGetDefSymbols(None, tool).items(): DefSymbol(target_pkg, i, j) return for l in libs: libname = l if l.startswith("lib"): libname = l[3:] # This is for backward compatibility - in the thirdparty dir, we kept some libs with "panda" prefix, like libpandatiff. if len(glob.glob(os.path.join(pkg_dir, "lib", "libpanda%s.*" % (libname)))) > 0 \ and len(glob.glob(os.path.join(pkg_dir, "lib", "lib%s.*" % (libname)))) == 0: libname = "panda" + libname LibName(target_pkg, "-l" + libname) for d, v in defs.values(): DefSymbol(target_pkg, d, v) return elif not custom_loc and GetHost() == "darwin" and framework != None: prefix = SDK["MACOSX"] if (os.path.isdir(prefix + "/Library/Frameworks/%s.framework" % framework) or os.path.isdir(prefix + "/System/Library/Frameworks/%s.framework" % framework) or os.path.isdir(prefix + "/Developer/Library/Frameworks/%s.framework" % framework) or os.path.isdir(prefix + "/Users/%s/System/Library/Frameworks/%s.framework" % (getpass.getuser(), framework))): LibName(target_pkg, "-framework " + framework) for d, v in defs.values(): DefSymbol(target_pkg, d, v) return elif VERBOSE: print(ColorText("cyan", "Couldn't find the framework %s" % (framework))) elif not custom_loc and LocateBinary(tool) != None and (tool != "pkg-config" or pkgconfig != None): if (isinstance(pkgconfig, str) or tool != "pkg-config"): if (PkgConfigHavePkg(pkgconfig, tool)): return PkgConfigEnable(target_pkg, pkgconfig, tool) else: have_all_pkgs = True for pc in pkgconfig: if (PkgConfigHavePkg(pc, tool)): PkgConfigEnable(target_pkg, pc, tool) else: have_all_pkgs = False if (have_all_pkgs): return if pkgconfig is not None and not libs: # pkg-config is all we can do, abort if it wasn't found. if pkg in PkgListGet(): print("%sWARNING:%s Could not locate pkg-config package %s, excluding from build" % (GetColor("red"), GetColor(), pkgconfig)) PkgDisable(pkg) else: print("%sERROR:%s Could not locate pkg-config package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig)) exit() else: # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves. have_pkg = True for l in libs: libname = l if l.startswith("lib"): libname = l[3:] if custom_loc: # Try searching in the package's LibDirectories. lpath = [dir for ppkg, dir in LIBDIRECTORIES if pkg == ppkg] location = LocateLibrary(libname, lpath) if location is not None: LibName(target_pkg, location) else: have_pkg = False print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()) elif SystemLibraryExists(libname): # It exists in a system library directory. LibName(target_pkg, "-l" + libname) else: # Try searching in the package's LibDirectories. lpath = [dir for ppkg, dir in LIBDIRECTORIES if pkg == ppkg or ppkg == "ALWAYS"] location = LocateLibrary(libname, lpath) if location is not None: LibName(target_pkg, "-l" + libname) else: have_pkg = False if VERBOSE or custom_loc: print(GetColor("cyan") + "Couldn't find library lib" + libname + GetColor()) # Determine which include directories to look in. incdirs = [] if not custom_loc: incdirs += list(SYS_INC_DIRS) for ppkg, pdir in INCDIRECTORIES: if pkg == ppkg or (ppkg == "ALWAYS" and not custom_loc): incdirs.append(pdir) # The incs list contains both subdirectories to explicitly add to # the include path and header files to check the existence of. for i in incs: incdir = None for dir in incdirs: if len(glob.glob(os.path.join(dir, i))) > 0: incdir = sorted(glob.glob(os.path.join(dir, i)))[-1] # Note: It's possible to specify a file instead of a dir, for the sake of checking if it exists. if incdir is None and i.endswith(".h"): have_pkg = False if VERBOSE or custom_loc: print(GetColor("cyan") + "Couldn't find header file " + i + GetColor()) if incdir is not None and os.path.isdir(incdir): IncDirectory(target_pkg, incdir) if not have_pkg: if custom_loc: print("%sERROR:%s Could not locate thirdparty package %s in specified directory, aborting build" % (GetColor("red"), GetColor(), pkg.lower())) exit() elif pkg in PkgListGet(): print("%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), pkg.lower())) PkgDisable(pkg) else: print("%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), pkg.lower())) exit() ######################################################################## ## ## SDK Location ## ## This section is concerned with locating the install directories ## for various third-party packages. The results are stored in the ## SDK table. ## ## Microsoft keeps changing the &*#$*& registry key for the DirectX SDK. ## The only way to reliably find it is to search through the installer's ## uninstall-directories, look in each one, and see if it contains the ## relevant files. ## ######################################################################## SDK = {} def GetSdkDir(sdkname, sdkkey = None): # Returns the default SDK directory. If it exists, # and sdkkey is not None, it is put in SDK[sdkkey]. # Note: return value may not be an existing path. sdkbase = "sdks" if "MAKEPANDA_SDKS" in os.environ: sdkbase = os.environ["MAKEPANDA_SDKS"] sdir = sdkbase[:] target = GetTarget() target_arch = GetTargetArch() if target == 'windows': if target_arch == 'x64': sdir += "/win64" else: sdir += "/win32" elif target == 'linux': sdir += "/linux" sdir += platform.architecture()[0][:2] elif target == 'darwin': sdir += "/macosx" sdir += "/" + sdkname # If it does not exist, try the old location. if (not os.path.isdir(sdir)): sdir = sdkbase + "/" + sdir if (target == 'linux'): sdir += "-linux" sdir += platform.architecture()[0][:2] elif (target == "darwin"): sdir += "-osx" if (sdkkey and os.path.isdir(sdir)): SDK[sdkkey] = sdir return sdir def SdkLocateDirectX( strMode = 'default' ): if (GetHost() != "windows"): return if strMode == 'default': GetSdkDir("directx9", "DX9") if ("DX9" not in SDK): strMode = 'latest' if strMode == 'latest': ## We first try to locate the August SDK in 64 bits, then 32. if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath") if (dir != 0): print("Using DirectX SDK June 2010") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath") if (dir != 0): print("Using DirectX SDK June 2010") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath") if (dir != 0): print("Using DirectX SDK Aug 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath") if (dir != 0): print("Using DirectX SDK Aug 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): ## Try to locate the key within the "new" March 2009 location in the registry (yecch): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath") if (dir != 0): print("Using DirectX SDK March 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") archStr = GetTargetArch() if ("DX9" not in SDK): uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" for subdir in ListRegistryKeys(uninstaller): if (subdir[0]=="{"): dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation") if (dir != 0): if (("DX9" not in SDK) and (os.path.isfile(dir+"\\Include\\d3d9.h")) and (os.path.isfile(dir+"\\Include\\d3dx9.h")) and (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3d9.lib")) and (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3dx9.lib"))): SDK["DX9"] = dir.replace("\\", "/").rstrip("/") if ("DX9" not in SDK): return elif strMode == 'jun2010': if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath") if (dir != 0): print("Found DirectX SDK June 2010") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (June 2010)", "InstallPath") if (dir != 0): print("Found DirectX SDK June 2010") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): exit("Couldn't find DirectX June2010 SDK") elif strMode == 'aug2009': if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Wow6432Node\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath") if (dir != 0): print("Found DirectX SDK Aug 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (August 2009)", "InstallPath") if (dir != 0): print("Found DirectX SDK Aug 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") SDK["GENERIC_DXERR_LIBRARY"] = 1; if ("DX9" not in SDK): exit("Couldn't find DirectX Aug 2009 SDK") elif strMode == 'mar2009': if ("DX9" not in SDK): ## Try to locate the key within the "new" March 2009 location in the registry (yecch): dir = GetRegistryKey("SOFTWARE\\Microsoft\\DirectX\\Microsoft DirectX SDK (March 2009)", "InstallPath") if (dir != 0): print("Found DirectX SDK March 2009") SDK["DX9"] = dir.replace("\\", "/").rstrip("/") if ("DX9" not in SDK): exit("Couldn't find DirectX March 2009 SDK") elif strMode == 'aug2006': archStr = GetTargetArch() if ("DX9" not in SDK): uninstaller = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" for subdir in ListRegistryKeys(uninstaller): if (subdir[0]=="{"): dir = GetRegistryKey(uninstaller+"\\"+subdir, "InstallLocation") if (dir != 0): if (("DX9" not in SDK) and (os.path.isfile(dir+"\\Include\\d3d9.h")) and (os.path.isfile(dir+"\\Include\\d3dx9.h")) and (os.path.isfile(dir+"\\Include\\dxsdkver.h")) and (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3d9.lib")) and (os.path.isfile(dir+"\\Lib\\" + archStr + "\\d3dx9.lib"))): SDK["DX9"] = dir.replace("\\", "/").rstrip("/") if ("DX9" not in SDK): exit("Couldn't find a DirectX Aug 2006 SDK") if ("DX9" in SDK): SDK["DIRECTCAM"] = SDK["DX9"] def SdkLocateMaya(): for (ver, key) in MAYAVERSIONINFO: if (PkgSkip(ver)==0 and ver not in SDK): GetSdkDir(ver.lower().replace("x",""), ver) if (not ver in SDK): if (GetHost() == "windows"): for dev in ["Alias|Wavefront","Alias","Autodesk"]: fullkey="SOFTWARE\\"+dev+"\\Maya\\"+key+"\\Setup\\InstallPath" res = GetRegistryKey(fullkey, "MAYA_INSTALL_LOCATION", override64=False) if (res != 0): res = res.replace("\\", "/").rstrip("/") SDK[ver] = res elif (GetHost() == "darwin"): ddir = "/Applications/Autodesk/maya"+key if (os.path.isdir(ddir)): SDK[ver] = ddir else: if (GetTargetArch() in ("x86_64", "amd64")): ddir1 = "/usr/autodesk/maya"+key+"-x64" ddir2 = "/usr/aw/maya"+key+"-x64" else: ddir1 = "/usr/autodesk/maya"+key ddir2 = "/usr/aw/maya"+key if (os.path.isdir(ddir1)): SDK[ver] = ddir1 elif (os.path.isdir(ddir2)): SDK[ver] = ddir2 def SdkLocateMax(): if (GetHost() != "windows"): return for version,key1,key2,subdir in MAXVERSIONINFO: if (PkgSkip(version)==0): if (version not in SDK): GetSdkDir("maxsdk"+version.lower()[3:], version) GetSdkDir("maxsdk"+version.lower()[3:], version+"CS") if (not version in SDK): top = GetRegistryKey(key1,key2) if (top != 0): SDK[version] = top + "maxsdk" if (os.path.isdir(top + "\\" + subdir)!=0): SDK[version+"CS"] = top + subdir def SdkLocatePython(prefer_thirdparty_python=False): if PkgSkip("PYTHON"): # We're not compiling with Python support. We still need to set this # in case we want to run any scripts that use Python, though. SDK["PYTHONEXEC"] = os.path.realpath(sys.executable) return if GetTarget() == 'windows': sdkdir = GetThirdpartyBase() + "/win-python" if sys.version_info >= (3, 0): # Python 3 build... sdkdir += "%d.%d" % sys.version_info[:2] if GetOptimize() <= 2: sdkdir += "-dbg" if GetTargetArch() == 'x64': sdkdir += "-x64" SDK["PYTHON"] = sdkdir SDK["PYTHONEXEC"] = SDK["PYTHON"].replace('/', '\\') + "\\python" if (GetOptimize() <= 2): SDK["PYTHONEXEC"] += "_d.exe" else: SDK["PYTHONEXEC"] += ".exe" if (not os.path.isfile(SDK["PYTHONEXEC"])): exit("Could not find %s!" % SDK["PYTHONEXEC"]) # Determine which version it is by checking which dll is in the directory. if (GetOptimize() <= 2): py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll") else: py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll") if len(py_dlls) == 0: exit("Could not find the Python dll in %s." % (SDK["PYTHON"])) elif len(py_dlls) > 1: exit("Found multiple Python dlls in %s." % (SDK["PYTHON"])) py_dll = os.path.basename(py_dlls[0]) ver = py_dll[6] + "." + py_dll[7] SDK["PYTHONVERSION"] = "python" + ver os.environ["PYTHONHOME"] = SDK["PYTHON"] if sys.version[:3] != ver: print("Warning: running makepanda with Python %s, but building Panda3D with Python %s." % (sys.version[:3], ver)) elif CrossCompiling() or (prefer_thirdparty_python and os.path.isdir(os.path.join(GetThirdpartyDir(), "python"))): tp_python = os.path.join(GetThirdpartyDir(), "python") if GetTarget() == 'darwin': py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].dylib") else: py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].so") if len(py_libs) == 0: py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].a") if len(py_libs) == 0: exit("Could not find the Python library in %s." % (tp_python)) elif len(py_libs) > 1: exit("Found multiple Python libraries in %s." % (tp_python)) py_lib = os.path.basename(py_libs[0]) SDK["PYTHONVERSION"] = "python" + py_lib[9] + "." + py_lib[11] SDK["PYTHONEXEC"] = tp_python + "/bin/" + SDK["PYTHONVERSION"] SDK["PYTHON"] = tp_python + "/include/" + SDK["PYTHONVERSION"] elif GetTarget() == 'darwin': # On Mac OS X, use the system Python framework. py_fwx = SDK.get("MACOSX", "") + "/System/Library/Frameworks/Python.framework/Versions/Current" if not os.path.islink(py_fwx): exit("Could not locate Python installation at %s" % (py_fwx)) ver = os.path.basename(os.readlink(py_fwx)) py_fwx = SDK.get("MACOSX", "") + "/System/Library/Frameworks/Python.framework/Versions/%s" % ver SDK["PYTHON"] = py_fwx + "/Headers" SDK["PYTHONVERSION"] = "python" + ver SDK["PYTHONEXEC"] = "/System/Library/Frameworks/Python.framework/Versions/" + ver + "/bin/python" + ver if sys.version[:3] != ver: print("Warning: building with Python %s instead of %s since you targeted a specific Mac OS X version." % (ver, sys.version[:3])) #elif GetTarget() == 'windows': # SDK["PYTHON"] = os.path.dirname(sysconfig.get_python_inc()) # SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version() # SDK["PYTHONEXEC"] = sys.executable else: SDK["PYTHON"] = sysconfig.get_python_inc() SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version() SDK["PYTHONEXEC"] = os.path.realpath(sys.executable) if CrossCompiling(): # We need a version of Python we can run. SDK["PYTHONEXEC"] = sys.executable host_version = "python" + sysconfig.get_python_version() if SDK["PYTHONVERSION"] != host_version: exit("Host Python version (%s) must be the same as target Python version (%s)!" % (host_version, SDK["PYTHONVERSION"])) if GetVerbose(): print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:9], SDK["PYTHON"])) else: print("Using Python %s" % (SDK["PYTHONVERSION"][6:9])) def SdkLocateVisualStudio(): if (GetHost() != "windows"): return vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "10.0") if (vcdir != 0) and (vcdir[-4:] == "\\VC\\"): vcdir = vcdir[:-3] SDK["VISUALSTUDIO"] = vcdir elif (os.path.isfile("C:\\Program Files\\Microsoft Visual Studio 10.0\\VC\\bin\\cl.exe")): SDK["VISUALSTUDIO"] = "C:\\Program Files\\Microsoft Visual Studio 10.0\\" elif (os.path.isfile("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\bin\\cl.exe")): SDK["VISUALSTUDIO"] = "C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\" elif "VCINSTALLDIR" in os.environ: vcdir = os.environ["VCINSTALLDIR"] if (vcdir[-3:] == "\\VC"): vcdir = vcdir[:-2] elif (vcdir[-4:] == "\\VC\\"): vcdir = vcdir[:-3] SDK["VISUALSTUDIO"] = vcdir def SdkLocateMSPlatform(strMode = 'default'): if (GetHost() != "windows"): return platsdk = None platsdk = GetRegistryKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1", "InstallationFolder") if (platsdk and not os.path.isdir(platsdk)): platsdk = None if not platsdk: # Most common location. Worth a try. platsdk = "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.1" if not os.path.isdir(platsdk): platsdk = None if not platsdk: exit("Couldn't find Windows SDK v7.1") if not platsdk.endswith("\\"): platsdk += "\\" SDK["MSPLATFORM"] = platsdk def SdkLocateMacOSX(osxtarget = None): if (GetHost() != "darwin"): return if (osxtarget != None): sdkname = "MacOSX%d.%d" % osxtarget if (os.path.exists("/Developer/SDKs/%su.sdk" % sdkname)): SDK["MACOSX"] = "/Developer/SDKs/%su.sdk" % sdkname elif (os.path.exists("/Developer/SDKs/%s.sdk" % sdkname)): SDK["MACOSX"] = "/Developer/SDKs/%s.sdk" % sdkname elif (os.path.exists("/Developer/SDKs/%s.0.sdk" % sdkname)): SDK["MACOSX"] = "/Developer/SDKs/%s.0.sdk" % sdkname elif (os.path.exists("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % sdkname)): SDK["MACOSX"] = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % sdkname else: handle = os.popen("xcode-select -print-path") result = handle.read().strip().rstrip('/') handle.close() if (os.path.exists("%s/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % (result, sdkname))): SDK["MACOSX"] = "%s/Platforms/MacOSX.platform/Developer/SDKs/%s.sdk" % (result, sdkname) else: exit("Couldn't find any MacOSX SDK for OSX version %s!" % sdkname) else: SDK["MACOSX"] = "" # Latest first PHYSXVERSIONINFO = [ ("PHYSX284", "v2.8.4"), ("PHYSX283", "v2.8.3"), ("PHYSX281", "v2.8.1"), ] def SdkLocatePhysX(): # First check for a physx directory in sdks. dir = GetSdkDir("physx") if (dir and os.path.isdir(dir)): SDK["PHYSX"] = dir SDK["PHYSXLIBS"] = dir + "/lib" return if CrossCompiling(): return # Try to find a PhysX installation on the system. for (ver, key) in PHYSXVERSIONINFO: if (GetHost() == "windows"): folders = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\Folders" for folder in ListRegistryValues(folders): if folder.endswith("NVIDIA PhysX SDK\\%s\\SDKs\\" % key) or \ folder.endswith("NVIDIA PhysX SDK\\%s_win\\SDKs\\" % key): SDK["PHYSX"] = folder if GetTargetArch() == 'x64': SDK["PHYSXLIBS"] = folder + "/lib/win64" AddToPathEnv("PATH", folder + "/../Bin/win64/") else: SDK["PHYSXLIBS"] = folder + "/lib/win32" AddToPathEnv("PATH", folder + "/../Bin/win32/") return elif (GetHost() == "linux"): incpath = "/usr/include/PhysX/%s/SDKs" % key libpath = "/usr/lib/PhysX/%s" % key if (os.path.isdir(incpath) and os.path.isdir(libpath)): SDK["PHYSX"] = incpath SDK["PHYSXLIBS"] = libpath return def SdkLocateSpeedTree(): # Look for all of the SpeedTree SDK directories within the # sdks/win32/speedtree dir, and pick the highest-numbered one. dir = GetSdkDir("speedtree") if not os.path.exists(dir): return speedtrees = [] for dirname in os.listdir(dir): if dirname.startswith('SpeedTree SDK v'): version = dirname[15:].split()[0] version = tuple(map(int, version.split('.'))) speedtrees.append((version, dirname)) if not speedtrees: # No installed SpeedTree SDK. return speedtrees.sort() version, dirname = speedtrees[-1] SDK["SPEEDTREE"] = os.path.join(dir, dirname) SDK["SPEEDTREEAPI"] = "OpenGL" SDK["SPEEDTREEVERSION"] = '%s.%s' % (version[0], version[1]) def SdkLocateAndroid(): """This actually locates the Android NDK, not the Android SDK. NDK_ROOT must be set to its root directory.""" if GetTarget() != 'android': return # Determine the NDK installation directory. if 'NDK_ROOT' not in os.environ: exit('NDK_ROOT must be set when compiling for Android!') ndk_root = os.environ["NDK_ROOT"] if not os.path.isdir(ndk_root): exit("Cannot find %s. Please install Android NDK and set NDK_ROOT." % (ndk_root)) SDK["ANDROID_NDK"] = ndk_root # Determine the toolchain location. gcc_ver = '4.8' arch = GetTargetArch() if arch == 'armv7a' or arch == 'arm': arch = 'arm' toolchain = 'arm-linux-androideabi-' + gcc_ver elif arch == 'x86': toolchain = 'x86-' + gcc_ver elif arch == 'mips': toolchain = 'mipsel-linux-android-' + gcc_ver SDK["ANDROID_TOOLCHAIN"] = os.path.join(ndk_root, 'toolchains', toolchain) # Allow ANDROID_ABI to be used in makepanda.py. abi = ANDROID_ABI SDK["ANDROID_ABI"] = abi # Determine the sysroot directory. SDK["SYSROOT"] = os.path.join(ndk_root, 'platforms', 'android-9', 'arch-%s' % (arch)) #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include')) stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver) SDK["ANDROID_STL"] = stdlibc #IncDirectory("ALWAYS", os.path.join(stdlibc, 'include')) #IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include')) stl_lib = os.path.join(stdlibc, 'libs', abi, 'libgnustl_shared.so') LibName("ALWAYS", stl_lib) CopyFile(os.path.join(GetOutputDir(), 'libs', abi, 'libgnustl_shared.so'), stl_lib) ######################################################################## ## ## SDK Auto-Disables ## ## Disable packages whose SDKs could not be found. ## ######################################################################## def SdkAutoDisableDirectX(): for ver in DXVERSIONS + ["DIRECTCAM"]: if (PkgSkip(ver)==0): if (ver not in SDK): if (GetHost() == "windows"): WARNINGS.append("I cannot locate SDK for "+ver) WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower()) PkgDisable(ver) else: WARNINGS.append("Using "+ver+" sdk: "+SDK[ver]) def SdkAutoDisableMaya(): for (ver,key) in MAYAVERSIONINFO: if (ver not in SDK) and (PkgSkip(ver)==0): if (GetHost() == "windows"): WARNINGS.append("The registry does not appear to contain a pointer to the "+ver+" SDK.") else: WARNINGS.append("I cannot locate SDK for "+ver) WARNINGS.append("I have automatically added this command-line option: --no-"+ver.lower()) PkgDisable(ver) def SdkAutoDisableMax(): for version,key1,key2,subdir in MAXVERSIONINFO: if (PkgSkip(version)==0) and ((version not in SDK) or (version+"CS" not in SDK)): if (GetHost() == "windows"): if (version in SDK): WARNINGS.append("Your copy of "+version+" does not include the character studio SDK") else: WARNINGS.append("The registry does not appear to contain a pointer to "+version) WARNINGS.append("I have automatically added this command-line option: --no-"+version.lower()) PkgDisable(version) def SdkAutoDisablePhysX(): if ("PHYSX" not in SDK) and (PkgSkip("PHYSX")==0): PkgDisable("PHYSX") WARNINGS.append("I cannot locate SDK for PhysX") WARNINGS.append("I have automatically added this command-line option: --no-physx") def SdkAutoDisableSpeedTree(): if ("SPEEDTREE" not in SDK) and (PkgSkip("SPEEDTREE")==0): PkgDisable("SPEEDTREE") WARNINGS.append("I cannot locate SDK for SpeedTree") WARNINGS.append("I have automatically added this command-line option: --no-speedtree") ######################################################################## ## ## Visual Studio comes with a script called VSVARS32.BAT, which ## you need to run before using visual studio command-line tools. ## The following python subroutine serves the same purpose. ## ######################################################################## def AddToPathEnv(path,add): if path in os.environ: os.environ[path] = add + os.pathsep + os.environ[path] else: os.environ[path] = add def SetupVisualStudioEnviron(): if ("VISUALSTUDIO" not in SDK): exit("Could not find Visual Studio install directory") if ("MSPLATFORM" not in SDK): exit("Could not find the Microsoft Platform SDK") os.environ["VCINSTALLDIR"] = SDK["VISUALSTUDIO"] + "VC" os.environ["WindowsSdkDir"] = SDK["MSPLATFORM"] # Determine the directories to look in based on the architecture. arch = GetTargetArch() bindir = "" libdir = "" if (arch == 'x64'): bindir = 'amd64' libdir = 'amd64' elif (arch != 'x86'): bindir = arch libdir = arch if (arch != 'x86' and GetHostArch() == 'x86'): # Special version of the tools that run on x86. bindir = 'x86_' + bindir binpath = SDK["VISUALSTUDIO"] + "VC\\bin\\" + bindir if not os.path.isdir(binpath): exit("Couldn't find compilers in %s. You may need to install the Windows SDK 7.1 and the Visual C++ 2010 SP1 Compiler Update for Windows SDK 7.1.") AddToPathEnv("PATH", binpath) AddToPathEnv("PATH", SDK["VISUALSTUDIO"] + "Common7\\IDE") AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\include") AddToPathEnv("INCLUDE", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\include") AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\lib\\" + libdir) AddToPathEnv("LIB", SDK["VISUALSTUDIO"] + "VC\\atlmfc\\lib\\" + libdir) AddToPathEnv("PATH", SDK["MSPLATFORM"] + "bin") AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include") AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\atl") AddToPathEnv("INCLUDE", SDK["MSPLATFORM"] + "include\\mfc") if (arch != 'x64'): AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib") AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC100.CRT") AddToPathEnv("PATH",SDK["VISUALSTUDIO"] + "VC\\redist\\x86\\Microsoft.VC100.MFC") elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\x64")): AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\x64") elif (os.path.isdir(SDK["MSPLATFORM"] + "lib\\amd64")): AddToPathEnv("LIB", SDK["MSPLATFORM"] + "lib\\amd64") else: exit("Could not locate 64-bits libraries in Platform SDK!") ######################################################################## # # Include and Lib directories. # # These allow you to add include and lib directories to the # compiler search paths. These methods accept a "package" # parameter, which specifies which package the directory is # associated with. The include/lib directory is not used # if the package is not selected. The package can be 'ALWAYS'. # ######################################################################## INCDIRECTORIES = [] LIBDIRECTORIES = [] FRAMEWORKDIRECTORIES = [] LIBNAMES = [] DEFSYMBOLS = [] def IncDirectory(opt, dir): INCDIRECTORIES.append((opt, dir)) def LibDirectory(opt, dir): LIBDIRECTORIES.append((opt, dir)) def FrameworkDirectory(opt, dir): FRAMEWORKDIRECTORIES.append((opt, dir)) def LibName(opt, name): # Check to see if the lib file actually exists for the thirdparty library given # Are we a thirdparty library? if name.startswith(GetThirdpartyDir()): # Does this lib exist? if not os.path.exists(name): WARNINGS.append(name + " not found. Skipping Package " + opt) if (opt in PkgListGet()): if not PkgSkip(opt): print("%sWARNING:%s Could not locate thirdparty package %s, excluding from build" % (GetColor("red"), GetColor(), opt.lower())) PkgDisable(opt) return else: print("%sERROR:%s Could not locate thirdparty package %s, aborting build" % (GetColor("red"), GetColor(), opt.lower())) exit() LIBNAMES.append((opt, name)) def DefSymbol(opt, sym, val=""): DEFSYMBOLS.append((opt, sym, val)) ######################################################################## # # This subroutine prepares the environment for the build. # ######################################################################## def SetupBuildEnvironment(compiler): if GetVerbose(): print("Using compiler: %s" % compiler) print("Host OS: %s" % GetHost()) print("Host arch: %s" % GetHostArch()) print("Target OS: %s" % GetTarget()) print("Target arch: %s" % GetTargetArch()) if compiler == "MSVC": # Add the visual studio tools to PATH et al. SetupVisualStudioEnviron() if compiler == "GCC": # Invoke gcc to determine the system library directories. global SYS_LIB_DIRS, SYS_INC_DIRS if sys.platform == "darwin": # We need to add this one explicitly for some reason. SYS_LIB_DIRS.append(SDK["MACOSX"] + "/usr/lib") if not SDK.get("MACOSX"): # gcc doesn't add this one, but we do want it: local_lib = SDK.get("SYSROOT", "") + "/usr/local/lib" if os.path.isdir(local_lib): SYS_LIB_DIRS.append(local_lib) sysroot_flag = "" if SDK.get("MACOSX"): # The default compiler in Leopard does not respect --sysroot correctly. sysroot_flag = " -isysroot " + SDK["MACOSX"] if SDK.get("SYSROOT"): sysroot_flag = ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"]) # Extract the dirs from the line that starts with 'libraries: ='. cmd = GetCXX() + " -print-search-dirs" + sysroot_flag handle = os.popen(cmd) for line in handle: if not line.startswith('libraries: ='): continue line = line[12:].strip() for libdir in line.split(':'): libdir = os.path.normpath(libdir) if os.path.isdir(libdir): if libdir not in SYS_LIB_DIRS: SYS_LIB_DIRS.append(libdir) elif GetVerbose(): print("Ignoring non-existent library directory %s" % (libdir)) returnval = handle.close() if returnval != None and returnval != 0: print("%sWARNING:%s %s failed" % (GetColor("red"), GetColor(), cmd)) SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"] # Now extract the preprocessor's include directories. cmd = GetCXX() + sysroot_flag + " -x c++ -v -E /dev/null" null = open(os.devnull, 'w') handle = subprocess.Popen(cmd, stdout=null, stderr=subprocess.PIPE, shell=True) scanning = False for line in handle.communicate()[1].splitlines(): line = line.decode('utf-8', 'replace') # Start looking at a line that says: #include "..." search starts here if not scanning: if line.startswith('#include'): scanning = True continue if not line.startswith(' /'): continue line = line.strip() if line.endswith(" (framework directory)"): pass elif os.path.isdir(line): SYS_INC_DIRS.append(os.path.normpath(line)) elif GetVerbose(): print("Ignoring non-existent include directory %s" % (line)) if handle.returncode != 0 or not SYS_INC_DIRS: print("%sWARNING:%s %s failed or did not produce the expected result" % (GetColor("red"), GetColor(), cmd)) sysroot = SDK.get("SYSROOT", "") # Add some sensible directories as a fallback. SYS_INC_DIRS = [ sysroot + "/usr/include", sysroot + "/usr/local/include" ] pcbsd_inc = sysroot + "/usr/PCBSD/local/include" if os.path.isdir(pcbsd_inc): SYS_INC_DIRS.append(pcbsd_inc) # Print out the search paths if GetVerbose(): print("System library search path:") for dir in SYS_LIB_DIRS: print(" " + dir) print("System include search path:") for dir in SYS_INC_DIRS: print(" " + dir) # In the case of Android, we have to put the toolchain on the PATH in order to use it. if GetTarget() == 'android': # Locate the directory where the toolchain binaries reside. prebuilt_dir = os.path.join(SDK['ANDROID_TOOLCHAIN'], 'prebuilt') if not os.path.isdir(prebuilt_dir): exit('Not found: %s' % (prebuilt_dir)) host_tag = GetHost() + '-x86' if host_64: host_tag += '_64' elif host_tag == 'windows-x86': host_tag = 'windows' prebuilt_dir = os.path.join(prebuilt_dir, host_tag) if host_64 and not os.path.isdir(prebuilt_dir): # Try the 32-bits toolchain instead. prebuilt_dir = os.path.join(prebuilt_dir, host_tag) if not os.path.isdir(prebuilt_dir): if host_64: exit('Not found: %s or %s' % (prebuilt_dir, host_tag)) else: exit('Not found: %s' % (prebuilt_dir)) # Then, add it to the PATH. AddToPathEnv("PATH", os.path.join(prebuilt_dir, 'bin')) # If we're cross-compiling, no point in putting our output dirs on the path. if CrossCompiling(): return # Add our output directories to the environment. builtdir = os.path.join(os.path.abspath(GetOutputDir())) AddToPathEnv("PYTHONPATH", builtdir) AddToPathEnv("PANDA_PRC_DIR", os.path.join(builtdir, "etc")) AddToPathEnv("PATH", os.path.join(builtdir, "bin")) if GetHost() == 'windows': # extension_native_helpers.py currently expects to find libpandaexpress on sys.path. AddToPathEnv("PYTHONPATH", os.path.join(builtdir, "bin")) AddToPathEnv("PATH", os.path.join(builtdir, "plugins")) # Now for the special (DY)LD_LIBRARY_PATH on Unix-esque systems. if GetHost() != 'windows': # Get the current ldpath = os.environ.get("LD_LIBRARY_PATH", "").split(os.pathsep) if GetHost() == 'darwin': dyldpath = os.environ.get("DYLD_LIBRARY_PATH", "").split(os.pathsep) # Remove any potential current Panda installation lib dirs for i in ldpath[:]: if i.startswith("/usr/lib/panda3d") or \ i.startswith("/usr/local/panda"): ldpath.remove(i) if GetHost() == 'darwin': for i in dyldpath[:]: if i.startswith("/Applications/Panda3D") or \ i.startswith("/Developer/Panda3D"): dyldpath.remove(i) # Add built/lib/ to (DY)LD_LIBRARY_PATH ldpath.insert(0, os.path.join(builtdir, 'lib')) os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(ldpath) if GetHost() == 'darwin': dyldpath.insert(0, os.path.join(builtdir, 'lib')) os.environ["DYLD_LIBRARY_PATH"] = os.pathsep.join(dyldpath) # Workaround around compile issue on PCBSD if (os.path.exists("/usr/PCBSD")): os.environ["LD_LIBRARY_PATH"] += os.pathsep + "/usr/PCBSD/local/lib" ######################################################################## ## ## Routines to copy files into the build tree ## ######################################################################## def CopyFile(dstfile, srcfile): if dstfile[-1] == '/': dstdir = dstfile fnl = srcfile.rfind("/") if fnl < 0: fn = srcfile else: fn = srcfile[fnl+1:] dstfile = dstdir + fn if NeedsBuild([dstfile], [srcfile]): if os.path.islink(srcfile): # Preserve symlinks if os.path.exists(dstfile): os.unlink(dstfile) os.symlink(os.readlink(srcfile), dstfile) else: WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) JustBuilt([dstfile], [srcfile]) def CopyAllFiles(dstdir, srcdir, suffix=""): for x in GetDirectoryContents(srcdir, ["*"+suffix]): CopyFile(dstdir + x, srcdir + x) def CopyAllHeaders(dir, skip=[]): for filename in GetDirectoryContents(dir, ["*.h", "*.I", "*.T"], skip): srcfile = dir + "/" + filename dstfile = OUTPUTDIR + "/include/" + filename if (NeedsBuild([dstfile], [srcfile])): WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) JustBuilt([dstfile], [srcfile]) def CopyAllJavaSources(dir, skip=[]): for filename in GetDirectoryContents(dir, ["*.java"], skip): srcfile = dir + "/" + filename dstfile = OUTPUTDIR + "/src/org/panda3d/android/" + filename if (NeedsBuild([dstfile], [srcfile])): WriteBinaryFile(dstfile, ReadBinaryFile(srcfile)) JustBuilt([dstfile], [srcfile]) def CopyTree(dstdir, srcdir, omitVCS=True): if (os.path.isdir(dstdir)): for entry in os.listdir(srcdir): srcpth = os.path.join(srcdir, entry) dstpth = os.path.join(dstdir, entry) if (os.path.isfile(srcpth)): if not omitVCS or entry not in VCS_FILES: CopyFile(dstpth, srcpth) else: if not omitVCS or entry not in VCS_DIRS: CopyTree(dstpth, srcpth) else: if sys.platform == 'win32': cmd = 'xcopy /I/Y/E/Q "' + srcdir + '" "' + dstdir + '"' else: cmd = 'cp -R -f ' + srcdir + ' ' + dstdir oscmd(cmd) if omitVCS: DeleteVCS(dstdir) def CopyPythonTree(dstdir, srcdir, lib2to3_fixers=[]): if (not os.path.isdir(dstdir)): os.mkdir(dstdir) lib2to3 = None if len(lib2to3_fixers) > 0 and sys.version_info >= (3, 0): from lib2to3.main import main as lib2to3 lib2to3_args = ['-w', '-n', '--no-diffs', '-x', 'buffer', '-x', 'idioms', '-x', 'set_literal', '-x', 'ws_comma'] if lib2to3_fixers != ['all']: for fixer in lib2to3_fixers: lib2to3_args += ['-f', fixer] exclude_files = set(VCS_FILES) exclude_files.add('panda3d.py') refactor = [] for entry in os.listdir(srcdir): srcpth = os.path.join(srcdir, entry) dstpth = os.path.join(dstdir, entry) if os.path.isfile(srcpth): base, ext = os.path.splitext(entry) if entry not in exclude_files and ext not in SUFFIX_INC + ['.pyc', '.pyo']: if (NeedsBuild([dstpth], [srcpth])): WriteBinaryFile(dstpth, ReadBinaryFile(srcpth)) if ext == '.py' and not entry.endswith('-extensions.py'): refactor.append((dstpth, srcpth)) else: JustBuilt([dstpth], [srcpth]) elif entry not in VCS_DIRS: CopyPythonTree(dstpth, srcpth, lib2to3_fixers) for dstpth, srcpth in refactor: if lib2to3 is not None: ret = lib2to3("lib2to3.fixes", lib2to3_args + [dstpth]) if ret != 0: os.remove(dstpth) exit("Error in lib2to3.") JustBuilt([dstpth], [srcpth]) ######################################################################## ## ## Parse PandaVersion.pp to extract the version number. ## ######################################################################## def ParsePandaVersion(fn): try: f = open(fn, "r") pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+PANDA_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)') for line in f: match = pattern.match(line, 0) if (match): f.close() return match.group(1) + "." + match.group(2) + "." + match.group(3) f.close() except: pass return "0.0.0" def ParsePluginVersion(fn): try: f = open(fn, "r") pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_PLUGIN_VERSION[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)') for line in f: match = pattern.match(line,0) if (match): f.close() return match.group(1) + "." + match.group(2) + "." + match.group(3) f.close() except: pass return "0.0.0" def ParseCoreapiVersion(fn): try: f = open(fn, "r") pattern = re.compile('^[ \t]*[#][ \t]*define[ \t]+P3D_COREAPI_VERSION.*([0-9]+)[ \t]*$') for line in f: match = pattern.match(line,0) if (match): f.close() return match.group(1) f.close() except: pass return "0" ########################################################################################## # # Utility function to generate a resource file # ########################################################################################## RESOURCE_FILE_TEMPLATE = """VS_VERSION_INFO VERSIONINFO FILEVERSION %(commaversion)s PRODUCTVERSION %(commaversion)s FILEFLAGSMASK 0x3fL FILEFLAGS %(debugflag)s FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904e4" BEGIN VALUE "FileDescription", "%(description)s\\0" VALUE "FileVersion", "%(dotversion)s" VALUE "LegalTrademarks", "\\0" VALUE "MIMEType", "%(mimetype)s\\0" VALUE "FileExtents", "%(extension)s\\0" VALUE "FileOpenName", "%(filedesc)s\\0" VALUE "OLESelfRegister", "\\0" VALUE "OriginalFilename", "%(filename)s\\0" VALUE "ProductName", "%(name)s %(version)s\\0" VALUE "ProductVersion", "%(dotversion)s" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END """ def GenerateResourceFile(**kwargs): if "debugflag" not in kwargs: if GetOptimize() <= 2: kwargs["debugflag"] = "0x1L" else: kwargs["debugflag"] = "0x0L" kwargs["dotversion"] = kwargs["version"] if len(kwargs["dotversion"].split(".")) == 3: kwargs["dotversion"] += ".0" if "commaversion" not in kwargs: kwargs["commaversion"] = kwargs["dotversion"].replace(".", ",") rcdata = "" if not "noinclude" in kwargs: rcdata += "#define APSTUDIO_READONLY_SYMBOLS\n" rcdata += "#include \"winresrc.h\"\n" rcdata += "#undef APSTUDIO_READONLY_SYMBOLS\n" rcdata += RESOURCE_FILE_TEMPLATE % kwargs if "icon" in kwargs: rcdata += "\nICON_FILE ICON \"%s\"\n" % kwargs["icon"] return rcdata def WriteResourceFile(basename, **kwargs): if not basename.endswith(".rc"): basename += ".rc" basename = GetOutputDir() + "/include/" + basename ConditionalWriteFile(basename, GenerateResourceFile(**kwargs)) return basename ######################################################################## ## ## FindLocation ## ######################################################################## ORIG_EXT = {} def GetOrigExt(x): return ORIG_EXT[x] def SetOrigExt(x, v): ORIG_EXT[x] = v def CalcLocation(fn, ipath): if fn.startswith("panda3d/") and fn.endswith(".py"): return OUTPUTDIR + "/" + fn if (fn.count("/")): return fn dllext = "" target = GetTarget() if (GetOptimize() <= 2 and target == 'windows'): dllext = "_d" if (fn == "AndroidManifest.xml"): return OUTPUTDIR+"/"+fn if (fn.endswith(".cxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".I")): return CxxFindSource(fn, ipath) if (fn.endswith(".h")): return CxxFindSource(fn, ipath) if (fn.endswith(".c")): return CxxFindSource(fn, ipath) if (fn.endswith(".py")): return CxxFindSource(fn, ipath) if (fn.endswith(".yxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".lxx")): return CxxFindSource(fn, ipath) if (fn.endswith(".pdef")):return CxxFindSource(fn, ipath) if (fn.endswith(".xml")): return CxxFindSource(fn, ipath) if (fn.endswith(".egg")): return OUTPUTDIR+"/models/"+fn if (fn.endswith(".egg.pz")):return OUTPUTDIR+"/models/"+fn if (target == 'windows'): if (fn.endswith(".def")): return CxxFindSource(fn, ipath) if (fn.endswith(".rc")): return CxxFindSource(fn, ipath) if (fn.endswith(".idl")): return CxxFindSource(fn, ipath) if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".res")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".tlb")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".dll")): return OUTPUTDIR+"/bin/"+fn[:-4]+dllext+".dll" if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+".pyd" if (fn.endswith(".ocx")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".ocx" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".mll" if (fn.endswith(".dlo")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dlo" if (fn.endswith(".dli")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dli" if (fn.endswith(".dle")): return OUTPUTDIR+"/plugins/"+fn[:-4]+dllext+".dle" if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".dll" if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+dllext+".lib" if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+dllext+".lib" elif (target == 'darwin'): if (fn.endswith(".mm")): return CxxFindSource(fn, ipath) if (fn.endswith(".r")): return CxxFindSource(fn, ipath) if (fn.endswith(".plist")): return CxxFindSource(fn, ipath) if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".dylib" if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+".so" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4] if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a" if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" if (fn.endswith(".rsrc")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".app")): return OUTPUTDIR+"/bin/"+fn elif (target == 'android'): # On Android, we build the libraries into built/tmp, then copy them. if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".so" if (fn.endswith(".pyd")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".so" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so" if (fn.endswith(".exe")): return OUTPUTDIR+"/tmp/lib"+fn[:-4]+".so" if (fn.endswith(".lib")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" else: if (fn.endswith(".obj")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".o" if (fn.endswith(".dll")): return OUTPUTDIR+"/lib/"+fn[:-4]+".so" if (fn.endswith(".pyd")): return OUTPUTDIR+"/panda3d/"+fn[:-4]+".so" if (fn.endswith(".mll")): return OUTPUTDIR+"/plugins/"+fn if (fn.endswith(".plugin")):return OUTPUTDIR+"/plugins/"+fn[:-7]+dllext+".so" if (fn.endswith(".exe")): return OUTPUTDIR+"/bin/"+fn[:-4] if (fn.endswith(".lib")): return OUTPUTDIR+"/lib/"+fn[:-4]+".a" if (fn.endswith(".ilb")): return OUTPUTDIR+"/tmp/"+fn[:-4]+".a" if (fn.endswith(".dat")): return OUTPUTDIR+"/tmp/"+fn if (fn.endswith(".in")): return OUTPUTDIR+"/pandac/input/"+fn return fn def FindLocation(fn, ipath): if (GetLinkAllStatic() and (fn.endswith(".dll") or fn.endswith(".pyd"))): fn = fn[:-4] + ".lib" loc = CalcLocation(fn, ipath) base, ext = os.path.splitext(fn) ORIG_EXT[loc] = ext return loc ######################################################################## ## ## TargetAdd ## ## Makepanda maintains a list of make-targets. Each target has ## these attributes: ## ## name - the name of the file being created. ## ext - the original file extension, prior to OS-specific translation ## inputs - the names of the input files to the compiler ## deps - other input files that the target also depends on ## opts - compiler options, a catch-all category ## ## TargetAdd will create the target if it does not exist. Then, ## depending on what options you pass, it will push data onto these ## various target attributes. This is cumulative: for example, if ## you use TargetAdd to add compiler options, then use TargetAdd ## again with more compiler options, both sets of options will be ## included. ## ## TargetAdd does some automatic dependency generation on C++ files. ## It will scan these files for include-files and automatically push ## the include files onto the list of dependencies. In order to do ## this, it needs an include-file search path. So if you supply ## any C++ input, you also need to supply compiler options containing ## include-directories, or alternately, a separate ipath parameter. ## ## The main body of 'makepanda' is a long list of TargetAdd ## directives building up a giant list of make targets. Then, ## finally, the targets are run and panda is built. ## ## Makepanda's dependency system does not understand multiple ## outputs from a single build step. When a build step generates ## a primary output file and a secondary output file, it is ## necessary to trick the dependency system. Insert a dummy ## build step that "generates" the secondary output file, using ## the primary output file as an input. There is a special ## compiler option DEPENDENCYONLY that creates such a dummy ## build-step. There are two cases where dummy build steps must ## be inserted: bison generates an OBJ and a secondary header ## file, interrogate generates an IN and a secondary IGATE.OBJ. ## ######################################################################## class Target: pass TARGET_LIST = [] TARGET_TABLE = {} def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0, winrc=0): if (dummy != 0): exit("Syntax error in TargetAdd "+target) if (ipath == 0): ipath = opts if (ipath == 0): ipath = [] if (type(input) == str): input = [input] if (type(dep) == str): dep = [dep] if os.path.splitext(target)[1] == '.pyd' and PkgSkip("PYTHON"): # It makes no sense to build Python modules with python disabled. return full = FindLocation(target, [OUTPUTDIR + "/include"]) if (full not in TARGET_TABLE): t = Target() t.name = full t.inputs = [] t.deps = {} t.opts = [] TARGET_TABLE[full] = t TARGET_LIST.append(t) else: t = TARGET_TABLE[full] if opts != 0: for x in opts: if (t.opts.count(x)==0): t.opts.append(x) ipath = [OUTPUTDIR + "/tmp"] + GetListOption(ipath, "DIR:") + [OUTPUTDIR+"/include"] if input != 0: for x in input: fullinput = FindLocation(x, ipath) t.inputs.append(fullinput) # Don't re-link a library or binary if just its dependency dlls have been altered. # This should work out fine in most cases, and often reduces recompilation time. if (os.path.splitext(x)[-1] not in SUFFIX_DLL): t.deps[fullinput] = 1 (base,suffix) = os.path.splitext(x) if (SUFFIX_INC.count(suffix)): for d in CxxCalcDependencies(fullinput, ipath, []): t.deps[d] = 1 if dep != 0: for x in dep: fulldep = FindLocation(x, ipath) t.deps[fulldep] = 1 if winrc != 0 and GetTarget() == 'windows': TargetAdd(target, input=WriteResourceFile(target.split("/")[-1].split(".")[0], **winrc)) if target.endswith(".in"): if not CrossCompiling(): t.deps[FindLocation("interrogate.exe", [])] = 1 t.deps[FindLocation("dtool_have_python.dat", [])] = 1 if target.endswith(".pz") and not CrossCompiling(): t.deps[FindLocation("pzip.exe", [])] = 1