Module: Pindo::BaseAndroidHelper

Included in:
AndroidBuildConfigHelper, AndroidBuildHelper, ApkHelper, BuildHelper, GPComplianceHelper, GradleHelper
Defined in:
lib/pindo/module/android/base_helper.rb

Instance Method Summary collapse

Instance Method Details

#build_jdk_path_from_unity_root(unity_path) ⇒ Object

根据 Unity 根路径构建 JDK 路径



434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/pindo/module/android/base_helper.rb', line 434

def build_jdk_path_from_unity_root(unity_path)
  # 1. 全局 Unity 路径
  if unity_path == "/Applications/Unity"
    return "/Applications/Unity/PlaybackEngines/AndroidPlayer/OpenJDK"
  end
  
  # 2. Unity Hub 路径(包含版本号)
  if unity_path.include?("/Unity/Hub/Editor/")
    # 检查是否有 PlaybackEngines 目录
    playback_engines_path = File.join(unity_path, "PlaybackEngines")
    if File.directory?(playback_engines_path)
      return File.join(playback_engines_path, "AndroidPlayer", "OpenJDK")
    end
  end
  
  # 3. 其他 Unity 版本路径
  if unity_path.include?("/Unity/") && !unity_path.include?("/Hub/")
    # 检查是否有 PlaybackEngines 目录
    playback_engines_path = File.join(unity_path, "PlaybackEngines")
    if File.directory?(playback_engines_path)
      return File.join(playback_engines_path, "AndroidPlayer", "OpenJDK")
    end
  end
  
  # 4. 默认尝试
  File.join(unity_path, "PlaybackEngines", "AndroidPlayer", "OpenJDK")
end

#check_and_replace_unity_jdk(jdk_path) ⇒ Object

检查并替换 Unity Editor 中的 JDK



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/pindo/module/android/base_helper.rb', line 463

def check_and_replace_unity_jdk(jdk_path)
  begin
    # 检查当前 JDK 版本
    java_bin = File.join(jdk_path, "bin", "java")
    if File.exist?(java_bin)
      version_output = `"#{java_bin}" -version 2>&1`
      if version_output =~ /version "(\d+)/
        current_version = $1.to_i
        puts "Unity Editor JDK 当前版本: #{current_version}"
        
        if current_version >= 11
          puts "\e[32m✓ Unity Editor JDK 版本 #{current_version} 符合要求\e[0m"
          return false
        else
          puts "\e[31m✗ Unity Editor JDK 版本 #{current_version} 不符合要求,需要 Java 11+\e[0m"
          return replace_unity_jdk_with_java11(jdk_path)
        end
      end
    else
      puts "\e[31m✗ Unity Editor JDK 中未找到 java 可执行文件\e[0m"
      return replace_unity_jdk_with_java11(jdk_path)
    end
    
    return false
  rescue => e
    puts "\e[31m检查 Unity Editor JDK 时出错: #{e.message}\e[0m"
    return false
  end
end

#check_and_update_unity_jdkObject

检查并更新 Unity Editor 中的 JDK 版本



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
# File 'lib/pindo/module/android/base_helper.rb', line 294

def check_and_update_unity_jdk
  return false unless RbConfig::CONFIG['host_os'] =~ /darwin/
  
  puts "\e[36m=== Unity Editor JDK 版本检测 ===\e[0m"
  
  # 查找 Unity Editor 安装路径
  unity_paths = find_unity_editor_paths
  if unity_paths.empty?
    puts "\e[33m未找到 Unity Editor 安装路径\e[0m"
    return false
  end
  
  updated_any = false
  unity_paths.each do |unity_path|
    # 根据不同的 Unity 路径结构构建 JDK 路径
    jdk_path = build_jdk_path_from_unity_root(unity_path)
    
    if File.directory?(jdk_path)
      puts "检查 Unity Editor JDK: #{jdk_path}"
      if check_and_replace_unity_jdk(jdk_path)
        updated_any = true
      end
    else
      # OpenJDK 目录不存在,尝试创建并安装 Java 11
      puts "OpenJDK 目录不存在: #{jdk_path}"
      puts "正在创建 OpenJDK 目录并安装 Java 11..."
      
      if create_and_install_openjdk(jdk_path)
        puts "\e[32m✓ OpenJDK 目录创建并安装 Java 11 成功\e[0m"
        updated_any = true
      else
        puts "\e[31m✗ OpenJDK 目录创建并安装 Java 11 失败\e[0m"
      end
    end
  end
  
  updated_any
end

#create_and_install_openjdk(jdk_path) ⇒ Object

创建 OpenJDK 目录并安装 Java 11



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/pindo/module/android/base_helper.rb', line 334

