Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Apple framework builds #2020

Merged
merged 15 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
compiler: xcode
compiler_version: "15.4"
python: None
cmake_config: -DMATERIALX_BUILD_IOS=ON -DCMAKE_OSX_SYSROOT=`xcrun --sdk iphoneos --show-sdk-path` -DCMAKE_OSX_ARCHITECTURES=arm64
cmake_config: -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=`xcrun --sdk iphoneos --show-sdk-path` -DCMAKE_OSX_ARCHITECTURES=arm64

- name: Windows_VS2019_Win32_Python37
os: windows-2019
Expand Down
54 changes: 52 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(MATERIALX_MAJOR_VERSION 1)
set(MATERIALX_MINOR_VERSION 39)
set(MATERIALX_BUILD_VERSION 2)
set(MATERIALX_LIBRARY_VERSION ${MATERIALX_MAJOR_VERSION}.${MATERIALX_MINOR_VERSION}.${MATERIALX_BUILD_VERSION})
math(EXPR MATERIALX_BUILD_IDENTIFIER "${MATERIALX_MAJOR_VERSION} * 10000 + ${MATERIALX_MINOR_VERSION} * 100 + ${MATERIALX_BUILD_VERSION}")
dgovil marked this conversation as resolved.
Show resolved Hide resolved

# Cmake setup
cmake_minimum_required(VERSION 3.24)
Expand Down Expand Up @@ -59,10 +60,27 @@ option(MATERIALX_COVERAGE_ANALYSIS "Build MaterialX libraries with coverage anal
option(MATERIALX_DYNAMIC_ANALYSIS "Build MaterialX libraries with dynamic analysis on supporting platforms." OFF)
option(MATERIALX_OSL_LEGACY_CLOSURES "Build OSL shader generation supporting the legacy OSL closures." OFF)

option(MATERIALX_BUILD_IOS "Build MaterialX for iOS." OFF)
option(MATERIALX_BUILD_IOS "Build MaterialX for iOS. (Deprecated. Set CMAKE_SYSTEM_NAME instead)" OFF)
set(MATERIALX_BUILD_APPLE_EMBEDDED OFF)
if (MATERIALX_BUILD_IOS)
MESSAGE(WARNING "The MATERIALX_BUILD_IOS is deprecated. Set the CMAKE_SYSTEM_NAME to the platform instead")
set(CMAKE_SYSTEM_NAME iOS)
add_definitions(-DTARGET_OS_IOS=1)
endif()

# Cross Compilation detection as defined in CMake docs
# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos
# Note: All these SDKs may not be supported by MaterialX
set(__build_apple_framework OFF)
if (CMAKE_SYSTEM_NAME MATCHES "iOS"
OR CMAKE_SYSTEM_NAME MATCHES "tvOS"
OR CMAKE_SYSTEM_NAME MATCHES "visionOS"
OR CMAKE_SYSTEM_NAME MATCHES "watchOS")
set(MATERIALX_BUILD_APPLE_EMBEDDED ON)
set(__build_apple_framework ${MATERIALX_BUILD_SHARED_LIBS})
# TARGET_OS_IPHONE refers to all IPHONE derived platforms
# https://chaosinmotion.com/2021/08/02/things-to-remember-compiler-conditionals-for-macos-ios-etc/
# This should be auto-defined, but leaving it in here because it was historically defined
add_definitions(-DTARGET_OS_IPHONE=1)
set(MATERIALX_BUILD_MONOLITHIC ON)
set(MATERIALX_BUILD_PYTHON OFF)
set(MATERIALX_BUILD_VIEWER OFF)
Expand All @@ -73,6 +91,18 @@ if (MATERIALX_BUILD_IOS)
set(MATERIALX_BUILD_TESTS OFF)
endif()

set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "-" CACHE STRING "The Codesigning identity needed to sign compiled objects")
option(MATERIALX_BUILD_APPLE_FRAMEWORK "Build MaterialX as an Apple Framework" ${__build_apple_framework})
if (MATERIALX_BUILD_APPLE_FRAMEWORK)
add_definitions(-DBUILD_APPLE_FRAMEWORK)
set(MATERIALX_BUILD_MONOLITHIC ON)
set(MATERIALX_BUILD_PYTHON OFF)
set(MATERIALX_BUILD_VIEWER OFF)
set(MATERIALX_BUILD_GRAPH_EDITOR OFF)
set(MATERIALX_BUILD_TESTS OFF)
set(MATERIALX_BUILD_SHARED_LIBS ON)
endif()

