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
45 changes: 44 additions & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,19 @@ jobs:
PACKAGE_DIR="${ARTIFACT_DIR}/${VERSION}"
BIN_DIR="${PACKAGE_DIR}/bin"
LIB_DIR="${PACKAGE_DIR}/lib"
SCRIPT_DIR="${PACKAGE_DIR}/script"
SCRIPT_PACKAGE_DEB_DIR="${SCRIPT_DIR}/package/deb"
SOURCE_LIB_LIST_FILE="build-release/lib-list.txt"
SOURCE_PREREQUISITE_ENTRY_FILE="build-release/prerequisite.sh"
SOURCE_PREREQUISITE_FILE="build-release/prerequisite-deb.sh"
SOURCE_PREREQUISITE_PACKAGE_LIST_FILE="build-release/package/deb/package-list.txt"
LIB_LIST_FILE="${LIB_DIR}/lib-list.txt"
PREREQUISITE_ENTRY_FILE="${SCRIPT_DIR}/prerequisite.sh"
PREREQUISITE_FILE="${SCRIPT_DIR}/prerequisite-deb.sh"
PREREQUISITE_PACKAGE_LIST_FILE="${SCRIPT_PACKAGE_DEB_DIR}/package-list.txt"
MANIFEST_FILE="${PACKAGE_DIR}/manifest.txt"
rm -rf "${ARTIFACT_DIR}"
mkdir -p "${BIN_DIR}" "${LIB_DIR}"
mkdir -p "${BIN_DIR}" "${LIB_DIR}" "${SCRIPT_DIR}" "${SCRIPT_PACKAGE_DEB_DIR}"

# Define supported binaries and their descriptions
declare -A BIN_MAP
Expand All @@ -110,6 +118,23 @@ jobs:
fi
done

# Derive runtime glibc floor from produced ELF binaries by scanning
# referenced GLIBC_* symbol versions and taking the maximum.
MIN_GLIBC="$(
find "${BIN_DIR}" -maxdepth 1 -type f -exec sh -c '
for f in "$@"; do
readelf --version-info "$f" 2>/dev/null \
| grep -oE "GLIBC_[0-9]+\.[0-9]+" \
| sed "s/^GLIBC_//"
done
' sh {} + | sort -V | tail -1
)"
if [[ -z "${MIN_GLIBC}" ]]; then
echo "ERROR: unable to determine min_glibc from binaries in ${BIN_DIR}"
exit 1
fi
echo "Detected min_glibc=${MIN_GLIBC}"

SUFFIX="${{ matrix.asset_suffix }}"
ARCHIVE_NAME="mududb-${VERSION}-${SUFFIX}.tar.gz"
ARCHIVE_URI="https://github.com/${GITHUB_REPOSITORY}/releases/download/${VERSION}/${ARCHIVE_NAME}"
Expand All @@ -118,7 +143,24 @@ jobs:
echo "ERROR: Dynamic library list not found at ${SOURCE_LIB_LIST_FILE}"
exit 1
fi
if [[ ! -f "${SOURCE_PREREQUISITE_FILE}" ]]; then
echo "ERROR: Prerequisite script not found at ${SOURCE_PREREQUISITE_FILE}"
exit 1
fi
if [[ ! -f "${SOURCE_PREREQUISITE_ENTRY_FILE}" ]]; then
echo "ERROR: Prerequisite entry script not found at ${SOURCE_PREREQUISITE_ENTRY_FILE}"
exit 1
fi
if [[ ! -f "${SOURCE_PREREQUISITE_PACKAGE_LIST_FILE}" ]]; then
echo "ERROR: Prerequisite package-list not found at ${SOURCE_PREREQUISITE_PACKAGE_LIST_FILE}"
exit 1
fi
cp "${SOURCE_LIB_LIST_FILE}" "${LIB_LIST_FILE}"
cp "${SOURCE_PREREQUISITE_ENTRY_FILE}" "${PREREQUISITE_ENTRY_FILE}"
cp "${SOURCE_PREREQUISITE_FILE}" "${PREREQUISITE_FILE}"
cp "${SOURCE_PREREQUISITE_PACKAGE_LIST_FILE}" "${PREREQUISITE_PACKAGE_LIST_FILE}"
chmod +x "${PREREQUISITE_ENTRY_FILE}"
chmod +x "${PREREQUISITE_FILE}"

