diff --git a/README.md b/README.md index 1177484c1bb..98803658175 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,23 @@ Or for visionOS: ``` > python OpenUSD/build_scripts/build_usd.py --build-target visionOS --build-monolithic /path/to/my_usd_install_dir ``` +###### Framework Builds (Experimental) + +Builds for Apple platforms may optionally build as a framework using the `--build-apple-framework` flag. + +**NOTE:** This feature is experimental and may change how it functions in future OpenUSD versions. + +- Framework builds are enabled by default for iOS and visionOS build targets. It can optionally be enabled for macOS. +- Framework builds require monolithic builds. +- Building a universal macOS framework is currently not supported. Please generate the arches separately and `lipo` + them together after. + +To add the Framework to your application, add `OpenUSD.framework` to your Xcode project. +It is recommended to set it to `Embed and Sign`. + +To setup headers, configure the Xcode `SYSTEM_HEADER_SEARCH_PATHS` to add the path to your headers. e.g +`$(SRCROOT)/OpenUSD.framework/Headers` if the framework exists in your projects root. + ##### Windows: diff --git a/build_scripts/build_usd.py b/build_scripts/build_usd.py index 8b1c67c2355..b240e26aad0 100644 --- a/build_scripts/build_usd.py +++ b/build_scripts/build_usd.py @@ -1077,6 +1077,9 @@ def _RunBuild(arch): if MacOSTargetEmbedded(context): env["SDKROOT"] = apple_utils.GetSDKRoot(context) buildArgs.append(f' compiler=clang arch=arm64 extra_inc=big_iron.inc target={context.buildTarget.lower()}') + if context.buildAppleFramework: + # Force build of static libs as well + buildArgs.append(f" extra_inc=big_iron.inc ") makeTBBCmd = 'make -j{procs} arch={arch} {buildArgs}'.format( arch=arch, procs=context.numJobs, buildArgs=" ".join(buildArgs)) @@ -1547,9 +1550,7 @@ def InstallDraco(context, force, buildArgs): def InstallMaterialX(context, force, buildArgs): with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)): - cmakeOptions = ['-DMATERIALX_BUILD_SHARED_LIBS=ON', - '-DMATERIALX_BUILD_TESTS=OFF' - ] + cmakeOptions = ['-DMATERIALX_BUILD_TESTS=OFF'] if MacOSTargetEmbedded(context): # The materialXShaderGen in hdSt assumes the GLSL shadergen is @@ -1569,6 +1570,10 @@ def InstallMaterialX(context, force, buildArgs): ' add_subdirectory(source/MaterialXRenderGlsl)\n' + ' endif()') ], multiLineMatches=True) + if MacOS() and context.buildAppleFramework: + cmakeOptions.extend(["-DMATERIALX_BUILD_SHARED_LIBS=OFF"]) + else: + cmakeOptions.extend(["-DMATERIALX_BUILD_SHARED_LIBS=ON"]) cmakeOptions += buildArgs RunCMake(context, force, cmakeOptions) @@ -1840,6 +1845,8 @@ def InstallUSD(context, force, buildArgs): if Windows(): # Increase the precompiled header buffer limit. extraArgs.append('-DCMAKE_CXX_FLAGS="/Zm150"') + if MacOS(): + extraArgs.append(f"-DPXR_BUILD_APPLE_FRAMEWORK={'ON' if context.buildAppleFramework else 'OFF'}") # Make sure to use boost installed by the build script and not any # system installed boost @@ -1967,6 +1974,12 @@ def InstallUSD(context, force, buildArgs): help=("Build target for macOS cross compilation. " "(default: {})".format( apple_utils.GetBuildTargetDefault()))) + subgroup = group.add_mutually_exclusive_group() + subgroup.add_argument("--build-apple-framework", dest="build_apple_framework", action="store_true", + help="Build USD as an Apple Framework (Default if using embedded platforms)") + subgroup.add_argument("--no-build-apple-framework", dest="no_build_apple_framework", action="store_true", + help="Do not build USD as an Apple Framework (Default if macOS)") + if apple_utils.IsHostArm(): # Intel Homebrew stores packages in /usr/local which unfortunately can # be where a lot of other things are too. So we only add this flag on arm macs. @@ -2315,6 +2328,12 @@ def __init__(self, args): else False) if apple_utils.IsHostArm() and args.ignore_homebrew: self.ignorePaths.append("/opt/homebrew") + + self.buildAppleFramework = ((args.build_apple_framework or MacOSTargetEmbedded(self)) + and not args.no_build_apple_framework) + if self.buildAppleFramework: + self.buildShared = False + self.buildMonolithic = True else: self.buildTarget = "" @@ -2327,7 +2346,7 @@ def __init__(self, args): self.forceBuild = [dep.lower() for dep in args.force_build] # Some components are disabled for embedded build targets - embedded = MacOSTargetEmbedded(self) + embedded = (MacOS() and (MacOSTargetEmbedded(self) or self.buildAppleFramework)) # Optional components self.buildTests = args.build_tests and not embedded @@ -2669,6 +2688,11 @@ def _JoinVersion(v): Use C++11 ABI {useCXX11ABI} """ +if MacOS(): + summaryMsg += """\ + Framework Build {buildAppleFramework} +""".format(buildAppleFramework=("On" if context.buildAppleFramework else "Off")) + summaryMsg += """\ Variant {buildVariant} Target {buildTarget} @@ -2866,3 +2890,9 @@ def FormatBuildArguments(buildArgs): if context.buildPrman: Print("See documentation at http://openusd.org/docs/RenderMan-USD-Imaging-Plugin.html " "for setting up the RenderMan plugin.\n") + +if MacOS() and context.buildAppleFramework: + Print(""" + Add the following framework to your Xcode Project, and add the headers folder within to your search path: + OpenUSD.framework + """) \ No newline at end of file diff --git a/cmake/defaults/CXXDefaults.cmake b/cmake/defaults/CXXDefaults.cmake index 5259eb9815a..c13fa39bbd2 100644 --- a/cmake/defaults/CXXDefaults.cmake +++ b/cmake/defaults/CXXDefaults.cmake @@ -86,3 +86,12 @@ if (PXR_PREFER_SAFETY_OVER_SPEED) else() set(PXR_PREFER_SAFETY_OVER_SPEED "0") endif() + +# Set that Apple Framework is being build +if (PXR_BUILD_APPLE_FRAMEWORK) + _add_define("PXR_BUILD_APPLE_FRAMEWORK") +endif() + +if(NOT PXR_EXTRA_BINARY_RELATIVE_RESOURCES STREQUAL "") + _add_define("PXR_EXTRA_BINARY_RELATIVE_RESOURCES=${PXR_EXTRA_BINARY_RELATIVE_RESOURCES}") +endif() \ No newline at end of file diff --git a/cmake/defaults/Options.cmake b/cmake/defaults/Options.cmake index 2b4edec8d8b..0fea80abb06 100644 --- a/cmake/defaults/Options.cmake +++ b/cmake/defaults/Options.cmake @@ -70,6 +70,20 @@ if(APPLE) set(PXR_ENABLE_OPENVDB_SUPPORT OFF) endif() endif () + + option(PXR_BUILD_APPLE_FRAMEWORK "Builds an Apple Framework." ${PXR_APPLE_EMBEDDED}) + set(PXR_APPLE_FRAMEWORK_NAME "OpenUSD" CACHE STRING "Name to provide Apple Framework build") + set(PXR_APPLE_IDENTIFIER_DOMAIN "org.openusd" CACHE STRING "Name to provide Apple Framework build") + if (${PXR_BUILD_APPLE_FRAMEWORK}) + if(${PXR_BUILD_USD_TOOLS}) + MESSAGE(STATUS "Setting PXR_BUILD_USD_TOOLS=OFF because PXR_BUILD_APPLE_FRAMEWORK is enabled.") + endif() + set(PXR_BUILD_USD_TOOLS OFF) + if(${PXR_ENABLE_PYTHON_SUPPORT}) + MESSAGE(STATUS "Setting PXR_ENABLE_PYTHON_SUPPORT=OFF because PXR_BUILD_APPLE_FRAMEWORK is enabled.") + endif () + set(PXR_ENABLE_PYTHON_SUPPORT OFF) + endif() endif() @@ -159,7 +173,21 @@ set(PXR_EXTRA_PLUGINS "" CACHE INTERNAL "Aggregation of extra plugin directories containing a plugInfo.json.") +set(PXR_EXTRA_BINARY_RELATIVE_RESOURCES "" + CACHE + STRING + "An optional resources folder relative to the binary to look for resources.") +if (${PXR_BUILD_APPLE_FRAMEWORK}) + MESSAGE(STATUS "Framework build requires monolithic builds.") + set(PXR_BUILD_MONOLITHIC ON) + set(BUILD_SHARED_LIBS OFF) + if(PXR_APPLE_EMBEDDED) + set(PXR_EXTRA_BINARY_RELATIVE_RESOURCES "Assets") + else() + set(PXR_EXTRA_BINARY_RELATIVE_RESOURCES "Resources") + endif() +endif () # Resolve options that depend on one another so that subsequent .cmake scripts # all have the final value for these options. if (${PXR_BUILD_USD_IMAGING} AND NOT ${PXR_BUILD_IMAGING}) diff --git a/cmake/macros/Private.cmake b/cmake/macros/Private.cmake index 6fe1134c14e..a6d7b7abaf5 100644 --- a/cmake/macros/Private.cmake +++ b/cmake/macros/Private.cmake @@ -691,6 +691,11 @@ function(_pxr_add_rpath rpathRef target) endfunction() function(_pxr_install_rpath rpathRef NAME) + if (PXR_BUILD_APPLE_FRAMEWORK) + # Apple Frameworks already fix the install path at the end + # so this maks things faster and reduces duplication errors + return() + endif() # Get and remove the origin. list(GET ${rpathRef} 0 origin) set(rpath ${${rpathRef}}) @@ -1538,3 +1543,33 @@ function(_pxr_library NAME) endif() endif() endfunction() # _pxr_library + +function(pxr_create_apple_framework) + # This function is used to create a framework build after the primary build is complete + # We could optionally have used the FRAMEWORK CMAKE Property (https://cmake.org/cmake/help/latest/prop_tgt/FRAMEWORK.html) + # However, the changes to make it work with the OpenUSD install process would have been significantly more + # invasive to orchestrate all the post-install steps that USD does. + # In the future, we can work towards making this + + # CMake can have a lot of different boolean representations, + # that need to be narrowed down to a constant form for zsh + if (PXR_APPLE_EMBEDDED) + set(EMBEDDED_BUILD "true") + else() + set(EMBEDDED_BUILD "false") + endif() + + _get_library_prefix(LIB_PREFIX) + if(TARGET usd_ms) + set(FRAMEWORK_ROOT_LIBRARY_NAME "${LIB_PREFIX}usd_ms.dylib") + else() + set(FRAMEWORK_ROOT_LIBRARY_NAME "${LIB_PREFIX}usd.dylib") + endif() + + # Install the Info.plist and shell script + configure_file(cmake/resources/Info.plist.in "${PROJECT_BINARY_DIR}/Info.plist" @ONLY) + configure_file(cmake/resources/AppleFrameworkBuild.zsh.in "${PROJECT_BINARY_DIR}/AppleFrameworkBuild.zsh" @ONLY) + + # Run the shell script for the primary configuration + install(CODE "execute_process(COMMAND zsh ${PROJECT_BINARY_DIR}/AppleFrameworkBuild.zsh )") +endfunction() # pxr_create_apple_framework \ No newline at end of file diff --git a/cmake/macros/Public.cmake b/cmake/macros/Public.cmake index 807cf7d83f1..f4740a050c9 100644 --- a/cmake/macros/Public.cmake +++ b/cmake/macros/Public.cmake @@ -1234,6 +1234,11 @@ function(pxr_toplevel_epilogue) # Setup the plugins in the top epilogue to ensure that everybody has had a # chance to update PXR_EXTRA_PLUGINS with their plugin paths. pxr_setup_plugins() + + if (PXR_BUILD_APPLE_FRAMEWORK) + pxr_create_apple_framework() + endif () + endfunction() # pxr_toplevel_epilogue function(pxr_monolithic_epilogue) diff --git a/cmake/modules/FindOpenSubdiv.cmake b/cmake/modules/FindOpenSubdiv.cmake index 77bdb27ca5c..a8fd3ecd989 100644 --- a/cmake/modules/FindOpenSubdiv.cmake +++ b/cmake/modules/FindOpenSubdiv.cmake @@ -78,9 +78,13 @@ endif() SET(_opensubdiv_LIBRARIES) FOREACH(COMPONENT ${_opensubdiv_FIND_COMPONENTS}) STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) + set(_lib_name ${COMPONENT}) + if (APPLE AND PXR_BUILD_APPLE_FRAMEWORK) + set(_lib_name lib${COMPONENT}.a) + endif () FIND_LIBRARY(OPENSUBDIV_${UPPERCOMPONENT}_LIBRARY NAMES - ${COMPONENT} + ${_lib_name} HINTS ${_opensubdiv_SEARCH_DIRS} PATH_SUFFIXES diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake index a0726bb80b0..d46aef32246 100644 --- a/cmake/modules/FindTBB.cmake +++ b/cmake/modules/FindTBB.cmake @@ -246,13 +246,22 @@ if(NOT TBB_FOUND) set(_lib_name ${_comp}) endif() + set(_lib_names_release ${_lib_name}) + set(_lib_names_debug ${_lib_name}_debug) + + if (APPLE AND PXR_BUILD_APPLE_FRAMEWORK) + # When building a monolithic framework, it's best to take the static lib + set(_lib_names_release lib${_lib_name}.a) + set(_lib_names_debug lib${_lib_name}_debug.a) + endif () + # Search for the libraries - find_library(TBB_${_comp}_LIBRARY_RELEASE ${_lib_name} + find_library(TBB_${_comp}_LIBRARY_RELEASE NAMES ${_lib_names_release} HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - find_library(TBB_${_comp}_LIBRARY_DEBUG ${_lib_name}_debug + find_library(TBB_${_comp}_LIBRARY_DEBUG NAMES ${_lib_names_debug} HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) diff --git a/cmake/resources/AppleFrameworkBuild.zsh.in b/cmake/resources/AppleFrameworkBuild.zsh.in new file mode 100644 index 00000000000..d484f4f4c21 --- /dev/null +++ b/cmake/resources/AppleFrameworkBuild.zsh.in @@ -0,0 +1,87 @@ +#!/bin/zsh + +# Creates an Apple framework for the given platform type +# documentation: https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle +# https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html +# We use this instead of setting the FRAMEWORK property on the monolithic target to reduce how invasive a change this might be. +# See pxr_create_apple_framework in Public.cmake for more details +echo "⌛️ Creating @PXR_APPLE_FRAMEWORK_NAME@ ..." + +# Variables are substituted by CMake +CMAKE_INSTALL_PREFIX="@CMAKE_INSTALL_PREFIX@" +PROJECT_BINARY_DIR="@PROJECT_BINARY_DIR@" + +FRAMEWORK_NAME="@PXR_APPLE_FRAMEWORK_NAME@" +FRAMEWORK_DIR="${CMAKE_INSTALL_PREFIX}/frameworks/${FRAMEWORK_NAME}.framework" +FRAMEWORK_LIBRARIES_DIR="${FRAMEWORK_DIR}/Libraries" +FRAMEWORK_ROOT_LIBRARY_NAME="@FRAMEWORK_ROOT_LIBRARY_NAME@" +EMBEDDED_BUILD=@EMBEDDED_BUILD@ +APPLY_HEADER_PREFIX=@APPLY_HEADER_PREFIX@ +FRAMEWORK_RESOURCES_DIR="${FRAMEWORK_DIR}" +MATERIALX_SOURCE_LIBRARIES="${CMAKE_INSTALL_PREFIX}/libraries/" +BUNDLE_IDENTIFIER="@PXR_APPLE_IDENTIFIER_DOMAIN@.@PXR_APPLE_FRAMEWORK_NAME@" +CODESIGN_ID="@CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY@" +OLD_RC_PATH="${CMAKE_INSTALL_PREFIX}/lib" + + +# Remove the existing directory if it exists +if [ -d ${FRAMEWORK_DIR} ]; then + echo "Removing existing framework"; + rm -Rf ${FRAMEWORK_DIR}; +fi + +# Create the parent directory +echo "Creating Framework Directory: ${FRAMEWORK_DIR}" +mkdir -p ${FRAMEWORK_DIR} + +if [ "$EMBEDDED_BUILD" = true ];then + FRAMEWORK_RESOURCES_DIR="${FRAMEWORK_DIR}/Assets" + FRAMEWORK_PLIST_LOCATION="${FRAMEWORK_DIR}/Info.plist" + FRAMEWORK_HEADERS_DIR="${FRAMEWORK_DIR}/Headers" + FRAMEWORK_LIB_PATH=""${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"" + FRAMEWORK_LINKER_PATH="@rpath/Frameworks/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" +else + FRAMEWORK_RESOURCES_DIR="${FRAMEWORK_DIR}/Versions/A/Resources/" + FRAMEWORK_PLIST_LOCATION="${FRAMEWORK_DIR}/Versions/A/Resources/Info.plist" + FRAMEWORK_HEADERS_DIR="${FRAMEWORK_DIR}/Versions/A/Headers" + FRAMEWORK_LIB_PATH="${FRAMEWORK_DIR}/Versions/A/${FRAMEWORK_NAME}" + FRAMEWORK_LINKER_PATH="@rpath/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" +fi + +echo "Creating Resources Root: ${FRAMEWORK_RESOURCES_DIR}" +mkdir -p ${FRAMEWORK_RESOURCES_DIR} + +echo "Creating Headers Root: ${FRAMEWORK_HEADERS_DIR}" +mkdir -p ${FRAMEWORK_HEADERS_DIR} + +# Copy the plist over +echo "Copying files into ${FRAMEWORK_DIR}" +ditto "${PROJECT_BINARY_DIR}/Info.plist" "${FRAMEWORK_PLIST_LOCATION}" + + +# Copy the MaterialX libraries if they exist +if [ -d "${MATERIALX_SOURCE_LIBRARIES}" ]; then + ditto ${MATERIALX_SOURCE_LIBRARIES} "${FRAMEWORK_RESOURCES_DIR}/libraries/" +fi + + +# Copy the primary directories over +ditto "${CMAKE_INSTALL_PREFIX}/include/" ${FRAMEWORK_HEADERS_DIR} +ditto "${CMAKE_INSTALL_PREFIX}/plugin/usd/" "${FRAMEWORK_RESOURCES_DIR}/usd" +ditto "${CMAKE_INSTALL_PREFIX}/lib/usd/" "${FRAMEWORK_RESOURCES_DIR}/usd" + +cp "${CMAKE_INSTALL_PREFIX}/lib/${FRAMEWORK_ROOT_LIBRARY_NAME}" "${FRAMEWORK_LIB_PATH}" + +# Setup symlinks +if [ "$EMBEDDED_BUILD" = false ];then + (cd "${FRAMEWORK_DIR}/Versions" && ln -s "A" "Current") + (cd ${FRAMEWORK_DIR} && ln -s "Versions/Current/Resources" "Resources") + (cd ${FRAMEWORK_DIR} && ln -s "Versions/Current/Headers" "Headers") + (cd ${FRAMEWORK_DIR} && ln -s "Versions/Current/${FRAMEWORK_NAME}" ${FRAMEWORK_NAME}) +fi + +# Fix the linkage on the primary dylib +install_name_tool -id "${FRAMEWORK_LINKER_PATH}" "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}" +install_name_tool -change "@rpath/libusd_ms.dylib" "${FRAMEWORK_LINKER_PATH}" "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}" + +echo "✅ Finished creating framework at ${FRAMEWORK_DIR}" \ No newline at end of file diff --git a/cmake/resources/Info.plist.in b/cmake/resources/Info.plist.in new file mode 100644 index 00000000000..90f3976502b --- /dev/null +++ b/cmake/resources/Info.plist.in @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + @PXR_APPLE_FRAMEWORK_NAME@ + CFBundleIdentifier + @PXR_APPLE_IDENTIFIER_DOMAIN@.@PXR_APPLE_FRAMEWORK_NAME@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @PXR_APPLE_FRAMEWORK_NAME@ + CFBundlePackageType + FMWK + CFBundleShortVersionString + @PXR_MAJOR_VERSION@.@PXR_MINOR_VERSION@.@PXR_PATCH_VERSION@ + CFBundleVersion + @PXR_VERSION@ + CSResourcesFileMapped + + + \ No newline at end of file diff --git a/pxr/base/plug/initConfig.cpp b/pxr/base/plug/initConfig.cpp index 12ca61a963e..46c61b08a32 100644 --- a/pxr/base/plug/initConfig.cpp +++ b/pxr/base/plug/initConfig.cpp @@ -93,6 +93,11 @@ ARCH_CONSTRUCTOR(Plug_InitConfig, 2) _AppendPathList(&result, buildLocation, binaryPath); _AppendPathList(&result, pluginBuildLocation, binaryPath); +#ifdef PXR_EXTRA_BINARY_RELATIVE_RESOURCES + std::string relativeResourcePath = std::string(TF_PP_STRINGIZE(PXR_EXTRA_BINARY_RELATIVE_RESOURCES)) + "/usd"; + _AppendPathList(&result, relativeResourcePath, binaryPath); +#endif + #ifdef PXR_INSTALL_LOCATION _AppendPathList(&result, installLocation, binaryPath); #endif // PXR_INSTALL_LOCATION diff --git a/pxr/usd/usdMtlx/utils.cpp b/pxr/usd/usdMtlx/utils.cpp index 8752bdcf0e7..90e1bd1aa4d 100644 --- a/pxr/usd/usdMtlx/utils.cpp +++ b/pxr/usd/usdMtlx/utils.cpp @@ -16,6 +16,8 @@ #include "pxr/usd/sdf/types.h" #include "pxr/usd/sdr/shaderProperty.h" #include "pxr/base/arch/fileSystem.h" +#include "pxr/base/arch/symbols.h" +#include "pxr/base/arch/systemInfo.h" #include "pxr/base/gf/matrix3d.h" #include "pxr/base/gf/matrix4d.h" #include "pxr/base/gf/vec2f.h" @@ -194,6 +196,22 @@ _ComputeStdlibSearchPaths() stdlibSearchPaths = _MergeSearchPaths(stdlibSearchPaths, { resourceMtlxLibrary }); } + +#ifdef PXR_EXTRA_BINARY_RELATIVE_RESOURCES + std::string relativeResourcePath = "../" + std::string(TF_PP_STRINGIZE(PXR_EXTRA_BINARY_RELATIVE_RESOURCES)) + "/libraries"; + std::string binaryPath; + if (!ArchGetAddressInfo( + reinterpret_cast(&_ComputeStdlibSearchPaths), &binaryPath, + nullptr, nullptr, nullptr) + && binaryPath.empty()) + { + binaryPath = ArchGetExecutablePath(); + binaryPath = TfGetPathName(binaryPath); + } + stdlibSearchPaths =_MergeSearchPaths(stdlibSearchPaths, { TfStringCatPaths(binaryPath, relativeResourcePath) }); + +#endif + return stdlibSearchPaths; }