Skip to content

Commit 2fe7b7d

Browse files
committed
Add iOS build support for USD Core
This PR adds **Core** iOS support to the OpenUSD project. This does not include Imaging, and any Imaging related components at this time. Imaging will be added in a follow up PR. MaterialX is also disabled as requested and the upgrade will be handled in a follow up PR. It is a minimal version of PixarAnimationStudios#2455 against the latest `dev` branch. Changes include: * Using latest dev branch * No imaging support. Will be added in a follow up PR. * Makes use of CMake's inbuilt iOS support, negating the need for toolchain support * Structures the code in such a way that we can add support for other iOS derived platforms+simulator in future PRs * Swaps `ARCH_OS_IOS` with `ARCH_OS_IPHONE` to align with compiler directives. IPHONE refers to all derivative platforms, whereas iOS refers to only iPhone/iPad (Confusing but the case for historical reasons as [documented here](https://chaosinmotion.com/2021/08/02/things-to-remember-compiler-conditionals-for-macos-ios-etc/)) * TBB requires SDKROOT to be passed in or it can often go off and find some random compiler toolchain and fail. * Add APPLE_EMBEDDED boolean to designate when using the CMake supported cross compilation targets. Added in Options.cmake so it can be used to configure defaults properly.
1 parent 328e504 commit 2fe7b7d

3 files changed

Lines changed: 163 additions & 40 deletions

File tree

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ then build and install USD into `/path/to/my_usd_install_dir`.
129129
> python OpenUSD/build_scripts/build_usd.py /path/to/my_usd_install_dir
130130
```
131131

132-
##### MacOS:
132+
##### macOS:
133133

134134
In a terminal, run `xcode-select` to ensure command line developer tools are
135135
installed. Then run the script.
@@ -141,6 +141,16 @@ then build and install USD into `/path/to/my_usd_install_dir`.
141141
> python OpenUSD/build_scripts/build_usd.py /path/to/my_usd_install_dir
142142
```
143143

144+
###### iOS
145+
146+
When building from a macOS system, you can cross compile for iOS based platforms.
147+
148+
iOS builds currently do not support Imaging.
149+
Additionally, they will not support Python bindings or command line tools.
150+
151+
To build for iOS, add the `--build-target iOS` parameter.
152+
153+
144154
##### Windows:
145155

146156
Launch the "x64 Native Tools Command Prompt" for your version of Visual Studio

build_scripts/apple_utils.py

Lines changed: 95 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -35,57 +35,62 @@
3535
import platform
3636
import shlex
3737
import subprocess
38+
import re
39+
from typing import Optional, List
3840

3941
TARGET_NATIVE = "native"
4042
TARGET_X86 = "x86_64"
4143
TARGET_ARM64 = "arm64"
4244
TARGET_UNIVERSAL = "universal"
45+
TARGET_IOS = "iOS"
46+
47+
EMBEDDED_PLATFORMS = [TARGET_IOS]
4348

4449
def GetBuildTargets():
4550
return [TARGET_NATIVE,
4651
TARGET_X86,
4752
TARGET_ARM64,
48-
TARGET_UNIVERSAL]
53+
TARGET_UNIVERSAL,
54+
TARGET_IOS]
4955

5056
def GetBuildTargetDefault():
51-
return TARGET_NATIVE;
57+
return TARGET_NATIVE
5258

5359
def MacOS():
5460
return platform.system() == "Darwin"
5561

5662
def GetLocale():
5763
return sys.stdout.encoding or locale.getdefaultlocale()[1] or "UTF-8"
5864

59-
def GetCommandOutput(command):
65+
def GetCommandOutput(command, **kwargs):
6066
"""Executes the specified command and returns output or None."""
6167
try:
62-
return subprocess.check_output(
63-
shlex.split(command),
64-
stderr=subprocess.STDOUT).decode(GetLocale(), 'replace').strip()
65-
except subprocess.CalledProcessError:
66-
pass
67-
return None
68+
return subprocess.check_output(command, stderr=subprocess.STDOUT, **kwargs).decode(GetLocale(), 'replace').strip()
69+
except:
70+
return None
6871

6972
def GetTargetArmArch():
7073
# Allows the arm architecture string to be overridden by
7174
# setting MACOS_ARM_ARCHITECTURE
7275
return os.environ.get('MACOS_ARM_ARCHITECTURE') or TARGET_ARM64
7376

7477
def GetHostArch():
75-
macArch = GetCommandOutput('arch').strip()
78+
macArch = GetCommandOutput(["arch"])
7679
if macArch == "i386" or macArch == TARGET_X86:
7780
macArch = TARGET_X86
7881
else:
7982
macArch = GetTargetArmArch()
8083
return macArch
8184

