Browse Source

Merge pull request #581 from adotkhan/master

Updated iOS library bundle type to XCFramework
Rod Hynes 5 years ago
parent
commit
04e5cb735e

+ 1 - 3
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel.xcodeproj/project.pbxproj

@@ -17,7 +17,6 @@
 		6685BDCD1E2E88A200F0E414 /* Psi-meta.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDCC1E2E88A200F0E414 /* Psi-meta.h */; };
 		6685BDCD1E2E88A200F0E414 /* Psi-meta.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDCC1E2E88A200F0E414 /* Psi-meta.h */; };
 		6685BDD41E2EBB1000F0E414 /* GoPsi.objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDD21E2EBB1000F0E414 /* GoPsi.objc.h */; };
 		6685BDD41E2EBB1000F0E414 /* GoPsi.objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDD21E2EBB1000F0E414 /* GoPsi.objc.h */; };
 		6685BDD51E2EBB1000F0E414 /* Universe.objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDD31E2EBB1000F0E414 /* Universe.objc.h */; };
 		6685BDD51E2EBB1000F0E414 /* Universe.objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6685BDD31E2EBB1000F0E414 /* Universe.objc.h */; };
-		6685BDD91E300AC200F0E414 /* strip-frameworks.sh in Resources */ = {isa = PBXBuildFile; fileRef = 6685BDD81E300AC200F0E414 /* strip-frameworks.sh */; };
 		669541B71EF9FECF0038E125 /* build-git-commit.txt in Resources */ = {isa = PBXBuildFile; fileRef = 669541B61EF9FECF0038E125 /* build-git-commit.txt */; };
 		669541B71EF9FECF0038E125 /* build-git-commit.txt in Resources */ = {isa = PBXBuildFile; fileRef = 669541B61EF9FECF0038E125 /* build-git-commit.txt */; };
 		66BAD3351E525FBC00CD06DE /* JailbreakCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 66BAD3331E525FBC00CD06DE /* JailbreakCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		66BAD3351E525FBC00CD06DE /* JailbreakCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 66BAD3331E525FBC00CD06DE /* JailbreakCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		66BAD3361E525FBC00CD06DE /* JailbreakCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BAD3341E525FBC00CD06DE /* JailbreakCheck.m */; };
 		66BAD3361E525FBC00CD06DE /* JailbreakCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 66BAD3341E525FBC00CD06DE /* JailbreakCheck.m */; };
@@ -428,7 +427,6 @@
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
 				669541B71EF9FECF0038E125 /* build-git-commit.txt in Resources */,
 				669541B71EF9FECF0038E125 /* build-git-commit.txt in Resources */,
-				6685BDD91E300AC200F0E414 /* strip-frameworks.sh in Resources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -453,7 +451,7 @@
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellPath = /bin/sh;
-			shellScript = "git rev-parse --verify HEAD > build-git-commit.txt";
+			shellScript = "git rev-parse --verify HEAD > build-git-commit.txt\n";
 		};
 		};
 /* End PBXShellScriptBuildPhase section */
 /* End PBXShellScriptBuildPhase section */
 
 

+ 0 - 72
MobileLibrary/iOS/PsiphonTunnel/scripts/strip-frameworks.sh

@@ -1,72 +0,0 @@
-################################################################################
-#
-# Copyright 2015 Realm Inc.
-#
-# 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.
-#
-################################################################################
-
-# This script strips all non-valid architectures from dynamic libraries in
-# the application's `Frameworks` directory.
-#
-# The following environment variables are required:
-#
-# BUILT_PRODUCTS_DIR
-# FRAMEWORKS_FOLDER_PATH
-# VALID_ARCHS
-# EXPANDED_CODE_SIGN_IDENTITY
-
-
-# Signs a framework with the provided identity
-code_sign() {
-  # Use the current code_sign_identitiy
-  echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
-  echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements $1"
-  /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
-}
-
-# Set working directory to product’s embedded frameworks 
-cd "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}"
-
-if [ "$ACTION" = "install" ]; then
-  echo "Copy .bcsymbolmap files to .xcarchive"
-  find . -name '*.bcsymbolmap' -type f -exec mv {} "${CONFIGURATION_BUILD_DIR}" \;
-else
-  # Delete *.bcsymbolmap files from framework bundle unless archiving
-  find . -name '*.bcsymbolmap' -type f -exec rm -rf "{}" +\;
-fi
-
-echo "Stripping frameworks"
-
-for file in $(find . -type f -perm +111); do
-  # Skip non-dynamic libraries
-  if ! [[ "$(file "$file")" == *"dynamically linked shared library"* ]]; then
-    continue
-  fi
-  # Get architectures for current file
-  archs="$(lipo -info "${file}" | rev | cut -d ':' -f1 | rev)"
-  stripped=""
-  for arch in $archs; do
-    if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
-      # Strip non-valid architectures in-place
-      lipo -remove "$arch" -output "$file" "$file" || exit 1
-      stripped="$stripped $arch"
-    fi
-  done
-  if [[ "$stripped" != "" ]]; then
-    echo "Stripped $file of architectures:$stripped"
-    if [ "${CODE_SIGNING_REQUIRED}" == "YES" ]; then
-      code_sign "${file}"
-    fi
-  fi
-done

