import sys
if sys.version_info.major == 2:
sys.exit("ERROR: USD does not support Python 2. Use a supported version "
"of Python 3 instead.")
import argparse
import codecs
import contextlib
import ctypes
import datetime
import fnmatch
import glob
import hashlib
import locale
import multiprocessing
import os
import platform
import re
import shlex
import shutil
import subprocess
import sys
import sysconfig
import zipfile
from urllib.request import urlopen
from shutil import which
verbosity = 1
def Print(msg):
if verbosity > 0:
print(msg)
def PrintWarning(warning):
if verbosity > 0:
print("WARNING:", warning)
def PrintStatus(status):
if verbosity >= 1:
print("STATUS:", status)
def PrintInfo(info):
if verbosity >= 2:
print("INFO:", info)
def PrintCommandOutput(output):
if verbosity >= 3:
sys.stdout.write(output)
def PrintError(error):
if verbosity >= 3 and sys.exc_info()[1] is not None:
import traceback
traceback.print_exc()
print ("ERROR:", error)
def Windows():
return platform.system() == "Windows"
def Linux():
return platform.system() == "Linux"
def MacOS():
return platform.system() == "Darwin"
if MacOS():
import apple_utils
TARGET_WASM='wasm'
TARGET_WASM64='wasm64'
def GetWasmCompilerFlags(buildTarget):
compileFlags = '-pthread --use-port=zlib'
linkerFlags = '-pthread'
if buildTarget == TARGET_WASM64:
compileFlags += ' -sMEMORY64=1'
linkerFlags += ' -sMEMORY64=1'
return (compileFlags, linkerFlags)
def GetBuildTargetDefault():
if MacOS():
return apple_utils.GetBuildTargetDefault()
else:
return ''
def GetBuildTargets():
if MacOS():
appleTargets = [str.casefold(x) for x in apple_utils.GetBuildTargets()]
appleTargets.extend([TARGET_WASM, TARGET_WASM64])
return appleTargets
elif Linux():
return [TARGET_WASM, TARGET_WASM64]
elif Windows():
return [TARGET_WASM, TARGET_WASM64]
else:
return []
def MacOSTargetEmbedded(context):
return MacOS() and apple_utils.TargetEmbeddedOS(context)
def GetLocale():
if Windows():
if sys.stdout.isatty():
return sys.stdout.encoding
else:
return "UTF-8"
return sys.stdout.encoding or locale.getdefaultlocale()[1] or "UTF-8"
def GetCommandOutput(command):
"""Executes the specified command and returns output or None."""
try:
return subprocess.check_output(
shlex.split(command),
stderr=subprocess.STDOUT).decode(GetLocale(), 'replace').strip()
except subprocess.CalledProcessError:
pass
return None
def GetXcodeDeveloperDirectory():
"""Returns the active developer directory as reported by 'xcode-select -p'.
Returns None if none is set."""
if not MacOS():
return None
return GetCommandOutput("xcode-select -p")
def GetVisualStudioCompilerAndVersion():
"""Returns a tuple containing the path to the Visual Studio compiler
and a tuple for its version, e.g. (14, 0). If the compiler is not found
or version number cannot be determined, returns None."""
if not Windows():
return None
msvcCompiler = which('cl')
if msvcCompiler:
match = re.search(
r"(\d+)\.(\d+)",
os.environ.get("VCToolsVersion", ""))
if match:
return (msvcCompiler, tuple(int(v) for v in match.groups()))
return None
def IsVisualStudioVersionOrGreater(desiredVersion):
if not Windows():
return False
msvcCompilerAndVersion = GetVisualStudioCompilerAndVersion()
if msvcCompilerAndVersion:
_, version = msvcCompilerAndVersion
return version >= desiredVersion
return False
def IsVisualStudio2026OrGreater():
VISUAL_STUDIO_2026_VERSION = (14, 50)
return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2026_VERSION)
def IsVisualStudio2022OrGreater():
VISUAL_STUDIO_2022_VERSION = (14, 30)
return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2022_VERSION)
def IsVisualStudio2019OrGreater():
VISUAL_STUDIO_2019_VERSION = (14, 20)
return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2019_VERSION)
def IsVisualStudio2017OrGreater():
VISUAL_STUDIO_2017_VERSION = (14, 1)
return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2017_VERSION)
def GetWindowsHostArch():
identifier = os.environ.get('PROCESSOR_IDENTIFIER')
if "ARMv" in identifier:
return "ARM64"
elif any(x64Arch in identifier for x64Arch in ["AMD64", "Intel64", "EM64T"]):
return "x64"
else:
raise RuntimeError("Unknown Windows host arch")
def GetPythonInfo(context):
"""Returns a tuple containing the path to the Python executable, shared
library, and include directory corresponding to the version of Python
currently running. Returns None if any path could not be determined.
This function is used to extract build information from the Python
interpreter used to launch this script. This allows us to support
having USD builds for different Python versions built on the same
machine. This is very useful, especially when developers have multiple
versions installed on their machine.
"""
if context.build_python_info:
return (context.build_python_info['PYTHON_EXECUTABLE'],
context.build_python_info['PYTHON_LIBRARY'],
context.build_python_info['PYTHON_INCLUDE_DIR'],
context.build_python_info['PYTHON_VERSION'])
pythonExecPath = sys.executable
pythonVersion = sysconfig.get_config_var("py_version_short")
def _GetPythonLibraryFilename(context):
if Windows():
return "python{version}{suffix}.lib".format(
version=sysconfig.get_config_var("py_version_nodot"),
suffix=('_d' if context.buildDebug and context.debugPython
else ''))
elif Linux():
return sysconfig.get_config_var("LDLIBRARY")
elif MacOS():
return "libpython{version}.dylib".format(
version=(sysconfig.get_config_var('LDVERSION') or
sysconfig.get_config_var('VERSION') or
pythonVersion))
else:
raise RuntimeError("Platform not supported")
pythonIncludeDir = sysconfig.get_path("include")
if not pythonIncludeDir or not os.path.isdir(pythonIncludeDir):
pythonIncludeDir = sysconfig.get_config_var("INCLUDEPY")
pythonBaseDir = sysconfig.get_config_var("installed_base")
if Windows():
pythonLibPath = os.path.join(pythonBaseDir, "libs",
_GetPythonLibraryFilename(context))
elif Linux():
pythonMultiarchSubdir = sysconfig.get_config_var("multiarchsubdir")
for pythonLibDir in (sysconfig.get_config_var("LIBDIR"),
os.path.join(pythonBaseDir, "lib")):
if pythonMultiarchSubdir:
pythonLibPath = \
os.path.join(pythonLibDir + pythonMultiarchSubdir,
_GetPythonLibraryFilename(context))
if os.path.isfile(pythonLibPath):
break
pythonLibPath = os.path.join(pythonLibDir,
_GetPythonLibraryFilename(context))
if os.path.isfile(pythonLibPath):
break
elif MacOS():
pythonLibPath = os.path.join(pythonBaseDir, "lib",
_GetPythonLibraryFilename(context))
else:
raise RuntimeError("Platform not supported")
return (pythonExecPath, pythonLibPath, pythonIncludeDir, pythonVersion)
def GetCPUCount():
try:
return multiprocessing.cpu_count()
except NotImplementedError:
return 1
def Run(cmd, logCommandOutput = True, env = None):
"""Run the specified command in a subprocess."""
PrintInfo('Running "{cmd}"'.format(cmd=cmd))
with codecs.open("log.txt", "a", "utf-8") as logfile:
logfile.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
logfile.write("\n")
logfile.write(cmd)
logfile.write("\n")
if logCommandOutput:
p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, env=env)
while True:
l = p.stdout.readline().decode(GetLocale(), 'replace')
if l:
logfile.write(l)
PrintCommandOutput(l)
elif p.poll() is not None:
break
else:
p = subprocess.Popen(shlex.split(cmd), env=env)
p.wait()
logfile.write("\n")
logfile.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
logfile.write("\n")
logfile.write("{cmd} exited with returncode {returncode}"
.format(cmd=cmd, returncode=p.returncode))
logfile.write("\n\n")
if p.returncode != 0:
if verbosity < 3:
with open("log.txt", "r") as logfile:
Print(logfile.read())
raise RuntimeError(
f"Failed to run '{cmd}' in {os.getcwd()} "
f"(exited with returncode {p.returncode}).\n\n"
f"See {os.path.abspath('log.txt')} for more details.")
@contextlib.contextmanager
def CurrentWorkingDirectory(dir):
"""Context manager that sets the current working directory to the given
directory and resets it to the original directory when closed."""
curdir = os.getcwd()
os.chdir(dir)
try: yield
finally: os.chdir(curdir)
def CopyFiles(context, src, dest):
"""Copy files like shutil.copy, but src may be a glob pattern."""
filesToCopy = glob.glob(src)
if not filesToCopy:
raise RuntimeError("File(s) to copy {src} not found".format(src=src))
instDestDir = os.path.join(context.instDir, dest)
if not os.path.isdir(instDestDir):
try:
os.mkdir(instDestDir)
except Exception as e:
raise RuntimeError(
"Unable to create {destDir}".format(destDir=instDestDir)) from e
for f in filesToCopy:
PrintCommandOutput("Copying {file} to {destDir}\n"
.format(file=f, destDir=instDestDir))
shutil.copy(f, instDestDir)
def CopyDirectory(context, srcDir, destDir):
"""Copy directory like shutil.copytree."""
instDestDir = os.path.join(context.instDir, destDir)
if os.path.isdir(instDestDir):
shutil.rmtree(instDestDir)
PrintCommandOutput("Copying {srcDir} to {destDir}\n"
.format(srcDir=srcDir, destDir=instDestDir))
shutil.copytree(srcDir, instDestDir)
def AppendCXX11ABIArg(buildFlag, context, buildArgs):
"""Append a build argument that defines _GLIBCXX_USE_CXX11_ABI
based on the settings in the context. This may either do nothing
or append an entry to buildArgs like:
<buildFlag>="-D_GLIBCXX_USE_CXX11_ABI={0, 1}"
If buildArgs contains settings for buildFlag, those settings will
be merged with the above define."""
if context.useCXX11ABI is None:
return
cxxFlags = ["-D_GLIBCXX_USE_CXX11_ABI={}".format(context.useCXX11ABI)]
splitArgs = [shlex.split(a) for a in buildArgs]
for p in [item for arg in splitArgs for item in arg]:
if p.startswith(buildFlag):
(_, _, flags) = p.partition("=")
cxxFlags.append(flags)
buildArgs.append('{flag}="{flags}"'.format(
flag=buildFlag, flags=" ".join(cxxFlags)))
def GetCMakeCacheValue(buildDir, variable):
"""Return the value of a CMake cache variable by querying the build
directory with 'cmake -N -LA <buildDir>', or None on failure."""
output = GetCommandOutput(
"cmake -N -LA {}".format(shlex.quote(buildDir)))
if output is None:
return None
for line in output.splitlines():
m = re.match(r"{}(?::[A-Z]+)?=(.*)".format(re.escape(variable)), line)
if m:
return m.group(1).strip()
return None
def RunCMake(context, force, extraArgs = None, installDir = None):
"""Invoke CMake to configure, build, and install a library whose
source code is located in the current working directory."""
if extraArgs is None:
extraArgs = []
else:
extraArgs = list(extraArgs)
instDir = installDir if installDir else context.instDir
if context.cmakeBuildArgs:
extraArgs.insert(0, context.cmakeBuildArgs)
srcDir = os.getcwd()
buildDir = os.path.join(context.buildDir, os.path.split(srcDir)[1])
if force and os.path.isdir(buildDir):
shutil.rmtree(buildDir)
if not os.path.isdir(buildDir):
os.makedirs(buildDir)
generator = context.cmakeGenerator
if generator is None and Windows():
if IsVisualStudio2026OrGreater():
generator = "Visual Studio 18 2026"
elif IsVisualStudio2022OrGreater():
generator = "Visual Studio 17 2022"
elif IsVisualStudio2019OrGreater():
generator = "Visual Studio 16 2019"
elif IsVisualStudio2017OrGreater():
generator = "Visual Studio 15 2017 Win64"
if generator is not None:
generator = '-G "{gen}"'.format(gen=generator)
if IsVisualStudio2019OrGreater() and "Visual Studio" in generator:
generator = generator + " -A " + GetWindowsHostArch()
toolset = context.cmakeToolset
if toolset is not None:
toolset = '-T "{toolset}"'.format(toolset=toolset)
osx_rpath = None
if MacOS() and not context.targetWasm:
osx_rpath = "-DCMAKE_MACOSX_RPATH=ON"
targetArch = apple_utils.GetTargetArch(context)
if context.targetNative or targetArch == apple_utils.GetHostArch():
extraArgs.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=YES')
else:
extraArgs.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO')
extraArgs.append('-DCMAKE_OSX_ARCHITECTURES={0}'.format(targetArch))
extraArgs = apple_utils.ConfigureCMakeExtraArgs(context, extraArgs)
if context.ignorePaths:
ignoredPaths = ";".join(context.ignorePaths)
extraArgs.append("-DCMAKE_IGNORE_PATH={0}".format(ignoredPaths))
extraArgs.append("-DCMAKE_IGNORE_PREFIX_PATH={0}".format(ignoredPaths))
config = "Release"
if context.buildDebug:
config = "Debug"
elif context.buildRelease:
config = "Release"
elif context.buildRelWithDebug:
config = "RelWithDebInfo"
AppendCXX11ABIArg("-DCMAKE_CXX_FLAGS", context, extraArgs)
with CurrentWorkingDirectory(buildDir):
Run(('{} '.format('emcmake.bat' if Windows() else 'emcmake') if context.targetWasm else '') +
'cmake '
'-DCMAKE_INSTALL_PREFIX="{instDir}" '
'-DCMAKE_PREFIX_PATH="{depsInstDir}" '
'-DCMAKE_BUILD_TYPE={config} '
'{osx_rpath} '
'{generator} '
'{toolset} '
'{extraArgs} '
'"{srcDir}"'
.format(instDir=instDir,
depsInstDir=context.instDir,
config=config,
srcDir=srcDir,
osx_rpath=(osx_rpath or ""),
generator=(generator or ""),
toolset=(toolset or ""),
extraArgs=(" ".join(extraArgs) if extraArgs else "")))
Run(('{} '.format('emmake.bat' if Windows() else 'emmake') if context.targetWasm else '') +
"cmake --build . --config {config} --target install -j {numJobs}"
.format(config=config, numJobs=context.numJobs))
def GetCMakeVersion():
"""
Returns the CMake version as tuple of integers (major, minor) or
(major, minor, patch) or None if an error occured while launching cmake and
parsing its output.
"""
output_string = GetCommandOutput("cmake --version")
if not output_string:
PrintWarning("Could not determine cmake version -- please install it "
"and adjust your PATH")
return None
match = re.search(r"version (\d+)\.(\d+)(\.(\d+))?", output_string)
if not match:
PrintWarning("Could not determine cmake version")
return None
major, minor, patch_group, patch = match.groups()
if patch_group is None:
return (int(major), int(minor))
else:
return (int(major), int(minor), int(patch))
def ComputeSHA256Hash(filename):
"""Returns the SHA256 hash of the specified file."""
hasher = hashlib.sha256()
with open(filename, "rb") as f:
buf = None
while buf != b'':
buf = f.read(4096)
hasher.update(buf)
return hasher.hexdigest()
def PatchFile(filename, patches, multiLineMatches=False):
"""Applies patches to the specified file. patches is a list of tuples
(old string, new string)."""
if multiLineMatches:
oldLines = [open(filename, 'r').read()]
else:
oldLines = open(filename, 'r').readlines()
newLines = oldLines
for (oldString, newString) in patches:
newLines = [s.replace(oldString, newString) for s in newLines]
if newLines != oldLines:
PrintInfo("Patching file {filename} (original in {oldFilename})..."
.format(filename=filename, oldFilename=filename + ".old"))
shutil.copy(filename, filename + ".old")
open(filename, 'w').writelines(newLines)
def DownloadFileWithCurl(url, outputFilename):
Run("curl {progress} -L -o {filename} {url}".format(
progress="-#" if verbosity >= 2 else "-s",
filename=outputFilename, url=url),
logCommandOutput=False)
def DownloadFileWithPowershell(url, outputFilename):
cmd = "powershell [Net.ServicePointManager]::SecurityProtocol = \
[Net.SecurityProtocolType]::Tls12; \"(new-object \
System.Net.WebClient).DownloadFile('{url}', '{filename}')\""\
.format(filename=outputFilename, url=url)
Run(cmd,logCommandOutput=False)
def DownloadFileWithUrllib(url, outputFilename):
r = urlopen(url)
with open(outputFilename, "wb") as outfile:
outfile.write(r.read())
def DownloadURL(url, context, force, extractDir = None,
dontExtract = None, destFileName = None,
expectedSHA256 = None):
"""Download and extract the archive file at given URL to the
source directory specified in the context.
dontExtract may be a sequence of path prefixes that will
be excluded when extracting the archive.
destFileName may be a string containing the filename where
the file will be downloaded. If unspecified, this filename
will be derived from the URL.
expectedSHA256 may be a string containing the expected SHA256
checksum for the downloaded file. If provided, this function
will raise a RuntimeError if the SHA256 checksum computed from
the file does not match.
Returns the absolute path to the directory where files have
been extracted."""
with CurrentWorkingDirectory(context.srcDir):
if destFileName:
filename = destFileName
else:
filename = url.split("/")[-1]
if force and os.path.exists(filename):
os.remove(filename)
if os.path.exists(filename):
PrintInfo("{0} already exists, skipping download"
.format(os.path.abspath(filename)))
else:
PrintInfo("Downloading {0} to {1}"
.format(url, os.path.abspath(filename)))
maxRetries = 5
lastError = None
tmpFilename = filename + ".tmp"
if os.path.exists(tmpFilename):
os.remove(tmpFilename)
for i in range(maxRetries):
try:
context.downloader(url, tmpFilename)
break
except Exception as e:
PrintCommandOutput("Retrying download due to error: {err}\n"
.format(err=e))
lastError = e
else:
errorMsg = str(lastError)
if "SSL: TLSV1_ALERT_PROTOCOL_VERSION" in errorMsg:
errorMsg += ("\n\n"
"Your OS or version of Python may not support "
"TLS v1.2+, which is required for downloading "
"files from certain websites. This support "
"was added in Python 2.7.9."
"\n\n"
"You can use curl to download dependencies "
"by installing it in your PATH and re-running "
"this script.")
raise RuntimeError("Failed to download {url}: {err}"
.format(url=url, err=errorMsg))
if expectedSHA256:
computedSHA256 = ComputeSHA256Hash(tmpFilename)
if computedSHA256 != expectedSHA256:
raise RuntimeError(
"Unexpected SHA256 for {url}: got {computed}, "
"expected {expected}".format(
url=url, computed=computedSHA256,
expected=expectedSHA256))
shutil.move(tmpFilename, filename)
archive = None
rootDir = None
members = None
try:
if zipfile.is_zipfile(filename):
archive = zipfile.ZipFile(filename)
if extractDir:
rootDir = extractDir
else:
rootDir = archive.namelist()[0].split('/')[0]
if dontExtract != None:
members = (m for m in archive.namelist()
if not any((fnmatch.fnmatch(m, p)
for p in dontExtract)))
else:
raise RuntimeError("unrecognized archive file type")
with archive:
extractedPath = os.path.abspath(rootDir)
if force and os.path.isdir(extractedPath):
shutil.rmtree(extractedPath)
if os.path.isdir(extractedPath):
PrintInfo("Directory {0} already exists, skipping extract"
.format(extractedPath))
else:
PrintInfo("Extracting archive to {0}".format(extractedPath))
tmpExtractedPath = os.path.abspath("extract_dir")
if os.path.isdir(tmpExtractedPath):
shutil.rmtree(tmpExtractedPath)
archive.extractall(tmpExtractedPath, members=members)
shutil.move(os.path.join(tmpExtractedPath, rootDir),
extractedPath)
shutil.rmtree(tmpExtractedPath)
return extractedPath
except Exception as e:
shutil.move(filename, filename + ".bad")
raise RuntimeError("Failed to extract archive {filename}: {err}"
.format(filename=filename, err=e))
AllDependencies = list()
AllDependenciesByName = dict()
class Dependency(object):
def __init__(self, name, installer, *files):
self.name = name
self.installer = installer
self.filesToCheck = files
AllDependencies.append(self)
AllDependenciesByName.setdefault(name.lower(), self)
def Exists(self, context):
return any([os.path.isfile(os.path.join(context.instDir, f))
for f in self.filesToCheck])
class PythonDependency(object):
def __init__(self, name, getInstructions, moduleNames):
self.name = name
self.getInstructions = getInstructions
self.moduleNames = moduleNames
def Exists(self, context):
for moduleName in self.moduleNames:
try:
pyModule = __import__(moduleName)
return True
except:
pass
return False
def AnyPythonDependencies(deps):
return any([type(d) is PythonDependency for d in deps])
ZLIB_URL = "https://github.com/madler/zlib/archive/v1.3.2.zip"
def InstallZlib(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(ZLIB_URL, context, force)):
extraArgs = [
"-DZLIB_BUILD_TESTING=OFF",
]
RunCMake(context, force, extraArgs + buildArgs)
ZLIB = Dependency("zlib", InstallZlib, "include/zlib.h")
BOOST_VERSION_FILES = [
"include/boost/version.hpp",
"include/boost-1_80/boost/version.hpp",
"include/boost-1_82/boost/version.hpp",
"include/boost-1_86/boost/version.hpp"
]
def InstallBoost_Helper(context, force, buildArgs):
if IsVisualStudio2026OrGreater():
BOOST_VERSION = (1, 90, 0)
BOOST_SHA256 = "bdc79f179d1a4a60c10fe764172946d0eeafad65e576a8703c4d89d49949973c"
if IsVisualStudio2022OrGreater():
BOOST_VERSION = (1, 86, 0)
BOOST_SHA256 = "cd20a5694e753683e1dc2ee10e2d1bb11704e65893ebcc6ced234ba68e5d8646"
elif MacOS():
BOOST_VERSION = (1, 82, 0)
BOOST_SHA256 = "f7c9e28d242abcd7a2c1b962039fcdd463ca149d1883c3a950bbcc0ce6f7c6d9"
else:
BOOST_VERSION = (1, 80, 0)
BOOST_SHA256 = "e34756f63abe8ac34b35352743f17d061fcc825969a2dd8458264edb38781782"
dontExtract = [
"*/doc/*",
"*/libs/*/doc/*",
"*/libs/wave/test/testwave/testfiles/utf8-test-*"
]
major, minor, patch = BOOST_VERSION
version = f"{major}.{minor}.{patch}"
filename = f"boost_{major}_{minor}_{patch}.zip"
urls = [
f"https://sourceforge.net/projects/boost/files/boost/{version}/{filename}/download",
f"https://archives.boost.io/release/{version}/source/{filename}"
]
sourceDir = None
for url in urls:
try:
sourceDir = DownloadURL(url, context, force,
dontExtract=dontExtract,
destFileName=filename,
expectedSHA256=BOOST_SHA256)
break
except Exception as e:
PrintWarning(str(e))
if url != urls[-1]:
PrintWarning("Trying alternative sources")
else:
raise RuntimeError("Failed to download boost")
with CurrentWorkingDirectory(sourceDir):
if Windows():
bootstrap = "bootstrap.bat"
else:
bootstrap = "./bootstrap.sh"
Run('chmod +x ' + bootstrap)
Run('chmod +x ./tools/build/src/engine/build.sh')
bootstrapCmd = '{bootstrap} --prefix="{instDir}"'.format(
bootstrap=bootstrap, instDir=context.instDir)
macOSArch = ""
if MacOS() and not context.targetWasm:
if apple_utils.GetTargetArch(context) == \
apple_utils.TARGET_X86:
macOSArch = "-arch {0}".format(apple_utils.TARGET_X86)
elif apple_utils.GetTargetArch(context) == \
apple_utils.GetTargetArmArch():
macOSArch = "-arch {0}".format(
apple_utils.GetTargetArmArch())
elif context.targetUniversal:
(primaryArch, secondaryArch) = \
apple_utils.GetTargetArchPair(context)
macOSArch="-arch {0} -arch {1}".format(
primaryArch, secondaryArch)
if macOSArch:
bootstrapCmd += " cxxflags=\"{0} -std=c++17 -stdlib=libc++\" " \
" cflags=\"{0}\" " \
" linkflags=\"{0}\"".format(macOSArch)
bootstrapCmd += " --with-toolset=clang"
Run(bootstrapCmd)
num_procs = min(64, context.numJobs)
boostBuildVariant = "profile"
if context.buildDebug:
boostBuildVariant= "debug"
elif context.buildRelease:
boostBuildVariant= "release"
elif context.buildRelWithDebug:
boostBuildVariant= "profile"
b2_settings = [
'--prefix="{instDir}"'.format(instDir=context.instDir),
'--build-dir="{buildDir}"'.format(buildDir=context.buildDir),
'-j{procs}'.format(procs=num_procs),
'address-model=64',
'link=shared',
'runtime-link=shared',
'threading=multi',
'variant={variant}'.format(variant=boostBuildVariant),
'--with-atomic',
'--with-regex'
]
if context.buildOIIO:
b2_settings.append("--with-date_time")
if context.buildOIIO or context.enableOpenVDB:
b2_settings.append("--with-chrono")
b2_settings.append("--with-system")
b2_settings.append("--with-thread")
if context.enableOpenVDB:
b2_settings.append("--with-iostreams")
b2_settings.append("-sNO_BZIP2=1")
if context.buildOIIO:
b2_settings.append("--with-filesystem")
if force:
b2_settings.append("-a")
if Windows():
if context.cmakeToolset == "v145":
b2_settings.append("toolset=msvc-14.5")
elif context.cmakeToolset == "v143":
b2_settings.append("toolset=msvc-14.3")
elif context.cmakeToolset == "v142":
b2_settings.append("toolset=msvc-14.2")
elif context.cmakeToolset == "v141":
b2_settings.append("toolset=msvc-14.1")
elif IsVisualStudio2026OrGreater():
b2_settings.append("toolset=msvc-14.5")
elif IsVisualStudio2022OrGreater():
b2_settings.append("toolset=msvc-14.3")
elif IsVisualStudio2019OrGreater():
b2_settings.append("toolset=msvc-14.2")
elif IsVisualStudio2017OrGreater():
b2_settings.append("toolset=msvc-14.1")
if MacOS():
b2_settings.append("toolset=clang")
b2_settings.append(f"define=BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0")
if macOSArch:
b2_settings.append("cxxflags=\"{0} -std=c++17 -stdlib=libc++\"".format(macOSArch))
b2_settings.append("cflags=\"{0}\"".format(macOSArch))
b2_settings.append("linkflags=\"{0}\"".format(macOSArch))
if context.buildDebug:
b2_settings.append("--debug-configuration")
b2_settings += buildArgs
AppendCXX11ABIArg("cxxflags", context, b2_settings)
b2 = "b2" if Windows() else "./b2"
Run('{b2} {options} install'
.format(b2=b2, options=" ".join(b2_settings)))
def InstallBoost(context, force, buildArgs):
try:
InstallBoost_Helper(context, force, buildArgs)
except:
for versionHeader in [
os.path.join(context.instDir, f) for f in BOOST_VERSION_FILES
]:
if os.path.isfile(versionHeader):
try: os.remove(versionHeader)
except: pass
raise
BOOST = Dependency("boost", InstallBoost, *BOOST_VERSION_FILES)
ONETBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2021.12.0.zip"
def InstallOneTBB(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(ONETBB_URL, context, force)):
cmakeOptions = ['-DTBB_TEST=OFF', '-DTBB_STRICT=OFF']
if context.targetWasm:
compileFlags, _ = GetWasmCompilerFlags(context.buildTarget)
cmakeOptions += [
'-DBUILD_SHARED_LIBS=OFF',
'-DCMAKE_CXX_FLAGS="{}"'.format(compileFlags),
'-DCMAKE_C_FLAGS="{}"'.format(compileFlags)
]
cmakeOptions += buildArgs
RunCMake(context, force, cmakeOptions)
ONETBB = Dependency("oneTBB", InstallOneTBB, "include/oneapi/tbb.h")
if Windows():
TBB_URL = "https://github.com/oneapi-src/oneTBB/releases/download/v2020.3/tbb-2020.3-win.zip"
TBB_ROOT_DIR_NAME = "tbb"
elif MacOS():
TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2020.3.zip"
else:
TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2020.3.1.zip"
def InstallTBB(context, force, buildArgs):
if context.targetWasm:
raise RuntimeError("OneTBB is required for WebAssembly builds.")
elif Windows():
InstallTBB_Windows(context, force, buildArgs)
elif MacOS():
InstallTBB_MacOS(context, force, buildArgs)
else:
InstallTBB_Linux(context, force, buildArgs)
def InstallTBB_Windows(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force,
TBB_ROOT_DIR_NAME)):
if buildArgs:
PrintWarning("Ignoring build arguments {}, TBB is "
"not built from source on this platform."
.format(buildArgs))
CopyFiles(context, "bin\\intel64\\vc14\\*.*", "bin")
CopyFiles(context, "lib\\intel64\\vc14\\*.*", "lib")
CopyDirectory(context, "include\\serial", "include\\serial")
CopyDirectory(context, "include\\tbb", "include\\tbb")
def InstallTBB_MacOS(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force)):
PatchFile("build/macos.clang.inc",
[("-m64",
"-m64 -arch {0}".format(apple_utils.TARGET_X86)),
("ifeq ($(arch),$(filter $(arch),armv7 armv7s arm64))",
"ifeq ($(arch),$(filter $(arch),armv7 armv7s {0}))"
.format(apple_utils.GetTargetArmArch()))])
if (MacOSTargetEmbedded(context) and
context.buildTarget != apple_utils.TARGET_IOS):
target_patches, clang_patches = apple_utils.GetTBBPatches(context)
shutil.copy(
src="build/ios.macos.inc",
dst=f"build/{context.buildTarget.lower()}.macos.inc")
PatchFile(
f"build/{context.buildTarget.lower()}.macos.inc",
target_patches)
shutil.copy(src="build/macos.clang.inc",
dst=f"build/{context.buildTarget.lower()}.clang.inc")
PatchFile(
f"build/{context.buildTarget.lower()}.clang.inc",
clang_patches)
primaryArch, secondaryArch = apple_utils.GetTargetArchPair(context)
target_patches, clang_patches = apple_utils.GetTBBPatches(context)
if target_patches:
shutil.copy(src="build/ios.macos.inc",
dst=f"build/{context.buildTarget.lower()}.macos.inc")
PatchFile(f"build/{context.buildTarget.lower()}.macos.inc",
target_patches)
if clang_patches:
shutil.copy(src="build/macos.clang.inc",
dst=f"build/{context.buildTarget.lower()}.clang.inc")
PatchFile(f"build/{context.buildTarget.lower()}.clang.inc",
clang_patches)
(primaryArch, secondaryArch) = apple_utils.GetTargetArchPair(context)
if (primaryArch == apple_utils.TARGET_X86):
primaryArch = "intel64"
if (secondaryArch == apple_utils.TARGET_X86):
secondaryArch = "intel64"
def _RunBuild(arch):
if not arch:
return
env = os.environ.copy()
if MacOSTargetEmbedded(context):
env["SDKROOT"] = apple_utils.GetSDKRoot(context)
buildArgs.extend([
'compiler=clang',
'arch=arm64',
'extra_inc=big_iron.inc',
f'target={context.buildTarget.lower()}',
])
elif context.buildAppleFramework:
buildArgs.append('extra_inc=big_iron.inc')
makeTBBCmd = 'make -j{procs} arch={arch} {buildArgs}'.format(
arch=arch, procs=context.numJobs,
buildArgs=" ".join(buildArgs))
Run(makeTBBCmd, env=env)
_RunBuild(primaryArch)
_RunBuild(secondaryArch)
if "2020" in TBB_URL:
PatchFile("Makefile", [("release", "debug")])
_RunBuild(primaryArch)
_RunBuild(secondaryArch)
if context.targetUniversal:
x86Files = glob.glob(os.getcwd() +
"/build/*intel64*_release/libtbb*.*")
armFiles = glob.glob(os.getcwd() +
"/build/*{0}*_release/libtbb*.*".format(
apple_utils.GetTargetArmArch()))
libNames = [os.path.basename(x) for x in x86Files]
x86Dir = os.path.dirname(x86Files[0])
armDir = os.path.dirname(armFiles[0])
apple_utils.CreateUniversalBinaries(context, libNames, x86Dir, armDir)
x86Files = glob.glob(
os.getcwd() + "/build/*intel64*_debug/libtbb*.*")
armFiles = glob.glob(
os.getcwd() + "/build/*{0}*_debug/libtbb*.*".format(
apple_utils.GetTargetArmArch()))
if x86Files and armFiles:
libNames = [os.path.basename(x) for x in x86Files]
x86Dir = os.path.dirname(x86Files[0])
armDir = os.path.dirname(armFiles[0])
apple_utils.CreateUniversalBinaries(context, libNames, x86Dir, armDir)
else:
CopyFiles(context, "build/*_release/libtbb*.*", "lib")
CopyFiles(context, "build/*_debug/libtbb*.*", "lib")
CopyDirectory(context, "include/serial", "include/serial")
CopyDirectory(context, "include/tbb", "include/tbb")
def InstallTBB_Linux(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force)):
AppendCXX11ABIArg("CXXFLAGS", context, buildArgs)
makeTBBCmd = 'make -j{procs} {buildArgs}'.format(
procs=context.numJobs,
buildArgs=" ".join(buildArgs))
Run(makeTBBCmd)
PatchFile("Makefile", [("release", "debug")])
Run(makeTBBCmd)
CopyFiles(context, "build/*_release/libtbb*.*", "lib")
CopyFiles(context, "build/*_debug/libtbb*.*", "lib")
CopyDirectory(context, "include/serial", "include/serial")
CopyDirectory(context, "include/tbb", "include/tbb")
TBB = Dependency("TBB", InstallTBB, "include/tbb/tbb.h")
JPEG_URL = "https://github.com/libjpeg-turbo/libjpeg-turbo/archive/3.1.4.1.zip"
def InstallJPEG(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(JPEG_URL, context, force)):
extraJPEGArgs = [
"-DWITH_TOOLS=FALSE"
]
if MacOS() and context.targetUniversal:
PatchFile("CMakeLists.txt",
[('set(COUNT 1)',
'if(FALSE)\n' +
'set(COUNT 1)'),
(' math(EXPR COUNT "${COUNT}+1")\n' +
'endforeach()',
' math(EXPR COUNT "${COUNT}+1")\n' +
'endforeach()\n' +
'endif()')],
multiLineMatches=True)
extraJPEGArgs.append("-DWITH_SIMD=FALSE")
elif not which("nasm"):
extraJPEGArgs.append("-DWITH_SIMD=FALSE")
extraJPEGArgs.append("-DCMAKE_POLICY_VERSION_MINIMUM=3.5")
RunCMake(context, force, extraJPEGArgs + buildArgs)
JPEG = Dependency("JPEG", InstallJPEG, "include/jpeglib.h")
TIFF_URL = "https://gitlab.com/libtiff/libtiff/-/archive/v4.7.1/libtiff-v4.7.1.zip"
def InstallTIFF(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(TIFF_URL, context, force)):
extraArgs = [
"-Dtiff-tools=OFF",
"-Dtiff-tests=OFF",
"-Dtiff-contrib=OFF",
"-Dtiff-docs=OFF"
]
if MacOS() or Windows():
extraArgs.append("-Dld-version-script=OFF")
extraArgs.append("-DCMAKE_POLICY_VERSION_MINIMUM=3.5")
RunCMake(context, force, extraArgs + buildArgs)
TIFF = Dependency("TIFF", InstallTIFF, "include/tiff.h")
PNG_URL = "https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.47.zip"
def InstallPNG(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(PNG_URL, context, force)):
macArgs = ["-DPNG_FRAMEWORK=OFF"]
if MacOS() and apple_utils.IsTargetArm(context):
macArgs += ["-DCMAKE_C_FLAGS=\"-DPNG_ARM_NEON_OPT=0\""]
RunCMake(context, force, buildArgs + macArgs)
PNG = Dependency("PNG", InstallPNG, "include/png.h")
OPENEXR_URL = "https://github.com/AcademySoftwareFoundation/openexr/archive/refs/tags/v3.1.13.zip"
def InstallOpenEXR(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(OPENEXR_URL, context, force)):
RunCMake(context, force,
['-DOPENEXR_INSTALL_TOOLS=OFF',
'-DOPENEXR_INSTALL_EXAMPLES=OFF',
'-DOPENEXR_FORCE_INTERNAL_IMATH=ON',
'-DBUILD_TESTING=OFF'] + buildArgs)
OPENEXR = Dependency("OpenEXR", InstallOpenEXR, "include/OpenEXR/ImfVersion.h")
PTEX_URL = "https://github.com/wdas/ptex/archive/refs/tags/v2.4.2.zip"
PTEX_VERSION = "v2.4.2"
def InstallPtex(context, force, buildArgs):
cmakeOptions = [
'-DBUILD_TESTING=OFF',
'-DPTEX_BUILD_STATIC_LIBS=OFF',
'-DPTEX_VER={v}'.format(v=PTEX_VERSION)
]
cmakeOptions += buildArgs
with CurrentWorkingDirectory(DownloadURL(PTEX_URL, context, force)):
RunCMake(context, force, cmakeOptions)
PTEX = Dependency("Ptex", InstallPtex, "include/PtexVersion.h")
BLOSC_URL = "https://github.com/Blosc/c-blosc/archive/v1.20.1.zip"
if MacOS():
BLOSC_URL = "https://github.com/Blosc/c-blosc/archive/v1.21.6.zip"
def InstallBLOSC(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(BLOSC_URL, context, force)):
macArgs = ["-DPREFER_EXTERNAL_ZLIB=ON"]
if MacOS() and apple_utils.IsTargetArm(context):
macArgs += ["-DDEACTIVATE_SSE2=ON"]
RunCMake(context, force, buildArgs + macArgs)
BLOSC = Dependency("Blosc", InstallBLOSC, "include/blosc.h")
OPENVDB_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v10.1.0.zip"
def InstallOpenVDB(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(OPENVDB_URL, context, force)):
PatchFile("openvdb/openvdb/tree/NodeManager.h",
[("OpT::template eval", "OpT::eval")])
PatchFile("openvdb/openvdb/tools/VelocityFields.h",
[("BOOST_STATIC_ASSERT(OrderRK <= 4);",
"static_assert(OrderRK <= 4);")])
extraArgs = [
'-DOPENVDB_BUILD_PYTHON_MODULE=OFF',
'-DOPENVDB_BUILD_BINARIES=OFF',
'-DOPENVDB_BUILD_UNITTESTS=OFF'
]
extraArgs.append('-DBoost_NO_SYSTEM_PATHS=ON')
extraArgs.append('-DBLOSC_ROOT="{instDir}"'
.format(instDir=context.instDir))
extraArgs.append('-DTBB_ROOT="{instDir}"'
.format(instDir=context.instDir))
extraArgs.append('-DILMBASE_ROOT="{instDir}"'
.format(instDir=context.instDir))
extraArgs += buildArgs
RunCMake(context, force, extraArgs)
OPENVDB = Dependency("OpenVDB", InstallOpenVDB, "include/openvdb/openvdb.h")
OIIO_URL = "https://github.com/OpenImageIO/oiio/archive/refs/tags/v2.5.16.0.zip"
def InstallOpenImageIO(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(OIIO_URL, context, force)):
buildOIIOTools = 'ON' if (context.buildUsdImaging
and context.buildTests
and not context.targetWasm) else 'OFF'
extraArgs = ['-DOIIO_BUILD_TOOLS={}'.format(buildOIIOTools),
'-DOIIO_BUILD_TESTS=OFF',
'-DBUILD_DOCS=OFF',
'-DUSE_PYTHON=OFF',
'-DSTOP_ON_WARNING=OFF']
extraArgs.append('-DOPENEXR_ROOT="{instDir}"'
.format(instDir=context.instDir))
if not context.enablePtex:
extraArgs.append('-DUSE_PTEX=OFF')
extraArgs.append('-DBoost_NO_SYSTEM_PATHS=ON')
extraArgs.append('-DBoost_NO_BOOST_CMAKE=OFF')
extraArgs.append('-DCMAKE_DEBUG_POSTFIX=""')
if MacOS():
extraArgs.append('-DIGNORE_HOMEBREWED_DEPS=ON')
extraArgs += buildArgs
RunCMake(context, force, extraArgs)
OPENIMAGEIO = Dependency("OpenImageIO", InstallOpenImageIO,
"include/OpenImageIO/oiioversion.h")
if MacOS():
OCIO_URL = "https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.4.2.zip"
else:
OCIO_URL = "https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.zip"
def InstallOpenColorIO(context, force, buildArgs):
ocioDestFileName = os.path.splitext(os.path.basename(OCIO_URL))[0]
with CurrentWorkingDirectory(DownloadURL(OCIO_URL, context, force,
destFileName=ocioDestFileName)):
extraArgs = ['-DOCIO_BUILD_APPS=OFF',
'-DOCIO_BUILD_DOCS=OFF',
'-DOCIO_BUILD_TESTS=OFF',
'-DOCIO_BUILD_GPU_TESTS=OFF',
'-DOCIO_BUILD_PYTHON=OFF']
if MacOS():
if apple_utils.IsTargetArm(context):
extraArgs.append('-DOCIO_USE_SSE=OFF')
extraArgs += buildArgs
RunCMake(context, force, extraArgs)
OPENCOLORIO = Dependency("OpenColorIO", InstallOpenColorIO,
"include/OpenColorIO/OpenColorABI.h")
OPENSUBDIV_URL = "https://github.com/PixarAnimationStudios/OpenSubdiv/archive/v3_6_1.zip"
def InstallOpenSubdiv(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(OPENSUBDIV_URL, context, force)):
extraArgs = [
'-DNO_EXAMPLES=ON',
'-DNO_TUTORIALS=ON',
'-DNO_REGRESSION=ON',
'-DNO_DOC=ON',
'-DNO_OMP=ON',
'-DNO_CUDA=ON',
'-DNO_OPENCL=ON',
'-DNO_DX=ON',
'-DNO_TESTS=ON',
'-DNO_GLEW=ON',
'-DNO_GLFW=ON',
'-DNO_PTEX=ON',
'-DNO_TBB=ON',
]
if context.targetWasm:
compileFlags, _ = GetWasmCompilerFlags(context.buildTarget)
extraArgs.append('-DBUILD_SHARED_LIB=OFF')
extraArgs.append('-DCMAKE_CXX_FLAGS="{}"'.format(compileFlags))
extraArgs.append('-DCMAKE_C_FLAGS="{}"'.format(compileFlags))
extraArgs.append('-DNO_METAL=ON')
extraArgs.append(
'-DOSD_PATCH_SHADER_SOURCE_GLSL=ON'
)
if MacOS():
extraArgs.extend([
'-DNO_OPENGL=ON',
'-DOSD_PATCH_SHADER_SOURCE_MSL=ON',
])
if context.buildAppleFramework:
extraArgs.append("-DBUILD_SHARED_LIBS=OFF")
extraArgs += buildArgs
RunCMake(context, force, extraArgs)
OPENSUBDIV = Dependency("OpenSubdiv", InstallOpenSubdiv,
"include/opensubdiv/version.h")
def GetJinja2Instructions():
return ('Jinja2 is not installed. If you have pip '
'installed, run "pip install Jinja2" to '
'install it, then re-run this script.\n'
'If Jinja2 is already installed, you may need to '
'update your PYTHONPATH to indicate where it is '
'located.')
JINJA2 = PythonDependency("Jinja2", GetJinja2Instructions,
moduleNames=["jinja2"])
def GetPyOpenGLInstructions():
return ('PyOpenGL is not installed. If you have pip '
'installed, run "pip install PyOpenGL" to '
'install it, then re-run this script.\n'
'If PyOpenGL is already installed, you may need to '
'update your PYTHONPATH to indicate where it is '
'located.')
PYOPENGL = PythonDependency("PyOpenGL", GetPyOpenGLInstructions,
moduleNames=["OpenGL"])
def GetPySideInstructions():
if MacOS():
return ('PySide6 is not installed. If you have pip '
'installed, run "pip install PySide6" '
'to install it, then re-run this script.\n'
'If PySide6 is already installed, you may need to '
'update your PYTHONPATH to indicate where it is '
'located.')
else:
return ('PySide2 or PySide6 are not installed. If you have pip '
'installed, run "pip install PySide2" '
'to install it, then re-run this script.\n'
'If PySide2 is already installed, you may need to '
'update your PYTHONPATH to indicate where it is '
'located.')
PYSIDE = PythonDependency("PySide", GetPySideInstructions,
moduleNames=["PySide2", "PySide6"])
ALEMBIC_URL = "https://github.com/alembic/alembic/archive/refs/tags/1.8.5.zip"
def InstallAlembic(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(ALEMBIC_URL, context, force)):
cmakeOptions = ['-DUSE_BINARIES=OFF', '-DUSE_TESTS=OFF', '-DUSE_HDF5=OFF']
cmakeOptions += buildArgs
RunCMake(context, force, cmakeOptions)
ALEMBIC = Dependency("Alembic", InstallAlembic, "include/Alembic/Abc/Base.h")
DRACO_URL = "https://github.com/google/draco/archive/refs/tags/1.5.6.zip"
def InstallDraco(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(DRACO_URL, context, force)):
cmakeOptions = [
'-DBUILD_SHARED_LIBS=ON',
]
cmakeOptions += buildArgs
RunCMake(context, force, cmakeOptions)
DRACO = Dependency("Draco", InstallDraco, "include/draco/compression/decode.h")
MATERIALX_URL = "https://github.com/AcademySoftwareFoundation/MaterialX/archive/v1.39.5.zip"
def InstallMaterialX(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)):
cmakeOptions = ['-DMATERIALX_BUILD_TESTS=OFF']
if MacOSTargetEmbedded(context):
cmakeOptions.extend([
'-DMATERIALX_BUILD_GEN_MSL=ON',
'-DMATERIALX_BUILD_GEN_GLSL=ON',
'-DMATERIALX_BUILD_IOS=ON'])
PatchFile("CMakeLists.txt",
[(' set(MATERIALX_BUILD_GEN_GLSL OFF)',
' set(MATERIALX_BUILD_GEN_GLSL ON)'),
(' if (MATERIALX_BUILD_GEN_GLSL)\n' +
' add_subdirectory(source/MaterialXRenderGlsl)\n' +
' endif()',
' if (MATERIALX_BUILD_GEN_GLSL AND NOT MATERIALX_BUILD_IOS)\n' +
' add_subdirectory(source/MaterialXRenderGlsl)\n' +
' endif()')
], multiLineMatches=True)
if MacOS() and context.buildAppleFramework:
cmakeOptions.extend(["-DMATERIALX_BUILD_SHARED_LIBS=OFF"])
else:
cmakeOptions.extend(["-DMATERIALX_BUILD_SHARED_LIBS=ON"])
cmakeOptions += buildArgs
RunCMake(context, force, cmakeOptions)
MATERIALX = Dependency("MaterialX", InstallMaterialX, "include/MaterialXCore/Library.h")
def InstallEmbree(context, force, buildArgs):
EMBREE_URL = "https://github.com/RenderKit/embree/archive/refs/tags/v4.3.3.zip"
with CurrentWorkingDirectory(DownloadURL(EMBREE_URL, context, force)):
extraArgs = [
'-DTBB_ROOT="{instDir}"'.format(instDir=context.instDir),
'-DEMBREE_TUTORIALS=OFF',
'-DEMBREE_ISPC_SUPPORT=OFF'
]
if MacOS():
PatchFile("kernels/CMakeLists.txt",
[("DISABLE_STACK_PROTECTOR_FOR_INTERSECTORS(${EMBREE_LIBRARY_FILES})\n"
"ADD_LIBRARY(embree ${EMBREE_LIB_TYPE} ${EMBREE_LIBRARY_FILES})\n",
"DISABLE_STACK_PROTECTOR_FOR_INTERSECTORS(${EMBREE_LIBRARY_FILES})\n"
"ADD_LIBRARY(embree ${EMBREE_LIB_TYPE} ${EMBREE_LIBRARY_FILES})\n"
"target_compile_options(embree PRIVATE -Wno-unused-but-set-variable)\n")],
multiLineMatches=True,
)
if context.targetUniversal:
extraArgs += [
'-DEMBREE_MAX_ISA=NEON',
'-DEMBREE_ISA_NEON=ON']
extraArgs += buildArgs
RunCMake(context, force, extraArgs)
EMBREE = Dependency("Embree", InstallEmbree,
"include/embree4/rtcore.h")
def InstallUSD(context, force, buildArgs):
with CurrentWorkingDirectory(context.usdSrcDir):
extraArgs = []
extraArgs.append('-DPXR_PREFER_SAFETY_OVER_SPEED={}'
.format('ON' if context.safetyFirst else 'OFF'))
extraArgs.append(f"-DPXR_ENABLE_COMPILER_CACHE={'ON' if context.useCompilerCache else 'OFF'}")
if context.buildOneTBB:
extraArgs.append('-DPXR_FIND_TBB_IN_CONFIG=ON')
else:
extraArgs.append('-DPXR_FIND_TBB_IN_CONFIG=OFF')
if context.buildPython:
extraArgs.append('-DPXR_ENABLE_PYTHON_SUPPORT=ON')
if context.pythonInstallDir is not None:
extraArgs.append('-DPXR_PYTHON_INSTALL_DIR={}'
.format(context.pythonInstallDir))
if context.buildDebug and context.debugPython:
extraArgs.append('-DPXR_USE_DEBUG_PYTHON=ON')
else:
extraArgs.append('-DPXR_USE_DEBUG_PYTHON=OFF')
pythonInfo = GetPythonInfo(context)
if pythonInfo:
extraArgs.append('-DPython3_EXECUTABLE="{pyExecPath}"'
.format(pyExecPath=pythonInfo[0]))
extraArgs.append('-DPython3_LIBRARY="{pyLibPath}"'
.format(pyLibPath=pythonInfo[1]))
extraArgs.append('-DPython3_INCLUDE_DIR="{pyIncPath}"'
.format(pyIncPath=pythonInfo[2]))
else:
extraArgs.append('-DPXR_ENABLE_PYTHON_SUPPORT=OFF')
if context.buildShared:
extraArgs.append('-DBUILD_SHARED_LIBS=ON')
elif context.buildMonolithic:
extraArgs.append('-DPXR_BUILD_MONOLITHIC=ON')
if context.buildDebug:
extraArgs.append('-DTBB_USE_DEBUG_BUILD=ON')
else:
extraArgs.append('-DTBB_USE_DEBUG_BUILD=OFF')
if context.buildDocs:
extraArgs.append('-DPXR_BUILD_DOCUMENTATION=ON')
else:
extraArgs.append('-DPXR_BUILD_DOCUMENTATION=OFF')
if context.buildHtmlDocs:
extraArgs.append('-DPXR_BUILD_HTML_DOCUMENTATION=ON')
else:
extraArgs.append('-DPXR_BUILD_HTML_DOCUMENTATION=OFF')
if context.buildPythonDocs:
extraArgs.append('-DPXR_BUILD_PYTHON_DOCUMENTATION=ON')
else:
extraArgs.append('-DPXR_BUILD_PYTHON_DOCUMENTATION=OFF')
if context.buildTests:
extraArgs.append('-DPXR_BUILD_TESTS=ON')
else:
extraArgs.append('-DPXR_BUILD_TESTS=OFF')
if context.buildExamples:
extraArgs.append('-DPXR_BUILD_EXAMPLES=ON')
else:
extraArgs.append('-DPXR_BUILD_EXAMPLES=OFF')
if context.buildTutorials:
extraArgs.append('-DPXR_BUILD_TUTORIALS=ON')
else:
extraArgs.append('-DPXR_BUILD_TUTORIALS=OFF')
if context.buildTools:
extraArgs.append('-DPXR_BUILD_USD_TOOLS=ON')
else:
extraArgs.append('-DPXR_BUILD_USD_TOOLS=OFF')
if context.buildUsdValidation:
extraArgs.append('-DPXR_BUILD_USD_VALIDATION=ON')
else:
extraArgs.append('-DPXR_BUILD_USD_VALIDATION=OFF')
if context.buildImaging:
extraArgs.append('-DPXR_BUILD_IMAGING=ON')
if context.enablePtex:
extraArgs.append('-DPXR_ENABLE_PTEX_SUPPORT=ON')
else:
extraArgs.append('-DPXR_ENABLE_PTEX_SUPPORT=OFF')
if context.enableOpenVDB:
extraArgs.append('-DPXR_ENABLE_OPENVDB_SUPPORT=ON')
else:
extraArgs.append('-DPXR_ENABLE_OPENVDB_SUPPORT=OFF')
if context.buildEmbree:
extraArgs.append('-DPXR_BUILD_EMBREE_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_EMBREE_PLUGIN=OFF')
if context.buildPrman:
if context.prmanLocation:
extraArgs.append('-DRENDERMAN_LOCATION="{location}"'
.format(location=context.prmanLocation))
extraArgs.append('-DPXR_BUILD_PRMAN_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_PRMAN_PLUGIN=OFF')
if MacOS():
if context.buildImageIO:
extraArgs.append('-DPXR_BUILD_IMAGEIO_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_IMAGEIO_PLUGIN=OFF')
if context.buildOIIO:
extraArgs.append('-DPXR_BUILD_OPENIMAGEIO_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_OPENIMAGEIO_PLUGIN=OFF')
if context.buildOCIO:
extraArgs.append('-DPXR_BUILD_OPENCOLORIO_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_OPENCOLORIO_PLUGIN=OFF')
if context.enableVulkan:
extraArgs.append('-DPXR_ENABLE_VULKAN_SUPPORT=ON')
else:
extraArgs.append('-DPXR_ENABLE_VULKAN_SUPPORT=OFF')
else:
extraArgs.append('-DPXR_BUILD_IMAGING=OFF')
if context.buildUsdImaging:
extraArgs.append('-DPXR_BUILD_USD_IMAGING=ON')
else:
extraArgs.append('-DPXR_BUILD_USD_IMAGING=OFF')
if context.buildUsdview:
extraArgs.append('-DPXR_BUILD_USDVIEW=ON')
else:
extraArgs.append('-DPXR_BUILD_USDVIEW=OFF')
if context.buildAlembic:
extraArgs.append('-DPXR_BUILD_ALEMBIC_PLUGIN=ON')
else:
extraArgs.append('-DPXR_BUILD_ALEMBIC_PLUGIN=OFF')
if context.buildDraco:
extraArgs.append('-DPXR_BUILD_DRACO_PLUGIN=ON')
draco_root = (context.dracoLocation
if context.dracoLocation else context.instDir)
extraArgs.append('-DDRACO_ROOT="{}"'.format(draco_root))
else:
extraArgs.append('-DPXR_BUILD_DRACO_PLUGIN=OFF')
if context.buildMaterialX:
extraArgs.append('-DPXR_ENABLE_MATERIALX_SUPPORT=ON')
else:
extraArgs.append('-DPXR_ENABLE_MATERIALX_SUPPORT=OFF')
if Windows() and not context.targetWasm:
extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"')
if MacOS():
if context.buildAppleFramework:
extraArgs.append('-DPXR_BUILD_APPLE_FRAMEWORK=ON')
extraArgs.append('-DTBB_USE_STATIC_LIBS=ON')
extraArgs.append('-DOPENSUBDIV_USE_STATIC_LIBS=ON')
else:
extraArgs.append('-DPXR_BUILD_APPLE_FRAMEWORK=OFF')
extraArgs.append('-DBoost_NO_SYSTEM_PATHS=ON')
extraArgs += buildArgs
if context.targetWasm:
extraArgs.append('-DCMAKE_FIND_ROOT_PATH="{}"'
.format(context.instDir))
extraArgs.append('-DTBB_INCLUDE_DIRS="{}"'
.format(os.path.join(context.instDir, 'include')))
extraArgs.append('-DTBB_tbb_LIBRARY_DEBUG="{}"'
.format(os.path.join(context.instDir, 'lib/libtbb_debug.a')))
extraArgs.append('-DTBB_tbb_LIBRARY_RELEASE="{}"'
.format(os.path.join(context.instDir, 'lib/libtbb.a')))
extraArgs.append('-DOPENSUBDIV_INCLUDE_DIR="{}"'
.format(os.path.join(context.instDir, 'include')))
extraArgs.append('-DOPENSUBDIV_OSDCPU_LIBRARY="{}"'
.format(os.path.join(context.instDir, 'lib/libosdCPU.a')))
extraArgs.append('-DBUILD_SHARED_LIBS=OFF')
compileFlags, linkFlags = GetWasmCompilerFlags(context.buildTarget)
extraArgs.append('-DCMAKE_CXX_FLAGS="{}"'.format(compileFlags))
extraArgs.append('-DCMAKE_C_FLAGS="{}"'.format(compileFlags))
extraArgs.append('-DCMAKE_EXE_LINKER_FLAGS="{}"'.format(linkFlags))
RunCMake(context, force, extraArgs, context.usdInstDir)
USD = Dependency("USD", InstallUSD, "include/pxr/pxr.h")
programDescription = """\
Installation Script for USD
Builds and installs USD and 3rd-party dependencies to specified location.
The `build_usd.py` script by default downloads and installs the zlib library
when necessary on platforms other than Linux. For those platforms, this behavior
may be overridden by supplying the `--no-zlib` command line option. If this
option is used, then the dependencies of OpenUSD which use zlib must be able to
discover the user supplied zlib in the build environment via the means of cmake's
`find_package` utility.
- Libraries:
The following is a list of libraries that this script will download and build
as needed. These names can be used to identify libraries for various script
options, like --force or --build-args.
{libraryList}
- Downloading Libraries:
If curl or powershell (on Windows) are installed and located in PATH, they
will be used to download dependencies. Otherwise, a built-in downloader will
be used.
- Specifying Custom Build Arguments:
Users may specify custom build arguments for libraries using the --build-args
option. This values for this option must take the form <library name>,<option>.
For example:
%(prog)s --build-args boost,cxxflags=... USD,-DPXR_STRICT_BUILD_MODE=ON ...
%(prog)s --build-args USD,"-DPXR_STRICT_BUILD_MODE=ON -DPXR_HEADLESS_TEST_MODE=ON" ...
These arguments will be passed directly to the build system for the specified
library. Multiple quotes may be needed to ensure arguments are passed on
exactly as desired. Users must ensure these arguments are suitable for the
specified library and do not conflict with other options, otherwise build
errors may occur.
- Embedded Build Targets
When cross compiling for an embedded target operating system, e.g. iOS, the
following components are disabled: python, tools, tests, examples, tutorials,
opencolorio, openimageio, openvdb, vulkan.
- Python Versions and DCC Plugins:
Some DCCs may ship with and run using their own version of Python. In that case,
it is important that USD and the plugins for that DCC are built using the DCC's
version of Python and not the system version. This can be done by running
%(prog)s using the DCC's version of Python.
If %(prog)s does not automatically detect the necessary information, the flag
--build-python-info can be used to explicitly pass in the Python that you want
USD to use to build the Python bindings with. This flag takes 4 arguments:
Python executable, Python include directory Python library and Python version.
Note that this is primarily an issue on MacOS, where a DCC's version of Python
is likely to conflict with the version provided by the system.
- C++11 ABI Compatibility:
On Linux, the --use-cxx11-abi parameter can be used to specify whether to use
the C++11 ABI for libstdc++ when building USD and any dependencies. The value
given to this parameter will be used to define _GLIBCXX_USE_CXX11_ABI for
all builds.
If this parameter is not specified, the compiler's default ABI will be used.
For more details see:
https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
""".format(
libraryList=" ".join(sorted([d.name for d in AllDependencies])))
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False, description=programDescription)
parser.add_argument("install_dir", type=str,
help="Directory where USD will be installed")
parser.add_argument("-n", "--dry_run", dest="dry_run", action="store_true",
help="Only summarize what would happen")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="count", default=1,
dest="verbosity",
help="Increase verbosity level (1-3)")
group.add_argument("-q", "--quiet", action="store_const", const=0,
dest="verbosity",
help="Suppress all output except for error messages")
group = parser.add_argument_group(title="Build Options")
group.add_argument("-j", "--jobs", type=int, default=GetCPUCount(),
help=("Number of build jobs to run in parallel. "
"(default: # of processors [{0}])"
.format(GetCPUCount())))
group.add_argument("--build", type=str,
help=("Build directory for USD and 3rd-party dependencies "
"(default: <install_dir>/build)"))
BUILD_DEBUG = "debug"
BUILD_RELEASE = "release"
BUILD_RELWITHDEBUG = "relwithdebuginfo"
group.add_argument("--build-variant", default=BUILD_RELEASE,
choices=[BUILD_DEBUG, BUILD_RELEASE, BUILD_RELWITHDEBUG],
help=("Build variant for USD and 3rd-party dependencies. "
"(default: {})".format(BUILD_RELEASE)))
group.add_argument("--ignore-paths", type=str, nargs="*", default=[],
help="Paths for CMake to ignore when configuring projects.")
group.add_argument("--build-target",
default=GetBuildTargetDefault(),
choices=GetBuildTargets(),
type=str.lower,
help=("Build target for cross compilation. "
"(default: {})".format(
GetBuildTargetDefault())))
if MacOS():
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument(
"--build-apple-framework",
dest="build_apple_framework",
action="store_true",
default=None,
help=("Build USD as an Apple Framework "
"(Default if using embedded platforms)"))
subgroup.add_argument(
"--no-build-apple-framework",
dest="build_apple_framework",
action="store_false",
default=None,
help="Do not build USD as an Apple Framework (Default if macOS)")
if apple_utils.IsHostArm():
group.add_argument("--ignore-homebrew", action="store_true",
help="Specify that CMake should ignore Homebrew packages.")
group.add_argument("--build-args", type=str, nargs="*", default=[],
help=("Custom arguments to pass to build system when "
"building libraries (see docs above)"))
group.add_argument("--cmake-build-args", type=str,
help=("Custom arguments to pass to all builds that use "
"cmake; a single string, similar to the args passed "
"for a single dependency in --build-args"))
group.add_argument("--build-python-info", type=str, nargs=4, default=[],
metavar=('PYTHON_EXECUTABLE', 'PYTHON_INCLUDE_DIR',
'PYTHON_LIBRARY', 'PYTHON_VERSION'),
help=("Specify a custom python to use during build"))
group.add_argument("--force", type=str, action="append", dest="force_build",
default=[],
help=("Force download and build of specified library "
"(see docs above)"))
group.add_argument("--force-all", action="store_true",
help="Force download and build of all libraries")
group.add_argument("--generator", type=str,
help=("CMake generator to use when building libraries with "
"cmake"))
group.add_argument("--toolset", type=str,
help=("CMake toolset to use when building libraries with "
"cmake"))
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--compiler-cache", dest="use_compiler_cache", action="store_true",
default=not Windows(),
help="Use ccache to enable faster iterative builds. (default on macOS and Linux)")
subgroup.add_argument("--no-compiler-cache", dest="use_compiler_cache", action="store_false",
help="Do not use ccache. (default on Windows)")
if MacOS():
codesignDefault = True if apple_utils.IsHostArm() else False
group.add_argument("--codesign", dest="macos_codesign",
default=codesignDefault, action="store_true",
help=("Enable code signing for macOS builds "
"(defaults to enabled on Apple Silicon)"))
group.add_argument("--codesign-id", dest="macos_codesign_id", type=str,
help=("A specific code-sign ID to use. If not provided, "
"the build will try and find one or use '-'"))
if Linux():
group.add_argument("--use-cxx11-abi", type=int, choices=[0, 1],
help=("Use C++11 ABI for libstdc++. (see docs above)"))
group = parser.add_argument_group(title="3rd Party Dependency Build Options")
group.add_argument("--src", type=str,
help=("Directory where dependencies will be downloaded "
"(default: <install_dir>/src)"))
group.add_argument("--inst", type=str,
help=("Directory where dependencies will be installed "
"(default: <install_dir>)"))
group = parser.add_argument_group(title="USD Options")
(SHARED_LIBS, MONOLITHIC_LIB) = (0, 1)
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--build-shared", dest="build_type",
action="store_const", const=SHARED_LIBS,
default=SHARED_LIBS,
help="Build individual shared libraries (default)")
subgroup.add_argument("--build-monolithic", dest="build_type",
action="store_const", const=MONOLITHIC_LIB,
help="Build a single monolithic shared library")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--tests", dest="build_tests", action="store_true",
default=False, help="Build unit tests")
subgroup.add_argument("--no-tests", dest="build_tests", action="store_false",
help="Do not build unit tests (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--examples", dest="build_examples", action="store_true",
default=True, help="Build examples (default)")
subgroup.add_argument("--no-examples", dest="build_examples", action="store_false",
help="Do not build examples")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--tutorials", dest="build_tutorials", action="store_true",
default=True, help="Build tutorials (default)")
subgroup.add_argument("--no-tutorials", dest="build_tutorials", action="store_false",
help="Do not build tutorials")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--tools", dest="build_tools", action="store_true",
default=True, help="Build USD tools (default)")
subgroup.add_argument("--no-tools", dest="build_tools", action="store_false",
help="Do not build USD tools")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--docs", dest="build_docs", action="store_true",
default=False, help="Build C++ documentation")
subgroup.add_argument("--no-docs", dest="build_docs", action="store_false",
help="Do not build C++ documentation (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--python-docs", dest="build_python_docs", action="store_true",
default=False, help="Build Python docs")
subgroup.add_argument("--no-python-docs", dest="build_python_docs", action="store_false",
help="Do not build Python documentation (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--python", dest="build_python", action="store_true",
default=True, help="Build python based components "
"(default)")
subgroup.add_argument("--no-python", dest="build_python", action="store_false",
help="Do not build python based components")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--prefer-safety-over-speed", dest="safety_first",
action="store_true", default=True, help=
"Enable extra safety checks (which may negatively "
"impact performance) against malformed input files "
"(default)")
subgroup.add_argument("--prefer-speed-over-safety", dest="safety_first",
action="store_false", help=
"Disable performance-impacting safety checks against "
"malformed input files")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--usdValidation", dest="build_usd_validation",
action="store_true", default=True, help="Build USD " \
"Validation library and validators (default)")
subgroup.add_argument("--no-usdValidation", dest="build_usd_validation",
action="store_false", help="Do not build USD " \
"Validation library and validators")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--debug-python", dest="debug_python",
action="store_true", help=
"Define Boost Python Debug if your Python library "
"comes with Debugging symbols.")
subgroup.add_argument("--no-debug-python", dest="debug_python",
action="store_false", help=
"Don't define Boost Python Debug if your Python "
"library comes with Debugging symbols.")
group.add_argument("--python-install-dir", type=str,
dest="python_install_dir", default=None,
help=("Directory to install USD Python bindings, relative "
"to the install prefix or absolute. Defaults to the "
"Python site-packages directory."))
(NO_IMAGING, IMAGING, USD_IMAGING) = (0, 1, 2)
group = parser.add_argument_group(title="Imaging and USD Imaging Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--imaging", dest="build_imaging",
action="store_const", const=IMAGING, default=USD_IMAGING,
help="Build imaging component")
subgroup.add_argument("--usd-imaging", dest="build_imaging",
action="store_const", const=USD_IMAGING,
help="Build imaging and USD imaging components (default)")
subgroup.add_argument("--no-imaging", dest="build_imaging",
action="store_const", const=NO_IMAGING,
help="Do not build imaging or USD imaging components")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--ptex", dest="enable_ptex", action="store_true",
default=False,
help="Enable Ptex support in imaging")
subgroup.add_argument("--no-ptex", dest="enable_ptex",
action="store_false",
help="Disable Ptex support in imaging (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--openvdb", dest="enable_openvdb", action="store_true",
default=False,
help="Enable OpenVDB support in imaging")
subgroup.add_argument("--no-openvdb", dest="enable_openvdb",
action="store_false",
help="Disable OpenVDB support in imaging (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--usdview", dest="build_usdview",
action="store_true", default=True,
help="Build usdview (default)")
subgroup.add_argument("--no-usdview", dest="build_usdview",
action="store_false",
help="Do not build usdview")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--zlib", dest="build_zlib",
action="store_true", default=True,
help="Install zlib on behalf of dependencies (default)")
subgroup.add_argument("--no-zlib", dest="build_zlib",
action="store_false",
help="Do not install zlib for dependencies")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--vulkan", dest="enable_vulkan", action="store_true",
default=False, help="Enable Vulkan support")
subgroup.add_argument("--no-vulkan", dest="enable_vulkan", action="store_false",
help="Disable Vulkan support (default)")
group = parser.add_argument_group(title="Imaging Plugin Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--embree", dest="build_embree", action="store_true",
default=False,
help="Build Embree sample imaging plugin")
subgroup.add_argument("--no-embree", dest="build_embree", action="store_false",
help="Do not build Embree sample imaging plugin (default)")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--prman", dest="build_prman", action="store_true",
default=False,
help="Build Pixar's RenderMan imaging plugin")
subgroup.add_argument("--no-prman", dest="build_prman", action="store_false",
help="Do not build Pixar's RenderMan imaging plugin (default)")
group.add_argument("--prman-location", type=str,
help="Directory where Pixar's RenderMan is installed.")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--openimageio", dest="build_oiio", action="store_true",
default=False,
help="Build OpenImageIO plugin for USD")
subgroup.add_argument("--no-openimageio", dest="build_oiio", action="store_false",
help="Do not build OpenImageIO plugin for USD (default)")
if MacOS():
group.add_argument("--imageio", dest="build_imageio", action="store_true",
default=True,
help="Build the ImageIO.framework plugin for USD (default).")
group.add_argument("--no-imageio", dest="build_imageio", action="store_false",
help="Do not build build the ImageIO.framework plugin for USD.")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--opencolorio", dest="build_ocio", action="store_true",
default=False,
help="Build OpenColorIO plugin for USD")
subgroup.add_argument("--no-opencolorio", dest="build_ocio", action="store_false",
help="Do not build OpenColorIO plugin for USD (default)")
group = parser.add_argument_group(title="Alembic Plugin Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--alembic", dest="build_alembic", action="store_true",
default=False,
help="Build Alembic plugin for USD")
subgroup.add_argument("--no-alembic", dest="build_alembic", action="store_false",
help="Do not build Alembic plugin for USD (default)")
group = parser.add_argument_group(title="Draco Plugin Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--draco", dest="build_draco", action="store_true",
default=False,
help="Build Draco plugin for USD")
subgroup.add_argument("--no-draco", dest="build_draco", action="store_false",
help="Do not build Draco plugin for USD (default)")
group.add_argument("--draco-location", type=str,
help="Directory where Draco is installed.")
group = parser.add_argument_group(title="MaterialX Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--materialx", dest="build_materialx", action="store_true",
default=True,
help="Enable MaterialX support (default)")
subgroup.add_argument("--no-materialx", dest="build_materialx", action="store_false",
help="Disable MaterialX support")
group = parser.add_argument_group(title="TBB Options")
subgroup = group.add_mutually_exclusive_group()
subgroup.add_argument("--onetbb", dest="build_onetbb", action="store_true",
default=False,
help="Build using oneTBB instead of TBB")
subgroup.add_argument("--no-onetbb", dest="build_onetbb", action="store_false",
help="Build using TBB (default)")
args = parser.parse_args()
class InstallContext:
def __init__(self, args):
self.usdSrcDir = os.path.normpath(
os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
self.usdInstDir = os.path.abspath(args.install_dir)
self.instDir = (os.path.abspath(args.inst) if args.inst
else self.usdInstDir)
self.srcDir = (os.path.abspath(args.src) if args.src
else os.path.join(self.usdInstDir, "src"))
self.buildDir = (os.path.abspath(args.build) if args.build
else os.path.join(self.usdInstDir, "build"))
if which("curl"):
self.downloader = DownloadFileWithCurl
self.downloaderName = "curl"
elif Windows() and which("powershell"):
self.downloader = DownloadFileWithPowershell
self.downloaderName = "powershell"
else:
self.downloader = DownloadFileWithUrllib
self.downloaderName = "built-in"
self.cmakeGenerator = args.generator
self.cmakeToolset = args.toolset
self.cmakeBuildArgs = args.cmake_build_args
self.useCompilerCache = args.use_compiler_cache
self.numJobs = args.jobs
if self.numJobs <= 0:
raise ValueError("Number of jobs must be greater than 0")
self.buildArgs = dict()
for a in args.build_args:
(depName, _, arg) = a.partition(",")
if not depName or not arg:
raise ValueError("Invalid argument for --build-args: {}"
.format(a))
if depName.lower() not in AllDependenciesByName:
raise ValueError("Invalid library for --build-args: {}"
.format(depName))
self.buildArgs.setdefault(depName.lower(), []).append(arg)
self.build_python_info = dict()
if args.build_python_info:
self.build_python_info['PYTHON_EXECUTABLE'] = args.build_python_info[0]
self.build_python_info['PYTHON_INCLUDE_DIR'] = args.build_python_info[1]
self.build_python_info['PYTHON_LIBRARY'] = args.build_python_info[2]
self.build_python_info['PYTHON_VERSION'] = args.build_python_info[3]
self.buildDebug = (args.build_variant == BUILD_DEBUG)
self.buildRelease = (args.build_variant == BUILD_RELEASE)
self.buildRelWithDebug = (args.build_variant == BUILD_RELWITHDEBUG)
self.debugPython = args.debug_python
self.buildShared = (args.build_type == SHARED_LIBS)
self.buildMonolithic = (args.build_type == MONOLITHIC_LIB)
self.ignorePaths = args.ignore_paths or []
self.targetWasm = (args.build_target == TARGET_WASM or
args.build_target == TARGET_WASM64)
self.buildTarget = args.build_target
if MacOS():
apple_utils.SetTarget(self)
self.macOSCodesign = False
if args.macos_codesign and not self.targetWasm:
self.macOSCodesign = (args.macos_codesign_id or
apple_utils.GetCodeSignID())
if apple_utils.IsHostArm() and args.ignore_homebrew:
self.ignorePaths.append("/opt/homebrew")
if args.build_apple_framework is None:
self.buildAppleFramework = MacOSTargetEmbedded(self)
else:
self.buildAppleFramework = args.build_apple_framework
if self.buildAppleFramework:
self.buildShared = False
self.buildMonolithic = True
else:
self.buildTarget = ""
self.buildAppleFramework = False
self.useCXX11ABI = \
(args.use_cxx11_abi if hasattr(args, "use_cxx11_abi") else None)
self.safetyFirst = args.safety_first
self.forceBuildAll = args.force_all
self.forceBuild = [dep.lower() for dep in args.force_build]
embedded = MacOSTargetEmbedded(self) or self.buildAppleFramework
self.buildTests = (args.build_tests and not embedded)
self.buildPython = (args.build_python and
not embedded and
not self.targetWasm)
self.pythonInstallDir = args.python_install_dir
self.buildExamples = (args.build_examples and
not embedded)
self.buildTutorials = (args.build_tutorials and
not embedded and
not self.targetWasm)
self.buildTools = (args.build_tools and
not embedded and
not self.targetWasm)
self.buildUsdValidation = (args.build_usd_validation and
not embedded and
not self.targetWasm)
self.buildDocs = args.build_docs or args.build_python_docs
self.buildHtmlDocs = args.build_docs
self.buildPythonDocs = args.build_python_docs
self.buildImaging = (args.build_imaging == IMAGING or
args.build_imaging == USD_IMAGING
and not self.targetWasm)
self.enablePtex = self.buildImaging and args.enable_ptex
self.enableOpenVDB = (self.buildImaging
and args.enable_openvdb
and not embedded)
self.enableVulkan = (self.buildImaging
and args.enable_vulkan
and not embedded)
self.buildUsdImaging = (args.build_imaging == USD_IMAGING and
not self.targetWasm)
self.buildUsdview = (self.buildUsdImaging and
self.buildPython and
args.build_usdview)
self.buildZlib = args.build_zlib
self.buildEmbree = self.buildImaging and args.build_embree
self.buildPrman = self.buildImaging and args.build_prman
self.prmanLocation = (os.path.abspath(args.prman_location)
if args.prman_location else None)
self.buildOIIO = ((args.build_oiio or (self.buildUsdImaging
and self.buildTests))
and not embedded and not self.targetWasm)
if MacOS():
self.buildImageIO = args.build_imageio
self.buildOCIO = args.build_ocio and not embedded
self.buildAlembic = args.build_alembic
self.buildDraco = args.build_draco
self.dracoLocation = (os.path.abspath(args.draco_location)
if args.draco_location else None)
self.buildMaterialX = args.build_materialx and not self.targetWasm
self.buildOneTBB = args.build_onetbb or self.targetWasm
def GetBuildArguments(self, dep):
return self.buildArgs.get(dep.name.lower(), [])
def ForceBuildDependency(self, dep):
if type(dep) is PythonDependency:
return False
return self.forceBuildAll or dep.name.lower() in self.forceBuild
try:
context = InstallContext(args)
except Exception as e:
PrintError(str(e))
sys.exit(1)
verbosity = args.verbosity
extraPaths = []
extraPythonPaths = []
if Windows():
extraPaths.append(os.path.join(context.instDir, "lib"))
extraPaths.append(os.path.join(context.instDir, "bin"))
if extraPaths:
paths = os.environ.get('PATH', '').split(os.pathsep) + extraPaths
os.environ['PATH'] = os.pathsep.join(paths)
if extraPythonPaths:
paths = os.environ.get('PYTHONPATH', '').split(os.pathsep) + extraPythonPaths
os.environ['PYTHONPATH'] = os.pathsep.join(paths)
if context.buildOneTBB or context.targetWasm:
TBB = ONETBB
requiredDependencies = [TBB]
if context.buildAlembic:
requiredDependencies += [ZLIB, OPENEXR, ALEMBIC]
if context.buildDraco:
requiredDependencies += [DRACO]
if context.buildMaterialX:
requiredDependencies += [MATERIALX]
if context.buildImaging:
if context.enablePtex:
requiredDependencies += [ZLIB, PTEX]
requiredDependencies += [OPENSUBDIV]
if context.enableOpenVDB:
requiredDependencies += [ZLIB, TBB, BLOSC, BOOST, OPENEXR, OPENVDB]
if context.buildOCIO:
requiredDependencies += [ZLIB, OPENCOLORIO]
if context.buildOIIO:
requiredDependencies += [ZLIB, BOOST, JPEG, TIFF, PNG, OPENEXR, OPENIMAGEIO]
if context.buildEmbree:
requiredDependencies += [TBB, EMBREE]
if context.buildUsdview:
requiredDependencies += [PYOPENGL, PYSIDE]
if ZLIB in requiredDependencies:
if Linux() or MacOS() or context.targetWasm or not context.buildZlib:
requiredDependencies = [r for r in requiredDependencies if r != ZLIB]
if context.buildDraco and context.buildMonolithic and Windows():
PrintError("Draco plugin can not be enabled for monolithic build on Windows")
sys.exit(1)
if Windows() and GetWindowsHostArch() == "ARM64" and not context.buildOneTBB:
PrintError("Windows ARM64 builds require oneTBB. Enable via the --onetbb argument")
sys.exit(1)
if context.enableVulkan and not 'VULKAN_SDK' in os.environ:
PrintError("Vulkan support cannot be enabled when VULKAN_SDK environment "
"variable is not set")
sys.exit(1)
if context.targetWasm:
if "--no-onetbb" in sys.argv:
PrintError("Wasm target requires oneTBB")
sys.exit(1)
if "--python" in sys.argv:
PrintError("Cannot build python components for wasm build targets")
sys.exit(1)
if "--tutorials" in sys.argv:
PrintError("Cannot build tutorials for wasm build targets")
sys.exit(1)
if "--tools" in sys.argv:
PrintError("Cannot build tools for wasm build targets")
sys.exit(1)
if "--materialx" in sys.argv:
PrintError("Cannot build materialx for wasm build targets")
sys.exit(1)
if "--usd-imaging" in sys.argv:
PrintError("Cannot build Usd Imaging for wasm build targets")
sys.exit(1)
if "--usdValidation" in sys.argv:
PrintError("Cannot build Usd Validation for wasm build targets")
sys.exit(1)
if MacOSTargetEmbedded(context):
if "--tests" in sys.argv:
PrintError("Cannot build tests for embedded build targets")
sys.exit(1)
if "--python" in sys.argv:
PrintError("Cannot build python components for embedded build targets")
sys.exit(1)
if "--examples" in sys.argv:
PrintError("Cannot build examples for embedded build targets")
sys.exit(1)
if "--tutorials" in sys.argv:
PrintError("Cannot build tutorials for embedded build targets")
sys.exit(1)
if "--tools" in sys.argv:
PrintError("Cannot build tools for embedded build targets")
sys.exit(1)
if "--openimageio" in sys.argv:
PrintError("Cannot build openimageio for embedded build targets")
sys.exit(1)
if "--opencolorio" in sys.argv:
PrintError("Cannot build opencolorio for embedded build targets")
sys.exit(1)
if "--openvdb" in sys.argv:
PrintError("Cannot build openvdb for embedded build targets")
sys.exit(1)
if "--vulkan" in sys.argv:
PrintError("Cannot build vulkan for embedded build targets")
sys.exit(1)
if "--usdview" in sys.argv:
if not context.buildUsdImaging:
PrintError("Cannot build usdview when usdImaging is disabled.")
sys.exit(1)
if not context.buildPython:
PrintError("Cannot build usdview when Python support is disabled.")
sys.exit(1)
dependenciesToBuild = []
for dep in requiredDependencies:
if context.ForceBuildDependency(dep) or not dep.Exists(context):
if dep not in dependenciesToBuild:
dependenciesToBuild.append(dep)
if context.targetWasm:
if not which("emcc"):
PrintError("Wasm compiler emcc not found -- please install a compiler")
sys.exit(1)
else:
if (not which("g++") and
not which("clang") and
not GetXcodeDeveloperDirectory() and
not GetVisualStudioCompilerAndVersion()):
PrintError("C++ compiler not found -- please install a compiler")
sys.exit(1)
isPython64Bit = (ctypes.sizeof(ctypes.c_voidp) == 8)
if not isPython64Bit:
PrintError("64bit python not found -- please install it and adjust your"
"PATH")
sys.exit(1)
if which("cmake"):
if MacOS() and context.buildTarget == apple_utils.TARGET_VISIONOS:
cmake_required_version = (3, 28)
else:
cmake_required_version = (3, 27)
cmake_version = GetCMakeVersion()
if not cmake_version:
PrintError("Failed to determine CMake version")
sys.exit(1)
if cmake_version < cmake_required_version:
def _JoinVersion(v):
return ".".join(str(n) for n in v)
PrintError("CMake version {req} or later required to build USD, "
"but version found was {found}".format(
req=_JoinVersion(cmake_required_version),
found=_JoinVersion(cmake_version)))
sys.exit(1)
else:
PrintError("CMake not found -- please install it and adjust your PATH")
sys.exit(1)
if context.targetWasm and Windows() and not context.cmakeGenerator:
if which("ninja"):
context.cmakeGenerator = 'Ninja'
else:
PrintError("Default generator ninja not found -- please install "
"it or another compatible generator and adjust your PATH")
sys.exit(1)
if context.buildDocs:
if not which("doxygen"):
PrintError("doxygen not found -- please install it and adjust your PATH")
sys.exit(1)
if context.buildHtmlDocs:
if not which("dot"):
PrintError("dot not found -- please install graphviz and adjust your "
"PATH")
sys.exit(1)
if context.buildPythonDocs:
if not context.buildDocs:
PrintError("Cannot build Python docs when doxygen docs are disabled.")
sys.exit(1)
if not context.buildPython:
PrintError("Cannot build Python docs when Python support is disabled.")
sys.exit(1)
if PYSIDE in requiredDependencies:
usdBuildArgs = context.GetBuildArguments(USD)
given_pysideUic = 'PYSIDEUICBINARY' in " ".join(usdBuildArgs)
pyside6Uic = ["pyside6-uic"]
found_pyside6Uic = any([which(p) for p in pyside6Uic])
pyside2Uic = ["pyside2-uic"]
found_pyside2Uic = any([which(p) for p in pyside2Uic])
if not given_pysideUic and not found_pyside2Uic and not found_pyside6Uic:
PrintError("PySide's user interface compiler was not found -- please"
" install PySide2 or PySide6 and adjust your PATH. (Note"
" that this program may be named {0} depending on your"
" platform)"
.format(" or ".join(set(pyside2Uic+pyside6Uic))))
sys.exit(1)
summaryMsg = """
Building with settings:
USD source directory {usdSrcDir}
USD install directory {usdInstDir}
3rd-party source directory {srcDir}
3rd-party install directory {instDir}
Build directory {buildDir}
CMake generator {cmakeGenerator}
CMake toolset {cmakeToolset}
Downloader {downloader}
Building {buildType}
"""
if context.useCXX11ABI is not None:
summaryMsg += """\
Use C++11 ABI {useCXX11ABI}
"""
if MacOS():
summaryMsg += """\
Framework Build {buildAppleFramework}
"""
summaryMsg += """\
Variant {buildVariant}
Target {buildTarget}
UsdValidation {buildUsdValidation}
Imaging {buildImaging}
Ptex support: {enablePtex}
OpenVDB support: {enableOpenVDB}
ImageIO support: {buildImageIO}
OpenImageIO support: {buildOIIO}
OpenColorIO support: {buildOCIO}
Embree support: {buildEmbree}
PRMan support: {buildPrman}
Vulkan support: {enableVulkan}
UsdImaging {buildUsdImaging}
usdview: {buildUsdview}
MaterialX support {buildMaterialX}
Python support {buildPython}
Python Debug: {debugPython}
Python docs: {buildPythonDocs}
Documentation {buildHtmlDocs}
Tests {buildTests}
Examples {buildExamples}
Tutorials {buildTutorials}
Tools {buildTools}
Alembic Plugin {buildAlembic}
Draco Plugin {buildDraco}
Dependencies {dependencies}"""
if context.buildArgs:
summaryMsg += """
Build arguments {buildArgs}"""
def FormatBuildArguments(buildArgs):
s = ""
for depName in sorted(buildArgs.keys()):
args = buildArgs[depName]
s += """
{name}: {args}""".format(
name=AllDependenciesByName[depName].name,
args=" ".join(args))
return s.lstrip()
omitUsdGenSchemaMsg = context.buildPython and not JINJA2.Exists(context)
omittedSchemaGenScripts = [
"usdGenSchema",
"usdgenschemafromsdr",
"usdInitSchema"
]
if omitUsdGenSchemaMsg:
summaryMsg += """
\nOmitted (Jinja2 not found): {omittedSchemaGenScripts}."""
summaryMsg += "\n"
summaryMsg = summaryMsg.format(
usdSrcDir=context.usdSrcDir,
usdInstDir=context.usdInstDir,
srcDir=context.srcDir,
buildDir=context.buildDir,
instDir=context.instDir,
cmakeGenerator=("Default" if not context.cmakeGenerator
else context.cmakeGenerator),
cmakeToolset=("Default" if not context.cmakeToolset
else context.cmakeToolset),
downloader=(context.downloaderName),
dependencies=("None" if not dependenciesToBuild else
", ".join([d.name for d in dependenciesToBuild])),
buildArgs=FormatBuildArguments(context.buildArgs),
useCXX11ABI=("On" if context.useCXX11ABI else "Off"),
buildType=("Shared libraries" if context.buildShared
else "Monolithic shared library" if context.buildMonolithic
else ""),
buildVariant=("Release" if context.buildRelease
else "Debug" if context.buildDebug
else "Release w/ Debug Info" if context.buildRelWithDebug
else ""),
buildTarget=(context.buildTarget),
buildAppleFramework=("On" if context.buildAppleFramework else "Off"),
buildImaging=("On" if context.buildImaging else "Off"),
enablePtex=("On" if context.enablePtex else "Off"),
enableOpenVDB=("On" if context.enableOpenVDB else "Off"),
buildImageIO=("On" if (MacOS() and context.buildImageIO) else "Off"),
buildOIIO=("On" if context.buildOIIO else "Off"),
buildOCIO=("On" if context.buildOCIO else "Off"),
buildEmbree=("On" if context.buildEmbree else "Off"),
buildPrman=("On" if context.buildPrman else "Off"),
buildUsdImaging=("On" if context.buildUsdImaging else "Off"),
buildUsdview=("On" if context.buildUsdview else "Off"),
buildPython=("On" if context.buildPython else "Off"),
debugPython=("On" if context.debugPython else "Off"),
buildPythonDocs=("On" if context.buildPythonDocs else "Off"),
buildHtmlDocs=("On" if context.buildHtmlDocs else "Off"),
buildTests=("On" if context.buildTests else "Off"),
buildExamples=("On" if context.buildExamples else "Off"),
buildTutorials=("On" if context.buildTutorials else "Off"),
enableVulkan=("On" if context.enableVulkan else "Off"),
buildTools=("On" if context.buildTools else "Off"),
buildUsdValidation=("On" if context.buildUsdValidation else "Off"),
buildAlembic=("On" if context.buildAlembic else "Off"),
buildDraco=("On" if context.buildDraco else "Off"),
buildMaterialX=("On" if context.buildMaterialX else "Off"),
omittedSchemaGenScripts=(", ".join(omittedSchemaGenScripts)))
Print(summaryMsg)
if args.dry_run:
sys.exit(0)
pythonDependencies = \
[dep for dep in dependenciesToBuild if type(dep) is PythonDependency]
if pythonDependencies:
for dep in pythonDependencies:
Print(dep.getInstructions())
sys.exit(1)
for dir in [context.usdInstDir, context.instDir, context.srcDir,
context.buildDir]:
try:
if os.path.isdir(dir):
testFile = os.path.join(dir, "canwrite")
open(testFile, "w").close()
os.remove(testFile)
else:
os.makedirs(dir)
except Exception as e:
PrintError("Could not write to directory {dir}. Change permissions "
"or choose a different location to install to."
.format(dir=dir))
sys.exit(1)
try:
for dep in dependenciesToBuild + [USD]:
PrintStatus("Installing {dep}...".format(dep=dep.name))
dep.installer(context,
buildArgs=context.GetBuildArguments(dep),
force=context.ForceBuildDependency(dep))
except Exception as e:
PrintError(str(e))
sys.exit(1)
usdCMakeBuildDir = os.path.join(context.buildDir,
os.path.basename(context.usdSrcDir))
pythonInstallDir = GetCMakeCacheValue(usdCMakeBuildDir, "PXR_PYTHON_INSTALL_DIR")
requiredInPythonPath = set([
os.path.join(context.usdInstDir, pythonInstallDir)
if pythonInstallDir is not None else
"<unknown: could not read PXR_PYTHON_INSTALL_DIR from CMake cache>"
])
requiredInPythonPath.update(extraPythonPaths)
requiredInPath = set([
os.path.join(context.usdInstDir, "bin")
])
requiredInPath.update(extraPaths)
if Windows():
requiredInPath.update([
os.path.join(context.usdInstDir, "lib"),
os.path.join(context.instDir, "bin"),
os.path.join(context.instDir, "lib")
])
if MacOS():
if context.macOSCodesign:
apple_utils.Codesign(context.usdInstDir,
identifier=context.macOSCodesign,
verbose_output=verbosity > 1)
additionalInstructions = any([context.buildPython, context.buildTools, context.buildPrman])
if additionalInstructions:
Print("\nSuccess! To use USD, please ensure that you have:")
else:
Print("\nSuccess! USD libraries were built.")
if context.buildPython:
Print("""
The following in your PYTHONPATH environment variable:
{requiredInPythonPath}""".format(
requiredInPythonPath="\n ".join(sorted(requiredInPythonPath))))
if context.buildPython or context.buildTools:
Print("""
The following in your PATH environment variable:
{requiredInPath}""".format(
requiredInPath="\n ".join(sorted(requiredInPath))))
if context.buildPrman:
Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html "
"for setting up the RenderMan plugin.\n")
if context.buildAppleFramework:
Print("\nSee documentation in README.md for using the framework in XCode.")