Selective build script for iOS

Usage:
bash tensorflow/lite/experimental/ios/build_frameworks.sh \
    --input_models=model1.tflite,model2.tflite \
    --target_archs=armv7,arm64,x86_64
PiperOrigin-RevId: 348727288
Change-Id: I61559417880fa66265b5bbb286b0cab80f9f6387
This commit is contained in:
Thai Nguyen 2020-12-22 18:57:27 -08:00 committed by TensorFlower Gardener
parent 9be3ad213b
commit ccd1722630

View File

@ -0,0 +1,212 @@
#!/bin/bash
# Copyright 2020 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/../../../" && pwd)"
TMP_DIR="tensorflow/lite/ios/tmp"
function print_usage {
echo "Usage:"
echo " $(basename ${BASH_SOURCE}) \\"
echo " --input_models=model1.tflite,model2.tflite \\"
echo " --target_archs=x86_64,armv7,arm64"
echo ""
echo "Where: "
echo " --input_models: Supported TFLite models."
echo " --target_archs: Supported arches included in the frameworks."
echo " Default: x86_64,armv7,arm64. i386 architecture is currently not"
echo " supported."
echo ""
exit 1
}
# generate_list_field takes two positional arguments:
# - Name of the field in the build rule.
# - Comma-separated list of values of this field.
# The function returns a string represents that field in the BUILD file. Ex:
# 'name = ["value1", "value2"],'
function generate_list_field {
local name="$1"
local list_string="$2"
IFS=","
read -ra list <<< "$list_string"
local message=("$name=[")
for item in "${list[@]}"
do
message+=("\"$item\",")
done
message+=('],')
printf '%s' "${message[@]}"
}
function print_output {
echo "Output can be found here:"
for i in "$@"
do
# ls command returns failure if the file does not exist.
ls -1a ${ROOT_DIR}/$i
done
}
function generate_tflite_framework {
pushd ${TMP_DIR} > /dev/null
# Generate the BUILD file.
message=(
'load("@build_bazel_rules_apple//apple:ios.bzl", "ios_static_framework")'
'load("//tensorflow/lite:build_def.bzl", "tflite_custom_c_library")'
'load("//tensorflow/lite/ios:ios.bzl", "TFL_MINIMUM_OS_VERSION")'
'tflite_custom_c_library('
' name = "custom_c_api",'
' '"$(generate_list_field "models" "$MODEL_NAMES")"
')'
'ios_static_framework('
' name = "TensorFlowLiteC_framework",'
' hdrs = ['
' "//tensorflow/lite/c:c_api_types.h",'
' "//tensorflow/lite/ios:common.h",'
' "//tensorflow/lite/ios:c_api.h",'
' "//tensorflow/lite/ios:xnnpack_delegate.h",'
' ],'
' bundle_name = "TensorFlowLiteC",'
' minimum_os_version = TFL_MINIMUM_OS_VERSION,'
' deps = ['
' ":custom_c_api",'
' "//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",'
' ],'
')'
)
printf '%s\n' "${message[@]}" >> BUILD
# Build the framework package.
popd > /dev/null
bazel build -c opt --config=ios --ios_multi_cpus=${TARGET_ARCHS} \
//${TMP_DIR}:TensorFlowLiteC_framework
OUT_FILES="${OUT_FILES} bazel-bin/${TMP_DIR}/TensorFlowLiteC_framework.zip"
}
function generate_flex_framework {
pushd ${TMP_DIR}
# Generating the BUILD file.
message=(
'load("//tensorflow/lite/delegates/flex:build_def.bzl", "tflite_flex_cc_library")'
'tflite_flex_cc_library('
' name = "custom_flex_delegate",'
' '"$(generate_list_field "models" "$MODEL_NAMES")"
')'
'ios_static_framework('
' name = "TensorFlowLiteSelectTfOps_framework",'
' avoid_deps = ["//tensorflow/lite/c:common"],'
' bundle_name = "TensorFlowLiteSelectTfOps",'
' minimum_os_version = TFL_MINIMUM_OS_VERSION,'
' deps = ['
' ":custom_flex_delegate",'
' ],'
')'
)
printf '%s\n' "${message[@]}" >> BUILD
popd
# Build the framework.
bazel build -c opt --config=ios --ios_multi_cpus=${TARGET_ARCHS} \
//${TMP_DIR}:TensorFlowLiteSelectTfOps_framework
OUT_FILES="${OUT_FILES} bazel-bin/${TMP_DIR}/TensorFlowLiteSelectTfOps_framework.zip"
}
# Check command line flags.
TARGET_ARCHS=x86_64,armv7,arm64
if [ "$#" -gt 2 ]; then
echo "ERROR: Too many arguments."
print_usage
fi
for i in "$@"
do
case $i in
--input_models=*)
FLAG_MODELS="${i#*=}"
shift;;
--target_archs=*)
TARGET_ARCHS="${i#*=}"
shift;;
*)
echo "ERROR: Unrecognized argument: ${i}"
print_usage;;
esac
done
cd $ROOT_DIR
# Bazel v3.4 is required to build tensorflow python.
if ! grep -q "3.4.0" ".bazelversion"; then
mv .bazelversion .bazelversion_old
echo "3.4.0" > .bazelversion
fi
# Check if users already run configure
if [ ! -f "$ROOT_DIR/.tf_configure.bazelrc" ]; then
echo "ERROR: Please run ./configure first."
exit 1
else
if ! grep -q "TF_CONFIGURE_IOS=\"1\"" "$ROOT_DIR/.tf_configure.bazelrc"; then
echo "ERROR: Please run ./configure with iOS config."
exit 1
fi
fi
# Prepare the tmp directory.
rm -rf ${TMP_DIR} && mkdir -p ${TMP_DIR}
# Copy models to tmp directory.
MODEL_NAMES=""
IFS=","
read -ra MODEL_PATHS <<< "${FLAG_MODELS}"
for model in "${MODEL_PATHS[@]}"
do
cp ${model} ${TMP_DIR}
if [ -z "$MODEL_NAMES" ]; then
MODEL_NAMES="$(basename ${model})"
else
MODEL_NAMES="${MODEL_NAMES},$(basename ${model})"
fi
done
# Build the custom framework.
generate_tflite_framework
if [ -z ${FLAG_MODELS} ]; then
print_output ${OUT_FILES}
exit 0
fi
# Build flex framework if one of the models contain flex ops.
bazel build -c opt --config=monolithic //tensorflow/lite/tools:list_flex_ops_no_kernel_main
bazel-bin/tensorflow/lite/tools/list_flex_ops_no_kernel_main --graphs=${FLAG_MODELS} > ${TMP_DIR}/ops_list.txt
if [[ `cat ${TMP_DIR}/ops_list.txt` != "[]" ]]; then
generate_flex_framework
fi
# List the output files.
if [ ! -f ".bazelversion_old" ]; then
rm .bazelversion
mv -f .bazelversion_old .bazelversion
fi
rm -rf ${TMP_DIR}
print_output ${OUT_FILES}