{
echo "name=mududb"
Expand All @@ -129,6 +171,7 @@ jobs:
echo "bin_dir=bin"
echo "lib_dir=lib"
echo "lib_list=lib/lib-list.txt"
echo "min_glibc=${MIN_GLIBC}"
echo ""
echo "[files]"
find "${PACKAGE_DIR}" -type f -printf "%P\n" | sort
Expand Down
3 changes: 3 additions & 0 deletions build-release/package/deb/package-list.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
x86_64-unknown-linux-gnu https://archive.ubuntu.com/ubuntu/pool/main/libu/liburing/liburing2_2.5-1build1_amd64.deb
aarch64-unknown-linux-gnu https://ports.ubuntu.com/ubuntu-ports/pool/main/libu/liburing/liburing2_2.5-1build1_arm64.deb
armv7-unknown-linux-gnueabihf https://ports.ubuntu.com/ubuntu-ports/pool/main/libu/liburing/liburing2_2.5-1build1_armhf.deb
167 changes: 167 additions & 0 deletions build-release/prerequisite-deb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env sh
set -eu

# Download deb prerequisites from a package list, then extract and install
# package contents into a specified installation directory.
usage() {
cat <<'EOF'
Usage:
prerequisite-deb.sh --install-dir <path> [--package-list <file>] [--target <rust-target>]

Options:
--install-dir Destination directory to install files from deb packages
--package-list Path to package list file (default: script-adjacent package/deb/package-list.txt)
--target Optional Rust target triple used for filtering package-list items

Package list format:
- Empty lines and lines starting with # are ignored.
- A line with one field is treated as a deb URL for all targets:
https://example.com/pkg.deb
- A line with two fields is treated as: <target> <deb_url>
x86_64-unknown-linux-gnu https://example.com/pkg-amd64.deb
EOF
}

need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "error: required command not found: $1" >&2
exit 1
fi
}

TARGET=""
INSTALL_DIR=""
PACKAGE_LIST=""

while [ "$#" -gt 0 ]; do
case "$1" in
--target)
TARGET="${2:-}"
shift 2
;;
--install-dir)
INSTALL_DIR="${2:-}"
shift 2
;;
--package-list)
PACKAGE_LIST="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "error: unknown argument: $1" >&2
usage >&2
exit 1
;;
esac
done

if [ -z "${INSTALL_DIR}" ]; then
echo "error: --install-dir is required" >&2
usage >&2
exit 1
fi

need_cmd curl
need_cmd dpkg-deb
need_cmd mktemp
need_cmd mkdir
need_cmd basename
need_cmd cp
need_cmd find
need_cmd readlink
need_cmd ln

if [ -z "${PACKAGE_LIST}" ]; then
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
PACKAGE_LIST="${SCRIPT_DIR}/package/deb/package-list.txt"
fi

if [ ! -f "${PACKAGE_LIST}" ]; then
echo "error: package list not found: ${PACKAGE_LIST}" >&2
exit 1
fi

TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "${TMP_DIR}"
}
trap cleanup EXIT INT TERM

mkdir -p "${INSTALL_DIR}"
mkdir -p "${INSTALL_DIR}/lib"
installed_count="0"

while IFS= read -r raw_line || [ -n "${raw_line}" ]; do
line="${raw_line#"${raw_line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"

[ -z "${line}" ] && continue
case "${line}" in
\#*) continue ;;
esac

set -- ${line}
if [ "$#" -eq 1 ]; then
entry_target=""
entry_url="$1"
elif [ "$#" -eq 2 ]; then
entry_target="$1"
entry_url="$2"
else
echo "error: invalid package-list line: ${line}" >&2
exit 1
fi

# Two-field line: <target> <url>, filter by --target when provided.
if [ -n "${entry_target}" ] && [ -n "${TARGET}" ] && [ "${entry_target}" != "${TARGET}" ]; then
continue
fi
if [ -n "${entry_target}" ] && [ -z "${TARGET}" ]; then
continue
fi

pkg_basename="$(basename "${entry_url}")"
deb_path="${TMP_DIR}/${installed_count}-${pkg_basename}"

echo "Downloading ${entry_url}"
curl -fL "${entry_url}" -o "${deb_path}"
dpkg-deb -x "${deb_path}" "${INSTALL_DIR}"
installed_count=$((installed_count + 1))
done < "${PACKAGE_LIST}"

if [ "${installed_count}" = "0" ]; then
echo "error: no deb packages installed from ${PACKAGE_LIST}" >&2
if [ -n "${TARGET}" ]; then
echo "hint: check whether package-list contains entries for target ${TARGET}" >&2
fi
exit 1
fi

# Keep the original deb filesystem layout, and also place shared libraries
# into <install-dir>/lib for simpler runtime linking.
find "${INSTALL_DIR}/usr/lib" -type f -name '*.so*' 2>/dev/null | while IFS= read -r so_file; do
cp -f "${so_file}" "${INSTALL_DIR}/lib/"
done

# Recreate shared-library symlinks in <install-dir>/lib, pointing to the
# corresponding .so files within the same directory.
find "${INSTALL_DIR}/usr/lib" -type l -name '*.so*' 2>/dev/null | while IFS= read -r so_link; do
link_name="$(basename "${so_link}")"
link_target="$(readlink "${so_link}")"
target_name="$(basename "${link_target}")"

# Skip if a non-symlink file already exists with the same name.
if [ -e "${INSTALL_DIR}/lib/${link_name}" ] && [ ! -L "${INSTALL_DIR}/lib/${link_name}" ]; then
continue
fi

if [ -e "${INSTALL_DIR}/lib/${target_name}" ]; then
ln -sfn "${target_name}" "${INSTALL_DIR}/lib/${link_name}"
fi
done

echo "Installed ${installed_count} deb package(s) to ${INSTALL_DIR}"
145 changes: 145 additions & 0 deletions build-release/prerequisite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env sh
set -eu

usage() {
cat <<'EOF'
Usage:
prerequisite.sh --install-dir <path> [--target <rust-target>] [--package-list <file>]

Description:
Platform dispatcher for prerequisite installation.
- Deb-supported Linux target: execute prerequisite-deb.sh
- Other platforms/targets: return error
EOF
}

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
DEB_SCRIPT="${SCRIPT_DIR}/prerequisite-deb.sh"
INSTALL_DIR=""

parse_install_dir() {
prev=""
for arg in "$@"; do
if [ "${prev}" = "--install-dir" ]; then
INSTALL_DIR="${arg}"
return 0
fi
prev="${arg}"
done
return 1
}

read_min_glibc_from_manifest() {
manifest_path="$1/manifest.txt"
if [ ! -f "${manifest_path}" ]; then
echo "error: manifest not found: ${manifest_path}" >&2
return 1
fi
awk -F= '/^min_glibc=/{print $2; exit}' "${manifest_path}"
}

detect_host_glibc() {
if command -v getconf >/dev/null 2>&1; then
getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}'
return 0
fi
if command -v ldd >/dev/null 2>&1; then
ldd --version 2>/dev/null | head -n1 | sed -E 's/.* ([0-9]+\.[0-9]+).*/\1/'
return 0
fi
return 1
}

