diff --git a/tensorflow_lite_support/ios/BUILD b/tensorflow_lite_support/ios/BUILD index 6e3264804..c3217a613 100644 --- a/tensorflow_lite_support/ios/BUILD +++ b/tensorflow_lite_support/ios/BUILD @@ -8,6 +8,12 @@ load( load( "//tensorflow_lite_support/ios:ios.bzl", "strip_c_api_include_path_prefix", + "strip_ios_api_include_path_prefix", +) + +load( + "@build_bazel_rules_apple//apple:ios.bzl", + "ios_static_framework", ) package( @@ -37,6 +43,24 @@ strip_c_api_include_path_prefix( ], ) +strip_ios_api_include_path_prefix( + name = "strip_ios_api_include_path", + hdr_labels = [ + "//tensorflow_lite_support/ios/task/vision:sources/TFLImageEmbedder.h", + "//tensorflow_lite_support/ios/task/vision:sources/TFLImageClassifier.h", + "//tensorflow_lite_support/ios/task/vision:sources/TFLObjectDetector.h", + "//tensorflow_lite_support/ios/task/vision:sources/TFLImageSegmenter.h", + "//tensorflow_lite_support/ios:sources/TFLCommon.h", + "//tensorflow_lite_support/ios/task/core:sources/TFLBaseOptions.h", + "//tensorflow_lite_support/ios/task/processor:sources/TFLClassificationOptions.h", + "//tensorflow_lite_support/ios/task/processor:sources/TFLCategory.h", + "//tensorflow_lite_support/ios/task/processor:sources/TFLClassificationResult.h", + "//tensorflow_lite_support/ios/task/processor:sources/TFLDetectionResult.h", + "//tensorflow_lite_support/ios/task/processor:sources/TFLSegmentationResult.h", + "//tensorflow_lite_support/odml/ios/image:apis/GMLImage.h", + ], +) + # This target builds a monolithic static framework for the TFLite Text API, # which includes the TFLite runtime in it. # @@ -59,39 +83,6 @@ tflite_ios_framework( ], ) -# Xcode 12 does not support ios fat libraries. Frameworks built for multiple -# architectures should be compiled into a .xcframework inside. Bazel currently -# does not support building .xcframework. You have to build the framework -# for the architecture you decide to test on. -# Use the below command to build for arm64 which lets you test the library on -# iOS devices. -# bazel build -c opt --config=ios_arm64 //tensorflow_lite_support/ios:TensorFlowLiteTaskVisionC_framework -tflite_ios_framework( - name = "TensorFlowLiteTaskVisionC_framework", - hdrs = [ - ":base_options.h", - ":bounding_box.h", - ":category.h", - ":classification_options.h", - ":classification_result.h", - ":common.h", - ":detection_result.h", - ":frame_buffer.h", - ":image_classifier.h", - ":image_segmenter.h", - ":object_detector.h", - ":segmentation_result.h", - ], - allowlist_symbols_file = ":allowlist_TensorFlowLiteTaskVision.txt", - bundle_name = "TensorFlowLiteTaskVisionC", - minimum_os_version = TFL_MINIMUM_OS_VERSION, - deps = [ - "//tensorflow_lite_support/c/task/vision:image_classifier", - "//tensorflow_lite_support/c/task/vision:image_segmenter", - "//tensorflow_lite_support/c/task/vision:object_detector", - ], -) - objc_library( name = "TFLCommon", hdrs = [ @@ -120,3 +111,37 @@ objc_library( "//tensorflow_lite_support/ios:TFLCommon", ], ) + +# Xcode 12 does not support ios fat libraries. Frameworks built for multiple +# architectures should be compiled into a .xcframework inside. Bazel currently +# does not support building .xcframework. You have to build the framework +# for the architecture you decide to test on. +# Use the below command to build for arm64 which lets you test the library on +# iOS devices. +# bazel build -c opt --config=ios_arm64 //tensorflow_lite_support/ios:TensorFlowLiteTaskVision_framework +ios_static_framework( + name = "TensorFlowLiteTaskVision_framework", + hdrs = [ + ":TFLImageEmbedder.h", + ":TFLImageClassifier.h", + ":TFLObjectDetector.h", + ":TFLImageSegmenter.h", + ":TFLCommon.h", + ":TFLBaseOptions.h", + ":TFLClassificationOptions.h", + ":TFLCategory.h", + ":TFLClassificationResult.h", + ":TFLDetectionResult.h", + ":TFLSegmentationResult.h", + ":GMLImage.h", + ], + bundle_name = "TensorFlowLiteTaskVision", + minimum_os_version = TFL_MINIMUM_OS_VERSION, + deps = [ + "//tensorflow_lite_support/ios/task/vision:TFLImageEmbedder", + "//tensorflow_lite_support/ios/task/vision:TFLImageClassifier", + "//tensorflow_lite_support/ios/task/vision:TFLObjectDetector", + "//tensorflow_lite_support/ios/task/vision:TFLImageSegmenter", + ], +) + diff --git a/tensorflow_lite_support/ios/TensorFlowLiteTaskVision.podspec.template b/tensorflow_lite_support/ios/TensorFlowLiteTaskVision.podspec.template index 137d891e9..08f90aee6 100644 --- a/tensorflow_lite_support/ios/TensorFlowLiteTaskVision.podspec.template +++ b/tensorflow_lite_support/ios/TensorFlowLiteTaskVision.podspec.template @@ -4,7 +4,7 @@ Pod::Spec.new do |s| s.authors = 'Google Inc.' s.license = { :type => 'Apache',:file => "LICENSE"} s.homepage = 'https://github.com/tensorflow/tflite-support' - s.source = { :http => '${TFLS_DOWNLOAD_URL}' } + s.source = { :http => 'https://dl.dropboxusercontent.com/s/kr3ngm6ji9ei8r5/TensorFlowLiteTaskVisionObjCPP-0.1.3-dev.tar.gz?dl=0' } s.summary = 'TensorFlow Lite Task Library - Vision' s.description = 'The Computer Vision APIs of the TFLite Task Library' @@ -12,51 +12,10 @@ Pod::Spec.new do |s| s.module_name = 'TensorFlowLiteTaskVision' s.static_framework = true - - root_dir_name = "tensorflow_lite_support" - objc_dir = root_dir_name + '/ios/' - objc_task_dir = objc_dir + 'task/' - objc_core_dir = objc_task_dir + 'core/' - objc_processor_dir = objc_task_dir + 'processor/' - objc_vision_dir = objc_task_dir + 'vision/' - gml_image_dir = root_dir_name + '/odml/ios/image/' - s.public_header_files = [ - objc_vision_dir + 'apis/*.h', - gml_image_dir + 'apis/*.h', - objc_dir + 'sources/TFLCommon.h', - objc_core_dir + 'sources/TFLBaseOptions.h', - objc_processor_dir + 'sources/{TFLClassificationOptions,TFLCategory,TFLClassificationResult,TFLDetectionResult,TFLSegmentationResult}.h', - objc_vision_dir + 'sources/*.h' - ] - - c_dir = root_dir_name + '/c/' - c_task_dir = c_dir + 'task/' - c_core_dir = c_task_dir + 'core/' - c_processor_dir = c_task_dir + 'processor/' - c_vision_dir = c_task_dir + 'vision/' - s.source_files = [ - c_dir + '*.h', - c_core_dir + '*.h', - c_processor_dir + '{category,bounding_box,classification_options,classification_result,detection_result,segmentation_result}.h', - c_vision_dir + '*.h', - c_vision_dir + 'core/*.h', - objc_dir + 'sources/*', - objc_core_dir + 'sources/*', - objc_processor_dir + 'sources/*', - objc_vision_dir + 'apis/', - objc_vision_dir + '*/sources/*', - objc_vision_dir + 'sources/*', - gml_image_dir + 'apis/*.h', - gml_image_dir + 'sources/*' - ] - - s.module_map = objc_vision_dir + 'apis/framework.modulemap' s.pod_target_xcconfig = { - 'HEADER_SEARCH_PATHS' => - '"${PODS_TARGET_SRCROOT}/' + root_dir_name + '/.."/**', - 'VALID_ARCHS' => 'x86_64 armv7 arm64', + 'VALID_ARCHS' => 'x86_64, arm64, armv7', } - s.library = 'c++' - s.vendored_frameworks = 'Frameworks/TensorFlowLiteTaskVisionC.framework' + s.frameworks = 'CoreMedia', 'Accelerate' + s.vendored_frameworks = 'Frameworks/TensorFlowLiteTaskVision.framework' end diff --git a/tensorflow_lite_support/ios/ios.bzl b/tensorflow_lite_support/ios/ios.bzl index cb8c92ac2..75362c57d 100644 --- a/tensorflow_lite_support/ios/ios.bzl +++ b/tensorflow_lite_support/ios/ios.bzl @@ -1,8 +1,8 @@ """TensorFlow Lite Support Library Helper Rules for iOS""" -# When the static framework is built with bazel, the all header files are moved +# When the static framework is built with bazel, all the header files are moved # to the "Headers" directory with no header path prefixes. This auxiliary rule -# is used for stripping the path prefix to the C API header files included by +# is used for stripping the path prefix from the C API header files included by # other C API header files. def strip_c_api_include_path_prefix(name, hdr_labels, prefix = ""): """Create modified header files with the common.h include path stripped out. @@ -28,3 +28,32 @@ def strip_c_api_include_path_prefix(name, hdr_labels, prefix = ""): > "$@" """.format(prefix, hdr_label), ) + +# When the static framework is built with bazel, all the header files are moved +# to the "Headers" directory with no header path prefixes. This auxiliary rule +# is used for stripping the path prefix from the iOS API header files imported by +# other iOS API header files. +def strip_ios_api_include_path_prefix(name, hdr_labels, prefix = ""): + """Create modified header files with the import path stripped out. + + Args: + name: The name to be used as a prefix to the generated genrules. + hdr_labels: List of header labels to strip out the include path. Each + label must end with a colon followed by the header file name. + prefix: Optional prefix path to prepend to the header inclusion path. + """ + for hdr_label in hdr_labels: + hdr_filename = hdr_label.split(":")[-1] + hdr_filename = hdr_filename.split("/")[-1] + hdr_basename = hdr_filename.split(".")[0] + native.genrule( + name = "{}_{}".format(name, hdr_basename), + srcs = [hdr_label], + outs = [hdr_filename], + cmd = """ + sed 's|#import ".*/\\([^/]\\{{1,\\}}\\.h\\)"|#import "{}\\1"|'\ + "$(location {})"\ + > "$@" + """.format(prefix, hdr_label), + ) + diff --git a/tensorflow_lite_support/ios/task/vision/BUILD b/tensorflow_lite_support/ios/task/vision/BUILD index 0a756dc56..6c7339d1b 100644 --- a/tensorflow_lite_support/ios/task/vision/BUILD +++ b/tensorflow_lite_support/ios/task/vision/BUILD @@ -72,3 +72,19 @@ objc_library( "//tensorflow_lite_support/odml/ios/image:MLImage", ], ) + +objc_library( + name = "TFLImageEmbedder", + srcs = [ + "sources/TFLImageEmbedder.mm", + ], + hdrs = [ + "sources/TFLImageEmbedder.h", + ], + features = ["-layering_check"], + module_name = "TFLImageEmbedder", + deps = [ + "//tensorflow_lite_support/cc/task/vision:image_embedder", + ], + copts = ["-ObjC++","-std=c++14"], +) diff --git a/tensorflow_lite_support/ios/task/vision/apis/TFLTaskVision.h b/tensorflow_lite_support/ios/task/vision/apis/TFLTaskVision.h deleted file mode 100644 index 3eb4f4964..000000000 --- a/tensorflow_lite_support/ios/task/vision/apis/TFLTaskVision.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 Google Inc. 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. - -#import "GMLImage.h" -#import "TFLBaseOptions.h" -#import "TFLCategory.h" -#import "TFLClassificationOptions.h" -#import "TFLClassificationResult.h" -#import "TFLCommon.h" -#import "TFLDetectionResult.h" -#import "TFLImageClassifier.h" -#import "TFLImageSegmenter.h" -#import "TFLObjectDetector.h" -#import "TFLSegmentationResult.h" diff --git a/tensorflow_lite_support/ios/task/vision/apis/framework.modulemap b/tensorflow_lite_support/ios/task/vision/apis/framework.modulemap deleted file mode 100644 index b89443cca..000000000 --- a/tensorflow_lite_support/ios/task/vision/apis/framework.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -framework module TensorFlowLiteTaskVision { - umbrella header "TFLTaskVision.h" - export * -} diff --git a/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h b/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h new file mode 100644 index 000000000..cc17766c4 --- /dev/null +++ b/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h @@ -0,0 +1,29 @@ +/* Copyright 2022 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. + ==============================================================================*/ +#import + + +NS_ASSUME_NONNULL_BEGIN + +/** + * A TensorFlow Lite Task Image Classifiier. + */ +NS_SWIFT_NAME(ImageEmbedder) +@interface TFLImageEmbedder : NSObject + +- (instancetype)initWithModelPath:(NSString *)modelPath; +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.mm b/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.mm new file mode 100644 index 000000000..78deec058 --- /dev/null +++ b/tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.mm @@ -0,0 +1,50 @@ +/* Copyright 2022 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. + ==============================================================================*/ + +#import "tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h" +#include "tensorflow_lite_support/cc/task/vision/image_embedder.h" + +namespace { +using ImageEmbedderCpp = ::tflite::task::vision::ImageEmbedder; +using ImageEmbedderOptionsCpp = + ::tflite::task::vision::ImageEmbedderOptions; +using ::tflite::support::StatusOr; +} + +@implementation TFLImageEmbedder { + std::unique_ptr cppImageEmbedder; +} + +- (instancetype)initWithModelPath:(NSString *)modelPath { + self = [super init]; + if (self) { + ImageEmbedderOptionsCpp cc_options; + + cc_options.mutable_model_file_with_metadata()->set_file_name(modelPath.UTF8String); + + StatusOr> embedder_status = + ImageEmbedderCpp::CreateFromOptions(cc_options); + + if (embedder_status.ok()) { + cppImageEmbedder = std::move(embedder_status.value()); + } + else { + return nil; + } + } + return self; +} + +@end diff --git a/tensorflow_lite_support/ios/test/task/vision/image_embedder/BUILD b/tensorflow_lite_support/ios/test/task/vision/image_embedder/BUILD new file mode 100644 index 000000000..74224e4ee --- /dev/null +++ b/tensorflow_lite_support/ios/test/task/vision/image_embedder/BUILD @@ -0,0 +1,36 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +load("@org_tensorflow//tensorflow/lite/ios:ios.bzl", "TFL_DEFAULT_TAGS", "TFL_DISABLED_SANITIZER_TAGS", "TFL_MINIMUM_OS_VERSION") +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") +load("@org_tensorflow//tensorflow/lite:special_rules.bzl", "tflite_ios_lab_runner") + +package( + default_visibility = ["//visibility:private"], + licenses = ["notice"], # Apache 2.0 +) + + +objc_library( + name = "TFLImageEmbedderObjcTestLibrary", + testonly = 1, + srcs = ["TFLImageEmbedderTests.m"], + data = [ + "//tensorflow_lite_support/cc/test/testdata/task/vision:test_images", + "//tensorflow_lite_support/cc/test/testdata/task/vision:test_models", + ], + tags = TFL_DEFAULT_TAGS, + deps = [ + "//tensorflow_lite_support/ios/task/vision:TFLImageEmbedder", + "//tensorflow_lite_support/ios/task/vision/utils:GMLImageUtils", + ], +) + +ios_unit_test( + name = "TFLImageEmbedderObjcTest", + minimum_os_version = TFL_MINIMUM_OS_VERSION, + runner = tflite_ios_lab_runner("IOS_LATEST"), + tags = TFL_DEFAULT_TAGS + TFL_DISABLED_SANITIZER_TAGS, + deps = [ + ":TFLImageEmbedderObjcTestLibrary", + ], +) + diff --git a/tensorflow_lite_support/ios/test/task/vision/image_embedder/TFLImageEmbedderTests.m b/tensorflow_lite_support/ios/test/task/vision/image_embedder/TFLImageEmbedderTests.m new file mode 100644 index 000000000..093c5f22c --- /dev/null +++ b/tensorflow_lite_support/ios/test/task/vision/image_embedder/TFLImageEmbedderTests.m @@ -0,0 +1,42 @@ +/* Copyright 2022 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. + ==============================================================================*/ +#import + +#import "tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TFLImageEmbedderTests : XCTestCase +@property(nonatomic, nullable) NSString *modelPath; +@end + +@implementation TFLImageEmbedderTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the + // class. + [super setUp]; + self.modelPath = [[NSBundle bundleForClass:self.class] pathForResource:@"mobilenet_v3_small_100_224_embedder" + ofType:@"tflite"]; + XCTAssertNotNil(self.modelPath); +} + +- (void)testInitSucceeds { + TFLImageEmbedder *embedder = [[TFLImageEmbedder alloc] initWithModelPath:self.modelPath]; + XCTAssertNotNil(embedder); +} +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow_lite_support/odml/ios/image/BUILD b/tensorflow_lite_support/odml/ios/image/BUILD index 24fff353f..63e3db87d 100644 --- a/tensorflow_lite_support/odml/ios/image/BUILD +++ b/tensorflow_lite_support/odml/ios/image/BUILD @@ -65,6 +65,8 @@ TEST_COPTS = RELEASE_COPTS + [ "-Wno-shadow", ] +exports_files(["apis/GMLImage.h"]) + objc_library( name = "MLImage", srcs = SOURCES, diff --git a/tensorflow_lite_support/tools/ci_build/builds/build_ios_vision_framework.sh b/tensorflow_lite_support/tools/ci_build/builds/build_ios_vision_framework.sh new file mode 100755 index 000000000..27fea03c6 --- /dev/null +++ b/tensorflow_lite_support/tools/ci_build/builds/build_ios_vision_framework.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# Copyright 2022 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 the following variables as appropriate. +# * BAZEL: path to bazel. defaults to the first one available in PATH +# * FRAMEWORK_NAME: name of the iOS framework to be built. Currently the +# * accepted values are TensorFlowLiteTaskVision, TensorFlowLiteTaskText. +# * TFLS_BUILD_VERSION: to specify the release version. defaults to 0.0.1-dev +# * IS_RELEASE_BUILD: set as true if this build should be a release build +# * ARCHIVE_FRAMEWORK: set as true if the framework should be archived +# * DEST_DIR: destination directory to which the framework will be copied + +set -ex + +if [[ "$(uname)" != "Darwin" ]]; then + echo "This build script only works on macOS." + exit 1 +fi + +BAZEL="${BAZEL:-$(which bazel)}" +TFLS_BUILD_VERSION=${TFLS_BUILD_VERSION:-0.0.1-dev} +TFLS_ROOT_DIR=$(git rev-parse --show-toplevel) + +if [[ ! -x "${BAZEL}" ]]; then + echo "bazel executable is not found." + exit 1 +fi + +if [ -z ${FRAMEWORK_NAME+x} ]; then + echo "Name of the iOS framework, which is to be built, must be set." + exit 1 +fi + +case $FRAMEWORK_NAME in + "TensorFlowLiteTaskVision") + ;; + *) + echo "Wrong framework name" + exit 1 + ;; +esac + +if [[ -z "${DEST_DIR+x}" || "${DEST_DIR}" == ${TFLS_ROOT_DIR}* ]]; then + echo "DEST_DIR variable must be set and not be under the repository root." + exit 1 +fi + +function build_ios_api_framework { + "${BAZEL}" build -c opt --config=ios_fat \ + //tensorflow_lite_support/ios:$1_framework +} + +function create_framework_archive { + TARGET_FRAMEWORK_NAME="$1" + # IOS_API_FRAMEWORK_NAME="$1C" + + TFLS_IOS_DIR=tensorflow_lite_support/ios + BAZEL_IOS_OUTDIR="bazel-bin/${TFLS_IOS_DIR}" + + # Change to the Bazel iOS output directory. + pushd "${BAZEL_IOS_OUTDIR}" + + # Create the temporary directory for the given framework. + ARCHIVE_NAME="${TARGET_FRAMEWORK_NAME}-${TFLS_BUILD_VERSION}" + TFLS_TMPDIR="$(mktemp -d)" + + # Copy the license file to TFLS_TMPDIR + cp "${TFLS_ROOT_DIR}/LICENSE" ${TFLS_TMPDIR} + FRAMEWORK_LOCATION=$(find "${TFLS_ROOT_DIR}/bazel-out/" -name ${TARGET_FRAMEWORK_NAME}_framework.zip) + + for path in ${FRAMEWORK_LOCATION} + do + unzip ${path} -d "${TFLS_TMPDIR}"/Frameworks + break + done + + #----- (3) Move the framework to the destination ----- + if [[ "${ARCHIVE_FRAMEWORK}" == true ]]; then + TARGET_DIR="$(realpath "${TARGET_FRAMEWORK_NAME}")" + + # Create the framework archive directory. + if [[ "${IS_RELEASE_BUILD}" == true ]]; then + # Get the first 16 bytes of the sha256 checksum of the root directory. + SHA256_CHECKSUM=$(find "${TFLS_TMPDIR}" -type f -print0 | xargs -0 shasum -a 256 | sort | shasum -a 256 | cut -c1-16) + FRAMEWORK_ARCHIVE_DIR="${TARGET_DIR}/${TFLS_BUILD_VERSION}/${SHA256_CHECKSUM}" + else + FRAMEWORK_ARCHIVE_DIR="${TARGET_DIR}/${TFLS_BUILD_VERSION}" + fi + mkdir -p "${FRAMEWORK_ARCHIVE_DIR}" + + # Zip up the framework and move to the archive directory. + pushd "${TFLS_TMPDIR}" + TFLS_ARCHIVE_FILE="${ARCHIVE_NAME}.tar.gz" + tar -cvzf "${TFLS_ARCHIVE_FILE}" . + mv "${TFLS_ARCHIVE_FILE}" "${FRAMEWORK_ARCHIVE_DIR}" + popd + + # Move the target directory to the Kokoro artifacts directory. + mv "${TARGET_DIR}" "$(realpath "${DEST_DIR}")"/ + else + rsync -r "${TFLS_TMPDIR}/" "$(realpath "${DEST_DIR}")/" + fi + + # Clean up the temporary directory for the framework. + rm -rf "${TFLS_TMPDIR}" + echo ${TFLS_TMPDIR} + + # Pop back to the TFLS root directory. + popd +} + +cd "${TFLS_ROOT_DIR}" +build_ios_api_framework $FRAMEWORK_NAME +create_framework_archive $FRAMEWORK_NAME +