diff --git a/CMakeLists.txt b/CMakeLists.txt index 10fa0f481..22f97965b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,14 @@ project(${XSTUDIO_GLOBAL_NAME} VERSION ${XSTUDIO_GLOBAL_VERSION} LANGUAGES CXX) # Pre-create the target so Qt's FindWrapOpenGL early-returns and never hits the # broken AGL block. Can be removed once the minimum supported Qt is >= 6.9. if(APPLE AND NOT TARGET WrapOpenGL::WrapOpenGL) - find_package(OpenGL REQUIRED) + # Force the system framework to be found, otherwise + # xstudio crashes under Tahoe and it prevents codesigning. + # Note that vcpkg's glew port needs to be patched similarly. + block(SCOPE_FOR VARIABLES) + set(CMAKE_FIND_FRAMEWORK ONLY) + find_package(OpenGL REQUIRED) + endblock() + add_library(WrapOpenGL::WrapOpenGL INTERFACE IMPORTED) target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE OpenGL::GL) endif() @@ -312,6 +319,11 @@ if (USE_VCPKG) add_subdirectory("scripts/qt_install") endif() +if (APPLE) + # Finalize the bundle by removing any absolute rpaths from and codesigning any binaries + add_subdirectory("scripts/macos_finalize_bundle") +endif() + if (WIN32) include(InstallRequiredSystemLibraries) diff --git a/cmake/macros.cmake b/cmake/macros.cmake index 920bcfcb9..378101da0 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -225,7 +225,7 @@ macro(add_plugin_qml name _dir) cmake_path(GET DIR FILENAME dirname) add_custom_command(TARGET ${name}_COPY_QML POST_BUILD COMMAND ${CMAKE_COMMAND} -E - copy_directory ${DIR} ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/PlugIns/xstudio/qml/${dirname}) + copy_directory ${DIR} ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/qml/${dirname}) endif() endforeach() else() diff --git a/cmake/vcpkg_overlay_ports/glew/fix-LNK2019.patch b/cmake/vcpkg_overlay_ports/glew/fix-LNK2019.patch new file mode 100644 index 000000000..bfb58ade7 --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/fix-LNK2019.patch @@ -0,0 +1,14 @@ +diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt +index 5649972..c0252ec 100644 +--- a/build/cmake/CMakeLists.txt ++++ b/build/cmake/CMakeLists.txt +@@ -129,9 +129,6 @@ if (MSVC) + target_compile_options (glew PRIVATE -GS-) + target_compile_options (glew_s PRIVATE -GS-) + # remove stdlib dependency +- target_link_libraries (glew LINK_PRIVATE -nodefaultlib -noentry) +- target_link_libraries (glew LINK_PRIVATE libvcruntime.lib) +- target_link_libraries (glew LINK_PRIVATE msvcrt.lib ) + string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + elseif (WIN32 AND ((CMAKE_C_COMPILER_ID MATCHES "GNU") OR (CMAKE_C_COMPILER_ID MATCHES "Clang"))) + # remove stdlib dependency on windows with GCC and Clang (for similar reasons diff --git a/cmake/vcpkg_overlay_ports/glew/opengl.patch b/cmake/vcpkg_overlay_ports/glew/opengl.patch new file mode 100644 index 000000000..9e47e1d7f --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/opengl.patch @@ -0,0 +1,17 @@ +--- a/build/cmake/CMakeLists.txt ++++ b/build/cmake/CMakeLists.txt +@@ -36,7 +36,13 @@ + set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + endif() + +-find_package (OpenGL REQUIRED) ++if (APPLE) ++ set(CMAKE_FIND_FRAMEWORK ONLY) ++ find_package (OpenGL REQUIRED) ++ unset(CMAKE_FIND_FRAMEWORK) ++else() ++ find_package (OpenGL REQUIRED) ++endif() + + # prefer GLVND + if (OPENGL_opengl_LIBRARY) diff --git a/cmake/vcpkg_overlay_ports/glew/portfile.cmake b/cmake/vcpkg_overlay_ports/glew/portfile.cmake new file mode 100644 index 000000000..40005dff1 --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/portfile.cmake @@ -0,0 +1,62 @@ +if("x11" IN_LIST FEATURES) + message(WARNING "${PORT} requires the following libraries from the system package manager:\n libxmu-dev\n libxi-dev\n libgl-dev\n\nThese can be installed on Ubuntu systems via apt-get install libxmu-dev libxi-dev libgl-dev.") +endif() + +# Don't change to vcpkg_from_github! The sources in the git repository (archives) are missing some files that are distributed inside releases. +# More info: https://github.com/nigels-com/glew/issues/31 and https://github.com/nigels-com/glew/issues/13 +vcpkg_download_distfile(ARCHIVE + URLS "https://github.com/nigels-com/glew/releases/download/glew-${VERSION}/glew-${VERSION}.tgz" + FILENAME "glew-${VERSION}.tgz" + SHA512 cb4caecf32ec0f180c2691dc7769ffc99571c64f259a2663a2b80e788f1c2fd5362c59e0caaeefed6fb78a4070366d244666a657358049b09071b59fae2377e0 +) + +vcpkg_extract_source_archive( + SOURCE_PATH + ARCHIVE "${ARCHIVE}" + SOURCE_BASE glew + PATCHES + fix-LNK2019.patch + trim-build.diff + opengl.patch +) + +set(options "") +if(VCPKG_TARGET_IS_ANDROID) + list(APPEND options "-DGLEW_X11=OFF") +endif() + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}/build/cmake" + OPTIONS + ${options} + -DBUILD_UTILS=OFF +) + +vcpkg_cmake_install() +vcpkg_copy_pdbs() +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/glew) +vcpkg_fixup_pkgconfig() + +# Burn-in CMake build config +vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/GL/glew.h" "ifndef GLEW_NO_GLU" "if 0") + +if(NOT VCPKG_BUILD_TYPE) + set(libname GLEW) + if(VCPKG_TARGET_IS_WINDOWS) + set(libname glew32) + endif() + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/glew.pc" " -l${libname}" " -l${libname}d") +endif() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/GL/glew.h" "#ifdef GLEW_STATIC" "#if 1") + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/GL/wglew.h" "#ifdef GLEW_STATIC" "#if 1") +endif() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share") + +file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE.txt") diff --git a/cmake/vcpkg_overlay_ports/glew/trim-build.diff b/cmake/vcpkg_overlay_ports/glew/trim-build.diff new file mode 100644 index 000000000..6a1b358b4 --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/trim-build.diff @@ -0,0 +1,23 @@ +diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt +index c5c1fb8..b1f41bf 100644 +--- a/build/cmake/CMakeLists.txt ++++ b/build/cmake/CMakeLists.txt +@@ -162,8 +162,10 @@ endforeach() + set(targets_to_install "") + if(BUILD_SHARED_LIBS) + list(APPEND targets_to_install glew) ++ set_target_properties(glew_s PROPERTIES EXCLUDE_FROM_ALL 1) + else () + list(APPEND targets_to_install glew_s) ++ set_target_properties(glew PROPERTIES EXCLUDE_FROM_ALL 1) + endif() + + install ( TARGETS ${targets_to_install} +@@ -235,6 +237,7 @@ if(WIN32 AND MSVC AND (NOT MSVC_VERSION LESS 1600)) + DESTINATION ${CMAKE_INSTALL_LIBDIR} + CONFIGURATIONS Debug RelWithDebInfo + COMPONENT Runtime ++ OPTIONAL # subject to BUILD_SHARED_LIBS + ) + endif() + diff --git a/cmake/vcpkg_overlay_ports/glew/usage b/cmake/vcpkg_overlay_ports/glew/usage new file mode 100644 index 000000000..8eebb4b3b --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/usage @@ -0,0 +1,14 @@ +glew provides CMake targets: + + # Legacy CMake find module + find_package(GLEW REQUIRED) + target_link_libraries(main PRIVATE GLEW::GLEW) + + # Modern CMake config package + find_package(GLEW CONFIG REQUIRED) + target_link_libraries(main PRIVATE GLEW::GLEW) + +glew provides pkg-config modules: + + # The OpenGL Extension Wrangler library + glew diff --git a/cmake/vcpkg_overlay_ports/glew/vcpkg-cmake-wrapper.cmake b/cmake/vcpkg_overlay_ports/glew/vcpkg-cmake-wrapper.cmake new file mode 100644 index 000000000..909a63540 --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,25 @@ +include(SelectLibraryConfigurations) +_find_package(GLEW CONFIG) +if(GLEW_FOUND AND TARGET GLEW::GLEW AND NOT DEFINED GLEW_INCLUDE_DIRS) + get_target_property(GLEW_INCLUDE_DIRS GLEW::GLEW INTERFACE_INCLUDE_DIRECTORIES) + set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIRS}) + get_target_property(_GLEW_DEFS GLEW::GLEW INTERFACE_COMPILE_DEFINITIONS) + if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC") + get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_LOCATION_DEBUG) + get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_LOCATION_RELEASE) + else() + get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_IMPLIB_DEBUG) + get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_IMPLIB_RELEASE) + endif() + get_target_property(_GLEW_LINK_INTERFACE GLEW::GLEW IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE) # same for debug and release + list(APPEND GLEW_LIBRARIES ${_GLEW_LINK_INTERFACE}) + list(APPEND GLEW_LIBRARY ${_GLEW_LINK_INTERFACE}) + select_library_configurations(GLEW) + if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC") + set(GLEW_STATIC_LIBRARIES ${GLEW_LIBRARIES}) + else() + set(GLEW_SHARED_LIBRARIES ${GLEW_LIBRARIES}) + endif() + unset(_GLEW_DEFS) + unset(_GLEW_LINK_INTERFACE) +endif() \ No newline at end of file diff --git a/cmake/vcpkg_overlay_ports/glew/vcpkg.json b/cmake/vcpkg_overlay_ports/glew/vcpkg.json new file mode 100644 index 000000000..a448a8179 --- /dev/null +++ b/cmake/vcpkg_overlay_ports/glew/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "glew", + "version": "2.3.1", + "description": "The OpenGL Extension Wrangler Library (GLEW) is a cross-platform open-source C/C++ extension loading library.", + "homepage": "https://github.com/nigels-com/glew", + "supports": "!android", + "dependencies": [ + "opengl", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/extern/quickfuture/CMakeLists.txt b/extern/quickfuture/CMakeLists.txt index 1e3176484..90b36c099 100644 --- a/extern/quickfuture/CMakeLists.txt +++ b/extern/quickfuture/CMakeLists.txt @@ -22,7 +22,7 @@ set(QML_FILES if(APPLE) set_target_properties(quickfuture PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/PlugIns/xstudio/qml/QuickFuture" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/qml/QuickFuture" ) else() set_target_properties(quickfuture @@ -49,7 +49,7 @@ set(QML_FUTURE_FILES ) if (APPLE) - set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/PlugIns/xstudio/qml/QuickFuture) + set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/qml/QuickFuture) else() set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/bin/plugin/qml/QuickFuture) endif() diff --git a/extern/quickpromise/CMakeLists.txt b/extern/quickpromise/CMakeLists.txt index 975068f77..4af7b8574 100644 --- a/extern/quickpromise/CMakeLists.txt +++ b/extern/quickpromise/CMakeLists.txt @@ -38,11 +38,15 @@ else() add_library(quickpromise SHARED) endif() -# On Windows the DLL must be next to the QML files for Qt to load the plugin. -# (Install puts it at bin/ separately, see below.) -if(WIN32) +if(APPLE) + set_target_properties(quickpromise + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/plugin/qml/QuickPromise" + ) +else() set_target_properties(quickpromise PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/plugin/qml/QuickPromise" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/plugin/qml/QuickPromise") endif() @@ -68,7 +72,7 @@ endif() add_custom_target(COPY_PROMISE_QML) if (APPLE) - set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/PlugIns/xstudio/qml/QuickPromise) + set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/qml/QuickPromise) else() set(QML_DEST_DIR ${CMAKE_BINARY_DIR}/bin/plugin/qml/QuickPromise) endif() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f7cdfa3da..188dbf71a 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -26,8 +26,8 @@ if(APPLE) add_custom_target(python_module ALL DEPENDS __pybind_xstudio ${VCPKG_LOCATION}/xstudio/api) - # Here we copy the entire python installation into Frameworks - install(DIRECTORY ${VCPKG_LOCATION}/lib/${PYTHONVP} DESTINATION ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Frameworks/lib/) + # Here we copy the entire python installation into Resources + install(DIRECTORY ${VCPKG_LOCATION}/lib/${PYTHONVP} DESTINATION ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/python/lib) elseif(WIN32) diff --git a/scripts/macos_finalize_bundle/CMakeLists.txt b/scripts/macos_finalize_bundle/CMakeLists.txt new file mode 100644 index 000000000..e4fc64025 --- /dev/null +++ b/scripts/macos_finalize_bundle/CMakeLists.txt @@ -0,0 +1,10 @@ +# After macdeployqt has done its thing to the bundle, it still needs +# some cleanup by removing absolute rpath's from and codesigning +# all the binaries. + +find_package(Python COMPONENTS Interpreter REQUIRED) + +set(_cmd "${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/macos_finalize_bundle.py ${CMAKE_BINARY_DIR}/xSTUDIO.app") + +install(CODE "message(\"Running: ${_cmd}\")") +install(CODE "execute_process(COMMAND ${_cmd})") diff --git a/scripts/macos_finalize_bundle/macos_finalize_bundle.py b/scripts/macos_finalize_bundle/macos_finalize_bundle.py new file mode 100755 index 000000000..ae4872fc7 --- /dev/null +++ b/scripts/macos_finalize_bundle/macos_finalize_bundle.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 + +import abc +import argparse +import os +import plistlib +import re +import subprocess +import sys +import shutil +import logging + + +logging.basicConfig( + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" +) + +CODESIGN_IDENTITY = os.environ.get("CODESIGN_IDENTITY", "-") +KEYCHAIN_PASSWORD = os.environ.get("KEYCHAIN_PASSWORD") + +def fatal_error(msg): + """Print error to stderr and exit with code 1""" + logging.critical(msg) + sys.exit(1) + + +def finalize_framework(path): + name = os.path.basename(path) + lib_name, _ = os.path.splitext(name) + lib_path = os.path.join(path, "Versions", "Current", lib_name) + remove_absolute_rpaths(lib_path) + sign_path(lib_path) + + +def unlock_keychain(): + + if not CODESIGN_IDENTITY: + logging.warning("No identity given. Skipping unlock_keychain") + return + if CODESIGN_IDENTITY == "-": + logging.info("ad-hoc identity given. Skipping unlock_keychain") + + if not KEYCHAIN_PASSWORD: + return + + try: + subprocess.check_call( + ["security", "unlock-keychain", "-p", + KEYCHAIN_PASSWORD, "login.keychain"] + ) + except subprocess.CalledProcessError as error: + fatal_error(f"Error unlocking keychain: {error}") + + +def finalize_app(path): + exe_path = os.path.join(path, "Contents", "MacOS") + app_name = os.path.basename(path) + info_plist_path = os.path.join(path, "Contents", "Info.plist") + info = plistlib.load(open(info_plist_path, "rb")) + app_name = info.get("CFBundleExecutable") + # sign any extra exes but skip the bundle exe + for name in os.listdir(exe_path): + if name == app_name: + continue + exe = os.path.join(exe_path, name) + remove_absolute_rpaths(exe) + sign_path(exe) + remove_absolute_rpaths(os.path.join(exe_path, app_name)) + sign_path(path) + + +def sign_path(path): + if not CODESIGN_IDENTITY: + logging.warning( + f"No identity given. Skipping code sign step for {path}") + return + code_sign = "/usr/bin/codesign" + logging.info(f"Signing {path} with identity {CODESIGN_IDENTITY}") + try: + subprocess.check_output( + [ + code_sign, + "--force", + "--sign", + CODESIGN_IDENTITY, + path, + ] + ) + except subprocess.CalledProcessError as error: + fatal_error( + f"Error signing {path}. stderr: {error.stderr}, stdout: {error.stdout}" + ) + + +def finalize_dir(full_path, name): + if name.endswith(".framework"): + finalize_framework(full_path) + if name.endswith(".app"): + finalize_app(full_path) + + +def finalize_bundle(output_path): + unlock_keychain() + for root, dirs, files in os.walk(output_path, topdown=False): + for name in files: + _, ext = os.path.splitext(name) + if ext in (".dylib", ".so"): + lib = os.path.join(root, name) + remove_absolute_rpaths(lib) + sign_path(lib) + for name in dirs: + full_path = os.path.join(root, name) + finalize_dir(full_path, name) + finalize_dir(output_path, os.path.basename(output_path)) + + +def get_rpaths(binary_path): + """ + Run otool -l on a binary and return list of rpaths. + + Args: + binary_path: Path to the binary/framework/dylib + + Returns: + List of rpath strings (e.g., ['@executable_path/../Frameworks']) + """ + try: + # Run otool -l + result = subprocess.run( + ["otool", "-l", binary_path], capture_output=True, text=True, check=True + ) + + rpaths = [] + lines = result.stdout.split("\n") + + # Look for LC_RPATH sections + for i, line in enumerate(lines): + if "cmd LC_RPATH" in line: + # The path is typically 2 lines after LC_RPATH + if i + 2 < len(lines): + path_line = lines[i + 2].strip() + # Extract path using regex + match = re.search(r"path\s+(.+?)\s+\(offset", path_line) + if match: + rpaths.append(match.group(1)) + + return rpaths + + except subprocess.CalledProcessError as e: + fatal_error(f"Error running otool: {e}") + except FileNotFoundError: + fatal_error("otool not found") + + +def delete_rpath(binary_path, rpath): + """ + Delete an rpath from a binary using install_name_tool. + + Args: + binary_path: Path to the binary/framework/dylib + rpath: The rpath to delete (e.g., '@executable_path/../Frameworks') + + Raises: + RuntimeError: If install_name_tool fails + """ + try: + subprocess.run( + ["install_name_tool", "-delete_rpath", rpath, binary_path], + capture_output=True, + text=True, + check=True, + ) + except subprocess.CalledProcessError as e: + fatal_error(f"install_name_tool failed: {e.stderr}") + except FileNotFoundError: + fatal_error("install_name_tool not found") + + +def remove_absolute_rpaths(binary_path): + """ + Remove all absolute rpaths from a binary, keeping only relative ones (@-prefixed). + + Args: + binary_path: Path to the binary/framework/dylib + """ + rpaths = get_rpaths(binary_path) + + for rpath in rpaths: + if not rpath.startswith("@"): + logging.info( + f"Deleting absolute rpath '{rpath}' from '{binary_path}'") + delete_rpath(binary_path, rpath) + + +def main(): + parser = argparse.ArgumentParser( + "finalize_macos_bundle", + description="rpath fixer and codesign tool for MacOS build" + ) + parser.add_argument("bundle") + + args = parser.parse_args() + finalize_bundle(args.bundle) + +if __name__ == "__main__": + main() diff --git a/scripts/qt_install/CMakeLists.txt b/scripts/qt_install/CMakeLists.txt index 7c59a03c3..bd51bc968 100644 --- a/scripts/qt_install/CMakeLists.txt +++ b/scripts/qt_install/CMakeLists.txt @@ -1,8 +1,17 @@ -# After everything else is installed, windeployqt will scan the contents and package up Qt dependencies. +# After everything else is installed, windeployqt/macdeployqt will scan the contents and package up Qt dependencies. get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) -find_program(windeployqt_exe windeployqt HINTS "${_qt_bin_dir}") -configure_file(qt_install.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/qt_install.cmake @ONLY) -install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/qt_install.cmake) +if(APPLE) + find_program(macdeployqt_exe macdeployqt HINTS "${_qt_bin_dir}") + set(_cmd "${macdeployqt_exe} ${CMAKE_BINARY_DIR}/xSTUDIO.app -qmldir=${CMAKE_SOURCE_DIR}/ui") +elseif(WIN32) + find_program(windeployqt_exe windeployqt HINTS "${_qt_bin_dir}") + set(_cmd "${windeployqt_exe} ${CMAKE_INSTALL_PREFIX}/bin/xstudio.exe --qmldir ${CMAKE_SOURCE_DIR}/ui)") +endif() + +if(NOT "${_cmd}" STREQUAL "") + install(CODE "message(\"Running: ${_cmd}\")") + install(CODE "execute_process(COMMAND ${_cmd})") +endif() diff --git a/scripts/qt_install/qt_install.cmake.in b/scripts/qt_install/qt_install.cmake.in deleted file mode 100644 index 53aa5e9f8..000000000 --- a/scripts/qt_install/qt_install.cmake.in +++ /dev/null @@ -1,3 +0,0 @@ -message("Running windeployqt... with args: ${CMAKE_INSTALL_PREFIX}/bin/xstudio.exe --qmldir @CMAKE_SOURCE_DIR@/ui") -execute_process(COMMAND "@windeployqt_exe@" "${CMAKE_INSTALL_PREFIX}/bin/xstudio.exe" --qmldir "@CMAKE_SOURCE_DIR@/ui" - WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}") diff --git a/src/embedded_python/src/embedded_python.cpp b/src/embedded_python/src/embedded_python.cpp index 32f84db54..8768f3507 100644 --- a/src/embedded_python/src/embedded_python.cpp +++ b/src/embedded_python/src/embedded_python.cpp @@ -56,8 +56,8 @@ void EmbeddedPython::setup() { std::string python_home; if (p.string().find("Contents/Frameworks") != std::string::npos) { // String match will happen On MacOS install, here python - // installation is in Frameworks colder in the app bundle - python_home = p.parent_path(); + // installation is in Resources folder in the app bundle + python_home = p.parent_path().parent_path() / "Resources/python"; } else { // Otherwise, we jump up twice to get above the 'lib' folder // where python310.so is installed, as python home should diff --git a/src/launch/xstudio/src/CMakeLists.txt b/src/launch/xstudio/src/CMakeLists.txt index aa460f563..b66245a36 100644 --- a/src/launch/xstudio/src/CMakeLists.txt +++ b/src/launch/xstudio/src/CMakeLists.txt @@ -130,10 +130,6 @@ elseif(APPLE) add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources") - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/") - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/xstudio_app.ico "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Resources/") @@ -144,17 +140,19 @@ elseif(APPLE) COMMAND ${CMAKE_COMMAND} -E copy ${FFMPEG_APP} "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/ffmpeg") - get_target_property(_qmake_executable Qt6::qmake IMPORTED_LOCATION) - get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) - find_program(macdeployqt_exe macdeployqt HINTS "${_qt_bin_dir}") - configure_file(macdeploy.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/macdeploy.cmake @ONLY) - install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/macdeploy.cmake) - - # Add custom command to fix library paths after build - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -DAPP_BUNDLE_DIR=${CMAKE_BINARY_DIR}/xSTUDIO.app - -P ${CMAKE_CURRENT_SOURCE_DIR}/fixup_macos_bundle.cmake - ) + # change its rpath so it can find its libraries in their build location so it and can be run in-place + # (this gets cleaned up during the install by macdeployqt) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND install_name_tool -rpath "@loader_path/../../lib" "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib" + "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/ffmpeg") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks" + "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/ffmpeg") + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" "${CMAKE_CURRENT_BINARY_DIR}/Info.plist") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_BINARY_DIR}/Info.plist" "${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Info.plist") else() diff --git a/src/launch/xstudio/src/Info.plist b/src/launch/xstudio/src/Info.plist index 57dc1e23c..a9a61f038 100644 --- a/src/launch/xstudio/src/Info.plist +++ b/src/launch/xstudio/src/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile xstudio_app.ico CFBundleIdentifier - com.googlecode.skia.${PRODUCT_NAME:rfc1034identifier} + com.dneg.xSTUDIO CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,16 +17,14 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? + ${XSTUDIO_GLOBAL_VERSION} CFBundleVersion 1 LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} + ${CMAKE_OSX_DEPLOYMENT_TARGET} NSMainNibFile - HelloWorld + MainMenu NSPrincipalClass NSApplication - \ No newline at end of file + diff --git a/src/launch/xstudio/src/fixup_macos_bundle.cmake b/src/launch/xstudio/src/fixup_macos_bundle.cmake deleted file mode 100644 index 4766332a6..000000000 --- a/src/launch/xstudio/src/fixup_macos_bundle.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Fix library paths in the app bundle to ensure they're relative -file(GLOB_RECURSE LIBRARIES "${APP_BUNDLE_DIR}/Contents/Frameworks/*.dylib") -foreach(LIB ${LIBRARIES}) - get_filename_component(LIB_NAME ${LIB} NAME) - execute_process(COMMAND install_name_tool -id "@rpath/${LIB_NAME}" ${LIB}) -endforeach() - -# Fix executable references to libraries -execute_process( - COMMAND - install_name_tool - -add_rpath - "@executable_path/../Frameworks" - ${APP_BUNDLE_DIR}/Contents/MacOS/xstudio.bin -) diff --git a/src/launch/xstudio/src/macdeploy.cmake.in b/src/launch/xstudio/src/macdeploy.cmake.in deleted file mode 100644 index 4a7b59b7e..000000000 --- a/src/launch/xstudio/src/macdeploy.cmake.in +++ /dev/null @@ -1,26 +0,0 @@ - -# This command manipulates the rpath in ffmpeg so it can find the ffmpeg libs in the Frameworks -# folder in the app bundle -execute_process(COMMAND install_name_tool ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/ffmpeg -rpath @loader_path/../../lib @loader_path/../Frameworks WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}") - -# macdeployqt finds xSTUDIO's library dependencies and adds them to the app bundle, as well as -# making necessary rpath manipulations -message("Running @macdeployqt_exe@ with args: ${CMAKE_BINARY_DIR}/xSTUDIO.app -qmldir=@CMAKE_SOURCE_DIR@/ui") -execute_process(COMMAND "@macdeployqt_exe@" ${CMAKE_BINARY_DIR}/xSTUDIO.app -qmldir=@CMAKE_SOURCE_DIR@/ui - WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}") - -# Fix library paths to ensure they're relative to the bundle -execute_process( - COMMAND - install_name_tool -id - "@rpath/libglobal.dylib" - ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/Frameworks/libglobal.dylib -) -execute_process( - COMMAND - install_name_tool - -change - "@rpath/libglobal.dylib" - "@executable_path/../Frameworks/libglobal.dylib" - ${CMAKE_BINARY_DIR}/xSTUDIO.app/Contents/MacOS/xstudio.bin -) diff --git a/vcpkg.json b/vcpkg.json index 77b7eec24..430dc6236 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -74,6 +74,10 @@ "name": "freetype", "version": "2.10.1-6#0" }, + { + "name": "glew", + "version": "2.3.1" + }, { "name": "boost-modular-build-helper", "version": "1.84.0#3"