8285
def GetTargetArch(context):
86+
if context.buildTarget in EMBEDDED_PLATFORMS:
87+
return GetTargetArmArch()
8388
if context.targetNative:
8489
macTargets = GetHostArch()
8590
else:
8691
if context.targetX86:
8792
macTargets = TARGET_X86
88-
if context.targetARM64:
93+
if context.targetARM64 or context.buildTarget in EMBEDDED_PLATFORMS:
8994
macTargets = GetTargetArmArch()
9095
if context.targetUniversal:
9196
macTargets = TARGET_X86 + ";" + GetTargetArmArch()
@@ -106,6 +111,8 @@ def GetTargetArchPair(context):
106111
primaryArch = TARGET_X86
107112
if context.targetARM64:
108113
primaryArch = GetTargetArmArch()
114+
if context.buildTarget in EMBEDDED_PLATFORMS:
115+
primaryArch = GetTargetArmArch()
109116
if context.targetUniversal:
110117
primaryArch = GetHostArch()
111118
if (primaryArch == TARGET_X86):
@@ -118,18 +125,33 @@ def GetTargetArchPair(context):
118125
def SupportsMacOSUniversalBinaries():
119126
if not MacOS():
120127
return False
121-
XcodeOutput = GetCommandOutput('/usr/bin/xcodebuild -version')
128+
XcodeOutput = GetCommandOutput(["/usr/bin/xcodebuild", "-version"])
122129
XcodeFind = XcodeOutput.rfind('Xcode ', 0, len(XcodeOutput))
123130
XcodeVersion = XcodeOutput[XcodeFind:].split(' ')[1]
124131
return (XcodeVersion > '11.0')
125132

133+
134+
def GetSDKRoot(context) -> Optional[str]:
135+
sdk = "macosx"
136+
if context.buildTarget == TARGET_IOS:
137+
sdk = "iphoneos"
138+
139+
for arg in (context.cmakeBuildArgs or '').split():
140+
if "CMAKE_OSX_SYSROOT" in arg:
141+
override = arg.split('=')[1].strip('"').strip()
142+
if override:
143+
sdk = override
144+
return GetCommandOutput(["xcrun", "--sdk", sdk, "--show-sdk-path"])
145+
146+
126147
def SetTarget(context, targetName):
127148
context.targetNative = (targetName == TARGET_NATIVE)
128149
context.targetX86 = (targetName == TARGET_X86)
129150
context.targetARM64 = (targetName == GetTargetArmArch())
130151
context.targetUniversal = (targetName == TARGET_UNIVERSAL)
152+
context.targetIos = (targetName == TARGET_IOS)
131153
if context.targetUniversal and not SupportsMacOSUniversalBinaries():
132-
self.targetUniversal = False
154+
context.targetUniversal = False
133155
raise ValueError(
134156
"Universal binaries only supported in macOS 11.0 and later.")
135157

@@ -138,7 +160,7 @@ def GetTargetName(context):
138160
TARGET_X86 if context.targetX86 else
139161
GetTargetArmArch() if context.targetARM64 else
140162
TARGET_UNIVERSAL if context.targetUniversal else
141-
"")
163+
context.buildTarget)
142164

143165
devout = open(os.devnull, 'w')
144166

@@ -150,26 +172,63 @@ def ExtractFilesRecursive(path, cond):
150172
files.append(os.path.join(r, file))
151173
return files
152174

153-
def CodesignFiles(files):
154-
SDKVersion = subprocess.check_output(
155-
['xcodebuild', '-version']).strip()[6:10]
156-
codeSignIDs = subprocess.check_output(
157-
['security', 'find-identity', '-vp', 'codesigning'])
175+
def _GetCodeSignStringFromTerminal():
176+
codeSignIDs = GetCommandOutput(['security', 'find-identity', '-vp', 'codesigning'])
177+
return codeSignIDs
158178

