build_libkrun.sh•14.5 kB
#!/bin/sh
# build_libkrun.sh
# ---------------
# This script automates the building of libkrun and libkrunfw libraries,
# which are essential components for running micro virtual machines.
#
# Usage:
# ./build_libkrun.sh [options]
#
# Options:
# --no-cleanup, --no-clean Skip cleanup of build directories and VMs after completion
# --force-build, --force Force rebuild even if libraries are already built
#
# Requirements:
# - git
# - make
# - Rust/Cargo (for libkrun)
# - Python with packages in ~/.local/lib/python3.*/site-packages (for libkrunfw)
# - On macOS: krunvm must be installed (brew tap slp/krun && brew install krunvm)
# - On Linux: patchelf must be installed
#
# The script performs the following tasks:
# 1. Creates build directory if needed
# 2. Clones libkrunfw from Github
# 3. Clones libkrun from GitHub
# 4. Builds both libraries in the build directory
# 5. Creates non-versioned variants of libraries
# 6. Handles cleanup (including VM deletion on macOS) unless --no-cleanup is specified
#
# Library Build Paths:
# Libraries are built and placed in the ./build directory:
# Linux:
# - ./build/libkrun.so.$ABI_VERSION (versioned)
# - ./build/libkrun.so (symlink to versioned)
# - ./build/libkrunfw.so.$ABI_VERSION (versioned)
# - ./build/libkrunfw.so (symlink to versioned)
# macOS:
# - ./build/libkrun.$ABI_VERSION.dylib (versioned)
# - ./build/libkrun.dylib (symlink to versioned)
# - ./build/libkrunfw.$ABI_VERSION.dylib (versioned)
# - ./build/libkrunfw.dylib (symlink to versioned)
# Note: $ABI_VERSION is determined from each library's Makefile
#
# Error Handling:
# - The script checks for errors after each critical operation
# - Exits with status code 1 on any failure
# - Performs cleanup on exit unless --no-cleanup is specified
# - On macOS, cleanup includes deleting libkrunfw-builder and libkrun-builder VMs
#
# Platform Support:
# - Linux: Full support
# - macOS: Requires krunvm, handles platform-specific paths and library extensions
# - Other platforms are not supported
#
# Examples:
# # Standard build
# ./build_libkrun.sh
#
# # Build without cleaning up build directory and VMs
# ./build_libkrun.sh --no-cleanup
#
# # Force rebuild even if libraries exist
# ./build_libkrun.sh --force-build
#
# # Combine options
# ./build_libkrun.sh --no-cleanup --force-build
# Color variables
RED="\033[1;31m"
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
RESET="\033[0m"
# Package requirements per distribution
DEBIAN_PACKAGES="patchelf bc libelf-dev gcc flex bison git python3 curl"
FEDORA_PACKAGES="patchelf bc elfutils-libelf-devel gcc flex bison git python3 curl"
ARCH_PACKAGES="patchelf bc libelf gcc flex bison git python3 curl"
SUSE_PACKAGES="patchelf bc libelf-devel gcc flex bison git python3 curl"
# Function to detect Linux distribution
detect_linux_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
echo "$ID"
elif [ -f /etc/debian_version ]; then
echo "debian"
elif [ -f /etc/fedora-release ]; then
echo "fedora"
elif [ -f /etc/arch-release ]; then
echo "arch"
else
echo "unknown"
fi
}
# Function to check for required packages
check_linux_packages() {
local distro=$(detect_linux_distro)
local missing_packages=""
local install_command=""
local packages=""
case $distro in
debian|ubuntu)
install_command="apt-get install -y"
packages=$DEBIAN_PACKAGES
# Use dpkg-query and an exact status check to verify that the package is fully installed
for pkg in $packages; do
status=$(dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null || echo "not installed")
if [ "$status" != "install ok installed" ]; then
missing_packages="$missing_packages $pkg"
fi
done
;;
fedora|rhel|centos)
install_command="dnf install -y"
packages=$FEDORA_PACKAGES
# Check if rpm command exists and packages are installed
for pkg in $packages; do
if ! rpm -q "$pkg" >/dev/null 2>&1; then
missing_packages="$missing_packages $pkg"
fi
done
;;
arch)
install_command="pacman -S --noconfirm"
packages=$ARCH_PACKAGES
# Check if pacman command exists and packages are installed
for pkg in $packages; do
if ! pacman -Qi "$pkg" >/dev/null 2>&1; then
missing_packages="$missing_packages $pkg"
fi
done
;;
suse|opensuse|sles)
install_command="zypper install -y"
packages=$SUSE_PACKAGES
# Check if rpm command exists and packages are installed
for pkg in $packages; do
if ! rpm -q "$pkg" >/dev/null 2>&1; then
missing_packages="$missing_packages $pkg"
fi
done
;;
*)
warn "Unable to detect Linux distribution. Please ensure required packages are installed manually:"
info "Required packages: patchelf, bc, libelf-dev/elfutils-libelf-devel, gcc, flex, bison"
return
;;
esac
if [ -n "$missing_packages" ]; then
error "Missing required packages:$missing_packages"
info "You can install them using:"
info "sudo $install_command$missing_packages"
exit 1
fi
}
# Function to check for required packages
check_python_dependencies() {
# Check if python3 is installed
if ! command -v python3 >/dev/null 2>&1; then
error "python3 is not installed. Please install python3."
exit 1
fi
# Check if pip is available using python3 -m pip
if ! python3 -m pip --version >/dev/null 2>&1; then
error "pip is not installed. Please install pip for python3 (e.g., apt-get install python3-pip)."
exit 1
fi
# Check if pyelftools is installed (you import it as 'elftools')
if ! python3 -c "import elftools" >/dev/null 2>&1; then
error "pyelftools module is not installed. Please run: pip3 install pyelftools"
exit 1
fi
}
# Logging functions
info() {
printf "${GREEN}:: %s${RESET}\n" "$1"
}
warn() {
printf "${YELLOW}:: %s${RESET}\n" "$1"
}
error() {
printf "${RED}:: %s${RESET}\n" "$1"
}
# Store the original working directory
ORIGINAL_DIR="$(pwd)"
# Ensure PATH includes common binary locations
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
# Set up variables
BUILD_DIR="$ORIGINAL_DIR/build"
LIBKRUNFW_REPO="https://github.com/microsandbox/libkrunfw.git"
LIBKRUN_REPO="https://github.com/microsandbox/libkrun.git"
NO_CLEANUP=false
FORCE_BUILD=false
# Parse command line arguments
for arg in "$@"
do
case $arg in
--no-clean|--no-cleanup)
NO_CLEANUP=true
shift
;;
--force|--force-build)
FORCE_BUILD=true
shift
;;
esac
done
# Determine the OS type
OS_TYPE="$(uname -s)"
# Check if krunvm is installed on macOS, if applicable
if [ "$OS_TYPE" = "Darwin" ]; then
if ! which krunvm >/dev/null 2>&1; then
printf "${RED}krunvm command not found. Please install it using: brew tap slp/krun && brew install krunvm${RESET}\n"
exit 1
fi
fi
# Check for required packages and Python dependencies on Linux
if [ "$OS_TYPE" = "Linux" ]; then
check_linux_packages
check_python_dependencies
fi
# Function to handle cleanup
cleanup() {
if [ "$NO_CLEANUP" = true ]; then
info "Skipping cleanup as requested."
return
fi
warn "Cleaning up..."
cd "$ORIGINAL_DIR" || { error "Failed to change back to original directory"; exit 1; }
rm -rf "$BUILD_DIR"
if [ "$OS_TYPE" = "Darwin" ]; then
warn "Deleting libkrunfw-builder VM..."
krunvm delete libkrunfw-builder
warn "Deleting libkrun-builder VM..."
krunvm delete libkrun-builder
fi
info "Cleanup complete."
}
# Trap EXIT signal to run cleanup
trap cleanup EXIT
# Function to check command success
check_success() {
if [ $? -ne 0 ]; then
error "Error occurred: $1"
exit 1
fi
}
# Common function to check for existing installations
check_existing_lib() {
if [ "$FORCE_BUILD" = true ]; then
info "Force build enabled. Skipping check for existing $1."
return 0
fi
local lib_name="$1"
# Get ABI version from the appropriate Makefile
local abi_version=$(get_abi_version "$BUILD_DIR/$lib_name/Makefile")
case "$OS_TYPE" in
Linux)
lib_path="$BUILD_DIR/$lib_name.so.$abi_version"
;;
Darwin)
lib_path="$BUILD_DIR/$lib_name.$abi_version.dylib"
;;
*)
error "Unsupported OS: $OS_TYPE"
exit 1
;;
esac
if [ -f "$lib_path" ]; then
info "$lib_name already exists in $lib_path. Skipping build."
return 1
fi
return 0
}
# Function to create build directory
create_build_directory() {
cd "$ORIGINAL_DIR" || { error "Failed to change to original directory"; exit 1; }
if [ -d "$BUILD_DIR" ]; then
info "Build directory already exists. Skipping creation..."
else
info "Creating build directory..."
mkdir -p "$BUILD_DIR"
check_success "Failed to create build directory"
fi
}
# Common function to clone repositories
clone_repo() {
cd "$BUILD_DIR" || { error "Failed to change to build directory"; exit 1; }
local repo_url="$1"
local repo_name="$2"
shift 2 # Remove the first two arguments, leaving any additional args
if [ -d "$repo_name" ]; then
info "$repo_name directory already exists. Skipping cloning..."
else
info "Cloning $repo_name repository..."
git clone "$repo_url" "$@" # Pass any remaining arguments to git clone
check_success "Failed to clone $repo_name repository"
fi
}
# Function to extract ABI version from Makefile
get_abi_version() {
local makefile="$1"
local abi_version=$(grep "^ABI_VERSION.*=" "$makefile" | cut -d'=' -f2 | tr -d ' ')
if [ -z "$abi_version" ]; then
error "Could not determine ABI version from $makefile"
exit 1
fi
echo "$abi_version"
}
# Function to extract FULL_VERSION from Makefile
get_full_version() {
local makefile="$1"
local full_version=$(grep "^FULL_VERSION.*=" "$makefile" | cut -d'=' -f2 | tr -d ' ')
if [ -z "$full_version" ]; then
error "Could not determine FULL_VERSION from $makefile"
exit 1
fi
echo "$full_version"
}
# Function to build and copy libkrunfw
build_libkrunfw() {
cd "$BUILD_DIR/libkrunfw" || { error "Failed to change to libkrunfw directory"; exit 1; }
local abi_version=$(get_abi_version "Makefile")
info "Detected libkrunfw ABI version: $abi_version"
info "Building libkrunfw..."
export PYTHONPATH="$HOME/.local/lib/python3.*/site-packages:$PYTHONPATH"
case "$OS_TYPE" in
Darwin)
# On macOS, we need sudo to allow krunvm set xattr on the volume
sudo make PYTHONPATH="$PYTHONPATH"
;;
*)
make PYTHONPATH="$PYTHONPATH"
;;
esac
check_success "Failed to build libkrunfw"
# Copy the library to build directory and create symlink
info "Copying libkrunfw to build directory..."
cd "$BUILD_DIR" || { error "Failed to change to build directory"; exit 1; }
case "$OS_TYPE" in
Linux)
cp libkrunfw/libkrunfw.so.$abi_version.* "libkrunfw.so.$abi_version"
patchelf --set-rpath '$ORIGIN' "libkrunfw.so.$abi_version"
ln -sf "libkrunfw.so.$abi_version" "libkrunfw.so"
;;
Darwin)
cp libkrunfw/libkrunfw.$abi_version.dylib "libkrunfw.$abi_version.dylib"
install_name_tool -id "@rpath/libkrunfw.$abi_version.dylib" "libkrunfw.$abi_version.dylib"
ln -sf "libkrunfw.$abi_version.dylib" "libkrunfw.dylib"
;;
*)
error "Unsupported OS: $OS_TYPE"
exit 1
;;
esac
check_success "Failed to copy libkrunfw"
}
# Function to build and copy libkrun
build_libkrun() {
cd "$BUILD_DIR/libkrun" || { error "Failed to change to libkrun directory"; exit 1; }
local abi_version=$(get_abi_version "Makefile")
local full_version=$(get_full_version "Makefile")
info "Detected libkrun ABI version: $abi_version"
info "Detected libkrun FULL version: $full_version"
info "Building libkrun..."
# Update library path to use our build directory
export LIBRARY_PATH="$BUILD_DIR:$LIBRARY_PATH"
export PATH="$HOME/.cargo/bin:$PATH"
case "$OS_TYPE" in
Darwin)
sudo make LIBRARY_PATH="$LIBRARY_PATH" PATH="$PATH"
;;
*)
make LIBRARY_PATH="$LIBRARY_PATH" PATH="$PATH"
;;
esac
check_success "Failed to build libkrun"
# Copy and rename the library to build directory and create symlink
info "Copying libkrun to build directory..."
cd "$BUILD_DIR" || { error "Failed to change to build directory"; exit 1; }
case "$OS_TYPE" in
Linux)
cp libkrun/target/release/libkrun.so.$full_version "libkrun.so.$abi_version"
patchelf --set-rpath '$ORIGIN' "libkrun.so.$abi_version"
patchelf --set-needed "libkrunfw.so.4" "libkrun.so.$abi_version"
ln -sf "libkrun.so.$abi_version" "libkrun.so"
;;
Darwin)
cp libkrun/target/release/libkrun.$full_version.dylib "libkrun.$abi_version.dylib"
install_name_tool -id "@rpath/libkrun.$abi_version.dylib" "libkrun.$abi_version.dylib"
install_name_tool -change "libkrunfw.4.dylib" "@rpath/libkrunfw.4.dylib" "libkrun.$abi_version.dylib"
ln -sf "libkrun.$abi_version.dylib" "libkrun.dylib"
;;
*)
error "Unsupported OS: $OS_TYPE"
exit 1
;;
esac
check_success "Failed to copy libkrun"
}
# Main script execution
check_existing_lib "libkrunfw"
if [ $? -eq 0 ]; then
create_build_directory
clone_repo "$LIBKRUNFW_REPO" "libkrunfw" --single-branch --branch develop
build_libkrunfw
fi
check_existing_lib "libkrun"
if [ $? -eq 0 ]; then
create_build_directory
clone_repo "$LIBKRUN_REPO" "libkrun" --single-branch --branch develop
build_libkrun
fi
# Finished
info "Setup complete."