+ 8 - 11
MobileLibrary/iOS/USAGE.md

@@ -6,9 +6,14 @@ Psiphon Library for iOS enables you to easily embed Psiphon in your iOS app.
 You can then tunnel requests through Psiphon, ensuring that your app can't be
 You can then tunnel requests through Psiphon, ensuring that your app can't be
 blocked by censors.
 blocked by censors.
 
 
-The Psiphon Library is available as a `.framework` that can be easily included
+The Psiphon Library is available as a XCFramework bundle `.xcframework` that can be easily included
 in your project using these instructions.
 in your project using these instructions.
 
 
+## Requirements
+
+Psiphon Library for iOS requires Xcode 11 or above.
+If using CocoaPods, CocoaPods version 1.10 or greater is required.
+
 ## Using the Library in your App
 ## Using the Library in your App
 
 
 **First step:** Review the sample app, located under `SampleApps`.
 **First step:** Review the sample app, located under `SampleApps`.
@@ -20,7 +25,7 @@ This code is a canonical guide for integrating the Library.
 
 
 1. Get the latest iOS release from the project's [Releases](https://github.com/Psiphon-Labs/psiphon-tunnel-core/releases) page.
 1. Get the latest iOS release from the project's [Releases](https://github.com/Psiphon-Labs/psiphon-tunnel-core/releases) page.
 
 
-2. Add `PsiphonTunnel.framework` to project (drag into project tree).
+2. Add `PsiphonTunnel.xcframework` to project (drag into project tree).
 
 
 3. In the "General" settings for the target, set "Deployment Target" to 9.3.
 3. In the "General" settings for the target, set "Deployment Target" to 9.3.
 
 
@@ -28,15 +33,7 @@ This code is a canonical guide for integrating the Library.
 
 
 5. In the "Build Settings" for the target, click the `+` at the top, then "Add User-Defined Setting". Name the new setting `STRIP_BITCODE_FROM_COPIED_FILES` and set it to `NO`.
 5. In the "Build Settings" for the target, click the `+` at the top, then "Add User-Defined Setting". Name the new setting `STRIP_BITCODE_FROM_COPIED_FILES` and set it to `NO`.
 
 
