diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2019-09-30 23:35:07 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2019-09-30 23:35:07 -0400 |
commit | 031bc1627fdc38d1c178707c86dd267224b94693 (patch) | |
tree | 9e44e7bfd24da233e0b950892f48932bdde37618 /spm | |
download | spm-031bc1627fdc38d1c178707c86dd267224b94693.tar.gz |
Initial commit
Diffstat (limited to 'spm')
-rwxr-xr-x | spm | 476 |
1 files changed, 476 insertions, 0 deletions
@@ -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[@]}" |