construct_universal_dylib.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import argparse
  2. import os
  3. from pathlib import Path
  4. import platform
  5. import shutil
  6. import subprocess
  7. parser = argparse.ArgumentParser(
  8. description="Construct Universal dylibs for nuget package"
  9. )
  10. parser.add_argument(
  11. "arm64_input_directory", help="ARM64 Input directory containing dylibs"
  12. )
  13. parser.add_argument(
  14. "x86_64_input_directory", help="x86_64 Input directory containing dylibs"
  15. )
  16. parser.add_argument("output_directory", help="Output directory")
  17. parser.add_argument("rglob", help="rglob")
  18. args = parser.parse_args()
  19. # Use Apple LLVM on Darwin, otherwise standard LLVM.
  20. if platform.system() == "Darwin":
  21. LIPO = "lipo"
  22. else:
  23. LIPO = shutil.which("llvm-lipo")
  24. if LIPO is None:
  25. for llvm_ver in [15, 14, 13]:
  26. lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
  27. if lipo_path is not None:
  28. LIPO = lipo_path
  29. break
  30. if LIPO is None:
  31. raise Exception("Cannot find a valid location for LLVM lipo!")
  32. arm64_input_directory: Path = Path(args.arm64_input_directory)
  33. x86_64_input_directory: Path = Path(args.x86_64_input_directory)
  34. output_directory: Path = Path(args.output_directory)
  35. rglob = args.rglob
  36. def get_new_name(
  37. input_directory: Path, output_directory: str, input_dylib_path: Path
  38. ) -> Path:
  39. input_component = str(input_dylib_path).replace(str(input_directory), "")[1:]
  40. return Path(os.path.join(output_directory, input_component))
  41. def is_fat_file(dylib_path: Path) -> str:
  42. res = subprocess.check_output([LIPO, "-info", str(dylib_path.absolute())]).decode(
  43. "utf-8"
  44. )
  45. return not res.split("\n")[0].startswith("Non-fat file")
  46. def construct_universal_dylib(
  47. arm64_input_dylib_path: Path, x86_64_input_dylib_path: Path, output_dylib_path: Path
  48. ):
  49. if output_dylib_path.exists() or output_dylib_path.is_symlink():
  50. os.remove(output_dylib_path)
  51. os.makedirs(output_dylib_path.parent, exist_ok=True)
  52. if arm64_input_dylib_path.is_symlink():
  53. os.symlink(
  54. os.path.basename(arm64_input_dylib_path.resolve()), output_dylib_path
  55. )
  56. else:
  57. if is_fat_file(arm64_input_dylib_path) or not x86_64_input_dylib_path.exists():
  58. with open(output_dylib_path, "wb") as dst:
  59. with open(arm64_input_dylib_path, "rb") as src:
  60. dst.write(src.read())
  61. else:
  62. subprocess.check_call(
  63. [
  64. LIPO,
  65. str(arm64_input_dylib_path.absolute()),
  66. str(x86_64_input_dylib_path.absolute()),
  67. "-output",
  68. str(output_dylib_path.absolute()),
  69. "-create",
  70. ]
  71. )
  72. print(rglob)
  73. for path in arm64_input_directory.rglob("**/*.dylib"):
  74. construct_universal_dylib(
  75. path,
  76. get_new_name(arm64_input_directory, x86_64_input_directory, path),
  77. get_new_name(arm64_input_directory, output_directory, path),
  78. )