Skip to content

Commit 8d23ab3

Browse files
committed
Add Framework build support
1 parent 1e08671 commit 8d23ab3

10 files changed

Lines changed: 327 additions & 27 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ Or for visionOS:
173173
> python OpenUSD/build_scripts/build_usd.py --build-target visionOS --build-monolithic /path/to/my_usd_install_dir
174174
```
175175

176+
Additionally you can provide the `--build-apple-framework` flag to create a framework output, for easier integration
177+
into an Xcode application. Framework support is currently experimental. It defaults to On for iOS and visionOS.
178+
Framework builds will enable monolithic builds by default.
179+
180+
176181
##### Windows:
177182

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

build_scripts/apple_utils.py

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import sys
1616
import locale
1717
import os
18+
import re
1819
import platform
1920
import shlex
2021
import subprocess
@@ -163,20 +164,56 @@ def ExtractFilesRecursive(path, cond):
163164
files.append(os.path.join(r, file))
164165
return files
165166

166-
def CodesignFiles(files):
167-
SDKVersion = subprocess.check_output(
168-
['xcodebuild', '-version']).strip()[6:10]
169-
codeSignIDs = subprocess.check_output(
170-
['security', 'find-identity', '-vp', 'codesigning'])
167+
def _GetCodeSignStringFromTerminal():
168+
codeSignIDs = GetCommandOutput(['security', 'find-identity', '-vp', 'codesigning'])
169+
return codeSignIDs
171170

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

181218
for f in files:
182219
subprocess.call(['codesign', '-f', '-s', '{codesignid}'
@@ -245,4 +282,10 @@ def ConfigureCMakeExtraArgs(context, args:List[str]) -> List[str]:
245282
args.append(f"-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH")
246283
args.append(f"-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH")
247284

285+
286+
# Signing needs to happen on all systems
287+
if context.macOSCodesign:
288+
args.append(f"-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY={GetCodeSignID()}")
289+
args.append(f"-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM={GetDevelopmentTeamID()}")
290+
248291
return args

build_scripts/build_usd.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ def InstallBoost(context, force, buildArgs):
955955

956956
def InstallOneTBB(context, force, buildArgs):
957957
with CurrentWorkingDirectory(DownloadURL(ONETBB_URL, context, force)):
958-
RunCMake(context, force,
958+
RunCMake(context, force,
959959
['-DTBB_TEST=OFF',
960960
'-DTBB_STRICT=OFF'] + buildArgs)
961961

@@ -1326,12 +1326,12 @@ def InstallOpenImageIO(context, force, buildArgs):
13261326
'-DUSE_PYTHON=OFF',
13271327
'-DSTOP_ON_WARNING=OFF']
13281328

1329-
# OIIO's FindOpenEXR module circumvents CMake's normal library
1329+
# OIIO's FindOpenEXR module circumvents CMake's normal library
13301330
# search order, which causes versions of OpenEXR installed in
13311331
# /usr/local or other hard-coded locations in the module to
1332-
# take precedence over the version we've built, which would
1333-
# normally be picked up when we specify CMAKE_PREFIX_PATH.
1334-
# This may lead to undefined symbol errors at build or runtime.
1332+
# take precedence over the version we've built, which would
1333+
# normally be picked up when we specify CMAKE_PREFIX_PATH.
1334+
# This may lead to undefined symbol errors at build or runtime.
13351335
# So, we explicitly specify the OpenEXR we want to use here.
13361336
extraArgs.append('-DOPENEXR_ROOT="{instDir}"'
13371337
.format(instDir=context.instDir))
@@ -1790,6 +1790,10 @@ def InstallUSD(context, force, buildArgs):
17901790
if Windows():
17911791
# Increase the precompiled header buffer limit.
17921792
extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"')
1793+
if MacOS():
1794+
extraArgs.append(f"-DPXR_BUILD_APPLE_FRAMEWORK={'ON' if context.buildAppleFramework else 'OFF'}")
1795+
if context.macOSCodesign:
1796+
extraArgs.append(f"-DPXR_APPLE_CODESIGN_IDENTITY={context.macOSCodesign}")
17931797

17941798
# Make sure to use boost installed by the build script and not any
17951799
# system installed boost
@@ -1910,6 +1914,12 @@ def InstallUSD(context, force, buildArgs):
19101914
help=("Build target for macOS cross compilation. "
19111915
"(default: {})".format(
19121916
apple_utils.GetBuildTargetDefault())))
1917+
subgroup = group.add_mutually_exclusive_group()
1918+
subgroup.add_argument("--build-apple-framework", dest="build_apple_framework", action="store_true",
1919+
help="Build USD as an Apple Framework (Default if using build)")
1920+
subgroup.add_argument("--no-build-apple-framework", dest="no_build_apple_framework", action="store_true",
1921+
help="Do not build USD as an Apple Framework (Default if macOS)")
1922+
19131923
if apple_utils.IsHostArm():
19141924
# Intel Homebrew stores packages in /usr/local which unfortunately can
19151925
# be where a lot of other things are too. So we only add this flag on arm macs.
@@ -1945,6 +1955,7 @@ def InstallUSD(context, force, buildArgs):
19451955
default=codesignDefault, action="store_true",
19461956
help=("Enable code signing for macOS builds "
19471957
"(defaults to enabled on Apple Silicon)"))
1958+
group.add_argument("--codesign-id", dest="macos_codesign_id", type=str)
19481959

19491960
if Linux():
19501961
group.add_argument("--use-cxx11-abi", type=int, choices=[0, 1],
@@ -2234,11 +2245,18 @@ def __init__(self, args):
22342245
self.buildTarget = args.build_target
22352246
apple_utils.SetTarget(self, self.buildTarget)
22362247

2237-
self.macOSCodesign = \
2238-
(args.macos_codesign if hasattr(args, "macos_codesign")
2239-
else False)
2248+
self.macOSCodesign = False
2249+
if args.macos_codesign:
2250+
self.macOSCodesign = args.macos_codesign_id or apple_utils.GetCodeSignID()
22402251
if apple_utils.IsHostArm() and args.ignore_homebrew:
22412252
self.ignorePaths.append("/opt/homebrew")
2253+
2254+
self.buildAppleFramework = ((args.build_apple_framework
2255+
or self.buildTarget in apple_utils.EMBEDDED_PLATFORMS)
2256+
and not args.no_build_apple_framework)
2257+
if self.buildAppleFramework:
2258+
self.buildShared = False
2259+
self.buildMonolithic = True
22422260
else:
22432261
self.buildTarget = ""
22442262

@@ -2585,6 +2603,7 @@ def _JoinVersion(v):
25852603
summaryMsg += """\
25862604
Variant {buildVariant}
25872605
Target {buildTarget}
2606+
Framework Build {buildAppleFramework}
25882607
Imaging {buildImaging}
25892608
Ptex support: {enablePtex}
25902609
OpenVDB support: {enableOpenVDB}
@@ -2669,7 +2688,8 @@ def FormatBuildArguments(buildArgs):
26692688
buildMaterialX=("On" if context.buildMaterialX else "Off"),
26702689
buildMayapyTests=("On" if context.buildMayapyTests else "Off"),
26712690
buildAnimXTests=("On" if context.buildAnimXTests else "Off"),
2672-
enableHDF5=("On" if context.enableHDF5 else "Off"))
2691+
enableHDF5=("On" if context.enableHDF5 else "Off"),
2692+
buildAppleFramework=("On" if MacOS() and context.buildAppleFramework else "Off"))
26732693

26742694
Print(summaryMsg)
26752695

@@ -2731,8 +2751,9 @@ def FormatBuildArguments(buildArgs):
27312751
])
27322752

27332753
if MacOS():
2734-
if context.macOSCodesign:
2735-
apple_utils.Codesign(context.usdInstDir, verbosity > 1)
2754+
# We don't need to codesign when building a framework because it's handled during framework creation
2755+
if context.macOSCodesign and not context.buildAppleFramework:
2756+
apple_utils.Codesign(context, verbosity > 1)
27362757

27372758
additionalInstructions = any([context.buildPython, context.buildTools, context.buildPrman])
27382759
if additionalInstructions:
@@ -2755,3 +2776,12 @@ def FormatBuildArguments(buildArgs):
27552776
if context.buildPrman:
27562777
Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html "
27572778
"for setting up the RenderMan plugin.\n")
2779+
2780+
if context.buildAppleFramework:
2781+
Print("""
2782+
Added the following framework to your Xcode Project, (recommended as Embed Without Signing):
2783+
OpenUSD.framework
2784+
2785+
Set the following compiler argument, to find the headers:
2786+
SYSTEM_HEADER_SEARCH_PATHS=$(SRCROOT)/$(TARGET_NAME)/OpenUSD.framework/Headers
2787+
""")

cmake/defaults/CXXDefaults.cmake

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,19 @@ if (PXR_PREFER_SAFETY_OVER_SPEED)
8989
else()
9090
set(PXR_PREFER_SAFETY_OVER_SPEED "0")
9191
endif()
92+
93+
# Setup CCache for C/C++ compilation
94+
if(PXR_ENABLE_COMPILER_CACHE)
95+
find_program(COMPILER_CACHE_PROGRAM ${PXR_COMPILER_CACHE_NAME})
96+
if(COMPILER_CACHE_PROGRAM)
97+
set(CMAKE_C_COMPILER_LAUNCHER "${COMPILER_CACHE_PROGRAM}")
98+
set(CMAKE_CXX_COMPILER_LAUNCHER "${COMPILER_CACHE_PROGRAM}")
99+
else ()
100+
MESSAGE(STATUS "Compiler Caching disabled. Could not find ${PXR_COMPILER_CACHE_NAME}.")
101+
endif()
102+
endif()
103+
104+
# Set that Apple Framework is being build
105+
if (PXR_BUILD_APPLE_FRAMEWORK)
106+
_add_define("PXR_BUILD_APPLE_FRAMEWORK")
107+
endif()

cmake/defaults/Options.cmake

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ option(PXR_PREFER_SAFETY_OVER_SPEED
3838
ON)
3939

4040
if(APPLE)
41+
set(PXR_APPLE_CODESIGN_IDENTITY "-" CACHE STRING "The Codesigning identity needed to sign compiled objects")
4142
# Cross Compilation detection as defined in CMake docs
4243
# Required to be handled here so it can configure options later on
4344
# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos
@@ -54,6 +55,16 @@ if(APPLE)
5455
endif()
5556
set(PXR_ENABLE_GL_SUPPORT OFF)
5657
endif ()
58+
59+
option(PXR_BUILD_APPLE_FRAMEWORK "Builds an Apple Framework." APPLE_EMBEDDED)
60+
set(PXR_APPLE_FRAMEWORK_NAME "OpenUSD" CACHE STRING "Name to provide Apple Framework build")
61+
set(PXR_APPLE_IDENTIFIER_DOMAIN "org.openusd" CACHE STRING "Name to provide Apple Framework build")
62+
if (${PXR_BUILD_APPLE_FRAMEWORK})
63+
if(${PXR_BUILD_USD_TOOLS})
64+
MESSAGE(STATUS "Setting PXR_BUILD_USD_TOOLS=OFF because PXR_BUILD_APPLE_FRAMEWORK is enabled.")
65+
set(PXR_BUILD_USD_TOOLS OFF)
66+
endif()
67+
endif()
5768
endif()
5869

5970

@@ -133,6 +144,12 @@ set(PXR_LIB_PREFIX ""
133144

134145
option(BUILD_SHARED_LIBS "Build shared libraries." ON)
135146
option(PXR_BUILD_MONOLITHIC "Build a monolithic library." OFF)
147+
if (${PXR_BUILD_APPLE_FRAMEWORK})
148+
set(BUILD_SHARED_LIBS OFF)
149+
set(PXR_BUILD_MONOLITHIC ON)
150+
MESSAGE(STATUS "Setting PXR_BUILD_MONOLITHIC=ON for Framework build")
151+
endif ()
152+
136153
set(PXR_MONOLITHIC_IMPORT ""
137154
CACHE
138155
STRING
@@ -228,3 +245,11 @@ if (${PXR_BUILD_PYTHON_DOCUMENTATION})
228245
set(PXR_BUILD_PYTHON_DOCUMENTATION "OFF" CACHE BOOL "" FORCE)
229246
endif()
230247
endif()
248+
249+
# Configure the use of compiler caches for faster compilation
250+
option(PXR_ENABLE_COMPILER_CACHE "Enable the use of a compiler cache" OFF)
251+
set(PXR_COMPILER_CACHE_NAME "ccache"
252+
CACHE
253+
STRING
254+
"The name of the compiler cache program to use"
255+
)

cmake/macros/Public.cmake

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,12 @@ function(pxr_toplevel_epilogue)
11041104
# Setup the plugins in the top epilogue to ensure that everybody has had a
11051105
# chance to update PXR_EXTRA_PLUGINS with their plugin paths.
11061106
pxr_setup_plugins()
1107+
1108+
# Build
1109+
if (PXR_BUILD_APPLE_FRAMEWORK)
1110+
pxr_create_apple_framework()
1111+
endif ()
1112+
11071113
endfunction() # pxr_toplevel_epilogue
11081114

11091115
function(pxr_monolithic_epilogue)
@@ -1294,10 +1300,10 @@ function(pxr_build_python_documentation)
12941300
endfunction() # pxr_build_python_documentation
12951301

12961302
# Adding support for a "docs-only" directory, needed when adding doxygen docs
1297-
# not associated with a specific library/etc.
1303+
# not associated with a specific library/etc.
12981304
function(pxr_docs_only_dir NAME)
1299-
# Get list of doxygen files, which could include image files and/or
1300-
# snippets example cpp files
1305+
# Get list of doxygen files, which could include image files and/or
1306+
# snippets example cpp files
13011307
set(multiValueArgs
13021308
DOXYGEN_FILES
13031309
)
@@ -1317,4 +1323,27 @@ function(pxr_docs_only_dir NAME)
13171323
${args_DOXYGEN_FILES}
13181324
)
13191325
endif()
1320-
endfunction() # pxr_docs_only_dir
1326+
endfunction() # pxr_docs_only_dir
1327+
1328+
function(pxr_create_apple_framework)
1329+
# CMake can have a lot of different boolean representations, that need to be narrowed down
1330+
if (APPLE_EMBEDDED)
1331+
set(EMBEDDED_BUILD "true")
1332+
else()
1333+
set(EMBEDDED_BUILD "false")
1334+
endif()
1335+
1336+
_get_library_prefix(LIB_PREFIX)
1337+
if(TARGET usd_ms)
1338+
set(FRAMEWORK_ROOT_LIBRARY_NAME "${LIB_PREFIX}usd_ms.dylib")
1339+
else()
1340+
set(FRAMEWORK_ROOT_LIBRARY_NAME "${LIB_PREFIX}usd.dylib")
1341+
endif()
1342+
1343+
# Install the Info.plist and shell script
1344+
configure_file(cmake/resources/Info.plist.in "${PROJECT_BINARY_DIR}/Info.plist" @ONLY)
1345+
configure_file(cmake/resources/AppleFrameworkBuild.zsh.in "${PROJECT_BINARY_DIR}/AppleFrameworkBuild.zsh" @ONLY)
1346+
1347+
# Run the shell script for the primary configuration
1348+
install(CODE "execute_process(COMMAND zsh ${PROJECT_BINARY_DIR}/AppleFrameworkBuild.zsh )")
1349+
endfunction() # pxr_create_apple_framework

0 commit comments

Comments
 (0)