version_ge() {
required="$1"
actual="$2"
req_major="${required%%.*}"
req_minor="${required#*.}"
act_major="${actual%%.*}"
act_minor="${actual#*.}"
[ "${act_major}" -gt "${req_major}" ] && return 0
[ "${act_major}" -lt "${req_major}" ] && return 1
[ "${act_minor}" -ge "${req_minor}" ]
}

check_min_glibc() {
required="$1"
actual="$(detect_host_glibc || true)"
if [ -z "${actual}" ]; then
echo "error: unable to detect host glibc version (requires getconf or ldd)" >&2
return 1
fi
if ! version_ge "${required}" "${actual}"; then
echo "error: host glibc ${actual} is lower than required ${required}" >&2
return 1
fi
echo "glibc check passed: host ${actual}, required >= ${required}"
}

if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi

if ! parse_install_dir "$@"; then
echo "error: --install-dir is required" >&2
usage >&2
exit 1
fi

MIN_GLIBC="$(read_min_glibc_from_manifest "${INSTALL_DIR}" || true)"
if [ -n "${MIN_GLIBC}" ]; then
check_min_glibc "${MIN_GLIBC}"
fi

OS="$(uname -s 2>/dev/null || echo unknown)"
TARGET_ARG=""
prev=""
for arg in "$@"; do
if [ "${prev}" = "--target" ]; then
TARGET_ARG="${arg}"
break
fi
prev="${arg}"
done

is_deb_supported_target() {
case "$1" in
x86_64-unknown-linux-gnu|aarch64-unknown-linux-gnu|armv7-unknown-linux-gnueabihf)
return 0
;;
*)
return 1
;;
esac
}

if [ "${OS}" != "Linux" ]; then
echo "error: platform ${OS} is not supported by prerequisite-deb.sh" >&2
exit 1
fi

if [ -n "${TARGET_ARG}" ]; then
if ! is_deb_supported_target "${TARGET_ARG}"; then
echo "error: target ${TARGET_ARG} is not a supported deb platform" >&2
exit 1
fi
else
ARCH="$(uname -m 2>/dev/null || echo unknown)"
case "${ARCH}" in
x86_64|amd64) TARGET_ARG="x86_64-unknown-linux-gnu" ;;
aarch64|arm64) TARGET_ARG="aarch64-unknown-linux-gnu" ;;
armv7l|armv7) TARGET_ARG="armv7-unknown-linux-gnueabihf" ;;
*)
echo "error: host architecture ${ARCH} is not a supported deb platform; pass --target explicitly" >&2
exit 1
;;
esac
fi

if [ ! -x "${DEB_SCRIPT}" ]; then
echo "error: prerequisite deb script not found or not executable: ${DEB_SCRIPT}" >&2
exit 1
fi

exec "${DEB_SCRIPT}" "$@"
Loading