Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions scripts/ci/build-host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,15 @@ smoke_test_bootstrap() {
local -a run=(${2:-})
digest=$(awk '{print $1}' "$bootstrap_archive.sha256")
install_root="$PWD/bootstrap-installed"
# ${run[@]+...}: macOS's /bin/bash is 3.2, where expanding an empty array
# under set -u is an unbound-variable error -- and empty is the normal
# case here, for every host that runs its own binaries.
VITASDK_BOOTSTRAP_ARCHIVE="$bootstrap_archive" VITASDK_BOOTSTRAP_SHA256="$digest" \
"${run[@]}" build/vitasdk/share/vdpm/bootstrap-vitasdk.sh --install-dir "$install_root"
VITASDK="$install_root" "${run[@]}" "$install_root/bin/vdpm" --help >/dev/null
${run[@]+"${run[@]}"} build/vitasdk/share/vdpm/bootstrap-vitasdk.sh --install-dir "$install_root"
VITASDK="$install_root" ${run[@]+"${run[@]}"} "$install_root/bin/vdpm" --help >/dev/null
# vdpm ships pacman under libexec/vdpm, not bin/.
"${run[@]}" "$install_root/libexec/vdpm/pacman" --version >/dev/null
"${run[@]}" "$install_root/bin/arm-vita-eabi-gcc" --version
${run[@]+"${run[@]}"} "$install_root/libexec/vdpm/pacman" --version >/dev/null
${run[@]+"${run[@]}"} "$install_root/bin/arm-vita-eabi-gcc" --version
}

