279 lines
10 KiB
CMake
279 lines
10 KiB
CMake
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
#
|
|
# Build and install QuickLook for FCStd files (org.freecad.fcstd)
|
|
# This is used by Homebrew and Conda/Mamba scripts alike.
|
|
#
|
|
if(FREECAD_CREATE_MAC_APP OR (APPLE AND BUILD_WITH_CONDA))
|
|
add_subdirectory(QuickLook)
|
|
# Installation handled by QuickLook/CMakeLists.txt
|
|
endif()
|
|
|
|
|
|
#
|
|
# Build a Bundle in Homebrew.
|
|
# This is ignored by Conda/Mamba build scripts.
|
|
#
|
|
if(FREECAD_CREATE_MAC_APP)
|
|
|
|
set(PYTHON_DIR_BASENAME python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR})
|
|
|
|
if(Python3_LIBRARY_DIRS)
|
|
# Get first library directory
|
|
list(GET Python3_LIBRARY_DIRS 0 FIRST_PYTHON_LIB_DIR)
|
|
if(FIRST_PYTHON_LIB_DIR MATCHES "(.*Python\\.framework).*")
|
|
#framework
|
|
set(PYTHON_DIR ${CMAKE_MATCH_1}/Versions/${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/lib/${PYTHON_DIR_BASENAME})
|
|
else()
|
|
#unix
|
|
set(PYTHON_DIR ${FIRST_PYTHON_LIB_DIR}/${PYTHON_DIR_BASENAME})
|
|
endif()
|
|
else()
|
|
# Fallback: derive from Python3_EXECUTABLE
|
|
get_filename_component(PYTHON_BIN_DIR ${Python3_EXECUTABLE} DIRECTORY)
|
|
get_filename_component(PYTHON_PREFIX ${PYTHON_BIN_DIR} DIRECTORY)
|
|
set(PYTHON_DIR ${PYTHON_PREFIX}/lib/${PYTHON_DIR_BASENAME})
|
|
endif()
|
|
|
|
message(" PYTHON_DIR is ${PYTHON_DIR} --------------------ipatch--")
|
|
message(" PYTHON_DIR_BASENAME is ${PYTHON_DIR_BASENAME} --------------------ipatch--")
|
|
|
|
install(CODE "execute_process(COMMAND
|
|
${CMAKE_COMMAND} -E copy_directory ${PYTHON_DIR} ${CMAKE_INSTALL_LIBDIR}/${PYTHON_DIR_BASENAME}
|
|
)")
|
|
|
|
if(HOMEBREW_PREFIX)
|
|
|
|
set(MACOS_BUNDLE_CONTENTS_DIR "Contents")
|
|
set(MACOS_BUNDLE_RESOURCES_DIR "${MACOS_BUNDLE_CONTENTS_DIR}/Resources")
|
|
set(MACOS_BUNDLE_EXECUTABLES_DIR "${MACOS_BUNDLE_CONTENTS_DIR}/MacOS")
|
|
set(MACOS_BUNDLE_FRAMEWORKS_DIR "${MACOS_BUNDLE_CONTENTS_DIR}/Frameworks")
|
|
set(MACOS_BUNDLE_LIB_DIR "${MACOS_BUNDLE_CONTENTS_DIR}/lib")
|
|
|
|
message(" PYTHON_DIR is ${PYTHON_DIR} --------------------ipatch--")
|
|
|
|
file(GLOB HOMEBREW_PTH_FILES "${HOMEBREW_PREFIX}/lib/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/freecad*.pth")
|
|
|
|
message(STATUS "HOMEBREW_PTH_FILES are ${HOMEBREW_PTH_FILES} -----------------------ipatch--")
|
|
|
|
# process each .pth file found
|
|
foreach(PTH_FILE ${HOMEBREW_PTH_FILES})
|
|
# read the .pth file into a single string
|
|
file(READ ${PTH_FILE} FILE_CONTENT)
|
|
|
|
# split the content by newlines into a list of lines
|
|
string(REPLACE "\n" ";" LINES "${FILE_CONTENT}")
|
|
|
|
# Process each line from the .pth file.
|
|
foreach(LINE ${LINES})
|
|
# Remove leading/trailing whitespace.
|
|
string(STRIP "${LINE}" CLEAN_LINE)
|
|
|
|
# skip empty lines or comments.
|
|
if(CLEAN_LINE MATCHES "^#|^$")
|
|
continue()
|
|
endif()
|
|
|
|
# handle both site.addsitedir() calls and direct paths
|
|
if(CLEAN_LINE MATCHES "site\\.addsitedir\\('([^']+)'\\)")
|
|
# Extract the path from site.addsitedir() function call
|
|
string(REGEX MATCH "'([^']+)'" PATH_MATCH "${CLEAN_LINE}")
|
|
string(REPLACE "'" "" ADDITIONAL_DIR "${PATH_MATCH}")
|
|
elseif(NOT CLEAN_LINE MATCHES "import site")
|
|
# Direct path (like the last line in your example)
|
|
set(ADDITIONAL_DIR "${CLEAN_LINE}")
|
|
else()
|
|
# Skip import statements
|
|
continue()
|
|
endif()
|
|
|
|
# check if extracted path is valid
|
|
if(ADDITIONAL_DIR AND EXISTS "${ADDITIONAL_DIR}")
|
|
message(STATUS "Processing directory: ${ADDITIONAL_DIR}")
|
|
|
|
# Check if the path matches the Homebrew Cellar format or other paths
|
|
if(ADDITIONAL_DIR MATCHES "${HOMEBREW_PREFIX}/Cellar" OR ADDITIONAL_DIR MATCHES "/usr/local/Cellar")
|
|
|
|
# Get the package name from the path for organizing in site-packages
|
|
# This handles paths like: /usr/local/Cellar/coin3d@4.0.3_py312/4.0.3_1/lib/python3.12/site-packages/
|
|
# need to extract "coin3d@4.0.3_py312" from the path
|
|
string(REGEX MATCH "/Cellar/([^/]+)/" PACKAGE_MATCH "${ADDITIONAL_DIR}")
|
|
if(PACKAGE_MATCH)
|
|
string(REGEX REPLACE "/Cellar/([^/]+)/" "\\1" PACKAGE_NAME "${PACKAGE_MATCH}")
|
|
else()
|
|
# Fallback - just use the directory name before version
|
|
get_filename_component(PARENT_DIR "${ADDITIONAL_DIR}" DIRECTORY)
|
|
get_filename_component(GRANDPARENT_DIR "${PARENT_DIR}" DIRECTORY)
|
|
get_filename_component(PACKAGE_NAME "${GRANDPARENT_DIR}" NAME)
|
|
endif()
|
|
|
|
message(STATUS "Package name: ${PACKAGE_NAME}")
|
|
|
|
# Define the destination in the bundle's site-packages
|
|
set(BUNDLE_SITE_PACKAGES_DIR "${CMAKE_INSTALL_PREFIX}/lib/python3.12/site-packages")
|
|
|
|
# Copy all contents from the source site-packages to bundle site-packages
|
|
# Using glob to get all directories and files in the source
|
|
file(GLOB PACKAGE_CONTENTS "${ADDITIONAL_DIR}/*")
|
|
|
|
foreach(CONTENT_ITEM ${PACKAGE_CONTENTS})
|
|
get_filename_component(ITEM_NAME "${CONTENT_ITEM}" NAME)
|
|
|
|
if(IS_DIRECTORY "${CONTENT_ITEM}")
|
|
# Install directory
|
|
install(DIRECTORY "${CONTENT_ITEM}/"
|
|
DESTINATION "${BUNDLE_SITE_PACKAGES_DIR}/${ITEM_NAME}"
|
|
USE_SOURCE_PERMISSIONS)
|
|
message(STATUS " Installing directory: ${ITEM_NAME}")
|
|
else()
|
|
# Install file
|
|
install(FILES "${CONTENT_ITEM}"
|
|
DESTINATION "${BUNDLE_SITE_PACKAGES_DIR}")
|
|
message(STATUS " Installing file: ${ITEM_NAME}")
|
|
endif()
|
|
endforeach()
|
|
|
|
message(STATUS "Copied package contents from: ${ADDITIONAL_DIR}")
|
|
message(STATUS " To: ${BUNDLE_SITE_PACKAGES_DIR}")
|
|
|
|
else()
|
|
message(STATUS "Skipping non-Homebrew path: ${ADDITIONAL_DIR}")
|
|
endif()
|
|
else()
|
|
message(STATUS "Path does not exist or is invalid: ${ADDITIONAL_DIR}")
|
|
endif()
|
|
|
|
# Reset for next iteration
|
|
set(ADDITIONAL_DIR "")
|
|
|
|
endforeach(LINE)
|
|
endforeach(PTH_FILE)
|
|
endif()
|
|
|
|
set(QT_PLUGINS_DIR "${Qt6Core_DIR}/../../../plugins")
|
|
# Fallback for Homebrew Qt6 layout
|
|
if(NOT EXISTS "${QT_PLUGINS_DIR}")
|
|
set(QT_PLUGINS_DIR "/opt/homebrew/share/qt/plugins")
|
|
endif()
|
|
set(QT_ASSISTANT_PATH "${Qt6Core_DIR}/../../../libexec/Assistant.app/Contents/MacOS/Assistant")
|
|
set(QT_PLUGIN_SUBDIRS "platforms;imageformats;styles;iconengines")
|
|
|
|
|
|
execute_process(COMMAND "xcode-select" "--print-path"
|
|
OUTPUT_VARIABLE XCODE_PATH
|
|
ERROR_QUIET
|
|
)
|
|
string(STRIP ${XCODE_PATH} XCODE_PATH)
|
|
|
|
set(XCTEST_PATH "${XCODE_PATH}/Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework/Versions/Current")
|
|
|
|
# add qt assistant to bundle
|
|
if(EXISTS "${QT_ASSISTANT_PATH}")
|
|
install(PROGRAMS "${QT_ASSISTANT_PATH}" DESTINATION ${CMAKE_INSTALL_PREFIX}/MacOS)
|
|
else()
|
|
message(WARNING "Qt Assistant not found at ${QT_ASSISTANT_PATH}, skipping installation")
|
|
endif()
|
|
|
|
# Install specific Qt plugin subdirectories that FreeCAD needs
|
|
foreach(_subdir ${QT_PLUGIN_SUBDIRS})
|
|
if(IS_DIRECTORY "${QT_PLUGINS_DIR}/${_subdir}")
|
|
set(_resolved_files "")
|
|
file(GLOB _plugin_files "${QT_PLUGINS_DIR}/${_subdir}/*.dylib")
|
|
|
|
foreach(_plugin_file ${_plugin_files})
|
|
if(EXISTS "${_plugin_file}" AND NOT IS_DIRECTORY "${_plugin_file}")
|
|
get_filename_component(_resolved_file "${_plugin_file}" REALPATH)
|
|
list(APPEND _resolved_files ${_resolved_file})
|
|
endif()
|
|
endforeach()
|
|
|
|
if(_resolved_files)
|
|
install(FILES ${_resolved_files} DESTINATION "${CMAKE_INSTALL_LIBDIR}/qtplugins/${_subdir}")
|
|
|
|
# Also install platform plugins to standard Qt location for runtime loading
|
|
if("${_subdir}" STREQUAL "platforms")
|
|
install(FILES ${_resolved_files} DESTINATION "${CMAKE_INSTALL_PREFIX}/PlugIns/platforms")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endforeach()
|
|
|
|
#files installed by homebrew do not have write permission for regular user
|
|
install(CODE "execute_process(COMMAND chmod -R a+w ${CMAKE_INSTALL_LIBDIR})")
|
|
|
|
get_filename_component(APP_PATH ${CMAKE_INSTALL_PREFIX} PATH)
|
|
|
|
install(CODE
|
|
"message(STATUS \"Making bundle relocatable...\")
|
|
# The top-level CMakeLists.txt should prevent multiple package manager
|
|
# prefixes from being set, so the lib path will resolve correctly...
|
|
execute_process(
|
|
COMMAND python3
|
|
${CMAKE_SOURCE_DIR}/src/Tools/MakeMacBundleRelocatable.py
|
|
${APP_PATH}
|
|
${HOMEBREW_PREFIX}${MACPORTS_PREFIX}/lib
|
|
${HOMEBREW_PREFIX}/opt
|
|
${HOMEBREW_PREFIX}/opt/*/lib
|
|
${Qt${FREECAD_QT_MAJOR_VERSION}Core_DIR}/../../..
|
|
${XCTEST_PATH}
|
|
)"
|
|
)
|
|
|
|
# Fix Qt platform plugin dependencies after bundle relocation
|
|
# This addresses the SIGKILL issue where Qt plugins reference framework paths
|
|
# but the Qt libraries are installed as flat dylibs
|
|
install(CODE "
|
|
message(STATUS \"Fixing Qt platform plugin dependencies...\")
|
|
|
|
# Find platform plugins
|
|
file(GLOB_RECURSE PLATFORM_PLUGINS \"${CMAKE_INSTALL_PREFIX}/PlugIns/platforms/*.dylib\")
|
|
|
|
foreach(PLUGIN \${PLATFORM_PLUGINS})
|
|
message(STATUS \"Processing plugin: \${PLUGIN}\")
|
|
|
|
# Get current dependencies
|
|
execute_process(
|
|
COMMAND otool -L \"\${PLUGIN}\"
|
|
OUTPUT_VARIABLE PLUGIN_DEPS
|
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
)
|
|
|
|
# Fix framework-style Qt dependencies to use flat dylib names
|
|
if(FREECAD_QT_MAJOR_VERSION STREQUAL \"6\")
|
|
set(QT_FRAMEWORKS \"QtCore;QtGui;QtWidgets;QtOpenGL;QtPrintSupport;QtSvg;QtNetwork\")
|
|
else()
|
|
set(QT_FRAMEWORKS \"QtCore;QtGui;QtWidgets;QtOpenGL;QtPrintSupport;QtSvg;QtNetwork\")
|
|
endif()
|
|
|
|
foreach(FRAMEWORK \${QT_FRAMEWORKS})
|
|
# Change @rpath/QtXXX.framework/Versions/A/QtXXX to @rpath/QtXXX
|
|
execute_process(
|
|
COMMAND install_name_tool -change
|
|
\"@rpath/\${FRAMEWORK}.framework/Versions/A/\${FRAMEWORK}\"
|
|
\"@rpath/\${FRAMEWORK}\"
|
|
\"\${PLUGIN}\"
|
|
OUTPUT_QUIET
|
|
ERROR_QUIET
|
|
)
|
|
|
|
# Also handle versioned framework paths for Qt5
|
|
if(FREECAD_QT_MAJOR_VERSION STREQUAL \"5\")
|
|
execute_process(
|
|
COMMAND install_name_tool -change
|
|
\"@rpath/\${FRAMEWORK}.framework/Versions/5/\${FRAMEWORK}\"
|
|
\"@rpath/\${FRAMEWORK}\"
|
|
\"\${PLUGIN}\"
|
|
OUTPUT_QUIET
|
|
ERROR_QUIET
|
|
)
|
|
endif()
|
|
endforeach()
|
|
|
|
message(STATUS \"Fixed dependencies for: \${PLUGIN}\")
|
|
endforeach()
|
|
|
|
message(STATUS \"Qt platform plugin dependency fixing complete.\")
|
|
")
|
|
|
|
endif(FREECAD_CREATE_MAC_APP)
|