From d27586fea36081dd34a28dcef2e8cd9e3230ec3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Garramu=C3=B1o?= Date: Sat, 6 Apr 2024 06:06:09 -0300 Subject: [PATCH] Trying compile with sourceforge boost. --- .gitignore | 2 +- etc/SuperBuild/USD-patch/build_usd.py | 2654 +++++++++++++++++++ etc/SuperBuild/cmake/Modules/BuildUSD.cmake | 9 +- 3 files changed, 2662 insertions(+), 3 deletions(-) create mode 100644 etc/SuperBuild/USD-patch/build_usd.py diff --git a/.gitignore b/.gitignore index 6eca04e11..df2efe21f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ build_win64/ Debug Release RelWithDebInfo -build* +_build* debug_tests doc/doxygen *~ diff --git a/etc/SuperBuild/USD-patch/build_usd.py b/etc/SuperBuild/USD-patch/build_usd.py new file mode 100644 index 000000000..4a70e1cb9 --- /dev/null +++ b/etc/SuperBuild/USD-patch/build_usd.py @@ -0,0 +1,2654 @@ +# +# Copyright 2017 Pixar +# +# Licensed under the Apache License, Version 2.0 (the "Apache License") +# with the following modification; you may not use this file except in +# compliance with the Apache License and the following modification to it: +# Section 6. Trademarks. is deleted and replaced with: +# +# 6. Trademarks. This License does not grant permission to use the trade +# names, trademarks, service marks, or product names of the Licensor +# and its affiliates, except as required to comply with Section 4(c) of +# the License and to reproduce the content of the NOTICE file. +# +# You may obtain a copy of the Apache License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the Apache License with the above modification is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the Apache License for the specific +# language governing permissions and limitations under the Apache License. +# +# Check whether this script is being run under Python 2 first. Otherwise, +# any Python 3-only code below will cause the script to fail with an +# unhelpful error message. +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 distutils +import fnmatch +import glob +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 + +# Helpers for printing output +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) + +# Helpers for determining platform +def Windows(): + return platform.system() == "Windows" +def Linux(): + return platform.system() == "Linux" +def MacOS(): + return platform.system() == "Darwin" + +if MacOS(): + import apple_utils + +def GetLocale(): + if Windows(): + # Windows handles encoding a little differently then Linux / Mac + # For interactive streams (isatty() == True) it will use the + # console codepage, otherwise it will return an ANSI codepage. + # To keep things consistent we'll force UTF-8 for non-interactive + # streams (which is recommended by Microsoft). See: + # https://docs.python.org/3.6/library/sys.html#sys.stdout + # https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers + 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: + # VisualStudioVersion environment variable should be set by the + # Visual Studio Command Prompt. + match = re.search( + r"(\d+)\.(\d+)", + os.environ.get("VisualStudioVersion", "")) + 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 IsVisualStudio2022OrGreater(): + VISUAL_STUDIO_2022_VERSION = (17, 0) + return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2022_VERSION) + +def IsVisualStudio2019OrGreater(): + VISUAL_STUDIO_2019_VERSION = (16, 0) + return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2019_VERSION) + +def IsVisualStudio2017OrGreater(): + VISUAL_STUDIO_2017_VERSION = (15, 0) + return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2017_VERSION) + +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 information is used + in the Boost and USD builds. By taking this approach we can 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 we were given build python info then just use it. + 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']) + + # First we extract the information that can be uniformly dealt with across + # the platforms: + pythonExecPath = sys.executable + pythonVersion = sysconfig.get_config_var("py_version_short") # "3.7" + + # Lib path is unfortunately special for each platform and there is no + # config_var for it. But we can deduce it for each platform, and this + # logic works for any Python version. + 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): + # as a backup, and for legacy reasons - not preferred because + # it may be baked at build time + pythonIncludeDir = sysconfig.get_config_var("INCLUDEPY") + + # if in a venv, installed_base will be the "original" python, + # which is where the libs are ("base" will be the venv dir) + 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") + # Try multiple ways to get the python lib dir + 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): + """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") + + # Let exceptions escape from subprocess calls -- higher level + # code will handle them. + if logCommandOutput: + p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + 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)) + p.wait() + + if p.returncode != 0: + # If verbosity >= 3, we'll have already been printing out command output + # so no reason to print the log file again. + if verbosity < 3: + with open("log.txt", "r") as logfile: + Print(logfile.read()) + raise RuntimeError("Failed to run '{cmd}'\nSee {log} for more details." + .format(cmd=cmd, log=os.path.abspath("log.txt"))) + +@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) + 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: + + ="-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)] + + # buildArgs might look like: + # ["-DFOO=1", "-DBAR=2", ...] or ["-DFOO=1 -DBAR=2 ...", ...] + # + # See if any of the arguments in buildArgs start with the given + # buildFlag. If so, we want to take whatever that buildFlag has + # been set to and merge it in with the cxxFlags above. + # + # For example, if buildArgs = ['-DCMAKE_CXX_FLAGS="-w"', ...] + # we want to add "-w" to cxxFlags. + 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 FormatMultiProcs(numJobs, generator): + tag = "-j" + if generator: + if "Visual Studio" in generator: + tag = "/M:" # This will build multiple projects at once. + elif "Xcode" in generator: + tag = "-j " + + return "{tag}{procs}".format(tag=tag, procs=numJobs) + +def RunCMake(context, force, extraArgs = None): + """Invoke CMake to configure, build, and install a library whose + source code is located in the current working directory.""" + # Create a directory for out-of-source builds in the build directory + # using the name of the current working directory. + if extraArgs is None: + extraArgs = [] + else: + # ensure we can freely modify our extraArgs without affecting caller + extraArgs = list(extraArgs) + + if context.cmakeBuildArgs: + extraArgs.insert(0, context.cmakeBuildArgs) + srcDir = os.getcwd() + instDir = (context.usdInstDir if srcDir == context.usdSrcDir + else context.instDir) + 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 + + # On Windows, we need to explicitly specify the generator to ensure we're + # building a 64-bit project. (Surely there is a better way to do this?) + # TODO: figure out exactly what "vcvarsall.bat x64" sets to force x64 + if generator is None and Windows(): + if 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) + + # Note - don't want to add -A (architecture flag) if generator is, ie, Ninja + if IsVisualStudio2019OrGreater() and "Visual Studio" in generator: + generator = generator + " -A x64" + + toolset = context.cmakeToolset + if toolset is not None: + toolset = '-T "{toolset}"'.format(toolset=toolset) + + # On MacOS, enable the use of @rpath for relocatable builds. + osx_rpath = None + if MacOS(): + osx_rpath = "-DCMAKE_MACOSX_RPATH=ON" + + # For macOS cross compilation, set the Xcode architecture flags. + 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)) + + if context.ignorePaths: + ignoredPaths = ";".join(context.ignorePaths) + extraArgs.append("-DCMAKE_IGNORE_PATH={0}".format(ignoredPaths)) + extraArgs.append("-DCMAKE_IGNORE_PREFIX_PATH={0}".format(ignoredPaths)) + + # We use -DCMAKE_BUILD_TYPE for single-configuration generators + # (Ninja, make), and --config for multi-configuration generators + # (Visual Studio); technically we don't need BOTH at the same + # time, but specifying both is simpler than branching + config = "Release" + if context.buildDebug: + config = "Debug" + elif context.buildRelease: + config = "Release" + elif context.buildRelWithDebug: + config = "RelWithDebInfo" + + # Append extra argument controlling libstdc++ ABI if specified. + AppendCXX11ABIArg("-DCMAKE_CXX_FLAGS", context, extraArgs) + + with CurrentWorkingDirectory(buildDir): + Run('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("cmake --build . --config {config} --target install -- {multiproc}" + .format(config=config, + multiproc=FormatMultiProcs(context.numJobs, generator))) + +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 + + # cmake reports, e.g., "... version 3.14.3" + 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 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): + # Don't log command output so that curl's progress + # meter doesn't get written to the log file. + Run("curl {progress} -L -o {filename} {url}".format( + progress="-#" if verbosity >= 2 else "-s", + filename=outputFilename, url=url), + logCommandOutput=False) + +def DownloadFileWithPowershell(url, outputFilename): + # It's important that we specify to use TLS v1.2 at least or some + # of the downloads will fail. + 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): + """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. + + Returns the absolute path to the directory where files have + been extracted.""" + with CurrentWorkingDirectory(context.srcDir): + # Extract filename from URL and see if file already exists. + 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))) + + # To work around occasional hiccups with downloading from websites + # (SSL validation errors, etc.), retry a few times if we don't + # succeed in downloading the file. + maxRetries = 5 + lastError = None + + # Download to a temporary file and rename it to the expected + # filename when complete. This ensures that incomplete downloads + # will be retried if the script is run again. + 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)) + + shutil.move(tmpFilename, filename) + + # Open the archive and retrieve the name of the top-most directory. + # This assumes the archive contains a single directory with all + # of the contents beneath it, unless a specific extractDir is specified, + # which is to be used. + 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)) + + # Extract to a temporary directory then move the contents + # to the expected location when complete. This ensures that + # incomplete extracts will be retried if the script is run + # again. + 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: + # If extraction failed for whatever reason, assume the + # archive file was bad and move it aside so that re-running + # the script will try downloading and extracting again. + shutil.move(filename, filename + ".bad") + raise RuntimeError("Failed to extract archive {filename}: {err}" + .format(filename=filename, err=e)) + +############################################################ +# 3rd-Party Dependencies + +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): + # If one of the modules in our list imports successfully, we are good. + 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 + +ZLIB_URL = "https://github.com/madler/zlib/archive/v1.2.13.zip" + +def InstallZlib(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(ZLIB_URL, context, force)): + RunCMake(context, force, buildArgs) + +ZLIB = Dependency("zlib", InstallZlib, "include/zlib.h") + +############################################################ +# boost + +# The default installation of boost on Windows puts headers in a versioned +# subdirectory, which we have to account for here. Specifying "layout=system" +# would cause the Windows header install to match Linux/MacOS, but the +# "layout=system" flag also changes the naming of the boost dlls in a +# manner that causes problems for dependent libraries that rely on boost's +# trick of automatically linking the boost libraries via pragmas in boost's +# standard include files. Dependencies that use boost's pragma linking +# facility in general don't have enough configuration switches to also coerce +# the naming of the dlls and so it is best to rely on boost's most default +# settings for maximum compatibility. +# +# It's tricky to determine which version of boost we'll be using at this +# point in the script, so we simplify the logic by checking for any of the +# possible boost header locations that are possible outcomes from running +# this script. +BOOST_VERSION_FILES = [ + "include/boost/version.hpp", + "include/boost-1_76/boost/version.hpp", + "include/boost-1_78/boost/version.hpp", + "include/boost-1_82/boost/version.hpp" +] + +def InstallBoost_Helper(context, force, buildArgs): + # In general we use boost 1.76.0 to adhere to VFX Reference Platform CY2022. + # However, there are some cases where a newer version is required. + # - Building with Python 3.11 requires boost 1.82.0 or newer + # (https://github.com/boostorg/python/commit/a218ba) + # - Building with Python 3.10 requires boost 1.76.0 or newer + # (https://github.com/boostorg/python/commit/cbd2d9) + # XXX: Due to a typo we've been using 1.78.0 in this case for a while. + # We're leaving it that way to minimize potential disruption. + # - Building with Visual Studio 2022 requires boost 1.78.0 or newer. + # (https://github.com/boostorg/build/issues/735) + # - Building on MacOS requires boost 1.78.0 or newer to resolve Python 3 + # compatibility issues on Big Sur and Monterey. + pyInfo = GetPythonInfo(context) + pyVer = (int(pyInfo[3].split('.')[0]), int(pyInfo[3].split('.')[1])) + if context.buildPython and pyVer >= (3, 11): + BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.zip" + elif context.buildPython and pyVer >= (3, 10): + BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.zip" + elif IsVisualStudio2022OrGreater(): + BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.zip" + elif MacOS(): + BOOST_URL = "https://sourceforge.net/projects/boost/files/boost/1.78.0/boost_1_78_0.zip/download" + # BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.78.0/source/boost_1_78_0.zip" + else: + BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.zip" + + # Documentation files in the boost archive can have exceptionally + # long paths. This can lead to errors when extracting boost on Windows, + # since paths are limited to 260 characters by default on that platform. + # To avoid this, we skip extracting all documentation. + # + # For some examples, see: https://svn.boost.org/trac10/ticket/11677 + dontExtract = [ + "*/doc/*", + "*/libs/*/doc/*", + "*/libs/wave/test/testwave/testfiles/utf8-test-*" + ] + + with CurrentWorkingDirectory(DownloadURL(BOOST_URL, context, force, + dontExtract=dontExtract)): + if Windows(): + bootstrap = "bootstrap.bat" + else: + bootstrap = "./bootstrap.sh" + # zip doesn't preserve file attributes, so force +x manually. + Run('chmod +x ' + bootstrap) + Run('chmod +x ./tools/build/src/engine/build.sh') + + # For cross-compilation on macOS we need to specify the architecture + # for both the bootstrap and the b2 phase of building boost. + bootstrapCmd = '{bootstrap} --prefix="{instDir}"'.format( + bootstrap=bootstrap, instDir=context.instDir) + + macOSArch = "" + + if MacOS(): + 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}\" " \ + " cflags=\"{0}\" " \ + " linkflags=\"{0}\"".format(macOSArch) + bootstrapCmd += " --with-toolset=clang" + + Run(bootstrapCmd) + + # b2 supports at most -j64 and will error if given a higher value. + num_procs = min(64, context.numJobs) + + # boost only accepts three variants: debug, release, profile + 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.buildPython: + b2_settings.append("--with-python") + pythonInfo = GetPythonInfo(context) + # This is the only platform-independent way to configure these + # settings correctly and robustly for the Boost jam build system. + # There are Python config arguments that can be passed to bootstrap + # but those are not available in boostrap.bat (Windows) so we must + # take the following approach: + projectPath = 'python-config.jam' + with open(projectPath, 'w') as projectFile: + # Note that we must escape any special characters, like + # backslashes for jam, hence the mods below for the path + # arguments. Also, if the path contains spaces jam will not + # handle them well. Surround the path parameters in quotes. + projectFile.write('using python : %s\n' % pythonInfo[3]) + projectFile.write(' : "%s"\n' % pythonInfo[0].replace("\\","/")) + projectFile.write(' : "%s"\n' % pythonInfo[2].replace("\\","/")) + projectFile.write(' : "%s"\n' % os.path.dirname(pythonInfo[1]).replace("\\","/")) + if context.buildDebug and context.debugPython: + projectFile.write(' : on\n') + projectFile.write(' ;\n') + b2_settings.append("--user-config=python-config.jam") + + if context.buildDebug and context.debugPython: + b2_settings.append("python-debugging=on") + + if context.buildOIIO: + b2_settings.append("--with-date_time") + + if context.buildOIIO or context.enableOpenVDB: + b2_settings.append("--with-system") + b2_settings.append("--with-thread") + + if context.enableOpenVDB: + b2_settings.append("--with-iostreams") + + # b2 with -sNO_COMPRESSION=1 fails with the following error message: + # error: at [...]/boost_1_61_0/tools/build/src/kernel/modules.jam:107 + # error: Unable to find file or target named + # error: '/zlib//zlib' + # error: referred to from project at + # error: 'libs/iostreams/build' + # error: could not resolve project reference '/zlib' + + # But to avoid an extra library dependency, we can still explicitly + # exclude the bzip2 compression from boost_iostreams (note that + # OpenVDB uses blosc compression). + b2_settings.append("-sNO_BZIP2=1") + + if context.buildOIIO: + b2_settings.append("--with-filesystem") + + if force: + b2_settings.append("-a") + + if Windows(): + # toolset parameter for Visual Studio documented here: + # https://github.com/boostorg/build/blob/develop/src/tools/msvc.jam + if 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 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(): + # Must specify toolset=clang to ensure install_name for boost + # libraries includes @rpath + b2_settings.append("toolset=clang") + # + # Xcode 15.3 (and hence Apple Clang 15) removed the global + # declaration of std::piecewise_construct which causes boost build + # to fail. + # https://developer.apple.com/documentation/xcode-release-notes/xcode-15_3-release-notes. + # A fix for the same is also available in boost 1.84: + # https://github.com/boostorg/container/commit/79a75f470e75f35f5f2a91e10fcc67d03b0a2160 + b2_settings.append(f"define=BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0") + if macOSArch: + b2_settings.append("cxxflags=\"{0}\"".format(macOSArch)) + b2_settings.append("cflags=\"{0}\"".format(macOSArch)) + b2_settings.append("linkflags=\"{0}\"".format(macOSArch)) + + if context.buildDebug: + b2_settings.append("--debug-configuration") + + # Add on any user-specified extra arguments. + b2_settings += buildArgs + + # Append extra argument controlling libstdc++ ABI if specified. + 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): + # Boost's build system will install the version.hpp header before + # building its libraries. We make sure to remove it in case of + # any failure to ensure that the build script detects boost as a + # dependency to build the next time it's run. + 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) + +############################################################ +# Intel TBB + +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(): + # On MacOS Intel systems we experience various crashes in tests during + # teardown starting with 2018 Update 2. Until we figure that out, we use + # 2018 Update 1 on this platform. + TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2020.3.zip" + TBB_INTEL_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/2018_U1.zip" +else: + TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/refs/tags/v2020.3.zip" + +def InstallTBB(context, force, buildArgs): + if 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)): + # On Windows, we simply copy headers and pre-built DLLs to + # the appropriate location. + 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): + tbb_url = TBB_URL if apple_utils.IsTargetArm(context) else TBB_INTEL_URL + with CurrentWorkingDirectory(DownloadURL(tbb_url, context, force)): + # Ensure that the tbb build system picks the proper architecture. + 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()))]) + + (primaryArch, secondaryArch) = apple_utils.GetTargetArchPair(context) + + # tbb uses different arch names + if (primaryArch == apple_utils.TARGET_X86): + primaryArch = "intel64" + if (secondaryArch == apple_utils.TARGET_X86): + secondaryArch = "intel64" + + # Install both release and debug builds. + # See comments in InstallTBB_Linux. + def _RunBuild(arch): + if not arch: + return + makeTBBCmd = 'make -j{procs} arch={arch} {buildArgs}'.format( + arch=arch, procs=context.numJobs, + buildArgs=" ".join(buildArgs)) + Run(makeTBBCmd) + + _RunBuild(primaryArch) + _RunBuild(secondaryArch) + + # See comments in InstallTBB_Linux about why we patch the Makefile + # and rerun builds. This is only required for TBB 2020; 2019 and + # earlier build both release and debug, and 2021 has moved to CMake. + 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)): + # Append extra argument controlling libstdc++ ABI if specified. + AppendCXX11ABIArg("CXXFLAGS", context, buildArgs) + + # TBB does not support out-of-source builds in a custom location. + makeTBBCmd = 'make -j{procs} {buildArgs}'.format( + procs=context.numJobs, + buildArgs=" ".join(buildArgs)) + Run(makeTBBCmd) + + # Install both release and debug builds. USD requires the debug + # libraries when building in debug mode, and installing both + # makes it easier for users to install dependencies in some + # location that can be shared by both release and debug USD + # builds. + # + # As of TBB 2020 the build no longer produces a debug build along + # with the release build. There is also no way to specify a debug + # build when running make, even though the internals of the build + # still support it. + # + # To workaround this, we patch the Makefile to change "release" to + # "debug" and re-run the build, copying the debug libraries + # afterwards. + # + # See https://github.com/oneapi-src/oneTBB/issues/207/ + 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 + +JPEG_URL = "https://github.com/libjpeg-turbo/libjpeg-turbo/archive/2.0.1.zip" + +def InstallJPEG(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(JPEG_URL, context, force)): + extraJPEGArgs = buildArgs + if not which("nasm"): + extraJPEGArgs.append("-DWITH_SIMD=FALSE") + + RunCMake(context, force, extraJPEGArgs) + return os.getcwd() + +JPEG = Dependency("JPEG", InstallJPEG, "include/jpeglib.h") + +############################################################ +# TIFF + +TIFF_URL = "https://gitlab.com/libtiff/libtiff/-/archive/v4.0.7/libtiff-v4.0.7.zip" + +def InstallTIFF(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(TIFF_URL, context, force)): + # libTIFF has a build issue on Windows where tools/tiffgt.c + # unconditionally includes unistd.h, which does not exist. + # To avoid this, we patch the CMakeLists.txt to skip building + # the tools entirely. We do this on Linux and MacOS as well + # to avoid requiring some GL and X dependencies. + # + # We also need to skip building tests, since they rely on + # the tools we've just elided. + PatchFile("CMakeLists.txt", + [("add_subdirectory(tools)", "# add_subdirectory(tools)"), + ("add_subdirectory(test)", "# add_subdirectory(test)")]) + + # The libTIFF CMakeScript says the ld-version-script + # functionality is only for compilers using GNU ld on + # ELF systems or systems which provide an emulation; therefore + # skipping it completely on mac and windows. + if MacOS() or Windows(): + extraArgs = ["-Dld-version-script=OFF"] + else: + extraArgs = [] + extraArgs += buildArgs + RunCMake(context, force, extraArgs) + +TIFF = Dependency("TIFF", InstallTIFF, "include/tiff.h") + +############################################################ +# PNG + +PNG_URL = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip" + +def InstallPNG(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(PNG_URL, context, force)): + macArgs = [] + if MacOS() and apple_utils.IsTargetArm(context): + # Ensure libpng's build doesn't erroneously activate inappropriate + # Neon extensions + macArgs = ["-DCMAKE_C_FLAGS=\"-DPNG_ARM_NEON_OPT=0\""] + + if context.targetUniversal: + PatchFile("scripts/genout.cmake.in", + [("CMAKE_OSX_ARCHITECTURES", + "CMAKE_OSX_INTERNAL_ARCHITECTURES")]) + + RunCMake(context, force, buildArgs + macArgs) + +PNG = Dependency("PNG", InstallPNG, "include/png.h") + +############################################################ +# IlmBase/OpenEXR + +OPENEXR_URL = "https://github.com/AcademySoftwareFoundation/openexr/archive/refs/tags/v3.1.11.zip" + +def InstallOpenEXR(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(OPENEXR_URL, context, force)): + RunCMake(context, force, + ['-DOPENEXR_INSTALL_TOOLS=OFF', + '-DOPENEXR_INSTALL_EXAMPLES=OFF', + + # Force OpenEXR to build and use a separate Imath library + # instead of looking for one externally. This ensures that + # OpenEXR and other dependencies use the Imath library + # built via this script. + '-DOPENEXR_FORCE_INTERNAL_IMATH=ON', + '-DBUILD_TESTING=OFF'] + buildArgs) + +OPENEXR = Dependency("OpenEXR", InstallOpenEXR, "include/OpenEXR/ImfVersion.h") + +############################################################ +# Ptex + +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', + # We must tell the Ptex build system what version we're building + # otherwise we get errors when running CMake. + '-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 (Compression used by OpenVDB) + +# Using blosc v1.20.1 to avoid build errors on macOS Catalina (10.15) +# related to implicit declaration of functions in zlib. See: +# https://github.com/Blosc/python-blosc/issues/229 +BLOSC_URL = "https://github.com/Blosc/c-blosc/archive/v1.20.1.zip" + +def InstallBLOSC(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(BLOSC_URL, context, force)): + macArgs = [] + if MacOS() and apple_utils.IsTargetArm(context): + # Need to disable SSE for macOS ARM targets. + macArgs = ["-DDEACTIVATE_SSE2=ON"] + RunCMake(context, force, buildArgs + macArgs) + +BLOSC = Dependency("Blosc", InstallBLOSC, "include/blosc.h") + +############################################################ +# OpenVDB + +OPENVDB_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v9.1.0.zip" + +# OpenVDB v9.1.0 requires TBB 2019.0 or above, but this script installs +# TBB 2018 on macOS Intel systems for reasons documented above. So we +# keep OpenVDB at the version specified for the VFX Reference Platform +# CY2021, which is the last version that supported 2018. +OPENVDB_INTEL_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/refs/tags/v8.2.0.zip" + +def InstallOpenVDB(context, force, buildArgs): + openvdb_url = OPENVDB_URL + if MacOS() and not apple_utils.IsTargetArm(context): + openvdb_url = OPENVDB_INTEL_URL + + with CurrentWorkingDirectory(DownloadURL(openvdb_url, context, force)): + extraArgs = [ + '-DOPENVDB_BUILD_PYTHON_MODULE=OFF', + '-DOPENVDB_BUILD_BINARIES=OFF', + '-DOPENVDB_BUILD_UNITTESTS=OFF' + ] + + # Make sure to use boost installed by the build script and not any + # system installed boost + extraArgs.append('-DBoost_NO_BOOST_CMAKE=On') + extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True') + + extraArgs.append('-DBLOSC_ROOT="{instDir}"' + .format(instDir=context.instDir)) + extraArgs.append('-DTBB_ROOT="{instDir}"' + .format(instDir=context.instDir)) + # OpenVDB needs Half type from IlmBase + extraArgs.append('-DILMBASE_ROOT="{instDir}"' + .format(instDir=context.instDir)) + + # Add on any user-specified extra arguments. + extraArgs += buildArgs + + RunCMake(context, force, extraArgs) + +OPENVDB = Dependency("OpenVDB", InstallOpenVDB, "include/openvdb/openvdb.h") + +############################################################ +# OpenImageIO + +OIIO_URL = "https://github.com/OpenImageIO/oiio/archive/refs/tags/v2.3.21.0.zip" + +def InstallOpenImageIO(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(OIIO_URL, context, force)): + # The only time that we want to build tools with OIIO is for testing + # purposes. Libraries such as usdImagingGL might need to use tools like + # idiff to compare the output images from their tests + buildOIIOTools = 'ON' if (context.buildUsdImaging + and context.buildTests) else 'OFF' + extraArgs = ['-DOIIO_BUILD_TOOLS={}'.format(buildOIIOTools), + '-DOIIO_BUILD_TESTS=OFF', + '-DBUILD_DOCS=OFF', + '-DUSE_PYTHON=OFF', + '-DSTOP_ON_WARNING=OFF'] + + # OIIO's FindOpenEXR module circumvents CMake's normal library + # search order, which causes versions of OpenEXR installed in + # /usr/local or other hard-coded locations in the module to + # take precedence over the version we've built, which would + # normally be picked up when we specify CMAKE_PREFIX_PATH. + # This may lead to undefined symbol errors at build or runtime. + # So, we explicitly specify the OpenEXR we want to use here. + extraArgs.append('-DOPENEXR_ROOT="{instDir}"' + .format(instDir=context.instDir)) + + # If Ptex support is disabled in USD, disable support in OpenImageIO + # as well. This ensures OIIO doesn't accidentally pick up a Ptex + # library outside of our build. + if not context.enablePtex: + extraArgs.append('-DUSE_PTEX=OFF') + + # Make sure to use boost installed by the build script and not any + # system installed boost + extraArgs.append('-DBoost_NO_BOOST_CMAKE=On') + extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True') + + # OpenImageIO 2.3.5 changed the default postfix for debug library + # names from "" to "_d". USD's build system currently does not support + # finding the library under this name, so as an interim workaround + # we reset it back to its old value. + extraArgs.append('-DCMAKE_DEBUG_POSTFIX=""') + + # Add on any user-specified extra arguments. + extraArgs += buildArgs + + RunCMake(context, force, extraArgs) + +OPENIMAGEIO = Dependency("OpenImageIO", InstallOpenImageIO, + "include/OpenImageIO/oiioversion.h") + +############################################################ +# OpenColorIO + +OCIO_URL = "https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.1.3.zip" + +def InstallOpenColorIO(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(OCIO_URL, context, force)): + extraArgs = ['-DOCIO_BUILD_APPS=OFF', + '-DOCIO_BUILD_NUKE=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') + + # Add on any user-specified extra arguments. + extraArgs += buildArgs + + RunCMake(context, force, extraArgs) + +OPENCOLORIO = Dependency("OpenColorIO", InstallOpenColorIO, + "include/OpenColorIO/OpenColorABI.h") + +############################################################ +# OpenSubdiv + +OPENSUBDIV_URL = "https://github.com/PixarAnimationStudios/OpenSubdiv/archive/v3_6_0.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', + ] + + # If Ptex support is disabled in USD, disable support in OpenSubdiv + # as well. This ensures OSD doesn't accidentally pick up a Ptex + # library outside of our build. + if not context.enablePtex: + extraArgs.append('-DNO_PTEX=ON') + + # NOTE: For now, we disable TBB in our OpenSubdiv build. + # This avoids an issue where OpenSubdiv will link against + # all TBB libraries it finds, including libtbbmalloc and + # libtbbmalloc_proxy. On Linux and MacOS, this has the + # unwanted effect of replacing the system allocator with + # tbbmalloc. + extraArgs.append('-DNO_TBB=ON') + + # Add on any user-specified extra arguments. + extraArgs += buildArgs + + # OpenSubdiv seems to error when building on windows w/ Ninja... + # ...so just use the default generator (ie, Visual Studio on Windows) + # until someone can sort it out + oldGenerator = context.cmakeGenerator + if oldGenerator == "Ninja" and Windows(): + context.cmakeGenerator = None + + try: + RunCMake(context, force, extraArgs) + finally: + context.cmakeGenerator = oldGenerator + +OPENSUBDIV = Dependency("OpenSubdiv", InstallOpenSubdiv, + "include/opensubdiv/version.h") + +############################################################ +# PyOpenGL + +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"]) + +############################################################ +# PySide + +def GetPySideInstructions(): + # For licensing reasons, this script cannot install PySide itself. + if MacOS(): + # PySide6 is required for Apple Silicon support, so is the default + # across all macOS hardware platforms. + 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"]) + +############################################################ +# HDF5 + +HDF5_URL = "https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.10/hdf5-1.10.0-patch1/src/hdf5-1.10.0-patch1.zip" + +def InstallHDF5(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(HDF5_URL, context, force)): + if MacOS(): + PatchFile("config/cmake_ext_mod/ConfigureChecks.cmake", + [("if (ARCH_LENGTH GREATER 1)", "if (FALSE)")]) + if context.targetUniversal: + PatchFile("config/cmake/H5pubconf.h.in", + [(" #define H5_SIZEOF_LONG_LONG 8", + " #define H5_SIZEOF_LONG_LONG 8\n" + + " #define H5_SIZEOF_LONG_DOUBLE 16")]) + RunCMake(context, force, + ['-DBUILD_TESTING=OFF', + '-DHDF5_BUILD_TOOLS=OFF', + '-DHDF5_BUILD_EXAMPLES=OFF'] + buildArgs) + +HDF5 = Dependency("HDF5", InstallHDF5, "include/hdf5.h") + +############################################################ +# Alembic + +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'] + if context.enableHDF5: + # HDF5 requires the H5_BUILT_AS_DYNAMIC_LIB macro be defined if + # it was built with CMake as a dynamic library. + cmakeOptions += [ + '-DUSE_HDF5=ON', + '-DHDF5_ROOT="{instDir}"'.format(instDir=context.instDir), + '-DCMAKE_CXX_FLAGS="-D H5_BUILT_AS_DYNAMIC_LIB"'] + else: + cmakeOptions += ['-DUSE_HDF5=OFF'] + + cmakeOptions += buildArgs + + RunCMake(context, force, cmakeOptions) + +ALEMBIC = Dependency("Alembic", InstallAlembic, "include/Alembic/Abc/Base.h") + +############################################################ +# Draco + +DRACO_URL = "https://github.com/google/draco/archive/refs/tags/1.3.6.zip" + +def InstallDraco(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(DRACO_URL, context, force)): + cmakeOptions = [ + '-DBUILD_USD_PLUGIN=ON', + ] + cmakeOptions += buildArgs + RunCMake(context, force, cmakeOptions) + +DRACO = Dependency("Draco", InstallDraco, "include/draco/compression/decode.h") + +############################################################ +# MaterialX + +MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.7.zip" + +def InstallMaterialX(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)): + # MaterialX 1.38.7 fails to build on windows using VS2017 because of a + # missing header include, following patch fixes the same. This patch + # should be removed when underlying issue is resolved. + # https://github.com/AcademySoftwareFoundation/MaterialX/issues/1401 + if IsVisualStudio2017OrGreater() and not IsVisualStudio2019OrGreater(): + PatchFile("source\\MaterialXGenMsl\\MslShaderGenerator.cpp", + [("#include ", + "#include \n" + + "#include ")]) + cmakeOptions = ['-DMATERIALX_BUILD_SHARED_LIBS=ON', + '-DMATERIALX_BUILD_TESTS=OFF' + ] + cmakeOptions += buildArgs + RunCMake(context, force, cmakeOptions) + +MATERIALX = Dependency("MaterialX", InstallMaterialX, "include/MaterialXCore/Library.h") + +############################################################ +# Embree +# For MacOS we use version 3.13.3 to include a fix from Intel +# to build on Apple Silicon. +if MacOS(): + EMBREE_URL = "https://github.com/embree/embree/archive/v3.13.3.zip" +else: + EMBREE_URL = "https://github.com/embree/embree/archive/v3.2.2.zip" + +def InstallEmbree(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(EMBREE_URL, context, force)): + extraArgs = [ + '-DTBB_ROOT={instDir}'.format(instDir=context.instDir), + '-DEMBREE_TUTORIALS=OFF', + '-DEMBREE_ISPC_SUPPORT=OFF' + ] + + if MacOS() and context.targetUniversal: + extraArgs += [ + '-DEMBREE_MAX_ISA=NEON', + '-DEMBREE_ISA_NEON=ON'] + + extraArgs += buildArgs + + RunCMake(context, force, extraArgs) + +EMBREE = Dependency("Embree", InstallEmbree, "include/embree3/rtcore.h") + +############################################################ +# AnimX + +# This GitHub project has no releases, so we take the latest. +# As of 2023, there have been no commits since 2018. +ANIMX_URL = "https://github.com/Autodesk/animx/archive/refs/heads/master.zip" + +def InstallAnimX(context, force, buildArgs): + with CurrentWorkingDirectory(DownloadURL(ANIMX_URL, context, force)): + # AnimX strangely installs its output to the inst root, rather than the + # lib subdirectory. Fix. + PatchFile("src/CMakeLists.txt", + [("LIBRARY DESTINATION .", "LIBRARY DESTINATION lib")]) + + extraArgs = [ + '-DANIMX_BUILD_MAYA_TESTSUITE=OFF', + '-DMAYA_64BIT_TIME_PRECISION=ON', + '-DANIMX_BUILD_SHARED=ON', + '-DANIMX_BUILD_STATIC=OFF' + ] + RunCMake(context, force, extraArgs) + +ANIMX = Dependency("AnimX", InstallAnimX, "include/animx.h") + + +############################################################ +# USD + +def InstallUSD(context, force, buildArgs): + with CurrentWorkingDirectory(context.usdSrcDir): + extraArgs = [] + + extraArgs.append('-DPXR_PREFER_SAFETY_OVER_SPEED={}' + .format('ON' if context.safetyFirst else 'OFF')) + + if context.buildPython: + extraArgs.append('-DPXR_ENABLE_PYTHON_SUPPORT=ON') + + # Many people on Windows may not have Python libraries with debug + # symbols (denoted by a '_d') installed. This is the common + # case when a user installs Python from the official download + # website. Therefore we can still let people decide to build USD + # with the release version of Python if debugging into Python land + # is not what they want which can be done by setting the debugPython + # argument. + if context.buildDebug and context.debugPython: + extraArgs.append('-DPXR_USE_DEBUG_PYTHON=ON') + else: + extraArgs.append('-DPXR_USE_DEBUG_PYTHON=OFF') + + # CMake has trouble finding the executable, library, and include + # directories when there are multiple versions of Python installed. + # This can lead to crashes due to USD being linked against one + # version of Python but running through some other Python + # interpreter version. This primarily shows up on macOS, as it's + # common to have a Python install that's separate from the one + # included with the system. + # + # To avoid this, we try to determine these paths from Python + # itself rather than rely on CMake's heuristics. + 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.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 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') + + 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') + if context.enableHDF5: + extraArgs.append('-DPXR_ENABLE_HDF5_SUPPORT=ON') + + # CMAKE_PREFIX_PATH isn't sufficient for the FindHDF5 module + # to find the HDF5 we've built, so provide an extra hint. + extraArgs.append('-DHDF5_ROOT="{instDir}"' + .format(instDir=context.instDir)) + else: + extraArgs.append('-DPXR_ENABLE_HDF5_SUPPORT=OFF') + 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 context.buildMayapyTests: + extraArgs.append('-DPXR_BUILD_MAYAPY_TESTS=ON') + extraArgs.append('-DMAYAPY_LOCATION="{mayapyLocation}"' + .format(mayapyLocation=context.mayapyLocation)) + else: + extraArgs.append('-DPXR_BUILD_MAYAPY_TESTS=OFF') + + if context.buildAnimXTests: + extraArgs.append('-DPXR_BUILD_ANIMX_TESTS=ON') + else: + extraArgs.append('-DPXR_BUILD_ANIMX_TESTS=OFF') + + if Windows(): + # Increase the precompiled header buffer limit. + extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"') + + # Make sure to use boost installed by the build script and not any + # system installed boost + extraArgs.append('-DBoost_NO_BOOST_CMAKE=On') + extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True') + extraArgs += buildArgs + + RunCMake(context, force, extraArgs) + +USD = Dependency("USD", InstallUSD, "include/pxr/pxr.h") + +############################################################ +# Install script + +programDescription = """\ +Installation Script for USD + +Builds and installs USD and 3rd-party dependencies to specified location. + +- 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 ,