main.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package main
  2. import (
  3. "os"
  4. "path/filepath"
  5. "regexp"
  6. "strings"
  7. "github.com/sagernet/sing-box/cmd/internal/build_shared"
  8. "github.com/sagernet/sing-box/log"
  9. "github.com/sagernet/sing/common"
  10. "howett.net/plist"
  11. )
  12. func main() {
  13. newVersion := common.Must1(build_shared.ReadTagVersion())
  14. applePath, err := filepath.Abs("../sing-box-for-apple")
  15. if err != nil {
  16. log.Fatal(err)
  17. }
  18. common.Must(os.Chdir(applePath))
  19. projectFile := common.Must1(os.Open("sing-box.xcodeproj/project.pbxproj"))
  20. var project map[string]any
  21. decoder := plist.NewDecoder(projectFile)
  22. common.Must(decoder.Decode(&project))
  23. objectsMap := project["objects"].(map[string]any)
  24. projectContent := string(common.Must1(os.ReadFile("sing-box.xcodeproj/project.pbxproj")))
  25. newContent, updated0 := findAndReplace(objectsMap, projectContent, []string{"io.nekohasekai.sfavt"}, newVersion.VersionString())
  26. newContent, updated1 := findAndReplace(objectsMap, newContent, []string{"io.nekohasekai.sfavt.standalone", "io.nekohasekai.sfavt.system"}, newVersion.String())
  27. if updated0 || updated1 {
  28. log.Info("updated version to ", newVersion.VersionString(), " (", newVersion.String(), ")")
  29. }
  30. var updated2 bool
  31. if macProjectVersion := os.Getenv("MACOS_PROJECT_VERSION"); macProjectVersion != "" {
  32. newContent, updated2 = findAndReplaceProjectVersion(objectsMap, newContent, []string{"SFM"}, macProjectVersion)
  33. if updated2 {
  34. log.Info("updated macos project version to ", macProjectVersion)
  35. }
  36. }
  37. if updated0 || updated1 || updated2 {
  38. common.Must(os.WriteFile("sing-box.xcodeproj/project.pbxproj", []byte(newContent), 0o644))
  39. }
  40. }
  41. func findAndReplace(objectsMap map[string]any, projectContent string, bundleIDList []string, newVersion string) (string, bool) {
  42. objectKeyList := findObjectKey(objectsMap, bundleIDList)
  43. var updated bool
  44. for _, objectKey := range objectKeyList {
  45. matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{"))
  46. indexes := matchRegexp.FindStringIndex(projectContent)
  47. if len(indexes) < 2 {
  48. println(projectContent)
  49. log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey))
  50. }
  51. indexStart := indexes[1]
  52. indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}")
  53. versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "MARKETING_VERSION = ") + 20
  54. versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";")
  55. version := projectContent[versionStart:versionEnd]
  56. if version == newVersion {
  57. continue
  58. }
  59. updated = true
  60. projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:]
  61. }
  62. return projectContent, updated
  63. }
  64. func findAndReplaceProjectVersion(objectsMap map[string]any, projectContent string, directoryList []string, newVersion string) (string, bool) {
  65. objectKeyList := findObjectKeyByDirectory(objectsMap, directoryList)
  66. var updated bool
  67. for _, objectKey := range objectKeyList {
  68. matchRegexp := common.Must1(regexp.Compile(objectKey + ".*= \\{"))
  69. indexes := matchRegexp.FindStringIndex(projectContent)
  70. if len(indexes) < 2 {
  71. println(projectContent)
  72. log.Fatal("failed to find object key ", objectKey, ": ", strings.Index(projectContent, objectKey))
  73. }
  74. indexStart := indexes[1]
  75. indexEnd := indexStart + strings.Index(projectContent[indexStart:], "}")
  76. versionStart := indexStart + strings.Index(projectContent[indexStart:indexEnd], "CURRENT_PROJECT_VERSION = ") + 26
  77. versionEnd := versionStart + strings.Index(projectContent[versionStart:indexEnd], ";")
  78. version := projectContent[versionStart:versionEnd]
  79. if version == newVersion {
  80. continue
  81. }
  82. updated = true
  83. projectContent = projectContent[:versionStart] + newVersion + projectContent[versionEnd:]
  84. }
  85. return projectContent, updated
  86. }
  87. func findObjectKey(objectsMap map[string]any, bundleIDList []string) []string {
  88. var objectKeyList []string
  89. for objectKey, object := range objectsMap {
  90. buildSettings := object.(map[string]any)["buildSettings"]
  91. if buildSettings == nil {
  92. continue
  93. }
  94. bundleIDObject := buildSettings.(map[string]any)["PRODUCT_BUNDLE_IDENTIFIER"]
  95. if bundleIDObject == nil {
  96. continue
  97. }
  98. if common.Contains(bundleIDList, bundleIDObject.(string)) {
  99. objectKeyList = append(objectKeyList, objectKey)
  100. }
  101. }
  102. return objectKeyList
  103. }
  104. func findObjectKeyByDirectory(objectsMap map[string]any, directoryList []string) []string {
  105. var objectKeyList []string
  106. for objectKey, object := range objectsMap {
  107. buildSettings := object.(map[string]any)["buildSettings"]
  108. if buildSettings == nil {
  109. continue
  110. }
  111. infoPListFile := buildSettings.(map[string]any)["INFOPLIST_FILE"]
  112. if infoPListFile == nil {
  113. continue
  114. }
  115. for _, searchDirectory := range directoryList {
  116. if strings.HasPrefix(infoPListFile.(string), searchDirectory+"/") {
  117. objectKeyList = append(objectKeyList, objectKey)
  118. }
  119. }
  120. }
  121. return objectKeyList
  122. }