# Builds against $stage1_dir if set, then stages outputs plus provenance.
Expand Down Expand Up @@ -194,8 +197,8 @@ build_and_stage() {
targets+=(core-package bootstrap-archive)
fi

cmake "${cmake_args[@]}"
cmake --build build --target "${targets[@]}" --parallel "$(ci_nproc)"
cmake ${cmake_args[@]+"${cmake_args[@]}"}
cmake --build build --target ${targets[@]+"${targets[@]}"} --parallel "$(ci_nproc)"
report_ccache_statistics

# Verified wherever it can be run, which is not the same as natively.
Expand All @@ -221,7 +224,7 @@ stage_and_write_provenance() {
fi
local -a names=()
local file
for file in "${produced[@]}"; do
for file in ${produced[@]+"${produced[@]}"}; do
[[ -f $file ]] || continue
cp "$file" "$out_dir/"
names+=("$(basename "$file")")
Expand Down Expand Up @@ -268,7 +271,7 @@ build_musl_host() {
# so the container reaches the same cache the runner restored.
docker run --rm \
-v "$PWD":/src -w /src \
"${docker_env[@]}" \
${docker_env[@]+"${docker_env[@]}"} \
alpine:3.20 sh -eux -c '
apk add --no-cache bash build-base cmake git autoconf automake \
libtool libarchive-tools texinfo bison flex pkgconf curl xz \
Expand Down Expand Up @@ -319,12 +322,12 @@ install_dependencies() {
i686-w64-mingw32) extra=(g++-mingw-w64-i686) ;;
x86_64-unknown-freebsd | aarch64-unknown-freebsd) extra=(clang lld llvm) ;;
esac
"${sudo_cmd[@]}" apt-get update -qq
"${sudo_cmd[@]}" env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
${sudo_cmd[@]+"${sudo_cmd[@]}"} apt-get update -qq
${sudo_cmd[@]+"${sudo_cmd[@]}"} env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
cmake cmake-data git build-essential autoconf automake libtool \
texinfo bison flex pkg-config python3 python3-pip curl bzip2 xz-utils \
libarchive-tools ccache \
"${extra[@]}"
${extra[@]+"${extra[@]}"}
pip3 install --quiet cmake==3.31.6
;;
esac
Expand All @@ -339,7 +342,7 @@ enable_ccache() {
shims="$(brew --prefix ccache)/libexec"
else
[[ -x /usr/sbin/update-ccache-symlinks ]] &&
"${sudo_cmd[@]}" /usr/sbin/update-ccache-symlinks
${sudo_cmd[@]+"${sudo_cmd[@]}"} /usr/sbin/update-ccache-symlinks
shims=/usr/lib/ccache
fi
[[ -d $shims ]] || {
Expand Down
2 changes: 1 addition & 1 deletion scripts/create-bootstrap-archive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ case $host in
bin/include/refresh-repositories.sh)
;;
esac
for relative_path in "${required[@]}"; do
for relative_path in ${required[@]+"${required[@]}"}; do
[[ -e $sdk_root/$relative_path ]] || {
printf 'bootstrap SDK is missing %s\n' "$relative_path" >&2
exit 1
Expand Down
2 changes: 1 addition & 1 deletion scripts/create-core-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ cp -a "$sdk_root/." "$core_root/"
rm -f "$core_root/.PKGINFO" "$core_root/.BUILDINFO" "$core_root/.MTREE" \
"$core_root/etc/pacman.conf"

for path in "${client_paths[@]}"; do
for path in ${client_paths[@]+"${client_paths[@]}"}; do
[[ -e $core_root/$path ]] || {
printf 'the SDK does not carry %s\n' "$path" >&2
exit 1
Expand Down
12 changes: 6 additions & 6 deletions scripts/create-core-repositories.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ normalize_database() {
}

mapfile -t sorted_architectures < <(printf '%s\n' "${!architectures[@]}" | LC_ALL=C sort)
for architecture in "${sorted_architectures[@]}"; do
for architecture in ${sorted_architectures[@]+"${sorted_architectures[@]}"}; do
read -r -a package_filenames <<<"${architectures[$architecture]}"
packages=()
for package_filename in "${package_filenames[@]}"; do
for package_filename in ${package_filenames[@]+"${package_filenames[@]}"}; do
packages+=("$staging_directory/$package_filename")
done
repo-add "$staging_directory/$architecture.db.tar.gz" "${packages[@]}"
repo-add "$staging_directory/$architecture.db.tar.gz" ${packages[@]+"${packages[@]}"}
normalize_database "$staging_directory/$architecture.db.tar.gz" \
"$temporary_directory/$architecture.db"
normalize_database "$staging_directory/$architecture.files.tar.gz" \
Expand Down Expand Up @@ -177,10 +177,10 @@ if [[ -n ${RELEASE_SCHEMA:-}${RELEASE_BUILD_ID:-}${RELEASE_BUILDSCRIPTS_REVISION
}

host_fragments=()
for architecture in "${sorted_architectures[@]}"; do
for architecture in ${sorted_architectures[@]+"${sorted_architectures[@]}"}; do
host_artifacts=("$architecture.db" "$architecture.files")
read -r -a package_filenames <<<"${architectures[$architecture]}"
host_artifacts+=("${package_filenames[@]}")
host_artifacts+=(${package_filenames[@]+"${package_filenames[@]}"})
while IFS= read -r -d '' extra; do
host_artifacts+=("$(basename "$extra")")
done < <(find "$staging_directory" -maxdepth 1 -type f \
Expand All @@ -191,7 +191,7 @@ if [[ -n ${RELEASE_SCHEMA:-}${RELEASE_BUILD_ID:-}${RELEASE_BUILDSCRIPTS_REVISION
printf 'no provenance echo found for published host: %s\n' "$architecture" >&2
exit 1
}
host_fragments+=("{\"name\":\"$(json_escape "$architecture")\",\"build_id\":\"$(json_escape "$build_id")\",\"artifacts\":$(json_string_array "${host_artifacts[@]}")}")
host_fragments+=("{\"name\":\"$(json_escape "$architecture")\",\"build_id\":\"$(json_escape "$build_id")\",\"artifacts\":$(json_string_array ${host_artifacts[@]+"${host_artifacts[@]}"})}")
done

hosts_joined=$(
Expand Down
2 changes: 1 addition & 1 deletion scripts/install-vdpm-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ else
bin/include/refresh-repositories.sh
)
fi
for relative_path in "${required[@]}"; do
for relative_path in ${required[@]+"${required[@]}"}; do
[[ -f $root/$relative_path && ! -L $root/$relative_path ]] || {
printf 'vdpm bundle is missing required regular file: %s\n' "$relative_path" >&2
exit 1
Expand Down
43 changes: 43 additions & 0 deletions tests/ci/test-empty-array-expansion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Arrays that reach a published host must expand the way bash 3.2 tolerates.
#
# macOS runners are the reason. Their /bin/bash is 3.2, where expanding an
# empty array under `set -u` is an unbound-variable error rather than
# nothing, and the failure comes after a complete build: the SDK is
# finalized and validated, then the script dies and stages nothing. It has
# happened twice -- once fixed in "Expand possibly-empty arrays the way
# macOS's bash 3.2 tolerates", then reintroduced with the Rosetta launcher,
# which is empty for every host that runs its own binaries.
#
# The rule is mechanical on purpose. Deciding case by case which array can
# be empty is exactly the judgement that failed: `${name[@]+"${name[@]}"}`
# means the same thing as `"${name[@]}"` whenever the array has elements,
# so requiring it everywhere costs nothing and needs no judgement.

set -euo pipefail

repository_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)

offenders=$(python3 - "$repository_root" <<'PYEOF'
import glob, os, re, sys

root = sys.argv[1]
# Value expansions only: ${#name[@]} and ${!name[@]} are fine in 3.2.
plain = re.compile(r'"\$\{([A-Za-z_][A-Za-z0-9_]*)\[@\]\}"')
tolerant = re.compile(r'\$\{([A-Za-z_][A-Za-z0-9_]*)\[@\]\+"\$\{\1\[@\]\}"\}')

for path in sorted(glob.glob(os.path.join(root, "scripts", "**", "*.sh"), recursive=True)):
with open(path, encoding="utf-8") as handle:
for number, line in enumerate(handle, 1):
if plain.search(tolerant.sub("", line)):
print(f"{os.path.relpath(path, root)}:{number}:{line.rstrip()}")
PYEOF
)

if [[ -n $offenders ]]; then
printf 'these expansions die on macOS bash 3.2 when the array is empty;\n' >&2
printf 'write ${name[@]+"${name[@]}"} instead:\n\n%s\n' "$offenders" >&2
exit 1
fi

printf 'empty-array expansion: all checks passed\n'
Loading