Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bf4b7e9

Browse files
chuckxcopybara-github
authored andcommittedJun 12, 2024·
Implement a Bazel target to produce an XCFramework.
An initial bundle is produced by an apple_static_xcframework target and then passed through postprocess_xcframework.sh. The script performs a symbol hiding pass on the artifacts within the bundle. This is done primarily to hide BoringSSL symbols to prevent symbol conflicts in situations where an application is linked against e.g. OpenSSL. PiperOrigin-RevId: 642506193 Change-Id: Ida33a14e94ab51957449fee9338644ce650802db
1 parent ac96e6d commit bf4b7e9

File tree

3 files changed

+247
-17
lines changed

3 files changed

+247
-17
lines changed
 

‎Tink/BUILD.bazel

+43-17
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
load("@build_bazel_apple_support//rules:apple_genrule.bzl", "apple_genrule")
2+
load("@build_bazel_rules_apple//apple:apple.bzl", "apple_static_xcframework", "apple_xcframework")
13
load(
24
"@build_bazel_rules_apple//apple:ios.bzl",
35
"ios_application",
46
"ios_build_test",
5-
"ios_static_framework",
67
"ios_unit_test",
78
)
89
load("//:minimum_os.bzl", "IOS_MINIMUM_OS")
@@ -96,29 +97,54 @@ objc_library(
9697
deps = PUBLIC_API_DEPS,
9798
)
9899

100+
PUBLIC_AND_UNSAFE_APIS = PUBLIC_APIS + [
101+
"TINKKeysetHandle+Cleartext.h",
102+
]
103+
104+
PUBLIC_AND_UNSAFE_API_DEPS = PUBLIC_API_DEPS + [
105+
":cleartext_keyset_handle",
106+
]
107+
99108
objc_library(
100109
name = "testonly",
101110
testonly = 1,
102-
hdrs = PUBLIC_APIS + [
103-
"TINKKeysetHandle+Cleartext.h",
104-
],
111+
hdrs = PUBLIC_AND_UNSAFE_APIS,
105112
visibility = ["//visibility:public"],
106-
deps = PUBLIC_API_DEPS + [
107-
":cleartext_keyset_handle",
108-
],
113+
deps = PUBLIC_AND_UNSAFE_API_DEPS,
109114
)
110115

