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