525 lines
13 KiB
Bash
Executable file
525 lines
13 KiB
Bash
Executable file
#!/bin/bash -ex
|
|
|
|
#
|
|
# JoeyBuild
|
|
# Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
|
#
|
|
# This software is provided 'as-is', without any express or implied
|
|
# warranty. In no event will the authors be held liable for any damages
|
|
# arising from the use of this software.
|
|
#
|
|
# Permission is granted to anyone to use this software for any purpose,
|
|
# including commercial applications, and to alter it and redistribute it
|
|
# freely, subject to the following restrictions:
|
|
#
|
|
# 1. The origin of this software must not be misrepresented; you must not
|
|
# claim that you wrote the original software. If you use this software
|
|
# in a product, an acknowledgment in the product documentation would be
|
|
# appreciated but is not required.
|
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
|
# misrepresented as being the original software.
|
|
# 3. This notice may not be removed or altered from any source distribution.
|
|
#
|
|
|
|
|
|
#
|
|
# This is intended to be used on a clean install of ubuntu-20.04.x-live-server-amd64.iso
|
|
#
|
|
|
|
|
|
G_HTTP_PORT=6502 # Port to use for HTTP server. Must be > 1024.
|
|
G_TITLE="JoeyBuild" # Title of application.
|
|
G_ORIGINAL_PATH=${PATH} # Original system path.
|
|
G_EHOME= # Home for this user.
|
|
G_SRC= # Location of JoeyLib source.
|
|
G_TEMP= # Directory to store temporary data.
|
|
G_TARGET= # Current target.
|
|
G_BUILD_PROJECT= # Used by build.
|
|
G_BUILD_PLATFORMS= # Used by build.
|
|
G_BUILD_RESULTS= # Used by build.
|
|
|
|
|
|
function addBuildUser() {
|
|
local USER=$1
|
|
local PASS=$2
|
|
local SALT=$(LC_ALL=C tr -cd "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" < /dev/urandom | head -c 8)
|
|
local CRYPT=$(perl -e 'print crypt($ARGV[0], $ARGV[1])' ${PASS} ${SALT})
|
|
|
|
tSudo useradd -m -G sftponly -s /usr/bin/false -p "${CRYPT}" "${USER}"
|
|
tSudo chown root:root /home/${USER}
|
|
tSudo chmod 755 /home/${USER}
|
|
tSudo mkdir -p /home/${USER}/build
|
|
tSudo chown ${USER}:${USER} /home/${USER}/build
|
|
tSudo chmod u+rwX /home/${USER}/build
|
|
tSudo chmod go-rwx /home/${USER}/build
|
|
}
|
|
|
|
|
|
function call() {
|
|
local _RESULT=$1
|
|
local _MODULE=$2
|
|
local _FUNCTION=$3
|
|
local _ARGS=${*:4}
|
|
local _VALUE=
|
|
|
|
G_TARGET=${_MODULE}
|
|
_VALUE="$( source ${G_EHOME}/targets/${_MODULE}.target && ${_FUNCTION} ${_ARGS} )"
|
|
G_TARGET=
|
|
|
|
eval $_RESULT=\${_VALUE}
|
|
}
|
|
|
|
|
|
function configureSFTP() {
|
|
if [[ ! -f /etc/ssh/sftponly_ready ]]; then
|
|
tSudo addgroup sftponly || true
|
|
tSudo sed -i 's/^Subsystem/#Subsystem/g' /etc/ssh/sshd_config
|
|
echo "Subsystem sftp internal-sftp -f AUTH -l VERBOSE" | tSudo tee -a /etc/ssh/sshd_config
|
|
echo "Match Group sftponly" | tSudo tee -a /etc/ssh/sshd_config
|
|
echo -e "\tChrootDirectory %h" | tSudo tee -a /etc/ssh/sshd_config
|
|
echo -e "\tForceCommand internal-sftp" | tSudo tee -a /etc/ssh/sshd_config
|
|
echo -e "\tAllowTcpForwarding no" | tSudo tee -a /etc/ssh/sshd_config
|
|
echo -e "\tX11Forwarding no" | tSudo tee -a /etc/ssh/sshd_config
|
|
tSudo systemctl restart sshd
|
|
tSudo touch /etc/ssh/sftponly_ready
|
|
fi
|
|
}
|
|
|
|
|
|
function delBuildUser() {
|
|
local USER=$1
|
|
|
|
tSudo userdel -f -r ${USER}
|
|
}
|
|
|
|
|
|
function doBuild() {
|
|
local USERNAME=$1
|
|
local SOURCE=/home/"${USERNAME}/build"
|
|
local LINE=
|
|
local FILE=
|
|
local EXTENSION=
|
|
local AFILES=()
|
|
local CFILES=()
|
|
local OFILES=()
|
|
local DFILES=()
|
|
local TEMP=
|
|
local TARGET=
|
|
local RESULT=
|
|
local LOG=
|
|
local PASS=
|
|
local PROJECT_TYPE=
|
|
|
|
pushd "${SOURCE}"
|
|
|
|
# Are there old results to clean up?
|
|
if [[ -f build.finished ]]; then
|
|
rm build.finished
|
|
fi
|
|
if [[ -d results ]]; then
|
|
rm -rf results
|
|
fi
|
|
mkdir -p results
|
|
G_BUILD_RESULTS=${SOURCE}/results
|
|
|
|
G_BUILD_PLATFORMS="["
|
|
G_BUILD_PROJECT=
|
|
PROJECT_TYPE=
|
|
|
|
# Read project information.
|
|
while IFS= read -r LINE; do
|
|
# If we don't have a project type, grab it from the first line of the project file and set things up.
|
|
if [[ -z ${PROJECT_TYPE} ]]; then
|
|
PROJECT_TYPE=${LINE}
|
|
# Skip to next loop pass.
|
|
continue
|
|
fi
|
|
|
|
# If we don't have a project name, grab it from the second line of the project file and set things up.
|
|
if [[ -z ${G_BUILD_PROJECT} ]]; then
|
|
G_BUILD_PROJECT=${LINE}
|
|
# Generate a list of non-source files.
|
|
for FILE in $(ls -1B); do
|
|
if [[ -f "${FILE}" ]]; then
|
|
if [[ "${FILE}" != "build.start" && "${FILE}" != "build.temp" && "${FILE}" != "build.tar.bz2" ]]; then
|
|
EXTENSION="${FILE##*.}"
|
|
if [[ "${EXTENSION}" != "c" && "${EXTENSION}" != "h" && "${EXTENSION}" != "asm" && "${EXTENSION}" != "macro" && "${EXTENSION}" != "inc" ]]; then
|
|
DFILES+=(${FILE})
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
# Generate a list of C files to compile.
|
|
TEMP=$(ls -1 *.c 2>/dev/null | wc -l)
|
|
if [[ ${TEMP} -ne 0 ]]; then
|
|
CFILES=($(ls -1 *.c))
|
|
fi
|
|
# Generate a list of ASM files to assemble.
|
|
TEMP=$(ls -1 *.asm 2>/dev/null | wc -l)
|
|
if [[ ${TEMP} -ne 0 ]]; then
|
|
AFILES=($(ls -1 *.asm))
|
|
fi
|
|
# Skip to next loop pass.
|
|
continue
|
|
fi
|
|
|
|
# This is for the 3rd and later lines of the project that list which targets to build.
|
|
TARGET=
|
|
for TEMP in ${LINE}; do
|
|
if [[ -z ${TARGET} ]]; then
|
|
TARGET=${TEMP}
|
|
call RESULT ${TARGET} enabled
|
|
if [[ ${RESULT} -ne 1 ]]; then
|
|
# Target is not enabled, stop.
|
|
break
|
|
fi
|
|
else
|
|
for PASS in "debug" "release"; do
|
|
tBoldBox tPURPLE "Building \"${G_BUILD_PROJECT}\" ${TARGET} ${TEMP} (${PASS})..."
|
|
G_DIST=dist/${TARGET}-${TEMP}/${PASS}
|
|
# We at least tried to build this target.
|
|
G_BUILD_PLATFORMS="${G_BUILD_PLATFORMS}${TARGET}.${TEMP}.${PASS} "
|
|
# Make sure we have the official JoeyLib header.
|
|
cp -f "${G_EHOME}/dist/joey.h" .
|
|
# Log file.
|
|
LOG="${G_BUILD_RESULTS}/build.${TARGET}.${TEMP}.${PASS}"
|
|
# Compile C files and generate object list.
|
|
if [[ "${#CFILES[@]}" != "0" ]]; then
|
|
call RESULT ${TARGET} compile ${TEMP} ${PASS} ${LOG} "${CFILES[@]}"
|
|
OFILES=(${RESULT})
|
|
fi
|
|
# Assemble ASM files and generate object list.
|
|
if [[ "${#AFILES[@]}" != "0" ]]; then
|
|
call RESULT ${TARGET} assemble ${TEMP} ${PASS} ${LOG} "${AFILES[@]}"
|
|
OFILES+=(${RESULT})
|
|
fi
|
|
# Link with JoeyLib.
|
|
call RESULT ${TARGET} link ${TEMP} ${PASS} ${LOG} "${OFILES[@]}"
|
|
# Process game data files.
|
|
if [[ "${#DFILES[@]}" != "0" ]]; then
|
|
call RESULT ${TARGET} package ${TEMP} ${PASS} ${LOG} "${DFILES[@]}"
|
|
fi
|
|
done
|
|
fi
|
|
done
|
|
|
|
done < build.start
|
|
|
|
# Compress the results.
|
|
tBoldBox tPURPLE "Packaging \"${G_BUILD_PROJECT}\"..."
|
|
tar cJf build.temp results
|
|
tSudo chown ${USERNAME}:${USERNAME} build.temp
|
|
|
|
# Erase everything except the temp file.
|
|
rm -rf "${G_BUILD_RESULTS}"
|
|
for FILE in $(ls -1); do
|
|
if [[ -f "${FILE}" ]]; then
|
|
if [[ "${FILE}" != "build.temp" ]]; then
|
|
rm "${FILE}"
|
|
fi
|
|
else
|
|
if [[ -d "${FILE}" ]]; then
|
|
rm -r "${FILE}"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Signal JoeyDev we're done.
|
|
mv build.temp build.tar.bz2
|
|
|
|
popd
|
|
|
|
tTrim TEMP "${G_BUILD_PLATFORMS}"
|
|
G_BUILD_PLATFORMS="${TEMP}]"
|
|
}
|
|
|
|
|
|
function doInstall() {
|
|
git config --global user.email "no-reply@kangaroopunch.com"
|
|
git config --global user.name "JoeyBuild VM Installer"
|
|
|
|
updateSystem
|
|
configureSFTP
|
|
|
|
# Add 'false' as a valid shell.
|
|
echo "/usr/bin/false" | tSudo tee -a /etc/shells
|
|
|
|
withTargets install "Installing SDK"
|
|
|
|
rebuildJoeyLib
|
|
|
|
# Start build server on reboot.
|
|
if [[ ! -f /etc/rc.local ]]; then
|
|
echo "#!/bin/bash" | tSudo tee /etc/rc.local > /dev/null
|
|
#echo "sudo -iHu ${USER} ${BASH_SOURCE[0]} server &> /dev/null &" | tSudo tee -a /etc/rc.local > /dev/null
|
|
echo "cd /home/${USER}" | tSudo tee -a /etc/rc.local > /dev/null
|
|
echo "${BASH_SOURCE[0]} server ${USER} &> /dev/null &" | tSudo tee -a /etc/rc.local > /dev/null
|
|
echo "exit 0" | tSudo tee -a /etc/rc.local > /dev/null
|
|
tSudo chmod +x /etc/rc.local
|
|
fi
|
|
}
|
|
|
|
|
|
function rebuildJoeyLib() {
|
|
|
|
# Do we have JoeyLib yet?
|
|
if [[ ! -f ${G_EHOME}/joeylib/LICENSE ]]; then
|
|
tBoldBox tBLUE "Downloading JoeyLib source..."
|
|
git clone https://skunkworks.kangaroopunch.com/skunkworks/joeylib.git ${G_EHOME}/joeylib &> /dev/null
|
|
fi
|
|
|
|
withTargets buildJoeyLib "Building JoeyLib"
|
|
|
|
mkdir -p dist
|
|
cp -f ${G_SRC}/joey.h dist/.
|
|
mkdir -p dist/3rdparty/memwatch
|
|
cp -f ${G_SRC}/3rdparty/memwatch/memwatch.h dist/3rdparty/memwatch/.
|
|
}
|
|
|
|
|
|
function startup() {
|
|
local ARGS=$@
|
|
local ACTION=$1
|
|
local NAME="$(basename $0)"
|
|
|
|
# Don't allow anything other than "server" to run as root.
|
|
if [[ ${EUID} -eq 0 ]]; then
|
|
if [[ "${ACTION}" != "server" ]]; then
|
|
echo "Do not run this script as root."
|
|
exit 1
|
|
fi
|
|
# We're running as root, so use specified G_EHOME.
|
|
G_EHOME="$(getent passwd ${2} | cut -d: -f6)"
|
|
else
|
|
# Not root, use standard G_EHOME.
|
|
G_EHOME="$(getent passwd ${USER} | cut -d: -f6)"
|
|
fi
|
|
|
|
G_SRC="${G_EHOME}/joeylib/joeylib/src"
|
|
G_TEMP="${G_EHOME}/temp"
|
|
|
|
# Do we have Towel yet?
|
|
if [[ ! -f "${G_EHOME}/towel/towel.sh" ]]; then
|
|
# Do we have GIT?
|
|
if [[ "$(which git || true)" == "" ]]; then
|
|
echo "Installing git..."
|
|
# This should be the only use of non-Towel sudo.
|
|
sudo apt-get -y install git
|
|
fi
|
|
echo "Downloading towel.sh support library..."
|
|
git clone https://skunkworks.kangaroopunch.com/skunkworks/towel.git "${G_EHOME}/towel" &> /dev/null
|
|
fi
|
|
|
|
# Load Towel
|
|
source "${G_EHOME}/towel/towel.sh"
|
|
|
|
# Give Towel a chance to handle arguments.
|
|
tArgsHandler ${ARGS}
|
|
|
|
# Do we have a config file?
|
|
if [[ ! -f "${G_EHOME}/joeybuild.config" ]]; then
|
|
tBoldBox tRED "Cannot find joeybuild.config file!"
|
|
exit 1
|
|
fi
|
|
source "${G_EHOME}/joeybuild.config"
|
|
tSudoSetPassword "${CONFIG_SUDO}"
|
|
|
|
case ${ACTION} in
|
|
add)
|
|
addBuildUser "${2}" "${3}"
|
|
;;
|
|
|
|
build)
|
|
doBuild "${2}"
|
|
;;
|
|
|
|
del)
|
|
delBuildUser "${2}"
|
|
;;
|
|
|
|
install)
|
|
doInstall
|
|
;;
|
|
|
|
rebuild)
|
|
rebuildJoeyLib
|
|
;;
|
|
|
|
server)
|
|
startBuildServer
|
|
;;
|
|
|
|
*)
|
|
#set +x
|
|
tBoldBox tGREEN "${G_TITLE} Options"
|
|
echo "${NAME} add USER PASS"
|
|
echo "${NAME} build USER"
|
|
echo "${NAME} del USER"
|
|
echo "${NAME} install"
|
|
echo "${NAME} rebuild"
|
|
echo "${NAME} server USER"
|
|
#set -x
|
|
;;
|
|
esac
|
|
}
|
|
|
|
|
|
function startBuildServer() {
|
|
local TARGET=
|
|
local RESULT=
|
|
local NAME=
|
|
local ARCHS=
|
|
local DESCRIPTION=
|
|
local USERNAME=
|
|
local LOG=${G_EHOME}/joeybuild.log
|
|
|
|
# NOTE: This function is run as root!
|
|
|
|
cd ${G_EHOME}
|
|
|
|
# Log startup.
|
|
echo "$(date) - Startup ${0}" >> ${LOG}
|
|
|
|
# Build supported project types and target details for JoeyDev.
|
|
echo "1.0" > dist/joeydev.info
|
|
echo "------------------------------------------------------------------------------" >> dist/joeydev.info
|
|
echo "project application \"JoeyLib Application\"" >> dist/joeydev.info
|
|
# echo "project joeylib \"JoeyLib Itself\"" >> http/joeydev.info
|
|
for TARGET in ${G_EHOME}/targets/*.target; do
|
|
NAME=$(basename -s .target ${TARGET})
|
|
call RESULT ${NAME} enabled
|
|
if [[ ${RESULT} -eq 1 ]]; then
|
|
call ARCHS ${NAME} architectures
|
|
call DESCRIPTION ${NAME} friendlyName
|
|
echo "target ${NAME} \"${DESCRIPTION}\" ${ARCHS}" >> dist/joeydev.info
|
|
fi
|
|
done
|
|
|
|
# Start the PHP web server if it's not already running.
|
|
php -S 0.0.0.0:${G_HTTP_PORT} -t dist >> ${LOG} 2>&1 &
|
|
|
|
# Start the actual build server.
|
|
cd /home
|
|
while true; do
|
|
|
|
# Find users with a "build.start" file.
|
|
for USERNAME in $(ls -1); do
|
|
|
|
mkdir -p "${USERNAME}/build"
|
|
chown -R ${USERNAME}:${USERNAME} "${USERNAME}/build"
|
|
chmod u+rwX "${USERNAME}/build"
|
|
chmod go-rwx "${USERNAME}/build"
|
|
|
|
# Handle building JoeyLib Applications.
|
|
if [[ -f "${USERNAME}/build/build.start" ]]; then
|
|
|
|
# Are there old results to clean up?
|
|
if [[ -f build.tar.bz2 ]]; then
|
|
rm build.tar.bz2
|
|
fi
|
|
|
|
doBuild ${USERNAME}
|
|
|
|
# Log it.
|
|
echo "$(date) - Compiled ${G_BUILD_PROJECT} for ${USERNAME} on ${G_BUILD_PLATFORMS}" >> ${LOG}
|
|
|
|
fi
|
|
done
|
|
|
|
sleep 1
|
|
|
|
done
|
|
}
|
|
|
|
|
|
function updateSystem() {
|
|
local MISSING=
|
|
|
|
tBoldBox tBLUE "Examining system..."
|
|
|
|
tSudo apt-get update
|
|
tSudo apt-get -y upgrade
|
|
tSudo apt-get -y dist-upgrade
|
|
|
|
# Shut Canonical up.
|
|
tSudo pro config set apt_news=false
|
|
|
|
#***TODO*** Split this into target modules.
|
|
tSudo dpkg --add-architecture i386
|
|
|
|
tCheckPackages MISSING \
|
|
attr \
|
|
autoconf \
|
|
bison \
|
|
build-essential \
|
|
bzip2 \
|
|
clang \
|
|
cmake \
|
|
cpio \
|
|
flex \
|
|
gawk \
|
|
gcc-multilib \
|
|
git \
|
|
gzip \
|
|
libbz2-dev \
|
|
liblzma-dev \
|
|
libssl-dev \
|
|
libxml2-dev \
|
|
libzstd-dev \
|
|
llvm \
|
|
mingw-w64 \
|
|
mtools \
|
|
nasm \
|
|
patch \
|
|
php-cli \
|
|
sed \
|
|
unzip \
|
|
uuid-dev \
|
|
xz-utils \
|
|
zlib1g-dev \
|
|
genisoimage \
|
|
jfsutils \
|
|
msitools \
|
|
ragel
|
|
|
|
if [[ "${MISSING}" != "" ]]; then
|
|
tSudo apt-get -y install ${MISSING}
|
|
fi
|
|
}
|
|
|
|
|
|
function withTargets() {
|
|
local FUNCTION=$1
|
|
local ACTION="$2"
|
|
local ARGS=${*:3}
|
|
local PASS=
|
|
local TARGET=
|
|
local RESULT=
|
|
local NAME=
|
|
local ARCHS=
|
|
local ARCH=
|
|
|
|
for TARGET in ${G_EHOME}/targets/*.target; do
|
|
NAME=$(basename -s .target ${TARGET})
|
|
call RESULT ${NAME} enabled
|
|
if [[ ${RESULT} -eq 1 ]]; then
|
|
call ARCHS ${NAME} architectures
|
|
for ARCH in ${ARCHS}; do
|
|
for PASS in "debug" "release"; do
|
|
tBoldBox tPURPLE "${ACTION} ${NAME} ${ARCH} (${PASS})..."
|
|
G_TARGET=${NAME}
|
|
G_INSTALLED=${G_EHOME}/installed/${NAME}-${ARCH}/${PASS}
|
|
G_DIST=dist/${G_TARGET}-${ARCH}/${PASS}
|
|
call RESULT ${NAME} ${FUNCTION} ${ARCH} ${PASS} ${ARGS}
|
|
G_DIST=
|
|
G_INSTALLED=
|
|
G_TARGET=
|
|
done
|
|
done
|
|
fi
|
|
done
|
|
}
|
|
|
|
|
|
# At the very end so we can stream this script from a web server.
|
|
startup $@ 2>&1 | tee lastrun.log
|