Class: Pindo::Unity::UnityProcHelper
- Inherits:
-
Object
- Object
- Pindo::Unity::UnityProcHelper
- Defined in:
- lib/pindo/module/unity/unity_proc_helper.rb
Overview
Unity 进程管理助手负责 Unity 进程的检测、管理和清理所有方法均为类方法,无需实例化
Class Method Summary collapse
-
.check_unity_processes(unity_exe_full_path: nil, project_path: nil) ⇒ Object
构建前检查 Unity 进程(交互式).
-
.cleanup_unity_processes_after_build(unity_exe_full_path: nil, project_path: nil) ⇒ Object
构建完成后清理 Unity 进程残留(自动).
-
.filter_valid_unity_processes(processes) ⇒ Array<Hash>
过滤掉僵尸进程和无效进程.
-
.get_process_info(pid) ⇒ String?
获取进程信息.
-
.kill_unity_process(pid) ⇒ Boolean
关闭 Unity 进程.
-
.kill_unity_process_with_sudo(pid) ⇒ Boolean
使用 sudo 关闭进程.
-
.parse_unity_processes(processes_output, unity_exe_full_path: nil, project_path: nil) ⇒ Array<Hash>
解析 Unity 进程信息.
-
.process_exists?(pid) ⇒ Boolean
检查进程是否存在.
-
.process_exists_and_active?(pid) ⇒ Boolean
检查进程是否真的存在且活跃.
Class Method Details
.check_unity_processes(unity_exe_full_path: nil, project_path: nil) ⇒ Object
构建前检查 Unity 进程(交互式)
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 11 def self.check_unity_processes(unity_exe_full_path: nil, project_path: nil) # 如果没有提供路径参数,直接跳过Unity进程检查 if unity_exe_full_path.nil? && project_path.nil? return end # 检查是否有Unity进程在运行 unity_processes = `ps aux | grep -i unity | grep -v grep`.strip if !unity_processes.empty? # 解析Unity进程信息,传入Unity路径和项目路径进行精确匹配 unity_pids = parse_unity_processes(unity_processes, unity_exe_full_path: unity_exe_full_path, project_path: project_path) # 过滤掉僵尸进程和无效进程 unity_pids = filter_valid_unity_processes(unity_pids) if unity_pids.any? puts "⚠️ 检测到与当前项目相关的 Unity 进程正在运行:" # 只显示真正的Unity进程,不显示 pindo 相关进程 unity_pids.each_with_index do |process_info, index| puts " #{index + 1}. PID: #{process_info[:pid]}, 命令: #{process_info[:command]}" end puts "" # 询问用户是否要自动关闭Unity进程 loop do puts "批处理模式需要关闭这些 Unity 进程以避免冲突" puts " [y] 是, 自动关闭所有 Unity 进程" puts " [s] 跳过, 我已经手动关闭 Unity 进程,继续构建(可能导致冲突)" puts " [e] 退出编译" print "请输入选择 (y/s/e): " input = STDIN.gets choice = input ? input.chomp.strip.downcase : "" case choice when 'y', 'yes', '1' puts "\n正在关闭 Unity 相关进程..." success_count = 0 unity_pids.each do |process_info| if kill_unity_process(process_info[:pid]) puts "✅ 成功关闭进程 #{process_info[:pid]}" success_count += 1 else puts "❌ 关闭进程 #{process_info[:pid]} 失败" end end if success_count > 0 puts "\n✅ 已关闭 #{success_count} 个 Unity 相关进程" puts "等待3秒后继续编译..." sleep(3) else puts "\n❌ 无法关闭 Unity 相关进程,请手动关闭后重试" raise "Unity进程冲突:无法自动关闭 Unity 相关进程,请手动关闭后重试" end break # 退出循环 when 's', 'skip', '2' puts "\n✅ 跳过检查,继续编译..." puts "假设Unity Editor已经手动关闭,如果仍在运行可能导致编译失败" sleep(1) break # 退出循环 when 'e', 'exit', '3' puts "\n⚠️ 用户选择退出编译" puts "退出Unity编译流程" exit 0 else puts "\n⚠️ 无效选择: '#{choice}'" puts "请输入 y (是), s (跳过), 或 e (退出)\n\n" # 继续循环,让用户重新输入 end end else # 没有检测到与当前项目相关的Unity进程,静默继续 end end end |
.cleanup_unity_processes_after_build(unity_exe_full_path: nil, project_path: nil) ⇒ Object
构建完成后清理 Unity 进程残留(自动)
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 95 def self.cleanup_unity_processes_after_build(unity_exe_full_path: nil, project_path: nil) begin # 等待一小段时间让 Unity 进程自然退出 sleep(2) # 如果没有提供路径参数,不清理进程 if unity_exe_full_path.nil? && project_path.nil? return end # 检查是否还有 Unity 进程在运行 unity_processes = `ps aux | grep -i unity | grep -v grep`.strip if !unity_processes.empty? # 解析进程信息,传入Unity路径和项目路径进行精确匹配 unity_pids = parse_unity_processes(unity_processes, unity_exe_full_path: unity_exe_full_path, project_path: project_path) if unity_pids.any? puts "\e[33m检测到构建后仍有项目相关的 Unity 进程残留,正在清理...\e[0m" # 自动清理残留的 Unity 进程 cleaned_count = 0 unity_pids.each do |process_info| if kill_unity_process(process_info[:pid]) cleaned_count += 1 end end if cleaned_count > 0 puts "\e[32m✅ 已清理 #{cleaned_count} 个 Unity 相关进程残留\e[0m" end end end rescue => e # 清理失败不影响主流程 # 静默处理,不输出错误信息 end end |
.filter_valid_unity_processes(processes) ⇒ Array<Hash>
过滤掉僵尸进程和无效进程
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 202 def self.filter_valid_unity_processes(processes) valid_processes = [] processes.each do |process_info| # PID 可能是字符串,确保正确处理 pid = process_info[:pid] pid_int = pid.to_i # 检查进程是否真的存在且活跃 if process_exists_and_active?(pid_int) valid_processes << process_info end end valid_processes end |
.get_process_info(pid) ⇒ String?
获取进程信息
365 366 367 368 369 370 371 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 365 def self.get_process_info(pid) begin `ps -p #{pid} -o comm=`.strip rescue => e nil end end |
.kill_unity_process(pid) ⇒ Boolean
关闭 Unity 进程
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 265 def self.kill_unity_process(pid) begin pid_int = pid.to_i # 安全检查:确保不是当前进程 if pid_int == Process.pid puts "⚠️ 跳过当前进程 #{pid}" return true end # 检查进程是否存在 unless process_exists?(pid_int) puts "进程 #{pid} 已不存在" return true end # 获取进程信息进行额外验证 process_info = get_process_info(pid_int) if process_info && !process_info.include?('Unity') puts "⚠️ 跳过非Unity进程 #{pid}: #{process_info}" return true end puts "正在关闭Unity进程 #{pid}..." # 先尝试优雅关闭 begin Process.kill('TERM', pid_int) puts "已发送TERM信号给进程 #{pid}" rescue Errno::EPERM puts "❌ 没有权限关闭进程 #{pid},尝试使用sudo" return kill_unity_process_with_sudo(pid) rescue Errno::ESRCH puts "进程 #{pid} 已不存在" return true end # 等待进程优雅退出 sleep(3) # 检查进程是否还存在 if process_exists?(pid_int) puts "进程 #{pid} 未响应TERM信号,尝试强制关闭..." begin Process.kill('KILL', pid_int) puts "已发送KILL信号给进程 #{pid}" sleep(2) rescue Errno::EPERM puts "❌ 没有权限强制关闭进程 #{pid}" return false rescue Errno::ESRCH puts "进程 #{pid} 已不存在" return true end end # 最终检查 if process_exists?(pid_int) puts "❌ 无法关闭进程 #{pid}" false else puts "✅ 成功关闭进程 #{pid}" true end rescue => e puts "❌ 关闭进程 #{pid} 时出错: #{e.message}" puts "错误类型: #{e.class}" false end end |
.kill_unity_process_with_sudo(pid) ⇒ Boolean
使用 sudo 关闭进程
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 340 def self.kill_unity_process_with_sudo(pid) begin puts "尝试使用sudo关闭进程 #{pid}..." result = system("sudo kill -TERM #{pid}") if result sleep(3) if process_exists?(pid.to_i) puts "TERM信号无效,尝试KILL信号..." system("sudo kill -KILL #{pid}") sleep(2) end !process_exists?(pid.to_i) else puts "❌ sudo命令执行失败" false end rescue => e puts "❌ sudo关闭进程时出错: #{e.message}" false end end |
.parse_unity_processes(processes_output, unity_exe_full_path: nil, project_path: nil) ⇒ Array<Hash>
解析 Unity 进程信息
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 138 def self.parse_unity_processes(processes_output, unity_exe_full_path: nil, project_path: nil) processes = [] processes_output.lines.each do |line| # 解析 ps aux 输出格式 parts = line.strip.split(/\s+/) if parts.length >= 11 pid = parts[1] command = parts[10..-1].join(' ') # 过滤掉grep进程本身和pindo进程 unless command.include?('grep') || command.include?('pindo') # 精确匹配Unity进程 is_relevant_unity_process = false match_reasons = [] # 1. 必须是Unity Editor主进程(排除VS Code等其他进程) is_unity_editor = command.match?(/Unity\.app.*\/Unity/i) || command.match?(/Unity\.exe/i) if is_unity_editor # 2. 检查是否是打开了指定项目的Unity进程 # Unity 使用 -projectpath 参数指定项目路径 if project_path # 标准化路径以确保匹配 normalized_project_path = File.(project_path) # 精确匹配项目路径(必须完全匹配,不能只是包含) if command.match?(/-projectpath\s+#{Regexp.escape(normalized_project_path)}(\s|$)/i) # 只有当项目路径完全匹配时才认为是相关进程 is_relevant_unity_process = true match_reasons << "Unity Editor打开了当前项目" # 如果还指定了Unity路径,也要验证 if unity_exe_full_path if command.include?(unity_exe_full_path) match_reasons << "使用指定的Unity版本" else # Unity路径不匹配,不是我们要的进程 is_relevant_unity_process = false match_reasons.clear end end end elsif unity_exe_full_path && command.include?(unity_exe_full_path) # 只提供了Unity路径,没有项目路径时才匹配 # 这种情况下匹配所有使用该Unity版本的进程 is_relevant_unity_process = true match_reasons << "使用指定的Unity版本" end end # 3. 如果没有提供路径参数,不匹配任何进程(由上层函数处理) if is_relevant_unity_process processes << { pid: pid, command: command } end end end end processes end |
.process_exists?(pid) ⇒ Boolean
检查进程是否存在
376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 376 def self.process_exists?(pid) begin Process.kill(0, pid) true rescue Errno::ESRCH false rescue Errno::EPERM # 权限不足,但进程存在 true end end |
.process_exists_and_active?(pid) ⇒ Boolean
检查进程是否真的存在且活跃
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/pindo/module/unity/unity_proc_helper.rb', line 222 def self.process_exists_and_active?(pid) begin # 使用更简单可靠的方式检查进程 # 尝试发送信号0来检查进程是否存在 Process.kill(0, pid) # 如果需要检查进程状态,使用不同的命令格式 # -o pid=,stat= 去掉标题行 process_info = `ps -p #{pid} -o pid=,stat= 2>/dev/null`.strip if process_info.empty? return false end # 解析进程状态 parts = process_info.split(/\s+/) if parts.length >= 2 stat = parts[1] # Z = 僵尸进程, T = 停止进程 # 只过滤僵尸进程,不过滤停止进程(T可能是正常的暂停状态) if stat.include?('Z') puts " 进程 #{pid} 是僵尸进程,过滤掉" return false end end true rescue Errno::ESRCH # 进程不存在 false rescue Errno::EPERM # 权限不足,但进程存在 true rescue => e puts "检查进程 #{pid} 时出错: #{e.message}" # 如果出错,假设进程存在(保守处理) true end end |