def create_and_install_openjdk(jdk_path)
  begin
    # 创建 OpenJDK 目录(使用 sudo 权限)
    puts "\e[33m需要管理员权限来创建 OpenJDK 目录,请输入密码...\e[0m"
    if system("sudo mkdir -p \"#{jdk_path}\"")
      puts "创建 OpenJDK 目录: #{jdk_path}"
    else
      puts "\e[31m✗ 创建 OpenJDK 目录失败\e[0m"
      return false
    end
    
    # 查找或安装 Java 11
    java11_path = find_java11_installation
    unless java11_path && File.directory?(java11_path)
      puts "\e[33m未找到系统中的 Java 11 安装,正在自动下载...\e[0m"
      
      # 自动下载并安装 Java 11
      if install_java_11
        java11_path = find_java11_installation
        unless java11_path && File.directory?(java11_path)
          puts "\e[31m✗ Java 11 自动安装失败\e[0m"
          return false
        end
        puts "\e[32m✓ Java 11 自动安装成功\e[0m"
      else
        puts "\e[31m✗ Java 11 自动安装失败\e[0m"
        return false
      end
    end
    
    puts "找到 Java 11 安装路径: #{java11_path}"
    
    # 复制 Java 11 到 OpenJDK 目录(使用 sudo 权限)
    puts "正在复制 Java 11 到 OpenJDK 目录..."
    if system("sudo cp -R \"#{java11_path}\"/* \"#{jdk_path}\"/")
      puts "\e[32m✓ Java 11 复制成功\e[0m"
      
      # 验证安装
      java_bin = File.join(jdk_path, "bin", "java")
      if File.exist?(java_bin)
        version_output = `"#{java_bin}" -version 2>&1`
        if version_output =~ /version "(\d+)/
          version = $1.to_i
          if version >= 11
            puts "\e[32m✓ Unity Editor JDK 安装成功,版本: #{version}\e[0m"
            return true
          else
            puts "\e[31m✗ 安装的 JDK 版本 #{version} 不符合要求\e[0m"
            return false
          end
        end
      end
      
      return true
    else
      puts "\e[31m✗ Java 11 复制失败\e[0m"
      return false
    end
    
  rescue => e
    puts "\e[31m创建并安装 OpenJDK 时出错: #{e.message}\e[0m"
    return false
  end
end

#ensure_java_version_complianceObject

检查并确保 Java 版本符合要求 (Java 11+)



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
# File 'lib/pindo/module/android/base_helper.rb', line 91

def ensure_java_version_compliance
  puts "\e[36m=== Java 版本检测 ===\e[0m"
  
  # 检查 Unity Editor 中的 JDK 版本(仅检测,不自动替换)
  # if check_and_update_unity_jdk
  #   puts "\e[32m✓ Unity Editor JDK 已更新为 Java 11+\e[0m"
  # end
  
  # 仅检测Unity Editor JDK版本,不进行替换
  unity_root_paths = find_unity_editor_paths
  if unity_root_paths.any?
    puts "\e[36m=== Unity Editor JDK 版本检测 ===\e[0m"
    unity_root_paths.each do |unity_root|
      # 构建JDK路径
      jdk_path = build_jdk_path_from_unity_root(unity_root)
      puts "\e[33m[DEBUG] 检查Unity路径: #{unity_root} -> JDK路径: #{jdk_path}\e[0m" if ENV['PINDO_DEBUG']
      
      if File.exist?(jdk_path)
        java_cmd = File.join(jdk_path, "bin", "java")
        if File.exist?(java_cmd)
          version_output = `"#{java_cmd}" -version 2>&1`
          if version_output =~ /version "(\d+)/
            major_version = $1.to_i
            if major_version >= 11
              puts "\e[32m✓ Unity Editor JDK 版本 #{major_version} 符合要求: #{jdk_path}\e[0m"
            else
              puts "\e[33m⚠ Unity Editor JDK 版本 #{major_version} 不符合要求 (需要 Java 11+): #{jdk_path}\e[0m"
            end
          else
            puts "\e[33m⚠ 无法解析Unity Editor JDK版本: #{jdk_path}\e[0m"
          end
        else
          puts "\e[33m⚠ Unity Editor JDK中未找到java命令: #{jdk_path}\e[0m"
        end
      else
        puts "\e[33m⚠ Unity Editor JDK路径不存在: #{jdk_path}\e[0m"
      end
    end
  else
    puts "\e[33m⚠ 未找到Unity Editor安装路径\e[0m"
  end
  
  # 查找当前可用的 Java 命令
  current_java = find_java_command
  puts "当前 Java 命令: #{current_java}"
  
  # 验证当前 Java 版本
  if verify_java_version(current_java)
    version_output = `"#{current_java}" -version 2>&1`
    if version_output =~ /version "(\d+)/
      major_version = $1.to_i
      puts "\e[32m✓ Java 版本 #{major_version} 符合要求 (需要 Java 11+)\e[0m"
      return true
    end
  end
  
  puts "\e[31m✗ 当前 Java 版本不符合要求,需要 Java 11+\e[0m"
  puts "\e[33m请手动安装 Java 11 或更高版本:\e[0m"
  puts "  • macOS: brew install openjdk@11"
  puts "  • 或下载: https://adoptium.net/"
  puts "  • 或使用 SDKMAN: sdk install java 11.0.21-tem"
  
  # 注释掉自动安装 Java 11 的逻辑
  # puts "\e[33m正在尝试自动安装和配置 Java 11...\e[0m"
  # if install_java_11
  #   puts "\e[32m✓ Java 11 安装成功\e[0m"
  #   return true
  # else
  #   puts "\e[31m✗ Java 11 自动安装失败\e[0m"
  #   puts "\e[33m请手动安装 Java 11 或更高版本:\e[0m"
  #   puts "  • macOS: brew install openjdk@11"
  #   puts "  • 或下载: https://adoptium.net/"
  #   puts "  • 或使用 SDKMAN: sdk install java 11.0.21-tem"
  #   return false
  # end
  
  return false