-6. In the "Build Phases" for the target, add a "Copy Files" phase. Set "Destination" to "Frameworks". Add `PsiphonTunnel.framework` to the list. Ensure "Code Sign on Copy" is checked.
-
-7. In the "Build Phases" for the target, add a "Run Script" phase. Set the script contents to:
-
-   ```no-highlight
-   bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/PsiphonTunnel.framework/strip-frameworks.sh"
-   ```
-
-   This step is required to work around an [App Store submission validation bug](http://www.openradar.me/23681704) that rejects apps containing a framework with simulator slices.
+6. In the "Build Phases" for the target, add a "Copy Files" phase. Set "Destination" to "Frameworks". Add `PsiphonTunnel.xcframework` to the list. Ensure "Code Sign on Copy" is checked.
 
 
 ## Compiling and testing
 ## Compiling and testing
 
 

+ 156 - 71
MobileLibrary/iOS/build-psiphon-framework.sh

@@ -20,7 +20,7 @@ PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin
 PATH=$GOROOT/bin:$PATH
 PATH=$GOROOT/bin:$PATH
 
 
 BASE_DIR=$(cd "$(dirname "$0")" ; pwd -P)
 BASE_DIR=$(cd "$(dirname "$0")" ; pwd -P)
-cd ${BASE_DIR}
+cd "${BASE_DIR}"
 
 
 # The location of the final framework build
 # The location of the final framework build
 BUILD_DIR="${BASE_DIR}/build"
 BUILD_DIR="${BASE_DIR}/build"
@@ -32,13 +32,6 @@ if [[ $? != 0 ]]; then
   exit 1
   exit 1
 fi
 fi
 
 
-VALID_IOS_ARCHS="arm64 armv7 armv7s"
-VALID_SIMULATOR_ARCHS="x86_64"
-FRAMEWORK="Psi"
-INTERMEDIATE_OUPUT_DIR="${BASE_DIR}/PsiphonTunnel/PsiphonTunnel"
-INTERMEDIATE_OUPUT_FILE="${FRAMEWORK}.framework"
-FRAMEWORK_BINARY="${INTERMEDIATE_OUPUT_DIR}/${INTERMEDIATE_OUPUT_FILE}/Versions/A/${FRAMEWORK}"
-
 UMBRELLA_FRAMEWORK_XCODE_PROJECT=${BASE_DIR}/PsiphonTunnel/PsiphonTunnel.xcodeproj/
 UMBRELLA_FRAMEWORK_XCODE_PROJECT=${BASE_DIR}/PsiphonTunnel/PsiphonTunnel.xcodeproj/
 
 
 # Exporting these seems necessary for subcommands to pick them up.
 # Exporting these seems necessary for subcommands to pick them up.
@@ -46,7 +39,7 @@ export GOPATH=${PWD}/go-ios-build
 export PATH=${GOPATH}/bin:${PATH}
 export PATH=${GOPATH}/bin:${PATH}
 
 
 # The GOPATH we're using is temporary, so make sure there isn't one from a previous run.
 # The GOPATH we're using is temporary, so make sure there isn't one from a previous run.
-rm -rf ${GOPATH}
+rm -rf "${GOPATH}"
 
 
 GOMOBILE_PINNED_REV=92f3b9caf7ba8f4f9c10074225afcba0cba47a62
 GOMOBILE_PINNED_REV=92f3b9caf7ba8f4f9c10074225afcba0cba47a62
 GOMOBILE_PATH=${GOPATH}/src/golang.org/x/mobile/cmd/gomobile
 GOMOBILE_PATH=${GOPATH}/src/golang.org/x/mobile/cmd/gomobile
@@ -55,7 +48,7 @@ TUNNEL_CORE_SRC_DIR=${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core
 
 
 PATH=${PATH}:${GOPATH}/bin
 PATH=${PATH}:${GOPATH}/bin
 
 
-mkdir -p ${GOPATH}
+mkdir -p "${GOPATH}"
 if [[ $? != 0 ]]; then
 if [[ $? != 0 ]]; then
   echo "FAILURE: mkdir -p ${GOPATH}"
   echo "FAILURE: mkdir -p ${GOPATH}"
   exit 1
   exit 1
@@ -63,7 +56,7 @@ fi
 
 
 # Symlink the current source directory into GOPATH, so that we're building the
 # Symlink the current source directory into GOPATH, so that we're building the
 # code in this local repo, rather than pulling from Github and building that.
 # code in this local repo, rather than pulling from Github and building that.
-mkdir -p ${GOPATH}/src/github.com/Psiphon-Labs
+mkdir -p "${GOPATH}/src/github.com/Psiphon-Labs"
 if [[ $? != 0 ]]; then
 if [[ $? != 0 ]]; then
   echo "mkdir -p ${GOPATH}/src/github.com/Psiphon-Labs"
   echo "mkdir -p ${GOPATH}/src/github.com/Psiphon-Labs"
   exit 1
   exit 1
@@ -74,32 +67,71 @@ if [[ $? != 0 ]]; then
   exit 1
   exit 1
 fi
 fi
 
 
-mkdir -p ${INTERMEDIATE_OUPUT_DIR}
-if [[ $? != 0 ]]; then
-  echo "FAILURE: mkdir -p ${INTERMEDIATE_OUPUT_DIR}"
-  exit 1
-fi
+# Builds Psi.framework library for the given platform.
+#
+# - Parameter 1: Possible values are "ios" and "simulator"
+# - Parameter 2: Variable name to set output path to.
+function gomobile_build_for_platform() {
+
+  # Possible values are "ios" and "simulator"
+  local PLATFORM=$1
+
+  local TARGETS=""
+
+  # gomobile pinned version 92f3b9c list of
+  # valid archs are "arm", "arm64", "386", "amd64".
+  # https://github.com/golang/mobile/blob/92f3b9caf7ba8f4f9c10074225afcba0cba47a62/cmd/gomobile/env.go#L26
+
+  case "${PLATFORM}" in
+    ios)
+      TARGETS="ios/arm,ios/arm64" 
+      ;;
+    simulator)
+      TARGETS="ios/amd64"
+      ;;
+    *)
+      echo "FATAL ERROR: Unknown platform ${PLATFORM}"
+      exit 1
+      ;;
+  esac
+
+  echo "Build library for platform ${PLATFORM}"
 
 
-# arg: binary_path
-function strip_architectures() {
-  valid_archs="${VALID_IOS_ARCHS} ${VALID_SIMULATOR_ARCHS}"
-  ARCHS="$(lipo -info "$1" | rev | cut -d ':' -f1 | rev)"
-  for ARCH in "${valid_archs}"; do
-    if ! [[ "${valid_archs}" == *"$ARCH"* ]]; then
-      echo "Stripping ARCH ${ARCH} from $1"
-      lipo -remove "$ARCH" -output "$1" "$1"
-      if [[ $? != 0 ]]; then
-        echo "FAILURE: lipo $1"
-        exit 1
-      fi
-    fi
-  done
-  return 0
+  local FRAMEWORK="Psi"
+
+  # Since frameworks for all platforms share the same name "Psi.framework",
+  # each framework should be in its own directory.
+  local INTERMEDIATE_OUPUT_DIR="${BUILD_DIR}/${PLATFORM}-psi-framework"
+
+  local INTERMEDIATE_OUPUT_FILE="${FRAMEWORK}.framework"
+  # local FRAMEWORK_BINARY="${INTERMEDIATE_OUPUT_DIR}/${INTERMEDIATE_OUPUT_FILE}/Versions/A/${FRAMEWORK}"
+
+  local GOBIND_OUT="${INTERMEDIATE_OUPUT_DIR}/${INTERMEDIATE_OUPUT_FILE}"
+
+  # We're using a generated-code prefix to workaround https://github.com/golang/go/issues/18693
+  # CGO_CFLAGS_ALLOW is to workaround https://github.com/golang/go/issues/23742 in Go 1.9.4
+  CGO_CFLAGS_ALLOW="-fmodules|-fblocks" "${GOPATH}"/bin/gomobile bind -v -x \
+  -target "${TARGETS}" \
+  -prefix Go \
+  -ldflags="${LDFLAGS}" \
+  -o "${GOBIND_OUT}" github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
+
+  rc=$?; if [[ $rc != 0 ]]; then
+    echo "FAILURE: gomobile bind failed".
+    exit $rc
+  fi
+
+
+  # Sets parameter $2 to value of GOBIND_OUT.
+  eval "$2=${GOBIND_OUT}"
 }
 }
 
 
