diff --git a/release/darwin/scripts/blender-system-info.sh.in b/release/darwin/scripts/blender-system-info.sh.in new file mode 100755 index 00000000000..e7c965142ac --- /dev/null +++ b/release/darwin/scripts/blender-system-info.sh.in @@ -0,0 +1,16 @@ +#!/bin/sh + +# Directory to this SH file. +BASE_DIR=$(dirname "$0") + +PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@" +SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/startup.py" +if test -f "$PYTHON_BIN"; then + exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" +fi + +echo "ERROR: Failed to find Python executable at: $PYTHON_BIN" +echo "Possible causes include:" +echo "- Your Blender installation is corrupt or missing python." +echo "- The location or name the python executable has changed." +exit 1 diff --git a/release/freedesktop/scripts/blender-system-info.sh.in b/release/freedesktop/scripts/blender-system-info.sh.in new file mode 100755 index 00000000000..feee4747b21 --- /dev/null +++ b/release/freedesktop/scripts/blender-system-info.sh.in @@ -0,0 +1,16 @@ +#!/bin/sh + +# Directory to this SH file. +BASE_DIR=$(dirname "$0") + +PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@" +SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/startup.py" +if test -f "$PYTHON_BIN"; then + exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" +fi + +echo "ERROR: Failed to find Python executable at: $PYTHON_BIN" +echo "Possible causes include:" +echo "- Your Blender installation is corrupt or missing python." +echo "- The location or name of the python executable has changed." +exit 1 diff --git a/release/windows/batch/blender_system_info.cmd.in b/release/windows/batch/blender_system_info.cmd.in new file mode 100644 index 00000000000..b250d41219e --- /dev/null +++ b/release/windows/batch/blender_system_info.cmd.in @@ -0,0 +1,16 @@ +@echo off + +set BLENDER_INSTALL_DIRECTORY=%~dp0 +set BLENDER_VERSION_FOLDER=%BLENDER_INSTALL_DIRECTORY%@BLENDER_VERSION@ +set PYTHON_BIN=%BLENDER_VERSION_FOLDER%\python\bin\@PYTHON_EXECUTABLE_NAME_ONLY@ + +if exist "%PYTHON_BIN%" ( + "%PYTHON_BIN%" -I "%BLENDER_VERSION_FOLDER%\scripts\modules\_bpy_internal\system_info\startup.py" + exit /b +) + +echo ERROR: Failed to find python executable at: %PYTHON_BIN% +echo Possible causes include: +echo - Your Blender installation is corrupt or missing @PYTHON_EXECUTABLE_NAME_ONLY@. +echo - The location or name of @PYTHON_EXECUTABLE_NAME_ONLY@ has changed. +pause diff --git a/scripts/modules/bl_ui_utils/bug_report_url.py b/scripts/modules/_bpy_internal/system_info/runtime.py similarity index 95% rename from scripts/modules/bl_ui_utils/bug_report_url.py rename to scripts/modules/_bpy_internal/system_info/runtime.py index 66c4458503d..f7550de01fd 100644 --- a/scripts/modules/bl_ui_utils/bug_report_url.py +++ b/scripts/modules/_bpy_internal/system_info/runtime.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later +# Keep the information collected in this script synchronized with `startup.py`. def url_prefill_from_blender(*, addon_info=None): import bpy diff --git a/scripts/modules/_bpy_internal/system_info/startup.py b/scripts/modules/_bpy_internal/system_info/startup.py new file mode 100755 index 00000000000..bde34ea8f52 --- /dev/null +++ b/scripts/modules/_bpy_internal/system_info/startup.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2024 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# Keep the information collected in this script synchronized with `runtime.py`. + +def prefill_bug_report_info() -> int: + import re + import struct + import platform + import subprocess + import webbrowser + import urllib.parse + from pathlib import Path + + print("Collecting system information...") + + query_params = {"type": "bug_report", "project": "blender"} + + query_params["os"] = "{:s} {:d} Bits".format( + platform.platform(), + struct.calcsize("P") * 8, + ) + + # There doesn't appear to be a easy way to collect GPU information in Python + # if Blender isn't opening and we can't import the GPU module. + # So just tell users to follow a written guide. + query_params["gpu"] = ( + "Follow our guide to collect this information:\n" + "https://developer.blender.org/docs/handbook/bug_reports/making_good_bug_reports/collect_system_information/" + ) + + os_type = platform.system() + script_directory = Path(__file__).parent.resolve() + if os_type == "Darwin": # macOS appears as Darwin. + blender_dir = script_directory.joinpath("../../../../../../MacOS/Blender") + elif os_type == "Windows": + blender_dir = script_directory.joinpath("../../../../../Blender.exe") + else: # Linux and other Unix systems. + blender_dir = script_directory.joinpath("../../../../../blender") + + try: + blender_output = subprocess.run( + [blender_dir, "--version"], + stdout=subprocess.PIPE, + encoding="utf-8", + errors="surrogateescape", + ) + except Exception as ex: + sys.stderr.write("{:s}\n".format(str(ex))) + return 1 + + text = blender_output.stdout + + # Gather Blender version information. + version_match = re.search(r"^Blender (.*)", text, flags=re.MULTILINE) + branch_match = re.search(r"^\s+build branch: (.*)", text, flags=re.MULTILINE) + commit_date_match = re.search(r"^\s+build commit date: (.*)", text, flags=re.MULTILINE) + commit_time_match = re.search(r"^\s+build commit time: (.*)", text, flags=re.MULTILINE) + build_hash_match = re.search(r"^\s+build hash: (.*)", text, flags=re.MULTILINE) + + if not (version_match or branch_match or commit_date_match or commit_time_match or build_hash_match): + # No valid Blender info could be found. + print("Blender did not provide any build information. Blender may be corrupt or blocked from running.") + print("Please try reinstalling Blender and double check your anti-virus isn't blocking it from running.") + return 1 + + missing_string = "" + + query_params["broken_version"] = "{:s}, branch: {:s}, commit date: {:s} {:s}, hash `{:s}`".format( + version_match.group(1) if version_match else missing_string, + branch_match.group(1) if branch_match else missing_string, + commit_date_match.group(1) if commit_date_match else missing_string, + commit_time_match.group(1) if commit_time_match else missing_string, + build_hash_match.group(1) if build_hash_match else missing_string, + ) + + query_str = urllib.parse.urlencode(query_params) + webbrowser.open("https://redirect.blender.org/?{:s}".format(query_str)) + + return 0 + + +if __name__ == "__main__": + import sys + sys.exit(prefill_bug_report_info()) diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 98deff21070..068c9e364f2 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -1086,7 +1086,7 @@ class WM_OT_url_open_preset(Operator): ) def _url_from_bug(self, _context): - from bl_ui_utils.bug_report_url import url_prefill_from_blender + from _bpy_internal.system_info.runtime import url_prefill_from_blender return url_prefill_from_blender() def _url_from_release_notes(self, _context): diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 784f38144d9..79d371de215 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -961,6 +961,23 @@ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libglapi.so.0.0.0)\n endif() unset(_target_LIB) endif() + + if(WITH_INSTALL_PORTABLE) + # The script uses the version of Python installed with Blender, + # so it must only be inside installed if `WITH_PYTHON AND WITH_PYTHON_INSTALL` + # are active. + get_filename_component(PYTHON_EXECUTABLE_NAME_ONLY ${PYTHON_EXECUTABLE} NAME) + configure_file( + ${CMAKE_SOURCE_DIR}/release/freedesktop/scripts/blender-system-info.sh.in + ${CMAKE_BINARY_DIR}/release/freedesktop/scripts/blender-system-info.sh + @ONLY + ) + unset(PYTHON_EXECUTABLE_NAME_ONLY) + install( + PROGRAMS ${CMAKE_BINARY_DIR}/release/freedesktop/scripts/blender-system-info.sh + DESTINATION "." + ) + endif() endif() if(WITH_DRACO) @@ -1471,6 +1488,27 @@ elseif(WIN32) if(NOT WITH_PYTHON_MODULE) + get_filename_component(PYTHON_EXECUTABLE_NAME_ONLY ${PYTHON_EXECUTABLE} NAME) + # Configure then generate file. Note that this copies the literal generator + # for the Python executable name. So the file needs to be generated afterwards + # to get the correct name. + configure_file( + ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_system_info.cmd.in + ${CMAKE_BINARY_DIR}/release/windows/batch/blender_system_info.with_vars.cmd.in + @ONLY + ) + unset(PYTHON_EXECUTABLE_NAME_ONLY) + # Replace Python executable generator with actual executable name. + file(GENERATE + OUTPUT ${CMAKE_BINARY_DIR}/release/windows/batch/blender_system_info_$.cmd + INPUT ${CMAKE_BINARY_DIR}/release/windows/batch/blender_system_info.with_vars.cmd.in + ) + install( + FILES + ${CMAKE_BINARY_DIR}/release/windows/batch/blender_system_info_$.cmd + DESTINATION ${TARGETDIR_EXE} + RENAME blender_system_info.cmd + ) install( FILES ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu.cmd @@ -1613,6 +1651,20 @@ elseif(APPLE) ) endif() + if(NOT WITH_PYTHON_MODULE AND WITH_PYTHON AND WITH_PYTHON_INSTALL) + get_filename_component(PYTHON_EXECUTABLE_NAME_ONLY ${PYTHON_EXECUTABLE} NAME) + configure_file( + ${CMAKE_SOURCE_DIR}/release/darwin/scripts/blender-system-info.sh.in + ${CMAKE_BINARY_DIR}/release/darwin/scripts/blender-system-info.sh + @ONLY + ) + unset(PYTHON_EXECUTABLE_NAME_ONLY) + install( + PROGRAMS ${CMAKE_BINARY_DIR}/release/darwin/scripts/blender-system-info.sh + DESTINATION "./Blender.app/Contents/Resources" + ) + endif() + if(WITH_DRACO) install( PROGRAMS $