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 )