111-
ios_static_framework(
112-
name = "Tink_framework",
113-
hdrs = PUBLIC_APIS + [
114-
"TINKKeysetHandle+Cleartext.h",
115-
],
116+
apple_static_xcframework(
117+
name = "preprocessed_tink_static_xcframework",
116118
bundle_name = "Tink",
117-
minimum_os_version = IOS_MINIMUM_OS,
118-
deps = [
119-
":cleartext_keyset_handle",
120-
":objc",
121-
],
119+
ios = {
120+
"simulator": [
121+
"x86_64",
122+
"arm64",
123+
],
124+
"device": [
125+
"arm64",
126+
"arm64e",
127+
],
128+
},
129+
minimum_os_versions = {
130+
"ios": IOS_MINIMUM_OS,
131+
},
132+
public_hdrs = PUBLIC_AND_UNSAFE_APIS,
133+
deps = PUBLIC_AND_UNSAFE_API_DEPS,
134+
)
135+
136+
apple_genrule(
137+
name = "Tink_static_xcframework",
138+
srcs = [":preprocessed_tink_static_xcframework.xcframework.zip"],
139+
outs = ["Tink.xcframework.zip"],
140+
cmd = (
141+
"INPUT=\"$(execpath :preprocessed_tink_static_xcframework.xcframework.zip)\" " +
142+
"OUTPUT=\"$(OUTS)\" " +
143+
"BUNDLE_NAME=\"Tink\" " +
144+
"MINIMUM_IOS_VERSION=\"" + IOS_MINIMUM_OS + "\" " +
145+
"\"$(execpath //tools/release:postprocess_xcframework)\""
146+
),
147+
tools = ["//tools/release:postprocess_xcframework"],
122148
)
123149

124150
############################

‎tools/release/BUILD.bazel

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package(default_visibility = ["//:__subpackages__"])
2+
3+
licenses(["notice"])
4+
5+
sh_binary(
6+
name = "postprocess_xcframework",
7+
srcs = ["postprocess_xcframework.sh"],
8+
)
+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#!/bin/bash
2+
# Copyright 2024 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
################################################################################
16+
17+
# A script to postprocess the Tink XCFramework bundle.
18+
#
19+
# This script is meant to be invoked by the Bazel //Tink:Tink_static_xcframework
20+
# apple_genrule target. It is not meant to be invoked directly.
21+
#
22+
# The primary purpose is to hide all non-Objective-C symbols from each object
23+
# file in the bundle. Since Tink Obj-C wraps Tink C++ which includes BoringSSL,
24+
# there's high likelihood for symbol conflicts when it's included in projects
25+
# that link against OpenSSL. Symbol hiding avoids this issue.
26+
#
27+
# The ZIP file specified by the INPUT environment variable is extracted into a
28+
# temporary directory. The BUNDLE_NAME environment variable is used to help
29+
# navigate the directory tree. The ZIP file contents are post processed using
30+
# XCode tooling. The results are placed into a ZIP archive in the file specified
31+
# by the OUTPUT environment variable.
32+
33+
set -x
34+
set -e
35+
set -u
36+
37+
# The following environment variables must be set.
38+
39+
# The ZIP file which is initially read in and processed.
40+
if [[ -z "${INPUT}" ]]; then
41+
echo "The INPUT environment variable must be set."
42+
exit 4
43+
fi
44+
45+
# The resultant ZIP file which is produced.
46+
if [[ -z "${OUTPUT}" ]]; then
47+
echo "The OUTPUT environment variable must be set."
48+
exit 4
49+
fi
50+
51+
# An identifier used to help traverse the directory tree within the ZIP file.
52+
if [[ -z "${BUNDLE_NAME}" ]]; then
53+
echo "The BUNDLE_NAME environment variable must be set."
54+
exit 4
55+
fi
56+
57+
# An argument fed to XCode tooling.
58+
if [[ -z "${MINIMUM_IOS_VERSION}" ]]; then
59+
echo "The MINIMUM_IOS_VERSION environment variable must be set."
60+
exit 4
61+
fi
62+
63+
# Create working directory.
64+
readonly WORK_DIR="$(mktemp -d -t \
65+
tink_postprocess_xcframework-"$(date "+%Y%m%dT%H%M%S")")"
66+
# Clean up on exit.
67+
trap "rm -rf ${WORK_DIR}" EXIT
68+
69+
process_object_file() {
70+
local -r obj_file="$1"
71+
72+
local -r PLATFORM="$(dirname "${obj_file}" | awk -F/ '{print $(NF-1)}')"
73+
cp "${obj_file}" "${WORK_DIR}/Tink.${PLATFORM}"
74+
75+
# Get list of architectures.
76+
local archs=()
77+
IFS=' ' read -r -a archs < <( \
78+
xcrun lipo -info "${obj_file}" \
79+
| sed -En -e \
80+
's/(Non-|Architectures in the )fat file: .+( is architecture| are): (.*)$/\3/p' \
81+
)
82+
83+
local multiple_arches=false
84+
if [[ "${#archs[@]}" -gt 1 ]]; then
85+
multiple_arches=true
86+
fi
87+
readonly multiple_arches
88+
89+
local merge_cmd=( xcrun lipo )
90+
91+
# Postprocess each architecture.
92+
for arch in "${archs[@]}"; do
93+
process_architecture
94+
done
95+
96+
# Repackage processed object files.
97+
merge_cmd+=( -create -output "${obj_file}" )
98+
"${merge_cmd[@]}"
99+
}
100+
101+
# Child function of process_object_file. Assumes presence of the following
102+
# variables:
103+
# * objc_file
104+
# * arch
105+
# * multiple_arches
106+
# * merge_cmd
107+
process_architecture() {
108+
# Create a subdirectory for the architeture in the working directory.
109+
local -r ARCH_DIR="${WORK_DIR}/${PLATFORM}.${arch}"
110+
mkdir "${ARCH_DIR}"
111+
112+
# Extract the architecture specific object file.
113+
local -r ARCH_FILE="${ARCH_DIR}/${arch}"
114+
if [[ "${multiple_arches}" == "true" ]]; then
115+
xcrun lipo "${obj_file}" -thin "${arch}" -output "${ARCH_FILE}"
116+
else
117+
# Or copy it if it's already a single architecture file.
118+
cp "${obj_file}" "${ARCH_FILE}"
119+
fi
120+
121+
# Extract list of Obj-C classes.
122+
local -r SYMBOL_QUERY="$(cat << 'EOF'
123+
/ (D|S) _OBJC_CLASS_/ { print $3 }
124+
/ (D|S) _OBJC_METACLASS_/ { print $3 }
125+
/ (D|S) _TINKVersion/ { print $3 }
126+
EOF
127+
)"
128+
local -r EXPORTED_SYMBOLS_FILE="${ARCH_DIR}/exported_symbols"
129+
xcrun nm -g "${ARCH_FILE}" \
130+
| awk "${SYMBOL_QUERY}" \
131+
| sort -u \
132+
> "${EXPORTED_SYMBOLS_FILE}"
133+
134+
# Hide all other symbols.
135+
local ld_args=()
136+
137+
# Check whether bitcode is present in all sections of the object file.
138+
# TODO: b/335778128 - Verify if this workaround is still necessary.
139+
local -r BITCODE_CHECK="$(cat << 'EOF'
140+
/^Sections:/ { sects += 1 }
141+
/ __bitcode|__asm / { bcs += 1 }
142+
END { if ( sects != bcs ) exit(1) }
143+
EOF
144+
)"
145+
if objdump --macho --section-headers "${ARCH_FILE}" \
146+
| awk "${BITCODE_CHECK}"; then
147+
ld_args+=( -bitcode_bundle )
148+
fi
149+
150+
if [[ "${PLATFORM}" =~ .*simulator ]]; then
151+
ld_args+=( -ios_simulator_version_min )
152+
else
153+
ld_args+=( -ios_version_min )
154+
fi
155+
ld_args+=( "${MINIMUM_IOS_VERSION}" )
156+
readonly ld_args
157+
158+
xcrun ld -r "${ld_args[@]}" \
159+
-force_load "${ARCH_FILE}" \
160+
-exported_symbols_list "${EXPORTED_SYMBOLS_FILE}" \
161+
-arch "${arch}" \
162+
-o "${ARCH_FILE}_processed.o"
163+
164+
# Capture output.
165+
merge_cmd+=( -arch "${arch}" "${ARCH_FILE}_processed.o" )
166+
}
167+
168+
main() {
169+
# Extract bundle.
170+
readonly XCFRAMEWORK_DIR="${WORK_DIR}/xcframework"
171+
unzip -qq "${INPUT}" -d "${XCFRAMEWORK_DIR}"
172+
173+
# Find object files within the bundle.
174+
declare -a OBJ_FILES
175+
while IFS=() read -r -d $'\0' obj_file; do
176+
OBJ_FILES+=("${obj_file}")
177+
done < <(find "${XCFRAMEWORK_DIR}" -name "${BUNDLE_NAME}" -print0)
178+
readonly OBJ_FILES
179+
180+
# Postprocess each object file.
181+
for obj_file in "${OBJ_FILES[@]}"; do
182+
process_object_file "${obj_file}"
183+
done
184+
185+
# Repackage bundle.
186+
(
187+
cd "${XCFRAMEWORK_DIR}"
188+
# Zero timestamps to produce determnistic outputs.
189+
# The ZIP file foramt uses 2-byte DOS time format with a 1980 epoch.
190+
TZ=UTC find . -exec touch -h -t 198001010000 {} \+
191+
zip --compression-method store --symlinks --recurse-paths --quiet \
192+
"${OLDPWD}/${OUTPUT}" .
193+
)
194+
}
195+
196+
main "$@"

0 commit comments

Comments
 (0)