+#
 # Check Go version
 # Check Go version
+#
+
 GO_VERSION=$(go version | sed -E -n 's/.*go([0-9]\.[0-9]+(\.[0-9]+)?).*/\1/p')
 GO_VERSION=$(go version | sed -E -n 's/.*go([0-9]\.[0-9]+(\.[0-9]+)?).*/\1/p')
-if [[ ${GO_VERSION} != ${GO_VERSION_REQUIRED} ]]; then
+if [[ ${GO_VERSION} != "${GO_VERSION_REQUIRED}" ]]; then
   echo "FAILURE: go version mismatch; require ${GO_VERSION_REQUIRED}; got ${GO_VERSION}"
   echo "FAILURE: go version mismatch; require ${GO_VERSION_REQUIRED}; got ${GO_VERSION}"
   exit 1
   exit 1
 fi
 fi
@@ -109,12 +141,12 @@ fi
 #
 #
 
 
 go get -u golang.org/x/mobile/cmd/gomobile
 go get -u golang.org/x/mobile/cmd/gomobile
-cd ${GOPATH}/src/golang.org/x/mobile/cmd/gomobile
+cd "${GOPATH}"/src/golang.org/x/mobile/cmd/gomobile
 git checkout master
 git checkout master
 git checkout -b pinned ${GOMOBILE_PINNED_REV}
 git checkout -b pinned ${GOMOBILE_PINNED_REV}
 
 
 go install
 go install
