diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9750eb355..1ffd6e3148 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8328bccbf3..09d079704c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,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) @@ -74,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) @@ -165,6 +194,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) @@ -498,6 +528,24 @@ 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 + math(EXPR CFBUNDLEVERSION "${MATERIALX_MAJOR_VERSION} * 10000 + ${MATERIALX_MINOR_VERSION} * 100 + ${MATERIALX_BUILD_VERSION}") + 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() diff --git a/cmake/modules/AppleFrameworkBuild.zsh.in b/cmake/modules/AppleFrameworkBuild.zsh.in new file mode 100644 index 0000000000..75b0dba0eb --- /dev/null +++ b/cmake/modules/AppleFrameworkBuild.zsh.in @@ -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}" diff --git a/cmake/modules/Info.plist.in b/cmake/modules/Info.plist.in new file mode 100644 index 0000000000..1293cdcbbf --- /dev/null +++ b/cmake/modules/Info.plist.in @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + MaterialX + CFBundleIdentifier + org.aswf.materialx + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + MaterialX + CFBundlePackageType + FMWK + CFBundleShortVersionString + @MATERIALX_LIBRARY_VERSION@ + CFBundleVersion + @CFBUNDLEVERSION@ + CSResourcesFileMapped + + + diff --git a/source/MaterialXFormat/Util.cpp b/source/MaterialXFormat/Util.cpp index 48ded1d6b1..3fc64e3819 100644 --- a/source/MaterialXFormat/Util.cpp +++ b/source/MaterialXFormat/Util.cpp @@ -9,6 +9,10 @@ #include #include +#if defined(__APPLE__) && defined(BUILD_APPLE_FRAMEWORK) + #include +#endif + MATERIALX_NAMESPACE_BEGIN string readFile(const FilePath& filePath) @@ -226,15 +230,33 @@ FileSearchPath getDefaultDataSearchPath() { const FilePath REQUIRED_LIBRARY_FOLDER("libraries/targets"); FilePath currentPath = FilePath::getModulePath(); + + FileSearchPath searchPath; + #if defined(BUILD_APPLE_FRAMEWORK) + const FilePath FRAMEWORK_RESOURCES("Resources"); + + Dl_info info; + if (dladdr(reinterpret_cast(&getDefaultDataSearchPath), &info)) + { + FilePath path = FilePath(info.dli_fname); + if (!path.isEmpty()) + { + path = path.getParentPath(); + searchPath.append(path / 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 diff --git a/source/MaterialXRenderHw/CMakeLists.txt b/source/MaterialXRenderHw/CMakeLists.txt index a8a806d1b6..d3adfb5b8f 100644 --- a/source/MaterialXRenderHw/CMakeLists.txt +++ b/source/MaterialXRenderHw/CMakeLists.txt @@ -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) @@ -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" diff --git a/source/MaterialXRenderHw/SimpleWindowIOS.cpp b/source/MaterialXRenderHw/SimpleWindowIOS.cpp index 1d4189b2ab..4efcebfa64 100644 --- a/source/MaterialXRenderHw/SimpleWindowIOS.cpp +++ b/source/MaterialXRenderHw/SimpleWindowIOS.cpp @@ -5,7 +5,7 @@ #if defined(__APPLE__) -#ifdef TARGET_OS_IOS +#ifdef TARGET_OS_IPHONE #include diff --git a/source/MaterialXRenderHw/SimpleWindowMac.cpp b/source/MaterialXRenderHw/SimpleWindowMac.cpp index a04d1f49ae..d7035f8191 100644 --- a/source/MaterialXRenderHw/SimpleWindowMac.cpp +++ b/source/MaterialXRenderHw/SimpleWindowMac.cpp @@ -5,7 +5,7 @@ #if defined(__APPLE__) -#ifndef TARGET_OS_IOS +#ifndef TARGET_OS_IPHONE #include #include diff --git a/source/MaterialXRenderHw/WindowCocoaWrappers.m b/source/MaterialXRenderHw/WindowCocoaWrappers.m index a012966a8f..3283008300 100644 --- a/source/MaterialXRenderHw/WindowCocoaWrappers.m +++ b/source/MaterialXRenderHw/WindowCocoaWrappers.m @@ -5,7 +5,7 @@ #if defined (__APPLE__) -#ifndef TARGET_OS_IOS +#ifndef TARGET_OS_IPHONE #import #import diff --git a/source/MaterialXRenderHw/WindowWrapper.cpp b/source/MaterialXRenderHw/WindowWrapper.cpp index 654498d5cd..e2c059799c 100644 --- a/source/MaterialXRenderHw/WindowWrapper.cpp +++ b/source/MaterialXRenderHw/WindowWrapper.cpp @@ -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 diff --git a/source/MaterialXRenderMsl/CMakeLists.txt b/source/MaterialXRenderMsl/CMakeLists.txt index 014c99eaae..741ce9d62a 100644 --- a/source/MaterialXRenderMsl/CMakeLists.txt +++ b/source/MaterialXRenderMsl/CMakeLists.txt @@ -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() @@ -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"