build-clash-lib.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #!/usr/bin/python3
  2. import os
  3. import sys
  4. import platform
  5. import argparse
  6. def build_architecture(arch, output, output_dir):
  7. os.environ["GOARCH"] = arch
  8. os.system(f"go build -buildmode=c-shared -o {output}")
  9. # 检查生成的头文件
  10. headers = [f for f in os.listdir(output_dir) if f.endswith(".h") and "libclash" in f]
  11. if not headers:
  12. raise FileNotFoundError(f"No header file generated for architecture: {arch}")
  13. # 重命名第一个找到的头文件为 libclash.h
  14. for header in headers:
  15. old_path = os.path.join(output_dir, header)
  16. new_path = os.path.join(output_dir, "libclash.h")
  17. if old_path != new_path:
  18. os.rename(old_path, new_path)
  19. print(f"[info] Renamed {header} to libclash.h")
  20. break # 只需要处理一次
  21. def clean_up_dynamic_libs(output_dir, output_x86_64, output_arm64):
  22. # 删除多余的动态库,确保只保留 libclash.dylib
  23. files_to_delete = [output_x86_64, output_arm64]
  24. for lib_file in files_to_delete:
  25. if os.path.exists(lib_file):
  26. os.remove(lib_file)
  27. print(f"[info] Deleted extra dynamic library: {lib_file}")
  28. def clean_up_headers(output_dir):
  29. # 删除多余的头文件,确保只有 libclash.h 存在
  30. headers = [f for f in os.listdir(output_dir) if f.endswith(".h") and f != "libclash.h"]
  31. for header in headers:
  32. header_path = os.path.join(output_dir, header)
  33. os.remove(header_path)
  34. print(f"[info] Deleted extra header file: {header_path}")
  35. def package_universal_binary(output_x86_64, output_arm64, final_output, output_dir):
  36. # 确保生成的二进制文件存在
  37. print(f"[info] Checking {output_x86_64} and {output_arm64} for packaging...")
  38. if not os.path.exists(output_x86_64):
  39. raise FileNotFoundError(f"File not found: {output_x86_64}")
  40. if not os.path.exists(output_arm64):
  41. raise FileNotFoundError(f"File not found: {output_arm64}")
  42. # 使用 lipo 将 x86_64 和 arm64 合并为通用二进制文件
  43. print("[info] Creating universal binary for macOS...")
  44. os.system(f"lipo -create -output {final_output} {output_x86_64} {output_arm64}")
  45. if not os.path.exists(final_output):
  46. raise FileNotFoundError(f"Failed to create universal binary: {final_output}")
  47. print(f"[info] Universal binary created: {final_output}")
  48. # 清理多余的动态库
  49. clean_up_dynamic_libs(output_dir, output_x86_64, output_arm64)
  50. # 清理并重命名头文件为 libclash.h
  51. clean_up_headers(output_dir)
  52. def build(target):
  53. os.chdir("core")
  54. os.environ["CGO_ENABLED"] = "1"
  55. output_base = "../libclash/libclash"
  56. output_dir = "../libclash"
  57. # 根据平台设置动态库文件扩展名
  58. if sys.platform == 'win32':
  59. output_x86_64 = output_base + ".dll"
  60. final_output = output_x86_64
  61. elif sys.platform == "darwin":
  62. output_x86_64 = output_base + "_x86_64.dylib"
  63. output_arm64 = output_base + "_arm64.dylib"
  64. final_output = output_base + ".dylib"
  65. else:
  66. output_x86_64 = output_base + "_x86_64.so"
  67. output_arm64 = output_base + "_arm64.so"
  68. final_output = output_base + ".so"
  69. if sys.platform == 'win32':
  70. # Windows 平台仅编译 x86_64 版本
  71. print("[info] Compiling x86_64 target for Windows...")
  72. build_architecture("amd64", output_x86_64, output_dir)
  73. elif sys.platform == "darwin":
  74. # macOS 平台编译 x86_64 和 arm64,并合并为通用二进制
  75. print("[info] Compiling x86_64 target for macOS...")
  76. build_architecture("amd64", output_x86_64, output_dir)
  77. print("[info] Compiling arm64 target for macOS...")
  78. build_architecture("arm64", output_arm64, output_dir)
  79. # 在 build 阶段也执行 lipo 合并并清理
  80. package_universal_binary(output_x86_64, output_arm64, final_output, output_dir)
  81. else:
  82. # Linux 平台分别编译 x86_64 和 arm64,不合并为通用二进制
  83. print("[info] Compiling x86_64 target for Linux...")
  84. build_architecture("amd64", output_x86_64, output_dir)
  85. print("[info] Compiling arm64 target for Linux...")
  86. build_architecture("arm64", output_arm64, output_dir)
  87. os.chdir("..")
  88. def run_flutter_distributor():
  89. print("[info] Running flutter_distributor for packaging...")
  90. # 使用 flutter_distributor 打包 Flutter 项目
  91. os.system("flutter_distributor package --skip-clean --platform all")
  92. def package():
  93. if sys.platform == "darwin":
  94. # 打包 macOS 的通用二进制(通过 lipo 合并)
  95. output_base = "../libclash/libclash"
  96. output_x86_64 = output_base + "_x86_64.dylib"
  97. output_arm64 = output_base + "_arm64.dylib"
  98. final_output = output_base + ".dylib"
  99. output_dir = "../libclash"
  100. package_universal_binary(output_x86_64, output_arm64, final_output, output_dir)
  101. # 执行 flutter_distributor 进行打包
  102. run_flutter_distributor()
  103. def main():
  104. parser = argparse.ArgumentParser(description="Build and package libclash dynamic libraries.")
  105. parser.add_argument("action", choices=["build", "package"], help="Specify whether to build or package.")
  106. args = parser.parse_args()
  107. if args.action == "build":
  108. build("build")
  109. elif args.action == "package":
  110. build("package") # 在打包时也先进行构建
  111. package()
  112. if __name__ == "__main__":
  113. main()