-${GOPATH}/bin/gomobile init -v -x
+"${GOPATH}"/bin/gomobile init -v -x
 if [[ $? != 0 ]]; then
 if [[ $? != 0 ]]; then
   echo "FAILURE: ${GOPATH}/bin/gomobile init"
   echo "FAILURE: ${GOPATH}/bin/gomobile init"
   exit 1
   exit 1
@@ -125,17 +157,17 @@ fi
 #
 #
 
 
 # Ensure BUILD* variables reflect the tunnel-core repo
 # Ensure BUILD* variables reflect the tunnel-core repo
-cd ${TUNNEL_CORE_SRC_DIR}
+cd "${TUNNEL_CORE_SRC_DIR}"
 
 
 BUILDINFOFILE="${BASE_DIR}/psiphon-tunnel-core_buildinfo.txt"
 BUILDINFOFILE="${BASE_DIR}/psiphon-tunnel-core_buildinfo.txt"
 BUILDDATE=$(date +%Y-%m-%dT%H:%M:%S%z)
 BUILDDATE=$(date +%Y-%m-%dT%H:%M:%S%z)
 BUILDREPO=$(git config --get remote.origin.url)
 BUILDREPO=$(git config --get remote.origin.url)
 BUILDREV=$(git rev-parse --short HEAD)
 BUILDREV=$(git rev-parse --short HEAD)
 GOVERSION=$(go version | perl -ne '/go version (.*?) / && print $1')
 GOVERSION=$(go version | perl -ne '/go version (.*?) / && print $1')
-GOMOBILEVERSION=$(${GOPATH}/bin/gomobile version | perl -ne '/gomobile version (.*?) / && print $1')
+GOMOBILEVERSION=$("${GOPATH}"/bin/gomobile version | perl -ne '/gomobile version (.*?) / && print $1')
 
 
 # see DEPENDENCIES comment in MobileLibrary/Android/make.bash
 # see DEPENDENCIES comment in MobileLibrary/Android/make.bash
