diff --git a/README.md b/README.md index 89057cddec..7b42600eaa 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ then build and install USD into `/path/to/my_usd_install_dir`. > python USD/build_scripts/build_usd.py /path/to/my_usd_install_dir ``` -##### MacOS: +##### macOS: In a terminal, run `xcode-select` to ensure command line developer tools are installed. Then run the script. @@ -141,6 +141,14 @@ then build and install USD into `/path/to/my_usd_install_dir`. > python USD/build_scripts/build_usd.py /path/to/my_usd_install_dir ``` +###### iOS + +When building from a macOS system, you can also cross compile for other iOS. +To build for iOS, add the `--build-target iOS` parameter. + +iOS builds currently do not support Imaging. +Additionally, they will not support Python bindings or command line tools. + ##### Windows: Launch the "x64 Native Tools Command Prompt" for your version of Visual Studio diff --git a/build_scripts/apple_utils.py b/build_scripts/apple_utils.py index 2c82243896..8c5263502d 100644 --- a/build_scripts/apple_utils.py +++ b/build_scripts/apple_utils.py @@ -35,20 +35,26 @@ import platform import shlex import subprocess +import re +from typing import Optional, List TARGET_NATIVE = "native" TARGET_X86 = "x86_64" TARGET_ARM64 = "arm64" TARGET_UNIVERSAL = "universal" +TARGET_IOS = "ios" + +EMBEDDED_PLATFORMS = [TARGET_IOS] def GetBuildTargets(): return [TARGET_NATIVE, TARGET_X86, TARGET_ARM64, - TARGET_UNIVERSAL] + TARGET_UNIVERSAL, + TARGET_IOS] def GetBuildTargetDefault(): - return TARGET_NATIVE; + return TARGET_NATIVE def MacOS(): return platform.system() == "Darwin" @@ -56,15 +62,11 @@ def MacOS(): def GetLocale(): return sys.stdout.encoding or locale.getdefaultlocale()[1] or "UTF-8" -def GetCommandOutput(command): - """Executes the specified command and returns output or None.""" +def GetCommandOutput(command, **kwargs): try: - return subprocess.check_output( - shlex.split(command), - stderr=subprocess.STDOUT).decode(GetLocale(), 'replace').strip() - except subprocess.CalledProcessError: - pass - return None + return subprocess.check_output(command, stderr=subprocess.STDOUT, **kwargs).decode(GetLocale(), 'replace').strip() + except: + return None def GetTargetArmArch(): # Allows the arm architecture string to be overridden by @@ -72,7 +74,7 @@ def GetTargetArmArch(): return os.environ.get('MACOS_ARM_ARCHITECTURE') or TARGET_ARM64 def GetHostArch(): - macArch = GetCommandOutput('arch').strip() + macArch = GetCommandOutput(["arch"]) if macArch == "i386" or macArch == TARGET_X86: macArch = TARGET_X86 else: @@ -85,7 +87,7 @@ def GetTargetArch(context): else: if context.targetX86: macTargets = TARGET_X86 - if context.targetARM64: + if context.targetARM64 or context.targetIos: macTargets = GetTargetArmArch() if context.targetUniversal: macTargets = TARGET_X86 + ";" + GetTargetArmArch() @@ -106,6 +108,8 @@ def GetTargetArchPair(context): primaryArch = TARGET_X86 if context.targetARM64: primaryArch = GetTargetArmArch() + if context.targetIos: + primaryArch = TARGET_IOS if context.targetUniversal: primaryArch = GetHostArch() if (primaryArch == TARGET_X86): @@ -118,18 +122,52 @@ def GetTargetArchPair(context): def SupportsMacOSUniversalBinaries(): if not MacOS(): return False - XcodeOutput = GetCommandOutput('/usr/bin/xcodebuild -version') + XcodeOutput = GetCommandOutput(["/usr/bin/xcodebuild", "-version"]) XcodeFind = XcodeOutput.rfind('Xcode ', 0, len(XcodeOutput)) XcodeVersion = XcodeOutput[XcodeFind:].split(' ')[1] return (XcodeVersion > '11.0') + +def GetSDKVersion(sdk: str) -> Optional[str]: + """Gets the SDK version from the currently active Xcode""" + sdks = GetCommandOutput(["xcodebuild", "-showsdks"]).split(os.linesep) + for line in sdks: + line = line.strip() + if sdk not in line or "-sdk" not in line: + continue + + line = line.split("-sdk ")[-1] + if not line.startswith(sdk): + continue + + tokens = [t for t in line.replace(sdk, "").split(".") if t.isnumeric()] + if not tokens: + continue + return ".".join(tokens) + return None + +def GetSDKRoot(context) -> Optional[str]: + sdk = "macosx" + if context.buildTarget == TARGET_IOS: + sdk = "iphoneos" + + for arg in (context.cmakeBuildArgs or '').split(): + if "CMAKE_OSX_SYSROOT" in arg: + override = arg.split('=')[1].strip('"').strip() + if override: + sdk = override + return GetCommandOutput(["xcrun", "--sdk", sdk, "--show-sdk-path"]) + + def SetTarget(context, targetName): context.targetNative = (targetName == TARGET_NATIVE) context.targetX86 = (targetName == TARGET_X86) context.targetARM64 = (targetName == GetTargetArmArch()) context.targetUniversal = (targetName == TARGET_UNIVERSAL) + context.targetIos = (targetName == TARGET_IOS) + context.iosVersion = GetSDKVersion("iphoneos") if context.targetIos else None if context.targetUniversal and not SupportsMacOSUniversalBinaries(): - self.targetUniversal = False + context.targetUniversal = False raise ValueError( "Universal binaries only supported in macOS 11.0 and later.") @@ -150,26 +188,63 @@ def ExtractFilesRecursive(path, cond): files.append(os.path.join(r, file)) return files -def CodesignFiles(files): - SDKVersion = subprocess.check_output( - ['xcodebuild', '-version']).strip()[6:10] - codeSignIDs = subprocess.check_output( - ['security', 'find-identity', '-vp', 'codesigning']) +def _GetCodeSignStringFromTerminal(): + codeSignIDs = GetCommandOutput(['security', 'find-identity', '-vp', 'codesigning']) + return codeSignIDs - codeSignID = "-" +def GetCodeSignID(): if os.environ.get('CODE_SIGN_ID'): - codeSignID = os.environ.get('CODE_SIGN_ID') - elif float(SDKVersion) >= 11.0 and \ - codeSignIDs.find(b'Apple Development') != -1: - codeSignID = "Apple Development" - elif codeSignIDs.find(b'Mac Developer') != -1: - codeSignID = "Mac Developer" + return os.environ.get('CODE_SIGN_ID') + + codeSignIDs = _GetCodeSignStringFromTerminal() + if not codeSignIDs: + return "-" + for codeSignID in codeSignIDs.splitlines(): + if "CSSMERR_TP_CERT_REVOKED" in codeSignID: + continue + if ")" not in codeSignID: + continue + codeSignID = codeSignID.split()[1] + break + else: + raise RuntimeError("Could not find a valid codesigning ID") + + return codeSignID or "-" + +def GetCodeSignIDHash(): + codeSignIDs = _GetCodeSignStringFromTerminal() + try: + return re.findall(r'\(.*?\)', codeSignIDs)[0][1:-1] + except: + raise Exception("Unable to parse codesign ID hash") + +def GetDevelopmentTeamID(): + if os.environ.get("DEVELOPMENT_TEAM"): + return os.environ.get("DEVELOPMENT_TEAM") + codesignID = GetCodeSignIDHash() + + certs = subprocess.check_output(["security", "find-certificate", "-c", codesignID, "-p"]) + subject = GetCommandOutput(["openssl", "x509", "-subject"], input=certs) + subject = subject.splitlines()[0] + + # Extract the Organizational Unit (OU field) from the cert + try: + team = [elm for elm in subject.split( + '/') if elm.startswith('OU')][0].split('=')[1] + if team is not None and team != "": + return team + except Exception as ex: + raise Exception("No development team found with exception " + ex) + +def CodesignFiles(files): + codeSignID = GetCodeSignID() for f in files: subprocess.call(['codesign', '-f', '-s', '{codesignid}' - .format(codesignid=codeSignID), f], + .format(codesignid=codeSignID), f], stdout=devout, stderr=devout) + def Codesign(install_path, verbose_output=False): if not MacOS(): return False @@ -217,3 +292,12 @@ def CreateUniversalBinaries(context, libNames, x86Dir, armDir): instDir=context.instDir, libName=targetName), outputName) return lipoCommands + +def ConfigureCMakeExtraArgs(context, args:List[str]) -> List[str]: + system_name = None + if context.buildTarget == TARGET_IOS: + system_name = "iOS" + + if system_name: + args.append(f"-DCMAKE_SYSTEM_NAME={system_name}") + return args \ No newline at end of file diff --git a/build_scripts/build_usd.py b/build_scripts/build_usd.py index 5c47ab5547..aeea1aa6ca 100644 --- a/build_scripts/build_usd.py +++ b/build_scripts/build_usd.py @@ -51,6 +51,7 @@ from urllib.request import urlopen from shutil import which +import apple_utils # Helpers for printing output verbosity = 1 @@ -89,9 +90,6 @@ def Linux(): def MacOS(): return platform.system() == "Darwin" -if MacOS(): - import apple_utils - def GetLocale(): if Windows(): # Windows handles encoding a little differently then Linux / Mac @@ -251,7 +249,7 @@ def GetCPUCount(): except NotImplementedError: return 1 -def Run(cmd, logCommandOutput = True): +def Run(cmd, logCommandOutput = True, env=None): """Run the specified command in a subprocess.""" PrintInfo('Running "{cmd}"'.format(cmd=cmd)) @@ -265,7 +263,7 @@ def Run(cmd, logCommandOutput = True): # code will handle them. if logCommandOutput: p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, env=env) while True: l = p.stdout.readline().decode(GetLocale(), 'replace') if l: @@ -274,7 +272,7 @@ def Run(cmd, logCommandOutput = True): elif p.poll() is not None: break else: - p = subprocess.Popen(shlex.split(cmd)) + p = subprocess.Popen(shlex.split(cmd), env=env) p.wait() if p.returncode != 0: @@ -420,6 +418,7 @@ def RunCMake(context, force, extraArgs = None): extraArgs.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO') extraArgs.append('-DCMAKE_OSX_ARCHITECTURES={0}'.format(targetArch)) + extraArgs = apple_utils.ConfigureCMakeExtraArgs(context, extraArgs) if context.ignorePaths: ignoredPaths = ";".join(context.ignorePaths) @@ -689,6 +688,19 @@ def AnyPythonDependencies(deps): def InstallZlib(context, force, buildArgs): with CurrentWorkingDirectory(DownloadURL(ZLIB_URL, context, force)): + # The following test files aren't portable to embedded platforms. + # They're not required for use on any platforms, so we elide them for efficiency + PatchFile("CMakeLists.txt", + [("add_executable(example test/example.c)", + ""), + ("add_executable(minigzip test/minigzip.c)", + ""), + ("target_link_libraries(example zlib)", + ""), + ("target_link_libraries(minigzip zlib)", + ""), + ("add_test(example example)", + "")]) RunCMake(context, force, buildArgs) ZLIB = Dependency("zlib", InstallZlib, "include/zlib.h") @@ -996,15 +1008,31 @@ def InstallTBB_MacOS(context, force, buildArgs): if (secondaryArch == apple_utils.TARGET_X86): secondaryArch = "intel64" + # fixup if condition in main make config + PatchFile("build/macos.clang.inc", + [("TARGET)\nelse\n", "TARGET)\nelse ifeq (macos,$(target))\n")], + multiLineMatches=True) + + if context.buildTarget == apple_utils.TARGET_IOS: + primaryArch = "arm64" + buildArgs.append('compiler=clang arch=arm64 extra_inc=big_iron.inc ') + # Install both release and debug builds. # See comments in InstallTBB_Linux. def _RunBuild(arch): if not arch: return - makeTBBCmd = 'make -j{procs} arch={arch} {buildArgs}'.format( + platformArg = "" + env = os.environ.copy() + if context.buildTarget == apple_utils.TARGET_IOS: + platformArg = "target=ios" + env["SDKROOT"] = apple_utils.GetSDKRoot(context) + makeTBBCmd = 'make -j{procs} arch={arch} {platformArg} {buildArgs}'.format( arch=arch, procs=context.numJobs, + platformArg = platformArg, buildArgs=" ".join(buildArgs)) - Run(makeTBBCmd) + + Run(makeTBBCmd, env=env) _RunBuild(primaryArch) _RunBuild(secondaryArch) @@ -1356,6 +1384,9 @@ def InstallOpenSubdiv(context, force, buildArgs): '-DNO_GLFW=ON', ] + if MacOS(): + extraArgs.append('-DNO_OPENGL=ON') + # If Ptex support is disabled in USD, disable support in OpenSubdiv # as well. This ensures OSD doesn't accidentally pick up a Ptex # library outside of our build. @@ -1491,7 +1522,7 @@ def InstallDraco(context, force, buildArgs): ############################################################ # MaterialX -MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.7.zip" +MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.8.zip" def InstallMaterialX(context, force, buildArgs): with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)): @@ -1507,6 +1538,12 @@ def InstallMaterialX(context, force, buildArgs): cmakeOptions = ['-DMATERIALX_BUILD_SHARED_LIBS=ON', '-DMATERIALX_BUILD_TESTS=OFF' ] + if context.buildTarget in apple_utils.EMBEDDED_PLATFORMS: + cmakeOptions.extend([ + "-DMATERIALX_BUILD_GEN_MSL=ON", + "-DMATERIALX_BUILD_GEN_GLSL=OFF", + "-DMATERIALX_BUILD_IOS=ON" + ]) cmakeOptions += buildArgs RunCMake(context, force, cmakeOptions) @@ -1747,6 +1784,13 @@ def InstallUSD(context, force, buildArgs): # Increase the precompiled header buffer limit. extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"') + if context.buildTarget in apple_utils.EMBEDDED_PLATFORMS: + # Required to find locally built boost etc. + extraArgs.extend([ + '-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH', + '-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH', + '-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH']) + # Make sure to use boost installed by the build script and not any # system installed boost extraArgs.append('-DBoost_NO_BOOST_CMAKE=On') @@ -2172,6 +2216,8 @@ def __init__(self, args): self.buildMonolithic = (args.build_type == MONOLITHIC_LIB) self.ignorePaths = args.ignore_paths or [] + self.buildTarget = None + self.macOSCodesign = "" # Build target and code signing if MacOS(): self.buildTarget = args.build_target @@ -2182,8 +2228,8 @@ def __init__(self, args): else False) if apple_utils.IsHostArm() and args.ignore_homebrew: self.ignorePaths.append("/opt/homebrew") - else: - self.buildTarget = "" + + coreOnly = self.buildTarget in apple_utils.EMBEDDED_PLATFORMS self.useCXX11ABI = \ (args.use_cxx11_abi if hasattr(args, "use_cxx11_abi") else None) @@ -2195,10 +2241,10 @@ def __init__(self, args): # Optional components self.buildTests = args.build_tests - self.buildPython = args.build_python + self.buildPython = args.build_python and not coreOnly self.buildExamples = args.build_examples self.buildTutorials = args.build_tutorials - self.buildTools = args.build_tools + self.buildTools = args.build_tools and not coreOnly # - Documentation self.buildDocs = args.build_docs or args.build_python_docs @@ -2207,7 +2253,7 @@ def __init__(self, args): # - Imaging self.buildImaging = (args.build_imaging == IMAGING or - args.build_imaging == USD_IMAGING) + args.build_imaging == USD_IMAGING) and not coreOnly self.enablePtex = self.buildImaging and args.enable_ptex self.enableOpenVDB = self.buildImaging and args.enable_openvdb @@ -2634,8 +2680,11 @@ def FormatBuildArguments(buildArgs): if context.macOSCodesign: apple_utils.Codesign(context.usdInstDir, verbosity > 1) -Print(""" -Success! To use USD, please ensure that you have:""") +printInstructions = any([context.buildPython, context.buildTools, context.buildPrman]) +if printInstructions: + Print("\nSuccess! To use USD, please ensure that you have:") +else: + Print("\nSuccess! USD libraries were built.") if context.buildPython: Print(""" @@ -2643,10 +2692,11 @@ def FormatBuildArguments(buildArgs): {requiredInPythonPath}""".format( requiredInPythonPath="\n ".join(sorted(requiredInPythonPath)))) -Print(""" - The following in your PATH environment variable: - {requiredInPath} -""".format(requiredInPath="\n ".join(sorted(requiredInPath)))) +if context.buildTools: + Print(""" + The following in your PATH environment variable: + {requiredInPath} + """.format(requiredInPath="\n ".join(sorted(requiredInPath)))) if context.buildPrman: Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html " diff --git a/pxr/base/arch/align.cpp b/pxr/base/arch/align.cpp index 5ec8a8e65b..8e1aa4029c 100644 --- a/pxr/base/arch/align.cpp +++ b/pxr/base/arch/align.cpp @@ -26,11 +26,12 @@ #include "pxr/base/arch/defines.h" #include "pxr/base/arch/error.h" -#if defined(ARCH_OS_DARWIN) +#if defined(ARCH_OS_IPHONE) +#elif defined(ARCH_OS_DARWIN) # include #else # include -#endif /* defined(ARCH_OS_DARWIN) */ +#endif /* defined(ARCH_OS_IPHONE) */ #include diff --git a/pxr/base/arch/debugger.cpp b/pxr/base/arch/debugger.cpp index fd093f09e9..4039d0d57e 100644 --- a/pxr/base/arch/debugger.cpp +++ b/pxr/base/arch/debugger.cpp @@ -34,7 +34,9 @@ #if defined(ARCH_OS_LINUX) || defined(ARCH_OS_DARWIN) #include "pxr/base/arch/inttypes.h" #include +#if !defined(ARCH_OS_IPHONE) #include +#endif #include #include #include diff --git a/pxr/base/arch/defines.h b/pxr/base/arch/defines.h index cb6ad44c5b..b5c10f88b5 100644 --- a/pxr/base/arch/defines.h +++ b/pxr/base/arch/defines.h @@ -34,7 +34,10 @@ #include "TargetConditionals.h" #define ARCH_OS_DARWIN #if TARGET_OS_IPHONE -#define ARCH_OS_IOS +// TARGET_OS_IPHONE refers to all iOS derivative platforms +// TARGET_OS_IOS refers to iPhone/iPad +// For now, we only specialize for the umbrella TARGET_OS_IPHONE group +#define ARCH_OS_IPHONE #else #define ARCH_OS_OSX #endif diff --git a/pxr/base/arch/mallocHook.cpp b/pxr/base/arch/mallocHook.cpp index e47787d7a6..cf776feb8a 100644 --- a/pxr/base/arch/mallocHook.cpp +++ b/pxr/base/arch/mallocHook.cpp @@ -33,11 +33,12 @@ #endif #include -#if defined(ARCH_OS_DARWIN) +#if defined(ARCH_OS_IPHONE) +#elif defined(ARCH_OS_DARWIN) # include #else # include -#endif /* defined(ARCH_OS_DARWIN) */ +#endif /* defined(ARCH_OS_IPHONE) */ PXR_NAMESPACE_OPEN_SCOPE diff --git a/pxr/imaging/garch/glPlatformContextDarwin.mm b/pxr/imaging/garch/glPlatformContextDarwin.mm index fccfa3b81b..950663319f 100644 --- a/pxr/imaging/garch/glPlatformContextDarwin.mm +++ b/pxr/imaging/garch/glPlatformContextDarwin.mm @@ -27,7 +27,7 @@ #include "pxr/pxr.h" #include "glPlatformContextDarwin.h" -#ifdef ARCH_OS_IOS +#ifdef ARCH_OS_IPHONE typedef EAGLContext NSGLContext; #else typedef NSOpenGLContext NSGLContext; @@ -90,7 +90,7 @@ void GarchNSGLContextState::MakeCurrent() { -#if ARCH_OS_IOS +#if ARCH_OS_IPHONE [EAGLContext setCurrentContext:_detail->context]; #else [_detail->context makeCurrentContext]; diff --git a/pxr/imaging/hgiMetal/capabilities.mm b/pxr/imaging/hgiMetal/capabilities.mm index 7d8065bd19..20aedef37a 100644 --- a/pxr/imaging/hgiMetal/capabilities.mm +++ b/pxr/imaging/hgiMetal/capabilities.mm @@ -50,7 +50,7 @@ if (@available(macOS 100.100, ios 12.0, *)) { unifiedMemory = true; } else if (@available(macOS 10.15, ios 13.0, *)) { -#if defined(ARCH_OS_IOS) || (defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15) +#if defined(ARCH_OS_IPHONE) || (defined(__MAC_10_15) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15) unifiedMemory = [device hasUnifiedMemory]; #else unifiedMemory = [device isLowPower]; diff --git a/pxr/imaging/hgiMetal/shaderGenerator.mm b/pxr/imaging/hgiMetal/shaderGenerator.mm index 0138b66c15..3a8e252fae 100644 --- a/pxr/imaging/hgiMetal/shaderGenerator.mm +++ b/pxr/imaging/hgiMetal/shaderGenerator.mm @@ -375,7 +375,7 @@ void _Init( } if (@available(macos 100.100, ios 12.0, *)) { - header << "#define ARCH_OS_IOS\n"; + header << "#define ARCH_OS_IPHONE\n"; // Define all iOS 12 feature set enums onwards if ([device supportsFeatureSet:MTLFeatureSet(12)]) header << "#define METAL_FEATURESET_IOS_GPUFAMILY1_v5\n"; diff --git a/pxr/imaging/hio/stbImage.cpp b/pxr/imaging/hio/stbImage.cpp index c9a85938a5..9ff197868d 100644 --- a/pxr/imaging/hio/stbImage.cpp +++ b/pxr/imaging/hio/stbImage.cpp @@ -439,7 +439,7 @@ Hio_StbImage::ReadCropped(int const cropTop, // thus we explicitly call stbi__vertical_flip below - assuming // that no other client called stbi_set_flip_vertically_on_load(true). -#if defined(ARCH_OS_IOS) +#if defined(ARCH_OS_IPHONE) stbi_convert_iphone_png_to_rgb(true); #endif