1*523fa7a6SAndroid Build Coastguard Worker#!/bin/bash 2*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) Meta Platforms, Inc. and affiliates. 3*523fa7a6SAndroid Build Coastguard Worker# All rights reserved. 4*523fa7a6SAndroid Build Coastguard Worker# 5*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the 6*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree. 7*523fa7a6SAndroid Build Coastguard Worker 8*523fa7a6SAndroid Build Coastguard Workerset -eu 9*523fa7a6SAndroid Build Coastguard Worker 10*523fa7a6SAndroid Build Coastguard Workerfunction usage() { 11*523fa7a6SAndroid Build Coastguard Worker echo "This script creates XCFrameworks from libraries in specified directories." 12*523fa7a6SAndroid Build Coastguard Worker echo "It merges libraries using libtool and creates an XCFramework using xcodebuild." 13*523fa7a6SAndroid Build Coastguard Worker echo "" 14*523fa7a6SAndroid Build Coastguard Worker echo "Usage: $0 --directory=<dir> --framework=<lib> [--output=<output>]" 15*523fa7a6SAndroid Build Coastguard Worker echo " --directory: Directory containing the libs" 16*523fa7a6SAndroid Build Coastguard Worker echo " --framework: Framework to create in the format 'target:lib1,lib2:headers'" 17*523fa7a6SAndroid Build Coastguard Worker echo " 'target' is the name of the target library." 18*523fa7a6SAndroid Build Coastguard Worker echo " 'lib1,lib2' is a comma-separated list of input libraries." 19*523fa7a6SAndroid Build Coastguard Worker echo " 'headers' is an optional path to a directory with headers." 20*523fa7a6SAndroid Build Coastguard Worker echo " --output: Optional output directory. Defaults to the current directory." 21*523fa7a6SAndroid Build Coastguard Worker echo "" 22*523fa7a6SAndroid Build Coastguard Worker echo "Example:" 23*523fa7a6SAndroid Build Coastguard Worker echo "$0 --directory=ios-arm64 --directory=ios-arm64-simulator --framework=\"mylib:lib1.a,lib2.a:include\" --output=output/dir" 24*523fa7a6SAndroid Build Coastguard Worker exit 1 25*523fa7a6SAndroid Build Coastguard Worker} 26*523fa7a6SAndroid Build Coastguard Worker 27*523fa7a6SAndroid Build Coastguard Workercommand -v libtool >/dev/null 2>&1 || { echo >&2 "libtool is required but it's not installed. Aborting."; exit 1; } 28*523fa7a6SAndroid Build Coastguard Workercommand -v xcodebuild >/dev/null 2>&1 || { echo >&2 "xcodebuild is required but it's not installed. Aborting."; exit 1; } 29*523fa7a6SAndroid Build Coastguard Worker 30*523fa7a6SAndroid Build Coastguard Workerdirectories=() 31*523fa7a6SAndroid Build Coastguard Workerframeworks=() 32*523fa7a6SAndroid Build Coastguard Workeroutput=$(pwd) 33*523fa7a6SAndroid Build Coastguard Worker 34*523fa7a6SAndroid Build Coastguard Workerfor arg in "$@"; do 35*523fa7a6SAndroid Build Coastguard Worker case $arg in 36*523fa7a6SAndroid Build Coastguard Worker -h|--help) usage ;; 37*523fa7a6SAndroid Build Coastguard Worker --directory=*) directories+=("${arg#*=}") ;; 38*523fa7a6SAndroid Build Coastguard Worker --framework=*) frameworks+=("${arg#*=}") ;; 39*523fa7a6SAndroid Build Coastguard Worker --output=*) output="${arg#*=}" ;; 40*523fa7a6SAndroid Build Coastguard Worker *) 41*523fa7a6SAndroid Build Coastguard Worker echo "Invalid argument: $arg" 42*523fa7a6SAndroid Build Coastguard Worker exit 1 43*523fa7a6SAndroid Build Coastguard Worker ;; 44*523fa7a6SAndroid Build Coastguard Worker esac 45*523fa7a6SAndroid Build Coastguard Workerdone 46*523fa7a6SAndroid Build Coastguard Worker 47*523fa7a6SAndroid Build Coastguard Workerif [ ${#directories[@]} -eq 0 ] || [ ${#frameworks[@]} -eq 0 ]; then 48*523fa7a6SAndroid Build Coastguard Worker echo "Both --directory and --framework options are required" 49*523fa7a6SAndroid Build Coastguard Worker usage 50*523fa7a6SAndroid Build Coastguard Workerfi 51*523fa7a6SAndroid Build Coastguard Worker 52*523fa7a6SAndroid Build Coastguard Workermkdir -p "${output}" 53*523fa7a6SAndroid Build Coastguard Worker 54*523fa7a6SAndroid Build Coastguard Workercreate_xcframework() { 55*523fa7a6SAndroid Build Coastguard Worker local target_library_name 56*523fa7a6SAndroid Build Coastguard Worker target_library_name=$(echo "$1" | cut -d: -f1) 57*523fa7a6SAndroid Build Coastguard Worker local libraries_list 58*523fa7a6SAndroid Build Coastguard Worker libraries_list=$(echo "$1" | cut -d: -f2 | tr ',' '\n') 59*523fa7a6SAndroid Build Coastguard Worker local headers_directory 60*523fa7a6SAndroid Build Coastguard Worker headers_directory=$(echo "$1" | cut -d: -f3) 61*523fa7a6SAndroid Build Coastguard Worker local dir 62*523fa7a6SAndroid Build Coastguard Worker local libraries=() 63*523fa7a6SAndroid Build Coastguard Worker local merged_libs=() 64*523fa7a6SAndroid Build Coastguard Worker 65*523fa7a6SAndroid Build Coastguard Worker if [[ -n "$headers_directory" && ! -d "$headers_directory" ]]; then 66*523fa7a6SAndroid Build Coastguard Worker echo "Headers directory ${headers_directory} does not exist" 67*523fa7a6SAndroid Build Coastguard Worker exit 1 68*523fa7a6SAndroid Build Coastguard Worker fi 69*523fa7a6SAndroid Build Coastguard Worker 70*523fa7a6SAndroid Build Coastguard Worker # For each directory, create a merged library using libtool. 71*523fa7a6SAndroid Build Coastguard Worker for dir in "${directories[@]}"; do 72*523fa7a6SAndroid Build Coastguard Worker 73*523fa7a6SAndroid Build Coastguard Worker if [ ! -d "${dir}" ]; then 74*523fa7a6SAndroid Build Coastguard Worker echo "Directory ${dir} does not exist" 75*523fa7a6SAndroid Build Coastguard Worker exit 1 76*523fa7a6SAndroid Build Coastguard Worker fi 77*523fa7a6SAndroid Build Coastguard Worker 78*523fa7a6SAndroid Build Coastguard Worker local dir_suffix 79*523fa7a6SAndroid Build Coastguard Worker dir_suffix=$(echo "$dir" | tr '[:upper:]' '[:lower:]' | sed 's/\//-/g') 80*523fa7a6SAndroid Build Coastguard Worker local merged_lib="${output}/lib${target_library_name}-${dir_suffix}.a" 81*523fa7a6SAndroid Build Coastguard Worker 82*523fa7a6SAndroid Build Coastguard Worker # Remove the existing .a file if it exists. 83*523fa7a6SAndroid Build Coastguard Worker if [ -f "${merged_lib}" ]; then 84*523fa7a6SAndroid Build Coastguard Worker echo "Removing existing file ${merged_lib}" 85*523fa7a6SAndroid Build Coastguard Worker rm "${merged_lib}" 86*523fa7a6SAndroid Build Coastguard Worker fi 87*523fa7a6SAndroid Build Coastguard Worker 88*523fa7a6SAndroid Build Coastguard Worker echo -e "\nMerging libraries:\n${libraries_list}\nfrom ${dir}\ninto library ${merged_lib}" 89*523fa7a6SAndroid Build Coastguard Worker 90*523fa7a6SAndroid Build Coastguard Worker local lib_paths=() 91*523fa7a6SAndroid Build Coastguard Worker for lib in ${libraries_list}; do 92*523fa7a6SAndroid Build Coastguard Worker if [ ! -f "${dir}/${lib}" ]; then 93*523fa7a6SAndroid Build Coastguard Worker echo "File ${dir}/${lib} does not exist" 94*523fa7a6SAndroid Build Coastguard Worker exit 1 95*523fa7a6SAndroid Build Coastguard Worker fi 96*523fa7a6SAndroid Build Coastguard Worker lib_paths+=("${dir}/${lib}") 97*523fa7a6SAndroid Build Coastguard Worker done 98*523fa7a6SAndroid Build Coastguard Worker 99*523fa7a6SAndroid Build Coastguard Worker libtool -static -o "${merged_lib}" "${lib_paths[@]}" 100*523fa7a6SAndroid Build Coastguard Worker 101*523fa7a6SAndroid Build Coastguard Worker merged_libs+=("${merged_lib}") 102*523fa7a6SAndroid Build Coastguard Worker 103*523fa7a6SAndroid Build Coastguard Worker if [[ -n "$headers_directory" ]]; then 104*523fa7a6SAndroid Build Coastguard Worker echo -e "\nIncluding headers from ${headers_directory}" 105*523fa7a6SAndroid Build Coastguard Worker libraries+=("-library" "${merged_lib}" "-headers" "${headers_directory}") 106*523fa7a6SAndroid Build Coastguard Worker else 107*523fa7a6SAndroid Build Coastguard Worker libraries+=("-library" "${merged_lib}") 108*523fa7a6SAndroid Build Coastguard Worker fi 109*523fa7a6SAndroid Build Coastguard Worker done 110*523fa7a6SAndroid Build Coastguard Worker 111*523fa7a6SAndroid Build Coastguard Worker # Remove the existing .xcframework if it exists. 112*523fa7a6SAndroid Build Coastguard Worker local xcframework="${output}/${target_library_name}.xcframework" 113*523fa7a6SAndroid Build Coastguard Worker if [ -d "${xcframework}" ]; then 114*523fa7a6SAndroid Build Coastguard Worker echo -e "\nRemoving existing XCFramework ${xcframework}" 115*523fa7a6SAndroid Build Coastguard Worker rm -rf "${xcframework}" 116*523fa7a6SAndroid Build Coastguard Worker fi 117*523fa7a6SAndroid Build Coastguard Worker 118*523fa7a6SAndroid Build Coastguard Worker echo -e "\nCreating XCFramework ${xcframework}" 119*523fa7a6SAndroid Build Coastguard Worker 120*523fa7a6SAndroid Build Coastguard Worker xcodebuild -create-xcframework "${libraries[@]}" -output "${xcframework}" 121*523fa7a6SAndroid Build Coastguard Worker 122*523fa7a6SAndroid Build Coastguard Worker echo -e "\nDeleting intermediate libraries:" 123*523fa7a6SAndroid Build Coastguard Worker for merged_lib in "${merged_libs[@]}"; do 124*523fa7a6SAndroid Build Coastguard Worker if [[ -f "${merged_lib}" ]]; then 125*523fa7a6SAndroid Build Coastguard Worker echo "Deleting ${merged_lib}" 126*523fa7a6SAndroid Build Coastguard Worker rm "${merged_lib}" 127*523fa7a6SAndroid Build Coastguard Worker fi 128*523fa7a6SAndroid Build Coastguard Worker done 129*523fa7a6SAndroid Build Coastguard Worker} 130*523fa7a6SAndroid Build Coastguard Worker 131*523fa7a6SAndroid Build Coastguard Worker# Create an XCFramework for each target library. 132*523fa7a6SAndroid Build Coastguard Workerfor target_lib in "${frameworks[@]}"; do 133*523fa7a6SAndroid Build Coastguard Worker create_xcframework "$target_lib" 134*523fa7a6SAndroid Build Coastguard Workerdone 135