159-
codeSignID = "-"
179+
def GetCodeSignID():
160180
if os.environ.get('CODE_SIGN_ID'):
161-
codeSignID = os.environ.get('CODE_SIGN_ID')
162-
elif float(SDKVersion) >= 11.0 and \
163-
codeSignIDs.find(b'Apple Development') != -1:
164-
codeSignID = "Apple Development"
165-
elif codeSignIDs.find(b'Mac Developer') != -1:
166-
codeSignID = "Mac Developer"
181+
return os.environ.get('CODE_SIGN_ID')
182+
183+
codeSignIDs = _GetCodeSignStringFromTerminal()
184+
if not codeSignIDs:
185+
return "-"
186+
for codeSignID in codeSignIDs.splitlines():
187+
if "CSSMERR_TP_CERT_REVOKED" in codeSignID:
188+
continue
189+
if ")" not in codeSignID:
190+
continue
191+
codeSignID = codeSignID.split()[1]
192+
break
193+
else:
194+
raise RuntimeError("Could not find a valid codesigning ID")
195+
196+
return codeSignID or "-"
197+
198+
def GetCodeSignIDHash():
199+
codeSignIDs = _GetCodeSignStringFromTerminal()
200+
try:
201+
return re.findall(r'\(.*?\)', codeSignIDs)[0][1:-1]
202+
except:
203+
raise Exception("Unable to parse codesign ID hash")
204+
205+
def GetDevelopmentTeamID():
206+
if os.environ.get("DEVELOPMENT_TEAM"):
207+
return os.environ.get("DEVELOPMENT_TEAM")
208+
codesignID = GetCodeSignIDHash()
209+
210+
certs = subprocess.check_output(["security", "find-certificate", "-c", codesignID, "-p"])
211+
subject = GetCommandOutput(["openssl", "x509", "-subject"], input=certs)
212+
subject = subject.splitlines()[0]
213+
214+
# Extract the Organizational Unit (OU field) from the cert
215+
try:
216+
team = [elm for elm in subject.split(
217+
'/') if elm.startswith('OU')][0].split('=')[1]
218+
if team is not None and team != "":
219+
return team
220+
except Exception as ex:
221+
raise Exception("No development team found with exception " + ex)
222+
223+
def CodesignFiles(files):
224+
codeSignID = GetCodeSignID()
167225

168226
for f in files:
169227
subprocess.call(['codesign', '-f', '-s', '{codesignid}'
170-
.format(codesignid=codeSignID), f],
228+
.format(codesignid=codeSignID), f],
171229
stdout=devout, stderr=devout)
172230

231+
173232
def Codesign(install_path, verbose_output=False):
174233
if not MacOS():
175234
return False
@@ -217,3 +276,12 @@ def CreateUniversalBinaries(context, libNames, x86Dir, armDir):
217276
instDir=context.instDir, libName=targetName),
218277
outputName)
219278
return lipoCommands
279+
280+
def ConfigureCMakeExtraArgs(context, args:List[str]) -> List[str]:
281+
system_name = None
282+
if context.buildTarget == TARGET_IOS:
283+
system_name = "iOS"
284+
285+
if system_name:
286+
args.append(f"-DCMAKE_SYSTEM_NAME={system_name}")
287+
return args

build_scripts/build_usd.py

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ def GetCPUCount():
252252
except NotImplementedError:
253253
return 1
254254

255-
def Run(cmd, logCommandOutput = True):
255+
def Run(cmd, logCommandOutput = True, env = None):
256256
"""Run the specified command in a subprocess."""
257257
PrintInfo('Running "{cmd}"'.format(cmd=cmd))
258258

@@ -266,7 +266,7 @@ def Run(cmd, logCommandOutput = True):
266266
# code will handle them.
267267
if logCommandOutput:
268268
p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE,
269-
stderr=subprocess.STDOUT)
269+
stderr=subprocess.STDOUT, env=env)
270270
while True:
271271
l = p.stdout.readline().decode(GetLocale(), 'replace')
272272
if l:
@@ -421,6 +421,7 @@ def RunCMake(context, force, extraArgs = None):
421421
extraArgs.append('-DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO')
422422

423423
extraArgs.append('-DCMAKE_OSX_ARCHITECTURES={0}'.format(targetArch))
424+
extraArgs = apple_utils.ConfigureCMakeExtraArgs(context, extraArgs)
424425

425426
if context.ignorePaths:
426427
ignoredPaths = ";".join(context.ignorePaths)
@@ -690,6 +691,19 @@ def AnyPythonDependencies(deps):
690691

691692
def InstallZlib(context, force, buildArgs):
692693
with CurrentWorkingDirectory(DownloadURL(ZLIB_URL, context, force)):
694+
# The following test files aren't portable to embedded platforms.
695+
# They're not required for use on any platforms, so we elide them for efficiency
696+
PatchFile("CMakeLists.txt",
697+
[("add_executable(example test/example.c)",
698+
""),
699+
("add_executable(minigzip test/minigzip.c)",
700+
""),
701+
("target_link_libraries(example zlib)",
702+
""),
703+
("target_link_libraries(minigzip zlib)",
704+
""),
705+
("add_test(example example)",
706+
"")])
693707
RunCMake(context, force, buildArgs)
694708

695709
ZLIB = Dependency("zlib", InstallZlib, "include/zlib.h")
@@ -724,8 +738,8 @@ def InstallBoost_Helper(context, force, buildArgs):
724738
# However, there are some cases where a newer version is required.
725739
# - Building with Python 3.11 requires boost 1.82.0 or newer
726740
# (https://github.com/boostorg/python/commit/a218ba)
727-
# - Building on MacOS requires v1.82.0 or later for C++17 support starting
728-
# with Xcode 15. We choose to use this version for all MacOS builds for
741+
# - Building on MacOS requires v1.82.0 or later for C++17 support starting
742+
# with Xcode 15. We choose to use this version for all MacOS builds for
729743
# simplicity."
730744
# - Building with Python 3.10 requires boost 1.76.0 or newer
731745
# (https://github.com/boostorg/python/commit/cbd2d9)
@@ -822,6 +836,10 @@ def InstallBoost_Helper(context, force, buildArgs):
822836
'--with-regex'
823837
]
824838