if (MATERIALX_BUILD_JS)
set(MATERIALX_BUILD_RENDER OFF)
set(MATERIALX_BUILD_TESTS OFF)
Expand Down Expand Up @@ -163,6 +193,7 @@ mark_as_advanced(MATERIALX_INSTALL_STDLIB_PATH)
mark_as_advanced(MATERIALX_BUILD_JS)
mark_as_advanced(MATERIALX_EMSDK_PATH)
mark_as_advanced(MATERIALX_BUILD_IOS)
mark_as_advanced(MATERIALX_BUILD_APPLE_FRAMEWORK)
if (MATERIALX_BUILD_GEN_MDL)
mark_as_advanced(MATERIALX_MDLC_EXECUTABLE)
mark_as_advanced(MATERIALX_MDL_RENDER_EXECUTABLE)
Expand Down Expand Up @@ -496,6 +527,25 @@ if (MATERIALX_BUILD_MONOLITHIC)
# Note : we don't install the headers etc. here, and rely on each separate modules CMakeLists.txt
# to do that installation, thus we respect the build options configuration, and only install
# the headers for the modules we've built in to the monolithic build.

# Finally do the framework build if requested
# This uses a zsh script since zsh is guaranteed to exist on systems
if(MATERIALX_BUILD_APPLE_FRAMEWORK)
# Conform cmake formats to zsh expected formats
set(__embedded_build "false")
if (MATERIALX_BUILD_APPLE_EMBEDDED)
set(__embedded_build "true")
endif()

# Install the Info.plist and shell script
configure_file(cmake/modules/Info.plist.in "${PROJECT_BINARY_DIR}/Info.plist" @ONLY)
configure_file(cmake/modules/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 )")

endif()

endif()
endif()

Expand Down
113 changes: 113 additions & 0 deletions cmake/modules/AppleFrameworkBuild.zsh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/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
echo "⌛️ Creating MaterialX.framework ..."

# Variables are substituted by CMake
CMAKE_INSTALL_PREFIX="@CMAKE_INSTALL_PREFIX@"
PROJECT_BINARY_DIR="@PROJECT_BINARY_DIR@"

FRAMEWORK_NAME="MaterialX"
FRAMEWORK_DIR="${CMAKE_INSTALL_PREFIX}/frameworks/${FRAMEWORK_NAME}.framework"
FRAMEWORK_LIBRARIES_DIR="${FRAMEWORK_DIR}/Libraries"
FRAMEWORK_ROOT_LIBRARY_NAME="libMaterialX.@MATERIALX_LIBRARY_VERSION@.dylib"
EMBEDDED_BUILD=@__embedded_build@
FRAMEWORK_RESOURCES_DIR="${FRAMEWORK_DIR}"
MATERIALX_SOURCE_LIBRARIES="${CMAKE_INSTALL_PREFIX}/libraries/"
BUNDLE_IDENTIFIER="org.aswf.materialx"
CODESIGN_ID="@CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_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}
}

# 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}/Resources"
FRAMEWORK_PLIST_LOCATION="${FRAMEWORK_DIR}/Info.plist"
FRAMEWORK_HEADERS_DIR="${FRAMEWORK_DIR}/Headers"
FRAMEWORK_LIB_PATH=""${FRAMEWORK_DIR}/${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}"
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 primary directories over
ditto "${CMAKE_INSTALL_PREFIX}/include/" ${FRAMEWORK_HEADERS_DIR}
ditto "${CMAKE_INSTALL_PREFIX}/libraries/" "${FRAMEWORK_RESOURCES_DIR}/libraries"
ditto "${CMAKE_INSTALL_PREFIX}/resources" "${FRAMEWORK_RESOURCES_DIR}/render"

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
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}"

# Frameworks require all includes to use the framework name as the prefix for automatic discovery
echo "Modifying headers..."
HEADER_SET=""
for i in $(cd $FRAMEWORK_HEADERS_DIR && ls -d */ | cut -f1 -d'/');do
HEADER_SET+="${i}|"
done;
# Sed on macOS is POSIX compliant and so uses different args than GNU
# Things to be aware of are [[:<:]] and [[:>:]] are for word boundaries and spaces are explicit literals
INCLUDE_PATTERN="^# *include [\"|<]([[:<:]](${HEADER_SET::-1})[[:>:]].*)[\"|>].*$"
find ${FRAMEWORK_HEADERS_DIR} -type f -name "*.h*" -print0 | xargs -0 sed -i "" -E "s,${INCLUDE_PATTERN},#include <${FRAMEWORK_NAME}/\1>,g"

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

echo "✅ Finished creating framework at ${FRAMEWORK_DIR}"
24 changes: 24 additions & 0 deletions cmake/modules/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>MaterialX</string>
<key>CFBundleIdentifier</key>
<string>org.aswf.materialx</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>MaterialX</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>@MATERIALX_LIBRARY_VERSION@</string>
<key>CFBundleVersion</key>
<string>@MATERIALX_BUILD_IDENTIFIER@</string>
<key>CSResourcesFileMapped</key>
<true />
</dict>
</plist>
19 changes: 19 additions & 0 deletions source/MaterialXFormat/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <sys/syslimits.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#include <dlfcn.h>
#endif

