From 8525d0abc8f66ce669643cf36978436df6d15ddf Mon Sep 17 00:00:00 2001 From: honjow Date: Fri, 10 Jan 2025 23:28:29 +0800 Subject: [PATCH] Add build python-fuse --- .github/workflows/release.yml | 9 +++++++ .gitignore | 1 + .gitmodules | 3 +++ Makefile | 13 ++++++++++ main.py | 2 ++ py_modules/config.py | 49 ++++++++++++++++++----------------- py_modules/fan.py | 20 +++++++++++--- py_modules/pfuse/utils.py | 39 +++++++++++++++++++++++----- submodule/python-fuse | 1 + 9 files changed, 103 insertions(+), 34 deletions(-) create mode 160000 submodule/python-fuse diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d172e78..6f06718 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,6 +36,15 @@ jobs: make cp -f ryzenadj ../../../bin + - name: build python-fuse + run: | + PWD=$(pwd) + pacman -S python-fuse --noconfirm --needed --overwrite='*' + cd ${PWD}/submodule/python-fuse && \ + PYTHONPATH=${PWD}/py_modules/site-packages \ + python3 setup.py install --prefix=${PWD} --install-lib=install && \ + cp -r ./install/fuse*/fuse* ${PWD}/py_modules/site-packages/ + - name: change log level run: | sed -i 's/logging.DEBUG/logging.INFO/' py_modules/config.py diff --git a/.gitignore b/.gitignore index 42865fe..faa603f 100755 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ coverage node_modules bower_components # py_modules +py_modules/site-packages # Editors .idea diff --git a/.gitmodules b/.gitmodules index 8553631..056ddb6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "submodule/RyzenAdj"] path = submodule/RyzenAdj url = https://github.com/FlyGoat/RyzenAdj.git +[submodule "submodule/python-fuse"] + path = submodule/python-fuse + url = https://github.com/libfuse/python-fuse.git diff --git a/Makefile b/Makefile index 8c3bb6f..edac61a 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ deploy-steamdeck: ## Deploy plugin build to steamdeck @rsync -azp --delete --progress -e "ssh -p $(DECK_PORT) -i $(DECK_KEY)" \ --chmod=Du=rwx,Dg=rx,Do=rx,Fu=rwx,Fg=rx,Fo=rx \ --exclude='.git/' \ + --exclude='*.pyc' \ --exclude='.github/' \ --exclude='.vscode/' \ --exclude='node_modules/' \ @@ -79,6 +80,18 @@ deploy-steamdeck: ## Deploy plugin build to steamdeck @ssh $(DECK_USER)@$(DECK_HOST) -p $(DECK_PORT) -i $(DECK_KEY) \ 'chmod -v 755 $(DECK_HOME)/homebrew/plugins/' +local-fuse: ## Copy fuse module to local site-packages + @echo "+ $@" + @mkdir -p ./py_modules/site-packages + @rm -rf ./py_modules/site-packages/fuse* ./py_modules/site-packages/fuseparts + @rm -rf ./submodule/python-fuse/build + @cd ./submodule/python-fuse && \ + PYTHONPATH=$(PWD)/py_modules/site-packages \ + python3 setup.py install --prefix=$(PWD) --install-lib=install && \ + cp -r ./install/fuse*/fuse* $(PWD)/py_modules/site-packages/ + @rm -rf $(PWD)/submodule/python-fuse/build + + restart-decky: ## Restart Decky on remote steamdeck @echo "+ $@" @ssh -t $(DECK_USER)@$(DECK_HOST) -p $(DECK_PORT) -i $(DECK_KEY) \ diff --git a/main.py b/main.py index 2a8f350..79d314f 100755 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import decky +import sys try: import update @@ -9,6 +10,7 @@ from fuse_manager import FuseManager from gpu import gpuManager from sysInfo import sysInfoManager + sys.path.append(f"{decky.DECKY_PLUGIN_DIR}/py_modules/site-packages") except Exception as e: decky.logger.error(e, exc_info=True) diff --git a/py_modules/config.py b/py_modules/config.py index 062c2b9..be5f5aa 100755 --- a/py_modules/config.py +++ b/py_modules/config.py @@ -5,7 +5,6 @@ import decky import yaml -from helpers import get_homebrew_path from logging_handler import SystemdHandler # 日志配置 @@ -41,18 +40,33 @@ def setup_logger(): # 初始化 logger logger = setup_logger() + +DECKY_PLUGIN_DIR = decky.DECKY_PLUGIN_DIR +DECKY_PLUGIN_PY_DIR = f"{DECKY_PLUGIN_DIR}/py_modules" +SH_PATH = "{}/backend/sh_tools.sh".format(DECKY_PLUGIN_DIR) +RYZENADJ_PATH = "{}/bin/ryzenadj".format(DECKY_PLUGIN_DIR) +# AMD_GPU_DEVICE_PATH = glob.glob("/sys/class/drm/card?/device")[0] +# AMD_GPUFREQ_PATH = "{}/pp_od_clk_voltage".format(AMD_GPU_DEVICE_PATH) +# AMD_GPULEVEL_PATH = "{}/power_dpm_force_performance_level".format(AMD_GPU_DEVICE_PATH) +PLATFORM_PROFILE_PATH = "/sys/firmware/acpi/platform_profile" +PLATFORM_PROFILE_CHOICES_PATH = "/sys/firmware/acpi/platform_profile_choices" + +FAN_CONFIG_DIR = f"{DECKY_PLUGIN_PY_DIR}/fan_config" +FAN_HWMON_CONFIG_DIR = f"{FAN_CONFIG_DIR}/hwmon" +FAN_EC_CONFIG_DIR = f"{FAN_CONFIG_DIR}/ec" + +HWMON_CONFS = glob.glob(f"{FAN_HWMON_CONFIG_DIR}/*.yml") +EC_CONFS = glob.glob(f"{FAN_EC_CONFIG_DIR}/*.yml") + +HWMON_SCHEMA = f"{FAN_CONFIG_DIR}/schema/hwmon.json" +EC_SCHEMA = f"{FAN_CONFIG_DIR}/schema/ec.json" + +API_URL = "https://api.github.com/repos/mengmeet/PowerControl/releases/latest" + +CONFIG_KEY = "PowerControl" + # 路径配置 try: - HOMEBREW_PATH = get_homebrew_path() - DECKY_PLUGIN_DIR = decky.DECKY_PLUGIN_DIR - SH_PATH = "{}/backend/sh_tools.sh".format(DECKY_PLUGIN_DIR) - RYZENADJ_PATH = "{}/bin/ryzenadj".format(DECKY_PLUGIN_DIR) - # AMD_GPU_DEVICE_PATH = glob.glob("/sys/class/drm/card?/device")[0] - # AMD_GPUFREQ_PATH = "{}/pp_od_clk_voltage".format(AMD_GPU_DEVICE_PATH) - # AMD_GPULEVEL_PATH = "{}/power_dpm_force_performance_level".format(AMD_GPU_DEVICE_PATH) - PLATFORM_PROFILE_PATH = "/sys/firmware/acpi/platform_profile" - PLATFORM_PROFILE_CHOICES_PATH = "/sys/firmware/acpi/platform_profile_choices" - for p in glob.glob("/sys/class/drm/card?"): if os.path.exists(f"{p}/device/enable"): with open(f"{p}/device/enable", "r") as f: @@ -74,9 +88,6 @@ def setup_logger(): INTEL_GPU_MIN_LIMIT = f"{p}/gt_RPn_freq_mhz" INTEL_GPU_CUR_FREQ = f"{p}/gt_cur_freq_mhz" break - - FAN_HWMON_CONFIG_DIR = f"{DECKY_PLUGIN_DIR}/backend/fan_config/hwmon" - FAN_EC_CONFIG_DIR = f"{DECKY_PLUGIN_DIR}/backend/fan_config/ec" except Exception as e: logger.error(f"路径配置异常|{e}", exc_info=True) @@ -96,9 +107,6 @@ def setup_logger(): except Exception as e: logger.error(f"设备信息配置异常|{e}", exc_info=True) -API_URL = "https://api.github.com/repos/mengmeet/PowerControl/releases/latest" - -CONFIG_KEY = "PowerControl" # 是否验证 yml 配置文件 VERIFY_YML = False @@ -178,13 +186,6 @@ def load_yaml(file_path: str, chk_schema=None) -> dict: return data -HWMON_CONFS = glob.glob(f"{FAN_HWMON_CONFIG_DIR}/*.yml") -EC_CONFS = glob.glob(f"{FAN_EC_CONFIG_DIR}/*.yml") - -HWMON_SCHEMA = f"{DECKY_PLUGIN_DIR}/py_modules/fan_config/schema/hwmon.json" -EC_SCHEMA = f"{DECKY_PLUGIN_DIR}/py_modules/fan_config/schema/ec.json" - - def get_all_howmon_fans(): with open(HWMON_SCHEMA, "r") as f: hwmon_schema = json.load(f) diff --git a/py_modules/fan.py b/py_modules/fan.py index c1ceaa7..73e7ad5 100755 --- a/py_modules/fan.py +++ b/py_modules/fan.py @@ -1,4 +1,5 @@ import os +import json from conf_manager import confManager from config import FAN_EC_CONFIG, FAN_HWMON_LIST, PRODUCT_NAME, PRODUCT_VERSION, logger @@ -76,7 +77,9 @@ def update_fan_max_value(self, cpu_temp: int): # 解析处理 HWMON 风扇配置 def __parse_fan_configuration_HWMON(self, name_path_map): + logger.info(f"解析 HWMON 风扇配置, len:{len(FAN_HWMON_LIST)}") for hwmon_name in FAN_HWMON_LIST: + logger.debug(f"解析 HWMON 风扇配置, hwmon_name:{hwmon_name}") if hwmon_name not in name_path_map: continue for hwmon_config in FAN_HWMON_LIST[hwmon_name]: @@ -304,15 +307,20 @@ def __parse_fan_configuration_EC(self): # 转化风扇配置 def parse_fan_configuration(self): hwmon_path = "/sys/class/hwmon" - hwmon_files = os.listdir(hwmon_path) + hwmon_dirs = os.listdir(hwmon_path) + logger.info(f"解析 HWMON 风扇配置, hwmon_dirs:{hwmon_dirs}") name_path_map = {} - umount_fuse_igpu() + try: + umount_fuse_igpu() + except Exception: + logger.error("umount_fuse_igpu error", exc_info=True) # 转化hwmon信息 - for file in hwmon_files: - path = hwmon_path + "/" + file + for dir in hwmon_dirs: + path = hwmon_path + "/" + dir name = open(path + "/name").read().strip() + logger.debug(f">>> name:{name}, path:{path}") name_path_map[name] = path if name == "amdgpu": self.gpu_temp_path = path + "/temp1_input" @@ -325,6 +333,10 @@ def parse_fan_configuration(self): if not self.cpu_temp_path and name == "acpitz": self.cpu_temp_path = path + "/temp1_input" + logger.info( + f">>> name_path_map {json.dumps(name_path_map, indent=4, ensure_ascii=False)}" + ) + # 解析 hwmon 信息 self.__parse_fan_configuration_HWMON(name_path_map) diff --git a/py_modules/pfuse/utils.py b/py_modules/pfuse/utils.py index 2afd303..944f196 100644 --- a/py_modules/pfuse/utils.py +++ b/py_modules/pfuse/utils.py @@ -2,8 +2,10 @@ import os import time from threading import Event, Thread +import subprocess from config import logger +import decky TDP_MOUNT = "/run/powercontrol/hwmon" FUSE_MOUNT_SOCKET = "/run/powercontrol/socket" @@ -98,12 +100,25 @@ def prepare_tdp_mount(debug: bool = False, passhtrough: bool = False): if not os.path.ismount(TDP_MOUNT): logger.info(f"Creating bind mount for:\n'{gpu}'\nto:\n'{TDP_MOUNT}'") cmd = f"mount --bind '{gpu}' '{TDP_MOUNT}'" - r = os.system(cmd) - assert not r, f"Failed:\n{cmd}" + try: + result = subprocess.run( + cmd, shell=True, capture_output=True, text=True, check=True + ) + except subprocess.CalledProcessError as e: + error_msg = f"Failed to create bind mount:\nCommand: {cmd}\nReturn code: {e.returncode}\nError: {e.stderr}" + logger.error(error_msg) + raise RuntimeError(error_msg) + logger.info("Making bind mount private.") cmd = f"mount --make-private '{TDP_MOUNT}'" - r = os.system(cmd) - assert not r, f"Failed:\n{cmd}" + try: + result = subprocess.run( + cmd, shell=True, capture_output=True, text=True, check=True + ) + except subprocess.CalledProcessError as e: + error_msg = f"Failed to make mount private:\nCommand: {cmd}\nReturn code: {e.returncode}\nError: {e.stderr}" + logger.error(error_msg) + raise RuntimeError(error_msg) else: logger.info(f"Bind mount already exists at:\n'{TDP_MOUNT}'") @@ -116,6 +131,11 @@ def prepare_tdp_mount(debug: bool = False, passhtrough: bool = False): logger.info(f"Using Python: '{exe_python}'") # get this file's directory thisdir = os.path.dirname(os.path.abspath(__file__)) + + # add py_modules/site-packages to PYTHONPATH + custom_python_path = os.environ.get("PYTHONPATH", "") + custom_python_path += f":{decky.DECKY_PLUGIN_DIR}/py_modules/site-packages" + os.environ["PYTHONPATH"] = custom_python_path cmd = ( f"{exe_python} {thisdir}/driver.py '{gpu}'" + f" -o root={TDP_MOUNT} -o nonempty -o allow_other" @@ -125,8 +145,15 @@ def prepare_tdp_mount(debug: bool = False, passhtrough: bool = False): if debug: cmd += " -f" logger.info(f"Executing:\n'{cmd}'") - r = os.system(cmd) - assert not r, f"Failed:\n{cmd}" + try: + result = subprocess.run( + cmd, shell=True, capture_output=True, text=True, check=True + ) + logger.debug(f"Command output:\n{result.stdout}") + except subprocess.CalledProcessError as e: + error_msg = f"Failed to launch FUSE mount:\nCommand: {cmd}\nReturn code: {e.returncode}\nError: {e.stderr}" + logger.error(error_msg) + raise RuntimeError(error_msg) except Exception: logger.error("Error preparing fuse mount.", exc_info=True) return False diff --git a/submodule/python-fuse b/submodule/python-fuse new file mode 160000 index 0000000..c4169e5 --- /dev/null +++ b/submodule/python-fuse @@ -0,0 +1 @@ +Subproject commit c4169e5e864bfb1eec342fe6dd0427959eeeaa38