pindo 5.11.2 → 5.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,361 @@
1
+ require_relative '../pindo_task'
2
+ require_relative '../task_config'
3
+
4
+ module Pindo
5
+ module TaskSystem
6
+ class BuildTask < PindoTask
7
+ attr_reader :platform, :build_type, :output_path
8
+
9
+ def self.task_type
10
+ :build
11
+ end
12
+
13
+ def self.execution_mode
14
+ ExecutionMode::TYPE_EXCLUSIVE # 同类型串行(不能同时构建多个)
15
+ end
16
+
17
+ def self.execution_type
18
+ ExecutionType::ASYNC # 在新线程异步执行
19
+ end
20
+
21
+ def initialize(platform, options = {})
22
+ @platform = platform
23
+ @build_type = options[:build_type] || platform
24
+ @project_path = options[:project_path] || Dir.pwd
25
+ @build_config = options[:build_config] || {}
26
+ @output_path = nil
27
+
28
+ name = case platform
29
+ when 'ios', 'ipa'
30
+ "构建 IPA"
31
+ when 'android', 'apk'
32
+ "构建 APK"
33
+ when 'web', 'html'
34
+ "构建 HTML"
35
+ else
36
+ "构建 #{platform.upcase}"
37
+ end
38
+
39
+ super(name, options)
40
+ end
41
+
42
+ protected
43
+
44
+ def do_work
45
+ update_progress(0, "准备构建环境...")
46
+
47
+ case @platform
48
+ when 'ios', 'ipa'
49
+ build_ios
50
+ when 'android', 'apk'
51
+ build_android
52
+ when 'web', 'html'
53
+ build_web
54
+ else
55
+ raise "Unsupported platform: #{@platform}"
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def build_ios
62
+ update_progress(10, "配置 iOS 工程...")
63
+
64
+ # 获取 Bundle ID
65
+ mainapp_bundleid = @context[:bundle_id] || @build_config[:bundle_id]
66
+
67
+ unless mainapp_bundleid
68
+ raise "iOS Bundle ID 未指定"
69
+ end
70
+
71
+ update_progress(20, "准备编译参数...")
72
+
73
+ args_temp = []
74
+ args_temp << "--bundleid=#{mainapp_bundleid}"
75
+
76
+ # 不传递 upload 参数,只构建
77
+ if @context[:project_name]
78
+ args_temp << "--proj=#{@context[:project_name]}"
79
+ end
80
+
81
+ update_progress(30, "执行 iOS 构建...")
82
+
83
+ # 切换到 iOS 目录并执行构建
84
+ ios_dir = File.join(@project_path, "GoodPlatform", "iOS")
85
+ if File.directory?(ios_dir)
86
+ Dir.chdir(ios_dir) do
87
+ # 调用 iOS autobuild 命令(不包含上传)
88
+ require 'pindo/command/ios/autobuild'
89
+ Pindo::Command::Ios::Autobuild::run(args_temp)
90
+ end
91
+ else
92
+ # 如果没有 iOS 子目录,在当前目录执行
93
+ require 'pindo/command/ios/autobuild'
94
+ Pindo::Command::Ios::Autobuild::run(args_temp)
95
+ end
96
+
97
+ update_progress(90, "iOS 构建完成")
98
+
99
+ # TODO: 获取实际的输出路径
100
+ @output_path = find_ipa_output_path
101
+
102
+ update_progress(100, "IPA 构建成功")
103
+ {
104
+ success: true,
105
+ platform: 'ios',
106
+ output: @output_path
107
+ }
108
+ end
109
+
110
+ def build_android
111
+ update_progress(10, "配置 Android 工程...")
112
+
113
+ # 获取 Bundle Name
114
+ bundle_name = @context[:bundle_name] || @build_config[:bundle_name]
115
+
116
+ update_progress(20, "准备编译参数...")
117
+
118
+ args_temp = []
119
+
120
+ if bundle_name
121
+ args_temp << "--bundle_name=#{bundle_name}"
122
+ end
123
+
124
+ # 不传递 upload 参数,只构建
125
+ if @context[:project_name]
126
+ args_temp << "--proj=#{@context[:project_name]}"
127
+ end
128
+
129
+ update_progress(30, "执行 Android 构建...")
130
+
131
+ # 切换到 Android 目录并执行构建
132
+ android_dir = File.join(@project_path, "GoodPlatform", "Android")
133
+ if File.directory?(android_dir)
134
+ Dir.chdir(android_dir) do
135
+ # 调用 Android autobuild 命令(不包含上传)
136
+ require 'pindo/command/android/autobuild'
137
+ Pindo::Command::Android::Autobuild::run(args_temp)
138
+ end
139
+ else
140
+ # 如果没有 Android 子目录,在当前目录执行
141
+ require 'pindo/command/android/autobuild'
142
+ Pindo::Command::Android::Autobuild::run(args_temp)
143
+ end
144
+
145
+ update_progress(90, "Android 构建完成")
146
+
147
+ # TODO: 获取实际的输出路径
148
+ @output_path = find_apk_output_path
149
+
150
+ update_progress(100, "APK 构建成功")
151
+ {
152
+ success: true,
153
+ platform: 'android',
154
+ output: @output_path
155
+ }
156
+ end
157
+
158
+ def build_web
159
+ update_progress(10, "配置 Web 工程...")
160
+
161
+ # 获取 Bundle Name (Web 也可能需要)
162
+ bundle_name = @context[:bundle_name] || @build_config[:bundle_name]
163
+
164
+ update_progress(20, "准备编译参数...")
165
+
166
+ args_temp = []
167
+
168
+ if bundle_name
169
+ args_temp << "--bundle_name=#{bundle_name}"
170
+ end
171
+
172
+ # 不传递 upload 参数,只构建
173
+ if @context[:project_name]
174
+ args_temp << "--proj=#{@context[:project_name]}"
175
+ end
176
+
177
+ update_progress(30, "执行 Web 构建...")
178
+
179
+ # 切换到 Web 目录并执行构建
180
+ web_dir = File.join(@project_path, "GoodPlatform", "Web")
181
+ if File.directory?(web_dir)
182
+ Dir.chdir(web_dir) do
183
+ # TODO: 需要确认 Web autobuild 命令的正确模块
184
+ # 暂时使用 Android::Autobuild(根据原代码)
185
+ require 'pindo/command/android/autobuild'
186
+ Pindo::Command::Android::Autobuild::run(args_temp)
187
+ end
188
+ else
189
+ # 如果没有 Web 子目录,在当前目录执行
190
+ require 'pindo/command/android/autobuild'
191
+ Pindo::Command::Android::Autobuild::run(args_temp)
192
+ end
193
+
194
+ update_progress(90, "Web 构建完成")
195
+
196
+ # TODO: 获取实际的输出路径
197
+ @output_path = find_web_output_path
198
+
199
+ update_progress(100, "HTML 构建成功")
200
+ {
201
+ success: true,
202
+ platform: 'web',
203
+ output: @output_path
204
+ }
205
+ end
206
+
207
+ # 查找 IPA 输出路径
208
+ def find_ipa_output_path
209
+ # 记录查找前的时间,用于过滤旧文件
210
+ start_time = @started_at || Time.now - 60
211
+
212
+ # 使用配置中的搜索路径
213
+ search_paths = TaskConfig::BUILD_OUTPUT_PATTERNS[:ipa].map do |pattern|
214
+ File.join(@project_path, pattern)
215
+ end
216
+
217
+ ipa_files = []
218
+ search_paths.each do |pattern|
219
+ files = Dir.glob(pattern)
220
+ # 过滤出在构建开始后创建或修改的文件
221
+ recent_files = files.select { |f| File.mtime(f) >= start_time }
222
+ ipa_files.concat(recent_files)
223
+ end
224
+
225
+ if ipa_files.empty?
226
+ # 如果没有找到新文件,查找最近的 IPA 文件
227
+ search_paths.each do |pattern|
228
+ ipa_files.concat(Dir.glob(pattern))
229
+ end
230
+ end
231
+
232
+ # 返回最新的 IPA 文件
233
+ if ipa_files.any?
234
+ latest_ipa = ipa_files.max_by { |f| File.mtime(f) }
235
+ puts " 找到 IPA 文件: #{latest_ipa}"
236
+ latest_ipa
237
+ else
238
+ puts " 警告: 未找到 IPA 文件"
239
+ nil
240
+ end
241
+ end
242
+
243
+ # 查找 APK 输出路径
244
+ def find_apk_output_path
245
+ # 记录查找前的时间,用于过滤旧文件
246
+ start_time = @started_at || Time.now - 60
247
+
248
+ # 使用配置中的搜索路径
249
+ search_paths = []
250
+ search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:apk].map { |p| File.join(@project_path, p) })
251
+ search_paths.concat(TaskConfig::BUILD_OUTPUT_PATTERNS[:aab].map { |p| File.join(@project_path, p) })
252
+
253
+ package_files = []
254
+ search_paths.each do |pattern|
255
+ files = Dir.glob(pattern)
256
+ # 过滤出在构建开始后创建或修改的文件
257
+ recent_files = files.select { |f| File.mtime(f) >= start_time }
258
+ package_files.concat(recent_files)
259
+ end
260
+
261
+ if package_files.empty?
262
+ # 如果没有找到新文件,查找最近的包文件
263
+ search_paths.each do |pattern|
264
+ package_files.concat(Dir.glob(pattern))
265
+ end
266
+ end
267
+
268
+ # 过滤掉测试包和未签名包
269
+ package_files.reject! do |f|
270
+ basename = File.basename(f).downcase
271
+ TaskConfig::EXCLUDED_PATTERNS.any? { |pattern| basename.include?(pattern) }
272
+ end
273
+
274
+ # 返回最新的包文件(优先 AAB,其次 APK)
275
+ if package_files.any?
276
+ # 分类文件
277
+ aab_files = package_files.select { |f| f.end_with?(".aab") }
278
+ apk_files = package_files.select { |f| f.end_with?(".apk") }
279
+
280
+ # 优先返回 AAB 文件
281
+ latest_package = if aab_files.any?
282
+ aab_files.max_by { |f| File.mtime(f) }
283
+ elsif apk_files.any?
284
+ apk_files.max_by { |f| File.mtime(f) }
285
+ else
286
+ package_files.max_by { |f| File.mtime(f) }
287
+ end
288
+
289
+ puts " 找到 Android 包文件: #{latest_package}"
290
+ latest_package
291
+ else
292
+ puts " 警告: 未找到 APK/AAB 文件"
293
+ nil
294
+ end
295
+ end
296
+
297
+ # 查找 Web 输出路径
298
+ def find_web_output_path
299
+ # 记录查找前的时间,用于过滤旧文件
300
+ start_time = @started_at || Time.now - 60
301
+
302
+ # 可能的 Web 构建输出位置(通常是文件夹)
303
+ search_paths = [
304
+ File.join(@project_path, "build", "web"),
305
+ File.join(@project_path, "build", "webgl"),
306
+ File.join(@project_path, "output", "web"),
307
+ File.join(@project_path, "output", "webgl"),
308
+ File.join(@project_path, "GoodPlatform", "Web"),
309
+ File.join(@project_path, "GoodPlatform", "WebGL"),
310
+ File.join(@project_path, "Build", "WebGL"),
311
+ File.join(@project_path, "dist"),
312
+ File.join(@project_path, "public")
313
+ ]
314
+
315
+ # 查找存在且最近修改的文件夹
316
+ web_dirs = search_paths.select do |path|
317
+ File.directory?(path) && File.mtime(path) >= start_time
318
+ end
319
+
320
+ if web_dirs.empty?
321
+ # 如果没有找到新文件夹,查找包含 index.html 的文件夹
322
+ web_dirs = search_paths.select do |path|
323
+ File.directory?(path) && File.exist?(File.join(path, "index.html"))
324
+ end
325
+ end
326
+
327
+ # 如果还是没找到,查找 zip 文件
328
+ if web_dirs.empty?
329
+ zip_patterns = [
330
+ File.join(@project_path, "build", "**", "*web*.zip"),
331
+ File.join(@project_path, "output", "**", "*web*.zip"),
332
+ File.join(@project_path, "**", "*webgl*.zip")
333
+ ]
334
+
335
+ zip_files = []
336
+ zip_patterns.each do |pattern|
337
+ files = Dir.glob(pattern)
338
+ recent_files = files.select { |f| File.mtime(f) >= start_time }
339
+ zip_files.concat(recent_files)
340
+ end
341
+
342
+ if zip_files.any?
343
+ latest_zip = zip_files.max_by { |f| File.mtime(f) }
344
+ puts " 找到 Web 压缩包: #{latest_zip}"
345
+ return latest_zip
346
+ end
347
+ end
348
+
349
+ # 返回最新的 Web 目录
350
+ if web_dirs.any?
351
+ latest_dir = web_dirs.max_by { |d| File.mtime(d) }
352
+ puts " 找到 Web 输出目录: #{latest_dir}"
353
+ latest_dir
354
+ else
355
+ puts " 警告: 未找到 Web 输出文件或目录"
356
+ nil
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end
@@ -0,0 +1,201 @@
1
+ require_relative '../pindo_task'
2
+
3
+ module Pindo
4
+ module TaskSystem
5
+ class UnityExportTask < PindoTask
6
+ attr_reader :platform, :unity_project_path, :export_path
7
+
8
+ def self.task_type
9
+ :unity_export
10
+ end
11
+
12
+ def self.execution_mode
13
+ ExecutionMode::EXCLUSIVE # Unity 必须独占执行
14
+ end
15
+
16
+ def self.execution_type
17
+ ExecutionType::SYNC # 在主线程同步执行(Unity 不能多进程)
18
+ end
19
+
20
+ def initialize(platform, options = {})
21
+ @platform = platform
22
+ @unity_project_path = options[:project_path] || Dir.pwd
23
+ @unity_exe_path = options[:unity_exe_path]
24
+ @unity_helper = options[:unity_helper]
25
+ @is_library_mode = options[:library_mode] || false
26
+ @export_path = nil
27
+
28
+ name = case platform
29
+ when 'ios', 'ipa'
30
+ "Unity 导出 iOS"
31
+ when 'android', 'apk'
32
+ "Unity 导出 Android"
33
+ when 'web', 'html'
34
+ "Unity 导出 WebGL"
35
+ else
36
+ "Unity 导出 #{platform.upcase}"
37
+ end
38
+
39
+ super(name, options)
40
+ end
41
+
42
+ def validate
43
+ # 验证 Unity 工程是否存在
44
+ unless @unity_helper
45
+ @unity_helper = Pindo::Client::UnityHelper.share_instance
46
+ end
47
+
48
+ unless @unity_helper.unity_project?(@unity_project_path)
49
+ @error = "当前目录不是 Unity 工程:#{@unity_project_path}"
50
+ return false
51
+ end
52
+
53
+ true
54
+ end
55
+
56
+ protected
57
+
58
+ def do_work
59
+ update_progress(0, "启动 Unity...")
60
+
61
+ # 确保 Unity helper 已初始化
62
+ @unity_helper ||= Pindo::Client::UnityHelper.share_instance
63
+
64
+ # 获取 Unity 版本
65
+ update_progress(10, "检查 Unity 版本...")
66
+ project_unity_version = @unity_helper.get_unity_version(@unity_project_path)
67
+
68
+ # 查找 Unity 执行路径
69
+ unless @unity_exe_path
70
+ update_progress(20, "查找 Unity 路径...")
71
+ @unity_exe_path = @unity_helper.find_unity_path(
72
+ project_unity_version: project_unity_version,
73
+ force_change_version: false
74
+ )
75
+ end
76
+
77
+ if @unity_exe_path.nil? || @unity_exe_path.empty?
78
+ raise "无法找到 Unity 执行路径"
79
+ end
80
+
81
+ update_progress(30, "准备 Unity 导出...")
82
+
83
+ # 显示 Unity 信息
84
+ puts " Unity 版本: #{project_unity_version}"
85
+ puts " Unity 路径: #{@unity_exe_path}"
86
+
87
+ # 执行 Unity 导出
88
+ case @platform
89
+ when 'ios', 'ipa'
90
+ export_ios
91
+ when 'android', 'apk'
92
+ export_android
93
+ when 'web', 'html'
94
+ export_web
95
+ else
96
+ raise "Unsupported platform: #{@platform}"
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def export_ios
103
+ update_progress(40, "执行 Unity iOS 导出...")
104
+
105
+ platform = 'iOS'
106
+ result = execute_unity_build(platform)
107
+
108
+ update_progress(90, "Unity iOS 导出完成")
109
+
110
+ # 设置导出路径
111
+ @export_path = File.join(@unity_project_path, "GoodPlatform", "iOS")
112
+
113
+ update_progress(100, "Unity iOS 导出成功")
114
+ {
115
+ success: true,
116
+ platform: 'ios',
117
+ export_path: @export_path,
118
+ unity_result: result
119
+ }
120
+ end
121
+
122
+ def export_android
123
+ update_progress(40, "执行 Unity Android 导出...")
124
+
125
+ platform = 'Android'
126
+ result = execute_unity_build(platform)
127
+
128
+ update_progress(90, "Unity Android 导出完成")
129
+
130
+ # 设置导出路径
131
+ @export_path = File.join(@unity_project_path, "GoodPlatform", "Android")
132
+
133
+ update_progress(100, "Unity Android 导出成功")
134
+ {
135
+ success: true,
136
+ platform: 'android',
137
+ export_path: @export_path,
138
+ unity_result: result
139
+ }
140
+ end
141
+
142
+ def export_web
143
+ update_progress(40, "执行 Unity WebGL 导出...")
144
+
145
+ platform = 'WebGL'
146
+ result = execute_unity_build(platform)
147
+
148
+ update_progress(90, "Unity WebGL 导出完成")
149
+
150
+ # 设置导出路径
151
+ @export_path = File.join(@unity_project_path, "GoodPlatform", "Web")
152
+
153
+ update_progress(100, "Unity WebGL 导出成功")
154
+ {
155
+ success: true,
156
+ platform: 'web',
157
+ export_path: @export_path,
158
+ unity_result: result
159
+ }
160
+ end
161
+
162
+ def execute_unity_build(platform)
163
+ # 检查 Unity 进程
164
+ update_progress(50, "检查 Unity 进程...")
165
+ @unity_helper.check_unity_processes(
166
+ unity_exe_full_path: @unity_exe_path,
167
+ project_path: @unity_project_path
168
+ )
169
+
170
+ # 执行构建
171
+ update_progress(60, "开始 Unity 构建...")
172
+ begin
173
+ result = @unity_helper.build_project(
174
+ unity_exe_full_path: @unity_exe_path,
175
+ project_path: @unity_project_path,
176
+ platform: platform,
177
+ isLibrary: @is_library_mode,
178
+ indexNo: nil
179
+ )
180
+
181
+ # 清理 Unity 进程
182
+ update_progress(85, "清理 Unity 进程...")
183
+ @unity_helper.cleanup_unity_processes_after_build(
184
+ unity_exe_full_path: @unity_exe_path,
185
+ project_path: @unity_project_path
186
+ )
187
+
188
+ result
189
+ rescue => e
190
+ # 确保清理进程
191
+ @unity_helper.cleanup_unity_processes_after_build(
192
+ unity_exe_full_path: @unity_exe_path,
193
+ project_path: @unity_project_path
194
+ ) rescue nil
195
+
196
+ raise e
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end