end

#extract_config_block_groovy(signing_configs_block, config_type) ⇒ Object



801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
# File 'lib/pindo/module/android/base_helper.rb', line 801

def extract_config_block_groovy(signing_configs_block, config_type)
  # 支持 xxx { ... }
  all_blocks = signing_configs_block.scan(/([A-Za-z0-9_]+)\s*\{([\s\S]*?)^\s*\}/m)
  priority_names = [config_type, "Android_Sign", "release", "debug"]
  config_block = nil
  priority_names.each do |name|
    block = all_blocks.find { |n, b| n == name && b.include?("storeFile") }
    if block
      config_block = block[1]
      break
    end
  end
  if config_block.nil?
    all_blocks.each do |name, block|
      if block.include?("storeFile")
        config_block = block
        break
      end
    end
  end
  config_block
end

#extract_config_block_kts(signing_configs_block, config_type) ⇒ Object



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
# File 'lib/pindo/module/android/base_helper.rb', line 776

def extract_config_block_kts(signing_configs_block, config_type)
  # 支持 create("xxx") { ... }
  all_blocks = signing_configs_block.scan(/create\(["']?([A-Za-z0-9_]+)["']?\)\s*\{([\s\S]*?)^\s*\}/m)
  # 优先 release/debug/Android_Sign/自定义名
  priority_names = [config_type, "Android_Sign", "release", "debug"]
  config_block = nil
  priority_names.each do |name|
    block = all_blocks.find { |n, b| n == name && b.include?("storeFile") }
    if block
      config_block = block[1]
      break
    end
  end
  # 没找到就取第一个有 storeFile 的块
  if config_block.nil?
    all_blocks.each do |name, block|
      if block.include?("storeFile")
        config_block = block
        break
      end
    end
  end
  config_block
end

#extract_keystore_fields_common(fields, ext_values) ⇒ Object

通用字段递归解析



836
837
838
# File 'lib/pindo/module/android/base_helper.rb', line 836

def extract_keystore_fields_common(fields, ext_values)
  fields.map { |val| extract_var_or_value(val, ext_values) }
end

#extract_keystore_fields_groovy(config_block, ext_values) ⇒ Object



840
841
842
843
844
845
846
847
# File 'lib/pindo/module/android/base_helper.rb', line 840

def extract_keystore_fields_groovy(config_block, ext_values)
  # 只处理 groovy 语法
  store_file = config_block[/storeFile\s+file\(["']?([^\)"']+)["']?\)/, 1]
  store_password = config_block[/storePassword\s+["']?([^\)"']+)["']?/, 1]
  key_alias = config_block[/keyAlias\s+["']?([^\)"']+)["']?/, 1]
  key_password = config_block[/keyPassword\s+["']?([^\)"']+)["']?/, 1]
  extract_keystore_fields_common([store_file, store_password, key_alias, key_password], ext_values)
end

#extract_keystore_fields_kts(config_block, ext_values) ⇒ Object



849
850
851
852
853
854
855
856
857
# File 'lib/pindo/module/android/base_helper.rb', line 849

def extract_keystore_fields_kts(config_block, ext_values)
  # 只处理 kts 语法
  store_file = config_block[/storeFile\s*=\s*file\(["']?([^\)"']+)["']?\)/, 1] ||
               config_block[/storeFile\s*=\s*rootProject\.file\(["']?([^\)"']+)["']?\)/, 1]
  store_password = config_block[/storePassword\s*=\s*["']?([^\)"']+)["']?/, 1]
  key_alias = config_block[/keyAlias\s*=\s*["']?([^\)"']+)["']?/, 1]
  key_password = config_block[/keyPassword\s*=\s*["']?([^\)"']+)["']?/, 1]
  extract_keystore_fields_common([store_file, store_password, key_alias, key_password], ext_values)
end

#extract_signing_configs_groovy(content) ⇒ Object



770
771
772
773
774
# File 'lib/pindo/module/android/base_helper.rb', line 770

def extract_signing_configs_groovy(content)
  # 提取 signingConfigs { ... },支持多层嵌套
  match = content.match(/signingConfigs\s*\{([\s\S]*?)^\}/m)
  match ? match[1] : nil
end

#extract_signing_configs_kts(content) ⇒ Object

— 工具方法 —



764
765
766
767
768
# File 'lib/pindo/module/android/base_helper.rb', line 764

def extract_signing_configs_kts(content)
  # 提取 signingConfigs { ... },支持多层嵌套
  match = content.match(/signingConfigs\s*\{([\s\S]*?)^\}/m)
  match ? match[1] : nil
end

#extract_var_or_value(val, ext_values, depth = 0) ⇒ Object



859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
# File 'lib/pindo/module/android/base_helper.rb', line 859

def extract_var_or_value(val, ext_values, depth=0)
  return nil if val.nil? || val.empty? || depth > 5
  # 匹配 ${KEY}、$KEY、"${KEY}"、'${KEY}'
  if val =~ /^\$\{?([A-Za-z0-9_]+)\}?$/
    key = $1
    v = ext_values[key]
    v ? extract_var_or_value(v, ext_values, depth+1) : nil
  elsif val =~ /^"?\$\{([A-Za-z0-9_]+)\}"?$/
    key = $1
    v = ext_values[key]
    v ? extract_var_or_value(v, ext_values, depth+1) : nil
  else
    val
  end
end

#find_android_subproject(project_path) ⇒ Object



948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
# File 'lib/pindo/module/android/base_helper.rb', line 948

def find_android_subproject(project_path)
  android_dir = File.join(project_path, "Unity")
  return nil unless File.directory?(android_dir)

  main_module = get_main_module(android_dir)
  return nil unless main_module

  src_main = File.join(main_module, "src/main")
  return nil unless File.directory?(src_main)

  manifest = File.join(src_main, "AndroidManifest.xml")
  return nil unless File.exist?(manifest)

  android_dir
end

#find_java11_installationObject

查找系统中的 Java 11 安装



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/pindo/module/android/base_helper.rb', line 572

def find_java11_installation
  # 检查常见的 Java 11 安装路径
  java11_paths = [
    # Homebrew 安装路径
    "/opt/homebrew/opt/openjdk@11",
    "/usr/local/opt/openjdk@11",
    # SDKMAN 安装路径
    File.expand_path("~/.sdkman/candidates/java/11.0.21-tem"),
    File.expand_path("~/.sdkman/candidates/java/11.0.20-tem"),
    # 系统安装路径
    "/Library/Java/JavaVirtualMachines/openjdk-11.jdk/Contents/Home",
    "/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home",
    # Pindo 安装的 Java 11
    File.expand_path("~/.pindo/java11")
  ]
  
  java11_paths.each do |path|
    if File.directory?(path)
      java_bin = File.join(path, "bin", "java")
      if File.exist?(java_bin)
        # 验证版本
        version_output = `"#{java_bin}" -version 2>&1`
        if version_output =~ /version "(\d+)/
          version = $1.to_i
          if version >= 11
            return path
          end
        end
      end
    end
  end
  
  nil
end

#find_java_commandObject

查找正确的 Java 命令 (Java 11+)



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
# File 'lib/pindo/module/android/base_helper.rb', line 36

def find_java_command
  # 检查 JAVA_HOME 环境变量
  if ENV['JAVA_HOME'] && File.exist?(File.join(ENV['JAVA_HOME'], 'bin', 'java'))
    java_path = File.join(ENV['JAVA_HOME'], 'bin', 'java')
    # 验证 Java 版本
    if verify_java_version(java_path)
      return java_path
    end
  end

  # 尝试常见的 Java 路径
  common_java_paths = [
    '/usr/libexec/java_home',  # macOS
    '/usr/bin/java',
    '/usr/local/bin/java',
    '/opt/homebrew/bin/java',  # macOS Homebrew
    '/usr/local/opt/openjdk/bin/java'  # macOS Homebrew OpenJDK
  ]

  common_java_paths.each do |java_path|
    if File.exist?(java_path)
      # 对于 macOS 的 java_home,需要特殊处理
      if java_path.include?('java_home')
        java_home_output = `#{java_path} 2>/dev/null`.strip
        if !java_home_output.empty?
          actual_java_path = File.join(java_home_output, 'bin', 'java')
          if File.exist?(actual_java_path) && verify_java_version(actual_java_path)
            return actual_java_path
          end
        end
      elsif verify_java_version(java_path)
        return java_path
      end
    end
  end

  # 如果都找不到,返回默认的 java 命令
  'java'
end

#find_unity_editor_pathsObject

查找 Unity Editor 安装路径



400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/pindo/module/android/base_helper.rb', line 400

def find_unity_editor_paths
  unity_paths = []
  
  # 1. 检查全局 Unity PlaybackEngines 路径(最常见)
  global_playback_engines = "/Applications/Unity/PlaybackEngines"
  if File.directory?(global_playback_engines)
    unity_paths << "/Applications/Unity"
  end
  
  # 2. 检查 Unity Hub 中的多个版本(支持 2021.3.* 版本范围)
  unity_hub_patterns = [
    "/Applications/Unity/Hub/Editor/2021.3.*/PlaybackEngines"
  ]
  
  unity_hub_patterns.each do |pattern|
    Dir.glob(pattern).each do |playback_engines_path|
      # Unity Hub 版本:/Applications/Unity/Hub/Editor/2022.3.15f1/PlaybackEngines
      unity_root = playback_engines_path.gsub("/PlaybackEngines", "")
      unity_paths << unity_root
    end
  end
  
  # 3. 检查其他常见的 Unity 安装路径
  other_pattern = "/Applications/Unity/*/PlaybackEngines"
  Dir.glob(other_pattern).each do |playback_engines_path|
    # 其他 Unity 版本:/Applications/Unity/2022.3.15f1/PlaybackEngines
    unity_root = playback_engines_path.gsub("/PlaybackEngines", "")
    unity_paths << unity_root
  end
  
  unity_paths.uniq
end

#fix_store_file_path(store_file, project_path) ⇒ Object



884
885
886
887
888
889
890
891
892
# File 'lib/pindo/module/android/base_helper.rb', line 884

def fix_store_file_path(store_file, project_path)
  return nil if store_file.nil? || store_file.empty?
  # 兼容 $rootDir、${rootDir}、project.rootDir、rootProject.file
  store_file = store_file.gsub(/^\$?\{?rootDir\}?\/?/, project_path + "/")
  store_file = store_file.gsub(/^project\.rootDir\/?/, project_path + "/")
  store_file = store_file.gsub(/^rootProject\.file\(['"](.+?)['"]\)/, project_path + '/\\1')
  store_file = File.expand_path(store_file, project_path) unless File.exist?(store_file)
  store_file
end

#get_build_toolsObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/pindo/module/android/base_helper.rb', line 4

def get_build_tools
  # 获取 gem 资源文件路径
  
  pindo_dir ||= File.expand_path(ENV['PINDO_DIR'] || '~/.pindo')
  pindo_common_configdir ||= File.join(pindo_dir, "pindo_common_config")
  tools_dir = File.join(pindo_common_configdir, 'android_tools')

  # 如果开发环境找不到,尝试在 gem 安装目录找
  unless File.directory?(tools_dir)
    raise "缺少必要的构建工具: #{tools_dir},请执行pindo setup更新pindo配置"
  end

  # 检查必要的工具是否存在
  required_tools = {
    bundle_tool: 'bundletool.jar',
    gradlew: 'gradlew',
    gradle_wrapper: 'gradle-wrapper.jar'
  }

  tools = {}
  required_tools.each do |key, filename|
    path = File.join(tools_dir, filename)
    unless File.exist?(path)
      raise "缺少必要的构建工具: #{filename},请执行pindo setup更新pindo配置"
    end
    tools[key] = path
  end

  tools
end

#get_ext_values(project_path) ⇒ Object

新增方法:从 ext 配置文件中获取变量值



895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
# File 'lib/pindo/module/android/base_helper.rb', line 895

def get_ext_values(project_path)
  ext_values = {}

  ext_files = [
    File.join(project_path, "buildScripts/cfg/ext_signing.gradle"),
    File.join(project_path, "buildScripts/cfg/ext_signing.gradle.kts"),
    File.join(project_path, "gradle.properties"),
    File.join(project_path, "local.properties")
  ]

  ext_files.each do |file_path|
    next unless File.exist?(file_path)

    content = File.read(file_path)

    # 支持 ext.KEY = "VALUE"
    content.scan(/ext\.(\w+)\s*=\s*["']([^"']+)["']/m).each do |key, value|
      ext_values[key] = value
    end

    # 支持 ext["KEY"] = "VALUE"
    content.scan(/ext\[\s*["'](\w+)["']\s*\]\s*=\s*["']([^"']+)["']/m).each do |key, value|
      ext_values[key] = value
    end

    # 支持 KEY = "VALUE"
    content.scan(/^(\w+)\s*=\s*["']([^"']+)["']/m).each do |key, value|
      ext_values[key] = value
    end
  end

  ext_values
end

#get_keystore_config(project_path, debug = false) ⇒ Object



678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
# File 'lib/pindo/module/android/base_helper.rb', line 678

def get_keystore_config(project_path, debug = false)
  main_module = get_main_module(project_path)
  return nil unless main_module

  gradle_kts_path = File.join(main_module, "build.gradle.kts")
  gradle_path     = File.join(main_module, "build.gradle")

  if File.exist?(gradle_kts_path)
    puts "KTS 项目,读取 #{File.basename(gradle_kts_path)} 文件"
    get_keystore_config_kts(gradle_kts_path, project_path, debug)
  elsif File.exist?(gradle_path)
    puts "Groovy 项目,读取 #{File.basename(gradle_path)} 文件"
    get_keystore_config_groovy(gradle_path, project_path, debug)
  else
    puts "未找到 build.gradle 或 build.gradle.kts 文件"
    nil
  end
end

#get_keystore_config_groovy(gradle_path, project_path, debug) ⇒ Object

处理 Groovy DSL (build.gradle)



731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# File 'lib/pindo/module/android/base_helper.rb', line 731

def get_keystore_config_groovy(gradle_path, project_path, debug)
  content = File.read(gradle_path)
  puts "读取 #{File.basename(gradle_path)} 文件"
  signing_configs_block = extract_signing_configs_groovy(content)
  # puts "==== signingConfigs block ===="
  # puts signing_configs_block
  # puts "==============================" if signing_configs_block
  return nil unless signing_configs_block

  config_type = debug ? 'debug' : 'release'
  config_block = extract_config_block_groovy(signing_configs_block, config_type)
  # puts "==== config_block ===="
  # puts config_block
  # puts "=====================" if config_block
  return nil unless config_block

  config_block = remove_groovy_comments(config_block)
  ext_values = get_ext_values(project_path)
  store_file, store_password, key_alias, key_password = extract_keystore_fields_groovy(config_block, ext_values)
  store_file = fix_store_file_path(store_file, project_path)
  keystore_config = {
    store_file: store_file,
    store_password: store_password,
    key_alias: key_alias,
    key_password: key_password
  }
  if keystore_config.values.any?(&:nil?)
    puts "警告: 部分 keystore 配置为 nil: #{keystore_config}"
  end
  keystore_config
end

#get_keystore_config_kts(gradle_kts_path, project_path, debug) ⇒ Object

处理 Kotlin DSL (build.gradle.kts)



698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/pindo/module/android/base_helper.rb', line 698

def get_keystore_config_kts(gradle_kts_path, project_path, debug)
  content = File.read(gradle_kts_path)
  puts "读取 #{File.basename(gradle_kts_path)} 文件"
  signing_configs_block = extract_signing_configs_kts(content)
  # puts "==== signingConfigs block ===="
  # puts signing_configs_block
  # puts "==============================" if signing_configs_block
  return nil unless signing_configs_block

  config_type = debug ? 'debug' : 'release'
  config_block = extract_config_block_kts(signing_configs_block, config_type)
  # puts "==== config_block ===="
  # puts config_block
  # puts "=====================" if config_block
  return nil unless config_block

  config_block = remove_kts_comments(config_block)
  ext_values = get_ext_values(project_path)
  store_file, store_password, key_alias, key_password = extract_keystore_fields_kts(config_block, ext_values)
  store_file = fix_store_file_path(store_file, project_path)
  keystore_config = {
    store_file: store_file,
    store_password: store_password,
    key_alias: key_alias,
    key_password: key_password
  }
  if keystore_config.values.any?(&:nil?)
    puts "警告: 部分 keystore 配置为 nil: #{keystore_config}"
  end
  keystore_config
end

#get_main_module(project_path) ⇒ Object

xml_content = File.read(strings_xml_path)

doc = Nokogiri::XML(xml_content)
doc.at_xpath("//string[@name='app_name']")&.text

end



640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/pindo/module/android/base_helper.rb', line 640

def get_main_module(project_path)
  settings_gradle_path = File.join(project_path, "settings.gradle")
  settings_gradle_kts_path = File.join(project_path, "settings.gradle.kts")
  
  # 优先使用 settings.gradle.kts,如果不存在则使用 settings.gradle
  if File.exist?(settings_gradle_kts_path)
    settings_gradle_path = settings_gradle_kts_path
  elsif !File.exist?(settings_gradle_path)
    return nil
  end

  content = File.read(settings_gradle_path)
  # 兼容 include ':app' 和 include(":app")
  modules = content.scan(/include\s*\(?\s*['\"]?([:\w\-]+)['\"]?\s*\)?/).flatten

  main_module = modules.find do |m|
    module_name = m.split(':').last
    gradle_path = File.join(project_path, module_name, "build.gradle")
    gradle_kts_path = File.join(project_path, module_name, "build.gradle.kts")
    
    # 优先使用 build.gradle.kts,如果不存在则使用 build.gradle
    if File.exist?(gradle_kts_path)
      gradle_path = gradle_kts_path
    end
    
    if File.exist?(gradle_path)
      gradle_content = File.read(gradle_path)
      gradle_content.include?("apply plugin: 'com.android.application") ||
        gradle_content.include?("id 'com.android.application") ||
        (gradle_content.include?("plugins {") && gradle_content.include?("com.android.application"))
    end
  end
  return nil unless main_module

  module_name = main_module.split(':').last
  File.join(project_path, module_name)
end

#install_adoptium_java_11Object

下载并安装 Adoptium OpenJDK 11



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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
# File 'lib/pindo/module/android/base_helper.rb', line 237

def install_adoptium_java_11
  begin
    # 创建临时目录
    temp_dir = Dir.mktmpdir("java_install_")
    
    # 下载 URL (macOS x64)
    download_url = "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.21%2B9/OpenJDK11U-jdk_x64_mac_hotspot_11.0.21_9.tar.gz"
    tar_file = File.join(temp_dir, "openjdk11.tar.gz")
    
    puts "下载 OpenJDK 11..."
    if system("curl -L -o \"#{tar_file}\" \"#{download_url}\"")
      puts "解压 OpenJDK 11..."
      if system("tar -xzf \"#{tar_file}\" -C \"#{temp_dir}\"")
        # 查找解压后的目录
        jdk_dir = Dir.glob(File.join(temp_dir, "jdk-11*")).first
        if jdk_dir && File.directory?(jdk_dir)
          # 尝试安装到用户目录而不是系统目录
          user_install_dir = File.expand_path("~/.pindo/java11")
          if system("mkdir -p \"#{File.dirname(user_install_dir)}\" && mv \"#{jdk_dir}\" \"#{user_install_dir}\"")
            # 设置环境变量
            ENV['JAVA_HOME'] = user_install_dir
            puts "\e[32m✓ 已安装并设置 JAVA_HOME: #{user_install_dir}\e[0m"
            return true
          else
            puts "\e[33m无法安装到用户目录,尝试系统目录...\e[0m"
            # 回退到系统目录
            install_dir = "/usr/local/openjdk11"
            if system("sudo mkdir -p /usr/local && sudo mv \"#{jdk_dir}\" \"#{install_dir}\"")
              # 设置环境变量
              ENV['JAVA_HOME'] = install_dir
              puts "\e[32m✓ 已安装并设置 JAVA_HOME: #{install_dir}\e[0m"
              return true
            else
              puts "\e[31m无法安装到系统目录,需要管理员权限\e[0m"
            end
          end
        else
          puts "\e[31m解压后未找到 JDK 目录\e[0m"
        end
      else
        puts "\e[31m解压 OpenJDK 11 失败\e[0m"
      end
    else
      puts "\e[31m下载 OpenJDK 11 失败\e[0m"
    end
    
    return false
  rescue => e
    puts "\e[31m下载安装 OpenJDK 11 失败: #{e.message}\e[0m"
    return false
  ensure
    # 清理临时目录
    FileUtils.rm_rf(temp_dir) if temp_dir && File.directory?(temp_dir)
  end
end

#install_java_11Object

自动安装 Java 11



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/pindo/module/android/base_helper.rb', line 171

def install_java_11
  begin
    # 检查是否在 macOS 上
    if RbConfig::CONFIG['host_os'] =~ /darwin/
      return install_java_11_macos
    else
      puts "\e[33m当前系统不是 macOS,请手动安装 Java 11\e[0m"
      return false
    end
  rescue => e
    puts "\e[31m安装 Java 11 时出错: #{e.message}\e[0m"
    return false
  end
end

#install_java_11_macosObject

在 macOS 上安装 Java 11



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/pindo/module/android/base_helper.rb', line 187

def install_java_11_macos
  puts "\e[36m在 macOS 上安装 Java 11...\e[0m"
  
  # 方法1: 使用 Homebrew 安装
  if system("which brew > /dev/null 2>&1")
    puts "使用 Homebrew 安装 OpenJDK 11..."
    if system("brew install openjdk@11")
      # 设置 JAVA_HOME
      java_home_path = `brew --prefix openjdk@11`.strip
      if !java_home_path.empty? && File.directory?(java_home_path)
        java_bin_path = File.join(java_home_path, "bin", "java")
        if File.exist?(java_bin_path)
          # 设置环境变量
          ENV['JAVA_HOME'] = java_home_path
          puts "\e[32m✓ 已设置 JAVA_HOME: #{java_home_path}\e[0m"
          return true
        end
      end
    else
      puts "\e[33mHomebrew 安装 OpenJDK 11 失败\e[0m"
    end
  else
    puts "\e[33m未找到 Homebrew,跳过 Homebrew 安装方式\e[0m"
  end
  
  # 方法2: 使用 SDKMAN 安装
  if system("which sdk > /dev/null 2>&1")
    puts "使用 SDKMAN 安装 Java 11..."
    if system("sdk install java 11.0.21-tem")
      # 设置环境变量
      sdkman_home = ENV['SDKMAN_DIR'] || File.expand_path("~/.sdkman")
      java_home_path = File.join(sdkman_home, "candidates", "java", "11.0.21-tem")
      if File.directory?(java_home_path)
        ENV['JAVA_HOME'] = java_home_path
        puts "\e[32m✓ 已设置 JAVA_HOME: #{java_home_path}\e[0m"
        return true
      end
    else
      puts "\e[33mSDKMAN 安装 Java 11 失败\e[0m"
    end
  else
    puts "\e[33m未找到 SDKMAN,跳过 SDKMAN 安装方式\e[0m"
  end
  
  # 方法3: 下载并安装 Adoptium OpenJDK
  puts "尝试下载并安装 Adoptium OpenJDK 11..."
  return install_adoptium_java_11
end

#modify_il2cpp_config(project_path) ⇒ Object



607
608
609
610
611
612
613
614
615
616
# File 'lib/pindo/module/android/base_helper.rb', line 607

def modify_il2cpp_config(project_path)
  # 设置Il2CppOutputProject可执行权限
  system("chmod", "-R", "777",
  File.join(project_path, "unityLibrary/src/main/Il2CppOutputProject"))

  il2cpp_config_path = File.join(project_path, "unityLibrary/src/main/Il2CppOutputProject/IL2CPP/libil2cpp/il2cpp-config.h")
  content = File.read(il2cpp_config_path)
  content.gsub!("il2cpp::vm::Exception::Raise", "//il2cpp::vm::Exception::Raise")
  File.write(il2cpp_config_path, content)
end

#remove_desktop_google_service(project_path) ⇒ Object



618
619
620
621
622
623
# File 'lib/pindo/module/android/base_helper.rb', line 618

def remove_desktop_google_service(project_path)
  # 删除google-services-desktop.json
  desktop_google_service_path = File.join(project_path,
    "unityLibrary/src/main/assets/google-services-desktop.json")
  File.delete(desktop_google_service_path) if File.exist?(desktop_google_service_path)
end

#remove_groovy_comments(block) ⇒ Object



829
830
831
832
833
# File 'lib/pindo/module/android/base_helper.rb', line 829

def remove_groovy_comments(block)
  # 去除 // 和 /* ... */ 注释
  block = block.gsub(/\/\*[\s\S]*?\*\//, "")
  block.lines.reject { |line| line.strip.start_with?("//") }.join
end

#remove_kts_comments(block) ⇒ Object



824
825
826
827
# File 'lib/pindo/module/android/base_helper.rb', line 824

def remove_kts_comments(block)
  # 去除 // 注释
  block.lines.reject { |line| line.strip.start_with?("//") }.join
end

#replace_unity_jdk_with_java11(jdk_path) ⇒ Object

替换 Unity Editor 中的 JDK 为 Java 11



494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
# File 'lib/pindo/module/android/base_helper.rb', line 494

def replace_unity_jdk_with_java11(jdk_path)
  begin
    puts "\e[33m正在替换 Unity Editor JDK 为 Java 11...\e[0m"
    
    # 查找系统中的 Java 11
    java11_path = find_java11_installation
    unless java11_path && File.directory?(java11_path)
      puts "\e[33m未找到系统中的 Java 11 安装,正在自动下载...\e[0m"
      
      # 自动下载并安装 Java 11
      if install_java_11
        java11_path = find_java11_installation
        unless java11_path && File.directory?(java11_path)
          puts "\e[31m✗ Java 11 自动安装失败\e[0m"
          return false
        end
        puts "\e[32m✓ Java 11 自动安装成功\e[0m"
      else
        puts "\e[31m✗ Java 11 自动安装失败\e[0m"
        return false
      end
    end
    
    puts "找到 Java 11 安装路径: #{java11_path}"
    
    # 备份原始 JDK(使用 sudo 权限)
    backup_path = "#{jdk_path}_backup_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
    puts "备份原始 JDK 到: #{backup_path}"
    puts "\e[33m需要管理员权限来备份和替换 Unity Editor JDK,请输入密码...\e[0m"
    
    if system("sudo cp -R \"#{jdk_path}\" \"#{backup_path}\"")
      puts "\e[32m✓ 原始 JDK 备份成功\e[0m"
    else
      puts "\e[31m✗ 原始 JDK 备份失败\e[0m"
      return false
    end
    
    # 删除原始 JDK 内容(使用 sudo 权限)
    puts "删除原始 JDK 内容..."
    if system("sudo rm -rf \"#{jdk_path}/*\"")
      puts "\e[32m✓ 原始 JDK 内容删除成功\e[0m"
    else
      puts "\e[31m✗ 原始 JDK 内容删除失败\e[0m"
      return false
    end
    
    # 复制 Java 11 到 Unity JDK 目录(使用 sudo 权限)
    puts "复制 Java 11 到 Unity JDK 目录..."
    if system("sudo cp -R \"#{java11_path}\"/* \"#{jdk_path}/\"")
      puts "\e[32m✓ Java 11 复制成功\e[0m"
      
      # 验证替换结果
      new_java_bin = File.join(jdk_path, "bin", "java")
      if File.exist?(new_java_bin)
        version_output = `"#{new_java_bin}" -version 2>&1`
        if version_output =~ /version "(\d+)/
          new_version = $1.to_i
          if new_version >= 11
            puts "\e[32m✓ Unity Editor JDK 已成功替换为 Java #{new_version}\e[0m"
            return true
          else
            puts "\e[31m✗ 替换后的 JDK 版本仍然不符合要求: #{new_version}\e[0m"
          end
        end
      end
    else
      puts "\e[31m✗ Java 11 复制失败\e[0m"
      return false
    end
    
    return false
  rescue => e
    puts "\e[31m替换 Unity Editor JDK 时出错: #{e.message}\e[0m"
    return false
  end
end

#resolve_var(val, ext_values, depth = 0) ⇒ Object



875
876
877
878
879
880
881
882
# File 'lib/pindo/module/android/base_helper.rb', line 875

def resolve_var(val, ext_values, depth=0)
  return val if val.nil? || depth > 5
  if ext_values[val]
    resolve_var(ext_values[val], ext_values, depth+1)
  else
    val
  end
end

#unity_android_project?(project_path) ⇒ Boolean

Returns:

  • (Boolean)


929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
# File 'lib/pindo/module/android/base_helper.rb', line 929

def unity_android_project?(project_path)
  # 检查 unityLibrary 模块是否存在
  unity_library_path = File.join(project_path, "unityLibrary")
  return false unless File.directory?(unity_library_path)

  # 检查 unityLibrary 的 build.gradle 或 build.gradle.kts 是否存在
  unity_gradle_path = File.join(unity_library_path, "build.gradle")
  unity_gradle_kts_path = File.join(unity_library_path, "build.gradle.kts")    
  if File.exist?(unity_gradle_kts_path)
    unity_gradle_path = unity_gradle_kts_path
  elsif !File.exist?(unity_gradle_path)
    return false
  end

  # 检查 build.gradle 中是否包含 Unity 特有的配置
  content = File.read(unity_gradle_path)
  content.include?("com.android.library") && content.include?("BuildIl2Cpp")
end

#verify_java_version(java_path) ⇒ Object

验证 Java 版本是否满足要求 (Java 11+)



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/pindo/module/android/base_helper.rb', line 77

def verify_java_version(java_path)
  begin
    version_output = `"#{java_path}" -version 2>&1`
    if version_output =~ /version "(\d+)/
      major_version = $1.to_i
      return major_version >= 11
    end
  rescue
    # 如果执行失败,返回 false
  end
  false
end