Skip to content

Commit

Permalink
Enable building for Apple Frameworks
Browse files Browse the repository at this point in the history
  • Loading branch information
dgovil committed Jun 18, 2024
1 parent fe9724d commit 352d4d6
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 10 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ then build and install USD into `/path/to/my_usd_install_dir`.
> python OpenUSD/build_scripts/build_usd.py /path/to/my_usd_install_dir
```

Additionally you can provide the `--build-apple-framework` flag to create a framework output, for easier integration
into an Xcode application. Framework support is currently experimental, and will always build monolithic.

###### iOS

When building from a macOS system, you can cross compile for iOS based platforms.
Expand All @@ -150,6 +153,7 @@ Additionally, they will not support Python bindings or command line tools.

To build for iOS, add the `--build-target iOS` parameter.

iOS builds default to building as a framework.

##### Windows:

Expand Down
42 changes: 36 additions & 6 deletions build_scripts/build_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,10 @@ 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'}")
if context.macOSCodesign:
extraArgs.append(f"-DPXR_APPLE_CODESIGN_IDENTITY={context.macOSCodesign}")

# Make sure to use boost installed by the build script and not any
# system installed boost
Expand Down Expand Up @@ -1845,6 +1849,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 build)")
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.
Expand Down Expand Up @@ -1880,6 +1890,7 @@ def InstallUSD(context, force, buildArgs):
default=codesignDefault, action="store_true",
help=("Enable code signing for macOS builds "
"(defaults to enabled on Apple Silicon)"))
group.add_argument("--codesign-id", dest="macos_codesign_id", type=str)

if Linux():
group.add_argument("--use-cxx11-abi", type=int, choices=[0, 1],
Expand Down Expand Up @@ -2161,11 +2172,18 @@ def __init__(self, args):
self.buildTarget = args.build_target
apple_utils.SetTarget(self, self.buildTarget)

self.macOSCodesign = \
(args.macos_codesign if hasattr(args, "macos_codesign")
else False)
self.macOSCodesign = False
if args.macos_codesign:
self.macOSCodesign = args.macos_codesign_id or apple_utils.GetCodeSignID()
if apple_utils.IsHostArm() and args.ignore_homebrew:
self.ignorePaths.append("/opt/homebrew")

self.buildAppleFramework = ((args.build_apple_framework
or self.buildTarget in apple_utils.EMBEDDED_PLATFORMS)
and not args.no_build_apple_framework)
if self.buildAppleFramework:
self.buildShared = False
self.buildMonolithic = True
else:
self.buildTarget = ""

Expand Down Expand Up @@ -2479,6 +2497,7 @@ def _JoinVersion(v):
summaryMsg += """\
Variant {buildVariant}
Target {buildTarget}
Framework Build {buildAppleFramework}
Imaging {buildImaging}
Ptex support: {enablePtex}
OpenVDB support: {enableOpenVDB}
Expand Down Expand Up @@ -2563,7 +2582,8 @@ def FormatBuildArguments(buildArgs):
buildMaterialX=("On" if context.buildMaterialX else "Off"),
buildMayapyTests=("On" if context.buildMayapyTests else "Off"),
buildAnimXTests=("On" if context.buildAnimXTests else "Off"),
enableHDF5=("On" if context.enableHDF5 else "Off"))
enableHDF5=("On" if context.enableHDF5 else "Off"),
buildAppleFramework=("On" if MacOS() and context.buildAppleFramework else "Off"))

Print(summaryMsg)

Expand Down Expand Up @@ -2625,8 +2645,9 @@ def FormatBuildArguments(buildArgs):
])

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

printInstructions = any([context.buildPython, context.buildTools, context.buildPrman])
if printInstructions:
Expand All @@ -2649,3 +2670,12 @@ 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 context.buildAppleFramework:
Print("""
Added the following framework to your Xcode Project, (recommended as Embed Without Signing):
OpenUSD.framework
Set the following compiler argument, to find the headers:
SYSTEM_HEADER_SEARCH_PATHS=$(SRCROOT)/$(TARGET_NAME)/OpenUSD.framework/Headers
""")
5 changes: 5 additions & 0 deletions cmake/defaults/CXXDefaults.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,8 @@ 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()
17 changes: 17 additions & 0 deletions cmake/defaults/Options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ option(PXR_PREFER_SAFETY_OVER_SPEED
ON)

if(APPLE)
set(PXR_APPLE_CODESIGN_IDENTITY "-" CACHE STRING "The Codesigning identity needed to sign compiled objects")
# Cross Compilation detection as defined in CMake docs
# Required to be handled here so it can configure options later on
# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos
Expand All @@ -57,6 +58,16 @@ if(APPLE)
set(PXR_BUILD_IMAGING OFF)
endif ()
endif ()

option(PXR_BUILD_APPLE_FRAMEWORK "Builds an Apple Framework." 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.")
set(PXR_BUILD_USD_TOOLS OFF)
endif()
endif()
endif()


Expand Down Expand Up @@ -136,6 +147,12 @@ set(PXR_LIB_PREFIX ""

option(BUILD_SHARED_LIBS "Build shared libraries." ON)
option(PXR_BUILD_MONOLITHIC "Build a monolithic library." OFF)
if (${PXR_BUILD_APPLE_FRAMEWORK})
set(BUILD_SHARED_LIBS OFF)
set(PXR_BUILD_MONOLITHIC ON)
MESSAGE(STATUS "Setting PXR_BUILD_MONOLITHIC=ON for Framework build")
endif ()

set(PXR_MONOLITHIC_IMPORT ""
CACHE
STRING
Expand Down
37 changes: 33 additions & 4 deletions cmake/macros/Public.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,12 @@ 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()

# Build
if (PXR_BUILD_APPLE_FRAMEWORK)
pxr_create_apple_framework()
endif ()

endfunction() # pxr_toplevel_epilogue

function(pxr_monolithic_epilogue)
Expand Down Expand Up @@ -1294,10 +1300,10 @@ function(pxr_build_python_documentation)
endfunction() # pxr_build_python_documentation

# Adding support for a "docs-only" directory, needed when adding doxygen docs
# not associated with a specific library/etc.
# not associated with a specific library/etc.
function(pxr_docs_only_dir NAME)
# Get list of doxygen files, which could include image files and/or
# snippets example cpp files
# Get list of doxygen files, which could include image files and/or
# snippets example cpp files
set(multiValueArgs
DOXYGEN_FILES
)
Expand All @@ -1317,4 +1323,27 @@ function(pxr_docs_only_dir NAME)
${args_DOXYGEN_FILES}
)
endif()
endfunction() # pxr_docs_only_dir
endfunction() # pxr_docs_only_dir

function(pxr_create_apple_framework)
# CMake can have a lot of different boolean representations, that need to be narrowed down
if (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
107 changes: 107 additions & 0 deletions cmake/resources/AppleFrameworkBuild.zsh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env zsh

# Creates an Apple framework for the given platform type
# documentation: https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle
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}/${FRAMEWORK_NAME}.framework"
FRAMEWORK_HEADERS_DIR="${FRAMEWORK_DIR}/Headers"
FRAMEWORK_LIBRARIES_DIR="${FRAMEWORK_DIR}/Libraries"
FRAMEWORK_PLUGIN_DIR="${FRAMEWORK_LIBRARIES_DIR}/usd"
FRAMEWORK_ROOT_LIBRARY_NAME="@FRAMEWORK_ROOT_LIBRARY_NAME@"
EMBEDDED_BUILD=@EMBEDDED_BUILD@
FRAMEWORK_RESOURCES_DIR="${FRAMEWORK_DIR}"
MATERIALX_SOURCE_LIBRARIES="${CMAKE_INSTALL_PREFIX}/libraries/"
BUNDLE_IDENTIFIER="@PXR_APPLE_IDENTIFIER_DOMAIN@.@PXR_APPLE_FRAMEWORK_NAME@"
CODESIGN_ID="@PXR_APPLE_CODESIGN_IDENTITY@"
OLD_RC_PATH="${CMAKE_INSTALL_PREFIX}/lib"

function fix_linkage() {
readonly file=${1:?"A file path must be specified."}
readonly prepend="${FRAMEWORK_NAME}.framework/Libraries"
filename=$(basename ${file})
# First, change the install name. This corresponds to LC_ID_DYLIB.
install_name_tool -id "@rpath/${prepend}/${filename}" ${file}

parts=("${(@f)$(otool -l ${file})}")
for line in ${parts}; do
dylib_name=""
[[ $line =~ ' *name @rpath/(.*\.dylib)' ]] && dylib_name=$match[1]
if [ -n "${dylib_name}" ]; then
install_name_tool -change "@rpath/${dylib_name}" "@rpath/${prepend}/${dylib_name}" "${file}"
fi
if [[ $line == *"${OLD_RC_PATH}"* ]]; then
install_name_tool -delete_rpath ${OLD_RC_PATH} ${file}
fi
done

codesign -f -s ${CODESIGN_ID} ${file}
}

if [ "$EMBEDDED_BUILD" = false ];then
PLIST_ROOT="${FRAMEWORK_DIR}/Versions/A/Resources/"
fi

# 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 directories..."
mkdir -p ${FRAMEWORK_DIR}
mkdir -p ${PLIST_ROOT}

# Copy the plist over
echo "Copying files into ${FRAMEWORK_DIR}"
ditto "${PROJECT_BINARY_DIR}/Info.plist" "${PLIST_ROOT}/Info.plist"

# Copy the primary directories over
ditto "${CMAKE_INSTALL_PREFIX}/include/" ${FRAMEWORK_HEADERS_DIR}
ditto "${CMAKE_INSTALL_PREFIX}/lib/" ${FRAMEWORK_LIBRARIES_DIR}
ditto "${CMAKE_INSTALL_PREFIX}/plugin/usd/" ${FRAMEWORK_PLUGIN_DIR}


# Remove any so files because boost generates them for iPhone
rm -rf ${FRAMEWORK_LIBRARIES_DIR}/cmake
for file in ${FRAMEWORK_LIBRARIES_DIR}/**/libboost*.so*; do
rm -f ${file}
done

# Remove any static archive files as well
for file in ${FRAMEWORK_LIBRARIES_DIR}/**/*.a; do
rm -f ${file}
done


# Copy the MaterialX libraries if they exist
if [ -d "${MATERIALX_SOURCE_LIBRARIES}" ]; then
ditto ${MATERIALX_SOURCE_LIBRARIES} "${FRAMEWORK_LIBRARIES_DIR}/materialx/"
fi


echo "Correcting linkage on libraries..."
# The root file needs to be a binary that matches the framework name
mv "${FRAMEWORK_LIBRARIES_DIR}/${FRAMEWORK_ROOT_LIBRARY_NAME}" "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"
(cd ${FRAMEWORK_LIBRARIES_DIR} && ln -s "../${FRAMEWORK_NAME}" ${FRAMEWORK_ROOT_LIBRARY_NAME})
fix_linkage "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"
install_name_tool -id "@rpath/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"
install_name_tool -change "@rpath/${FRAMEWORK_NAME}.framework/Libraries/${FRAMEWORK_NAME}" "@rpath/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"

# Do Dylib fixing here
# This finds all dylibs, but (.) skips linked files
for file in ${FRAMEWORK_LIBRARIES_DIR}/**/*.dylib(.); do
fix_linkage ${file}
done

# Sign the final framework
echo "Codesigning the framework..."
codesign --force --sign ${CODESIGN_ID} ${FRAMEWORK_DIR} --generate-entitlement-der --identifier ${BUNDLE_IDENTIFIER}

echo "✅ Finished creating framework at ${FRAMEWORK_DIR}"
24 changes: 24 additions & 0 deletions cmake/resources/Info.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>@PXR_APPLE_FRAMEWORK_NAME@</string>
<key>CFBundleIdentifier</key>
<string>@PXR_APPLE_IDENTIFIER_DOMAIN@.@PXR_APPLE_FRAMEWORK_NAME@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>@PXR_APPLE_FRAMEWORK_NAME@</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>@PXR_MAJOR_VERSION@.@PXR_MINOR_VERSION@.@PXR_PATCH_VERSION@</string>
<key>CFBundleVersion</key>
<string>@PXR_MAJOR_VERSION@.@PXR_MINOR_VERSION@.@PXR_PATCH_VERSION@</string>
<key>CSResourcesFileMapped</key>
<true />
</dict>
</plist>
4 changes: 4 additions & 0 deletions pxr/base/plug/initConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ ARCH_CONSTRUCTOR(Plug_InitConfig, 2, void)
_AppendPathList(&result, buildLocation, binaryPath);
_AppendPathList(&result, pluginBuildLocation, binaryPath);

#ifdef PXR_BUILD_APPLE_FRAMEWORK
_AppendPathList(&result, "Libraries/usd", binaryPath);
#endif

#ifdef PXR_INSTALL_LOCATION
_AppendPathList(&result, installLocation, binaryPath);
#endif // PXR_INSTALL_LOCATION
Expand Down
17 changes: 17 additions & 0 deletions pxr/usd/usdMtlx/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -182,6 +184,21 @@ _ComputeStdlibSearchPaths()
stdlibSearchPaths =
_MergeSearchPaths(stdlibSearchPaths, { PXR_MATERIALX_STDLIB_DIR });
#endif

#ifdef PXR_BUILD_APPLE_FRAMEWORK
std::string binaryPath;
if (!ArchGetAddressInfo(
reinterpret_cast<void*>(&_ComputeStdlibSearchPaths), &binaryPath,
nullptr, nullptr, nullptr)
&& binaryPath.empty())
{
binaryPath = ArchGetExecutablePath();
binaryPath = TfGetPathName(binaryPath);
}
stdlibSearchPaths =
_MergeSearchPaths(stdlibSearchPaths, { TfStringCatPaths(binaryPath, "../Libraries/materialx") });
#endif

return stdlibSearchPaths;
}

Expand Down

0 comments on commit 352d4d6

Please sign in to comment.