#!/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[@]}"