839+
if MacOS() and context.buildTarget in apple_utils.EMBEDDED_PLATFORMS:
840+
# Treat all embedded platforms as iphone derivatives since we're not actually building anything of note
841+
b2_settings.append("target-os=iphone")
842+
825843
if context.buildPython:
826844
b2_settings.append("--with-python")
827845
pythonInfo = GetPythonInfo(context)
@@ -1004,10 +1022,16 @@ def InstallTBB_MacOS(context, force, buildArgs):
10041022
def _RunBuild(arch):
10051023
if not arch:
10061024
return
1007-
makeTBBCmd = 'make -j{procs} arch={arch} {buildArgs}'.format(
1025+
platformArg = ""
1026+
env = os.environ.copy()
1027+
env["SDKROOT"] = apple_utils.GetSDKRoot(context)
1028+
if context.buildTarget in apple_utils.EMBEDDED_PLATFORMS:
1029+
platformArg = f"target={context.buildTarget.lower()}"
1030+
makeTBBCmd = 'make -j{procs} arch={arch} {platformArg} {buildArgs}'.format(
10081031
arch=arch, procs=context.numJobs,
1032+
platformArg=platformArg,
10091033
buildArgs=" ".join(buildArgs))
1010-
Run(makeTBBCmd)
1034+
Run(makeTBBCmd, env=env)
10111035

10121036
_RunBuild(primaryArch)
10131037
_RunBuild(secondaryArch)
@@ -1483,6 +1507,13 @@ def InstallMaterialX(context, force, buildArgs):
14831507
cmakeOptions = ['-DMATERIALX_BUILD_SHARED_LIBS=ON',
14841508
'-DMATERIALX_BUILD_TESTS=OFF'
14851509
]
1510+
1511+
if MacOS() and context.buildTarget in apple_utils.EMBEDDED_PLATFORMS:
1512+
cmakeOptions.extend([
1513+
'-DMATERIALX_BUILD_GEN_MSL=ON',
1514+
'-DMATERIALX_BUILD_GEN_GLSL=OFF',
1515+
'-DMATERIALX_BUILD_IOS=ON'])
1516+
14861517
cmakeOptions += buildArgs
14871518
RunCMake(context, force, cmakeOptions)
14881519

@@ -2190,6 +2221,16 @@ def __init__(self, args):
21902221
# - USD Imaging
21912222
self.buildUsdImaging = (args.build_imaging == USD_IMAGING)
21922223

2224+
# Disable Features for Apple Embedded platforms
2225+
if MacOS() and self.buildTarget in apple_utils.EMBEDDED_PLATFORMS:
2226+
self.buildImaging = False
2227+
self.buildUsdImaging = False
2228+
self.buildTools = False
2229+
self.buildPython = False
2230+
self.enableOpenVDB = False
2231+
self.enablePtex = False
2232+
2233+
21932234
# - usdview
21942235
self.buildUsdview = (self.buildUsdImaging and
21952236
self.buildPython and
@@ -2610,19 +2651,23 @@ def FormatBuildArguments(buildArgs):
26102651
if context.macOSCodesign:
26112652
apple_utils.Codesign(context.usdInstDir, verbosity > 1)
26122653

2613-
Print("""
2614-
Success! To use USD, please ensure that you have:""")
2654+
printInstructions = any([context.buildPython, context.buildTools, context.buildPrman])
2655+
if printInstructions:
2656+
Print("\nSuccess! To use USD, please ensure that you have:")
2657+
else:
2658+
Print("\nSuccess! USD libraries were built.")
26152659

26162660
if context.buildPython:
26172661
Print("""
26182662
The following in your PYTHONPATH environment variable:
26192663
{requiredInPythonPath}""".format(
26202664
requiredInPythonPath="\n ".join(sorted(requiredInPythonPath))))
26212665

2622-
Print("""
2623-
The following in your PATH environment variable:
2624-
{requiredInPath}
2625-
""".format(requiredInPath="\n ".join(sorted(requiredInPath))))
2666+
if context.buildTools:
2667+
Print("""
2668+
The following in your PATH environment variable:
2669+
{requiredInPath}
2670+
""".format(requiredInPath="\n ".join(sorted(requiredInPath))))
26262671

26272672
if context.buildPrman:
26282673
Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html "

0 commit comments

Comments
 (0)