Class: Pindo::Unity::UnityCommandHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/pindo/module/unity/unity_command_helper.rb

Overview

Unity 命令执行助手负责执行 Unity 命令行操作所有方法均为类方法,无需实例化

Class Method Summary collapse

Class Method Details

.execute_unity_command(unity_exe_full_path, project_path, method: 'GoodUnityBuild.BuildManager.BatchBuild', additional_args: {}) ⇒ Hash

执行 Unity 命令

Parameters:

  • unity_exe_full_path (String)

    Unity 可执行文件路径

  • project_path (String)

    Unity 项目路径

  • method (String) (defaults to: 'GoodUnityBuild.BuildManager.BatchBuild')

    Unity 方法名(默认: GoodUnityBuild.BuildManager.BatchBuild)

  • additional_args (Hash) (defaults to: {})

    额外参数

Returns:

  • (Hash)

    执行结果 { success:, stdout:, stderr:, exit_status:, unity_version: }



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
91
92
93
94
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
132
133
134
135
136
137
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
# File 'lib/pindo/module/unity/unity_command_helper.rb', line 21

def self.execute_unity_command(unity_exe_full_path, project_path, method: 'GoodUnityBuild.BuildManager.BatchBuild', additional_args: {})
  if unity_exe_full_path.nil?
    raise Informative, "Unity path not found!"
  end

  # 调试级别:通过环境变量或参数控制
  debug_level = ENV['UNITY_BUILD_DEBUG'] || additional_args[:debug_level] || 'normal'
  # debug_level: 'quiet' - 只显示错误
  #              'normal' - 显示错误、警告和关键进度
  #              'verbose' - 显示所有输出

  # 检查项目路径是否存在
  unless File.directory?(project_path)
    raise Informative, "Unity项目路径不存在: #{project_path}"
  end

  # 检查项目是否是Unity项目
  project_settings = File.join(project_path, "ProjectSettings")
  unless File.directory?(project_settings)
    raise Informative, "项目路径不是Unity项目: #{project_path}"
  end

  cmd_args = [
    unity_exe_full_path,
    "-batchmode",
    "-quit",
    "-projectPath",
    project_path.to_s,
    "-executeMethod",
    method
  ]

  # Add any additional arguments
  additional_args.each do |key, value|
    cmd_args << "-#{key}"
    cmd_args << value.to_s if value
  end

  puts "Unity command: #{cmd_args.join(' ')}"
  puts ""

  # 使用更智能的进度检测机制
  progress_thread = nil
  start_time = Time.now
  last_output_time = Time.now

  begin
    # 使用 Open3.popen3 来实时监控输出
    Open3.popen3(*cmd_args) do |stdin, stdout, stderr, wait_thr|
      stdin.close

      # 启动进度显示线程
      progress_thread = Thread.new do
        dots = 0
        while wait_thr.alive?
          sleep(2)
          dots = (dots + 1) % 4
          elapsed = (Time.now - start_time).to_i
          print "\r\e[33mUnity 构建中#{'.' * dots}#{' ' * (3 - dots)} (已用时: #{elapsed}秒)\e[0m"
          $stdout.flush
        end
      end

      # 实时读取输出
      stdout_buffer = ""
      stderr_buffer = ""

      # 定义错误关键词模式(优化正则,避免重复)
      error_pattern = /error|exception|failed|Build completed with a result of 'Failed'/i
      warning_pattern = /warning|warn/i
      success_pattern = /Build completed successfully|Exiting batchmode successfully/i

      # 使用非阻塞方式读取输出
      while wait_thr.alive?
        # 检查是否有输出可读
        ready_streams = IO.select([stdout, stderr], nil, nil, 1)

        if ready_streams
          ready_streams[0].each do |stream|
            if line = stream.gets
              # 记录输出
              if stream == stdout
                stdout_buffer += line
              else
                stderr_buffer += line
              end
              last_output_time = Time.now

              # 根据调试级别和内容类型显示输出
              case debug_level
              when 'verbose'
                # 详细模式:显示所有输出
                puts line
              when 'quiet'
                # 安静模式:只显示错误
                if line.match?(error_pattern)
                  puts "\e[31m[错误] #{line.strip}\e[0m"
                end
              else # 'normal'
                # 正常模式:显示错误、警告和关键进度
                if line.match?(error_pattern)
                  puts "\n\e[31m[错误] #{line.strip}\e[0m"
                elsif line.match?(warning_pattern)
                  puts "\n\e[33m[警告] #{line.strip}\e[0m"
                elsif line.match?(success_pattern)
                  puts "\n\e[32m[成功] #{line.strip}\e[0m"
                elsif line.match?(/\d+%/) || line.match?(/Building|Compiling|Processing/i)
                  # 显示进度相关信息
                  print "\r\e[36m[进度] #{line.strip}\e[0m"
                  $stdout.flush
                end
              end
            end
          end
        end

      end

      # 读取剩余输出
      stdout_buffer += stdout.read
      stderr_buffer += stderr.read

      # 停止进度显示线程
      progress_thread.kill if progress_thread

      # 检查构建是否真的成功
      build_success = wait_thr.value.success?

      # 检查是否有构建错误的关键词
      if stdout_buffer.match?(/Build completed with a result of 'Failed'|Build completed with a result of 'Cancelled'|BuildPlayerWindow\+BuildMethod\+Invoke|error|Error|ERROR|exception|Exception|EXCEPTION|failed|Failed|FAILED/)
        build_success = false
        # puts "\n\e[31m检测到构建错误信息,构建可能失败\e[0m"
      end

      if build_success
        print "\r\e[32mUnity 构建完成!\e[0m\n"
        # 构建完成后检查并清理可能的 Unity 进程残留
        UnityProcHelper.cleanup_unity_processes_after_build(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
      else
        print "\r\e[31mUnity 构建失败!\e[0m\n"
        puts "\e[31m构建输出:\e[0m"
        puts stdout_buffer if !stdout_buffer.empty?
        puts "\e[31m错误输出:\e[0m"
        puts stderr_buffer if !stderr_buffer.empty?
        # 构建失败时也清理可能的进程残留
        UnityProcHelper.cleanup_unity_processes_after_build(unity_exe_full_path: unity_exe_full_path, project_path: project_path)
      end

      # 返回结果
      {
        success: build_success,
        stdout: stdout_buffer,
        stderr: stderr_buffer,
        exit_status: wait_thr.value.exitstatus,
        unity_version: UnityEnvHelper.extract_version_from_path(unity_exe_full_path)
      }
    end
  rescue => e
    # 停止进度显示线程
    progress_thread.kill if progress_thread
    print "\r\e[31mUnity 构建失败!\e[0m\n"
    raise e
  end
end