#include <array>
Expand Down Expand Up @@ -371,6 +372,24 @@ FilePath FilePath::getModulePath()
#endif
}

#if defined(__APPLE__)
FilePath FilePath::getSharedLibraryPath()
{
Dl_info info;

if (dladdr(reinterpret_cast<void*>(&getSharedLibraryPath), &info))
{
FilePath path = FilePath(info.dli_fname);
if (!path.isEmpty())
{
path = path.getParentPath();
return path;
}
}
return {};
}
#endif

FileSearchPath getEnvironmentPath(const string& sep)
{
string searchPathEnv = getEnviron(MATERIALX_SEARCH_PATH_ENV_VAR);
Expand Down
5 changes: 5 additions & 0 deletions source/MaterialXFormat/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ class MX_FORMAT_API FilePath
/// Return the directory containing the executable module.
static FilePath getModulePath();

#if defined(__APPLE__)
/// Return the directory containing the shared library.
static FilePath getSharedLibraryPath();
dgovil marked this conversation as resolved.
Show resolved Hide resolved
#endif

private:
StringVec _vec;
Type _type;
Expand Down
13 changes: 11 additions & 2 deletions source/MaterialXFormat/Util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,24 @@ FileSearchPath getDefaultDataSearchPath()
{
const FilePath REQUIRED_LIBRARY_FOLDER("libraries/targets");
FilePath currentPath = FilePath::getModulePath();

FileSearchPath searchPath = FileSearchPath();

#if defined(BUILD_APPLE_FRAMEWORK)
const FilePath FRAMEWORK_RESOURCES("Resources");
searchPath.append(FilePath::getSharedLibraryPath() / FRAMEWORK_RESOURCES);
#endif

while (!currentPath.isEmpty())
{
if ((currentPath / REQUIRED_LIBRARY_FOLDER).exists())
{
return FileSearchPath(currentPath);
searchPath.append(FileSearchPath(currentPath));
break;
}
currentPath = currentPath.getParentPath();
}
return FileSearchPath();
return searchPath;
}

MATERIALX_NAMESPACE_END
4 changes: 2 additions & 2 deletions source/MaterialXRenderHw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*")

if(APPLE)
if (NOT MATERIALX_BUILD_IOS)
if (NOT MATERIALX_BUILD_APPLE_EMBEDDED)
find_library(COCOA_FRAMEWORK Cocoa)
endif()
elseif(UNIX)
Expand Down Expand Up @@ -31,7 +31,7 @@ if(APPLE)
PUBLIC
"-framework Foundation"
"-framework Metal")
if (NOT MATERIALX_BUILD_IOS)
if (NOT MATERIALX_BUILD_APPLE_EMBEDDED)
target_link_libraries(${TARGET_NAME}
PUBLIC
"-framework Cocoa"
Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRenderHw/SimpleWindowIOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#if defined(__APPLE__)

#ifdef TARGET_OS_IOS
#ifdef TARGET_OS_IPHONE

#include <MaterialXRenderHw/SimpleWindow.h>

Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRenderHw/SimpleWindowMac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#if defined(__APPLE__)

#ifndef TARGET_OS_IOS
#ifndef TARGET_OS_IPHONE

#include <MaterialXRenderHw/SimpleWindow.h>
#include <MaterialXRenderHw/WindowCocoaWrappers.h>
Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRenderHw/WindowCocoaWrappers.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#if defined (__APPLE__)

#ifndef TARGET_OS_IOS
#ifndef TARGET_OS_IPHONE

#import <Cocoa/Cocoa.h>
#import <AppKit/NSApplication.h>
Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRenderHw/WindowWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ WindowWrapper::WindowWrapper(ExternalWindowHandle externalHandle,
DisplayHandle display)
{
_externalHandle = externalHandle;
#ifndef TARGET_OS_IOS
#ifndef TARGET_OS_IPHONE
// Cache a pointer to the window.
_internalHandle = NSUtilGetView(externalHandle);
#else
Expand Down
4 changes: 2 additions & 2 deletions source/MaterialXRenderMsl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if(POLICY CMP0072)
endif()

if(APPLE)
if(NOT MATERIALX_BUILD_IOS)
if(NOT MATERIALX_BUILD_APPLE_EMBEDDED)
find_library(COCOA_FRAMEWORK Cocoa)
find_package(OpenGL REQUIRED)
endif()
Expand Down Expand Up @@ -50,7 +50,7 @@ if(MSVC)
PUBLIC
Opengl32)
elseif(APPLE)
if(NOT MATERIALX_BUILD_IOS)
if(NOT MATERIALX_BUILD_APPLE_EMBEDDED)
target_link_libraries(${TARGET_NAME}
PUBLIC
"-framework Cocoa"
Expand Down