299 lines
8.1 KiB
Bash
Executable File
299 lines
8.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
# SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|
|
|
ARCHS=(
|
|
"x86_64"
|
|
"aarch64"
|
|
)
|
|
|
|
HOSTARCH=$(uname -m)
|
|
|
|
SOURCE_DIR=""
|
|
BUILD_DIR=""
|
|
OUTPUT_DIR=""
|
|
EXECUTABLE_NAME=""
|
|
|
|
# set flags from command line
|
|
RELEASE=0
|
|
MULTIBUILD=0
|
|
|
|
|
|
function help() {
|
|
echo "Usage: $0 [options]"
|
|
echo "Options:"
|
|
echo " version Show version information and exit"
|
|
echo " "
|
|
echo " <directory> Build the project in <directory>"
|
|
echo " -r, --release Build release version"
|
|
echo " -m, --multi Build for multiple architectures"
|
|
}
|
|
|
|
function version() {
|
|
echo "magicbuild 0.1"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
# CHECK PACKAGES
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
|
|
function check_packages() {
|
|
|
|
# Define packages based on distribution
|
|
case $OS in
|
|
"Ubuntu"|"Debian GNU/Linux")
|
|
# Common packages for both Ubuntu and Debian
|
|
PACKAGES="cmake make g++ build-essential upx musl-tools wget tar ccache ninja-build mold"
|
|
;;
|
|
*)
|
|
print_error "Unsupported distribution: $OS"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Function to check if a package is installed
|
|
is_package_installed() {
|
|
dpkg -l "$1" 2>/dev/null | grep -q "^ii"
|
|
}
|
|
|
|
# Install missing packages
|
|
print_status "Checking required packages..."
|
|
for pkg in $PACKAGES; do
|
|
if ! is_package_installed "$pkg"; then
|
|
echo "Missing package: $pkg"
|
|
exit 1
|
|
fi
|
|
echo "Package $pkg is installed"
|
|
done
|
|
|
|
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
# MUSL CROSS COMPILERS
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
|
|
function installmusl() {
|
|
echo "Installing musl toolchain"
|
|
|
|
# Set install directory
|
|
if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
|
|
USER_HOME=$(eval echo "~${SUDO_USER}")
|
|
else
|
|
USER_HOME="$HOME"
|
|
fi
|
|
INSTALL_DIR="$USER_HOME/.musl-cross"
|
|
mkdir -p "$INSTALL_DIR"
|
|
|
|
TMPDIR=$(mktemp -d)
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
function install_musl_cross() {
|
|
local TOOLCHAIN="$1"
|
|
local MUSL_CC_URL="https://musl.cc"
|
|
if [ ! -d "$INSTALL_DIR/$TOOLCHAIN" ]; then
|
|
echo "Downloading $TOOLCHAIN musl cross toolchain..."
|
|
wget -nc -O "$TMPDIR/$TOOLCHAIN.tgz" "$MUSL_CC_URL/$TOOLCHAIN.tgz"
|
|
tar -C "$INSTALL_DIR" -xvf "$TMPDIR/$TOOLCHAIN.tgz"
|
|
fi
|
|
}
|
|
|
|
function check_path() {
|
|
if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
|
|
local BASHRC="$USER_HOME/.bashrc"
|
|
local TOOLCHAIN="$1"
|
|
local MUSL_PATH="$INSTALL_DIR/$TOOLCHAIN/bin"
|
|
if ! echo "$PATH" | grep -q "$MUSL_PATH"; then
|
|
echo "Adding $MUSL_PATH to PATH in $BASHRC"
|
|
PATH_LINE="export PATH=\"$MUSL_PATH:\$PATH\""
|
|
if ! grep -Fxq "$PATH_LINE" "$BASHRC"; then
|
|
echo "" >> "$BASHRC"
|
|
echo "# Add musl cross compilers to PATH for dropshell" >> "$BASHRC"
|
|
echo "$PATH_LINE" >> "$BASHRC"
|
|
echo "Added musl cross compilers to $BASHRC"
|
|
echo "You should run 'source ~/.bashrc' to update your PATH"
|
|
else
|
|
echo "You should run 'source ~/.bashrc' to update your PATH"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
TOOLCHAIN_LIST=(
|
|
"aarch64-linux-musl-cross"
|
|
"x86_64-linux-musl-cross"
|
|
"x86_64-linux-musl-native"
|
|
)
|
|
|
|
for TOOLCHAIN in "${TOOLCHAIN_LIST[@]}"; do
|
|
install_musl_cross "$TOOLCHAIN"
|
|
check_path "$TOOLCHAIN"
|
|
done
|
|
|
|
# Clean up
|
|
rm -rf "$TMPDIR"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
# BUILD
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
|
|
CMAKE_FILE="CMakeLists.txt"
|
|
|
|
function get_executable_name() {
|
|
EXECUTABLE_NAME=""
|
|
local var_value=""
|
|
|
|
echo "Getting executable name from ${SOURCE_DIR}/${CMAKE_FILE}"
|
|
|
|
while IFS= read -r line; do
|
|
# Look for set(PROJECT_EXE_NAME ipdemo)
|
|
if [[ "$line" =~ set\(PROJECT_EXE_NAME[[:space:]]+([a-zA-Z0-9_-]+)\) ]]; then
|
|
var_value=$(echo "$line" | sed -E 's/.*set\(PROJECT_EXE_NAME[[:space:]]+([a-zA-Z0-9_-]+)\).*/\1/')
|
|
fi
|
|
# Look for add_executable(${PROJECT_EXE_NAME}
|
|
if [[ "$line" =~ add_executable\([[:space:]]*\$\{PROJECT_EXE_NAME\}[[:space:]] ]]; then
|
|
echo "Found executable name: $var_value"
|
|
EXECUTABLE_NAME="$var_value"
|
|
fi
|
|
done < "${SOURCE_DIR}/${CMAKE_FILE}"
|
|
|
|
if [[ -z "$EXECUTABLE_NAME" ]]; then
|
|
echo "Executable name not found."
|
|
exit 1
|
|
else
|
|
echo "Executable name: $EXECUTABLE_NAME"
|
|
fi
|
|
}
|
|
|
|
function build_arch() {
|
|
local ARCH="$1"
|
|
local PREVDIR="$PWD"
|
|
local JOBS;
|
|
JOBS=$(nproc) # Set JOBS to the number of available CPU cores
|
|
cd "${SOURCE_DIR}" || exit 1
|
|
|
|
local ARCH_BUILD_DIR=${BUILD_DIR}/${ARCH}
|
|
mkdir -p "${ARCH_BUILD_DIR}"
|
|
|
|
echo "Building for ${ARCH} in ${ARCH_BUILD_DIR}, from ${SOURCE_DIR}"
|
|
|
|
if [ ! -f "${HOME}/.musl-cross/${ARCH}-linux-musl-cross/bin/${ARCH}-linux-musl-g++" ]; then
|
|
installmusl
|
|
fi
|
|
|
|
CMAKE_BUILD_TYPE="Release"
|
|
if [ "$RELEASE" -eq 0 ]; then
|
|
CMAKE_BUILD_TYPE="Debug"
|
|
fi
|
|
|
|
CC="${HOME}/.musl-cross/${ARCH}-linux-musl-cross/bin/${ARCH}-linux-musl-gcc"
|
|
CXX="${HOME}/.musl-cross/${ARCH}-linux-musl-cross/bin/${ARCH}-linux-musl-g++"
|
|
|
|
cmake -B "${ARCH_BUILD_DIR}" -G Ninja \
|
|
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
|
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
|
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
|
-DCMAKE_LINKER=mold \
|
|
-DCMAKE_C_COMPILER="${CC}" \
|
|
-DCMAKE_CXX_COMPILER="${CXX}"
|
|
cd "${ARCH_BUILD_DIR}" || exit 1
|
|
ninja -j"${JOBS}"
|
|
|
|
upx "${ARCH_BUILD_DIR}/${EXECUTABLE_NAME}"
|
|
if [ ! -d "${OUTPUT_DIR}" ]; then
|
|
mkdir -p "${OUTPUT_DIR}"
|
|
fi
|
|
cp "${ARCH_BUILD_DIR}/${EXECUTABLE_NAME}" "${OUTPUT_DIR}/${EXECUTABLE_NAME}.${ARCH}"
|
|
|
|
cd "${PREVDIR}" || exit 1
|
|
}
|
|
|
|
function build() {
|
|
if [ -z "$1" ]; then
|
|
help
|
|
echo " "
|
|
echo "Error: DIR is required"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Building project in directory $1"
|
|
if [ ! -d "$1" ]; then
|
|
echo "Directory $1 does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# remove trailing slash from SOURCE_DIR
|
|
SOURCE_DIR="$1"
|
|
SOURCE_DIR=$(echo "$SOURCE_DIR" | sed 's:/*$::')
|
|
|
|
# make SOURCE_DIR absolute
|
|
SOURCE_DIR=$(realpath "$SOURCE_DIR")
|
|
|
|
BUILD_DIR="${SOURCE_DIR}/build"
|
|
OUTPUT_DIR="${SOURCE_DIR}/output"
|
|
|
|
if [ ! -f "${SOURCE_DIR}/${CMAKE_FILE}" ]; then
|
|
echo "${CMAKE_FILE} not found in ${SOURCE_DIR}"
|
|
exit 1
|
|
fi
|
|
|
|
get_executable_name
|
|
|
|
# if have the -m option, then build for multiple architectures
|
|
if [ $MULTIBUILD -eq 1 ]; then
|
|
for arch in "${ARCHS[@]}"; do
|
|
build_arch "$arch"
|
|
done
|
|
else
|
|
build_arch "$HOSTARCH"
|
|
fi
|
|
}
|
|
|
|
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
# MAIN
|
|
# ----------------------------------------------------------------------------------------------------------
|
|
|
|
function main() {
|
|
|
|
while getopts "rm" opt; do
|
|
case $opt in
|
|
r)
|
|
RELEASE=1
|
|
;;
|
|
m)
|
|
MULTIBUILD=1
|
|
;;
|
|
*)
|
|
help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Shift the processed options out of the argument list
|
|
shift $((OPTIND-1))
|
|
|
|
# get command argument, now that the flags are set
|
|
CMD="$1"
|
|
|
|
if [ -z "$CMD" ]; then
|
|
help
|
|
exit 1
|
|
fi
|
|
|
|
|
|
case "$CMD" in
|
|
version)
|
|
version
|
|
;;
|
|
*)
|
|
build "$1"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@" |