2907 lines
105 KiB
Python
2907 lines
105 KiB
Python
########################################################################
|
|
##
|
|
## 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
|