1#
2# Copyright 2017 Pixar
3#
4# Licensed under the Apache License, Version 2.0 (the "Apache License")
5# with the following modification; you may not use this file except in
6# compliance with the Apache License and the following modification to it:
7# Section 6. Trademarks. is deleted and replaced with:
8#
9# 6. Trademarks. This License does not grant permission to use the trade
10#    names, trademarks, service marks, or product names of the Licensor
11#    and its affiliates, except as required to comply with Section 4(c) of
12#    the License and to reproduce the content of the NOTICE file.
13#
14# You may obtain a copy of the Apache License at
15#
16#     http://www.apache.org/licenses/LICENSE-2.0
17#
18# Unless required by applicable law or agreed to in writing, software
19# distributed under the Apache License with the above modification is
20# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21# KIND, either express or implied. See the Apache License for the specific
22# language governing permissions and limitations under the Apache License.
23#
24from __future__ import print_function
25
26import argparse
27import codecs
28import contextlib
29import ctypes
30import datetime
31import distutils
32import fnmatch
33import glob
34import locale
35import multiprocessing
36import os
37import platform
38import re
39import shlex
40import shutil
41import subprocess
42import sys
43import sysconfig
44import tarfile
45import zipfile
46
47if sys.version_info.major >= 3:
48    from urllib.request import urlopen
49    from shutil import which
50else:
51    from urllib2 import urlopen
52
53    # Doesn't deal with .bat / .cmd like shutil.which, but only option
54    # available with stock python-2
55    from distutils.spawn import find_executable as which
56
57# Helpers for printing output
58verbosity = 1
59
60def Print(msg):
61    if verbosity > 0:
62        print(msg)
63
64def PrintWarning(warning):
65    if verbosity > 0:
66        print("WARNING:", warning)
67
68def PrintStatus(status):
69    if verbosity >= 1:
70        print("STATUS:", status)
71
72def PrintInfo(info):
73    if verbosity >= 2:
74        print("INFO:", info)
75
76def PrintCommandOutput(output):
77    if verbosity >= 3:
78        sys.stdout.write(output)
79
80def PrintError(error):
81    if verbosity >= 3 and sys.exc_info()[1] is not None:
82        import traceback
83        traceback.print_exc()
84    print ("ERROR:", error)
85
86# Helpers for determining platform
87def Windows():
88    return platform.system() == "Windows"
89def Linux():
90    return platform.system() == "Linux"
91def MacOS():
92    return platform.system() == "Darwin"
93
94def Python3():
95    return sys.version_info.major == 3
96
97def GetLocale():
98    return sys.stdout.encoding or locale.getdefaultlocale()[1] or "UTF-8"
99
100def GetCommandOutput(command):
101    """Executes the specified command and returns output or None."""
102    try:
103        return subprocess.check_output(
104            shlex.split(command),
105            stderr=subprocess.STDOUT).decode(GetLocale(), 'replace').strip()
106    except subprocess.CalledProcessError:
107        pass
108    return None
109
110def GetXcodeDeveloperDirectory():
111    """Returns the active developer directory as reported by 'xcode-select -p'.
112    Returns None if none is set."""
113    if not MacOS():
114        return None
115
116    return GetCommandOutput("xcode-select -p")
117
118def GetVisualStudioCompilerAndVersion():
119    """Returns a tuple containing the path to the Visual Studio compiler
120    and a tuple for its version, e.g. (14, 0). If the compiler is not found
121    or version number cannot be determined, returns None."""
122    if not Windows():
123        return None
124
125    msvcCompiler = which('cl')
126    if msvcCompiler:
127        # VisualStudioVersion environment variable should be set by the
128        # Visual Studio Command Prompt.
129        match = re.search(
130            r"(\d+)\.(\d+)",
131            os.environ.get("VisualStudioVersion", ""))
132        if match:
133            return (msvcCompiler, tuple(int(v) for v in match.groups()))
134    return None
135
136def IsVisualStudioVersionOrGreater(desiredVersion):
137    if not Windows():
138        return False
139
140    msvcCompilerAndVersion = GetVisualStudioCompilerAndVersion()
141    if msvcCompilerAndVersion:
142        _, version = msvcCompilerAndVersion
143        return version >= desiredVersion
144    return False
145
146def IsVisualStudio2019OrGreater():
147    VISUAL_STUDIO_2019_VERSION = (16, 0)
148    return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2019_VERSION)
149
150def IsVisualStudio2017OrGreater():
151    VISUAL_STUDIO_2017_VERSION = (15, 0)
152    return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2017_VERSION)
153
154def IsVisualStudio2015OrGreater():
155    VISUAL_STUDIO_2015_VERSION = (14, 0)
156    return IsVisualStudioVersionOrGreater(VISUAL_STUDIO_2015_VERSION)
157
158def IsMayaPython():
159    """Determine whether we're running in Maya's version of Python. When
160    building against Maya's Python, there are some additional restrictions
161    on what we're able to build."""
162    try:
163        import maya
164        return True
165    except:
166        pass
167
168    return False
169
170def GetPythonInfo(context):
171    """Returns a tuple containing the path to the Python executable, shared
172    library, and include directory corresponding to the version of Python
173    currently running. Returns None if any path could not be determined.
174
175    This function is used to extract build information from the Python
176    interpreter used to launch this script. This information is used
177    in the Boost and USD builds. By taking this approach we can support
178    having USD builds for different Python versions built on the same
179    machine. This is very useful, especially when developers have multiple
180    versions installed on their machine, which is quite common now with
181    Python2 and Python3 co-existing.
182    """
183
184    # If we were given build python info then just use it.
185    if context.build_python_info:
186        return (context.build_python_info['PYTHON_EXECUTABLE'],
187                context.build_python_info['PYTHON_LIBRARY'],
188                context.build_python_info['PYTHON_INCLUDE_DIR'],
189                context.build_python_info['PYTHON_VERSION'])
190
191    # First we extract the information that can be uniformly dealt with across
192    # the platforms:
193    pythonExecPath = sys.executable
194    pythonVersion = sysconfig.get_config_var("py_version_short")  # "2.7"
195    pythonVersionNoDot = sysconfig.get_config_var("py_version_nodot") # "27"
196
197    # Lib path is unfortunately special for each platform and there is no
198    # config_var for it. But we can deduce it for each platform, and this
199    # logic works for any Python version.
200    def _GetPythonLibraryFilename(context):
201        if Windows():
202            suffix = '_d' if context.buildDebug and context.debugPython else ''
203            return "python" + pythonVersionNoDot + suffix + ".lib"
204        elif Linux():
205            return sysconfig.get_config_var("LDLIBRARY")
206        elif MacOS():
207            return "libpython" + pythonVersion + ".dylib"
208        else:
209            raise RuntimeError("Platform not supported")
210
211    # XXX: Handle the case where this script is being called using Maya's
212    # Python since the sysconfig variables are set up differently in Maya.
213    # Ideally we would not have any special Maya knowledge in here at all.
214    if IsMayaPython():
215        pythonBaseDir = sysconfig.get_config_var("base")
216
217        # On Windows, the "base" path points to a "Python\" subdirectory
218        # that contains the DLLs for site-package modules but not the
219        # directories for the headers and .lib file we need -- those
220        # are one level up.
221        if Windows():
222            pythonBaseDir = os.path.dirname(pythonBaseDir)
223
224        pythonIncludeDir = os.path.join(pythonBaseDir, "include",
225                                        "python" + pythonVersion)
226        pythonLibPath = os.path.join(pythonBaseDir, "lib",
227                                     _GetPythonLibraryFilename(context))
228    else:
229        pythonIncludeDir = sysconfig.get_config_var("INCLUDEPY")
230        if Windows():
231            pythonBaseDir = sysconfig.get_config_var("base")
232            pythonLibPath = os.path.join(pythonBaseDir, "libs",
233                                         _GetPythonLibraryFilename(context))
234        elif Linux():
235            pythonLibDir = sysconfig.get_config_var("LIBDIR")
236            pythonMultiarchSubdir = sysconfig.get_config_var("multiarchsubdir")
237            if pythonMultiarchSubdir:
238                pythonLibDir = pythonLibDir + pythonMultiarchSubdir
239            pythonLibPath = os.path.join(pythonLibDir,
240                                         _GetPythonLibraryFilename(context))
241        elif MacOS():
242            pythonBaseDir = sysconfig.get_config_var("base")
243            pythonLibPath = os.path.join(pythonBaseDir, "lib",
244                                         _GetPythonLibraryFilename(context))
245        else:
246            raise RuntimeError("Platform not supported")
247
248    return (pythonExecPath, pythonLibPath, pythonIncludeDir, pythonVersion)
249
250def GetCPUCount():
251    try:
252        return multiprocessing.cpu_count()
253    except NotImplementedError:
254        return 1
255
256def Run(cmd, logCommandOutput = True):
257    """Run the specified command in a subprocess."""
258    PrintInfo('Running "{cmd}"'.format(cmd=cmd))
259
260    with codecs.open("log.txt", "a", "utf-8") as logfile:
261        logfile.write(datetime.datetime.now().strftime("%Y-%m-%d %H:%M"))
262        logfile.write("\n")
263        logfile.write(cmd)
264        logfile.write("\n")
265
266        # Let exceptions escape from subprocess calls -- higher level
267        # code will handle them.
268        if logCommandOutput:
269            p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE,
270                                 stderr=subprocess.STDOUT)
271            while True:
272                l = p.stdout.readline().decode(GetLocale(), 'replace')
273                if l:
274                    logfile.write(l)
275                    PrintCommandOutput(l)
276                elif p.poll() is not None:
277                    break
278        else:
279            p = subprocess.Popen(shlex.split(cmd))
280            p.wait()
281
282    if p.returncode != 0:
283        # If verbosity >= 3, we'll have already been printing out command output
284        # so no reason to print the log file again.
285        if verbosity < 3:
286            with open("log.txt", "r") as logfile:
287                Print(logfile.read())
288        raise RuntimeError("Failed to run '{cmd}'\nSee {log} for more details."
289                           .format(cmd=cmd, log=os.path.abspath("log.txt")))
290
291@contextlib.contextmanager
292def CurrentWorkingDirectory(dir):
293    """Context manager that sets the current working directory to the given
294    directory and resets it to the original directory when closed."""
295    curdir = os.getcwd()
296    os.chdir(dir)
297    try: yield
298    finally: os.chdir(curdir)
299
300def CopyFiles(context, src, dest):
301    """Copy files like shutil.copy, but src may be a glob pattern."""
302    filesToCopy = glob.glob(src)
303    if not filesToCopy:
304        raise RuntimeError("File(s) to copy {src} not found".format(src=src))
305
306    instDestDir = os.path.join(context.instDir, dest)
307    for f in filesToCopy:
308        PrintCommandOutput("Copying {file} to {destDir}\n"
309                           .format(file=f, destDir=instDestDir))
310        shutil.copy(f, instDestDir)
311
312def CopyDirectory(context, srcDir, destDir):
313    """Copy directory like shutil.copytree."""
314    instDestDir = os.path.join(context.instDir, destDir)
315    if os.path.isdir(instDestDir):
316        shutil.rmtree(instDestDir)
317
318    PrintCommandOutput("Copying {srcDir} to {destDir}\n"
319                       .format(srcDir=srcDir, destDir=instDestDir))
320    shutil.copytree(srcDir, instDestDir)
321
322def AppendCXX11ABIArg(buildFlag, context, buildArgs):
323    """Append a build argument that defines _GLIBCXX_USE_CXX11_ABI
324    based on the settings in the context. This may either do nothing
325    or append an entry to buildArgs like:
326
327      <buildFlag>="-D_GLIBCXX_USE_CXX11_ABI={0, 1}"
328
329    If buildArgs contains settings for buildFlag, those settings will
330    be merged with the above define."""
331    if context.useCXX11ABI is None:
332        return
333
334    cxxFlags = ["-D_GLIBCXX_USE_CXX11_ABI={}".format(context.useCXX11ABI)]
335
336    # buildArgs might look like:
337    # ["-DFOO=1", "-DBAR=2", ...] or ["-DFOO=1 -DBAR=2 ...", ...]
338    #
339    # See if any of the arguments in buildArgs start with the given
340    # buildFlag. If so, we want to take whatever that buildFlag has
341    # been set to and merge it in with the cxxFlags above.
342    #
343    # For example, if buildArgs = ['-DCMAKE_CXX_FLAGS="-w"', ...]
344    # we want to add "-w" to cxxFlags.
345    splitArgs = [shlex.split(a) for a in buildArgs]
346    for p in [item for arg in splitArgs for item in arg]:
347        if p.startswith(buildFlag):
348            (_, _, flags) = p.partition("=")
349            cxxFlags.append(flags)
350
351    buildArgs.append('{flag}="{flags}"'.format(
352        flag=buildFlag, flags=" ".join(cxxFlags)))
353
354def FormatMultiProcs(numJobs, generator):
355    tag = "-j"
356    if generator:
357        if "Visual Studio" in generator:
358            tag = "/M:" # This will build multiple projects at once.
359        elif "Xcode" in generator:
360            tag = "-j "
361
362    return "{tag}{procs}".format(tag=tag, procs=numJobs)
363
364def RunCMake(context, force, extraArgs = None):
365    """Invoke CMake to configure, build, and install a library whose
366    source code is located in the current working directory."""
367    # Create a directory for out-of-source builds in the build directory
368    # using the name of the current working directory.
369    srcDir = os.getcwd()
370    instDir = (context.usdInstDir if srcDir == context.usdSrcDir
371               else context.instDir)
372    buildDir = os.path.join(context.buildDir, os.path.split(srcDir)[1])
373    if force and os.path.isdir(buildDir):
374        shutil.rmtree(buildDir)
375
376    if not os.path.isdir(buildDir):
377        os.makedirs(buildDir)
378
379    generator = context.cmakeGenerator
380
381    # On Windows, we need to explicitly specify the generator to ensure we're
382    # building a 64-bit project. (Surely there is a better way to do this?)
383    # TODO: figure out exactly what "vcvarsall.bat x64" sets to force x64
384    if generator is None and Windows():
385        if IsVisualStudio2019OrGreater():
386            generator = "Visual Studio 16 2019"
387        elif IsVisualStudio2017OrGreater():
388            generator = "Visual Studio 15 2017 Win64"
389        else:
390            generator = "Visual Studio 14 2015 Win64"
391
392    if generator is not None:
393        generator = '-G "{gen}"'.format(gen=generator)
394
395    if IsVisualStudio2019OrGreater():
396        generator = generator + " -A x64"
397
398    toolset = context.cmakeToolset
399    if toolset is not None:
400        toolset = '-T "{toolset}"'.format(toolset=toolset)
401
402    # On MacOS, enable the use of @rpath for relocatable builds.
403    osx_rpath = None
404    if MacOS():
405        osx_rpath = "-DCMAKE_MACOSX_RPATH=ON"
406
407    # We use -DCMAKE_BUILD_TYPE for single-configuration generators
408    # (Ninja, make), and --config for multi-configuration generators
409    # (Visual Studio); technically we don't need BOTH at the same
410    # time, but specifying both is simpler than branching
411    config = "Release"
412    if context.buildDebug:
413        config = "Debug"
414    elif context.buildRelease:
415        config = "Release"
416    elif context.buildRelWithDebug:
417        config = "RelWithDebInfo"
418
419    # Append extra argument controlling libstdc++ ABI if specified.
420    AppendCXX11ABIArg("-DCMAKE_CXX_FLAGS", context, extraArgs)
421
422    with CurrentWorkingDirectory(buildDir):
423        Run('cmake '
424            '-DCMAKE_INSTALL_PREFIX="{instDir}" '
425            '-DCMAKE_PREFIX_PATH="{depsInstDir}" '
426            '-DCMAKE_BUILD_TYPE={config} '
427            '{osx_rpath} '
428            '{generator} '
429            '{toolset} '
430            '{extraArgs} '
431            '"{srcDir}"'
432            .format(instDir=instDir,
433                    depsInstDir=context.instDir,
434                    config=config,
435                    srcDir=srcDir,
436                    osx_rpath=(osx_rpath or ""),
437                    generator=(generator or ""),
438                    toolset=(toolset or ""),
439                    extraArgs=(" ".join(extraArgs) if extraArgs else "")))
440        Run("cmake --build . --config {config} --target install -- {multiproc}"
441            .format(config=config,
442                    multiproc=FormatMultiProcs(context.numJobs, generator)))
443
444def GetCMakeVersion():
445    """
446    Returns the CMake version as tuple of integers (major, minor) or
447    (major, minor, patch) or None if an error occured while launching cmake and
448    parsing its output.
449    """
450
451    output_string = GetCommandOutput("cmake --version")
452    if not output_string:
453        PrintWarning("Could not determine cmake version -- please install it "
454                     "and adjust your PATH")
455        return None
456
457    # cmake reports, e.g., "... version 3.14.3"
458    match = re.search(r"version (\d+)\.(\d+)(\.(\d+))?", output_string)
459    if not match:
460        PrintWarning("Could not determine cmake version")
461        return None
462
463    major, minor, patch_group, patch = match.groups()
464    if patch_group is None:
465        return (int(major), int(minor))
466    else:
467        return (int(major), int(minor), int(patch))
468
469def PatchFile(filename, patches, multiLineMatches=False):
470    """Applies patches to the specified file. patches is a list of tuples
471    (old string, new string)."""
472    if multiLineMatches:
473        oldLines = [open(filename, 'r').read()]
474    else:
475        oldLines = open(filename, 'r').readlines()
476    newLines = oldLines
477    for (oldString, newString) in patches:
478        newLines = [s.replace(oldString, newString) for s in newLines]
479    if newLines != oldLines:
480        PrintInfo("Patching file {filename} (original in {oldFilename})..."
481                  .format(filename=filename, oldFilename=filename + ".old"))
482        shutil.copy(filename, filename + ".old")
483        open(filename, 'w').writelines(newLines)
484
485def DownloadFileWithCurl(url, outputFilename):
486    # Don't log command output so that curl's progress
487    # meter doesn't get written to the log file.
488    Run("curl {progress} -L -o {filename} {url}".format(
489        progress="-#" if verbosity >= 2 else "-s",
490        filename=outputFilename, url=url),
491        logCommandOutput=False)
492
493def DownloadFileWithPowershell(url, outputFilename):
494    # It's important that we specify to use TLS v1.2 at least or some
495    # of the downloads will fail.
496    cmd = "powershell [Net.ServicePointManager]::SecurityProtocol = \
497            [Net.SecurityProtocolType]::Tls12; \"(new-object \
498            System.Net.WebClient).DownloadFile('{url}', '{filename}')\""\
499            .format(filename=outputFilename, url=url)
500
501    Run(cmd,logCommandOutput=False)
502
503def DownloadFileWithUrllib(url, outputFilename):
504    r = urlopen(url)
505    with open(outputFilename, "wb") as outfile:
506        outfile.write(r.read())
507
508def DownloadURL(url, context, force, extractDir = None,
509        dontExtract = None):
510    """Download and extract the archive file at given URL to the
511    source directory specified in the context.
512
513    dontExtract may be a sequence of path prefixes that will
514    be excluded when extracting the archive.
515
516    Returns the absolute path to the directory where files have
517    been extracted."""
518    with CurrentWorkingDirectory(context.srcDir):
519        # Extract filename from URL and see if file already exists.
520        filename = url.split("/")[-1]
521        if force and os.path.exists(filename):
522            os.remove(filename)
523
524        if os.path.exists(filename):
525            PrintInfo("{0} already exists, skipping download"
526                      .format(os.path.abspath(filename)))
527        else:
528            PrintInfo("Downloading {0} to {1}"
529                      .format(url, os.path.abspath(filename)))
530
531            # To work around occasional hiccups with downloading from websites
532            # (SSL validation errors, etc.), retry a few times if we don't
533            # succeed in downloading the file.
534            maxRetries = 5
535            lastError = None
536
537            # Download to a temporary file and rename it to the expected
538            # filename when complete. This ensures that incomplete downloads
539            # will be retried if the script is run again.
540            tmpFilename = filename + ".tmp"
541            if os.path.exists(tmpFilename):
542                os.remove(tmpFilename)
543
544            for i in range(maxRetries):
545                try:
546                    context.downloader(url, tmpFilename)
547                    break
548                except Exception as e:
549                    PrintCommandOutput("Retrying download due to error: {err}\n"
550                                       .format(err=e))
551                    lastError = e
552            else:
553                errorMsg = str(lastError)
554                if "SSL: TLSV1_ALERT_PROTOCOL_VERSION" in errorMsg:
555                    errorMsg += ("\n\n"
556                                 "Your OS or version of Python may not support "
557                                 "TLS v1.2+, which is required for downloading "
558                                 "files from certain websites. This support "
559                                 "was added in Python 2.7.9."
560                                 "\n\n"
561                                 "You can use curl to download dependencies "
562                                 "by installing it in your PATH and re-running "
563                                 "this script.")
564                raise RuntimeError("Failed to download {url}: {err}"
565                                   .format(url=url, err=errorMsg))
566
567            shutil.move(tmpFilename, filename)
568
569        # Open the archive and retrieve the name of the top-most directory.
570        # This assumes the archive contains a single directory with all
571        # of the contents beneath it, unless a specific extractDir is specified,
572        # which is to be used.
573        archive = None
574        rootDir = None
575        members = None
576        try:
577            if tarfile.is_tarfile(filename):
578                archive = tarfile.open(filename)
579                if extractDir:
580                    rootDir = extractDir
581                else:
582                    rootDir = archive.getnames()[0].split('/')[0]
583                if dontExtract != None:
584                    members = (m for m in archive.getmembers()
585                               if not any((fnmatch.fnmatch(m.name, p)
586                                           for p in dontExtract)))
587            elif zipfile.is_zipfile(filename):
588                archive = zipfile.ZipFile(filename)
589                if extractDir:
590                    rootDir = extractDir
591                else:
592                    rootDir = archive.namelist()[0].split('/')[0]
593                if dontExtract != None:
594                    members = (m for m in archive.getnames()
595                               if not any((fnmatch.fnmatch(m, p)
596                                           for p in dontExtract)))
597            else:
598                raise RuntimeError("unrecognized archive file type")
599
600            with archive:
601                extractedPath = os.path.abspath(rootDir)
602                if force and os.path.isdir(extractedPath):
603                    shutil.rmtree(extractedPath)
604
605                if os.path.isdir(extractedPath):
606                    PrintInfo("Directory {0} already exists, skipping extract"
607                              .format(extractedPath))
608                else:
609                    PrintInfo("Extracting archive to {0}".format(extractedPath))
610
611                    # Extract to a temporary directory then move the contents
612                    # to the expected location when complete. This ensures that
613                    # incomplete extracts will be retried if the script is run
614                    # again.
615                    tmpExtractedPath = os.path.abspath("extract_dir")
616                    if os.path.isdir(tmpExtractedPath):
617                        shutil.rmtree(tmpExtractedPath)
618
619                    archive.extractall(tmpExtractedPath, members=members)
620
621                    shutil.move(os.path.join(tmpExtractedPath, rootDir),
622                                extractedPath)
623                    shutil.rmtree(tmpExtractedPath)
624
625                return extractedPath
626        except Exception as e:
627            # If extraction failed for whatever reason, assume the
628            # archive file was bad and move it aside so that re-running
629            # the script will try downloading and extracting again.
630            shutil.move(filename, filename + ".bad")
631            raise RuntimeError("Failed to extract archive {filename}: {err}"
632                               .format(filename=filename, err=e))
633
634############################################################
635# 3rd-Party Dependencies
636
637AllDependencies = list()
638AllDependenciesByName = dict()
639
640class Dependency(object):
641    def __init__(self, name, installer, *files):
642        self.name = name
643        self.installer = installer
644        self.filesToCheck = files
645
646        AllDependencies.append(self)
647        AllDependenciesByName.setdefault(name.lower(), self)
648
649    def Exists(self, context):
650        return all([os.path.isfile(os.path.join(context.instDir, f))
651                    for f in self.filesToCheck])
652
653class PythonDependency(object):
654    def __init__(self, name, getInstructions, moduleNames):
655        self.name = name
656        self.getInstructions = getInstructions
657        self.moduleNames = moduleNames
658
659    def Exists(self, context):
660        # If one of the modules in our list imports successfully, we are good.
661        for moduleName in self.moduleNames:
662            try:
663                pyModule = __import__(moduleName)
664                return True
665            except:
666                pass
667
668        return False
669
670def AnyPythonDependencies(deps):
671    return any([type(d) is PythonDependency for d in deps])
672
673############################################################
674# zlib
675
676ZLIB_URL = "https://github.com/madler/zlib/archive/v1.2.11.zip"
677
678def InstallZlib(context, force, buildArgs):
679    with CurrentWorkingDirectory(DownloadURL(ZLIB_URL, context, force)):
680        RunCMake(context, force, buildArgs)
681
682ZLIB = Dependency("zlib", InstallZlib, "include/zlib.h")
683
684############################################################
685# boost
686
687if MacOS():
688    BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.gz"
689    BOOST_VERSION_FILE = "include/boost/version.hpp"
690elif Linux():
691    if Python3():
692        BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.gz"
693    else:
694        BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.66.0/source/boost_1_66_0.tar.gz"
695    BOOST_VERSION_FILE = "include/boost/version.hpp"
696elif Windows():
697    # The default installation of boost on Windows puts headers in a versioned
698    # subdirectory, which we have to account for here. In theory, specifying
699    # "layout=system" would make the Windows install match Linux/MacOS, but that
700    # causes problems for other dependencies that look for boost.
701    #
702    # boost 1.70 is required for Visual Studio 2019. For simplicity, we use
703    # this version for all older Visual Studio versions as well.
704    BOOST_URL = "https://boostorg.jfrog.io/artifactory/main/release/1.70.0/source/boost_1_70_0.tar.gz"
705    BOOST_VERSION_FILE = "include/boost-1_70/boost/version.hpp"
706
707def InstallBoost_Helper(context, force, buildArgs):
708    # Documentation files in the boost archive can have exceptionally
709    # long paths. This can lead to errors when extracting boost on Windows,
710    # since paths are limited to 260 characters by default on that platform.
711    # To avoid this, we skip extracting all documentation.
712    #
713    # For some examples, see: https://svn.boost.org/trac10/ticket/11677
714    dontExtract = ["*/doc/*", "*/libs/*/doc/*"]
715
716    with CurrentWorkingDirectory(DownloadURL(BOOST_URL, context, force,
717                                             dontExtract=dontExtract)):
718        bootstrap = "bootstrap.bat" if Windows() else "./bootstrap.sh"
719        Run('{bootstrap} --prefix="{instDir}"'
720            .format(bootstrap=bootstrap, instDir=context.instDir))
721
722        # b2 supports at most -j64 and will error if given a higher value.
723        num_procs = min(64, context.numJobs)
724
725        # boost only accepts three variants: debug, release, profile
726        boostBuildVariant = "profile"
727        if context.buildDebug:
728            boostBuildVariant= "debug"
729        elif context.buildRelease:
730            boostBuildVariant= "release"
731        elif context.buildRelWithDebug:
732            boostBuildVariant= "profile"
733
734        b2_settings = [
735            '--prefix="{instDir}"'.format(instDir=context.instDir),
736            '--build-dir="{buildDir}"'.format(buildDir=context.buildDir),
737            '-j{procs}'.format(procs=num_procs),
738            'address-model=64',
739            'link=shared',
740            'runtime-link=shared',
741            'threading=multi',
742            'variant={variant}'.format(variant=boostBuildVariant),
743            '--with-atomic',
744            '--with-program_options',
745            '--with-regex'
746        ]
747
748        if context.buildPython:
749            b2_settings.append("--with-python")
750            pythonInfo = GetPythonInfo(context)
751            # This is the only platform-independent way to configure these
752            # settings correctly and robustly for the Boost jam build system.
753            # There are Python config arguments that can be passed to bootstrap
754            # but those are not available in boostrap.bat (Windows) so we must
755            # take the following approach:
756            projectPath = 'python-config.jam'
757            with open(projectPath, 'w') as projectFile:
758                # Note that we must escape any special characters, like
759                # backslashes for jam, hence the mods below for the path
760                # arguments. Also, if the path contains spaces jam will not
761                # handle them well. Surround the path parameters in quotes.
762                projectFile.write('using python : %s\n' % pythonInfo[3])
763                projectFile.write('  : "%s"\n' % pythonInfo[0].replace("\\","/"))
764                projectFile.write('  : "%s"\n' % pythonInfo[2].replace("\\","/"))
765                projectFile.write('  : "%s"\n' % os.path.dirname(pythonInfo[1]).replace("\\","/"))
766                if context.buildDebug and context.debugPython:
767                    projectFile.write('  : <python-debugging>on\n')
768                projectFile.write('  ;\n')
769            b2_settings.append("--user-config=python-config.jam")
770
771            if context.buildDebug and context.debugPython:
772                b2_settings.append("python-debugging=on")
773
774        if context.buildOIIO:
775            b2_settings.append("--with-date_time")
776
777        if context.buildOIIO or context.enableOpenVDB:
778            b2_settings.append("--with-system")
779            b2_settings.append("--with-thread")
780
781        if context.enableOpenVDB:
782            b2_settings.append("--with-iostreams")
783
784            # b2 with -sNO_COMPRESSION=1 fails with the following error message:
785            #     error: at [...]/boost_1_61_0/tools/build/src/kernel/modules.jam:107
786            #     error: Unable to find file or target named
787            #     error:     '/zlib//zlib'
788            #     error: referred to from project at
789            #     error:     'libs/iostreams/build'
790            #     error: could not resolve project reference '/zlib'
791
792            # But to avoid an extra library dependency, we can still explicitly
793            # exclude the bzip2 compression from boost_iostreams (note that
794            # OpenVDB uses blosc compression).
795            b2_settings.append("-sNO_BZIP2=1")
796
797        if context.buildOIIO:
798            b2_settings.append("--with-filesystem")
799
800        if force:
801            b2_settings.append("-a")
802
803        if Windows():
804            # toolset parameter for Visual Studio documented here:
805            # https://github.com/boostorg/build/blob/develop/src/tools/msvc.jam
806            if context.cmakeToolset == "v142":
807                b2_settings.append("toolset=msvc-14.2")
808            elif context.cmakeToolset == "v141":
809                b2_settings.append("toolset=msvc-14.1")
810            elif context.cmakeToolset == "v140":
811                b2_settings.append("toolset=msvc-14.0")
812            elif IsVisualStudio2019OrGreater():
813                b2_settings.append("toolset=msvc-14.2")
814            elif IsVisualStudio2017OrGreater():
815                b2_settings.append("toolset=msvc-14.1")
816            else:
817                b2_settings.append("toolset=msvc-14.0")
818
819        if MacOS():
820            # Must specify toolset=clang to ensure install_name for boost
821            # libraries includes @rpath
822            b2_settings.append("toolset=clang")
823
824        if context.buildDebug:
825            b2_settings.append("--debug-configuration")
826
827        # Add on any user-specified extra arguments.
828        b2_settings += buildArgs
829
830        # Append extra argument controlling libstdc++ ABI if specified.
831        AppendCXX11ABIArg("cxxflags", context, b2_settings)
832
833        b2 = "b2" if Windows() else "./b2"
834        Run('{b2} {options} install'
835            .format(b2=b2, options=" ".join(b2_settings)))
836
837def InstallBoost(context, force, buildArgs):
838    # Boost's build system will install the version.hpp header before
839    # building its libraries. We make sure to remove it in case of
840    # any failure to ensure that the build script detects boost as a
841    # dependency to build the next time it's run.
842    try:
843        InstallBoost_Helper(context, force, buildArgs)
844    except:
845        versionHeader = os.path.join(context.instDir, BOOST_VERSION_FILE)
846        if os.path.isfile(versionHeader):
847            try: os.remove(versionHeader)
848            except: pass
849        raise
850
851BOOST = Dependency("boost", InstallBoost, BOOST_VERSION_FILE)
852
853############################################################
854# Intel TBB
855
856if Windows():
857    TBB_URL = "https://github.com/oneapi-src/oneTBB/releases/download/2018_U6/tbb2018_20180822oss_win.zip"
858elif MacOS():
859    # On MacOS we experience various crashes in tests during teardown
860    # starting with 2018 Update 2. Until we figure that out, we use
861    # 2018 Update 1 on this platform.
862    TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/2018_U1.tar.gz"
863else:
864    TBB_URL = "https://github.com/oneapi-src/oneTBB/archive/2018_U6.tar.gz"
865
866def InstallTBB(context, force, buildArgs):
867    if Windows():
868        InstallTBB_Windows(context, force, buildArgs)
869    elif Linux() or MacOS():
870        InstallTBB_LinuxOrMacOS(context, force, buildArgs)
871
872def InstallTBB_Windows(context, force, buildArgs):
873    TBB_ROOT_DIR_NAME = "tbb2018_20180822oss"
874    with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force,
875        TBB_ROOT_DIR_NAME)):
876        # On Windows, we simply copy headers and pre-built DLLs to
877        # the appropriate location.
878        if buildArgs:
879            PrintWarning("Ignoring build arguments {}, TBB is "
880                         "not built from source on this platform."
881                         .format(buildArgs))
882
883        CopyFiles(context, "bin\\intel64\\vc14\\*.*", "bin")
884        CopyFiles(context, "lib\\intel64\\vc14\\*.*", "lib")
885        CopyDirectory(context, "include\\serial", "include\\serial")
886        CopyDirectory(context, "include\\tbb", "include\\tbb")
887
888def InstallTBB_LinuxOrMacOS(context, force, buildArgs):
889    with CurrentWorkingDirectory(DownloadURL(TBB_URL, context, force)):
890        # Note: TBB installation fails on OSX when cuda is installed, a
891        # suggested fix:
892        # https://github.com/spack/spack/issues/6000#issuecomment-358817701
893        if MacOS():
894            PatchFile("build/macos.inc",
895                    [("shell clang -v ", "shell clang --version ")])
896
897        # Append extra argument controlling libstdc++ ABI if specified.
898        AppendCXX11ABIArg("CXXFLAGS", context, buildArgs)
899
900        # TBB does not support out-of-source builds in a custom location.
901        Run('make -j{procs} {buildArgs}'
902            .format(procs=context.numJobs,
903                    buildArgs=" ".join(buildArgs)))
904
905        # Install both release and debug builds. USD requires the debug
906        # libraries when building in debug mode, and installing both
907        # makes it easier for users to install dependencies in some
908        # location that can be shared by both release and debug USD
909        # builds. Plus, the TBB build system builds both versions anyway.
910        CopyFiles(context, "build/*_release/libtbb*.*", "lib")
911        CopyFiles(context, "build/*_debug/libtbb*.*", "lib")
912        CopyDirectory(context, "include/serial", "include/serial")
913        CopyDirectory(context, "include/tbb", "include/tbb")
914
915TBB = Dependency("TBB", InstallTBB, "include/tbb/tbb.h")
916
917############################################################
918# JPEG
919
920if Windows():
921    JPEG_URL = "https://github.com/libjpeg-turbo/libjpeg-turbo/archive/1.5.1.zip"
922else:
923    JPEG_URL = "https://www.ijg.org/files/jpegsrc.v9b.tar.gz"
924
925def InstallJPEG(context, force, buildArgs):
926    if Windows():
927        InstallJPEG_Turbo(context, force, buildArgs)
928    else:
929        InstallJPEG_Lib(context, force, buildArgs)
930
931def InstallJPEG_Turbo(context, force, buildArgs):
932    with CurrentWorkingDirectory(DownloadURL(JPEG_URL, context, force)):
933        RunCMake(context, force, buildArgs)
934
935def InstallJPEG_Lib(context, force, buildArgs):
936    with CurrentWorkingDirectory(DownloadURL(JPEG_URL, context, force)):
937        Run('./configure --prefix="{instDir}" '
938            '--disable-static --enable-shared '
939            '{buildArgs}'
940            .format(instDir=context.instDir,
941                    buildArgs=" ".join(buildArgs)))
942        Run('make -j{procs} install'
943            .format(procs=context.numJobs))
944
945JPEG = Dependency("JPEG", InstallJPEG, "include/jpeglib.h")
946
947############################################################
948# TIFF
949
950TIFF_URL = "https://gitlab.com/libtiff/libtiff/-/archive/Release-v4-0-7/libtiff-Release-v4-0-7.tar.gz"
951
952def InstallTIFF(context, force, buildArgs):
953    with CurrentWorkingDirectory(DownloadURL(TIFF_URL, context, force)):
954        # libTIFF has a build issue on Windows where tools/tiffgt.c
955        # unconditionally includes unistd.h, which does not exist.
956        # To avoid this, we patch the CMakeLists.txt to skip building
957        # the tools entirely. We do this on Linux and MacOS as well
958        # to avoid requiring some GL and X dependencies.
959        #
960        # We also need to skip building tests, since they rely on
961        # the tools we've just elided.
962        PatchFile("CMakeLists.txt",
963                   [("add_subdirectory(tools)", "# add_subdirectory(tools)"),
964                    ("add_subdirectory(test)", "# add_subdirectory(test)")])
965
966        # The libTIFF CMakeScript says the ld-version-script
967        # functionality is only for compilers using GNU ld on
968        # ELF systems or systems which provide an emulation; therefore
969        # skipping it completely on mac and windows.
970        if MacOS() or Windows():
971            extraArgs = ["-Dld-version-script=OFF"]
972        else:
973            extraArgs = []
974        extraArgs += buildArgs
975        RunCMake(context, force, extraArgs)
976
977TIFF = Dependency("TIFF", InstallTIFF, "include/tiff.h")
978
979############################################################
980# PNG
981
982PNG_URL = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.29.tar.gz"
983
984def InstallPNG(context, force, buildArgs):
985    with CurrentWorkingDirectory(DownloadURL(PNG_URL, context, force)):
986        RunCMake(context, force, buildArgs)
987
988PNG = Dependency("PNG", InstallPNG, "include/png.h")
989
990############################################################
991# IlmBase/OpenEXR
992
993OPENEXR_URL = "https://github.com/AcademySoftwareFoundation/openexr/archive/v2.3.0.zip"
994
995def InstallOpenEXR(context, force, buildArgs):
996    with CurrentWorkingDirectory(DownloadURL(OPENEXR_URL, context, force)):
997        RunCMake(context, force,
998                 ['-DOPENEXR_BUILD_PYTHON_LIBS=OFF',
999                  '-DOPENEXR_PACKAGE_PREFIX="{}"'.format(context.instDir),
1000                  '-DOPENEXR_ENABLE_TESTS=OFF'] + buildArgs)
1001
1002OPENEXR = Dependency("OpenEXR", InstallOpenEXR, "include/OpenEXR/ImfVersion.h")
1003
1004############################################################
1005# Ptex
1006
1007PTEX_URL = "https://github.com/wdas/ptex/archive/v2.1.33.zip"
1008
1009def InstallPtex(context, force, buildArgs):
1010    if Windows():
1011        InstallPtex_Windows(context, force, buildArgs)
1012    else:
1013        InstallPtex_LinuxOrMacOS(context, force, buildArgs)
1014
1015def InstallPtex_Windows(context, force, buildArgs):
1016    with CurrentWorkingDirectory(DownloadURL(PTEX_URL, context, force)):
1017        # Ptex has a bug where the import library for the dynamic library and
1018        # the static library both get the same name, Ptex.lib, and as a
1019        # result one clobbers the other. We hack the appropriate CMake
1020        # file to prevent that. Since we don't need the static library we'll
1021        # rename that.
1022        #
1023        # In addition src\tests\CMakeLists.txt adds -DPTEX_STATIC to the
1024        # compiler but links tests against the dynamic library, causing the
1025        # links to fail. We patch the file to not add the -DPTEX_STATIC
1026        PatchFile('src\\ptex\\CMakeLists.txt',
1027                  [("set_target_properties(Ptex_static PROPERTIES OUTPUT_NAME Ptex)",
1028                    "set_target_properties(Ptex_static PROPERTIES OUTPUT_NAME Ptexs)")])
1029        PatchFile('src\\tests\\CMakeLists.txt',
1030                  [("add_definitions(-DPTEX_STATIC)",
1031                    "# add_definitions(-DPTEX_STATIC)")])
1032
1033        # Patch Ptex::String to export symbol for operator<<
1034        # This is required for newer versions of OIIO, which make use of the
1035        # this operator on Windows platform specifically.
1036        PatchFile('src\\ptex\\Ptexture.h',
1037                  [("std::ostream& operator << (std::ostream& stream, const Ptex::String& str);",
1038                    "PTEXAPI std::ostream& operator << (std::ostream& stream, const Ptex::String& str);")])
1039
1040
1041        RunCMake(context, force, buildArgs)
1042
1043def InstallPtex_LinuxOrMacOS(context, force, buildArgs):
1044    with CurrentWorkingDirectory(DownloadURL(PTEX_URL, context, force)):
1045        RunCMake(context, force, buildArgs)
1046
1047PTEX = Dependency("Ptex", InstallPtex, "include/PtexVersion.h")
1048
1049############################################################
1050# BLOSC (Compression used by OpenVDB)
1051
1052# Using blosc v1.20.1 to avoid build errors on macOS Catalina (10.15)
1053# related to implicit declaration of functions in zlib. See:
1054# https://github.com/Blosc/python-blosc/issues/229
1055BLOSC_URL = "https://github.com/Blosc/c-blosc/archive/v1.20.1.zip"
1056
1057def InstallBLOSC(context, force, buildArgs):
1058    with CurrentWorkingDirectory(DownloadURL(BLOSC_URL, context, force)):
1059        RunCMake(context, force, buildArgs)
1060
1061BLOSC = Dependency("Blosc", InstallBLOSC, "include/blosc.h")
1062
1063############################################################
1064# OpenVDB
1065
1066# Using version 6.1.0 since it has reworked its CMake files so that
1067# there are better options to not compile the OpenVDB binaries and to
1068# not require additional dependencies such as GLFW. Note that version
1069# 6.1.0 does require CMake 3.3 though.
1070
1071OPENVDB_URL = "https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.1.0.zip"
1072
1073def InstallOpenVDB(context, force, buildArgs):
1074    with CurrentWorkingDirectory(DownloadURL(OPENVDB_URL, context, force)):
1075        extraArgs = [
1076            '-DOPENVDB_BUILD_PYTHON_MODULE=OFF',
1077            '-DOPENVDB_BUILD_BINARIES=OFF',
1078            '-DOPENVDB_BUILD_UNITTESTS=OFF'
1079        ]
1080
1081        # Make sure to use boost installed by the build script and not any
1082        # system installed boost
1083        extraArgs.append('-DBoost_NO_BOOST_CMAKE=On')
1084        extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True')
1085
1086        extraArgs.append('-DBLOSC_ROOT="{instDir}"'
1087                         .format(instDir=context.instDir))
1088        extraArgs.append('-DTBB_ROOT="{instDir}"'
1089                         .format(instDir=context.instDir))
1090        # OpenVDB needs Half type from IlmBase
1091        extraArgs.append('-DILMBASE_ROOT="{instDir}"'
1092                         .format(instDir=context.instDir))
1093
1094        # Add on any user-specified extra arguments.
1095        extraArgs += buildArgs
1096
1097        RunCMake(context, force, extraArgs)
1098
1099OPENVDB = Dependency("OpenVDB", InstallOpenVDB, "include/openvdb/openvdb.h")
1100
1101############################################################
1102# OpenImageIO
1103
1104OIIO_URL = "https://github.com/OpenImageIO/oiio/archive/Release-2.1.16.0.zip"
1105
1106def InstallOpenImageIO(context, force, buildArgs):
1107    with CurrentWorkingDirectory(DownloadURL(OIIO_URL, context, force)):
1108        extraArgs = ['-DOIIO_BUILD_TOOLS=OFF',
1109                     '-DOIIO_BUILD_TESTS=OFF',
1110                     '-DUSE_PYTHON=OFF',
1111                     '-DSTOP_ON_WARNING=OFF']
1112
1113        # OIIO's FindOpenEXR module circumvents CMake's normal library
1114        # search order, which causes versions of OpenEXR installed in
1115        # /usr/local or other hard-coded locations in the module to
1116        # take precedence over the version we've built, which would
1117        # normally be picked up when we specify CMAKE_PREFIX_PATH.
1118        # This may lead to undefined symbol errors at build or runtime.
1119        # So, we explicitly specify the OpenEXR we want to use here.
1120        extraArgs.append('-DOPENEXR_ROOT="{instDir}"'
1121                         .format(instDir=context.instDir))
1122
1123        # If Ptex support is disabled in USD, disable support in OpenImageIO
1124        # as well. This ensures OIIO doesn't accidentally pick up a Ptex
1125        # library outside of our build.
1126        if not context.enablePtex:
1127            extraArgs.append('-DUSE_PTEX=OFF')
1128
1129        # Make sure to use boost installed by the build script and not any
1130        # system installed boost
1131        extraArgs.append('-DBoost_NO_BOOST_CMAKE=On')
1132        extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True')
1133
1134        # Add on any user-specified extra arguments.
1135        extraArgs += buildArgs
1136
1137        RunCMake(context, force, extraArgs)
1138
1139OPENIMAGEIO = Dependency("OpenImageIO", InstallOpenImageIO,
1140                         "include/OpenImageIO/oiioversion.h")
1141
1142############################################################
1143# OpenColorIO
1144
1145OCIO_URL = "https://github.com/imageworks/OpenColorIO/archive/v1.1.0.zip"
1146
1147def InstallOpenColorIO(context, force, buildArgs):
1148    with CurrentWorkingDirectory(DownloadURL(OCIO_URL, context, force)):
1149        extraArgs = ['-DOCIO_BUILD_TRUELIGHT=OFF',
1150                     '-DOCIO_BUILD_APPS=OFF',
1151                     '-DOCIO_BUILD_NUKE=OFF',
1152                     '-DOCIO_BUILD_DOCS=OFF',
1153                     '-DOCIO_BUILD_TESTS=OFF',
1154                     '-DOCIO_BUILD_PYGLUE=OFF',
1155                     '-DOCIO_BUILD_JNIGLUE=OFF',
1156                     '-DOCIO_STATIC_JNIGLUE=OFF']
1157
1158        # OpenImageIO v1.1.0 fails to build on Windows with the RelWithDebInfo
1159        # build type because it doesn't set up the correct properties for the
1160        # YAML library it relies on. This patch works around that issue.
1161        if Windows() and context.buildRelWithDebug:
1162            PatchFile("CMakeLists.txt",
1163                      [("IMPORTED_LOCATION_RELEASE",
1164                        "IMPORTED_LOCATION_RELWITHDEBINFO")])
1165
1166        # The OCIO build treats all warnings as errors but several come up
1167        # on various platforms, including:
1168        # - On gcc6, v1.1.0 emits many -Wdeprecated-declaration warnings for
1169        #   std::auto_ptr
1170        # - On clang, v1.1.0 emits a -Wself-assign-field warning. This is fixed
1171        #   in https://github.com/AcademySoftwareFoundation/OpenColorIO/commit/0be465feb9ac2d34bd8171f30909b276c1efa996
1172        #
1173        # To avoid build failures we force all warnings off for this build.
1174        if GetVisualStudioCompilerAndVersion():
1175            # This doesn't work because CMake stores default flags for
1176            # MSVC in CMAKE_CXX_FLAGS and this would overwrite them.
1177            # However, we don't seem to get any warnings on Windows
1178            # (at least with VS2015 and 2017).
1179            # extraArgs.append('-DCMAKE_CXX_FLAGS=/w')
1180            pass
1181        else:
1182            extraArgs.append('-DCMAKE_CXX_FLAGS=-w')
1183
1184        # Add on any user-specified extra arguments.
1185        extraArgs += buildArgs
1186
1187        RunCMake(context, force, extraArgs)
1188
1189OPENCOLORIO = Dependency("OpenColorIO", InstallOpenColorIO,
1190                         "include/OpenColorIO/OpenColorABI.h")
1191
1192############################################################
1193# OpenSubdiv
1194
1195OPENSUBDIV_URL = "https://github.com/PixarAnimationStudios/OpenSubdiv/archive/v3_4_3.zip"
1196
1197def InstallOpenSubdiv(context, force, buildArgs):
1198    with CurrentWorkingDirectory(DownloadURL(OPENSUBDIV_URL, context, force)):
1199        extraArgs = [
1200            '-DNO_EXAMPLES=ON',
1201            '-DNO_TUTORIALS=ON',
1202            '-DNO_REGRESSION=ON',
1203            '-DNO_DOC=ON',
1204            '-DNO_OMP=ON',
1205            '-DNO_CUDA=ON',
1206            '-DNO_OPENCL=ON',
1207            '-DNO_DX=ON',
1208            '-DNO_TESTS=ON',
1209            '-DNO_GLEW=ON',
1210            '-DNO_GLFW=ON',
1211        ]
1212
1213        # If Ptex support is disabled in USD, disable support in OpenSubdiv
1214        # as well. This ensures OSD doesn't accidentally pick up a Ptex
1215        # library outside of our build.
1216        if not context.enablePtex:
1217            extraArgs.append('-DNO_PTEX=ON')
1218
1219        # NOTE: For now, we disable TBB in our OpenSubdiv build.
1220        # This avoids an issue where OpenSubdiv will link against
1221        # all TBB libraries it finds, including libtbbmalloc and
1222        # libtbbmalloc_proxy. On Linux and MacOS, this has the
1223        # unwanted effect of replacing the system allocator with
1224        # tbbmalloc.
1225        extraArgs.append('-DNO_TBB=ON')
1226
1227        # Add on any user-specified extra arguments.
1228        extraArgs += buildArgs
1229
1230        # OpenSubdiv seems to error when building on windows w/ Ninja...
1231        # ...so just use the default generator (ie, Visual Studio on Windows)
1232        # until someone can sort it out
1233        oldGenerator = context.cmakeGenerator
1234        if oldGenerator == "Ninja" and Windows():
1235            context.cmakeGenerator = None
1236
1237        # OpenSubdiv 3.3 and later on MacOS occasionally runs into build
1238        # failures with multiple build jobs. Workaround this by using
1239        # just 1 job for now. See:
1240        # https://github.com/PixarAnimationStudios/OpenSubdiv/issues/1194
1241        oldNumJobs = context.numJobs
1242        if MacOS():
1243            context.numJobs = 1
1244
1245        try:
1246            RunCMake(context, force, extraArgs)
1247        finally:
1248            context.cmakeGenerator = oldGenerator
1249            context.numJobs = oldNumJobs
1250
1251OPENSUBDIV = Dependency("OpenSubdiv", InstallOpenSubdiv,
1252                        "include/opensubdiv/version.h")
1253
1254############################################################
1255# PyOpenGL
1256
1257def GetPyOpenGLInstructions():
1258    return ('PyOpenGL is not installed. If you have pip '
1259            'installed, run "pip install PyOpenGL" to '
1260            'install it, then re-run this script.\n'
1261            'If PyOpenGL is already installed, you may need to '
1262            'update your PYTHONPATH to indicate where it is '
1263            'located.')
1264
1265PYOPENGL = PythonDependency("PyOpenGL", GetPyOpenGLInstructions,
1266                            moduleNames=["OpenGL"])
1267
1268############################################################
1269# PySide
1270
1271def GetPySideInstructions():
1272    # For licensing reasons, this script cannot install PySide itself.
1273    if Windows():
1274        # There is no distribution of PySide2 for Windows for Python 2.7.
1275        # So use PySide instead. See the following for more details:
1276        # https://wiki.qt.io/Qt_for_Python/Considerations#Missing_Windows_.2F_Python_2.7_release
1277        return ('PySide is not installed. If you have pip '
1278                'installed, run "pip install PySide" '
1279                'to install it, then re-run this script.\n'
1280                'If PySide is already installed, you may need to '
1281                'update your PYTHONPATH to indicate where it is '
1282                'located.')
1283    else:
1284        return ('PySide2 is not installed. If you have pip '
1285                'installed, run "pip install PySide2" '
1286                'to install it, then re-run this script.\n'
1287                'If PySide2 is already installed, you may need to '
1288                'update your PYTHONPATH to indicate where it is '
1289                'located.')
1290
1291PYSIDE = PythonDependency("PySide", GetPySideInstructions,
1292                          moduleNames=["PySide", "PySide2"])
1293
1294############################################################
1295# HDF5
1296
1297HDF5_URL = "https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.10/hdf5-1.10.0-patch1/src/hdf5-1.10.0-patch1.zip"
1298
1299def InstallHDF5(context, force, buildArgs):
1300    with CurrentWorkingDirectory(DownloadURL(HDF5_URL, context, force)):
1301        RunCMake(context, force,
1302                 ['-DBUILD_TESTING=OFF',
1303                  '-DHDF5_BUILD_TOOLS=OFF',
1304                  '-DHDF5_BUILD_EXAMPLES=OFF'] + buildArgs)
1305
1306HDF5 = Dependency("HDF5", InstallHDF5, "include/hdf5.h")
1307
1308############################################################
1309# Alembic
1310
1311ALEMBIC_URL = "https://github.com/alembic/alembic/archive/1.7.10.zip"
1312
1313def InstallAlembic(context, force, buildArgs):
1314    with CurrentWorkingDirectory(DownloadURL(ALEMBIC_URL, context, force)):
1315        cmakeOptions = ['-DUSE_BINARIES=OFF', '-DUSE_TESTS=OFF']
1316        if context.enableHDF5:
1317            # HDF5 requires the H5_BUILT_AS_DYNAMIC_LIB macro be defined if
1318            # it was built with CMake as a dynamic library.
1319            cmakeOptions += [
1320                '-DUSE_HDF5=ON',
1321                '-DHDF5_ROOT="{instDir}"'.format(instDir=context.instDir),
1322                '-DCMAKE_CXX_FLAGS="-D H5_BUILT_AS_DYNAMIC_LIB"']
1323        else:
1324           cmakeOptions += ['-DUSE_HDF5=OFF']
1325
1326        cmakeOptions += buildArgs
1327
1328        RunCMake(context, force, cmakeOptions)
1329
1330ALEMBIC = Dependency("Alembic", InstallAlembic, "include/Alembic/Abc/Base.h")
1331
1332############################################################
1333# Draco
1334
1335DRACO_URL = "https://github.com/google/draco/archive/master.zip"
1336
1337def InstallDraco(context, force, buildArgs):
1338    with CurrentWorkingDirectory(DownloadURL(DRACO_URL, context, force)):
1339        cmakeOptions = ['-DBUILD_USD_PLUGIN=ON']
1340        cmakeOptions += buildArgs
1341        RunCMake(context, force, cmakeOptions)
1342
1343DRACO = Dependency("Draco", InstallDraco, "include/draco/compression/decode.h")
1344
1345############################################################
1346# MaterialX
1347
1348MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.0.zip"
1349
1350def InstallMaterialX(context, force, buildArgs):
1351    with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)):
1352        # USD requires MaterialX to be built as a shared library on Linux and MacOS
1353        # Currently MaterialX does not support shared builds on Windows
1354        cmakeOptions = []
1355        if Linux() or MacOS():
1356            cmakeOptions += ['-DMATERIALX_BUILD_SHARED_LIBS=ON']
1357
1358        cmakeOptions += buildArgs;
1359
1360        RunCMake(context, force, cmakeOptions)
1361
1362MATERIALX = Dependency("MaterialX", InstallMaterialX, "include/MaterialXCore/Library.h")
1363
1364############################################################
1365# Embree
1366# For MacOS we use version 3.7.0 to include a fix from Intel
1367# to build on Catalina.
1368if MacOS():
1369    EMBREE_URL = "https://github.com/embree/embree/archive/v3.7.0.tar.gz"
1370else:
1371    EMBREE_URL = "https://github.com/embree/embree/archive/v3.2.2.tar.gz"
1372
1373def InstallEmbree(context, force, buildArgs):
1374    with CurrentWorkingDirectory(DownloadURL(EMBREE_URL, context, force)):
1375        extraArgs = [
1376            '-DTBB_ROOT={instDir}'.format(instDir=context.instDir),
1377            '-DEMBREE_TUTORIALS=OFF',
1378            '-DEMBREE_ISPC_SUPPORT=OFF'
1379        ]
1380
1381        # By default Embree fails to build on Visual Studio 2015 due
1382        # to an internal compiler issue that is worked around via the
1383        # following flag. For more details see:
1384        # https://github.com/embree/embree/issues/157
1385        if IsVisualStudio2015OrGreater() and not IsVisualStudio2017OrGreater():
1386            extraArgs.append('-DCMAKE_CXX_FLAGS=/d2SSAOptimizer-')
1387
1388        extraArgs += buildArgs
1389
1390        RunCMake(context, force, extraArgs)
1391
1392EMBREE = Dependency("Embree", InstallEmbree, "include/embree3/rtcore.h")
1393
1394############################################################
1395# USD
1396
1397def InstallUSD(context, force, buildArgs):
1398    with CurrentWorkingDirectory(context.usdSrcDir):
1399        extraArgs = []
1400
1401        extraArgs.append('-DPXR_PREFER_SAFETY_OVER_SPEED=' +
1402                         'ON' if context.safetyFirst else 'OFF')
1403
1404        if context.buildPython:
1405            extraArgs.append('-DPXR_ENABLE_PYTHON_SUPPORT=ON')
1406            if Python3():
1407                extraArgs.append('-DPXR_USE_PYTHON_3=ON')
1408
1409            # Many people on Windows may not have python with the
1410            # debugging symbol ( python27_d.lib ) installed, this is the common case
1411            # where one downloads the python from official download website. Therefore we
1412            # can still let people decide to build USD with release version of python if
1413            # debugging into python land is not what they want which can be done by setting the
1414            # debugPython
1415            if context.buildDebug and context.debugPython:
1416                extraArgs.append('-DPXR_USE_DEBUG_PYTHON=ON')
1417            else:
1418                extraArgs.append('-DPXR_USE_DEBUG_PYTHON=OFF')
1419
1420            # CMake has trouble finding the executable, library, and include
1421            # directories when there are multiple versions of Python installed.
1422            # This can lead to crashes due to USD being linked against one
1423            # version of Python but running through some other Python
1424            # interpreter version. This primarily shows up on macOS, as it's
1425            # common to have a Python install that's separate from the one
1426            # included with the system.
1427            #
1428            # To avoid this, we try to determine these paths from Python
1429            # itself rather than rely on CMake's heuristics.
1430            pythonInfo = GetPythonInfo(context)
1431            if pythonInfo:
1432                # According to FindPythonLibs.cmake these are the variables
1433                # to set to specify which Python installation to use.
1434                extraArgs.append('-DPYTHON_EXECUTABLE="{pyExecPath}"'
1435                                 .format(pyExecPath=pythonInfo[0]))
1436                extraArgs.append('-DPYTHON_LIBRARY="{pyLibPath}"'
1437                                 .format(pyLibPath=pythonInfo[1]))
1438                extraArgs.append('-DPYTHON_INCLUDE_DIR="{pyIncPath}"'
1439                                 .format(pyIncPath=pythonInfo[2]))
1440        else:
1441            extraArgs.append('-DPXR_ENABLE_PYTHON_SUPPORT=OFF')
1442
1443        if context.buildShared:
1444            extraArgs.append('-DBUILD_SHARED_LIBS=ON')
1445        elif context.buildMonolithic:
1446            extraArgs.append('-DPXR_BUILD_MONOLITHIC=ON')
1447
1448        if context.buildDebug:
1449            extraArgs.append('-DTBB_USE_DEBUG_BUILD=ON')
1450        else:
1451            extraArgs.append('-DTBB_USE_DEBUG_BUILD=OFF')
1452
1453        if context.buildDocs:
1454            extraArgs.append('-DPXR_BUILD_DOCUMENTATION=ON')
1455        else:
1456            extraArgs.append('-DPXR_BUILD_DOCUMENTATION=OFF')
1457
1458        if context.buildTests:
1459            extraArgs.append('-DPXR_BUILD_TESTS=ON')
1460        else:
1461            extraArgs.append('-DPXR_BUILD_TESTS=OFF')
1462
1463        if context.buildExamples:
1464            extraArgs.append('-DPXR_BUILD_EXAMPLES=ON')
1465        else:
1466            extraArgs.append('-DPXR_BUILD_EXAMPLES=OFF')
1467
1468        if context.buildTutorials:
1469            extraArgs.append('-DPXR_BUILD_TUTORIALS=ON')
1470        else:
1471            extraArgs.append('-DPXR_BUILD_TUTORIALS=OFF')
1472
1473        if context.buildTools:
1474            extraArgs.append('-DPXR_BUILD_USD_TOOLS=ON')
1475        else:
1476            extraArgs.append('-DPXR_BUILD_USD_TOOLS=OFF')
1477
1478        if context.buildImaging:
1479            extraArgs.append('-DPXR_BUILD_IMAGING=ON')
1480            if context.enablePtex:
1481                extraArgs.append('-DPXR_ENABLE_PTEX_SUPPORT=ON')
1482            else:
1483                extraArgs.append('-DPXR_ENABLE_PTEX_SUPPORT=OFF')
1484
1485            if context.enableOpenVDB:
1486                extraArgs.append('-DPXR_ENABLE_OPENVDB_SUPPORT=ON')
1487            else:
1488                extraArgs.append('-DPXR_ENABLE_OPENVDB_SUPPORT=OFF')
1489
1490            if context.buildEmbree:
1491                extraArgs.append('-DPXR_BUILD_EMBREE_PLUGIN=ON')
1492            else:
1493                extraArgs.append('-DPXR_BUILD_EMBREE_PLUGIN=OFF')
1494
1495            if context.buildPrman:
1496                if context.prmanLocation:
1497                    extraArgs.append('-DRENDERMAN_LOCATION="{location}"'
1498                                     .format(location=context.prmanLocation))
1499                extraArgs.append('-DPXR_BUILD_PRMAN_PLUGIN=ON')
1500            else:
1501                extraArgs.append('-DPXR_BUILD_PRMAN_PLUGIN=OFF')
1502
1503            if context.buildOIIO:
1504                extraArgs.append('-DPXR_BUILD_OPENIMAGEIO_PLUGIN=ON')
1505            else:
1506                extraArgs.append('-DPXR_BUILD_OPENIMAGEIO_PLUGIN=OFF')
1507
1508            if context.buildOCIO:
1509                extraArgs.append('-DPXR_BUILD_OPENCOLORIO_PLUGIN=ON')
1510            else:
1511                extraArgs.append('-DPXR_BUILD_OPENCOLORIO_PLUGIN=OFF')
1512
1513        else:
1514            extraArgs.append('-DPXR_BUILD_IMAGING=OFF')
1515
1516        if context.buildUsdImaging:
1517            extraArgs.append('-DPXR_BUILD_USD_IMAGING=ON')
1518        else:
1519            extraArgs.append('-DPXR_BUILD_USD_IMAGING=OFF')
1520
1521        if context.buildUsdview:
1522            extraArgs.append('-DPXR_BUILD_USDVIEW=ON')
1523        else:
1524            extraArgs.append('-DPXR_BUILD_USDVIEW=OFF')
1525
1526        if context.buildAlembic:
1527            extraArgs.append('-DPXR_BUILD_ALEMBIC_PLUGIN=ON')
1528            if context.enableHDF5:
1529                extraArgs.append('-DPXR_ENABLE_HDF5_SUPPORT=ON')
1530
1531                # CMAKE_PREFIX_PATH isn't sufficient for the FindHDF5 module
1532                # to find the HDF5 we've built, so provide an extra hint.
1533                extraArgs.append('-DHDF5_ROOT="{instDir}"'
1534                                 .format(instDir=context.instDir))
1535            else:
1536                extraArgs.append('-DPXR_ENABLE_HDF5_SUPPORT=OFF')
1537        else:
1538            extraArgs.append('-DPXR_BUILD_ALEMBIC_PLUGIN=OFF')
1539
1540        if context.buildDraco:
1541            extraArgs.append('-DPXR_BUILD_DRACO_PLUGIN=ON')
1542            draco_root = (context.dracoLocation
1543                          if context.dracoLocation else context.instDir)
1544            extraArgs.append('-DDRACO_ROOT="{}"'.format(draco_root))
1545        else:
1546            extraArgs.append('-DPXR_BUILD_DRACO_PLUGIN=OFF')
1547
1548        if context.buildMaterialX:
1549            extraArgs.append('-DPXR_ENABLE_MATERIALX_SUPPORT=ON')
1550        else:
1551            extraArgs.append('-DPXR_ENABLE_MATERIALX_SUPPORT=OFF')
1552
1553        if Windows():
1554            # Increase the precompiled header buffer limit.
1555            extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"')
1556
1557        # Make sure to use boost installed by the build script and not any
1558        # system installed boost
1559        extraArgs.append('-DBoost_NO_BOOST_CMAKE=On')
1560        extraArgs.append('-DBoost_NO_SYSTEM_PATHS=True')
1561        extraArgs += buildArgs
1562
1563        RunCMake(context, force, extraArgs)
1564
1565USD = Dependency("USD", InstallUSD, "include/pxr/pxr.h")
1566
1567############################################################
1568# Install script
1569
1570programDescription = """\
1571Installation Script for USD
1572
1573Builds and installs USD and 3rd-party dependencies to specified location.
1574
1575- Libraries:
1576The following is a list of libraries that this script will download and build
1577as needed. These names can be used to identify libraries for various script
1578options, like --force or --build-args.
1579
1580{libraryList}
1581
1582- Downloading Libraries:
1583If curl or powershell (on Windows) are installed and located in PATH, they
1584will be used to download dependencies. Otherwise, a built-in downloader will
1585be used.
1586
1587- Specifying Custom Build Arguments:
1588Users may specify custom build arguments for libraries using the --build-args
1589option. This values for this option must take the form <library name>,<option>.
1590For example:
1591
1592%(prog)s --build-args boost,cxxflags=... USD,-DPXR_STRICT_BUILD_MODE=ON ...
1593%(prog)s --build-args USD,"-DPXR_STRICT_BUILD_MODE=ON -DPXR_HEADLESS_TEST_MODE=ON" ...
1594
1595These arguments will be passed directly to the build system for the specified
1596library. Multiple quotes may be needed to ensure arguments are passed on
1597exactly as desired. Users must ensure these arguments are suitable for the
1598specified library and do not conflict with other options, otherwise build
1599errors may occur.
1600
1601- Python Versions and DCC Plugins:
1602Some DCCs (most notably, Maya) may ship with and run using their own version of
1603Python. In that case, it is important that USD and the plugins for that DCC are
1604built using the DCC's version of Python and not the system version. This can be
1605done by running %(prog)s using the DCC's version of Python.
1606
1607The flag --build-python-info allows calling the %(prog)s with any python (such as
1608system python) but pass in the python that you want USD to use to build the python
1609bindings with. This flag takes 4 arguments: python executable, python include directory
1610python library and python version.
1611
1612For example, to build USD on macOS for use in Maya 2019, run:
1613
1614/Applications/Autodesk/maya2019/Maya.app/Contents/bin/mayapy %(prog)s --no-usdview ...
1615
1616Note that this is primarily an issue on macOS, where a DCC's version of Python
1617is likely to conflict with the version provided by the system. On other
1618platforms, %(prog)s *should* be run using the system Python and *should not*
1619be run using the DCC's Python.
1620
1621- C++11 ABI Compatibility:
1622On Linux, the --use-cxx11-abi parameter can be used to specify whether to use
1623the C++11 ABI for libstdc++ when building USD and any dependencies. The value
1624given to this parameter will be used to define _GLIBCXX_USE_CXX11_ABI for
1625all builds.
1626
1627If this parameter is not specified, the compiler's default ABI will be used.
1628
1629For more details see:
1630https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
1631""".format(
1632    libraryList=" ".join(sorted([d.name for d in AllDependencies])))
1633
1634parser = argparse.ArgumentParser(
1635    formatter_class=argparse.RawDescriptionHelpFormatter,
1636    description=programDescription)
1637
1638parser.add_argument("install_dir", type=str,
1639                    help="Directory where USD will be installed")
1640parser.add_argument("-n", "--dry_run", dest="dry_run", action="store_true",
1641                    help="Only summarize what would happen")
1642
1643group = parser.add_mutually_exclusive_group()
1644group.add_argument("-v", "--verbose", action="count", default=1,
1645                   dest="verbosity",
1646                   help="Increase verbosity level (1-3)")
1647group.add_argument("-q", "--quiet", action="store_const", const=0,
1648                   dest="verbosity",
1649                   help="Suppress all output except for error messages")
1650
1651group = parser.add_argument_group(title="Build Options")
1652group.add_argument("-j", "--jobs", type=int, default=GetCPUCount(),
1653                   help=("Number of build jobs to run in parallel. "
1654                         "(default: # of processors [{0}])"
1655                         .format(GetCPUCount())))
1656group.add_argument("--build", type=str,
1657                   help=("Build directory for USD and 3rd-party dependencies "
1658                         "(default: <install_dir>/build)"))
1659
1660BUILD_DEBUG = "debug"
1661BUILD_RELEASE = "release"
1662BUILD_RELWITHDEBUG = "relwithdebuginfo"
1663group.add_argument("--build-variant", default=BUILD_RELEASE,
1664                   choices=[BUILD_DEBUG, BUILD_RELEASE, BUILD_RELWITHDEBUG],
1665                   help=("Build variant for USD and 3rd-party dependencies. "
1666                         "(default: {})".format(BUILD_RELEASE)))
1667
1668group.add_argument("--build-args", type=str, nargs="*", default=[],
1669                   help=("Custom arguments to pass to build system when "
1670                         "building libraries (see docs above)"))
1671group.add_argument("--build-python-info", type=str, nargs=4, default=[],
1672                   metavar=('PYTHON_EXECUTABLE', 'PYTHON_INCLUDE_DIR', 'PYTHON_LIBRARY', 'PYTHON_VERSION'),
1673                   help=("Specify a custom python to use during build"))
1674group.add_argument("--force", type=str, action="append", dest="force_build",
1675                   default=[],
1676                   help=("Force download and build of specified library "
1677                         "(see docs above)"))
1678group.add_argument("--force-all", action="store_true",
1679                   help="Force download and build of all libraries")
1680group.add_argument("--generator", type=str,
1681                   help=("CMake generator to use when building libraries with "
1682                         "cmake"))
1683group.add_argument("--toolset", type=str,
1684                   help=("CMake toolset to use when building libraries with "
1685                         "cmake"))
1686
1687if Linux():
1688    group.add_argument("--use-cxx11-abi", type=int, choices=[0, 1],
1689                       help=("Use C++11 ABI for libstdc++. (see docs above)"))
1690
1691group = parser.add_argument_group(title="3rd Party Dependency Build Options")
1692group.add_argument("--src", type=str,
1693                   help=("Directory where dependencies will be downloaded "
1694                         "(default: <install_dir>/src)"))
1695group.add_argument("--inst", type=str,
1696                   help=("Directory where dependencies will be installed "
1697                         "(default: <install_dir>)"))
1698
1699group = parser.add_argument_group(title="USD Options")
1700
1701(SHARED_LIBS, MONOLITHIC_LIB) = (0, 1)
1702subgroup = group.add_mutually_exclusive_group()
1703subgroup.add_argument("--build-shared", dest="build_type",
1704                      action="store_const", const=SHARED_LIBS,
1705                      default=SHARED_LIBS,
1706                      help="Build individual shared libraries (default)")
1707subgroup.add_argument("--build-monolithic", dest="build_type",
1708                      action="store_const", const=MONOLITHIC_LIB,
1709                      help="Build a single monolithic shared library")
1710
1711subgroup = group.add_mutually_exclusive_group()
1712subgroup.add_argument("--tests", dest="build_tests", action="store_true",
1713                      default=False, help="Build unit tests")
1714subgroup.add_argument("--no-tests", dest="build_tests", action="store_false",
1715                      help="Do not build unit tests (default)")
1716subgroup = group.add_mutually_exclusive_group()
1717subgroup.add_argument("--examples", dest="build_examples", action="store_true",
1718                      default=True, help="Build examples (default)")
1719subgroup.add_argument("--no-examples", dest="build_examples", action="store_false",
1720                      help="Do not build examples")
1721subgroup = group.add_mutually_exclusive_group()
1722subgroup.add_argument("--tutorials", dest="build_tutorials", action="store_true",
1723                      default=True, help="Build tutorials (default)")
1724subgroup.add_argument("--no-tutorials", dest="build_tutorials", action="store_false",
1725                      help="Do not build tutorials")
1726subgroup = group.add_mutually_exclusive_group()
1727subgroup.add_argument("--tools", dest="build_tools", action="store_true",
1728                     default=True, help="Build USD tools (default)")
1729subgroup.add_argument("--no-tools", dest="build_tools", action="store_false",
1730                      help="Do not build USD tools")
1731subgroup = group.add_mutually_exclusive_group()
1732subgroup.add_argument("--docs", dest="build_docs", action="store_true",
1733                      default=False, help="Build documentation")
1734subgroup.add_argument("--no-docs", dest="build_docs", action="store_false",
1735                      help="Do not build documentation (default)")
1736subgroup = group.add_mutually_exclusive_group()
1737subgroup.add_argument("--python", dest="build_python", action="store_true",
1738                      default=True, help="Build python based components "
1739                                         "(default)")
1740subgroup.add_argument("--no-python", dest="build_python", action="store_false",
1741                      help="Do not build python based components")
1742subgroup = group.add_mutually_exclusive_group()
1743subgroup.add_argument("--prefer-safety-over-speed", dest="safety_first",
1744                      action="store_true", default=True, help=
1745                      "Enable extra safety checks (which may negatively "
1746                      "impact performance) against malformed input files "
1747                      "(default)")
1748subgroup.add_argument("--prefer-speed-over-safety", dest="safety_first",
1749                      action="store_false", help=
1750                      "Disable performance-impacting safety checks against "
1751                      "malformed input files")
1752
1753subgroup = group.add_mutually_exclusive_group()
1754subgroup.add_argument("--debug-python", dest="debug_python", action="store_true",
1755                      help="Define Boost Python Debug if your Python library comes with Debugging symbols.")
1756
1757subgroup.add_argument("--no-debug-python", dest="debug_python", action="store_false",
1758                      help="Don't define Boost Python Debug if your Python library comes with Debugging symbols.")
1759
1760(NO_IMAGING, IMAGING, USD_IMAGING) = (0, 1, 2)
1761
1762group = parser.add_argument_group(title="Imaging and USD Imaging Options")
1763subgroup = group.add_mutually_exclusive_group()
1764subgroup.add_argument("--imaging", dest="build_imaging",
1765                      action="store_const", const=IMAGING, default=USD_IMAGING,
1766                      help="Build imaging component")
1767subgroup.add_argument("--usd-imaging", dest="build_imaging",
1768                      action="store_const", const=USD_IMAGING,
1769                      help="Build imaging and USD imaging components (default)")
1770subgroup.add_argument("--no-imaging", dest="build_imaging",
1771                      action="store_const", const=NO_IMAGING,
1772                      help="Do not build imaging or USD imaging components")
1773subgroup = group.add_mutually_exclusive_group()
1774subgroup.add_argument("--ptex", dest="enable_ptex", action="store_true",
1775                      default=False,
1776                      help="Enable Ptex support in imaging")
1777subgroup.add_argument("--no-ptex", dest="enable_ptex",
1778                      action="store_false",
1779                      help="Disable Ptex support in imaging (default)")
1780subgroup = group.add_mutually_exclusive_group()
1781subgroup.add_argument("--openvdb", dest="enable_openvdb", action="store_true",
1782                      default=False,
1783                      help="Enable OpenVDB support in imaging")
1784subgroup.add_argument("--no-openvdb", dest="enable_openvdb",
1785                      action="store_false",
1786                      help="Disable OpenVDB support in imaging (default)")
1787subgroup = group.add_mutually_exclusive_group()
1788subgroup.add_argument("--usdview", dest="build_usdview",
1789                      action="store_true", default=True,
1790                      help="Build usdview (default)")
1791subgroup.add_argument("--no-usdview", dest="build_usdview",
1792                      action="store_false",
1793                      help="Do not build usdview")
1794
1795group = parser.add_argument_group(title="Imaging Plugin Options")
1796subgroup = group.add_mutually_exclusive_group()
1797subgroup.add_argument("--embree", dest="build_embree", action="store_true",
1798                      default=False,
1799                      help="Build Embree sample imaging plugin")
1800subgroup.add_argument("--no-embree", dest="build_embree", action="store_false",
1801                      help="Do not build Embree sample imaging plugin (default)")
1802subgroup = group.add_mutually_exclusive_group()
1803subgroup.add_argument("--prman", dest="build_prman", action="store_true",
1804                      default=False,
1805                      help="Build Pixar's RenderMan imaging plugin")
1806subgroup.add_argument("--no-prman", dest="build_prman", action="store_false",
1807                      help="Do not build Pixar's RenderMan imaging plugin (default)")
1808group.add_argument("--prman-location", type=str,
1809                   help="Directory where Pixar's RenderMan is installed.")
1810subgroup = group.add_mutually_exclusive_group()
1811subgroup.add_argument("--openimageio", dest="build_oiio", action="store_true",
1812                      default=False,
1813                      help="Build OpenImageIO plugin for USD")
1814subgroup.add_argument("--no-openimageio", dest="build_oiio", action="store_false",
1815                      help="Do not build OpenImageIO plugin for USD (default)")
1816subgroup = group.add_mutually_exclusive_group()
1817subgroup.add_argument("--opencolorio", dest="build_ocio", action="store_true",
1818                      default=False,
1819                      help="Build OpenColorIO plugin for USD")
1820subgroup.add_argument("--no-opencolorio", dest="build_ocio", action="store_false",
1821                      help="Do not build OpenColorIO plugin for USD (default)")
1822
1823group = parser.add_argument_group(title="Alembic Plugin Options")
1824subgroup = group.add_mutually_exclusive_group()
1825subgroup.add_argument("--alembic", dest="build_alembic", action="store_true",
1826                      default=False,
1827                      help="Build Alembic plugin for USD")
1828subgroup.add_argument("--no-alembic", dest="build_alembic", action="store_false",
1829                      help="Do not build Alembic plugin for USD (default)")
1830subgroup = group.add_mutually_exclusive_group()
1831subgroup.add_argument("--hdf5", dest="enable_hdf5", action="store_true",
1832                      default=False,
1833                      help="Enable HDF5 support in the Alembic plugin")
1834subgroup.add_argument("--no-hdf5", dest="enable_hdf5", action="store_false",
1835                      help="Disable HDF5 support in the Alembic plugin (default)")
1836
1837group = parser.add_argument_group(title="Draco Plugin Options")
1838subgroup = group.add_mutually_exclusive_group()
1839subgroup.add_argument("--draco", dest="build_draco", action="store_true",
1840                      default=False,
1841                      help="Build Draco plugin for USD")
1842subgroup.add_argument("--no-draco", dest="build_draco", action="store_false",
1843                      help="Do not build Draco plugin for USD (default)")
1844group.add_argument("--draco-location", type=str,
1845                   help="Directory where Draco is installed.")
1846
1847group = parser.add_argument_group(title="MaterialX Plugin Options")
1848subgroup = group.add_mutually_exclusive_group()
1849subgroup.add_argument("--materialx", dest="build_materialx", action="store_true",
1850                      default=False,
1851                      help="Build MaterialX plugin for USD")
1852subgroup.add_argument("--no-materialx", dest="build_materialx", action="store_false",
1853                      help="Do not build MaterialX plugin for USD (default)")
1854
1855args = parser.parse_args()
1856
1857class InstallContext:
1858    def __init__(self, args):
1859        # Assume the USD source directory is in the parent directory
1860        self.usdSrcDir = os.path.normpath(
1861            os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))
1862
1863        # Directory where USD will be installed
1864        self.usdInstDir = os.path.abspath(args.install_dir)
1865
1866        # Directory where dependencies will be installed
1867        self.instDir = (os.path.abspath(args.inst) if args.inst
1868                        else self.usdInstDir)
1869
1870        # Directory where dependencies will be downloaded and extracted
1871        self.srcDir = (os.path.abspath(args.src) if args.src
1872                       else os.path.join(self.usdInstDir, "src"))
1873
1874        # Directory where USD and dependencies will be built
1875        self.buildDir = (os.path.abspath(args.build) if args.build
1876                         else os.path.join(self.usdInstDir, "build"))
1877
1878        # Determine which downloader to use.  The reason we don't simply
1879        # use urllib2 all the time is that some older versions of Python
1880        # don't support TLS v1.2, which is required for downloading some
1881        # dependencies.
1882        if which("curl"):
1883            self.downloader = DownloadFileWithCurl
1884            self.downloaderName = "curl"
1885        elif Windows() and which("powershell"):
1886            self.downloader = DownloadFileWithPowershell
1887            self.downloaderName = "powershell"
1888        else:
1889            self.downloader = DownloadFileWithUrllib
1890            self.downloaderName = "built-in"
1891
1892        # CMake generator and toolset
1893        self.cmakeGenerator = args.generator
1894        self.cmakeToolset = args.toolset
1895
1896        # Number of jobs
1897        self.numJobs = args.jobs
1898        if self.numJobs <= 0:
1899            raise ValueError("Number of jobs must be greater than 0")
1900
1901        # Build arguments
1902        self.buildArgs = dict()
1903        for a in args.build_args:
1904            (depName, _, arg) = a.partition(",")
1905            if not depName or not arg:
1906                raise ValueError("Invalid argument for --build-args: {}"
1907                                 .format(a))
1908            if depName.lower() not in AllDependenciesByName:
1909                raise ValueError("Invalid library for --build-args: {}"
1910                                 .format(depName))
1911
1912            self.buildArgs.setdefault(depName.lower(), []).append(arg)
1913
1914        # Build python info
1915        self.build_python_info = dict()
1916        if args.build_python_info:
1917            self.build_python_info['PYTHON_EXECUTABLE'] = args.build_python_info[0]
1918            self.build_python_info['PYTHON_INCLUDE_DIR'] = args.build_python_info[1]
1919            self.build_python_info['PYTHON_LIBRARY'] = args.build_python_info[2]
1920            self.build_python_info['PYTHON_VERSION'] = args.build_python_info[3]
1921
1922        # Build type
1923        self.buildDebug = (args.build_variant == BUILD_DEBUG)
1924        self.buildRelease = (args.build_variant == BUILD_RELEASE)
1925        self.buildRelWithDebug = (args.build_variant == BUILD_RELWITHDEBUG)
1926
1927        self.debugPython = args.debug_python
1928
1929        self.buildShared = (args.build_type == SHARED_LIBS)
1930        self.buildMonolithic = (args.build_type == MONOLITHIC_LIB)
1931
1932        # Build options
1933        self.useCXX11ABI = \
1934            (args.use_cxx11_abi if hasattr(args, "use_cxx11_abi") else None)
1935        self.safetyFirst = args.safety_first
1936
1937        # Dependencies that are forced to be built
1938        self.forceBuildAll = args.force_all
1939        self.forceBuild = [dep.lower() for dep in args.force_build]
1940
1941        # Optional components
1942        self.buildTests = args.build_tests
1943        self.buildDocs = args.build_docs
1944        self.buildPython = args.build_python
1945        self.buildExamples = args.build_examples
1946        self.buildTutorials = args.build_tutorials
1947        self.buildTools = args.build_tools
1948
1949        # - Imaging
1950        self.buildImaging = (args.build_imaging == IMAGING or
1951                             args.build_imaging == USD_IMAGING)
1952        self.enablePtex = self.buildImaging and args.enable_ptex
1953        self.enableOpenVDB = self.buildImaging and args.enable_openvdb
1954
1955        # - USD Imaging
1956        self.buildUsdImaging = (args.build_imaging == USD_IMAGING)
1957
1958        # - usdview
1959        self.buildUsdview = (self.buildUsdImaging and
1960                             self.buildPython and
1961                             args.build_usdview)
1962
1963        # - Imaging plugins
1964        self.buildEmbree = self.buildImaging and args.build_embree
1965        self.buildPrman = self.buildImaging and args.build_prman
1966        self.prmanLocation = (os.path.abspath(args.prman_location)
1967                               if args.prman_location else None)
1968        self.buildOIIO = args.build_oiio
1969        self.buildOCIO = args.build_ocio
1970
1971        # - Alembic Plugin
1972        self.buildAlembic = args.build_alembic
1973        self.enableHDF5 = self.buildAlembic and args.enable_hdf5
1974
1975        # - Draco Plugin
1976        self.buildDraco = args.build_draco
1977        self.dracoLocation = (os.path.abspath(args.draco_location)
1978                                if args.draco_location else None)
1979
1980        # - MaterialX Plugin
1981        self.buildMaterialX = args.build_materialx
1982
1983    def GetBuildArguments(self, dep):
1984        return self.buildArgs.get(dep.name.lower(), [])
1985
1986    def ForceBuildDependency(self, dep):
1987        # Never force building a Python dependency, since users are required
1988        # to build these dependencies themselves.
1989        if type(dep) is PythonDependency:
1990            return False
1991        return self.forceBuildAll or dep.name.lower() in self.forceBuild
1992
1993try:
1994    context = InstallContext(args)
1995except Exception as e:
1996    PrintError(str(e))
1997    sys.exit(1)
1998
1999verbosity = args.verbosity
2000
2001# Augment PATH on Windows so that 3rd-party dependencies can find libraries
2002# they depend on. In particular, this is needed for building IlmBase/OpenEXR.
2003extraPaths = []
2004extraPythonPaths = []
2005if Windows():
2006    extraPaths.append(os.path.join(context.instDir, "lib"))
2007    extraPaths.append(os.path.join(context.instDir, "bin"))
2008
2009if extraPaths:
2010    paths = os.environ.get('PATH', '').split(os.pathsep) + extraPaths
2011    os.environ['PATH'] = os.pathsep.join(paths)
2012
2013if extraPythonPaths:
2014    paths = os.environ.get('PYTHONPATH', '').split(os.pathsep) + extraPythonPaths
2015    os.environ['PYTHONPATH'] = os.pathsep.join(paths)
2016
2017# Determine list of dependencies that are required based on options
2018# user has selected.
2019requiredDependencies = [ZLIB, BOOST, TBB]
2020
2021if context.buildAlembic:
2022    if context.enableHDF5:
2023        requiredDependencies += [HDF5]
2024    requiredDependencies += [OPENEXR, ALEMBIC]
2025
2026if context.buildDraco:
2027    requiredDependencies += [DRACO]
2028
2029if context.buildMaterialX:
2030    requiredDependencies += [MATERIALX]
2031
2032if context.buildImaging:
2033    if context.enablePtex:
2034        requiredDependencies += [PTEX]
2035
2036    requiredDependencies += [OPENSUBDIV]
2037
2038    if context.enableOpenVDB:
2039        requiredDependencies += [BLOSC, BOOST, OPENEXR, OPENVDB, TBB]
2040
2041    if context.buildOIIO:
2042        requiredDependencies += [BOOST, JPEG, TIFF, PNG, OPENEXR, OPENIMAGEIO]
2043
2044    if context.buildOCIO:
2045        requiredDependencies += [OPENCOLORIO]
2046
2047    if context.buildEmbree:
2048        requiredDependencies += [TBB, EMBREE]
2049
2050if context.buildUsdview:
2051    requiredDependencies += [PYOPENGL, PYSIDE]
2052
2053# Assume zlib already exists on Linux platforms and don't build
2054# our own. This avoids potential issues where a host application
2055# loads an older version of zlib than the one we'd build and link
2056# our libraries against.
2057if Linux():
2058    requiredDependencies.remove(ZLIB)
2059
2060# Error out if user is building monolithic library on windows with draco plugin
2061# enabled. This currently results in missing symbols.
2062if context.buildDraco and context.buildMonolithic and Windows():
2063    PrintError("Draco plugin can not be enabled for monolithic build on Windows")
2064    sys.exit(1)
2065
2066# Error out if user explicitly specified building usdview without required
2067# components. Otherwise, usdview will be silently disabled. This lets users
2068# specify "--no-python" without explicitly having to specify "--no-usdview",
2069# for instance.
2070if "--usdview" in sys.argv:
2071    if not context.buildUsdImaging:
2072        PrintError("Cannot build usdview when usdImaging is disabled.")
2073        sys.exit(1)
2074    if not context.buildPython:
2075        PrintError("Cannot build usdview when Python support is disabled.")
2076        sys.exit(1)
2077
2078dependenciesToBuild = []
2079for dep in requiredDependencies:
2080    if context.ForceBuildDependency(dep) or not dep.Exists(context):
2081        if dep not in dependenciesToBuild:
2082            dependenciesToBuild.append(dep)
2083
2084# Verify toolchain needed to build required dependencies
2085if (not which("g++") and
2086    not which("clang") and
2087    not GetXcodeDeveloperDirectory() and
2088    not GetVisualStudioCompilerAndVersion()):
2089    PrintError("C++ compiler not found -- please install a compiler")
2090    sys.exit(1)
2091
2092# Error out if a 64bit version of python interpreter is not being used
2093isPython64Bit = (ctypes.sizeof(ctypes.c_voidp) == 8)
2094if not isPython64Bit:
2095    PrintError("64bit python not found -- please install it and adjust your"
2096               "PATH")
2097    sys.exit(1)
2098
2099if which("cmake"):
2100    # Check cmake requirements
2101    if Windows():
2102        # Windows build depend on boost 1.70, which is not supported before
2103        # cmake version 3.14
2104        cmake_required_version = (3, 14)
2105    else:
2106        cmake_required_version = (3, 12)
2107    cmake_version = GetCMakeVersion()
2108    if not cmake_version:
2109        PrintError("Failed to determine CMake version")
2110        sys.exit(1)
2111
2112    if cmake_version < cmake_required_version:
2113        def _JoinVersion(v):
2114            return ".".join(str(n) for n in v)
2115        PrintError("CMake version {req} or later required to build USD, "
2116                   "but version found was {found}".format(
2117                       req=_JoinVersion(cmake_required_version),
2118                       found=_JoinVersion(cmake_version)))
2119        sys.exit(1)
2120else:
2121    PrintError("CMake not found -- please install it and adjust your PATH")
2122    sys.exit(1)
2123
2124if context.buildDocs:
2125    if not which("doxygen"):
2126        PrintError("doxygen not found -- please install it and adjust your PATH")
2127        sys.exit(1)
2128
2129    if not which("dot"):
2130        PrintError("dot not found -- please install graphviz and adjust your "
2131                   "PATH")
2132        sys.exit(1)
2133
2134if PYSIDE in requiredDependencies:
2135    # Special case - we are given the PYSIDEUICBINARY as cmake arg.
2136    usdBuildArgs = context.GetBuildArguments(USD)
2137    given_pysideUic = 'PYSIDEUICBINARY' in " ".join(usdBuildArgs)
2138
2139    # The USD build will skip building usdview if pyside2-uic or pyside-uic is
2140    # not found, so check for it here to avoid confusing users. This list of
2141    # PySide executable names comes from cmake/modules/FindPySide.cmake
2142    pyside2Uic = ["pyside2-uic", "python2-pyside2-uic", "pyside2-uic-2.7", "uic"]
2143    found_pyside2Uic = any([which(p) for p in pyside2Uic])
2144    pysideUic = ["pyside-uic", "python2-pyside-uic", "pyside-uic-2.7"]
2145    found_pysideUic = any([which(p) for p in pysideUic])
2146    if not given_pysideUic and not found_pyside2Uic and not found_pysideUic:
2147        if Windows():
2148            # Windows does not support PySide2 with Python2.7
2149            PrintError("pyside-uic not found -- please install PySide and"
2150                       " adjust your PATH. (Note that this program may be named"
2151                       " {0} depending on your platform)"
2152                   .format(" or ".join(pysideUic)))
2153        else:
2154            PrintError("pyside2-uic not found -- please install PySide2 and"
2155                       " adjust your PATH. (Note that this program may be"
2156                       " named {0} depending on your platform)"
2157                       .format(" or ".join(pyside2Uic)))
2158        sys.exit(1)
2159
2160if JPEG in requiredDependencies:
2161    # NASM is required to build libjpeg-turbo
2162    if (Windows() and not which("nasm")):
2163        PrintError("nasm not found -- please install it and adjust your PATH")
2164        sys.exit(1)
2165
2166# Summarize
2167summaryMsg = """
2168Building with settings:
2169  USD source directory          {usdSrcDir}
2170  USD install directory         {usdInstDir}
2171  3rd-party source directory    {srcDir}
2172  3rd-party install directory   {instDir}
2173  Build directory               {buildDir}
2174  CMake generator               {cmakeGenerator}
2175  CMake toolset                 {cmakeToolset}
2176  Downloader                    {downloader}
2177
2178  Building                      {buildType}
2179"""
2180
2181if context.useCXX11ABI is not None:
2182    summaryMsg += """\
2183    Use C++11 ABI               {useCXX11ABI}
2184"""
2185
2186summaryMsg += """\
2187    Variant                     {buildVariant}
2188    Imaging                     {buildImaging}
2189      Ptex support:             {enablePtex}
2190      OpenVDB support:          {enableOpenVDB}
2191      OpenImageIO support:      {buildOIIO}
2192      OpenColorIO support:      {buildOCIO}
2193      PRMan support:            {buildPrman}
2194    UsdImaging                  {buildUsdImaging}
2195      usdview:                  {buildUsdview}
2196    Python support              {buildPython}
2197      Python Debug:             {debugPython}
2198      Python 3:                 {enablePython3}
2199    Documentation               {buildDocs}
2200    Tests                       {buildTests}
2201    Examples                    {buildExamples}
2202    Tutorials                   {buildTutorials}
2203    Tools                       {buildTools}
2204    Alembic Plugin              {buildAlembic}
2205      HDF5 support:             {enableHDF5}
2206    Draco Plugin                {buildDraco}
2207    MaterialX Plugin            {buildMaterialX}
2208
2209  Dependencies                  {dependencies}"""
2210
2211if context.buildArgs:
2212    summaryMsg += """
2213  Build arguments               {buildArgs}"""
2214
2215def FormatBuildArguments(buildArgs):
2216    s = ""
2217    for depName in sorted(buildArgs.keys()):
2218        args = buildArgs[depName]
2219        s += """
2220                                {name}: {args}""".format(
2221            name=AllDependenciesByName[depName].name,
2222            args=" ".join(args))
2223    return s.lstrip()
2224
2225summaryMsg = summaryMsg.format(
2226    usdSrcDir=context.usdSrcDir,
2227    usdInstDir=context.usdInstDir,
2228    srcDir=context.srcDir,
2229    buildDir=context.buildDir,
2230    instDir=context.instDir,
2231    cmakeGenerator=("Default" if not context.cmakeGenerator
2232                    else context.cmakeGenerator),
2233    cmakeToolset=("Default" if not context.cmakeToolset
2234                  else context.cmakeToolset),
2235    downloader=(context.downloaderName),
2236    dependencies=("None" if not dependenciesToBuild else
2237                  ", ".join([d.name for d in dependenciesToBuild])),
2238    buildArgs=FormatBuildArguments(context.buildArgs),
2239    useCXX11ABI=("On" if context.useCXX11ABI else "Off"),
2240    buildType=("Shared libraries" if context.buildShared
2241               else "Monolithic shared library" if context.buildMonolithic
2242               else ""),
2243    buildVariant=("Release" if context.buildRelease
2244                  else "Debug" if context.buildDebug
2245                  else "Release w/ Debug Info" if context.buildRelWithDebug
2246                  else ""),
2247    buildImaging=("On" if context.buildImaging else "Off"),
2248    enablePtex=("On" if context.enablePtex else "Off"),
2249    enableOpenVDB=("On" if context.enableOpenVDB else "Off"),
2250    buildOIIO=("On" if context.buildOIIO else "Off"),
2251    buildOCIO=("On" if context.buildOCIO else "Off"),
2252    buildPrman=("On" if context.buildPrman else "Off"),
2253    buildUsdImaging=("On" if context.buildUsdImaging else "Off"),
2254    buildUsdview=("On" if context.buildUsdview else "Off"),
2255    buildPython=("On" if context.buildPython else "Off"),
2256    debugPython=("On" if context.debugPython else "Off"),
2257    enablePython3=("On" if Python3() else "Off"),
2258    buildDocs=("On" if context.buildDocs else "Off"),
2259    buildTests=("On" if context.buildTests else "Off"),
2260    buildExamples=("On" if context.buildExamples else "Off"),
2261    buildTutorials=("On" if context.buildTutorials else "Off"),
2262    buildTools=("On" if context.buildTools else "Off"),
2263    buildAlembic=("On" if context.buildAlembic else "Off"),
2264    buildDraco=("On" if context.buildDraco else "Off"),
2265    buildMaterialX=("On" if context.buildMaterialX else "Off"),
2266    enableHDF5=("On" if context.enableHDF5 else "Off"))
2267
2268Print(summaryMsg)
2269
2270if args.dry_run:
2271    sys.exit(0)
2272
2273# Scan for any dependencies that the user is required to install themselves
2274# and print those instructions first.
2275pythonDependencies = \
2276    [dep for dep in dependenciesToBuild if type(dep) is PythonDependency]
2277if pythonDependencies:
2278    for dep in pythonDependencies:
2279        Print(dep.getInstructions())
2280    sys.exit(1)
2281
2282# Ensure directory structure is created and is writable.
2283for dir in [context.usdInstDir, context.instDir, context.srcDir,
2284            context.buildDir]:
2285    try:
2286        if os.path.isdir(dir):
2287            testFile = os.path.join(dir, "canwrite")
2288            open(testFile, "w").close()
2289            os.remove(testFile)
2290        else:
2291            os.makedirs(dir)
2292    except Exception as e:
2293        PrintError("Could not write to directory {dir}. Change permissions "
2294                   "or choose a different location to install to."
2295                   .format(dir=dir))
2296        sys.exit(1)
2297
2298try:
2299    # Download and install 3rd-party dependencies, followed by USD.
2300    for dep in dependenciesToBuild + [USD]:
2301        PrintStatus("Installing {dep}...".format(dep=dep.name))
2302        dep.installer(context,
2303                      buildArgs=context.GetBuildArguments(dep),
2304                      force=context.ForceBuildDependency(dep))
2305except Exception as e:
2306    PrintError(str(e))
2307    sys.exit(1)
2308
2309# Done. Print out a final status message.
2310requiredInPythonPath = set([
2311    os.path.join(context.usdInstDir, "lib", "python")
2312])
2313requiredInPythonPath.update(extraPythonPaths)
2314
2315requiredInPath = set([
2316    os.path.join(context.usdInstDir, "bin")
2317])
2318requiredInPath.update(extraPaths)
2319
2320if Windows():
2321    requiredInPath.update([
2322        os.path.join(context.usdInstDir, "lib"),
2323        os.path.join(context.instDir, "bin"),
2324        os.path.join(context.instDir, "lib")
2325    ])
2326
2327Print("""
2328Success! To use USD, please ensure that you have:""")
2329
2330if context.buildPython:
2331    Print("""
2332    The following in your PYTHONPATH environment variable:
2333    {requiredInPythonPath}""".format(
2334        requiredInPythonPath="\n    ".join(sorted(requiredInPythonPath))))
2335
2336Print("""
2337    The following in your PATH environment variable:
2338    {requiredInPath}
2339""".format(requiredInPath="\n    ".join(sorted(requiredInPath))))
2340
2341if context.buildPrman:
2342    Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html "
2343          "for setting up the RenderMan plugin.\n")
2344