aboutsummaryrefslogtreecommitdiff
path: root/spm
diff options
context:
space:
mode:
Diffstat (limited to 'spm')
-rwxr-xr-xspm476
1 files changed, 476 insertions, 0 deletions
diff --git a/spm b/spm
new file mode 100755
index 0000000..e55b419
--- /dev/null
+++ b/spm
@@ -0,0 +1,476 @@
+#!/bin/bash
+TMPDIR=${TMPDIR:-/tmp}
+
+default_script=build.sh
+build_order=scripts/build.order
+
+build_scripts=$(cat ${build_order})
+
+# TODO: Make this part of MKTC
+SPM_PROG_RELOC=$HOME/Documents/work/reloc/reloc
+SPM_PREFIX_BIN=.SPM_PREFIX_BIN
+SPM_PREFIX_TEXT=.SPM_PREFIX_TEXT
+SPM_DEPENDS=.SPM_DEPENDS
+SPM_VERBOSE=0
+
+export prefix_placeholder=_0123456789_0123456789_0123456789_0123456789_0123456789_0123456789_0123456789_
+export prefix="/${prefix_placeholder}"
+export maxjobs=7
+export pkgdir=$(pwd)/pkgs
+mkdir -p ${pkgdir}
+source include/9999-template.sh
+
+
+function spm_abspath() {
+ local filename="${1}"
+ local start="$(dirname ${filename})"
+
+ pushd "${start}" &>/dev/null
+ end="$(pwd)"
+ popd &>/dev/null
+
+ if [[ -f ${filename} ]]; then
+ end="${end}/$(basename ${filename})"
+ fi
+
+ echo "${end}"
+}
+
+
+function spm_rpath_nearest() {
+ local cwd="$(pwd)"
+ local start=$(dirname $(spm_abspath ${1}))
+ local result=
+
+ # Jump to location of file
+ cd "$(dirname ${start})"
+
+ # Scan upward until we find a "lib" directory
+ # OR when:
+ # - Top of filesystem is reached (pretty much total failure [missing local dep])
+ # - Top of active environment is reached (post installation)
+ # - Top of default installation prefix is reached (during packaging)
+ while [[ $(pwd) != / ]]
+ do
+ result+="../"
+ if [[ -d lib ]] || [[ $(pwd) == ${SPM_ENV} ]] || [[ $(pwd) == *${prefix} ]]; then
+ result+="lib"
+ break
+ fi
+ cd ..
+ done
+
+ # Sanitize: removing double-slashes (if any)
+ result=${result/\/\//\/}
+
+ # Return to where we were instantiated
+ cd "${cwd}"
+
+ echo "${result}"
+}
+
+function spm_gen_package_rpath() {
+ local rpath_orig
+ local rpath_new
+
+ # Assimilate all file paths that contain an RPATH
+ for path in $(find . -type f -not -name '.SPM-*')
+ do
+ readelf -d "${path}" 2>/dev/null | grep RPATH &>/dev/null
+ if (( $? )); then
+ continue
+ fi
+ rpath_orig="$(readelf -d "${path}" | grep RPATH | awk -F'[][]' '{ print $2 }')"
+ rpath_new='$ORIGIN/'"$(spm_rpath_nearest ${path})"
+ echo "${path}: ${rpath_orig} -> ${rpath_new}"
+ patchelf --set-rpath "${rpath_new}" "${path}"
+ done
+}
+
+function spm_gen_package_prefixes() {
+ echo "Generating build prefix manifest"
+ # Create record files
+ >${SPM_PREFIX_BIN}
+ >${SPM_PREFIX_TEXT}
+
+ # Assimilate file path for anything containing our prefix
+ local count_text=0
+ local count_bin=0
+ local count_total=0
+ local prefixes=(
+ ${prefix}
+ ${build_runtime}
+ ${build_root}
+ ${destdir}
+ )
+
+ for pkg_prefix in "${prefixes[@]}"; do
+ for path in $(find . -type f -not -name ".SPM_*"); do
+ # Check for prefix
+ grep -l "${pkg_prefix}" "${path}" &>/dev/null
+
+ # Prefix present? (0: yes, 1: no)
+ if (( $? )); then
+ continue
+ fi
+
+ # Get file type
+ local mimetype="$(file -i ${path} | awk -F': ' '{ print $2 }')"
+ local outfile
+
+ # Record prefix data
+ if [[ ${mimetype} = *text/* ]]; then
+ outfile=${SPM_PREFIX_TEXT}
+ (( count_text++ ))
+ else
+ outfile=${SPM_PREFIX_BIN}
+ (( count_bin++ ))
+ fi
+
+ echo "#${prefix}" >> "${outfile}"
+ echo "${path}" >> "${outfile}"
+
+ done
+ done
+
+ count_total=$(( count_text + count_bin ))
+ if (( ${count_total} )); then
+ echo "Text: ${count_text}"
+ echo "Binary: ${count_bin}"
+ echo "Total: ${count_total}"
+ else
+ echo "No prefixes detected"
+ fi
+}
+
+
+function spm_gen_package_depends() {
+ echo "Generating dependency manifest"
+ local outfile="${SPM_DEPENDS}"
+ >${outfile}
+
+ for dep in "${depends[@]}"; do
+ echo "${dep}" >> "${outfile}"
+ done
+}
+
+
+_spm_install_depends=()
+_spm_install_seen=()
+function spm_install() {
+ local pkg="$(pkg_match $1)"
+ if [[ ! -f ${pkg} ]]; then
+ echo "Package not found: ${pkg}" >&2
+ exit 1
+ fi
+
+ local destroot="$2"
+ if [[ -z ${destroot} ]]; then
+ echo "destination root undefined" >&2
+ exit 1
+ elif [[ ! -d ${destroot} ]]; then
+ mkdir -p "${destroot}"
+ fi
+
+ # extract package into temp directory
+ local pkgtmp=$(mktemp -d ${TMPDIR}/spm.XXXX)
+ pushd "${pkgtmp}" &>/dev/null
+ echo "Unpacking: ${pkg}"
+ tar xf "${pkg}"
+
+ local prefix_base
+
+ # relocate binaries
+ if [[ -f ${SPM_PREFIX_BIN} ]]; then
+ while read path; do
+ if [[ -z $path ]]; then
+ continue
+ elif [[ $path =~ ^# ]]; then
+ if [[ -z $path ]]; then
+ continue;
+ fi
+ prefix_base="${path#\#}"
+ continue
+ fi
+
+ if [[ ! -f $path ]]; then
+ echo "WARNING: ${path} does not exist!" >&2
+ continue
+ fi
+
+ if (( $SPM_VERBOSE )); then
+ if [[ $path =~ .pyc$ ]]; then
+ continue
+ fi
+ echo "Relocating binary paths: ${path}"
+ fi
+ ${SPM_PROG_RELOC} "${prefix_base}" "${destroot}" "${path}" 1>/dev/null
+ done <<< $(cat "${SPM_PREFIX_BIN}")
+ rm -f ${SPM_PREFIX_BIN}
+ fi
+ prefix_base=""
+
+ # relocate text
+ if [[ -f ${SPM_PREFIX_TEXT} ]]; then
+ while read path; do
+ if [[ -z $path ]]; then
+ continue
+ elif [[ $path =~ ^# ]]; then
+ if [[ -z $path ]]; then
+ continue;
+ fi
+ prefix_base="${path#\#}"
+ continue
+ fi
+
+ if [[ ! -f $path ]]; then
+ echo "WARNING: ${path} does not exist!" >&2
+ continue
+ fi
+ if (( $SPM_VERBOSE )); then
+ if [[ $path =~ .pyc$ ]]; then
+ continue
+ fi
+ echo "Relocating text paths: ${path}"
+ fi
+ sed -i -e "s|${prefix_base}|${destroot}|g" "${path}"
+ done <<< $(cat "${SPM_PREFIX_TEXT}")
+ rm -f ${SPM_PREFIX_TEXT}
+ fi
+
+ # service package dependencies
+ if [[ -f ${SPM_DEPENDS} ]]; then
+ if [[ -z ${_spm_install_depends} ]]; then
+ _spm_install_depends=($(cat ${SPM_DEPENDS}))
+ else
+ _spm_install_depends+=($(cat ${SPM_DEPENDS}))
+ fi
+
+ for dep in "${_spm_install_depends[@]}"; do
+ # Track dependencies we have already processed to avoid infinite recursion
+ if [[ "${_spm_install_seen[@]}" =~ "$dep" ]]; then
+ # Pop dependency and do nothing
+ _spm_install_depends=("${_spm_install_depends[@]:1}")
+ continue
+ fi
+
+ # Pop dependency and process it
+ _spm_install_depends=("${_spm_install_depends[@]:1}")
+
+ # Stop processing when the array is totally empty
+ if (( ${#_spm_install_depends[@]} < 0 )); then
+ break
+ fi
+
+ _spm_install_seen+=("${dep}")
+ spm_install "${dep}" "${destroot}"
+ done
+ rm -f "${SPM_DEPENDS}"
+ fi
+
+ # install package
+ echo "Installing: ${pkg}"
+ rsync -a ./ "${destroot}"
+
+ popd &>/dev/null
+ if [[ -d $pkgtmp ]]; then
+ rm -rf "${pkgtmp}"
+ fi
+}
+
+
+function builder() {
+ for build_script in ${build_scripts}; do
+ build_script=$(readlink -f scripts/$build_script/build.sh)
+ build_script_root=$(dirname ${build_script})
+ export build_root="${build_script_root}/buildroot"
+ export build_runtime="${build_script_root}/runtime"
+ export destdir="${build_script_root}/root"
+
+ export CC=gcc
+ export CXX=g++
+ export LD_LIBRARY_PATH="${build_runtime}/lib"
+ export CFLAGS="-I${build_runtime}/include"
+ export CPPFLAGS="${CFLAGS}"
+ export LDFLAGS="-L${build_runtime}/lib -Wl,-rpath="'\$$ORIGIN'"/../lib"
+
+ echo "Building: ${build_script}"
+
+ if [[ ! -f ${build_script} ]]; then
+ echo "${build_script} does not exist, check ${build_order}" >&2
+ exit 1
+ fi
+
+ if [[ -d ${build_root} ]]; then
+ rm -rf ${build_root}
+ fi
+ mkdir -p ${build_root}
+
+ if [[ -d ${build_runtime} ]]; then
+ rm -rf ${build_runtime}
+ fi
+ mkdir -p ${build_runtime}
+
+ if [[ -d ${destdir} ]]; then
+ rm -rf ${destdir}
+ fi
+ mkdir -p ${destdir}
+
+ export PATH="${build_runtime}/bin:${build_runtime}/sbin:$PATH"
+ export PKG_CONFIG_PATH="${build_runtime}/lib/pkgconfig"
+
+ pushd ${build_root} &>/dev/null
+ source ${build_script}
+ for url in "${sources[@]}"; do
+ if [[ -f $(basename $url) ]]; then
+ continue
+ fi
+ echo "Downloading source: ${url}"
+ curl -LO $url
+ done
+
+ for dep in "${depends[@]}"; do
+ echo "Depends on: ${dep}"
+ pkg=$(basename $(pkg_match "${dep}"))
+ spm_install "${pkg}" "${build_runtime}"
+ done
+ hash -r
+
+ set -e
+ prepare
+ build
+ package
+ set +e
+
+ if [[ -d ${destdir} ]]; then
+ pushd ${destdir} &>/dev/null
+ if [[ -d ${destdir}/${prefix} ]]; then
+ pushd ${destdir}/${prefix} &>/dev/null
+ fi
+ spm_gen_package_rpath
+ spm_gen_package_prefixes
+ spm_gen_package_depends
+
+ pkg="${pkgdir}/${name}-${version}-${revision}.tar.gz"
+ echo "Creating package: ${pkg}"
+ pwd
+ ls -la
+ tar cfz "${pkg}" .SPM_* .
+
+ if [[ -d ${destdir}/${prefix} ]]; then
+ popd &>/dev/null
+ fi
+ popd &>/dev/null
+ fi
+ popd &>/dev/null
+ done
+}
+
+function pkg_match() {
+ if [[ -z $1 ]]; then
+ echo "pkg_match: missing argument, package" >&2
+ exit 1
+ fi
+ match=$(find "${pkgdir}" -type f -regex ".*/${1}\-?[0-9]+?.*" 2>/dev/null | sort | head -n 1)
+ echo "${match}"
+}
+
+function installer() {
+ local root=""
+ local packages=()
+
+ while [[ $# != 0 ]]; do
+ case "$1" in
+ --verbose|-v)
+ SPM_VERBOSE=1
+ ;;
+ --root|-r)
+ root="$2"
+ shift
+ ;;
+ -*|--*)
+ echo "installer: unknown argument: $1" >&2
+ exit 1
+ ;;
+ *)
+ # "Most-likely" match the requested package by name
+ p=$(basename $(pkg_match "${1}"))
+ packages+=("$p")
+ ;;
+ esac
+ shift
+ done
+
+ if [[ -z $root ]]; then
+ echo "missing required argument: --root {destination}" >&2
+ exit 1
+ fi
+
+ export PATH="${root}/bin:${PATH}"
+ for pkg in "${packages[@]}"; do
+ spm_install "${pkg}" "${root}"
+ done
+}
+
+function usage() {
+echo "Usage: $0 [build|install] {package}
+
+Options:
+ --help (-h) this message
+ --verbose (-v) increase verbosity
+
+Commands:
+ build build a package
+ install install a package
+
+Positional arguments:
+ package package to interact with
+
+"
+}
+
+
+if [[ $# < 2 ]]; then
+ usage
+ exit 1
+fi
+
+fn=
+args=
+while [[ $# != 0 ]]; do
+ case "$1" in
+ --verbose|-v)
+ SPM_VERBOSE=1
+ ;;
+ --help|-h)
+ usage
+ exit 0
+ ;;
+ build)
+ fn=builder
+ if [[ $2 != "all" ]]; then
+ build_scripts="$2"
+ if [[ $build_scripts =~ .*/build.sh ]]; then
+ build_scripts=$(dirname ${build_scripts})
+ fi
+ fi
+ shift 2
+ break
+ ;;
+
+ install)
+ fn=installer
+ shift
+ args=("$@")
+ break
+ ;;
+
+ *)
+ echo "unknown argument: $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+$fn "${args[@]}"