-cd ${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
+cd "${GOPATH}"/src/github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
 DEPENDENCIES=$(echo -n "{" && GOOS=darwin go list -tags "${BUILD_TAGS}" -f '{{range $dep := .Deps}}{{printf "%s\n" $dep}}{{end}}' | GOOS=darwin xargs go list -tags "${BUILD_TAGS}" -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs -I pkg bash -c 'cd $GOPATH/src/$0 && if echo -n "$0" | grep -vEq "^github.com/Psiphon-Labs/psiphon-tunnel-core/" ; then echo -n "\"$0\":\"$(git rev-parse --short HEAD)\"," ; fi' pkg | sed 's/,$//' | tr -d '\n' && echo -n "}")
 DEPENDENCIES=$(echo -n "{" && GOOS=darwin go list -tags "${BUILD_TAGS}" -f '{{range $dep := .Deps}}{{printf "%s\n" $dep}}{{end}}' | GOOS=darwin xargs go list -tags "${BUILD_TAGS}" -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs -I pkg bash -c 'cd $GOPATH/src/$0 && if echo -n "$0" | grep -vEq "^github.com/Psiphon-Labs/psiphon-tunnel-core/" ; then echo -n "\"$0\":\"$(git rev-parse --short HEAD)\"," ; fi' pkg | sed 's/,$//' | tr -d '\n' && echo -n "}")
 
 
 LDFLAGS="\
 LDFLAGS="\
@@ -149,7 +181,7 @@ LDFLAGS="\
 -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/buildinfo.dependencies=${DEPENDENCIES} \
 -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/buildinfo.dependencies=${DEPENDENCIES} \
 "
 "
 
 
-echo -e "${BUILDDATE}\n${BUILDREPO}\n${BUILDREV}\n" > $BUILDINFOFILE
+echo -e "${BUILDDATE}\n${BUILDREPO}\n${BUILDREV}\n" > "$BUILDINFOFILE"
 
 
 echo ""
 echo ""
 echo "Variables for ldflags:"
 echo "Variables for ldflags:"
@@ -160,52 +192,105 @@ echo " Go version: ${GOVERSION}"
 echo " Gomobile version: ${GOMOBILEVERSION}"
 echo " Gomobile version: ${GOMOBILEVERSION}"
 echo ""
 echo ""
 
 
-# We're using a generated-code prefix to workaround https://github.com/golang/go/issues/18693
-# CGO_CFLAGS_ALLOW is to workaround https://github.com/golang/go/issues/23742 in Go 1.9.4
-CGO_CFLAGS_ALLOW="-fmodules|-fblocks" ${GOPATH}/bin/gomobile bind -v -x -target ios -prefix Go -tags="${BUILD_TAGS}" -ldflags="${LDFLAGS}" -o "${INTERMEDIATE_OUPUT_DIR}/${INTERMEDIATE_OUPUT_FILE}" github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
-rc=$?; if [[ $rc != 0 ]]; then
-  echo "FAILURE: ${GOPATH}/bin/gomobile bind -target ios -tags="${BUILD_TAGS}" -ldflags="${LDFLAGS}" -o "${INTERMEDIATE_OUPUT_DIR}/${INTERMEDIATE_OUPUT_FILE}" github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi"
-  exit $rc
-fi
 
 
-strip_architectures "${FRAMEWORK_BINARY}"
+#
+# Clean previous output
+#
+rm -rf "${BUILD_DIR}"
+
 
 
 #
 #
-# Do the outer framework build using Xcode
+# Builds Psi.framework for each platform/variant.
 #
 #
+IOS_PSI_FRAMEWORK=""
+gomobile_build_for_platform "ios" IOS_PSI_FRAMEWORK
 
 
-# Clean previous output
-rm -rf "${BUILD_DIR}"
-rm -rf "${BUILD_DIR}-SIMULATOR"
+SIMULATOR_PSI_FRAMEWORK=""
+gomobile_build_for_platform "simulator" SIMULATOR_PSI_FRAMEWORK
 
 
-# Build the outer framework for phones...
-xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO" -configuration Release -sdk iphoneos ONLY_ACTIVE_ARCH=NO -project ${UMBRELLA_FRAMEWORK_XCODE_PROJECT} CONFIGURATION_BUILD_DIR="${BUILD_DIR}"
-rc=$?; if [[ $rc != 0 ]]; then
-  echo "FAILURE: xcodebuild iphoneos"
-  exit $rc
-fi
 
 
-# ...and for the simulator.
-xcodebuild clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO" -configuration Release -sdk iphonesimulator ARCHS=x86_64 VALID_ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO -project ${UMBRELLA_FRAMEWORK_XCODE_PROJECT} CONFIGURATION_BUILD_DIR="${BUILD_DIR}-SIMULATOR"
-rc=$?; if [[ $rc != 0 ]]; then
-  echo "FAILURE: xcodebuild iphonesimulator"
-  exit $rc
-fi
+#
+# Xcode archive for each platform.
+#
 
 
-# Add the simulator x86_64 binary into the main framework binary.
-lipo -create "${BUILD_DIR}/PsiphonTunnel.framework/PsiphonTunnel" "${BUILD_DIR}-SIMULATOR/PsiphonTunnel.framework/PsiphonTunnel" -output "${BUILD_DIR}/PsiphonTunnel.framework/PsiphonTunnel"
-rc=$?; if [[ $rc != 0 ]]; then
-  echo "FAILURE: lipo create"
-  exit $rc
-fi
+# Xcode project requires Psi.framework bundle at $PSI_FRAMEWORK.
+# Except for macOS, Apple does not support umbrella frameworks that
+# contain other frameworks. So for building framework for each platform/variant,
+# the Psi.framework should be copied to the path at $PSI_FRAMEWORK_PATH.
+PSI_FRAMEWORK_PATH="${BASE_DIR}/PsiphonTunnel/PsiphonTunnel"
+PSI_FRAMEWORK="${PSI_FRAMEWORK_PATH}/Psi.framework"
+
+
+# Build PsiphonTunnel framework for iOS.
+echo "$IOS_PSI_FRAMEWORK"
+echo "$SIMULATOR_PSI_FRAMEWORK"
+
+# Copies iOS Psi.framework 
+rm -rf "${PSI_FRAMEWORK}"
+cp -r "${IOS_PSI_FRAMEWORK}" "${PSI_FRAMEWORK_PATH}"
+
+IOS_ARCHIVE="${BUILD_DIR}/ios.xcarchive"
+
+xcodebuild clean archive \
+-project "${UMBRELLA_FRAMEWORK_XCODE_PROJECT}" \
+-scheme "PsiphonTunnel" \
+-configuration "Release" \
+-sdk iphoneos \
+-archivePath "${IOS_ARCHIVE}" \
+CODE_SIGN_IDENTITY="" \
+CODE_SIGNING_REQUIRED="NO" \
+CODE_SIGN_ENTITLEMENTS="" \
+CODE_SIGNING_ALLOWED="NO" \
+STRIP_BITCODE_FROM_COPIED_FILES="NO" \
+BUILD_LIBRARY_FOR_DISTRIBUTION="YES" \
+ONLY_ACTIVE_ARCH="NO" \
+SKIP_INSTALL="NO"
+
+# Build PsiphonTunnel framework for simulator.
+#
+# Note:
+# - Excludes 32-bit Intel: EXCLUDED_ARCHS="i386".
+# - Excludes ARM Macs: EXCLUDED_ARCHS="arm64".
+
+# Copies simulator Psi.framework 
+rm -rf "${PSI_FRAMEWORK}"
+cp -r "${SIMULATOR_PSI_FRAMEWORK}" "${PSI_FRAMEWORK_PATH}"
+
+SIMULATOR_ARCHIVE="${BUILD_DIR}/simulator.xcarchive"
+
+xcodebuild clean archive \
+-project "${UMBRELLA_FRAMEWORK_XCODE_PROJECT}" \
+-scheme "PsiphonTunnel" \
+-configuration "Release" \
+-sdk iphonesimulator \
+-archivePath "${SIMULATOR_ARCHIVE}" \
+CODE_SIGN_IDENTITY="" \
+CODE_SIGNING_REQUIRED="NO" \
+CODE_SIGN_ENTITLEMENTS="" \
+CODE_SIGNING_ALLOWED="NO" \
+STRIP_BITCODE_FROM_COPIED_FILES="NO" \
+BUILD_LIBRARY_FOR_DISTRIBUTION="YES" \
+ONLY_ACTIVE_ARCH="NO" \
+SKIP_INSTALL="NO" \
+EXCLUDED_ARCHS="arm64 i386"
+
+#
+# Building PsiphonTunnel.xcframework
+#
+
+xcodebuild -create-xcframework \
+-framework "${IOS_ARCHIVE}/Products/Library/Frameworks/PsiphonTunnel.framework" \
+-debug-symbols "${IOS_ARCHIVE}/dSYMs/PsiphonTunnel.framework.dSYM" \
+-framework "${SIMULATOR_ARCHIVE}/Products/Library/Frameworks/PsiphonTunnel.framework" \
+-debug-symbols "${SIMULATOR_ARCHIVE}/dSYMs/PsiphonTunnel.framework.dSYM" \
+-output "${BUILD_DIR}/PsiphonTunnel.xcframework"
 
 
-# Delete the temporary simulator build files.
-rm -rf "${BUILD_DIR}-SIMULATOR"
 
 
 # Jenkins loses symlinks from the framework directory, which results in a build
 # Jenkins loses symlinks from the framework directory, which results in a build
 # artifact that is invalid to use in an App Store app. Instead, we will zip the
 # artifact that is invalid to use in an App Store app. Instead, we will zip the
 # resulting build and use that as the artifact.
 # resulting build and use that as the artifact.
 cd "${BUILD_DIR}"
 cd "${BUILD_DIR}"
-zip --recurse-paths --symlinks build.zip * --exclude "*.DS_Store"
+
+zip --recurse-paths --symlinks build.zip ./PsiphonTunnel.xcframework --exclude "*.DS_Store"
 
 
 echo "BUILD DONE"
 echo "BUILD DONE"