Files
zosbuilder/scripts/lib/components.sh
Jan De Landtsheer b9f94105cf fix: major build system improvements and container output issues
- Fix container output visibility with proper TTY handling and debug mode
- Fix build order: kernel modules built before initramfs creation
- Implement two-stage kernel build to resolve chicken-and-egg dependency
- Fix sed command issues in kernel configuration with direct execution
- Add diffutils package to container for proper kernel build support
- Enhance NIC module/firmware correlation with intelligent selection
- Fix module staging logic: all NICs loaded in stage1 before network up
- Add smart firmware installation based on module requirements
- Create comprehensive function documentation (scripts/functionlist.md)
- Add debug container script for troubleshooting

Major fixes:
* Container builds now show real-time output
* Kernel builds work with proper GNU diff support
* Module/firmware selection optimized for common hardware
* Build process handles dependencies correctly
* Documentation provides complete function reference
2025-09-03 14:06:44 +02:00

521 lines
17 KiB
Bash

#!/bin/bash
# Component download and build system for ThreeFold Zero OS
# Source common functions
LIB_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${LIB_SCRIPT_DIR}/common.sh"
# Component configuration
RUST_TARGET="${RUST_TARGET:-x86_64-unknown-linux-musl}"
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}"
# Parse and process all components from sources.conf
function components_parse_sources_conf() {
local sources_file="$1"
local components_dir="$2"
local install_dir="${INSTALL_DIR:-${PROJECT_ROOT}/initramfs}"
section_header "Parsing Sources Configuration"
if [[ ! -f "$sources_file" ]]; then
log_error "Sources file not found: ${sources_file}"
return 1
fi
# Ensure components directory exists
safe_mkdir "$components_dir"
# Export install directory for build functions
export INSTALL_DIR="$install_dir"
log_info "Processing components from: ${sources_file}"
log_info "Components directory: ${components_dir}"
log_info "Install directory: ${install_dir}"
local component_count=0
# Hardcode known components to bypass parsing issues for now
log_info "Building ThreeFold components (hardcoded for reliability)"
# Component 1: zinit
component_count=$((component_count + 1))
log_info "Processing component ${component_count}: zinit (git)"
components_download_git "zinit" "https://github.com/threefoldtech/zinit" "master" "$components_dir"
components_build_component "zinit" "build_zinit" "$components_dir"
# Component 2: mycelium
component_count=$((component_count + 1))
log_info "Processing component ${component_count}: mycelium (git)"
components_download_git "mycelium" "https://github.com/threefoldtech/mycelium" "v0.6.1" "$components_dir"
components_build_component "mycelium" "build_mycelium" "$components_dir"
# Component 3: rfs (pre-built release)
component_count=$((component_count + 1))
log_info "Processing component ${component_count}: rfs (release)"
components_download_release "rfs" "https://github.com/threefoldtech/rfs/releases/download/v2.0.6/rfs" "v2.0.6" "$components_dir" ""
components_build_component "rfs" "install_rfs" "$components_dir"
# Component 4: corex
component_count=$((component_count + 1))
log_info "Processing component ${component_count}: corex (release)"
components_download_release "corex" "https://github.com/threefoldtech/corex/releases/download/2.1.4/corex-2.1.4-amd64-linux-static" "2.1.4" "$components_dir" "rename=corex"
components_build_component "corex" "install_corex" "$components_dir"
if [[ $component_count -eq 0 ]]; then
log_warn "No components found in sources configuration"
else
log_info "Processed ${component_count} components successfully"
fi
}
# Download Git repository
function components_download_git() {
local name="$1"
local url="$2"
local version="$3"
local components_dir="$4"
section_header "Downloading Git Component: ${name}"
local target_dir="${components_dir}/${name}"
log_info "Repository: ${url}"
log_info "Version/Branch: ${version}"
log_info "Target directory: ${target_dir}"
# Always do fresh clone to avoid git state issues
if [[ -d "$target_dir" ]]; then
log_info "Removing existing ${name} directory for fresh clone"
safe_execute rm -rf "$target_dir"
fi
log_info "Cloning ${name} from ${url}"
safe_execute git clone --depth 1 --branch "$version" "$url" "$target_dir"
# Verify checkout
safe_execute cd "$target_dir"
local current_ref=$(git rev-parse HEAD)
log_info "Current commit: ${current_ref}"
log_info "Git component download complete: ${name}"
}
# Download release binary/archive
function components_download_release() {
local name="$1"
local url="$2"
local version="$3"
local components_dir="$4"
local extra="$5"
section_header "Downloading Release Component: ${name}"
local target_dir="${components_dir}/${name}"
local filename=$(basename "$url")
log_info "Release URL: ${url}"
log_info "Version: ${version}"
log_info "Target directory: ${target_dir}"
safe_mkdir "$target_dir"
# Download release
log_info "Downloading release: ${filename}"
safe_execute wget --progress=dot:giga -O "${target_dir}/${filename}" "$url"
# Verify download
if [[ ! -f "${target_dir}/${filename}" ]]; then
log_error "Failed to download release: ${filename}"
return 1
fi
local file_size=$(get_file_size "${target_dir}/${filename}")
log_info "Downloaded file size: ${file_size}"
# Handle extra options (like rename)
if [[ -n "$extra" ]]; then
components_process_extra_options "$target_dir" "$filename" "$extra"
fi
log_info "Release component download complete: ${name}"
}
# Process extra options for components
function components_process_extra_options() {
local target_dir="$1"
local filename="$2"
local extra="$3"
log_info "Processing extra options: ${extra}"
# Handle rename option
if [[ "$extra" =~ rename=(.+) ]]; then
local new_name="${BASH_REMATCH[1]}"
log_info "Renaming ${filename} to ${new_name}"
safe_execute mv "${target_dir}/${filename}" "${target_dir}/${new_name}"
fi
# Handle extract option for archives
if [[ "$extra" =~ extract ]]; then
log_info "Extracting archive: ${filename}"
safe_execute cd "$target_dir"
case "$filename" in
*.tar.gz|*.tgz)
safe_execute tar -xzf "$filename"
;;
*.tar.bz2|*.tbz2)
safe_execute tar -xjf "$filename"
;;
*.tar.xz|*.txz)
safe_execute tar -xJf "$filename"
;;
*.zip)
safe_execute unzip "$filename"
;;
*)
log_warn "Unknown archive format: ${filename}"
;;
esac
fi
}
# Build component using specified build function
function components_build_component() {
local name="$1"
local build_func="$2"
local components_dir="$3"
section_header "Building Component: ${name}"
local component_dir="${components_dir}/${name}"
if [[ ! -d "$component_dir" ]]; then
log_error "Component directory not found: ${component_dir}"
return 1
fi
# Change to component directory
safe_execute cd "$component_dir"
log_info "Build function: ${build_func}"
log_info "Working directory: $(pwd)"
# Check if build function exists
if ! declare -f "$build_func" >/dev/null; then
log_error "Build function not found: ${build_func}"
return 1
fi
# Call the specific build function
log_info "Executing build function: ${build_func}"
"$build_func" "$name" "$component_dir"
log_info "Component build complete: ${name}"
}
# Setup Rust environment for musl builds
function components_setup_rust_env() {
section_header "Setting Up Rust Environment"
# Source cargo environment if available
if [[ -f /root/.cargo/env ]]; then
log_info "Sourcing cargo environment from /root/.cargo/env"
source /root/.cargo/env
fi
# Check if we have rustup (should be available now)
if command_exists "rustup"; then
log_info "Using rustup for Rust toolchain management"
# Ensure musl target is installed
if ! rustup target list --installed | grep -q "$RUST_TARGET"; then
log_info "Installing Rust target: ${RUST_TARGET}"
safe_execute rustup target add "$RUST_TARGET"
else
log_info "Rust target already installed: ${RUST_TARGET}"
fi
# Set environment variables for rustup (clean and simple)
export RUSTFLAGS="-C target-feature=+crt-static"
else
log_error "rustup not found after setup"
return 1
fi
log_info "Rust environment configured for musl builds"
log_info "RUST_TARGET: ${RUST_TARGET}"
log_info "RUSTFLAGS: ${RUSTFLAGS}"
log_info "CC: ${CC:-system-default}"
}
# Build function for zinit (standard Rust build)
function build_zinit() {
local name="$1"
local component_dir="$2"
section_header "Building zinit with musl target"
components_setup_rust_env
log_info "Building zinit from: ${component_dir}"
# Ensure we're in the correct directory
if [[ ! -d "$component_dir" ]]; then
log_error "Component directory not found: ${component_dir}"
return 1
fi
# Don't use safe_execute for cd - it runs in subshell
log_info "Executing: cd $component_dir"
cd "$component_dir" || {
log_error "Failed to change to directory: $component_dir"
return 1
}
local current_dir=$(pwd)
log_info "Current directory: ${current_dir}"
# Verify Cargo.toml exists
if [[ ! -f "Cargo.toml" ]]; then
log_error "Cargo.toml not found in: ${current_dir}"
return 1
fi
# Build with musl target (rustup properly configured)
safe_execute cargo build --release --target "$RUST_TARGET"
# Find and install binary
local binary_path="target/${RUST_TARGET}/release/zinit"
if [[ ! -f "$binary_path" ]]; then
log_error "zinit binary not found at: ${binary_path}"
return 1
fi
# Install to initramfs
safe_mkdir "${INSTALL_DIR}/sbin"
safe_execute cp "$binary_path" "${INSTALL_DIR}/sbin/zinit"
safe_execute chmod +x "${INSTALL_DIR}/sbin/zinit"
local binary_size=$(get_file_size "${INSTALL_DIR}/sbin/zinit")
log_info "Installed zinit binary (${binary_size}) to: ${INSTALL_DIR}/sbin/zinit"
}
# Build function for rfs (standard Rust build)
function build_rfs() {
local name="$1"
local component_dir="$2"
section_header "Building rfs with musl target"
components_setup_rust_env
log_info "Building rfs from: ${component_dir}"
# Ensure we're in the correct directory
if [[ ! -d "$component_dir" ]]; then
log_error "Component directory not found: ${component_dir}"
return 1
fi
# Don't use safe_execute for cd - it runs in subshell
log_info "Executing: cd $component_dir"
cd "$component_dir" || {
log_error "Failed to change to directory: $component_dir"
return 1
}
local current_dir=$(pwd)
log_info "Current directory: ${current_dir}"
# Verify Cargo.toml exists
if [[ ! -f "Cargo.toml" ]]; then
log_error "Cargo.toml not found in: ${current_dir}"
return 1
fi
# Build with musl target
safe_execute cargo build --release --target "$RUST_TARGET"
# Find and install binary
local binary_path="target/${RUST_TARGET}/release/rfs"
if [[ ! -f "$binary_path" ]]; then
log_error "rfs binary not found at: ${binary_path}"
return 1
fi
# Install to initramfs
safe_mkdir "${INSTALL_DIR}/usr/bin"
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/rfs"
safe_execute chmod +x "${INSTALL_DIR}/usr/bin/rfs"
local binary_size=$(get_file_size "${INSTALL_DIR}/usr/bin/rfs")
log_info "Installed rfs binary (${binary_size}) to: ${INSTALL_DIR}/usr/bin/rfs"
}
# Build function for mycelium (special subdirectory build)
function build_mycelium() {
local name="$1"
local component_dir="$2"
section_header "Building mycelium with musl target (special directory)"
components_setup_rust_env
log_info "Building mycelium from: ${component_dir}"
# Change to myceliumd subdirectory (special requirement)
local myceliumd_dir="${component_dir}/myceliumd"
if [[ ! -d "$myceliumd_dir" ]]; then
log_error "myceliumd directory not found at: ${myceliumd_dir}"
return 1
fi
# Don't use safe_execute for cd - it runs in subshell
log_info "Executing: cd $myceliumd_dir"
cd "$myceliumd_dir" || {
log_error "Failed to change to myceliumd directory: $myceliumd_dir"
return 1
}
log_info "Building in myceliumd subdirectory: $(pwd)"
# Build with musl target
safe_execute cargo build --release --target "$RUST_TARGET"
# Find and install binary (from target/x86.../release)
local binary_path="target/${RUST_TARGET}/release/mycelium"
if [[ ! -f "$binary_path" ]]; then
log_error "mycelium binary not found at: ${binary_path}"
return 1
fi
# Install to initramfs
safe_mkdir "${INSTALL_DIR}/usr/bin"
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/mycelium"
safe_execute chmod +x "${INSTALL_DIR}/usr/bin/mycelium"
local binary_size=$(get_file_size "${INSTALL_DIR}/usr/bin/mycelium")
log_info "Installed mycelium binary (${binary_size}) to: ${INSTALL_DIR}/usr/bin/mycelium"
}
# Install function for rfs (pre-built binary)
function install_rfs() {
local name="$1"
local component_dir="$2"
section_header "Installing rfs binary"
log_info "Installing rfs from: ${component_dir}"
# Find the rfs binary
local binary_path="${component_dir}/rfs"
if [[ ! -f "$binary_path" ]]; then
log_error "rfs binary not found at: ${binary_path}"
return 1
fi
# Make executable and install
safe_execute chmod +x "$binary_path"
safe_mkdir "${INSTALL_DIR}/usr/bin"
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/rfs"
safe_execute chmod +x "${INSTALL_DIR}/usr/bin/rfs"
local binary_size=$(get_file_size "${INSTALL_DIR}/usr/bin/rfs")
log_info "Installed rfs binary (${binary_size}) to: ${INSTALL_DIR}/usr/bin/rfs"
}
# Install function for corex (pre-built binary)
function install_corex() {
local name="$1"
local component_dir="$2"
section_header "Installing corex binary"
log_info "Installing corex from: ${component_dir}"
# Find the corex binary (may have been renamed)
local binary_path
if [[ -f "${component_dir}/corex" ]]; then
binary_path="${component_dir}/corex"
elif [[ -f "${component_dir}/corex-2.1.4-amd64-linux-static" ]]; then
binary_path="${component_dir}/corex-2.1.4-amd64-linux-static"
else
log_error "corex binary not found in: ${component_dir}"
return 1
fi
# Make executable and install
safe_execute chmod +x "$binary_path"
safe_mkdir "${INSTALL_DIR}/usr/bin"
safe_execute cp "$binary_path" "${INSTALL_DIR}/usr/bin/corex"
safe_execute chmod +x "${INSTALL_DIR}/usr/bin/corex"
local binary_size=$(get_file_size "${INSTALL_DIR}/usr/bin/corex")
log_info "Installed corex binary (${binary_size}) to: ${INSTALL_DIR}/usr/bin/corex"
}
# Verify all built components
function components_verify_installation() {
local install_dir="${INSTALL_DIR:-${PROJECT_ROOT}/initramfs}"
section_header "Verifying Component Installation"
# List of expected binaries and their locations
local expected_binaries=(
"sbin/zinit"
"usr/bin/rfs"
"usr/bin/mycelium"
"usr/bin/corex"
)
local missing_count=0
for binary in "${expected_binaries[@]}"; do
local full_path="${install_dir}/${binary}"
if [[ -f "$full_path" && -x "$full_path" ]]; then
local size=$(get_file_size "$full_path")
log_info "${binary} (${size})"
else
log_error "✗ Missing or not executable: ${binary}"
((missing_count++))
fi
done
if [[ $missing_count -eq 0 ]]; then
log_info "All components installed successfully"
return 0
else
log_error "${missing_count} components missing or invalid"
return 1
fi
}
# Clean component build artifacts
function components_cleanup() {
local components_dir="$1"
local keep_sources="${2:-false}"
section_header "Cleaning Component Build Artifacts"
if [[ "$keep_sources" == "true" ]]; then
log_info "Keeping source directories, cleaning build artifacts only"
# Clean Rust build artifacts
find "$components_dir" -name "target" -type d -exec rm -rf {} + 2>/dev/null || true
find "$components_dir" -name "Cargo.lock" -type f -delete 2>/dev/null || true
else
log_info "Removing all component directories"
safe_rmdir "$components_dir"
fi
log_info "Component cleanup complete"
}
# Export functions
export -f components_parse_sources_conf
export -f components_download_git components_download_release components_process_extra_options
export -f components_build_component components_setup_rust_env
export -f build_zinit build_rfs build_mycelium install_corex
export -f components_verify_installation components_cleanup
# Export functions for install_rfs
export -f install_rfs