#!/usr/bin/env bash trap spm_build_cleanup EXIT THIS_SCRIPT=$(dirname ${BASH_SOURCE[0]}) function pushd() { command pushd "$@" >/dev/null } function popd() { command popd >/dev/null } function _msg() { marker="$1" shift args="$@" printf '%s %s\n' "${marker}" "${args}" } function msg() { _msg "=>" "$@" } function msg2() { _msg " *" "$@" } function msg3() { _msg " >" "$@" } function msg_fail() { _msg "FAILED:" "$@" } function msg_error() { _msg "ERROR:" "$@" } function msg_warn() { _msg "WARNING:" "$@" } function tar() { local cmd=bsdtar $cmd "$@" } function spm_debug_shell() { local where where="${1}" if [[ -z "${where}" ]] || [[ ! -d "${where}" ]]; then where="$(pwd)" fi pushd "${where}" || exit 1 PS1="(SPMBUILD DEBUG)$ " export PS1 echo "ENTERING DEBUG SHELL" env bash # Exiting here prevents the user from leaving debug calls in their build scripts # (i.e. when this function is used, no package archives will be generated) exit 1 } function spm_build_initialize_stage1() { rp=$(which realpath 2>/dev/null \ || which spm_realpath 2>/dev/null \ || echo ./spm_realpath) export SPMDEV=$($rp "${THIS_SCRIPT}/../src/spm" 2>/dev/null) export SPM=$(which spm 2>/dev/null || echo ${SPMDEV}) spm_build_check_rt_env if [[ $? != 0 ]]; then msg_error "spm self-test failed" exit 1 fi if [[ ! -f ${SPM} ]]; then msg_error "Could not locate 'spm'" exit 1 fi export SPM_BUILD_SYS_TARGET=$(spm_build_get_sys_target) export SPM_BUILD_STORE="${HOME}/.spm/build" if [[ ! -d ${SPM_BUILD_STORE} ]]; then mkdir -p "${SPM_BUILD_STORE}" if [[ $? != 0 ]]; then msg_error "SPM_BUILD_STORE creation failed: ${SPM_BUILD_STORE}" exit 1 fi fi export SPM_BUILD_STORE_SOURCES="${SPM_BUILD_STORE}/sources" if [[ ! -d ${SPM_BUILD_STORE_SOURCES} ]]; then mkdir -p "${SPM_BUILD_STORE_SOURCES}" if [[ $? != 0 ]]; then msg_error "SPM_BUILD_STORE_SOURCES creation failed: ${SPM_BUILD_STORE_SOURCES}" exit 1 fi fi export SPM_BUILD_STORE_PACKAGES="${SPM_BUILD_STORE}/pkgs" if [[ ! -d ${SPM_BUILD_STORE_PACKAGES} ]]; then mkdir -p "${SPM_BUILD_STORE_PACKAGES}" if [[ $? != 0 ]]; then msg_error "SPM_BUILD_STORE_PACKAGES creation failed: ${SPM_BUILD_STORE_PACKAGES}" exit 1 fi fi # Set global TMPDIR to *help* prevent cluttering the system's temporary storage directory export TMPDIR="${SPM_BUILD_STORE}/tmp" if [[ ! -d ${TMPDIR} ]]; then mkdir -p "${TMPDIR}" if [[ $? != 0 ]]; then msg_error "TMPDIR creation failed: ${TMPDIR}" exit 1 fi fi } function spm_build_new_root() { local path path=$(mktemp) if [[ ! -f "${path}" ]]; then msg_error "spm_build_new_root: failed to create temporary file" exit 1 fi spm_build_mkruntime "$1" > "${path}" source "${path}" rm -f "${path}" } function spm_build_initialize_stage2() { # In order to get the SPM_* variables out spm, we need to give a path... # this is ugly but safe-ish spm_build_new_root "/usr" unset CFLAGS unset CPPFLAGS unset LDFLAGS unset FCFLAGS # All templates strings MUST be the same length to allow for proper relocation export _SPM_BUILD_ROOT_TEMPLATE="${TMPDIR}/spmbuild_root_XXXXXXXXX" export _SPM_BUILD_RUNTIME_TEMPLATE="${TMPDIR}/spmbuild_runtime_XXXXXX" export _SPM_BUILD_PKGDIR_TEMPLATE="${TMPDIR}/spmbuild_pkgdir_XXXXXXX" export SPM_BUILD_ROOT_BASE=$(mktemp -d "${_SPM_BUILD_ROOT_TEMPLATE}") export SPM_BUILD_ROOT="${SPM_BUILD_ROOT_BASE}/${SPM_META_PREFIX_PLACEHOLDER}" export _srcdir="${SPM_BUILD_ROOT}" mkdir -p "${SPM_BUILD_ROOT}" if [[ ! -d ${SPM_BUILD_ROOT} ]]; then msg_error "SPM_BUILD_ROOT creation failed: ${SPM_BUILD_ROOT}" exit 1 fi export SPM_BUILD_RUNTIME_BASE=$(mktemp -d "${_SPM_BUILD_RUNTIME_TEMPLATE}") export SPM_BUILD_RUNTIME="${SPM_BUILD_RUNTIME_BASE}/${SPM_META_PREFIX_PLACEHOLDER}" export _runtime="${SPM_BUILD_RUNTIME}" mkdir -p "${SPM_BUILD_RUNTIME}" if [[ ! -d ${SPM_BUILD_RUNTIME} ]]; then msg_error "SPM_BUILD_RUNTIME creation failed: ${SPM_BUILD_RUNTIME}" exit 1 fi export SPM_BUILD_PKGDIR_BASE=$(mktemp -d "${_SPM_BUILD_PKGDIR_TEMPLATE}") export SPM_BUILD_PKGDIR="${SPM_BUILD_PKGDIR_BASE}/${SPM_META_PREFIX_PLACEHOLDER}" export _pkgdir="${SPM_BUILD_PKGDIR}" mkdir -p "${SPM_BUILD_PKGDIR}" if [[ ! -d ${SPM_BUILD_PKGDIR} ]]; then msg_error "SPM_BUILD_PKGDIR creation failed: ${SPM_BUILD_PKGDIR}" exit 1 fi # _prefix is a not a real path. do not create it! # _prefix is double-wide export SPM_BUILD_PREFIX="/${SPM_META_PREFIX_PLACEHOLDER}${SPM_META_PREFIX_PLACEHOLDER}" export _prefix="${SPM_BUILD_PREFIX}" export SPM_BUILD_CRUMBS="${SPM_BUILD_PREFIX} ${SPM_BUILD_ROOT} ${SPM_BUILD_RUNTIME} ${SPM_BUILD_PKGDIR}" export _maxjobs="$(getconf _NPROCESSORS_ONLN 2>/dev/null)" if (( ${_maxjobs} > 1 )); then _maxjobs=$((_maxjobs - 1)) elif [[ -z ${_maxjobs} ]]; then _maxjobs=1 fi spm_build_new_root "${SPM_BUILD_RUNTIME}" LD_LIBRARY_PATH="${SPM_BUILD_RUNTIME}/lib:${SPM_BUILD_RUNTIME}/lib64" LD_RUN_PATH="${LD_LIBRARY_PATH}" if [[ $(uname -s) == Linux ]]; then export LD_LIBRARY_PATH export LD_RUN_PATH elif [[ $(uname -s) == Darwin ]]; then # Darwin is ridiculous. Don't rely on DYLD_* anything. unset DYLD_LIBRARY_PATH unset LD_LIBRARY_PATH unset LD_RUN_PATH #elif # ... another OS's equivalent variable here fi export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 } spm_build_cleaned=0 export spm_build_cleaned function spm_build_cleanup() { # Don't clean up more than once if (( spm_build_cleaned > 0)); then return fi if [[ ${keep} != 0 ]]; then msg "Temporary storage not destroyed" msg2 "${SPM_BUILD_ROOT_BASE}" msg2 "${SPM_BUILD_RUNTIME_BASE}" msg2 "${SPM_BUILD_PKGDIR_BASE}" return fi [[ -d ${SPM_BUILD_ROOT_BASE} ]] && rm -rf ${SPM_BUILD_ROOT_BASE} [[ -d ${SPM_BUILD_RUNTIME_BASE} ]] && rm -rf ${SPM_BUILD_RUNTIME_BASE} [[ -d ${SPM_BUILD_PKGDIR_BASE} ]] && rm -rf ${SPM_BUILD_PKGDIR_BASE} spm_build_cleaned=1 } function spm_build_install() { ${SPM} --verbose --yes --override-manifests --manifest "${SPM_BUILD_STORE_PACKAGES}" --install $@ --root "${SPM_BUILD_RUNTIME}" } function spm_build_mkprefixbin() { ${SPM} --yes --cmd mkprefixbin $@ } function spm_build_mkprefixtext() { ${SPM} --yes --cmd mkprefixtext $@ } function spm_build_mkmanifest() { ${SPM} --yes --cmd mkmanifest $@ } function spm_build_mkruntime() { ${SPM} --yes --cmd mkruntime "${1}" } function spm_build_rpath_set() { ${SPM} --yes --cmd rpath_set $@ } function spm_build_rpath_autoset() { ${SPM} --yes --cmd rpath_autoset $@ } function spm_build_get_package_ext() { ${SPM} --yes --cmd get_package_ext } function spm_build_get_sys_target() { ${SPM} --yes --cmd get_sys_target } function spm_build_check_rt_env() { ${SPM} --yes --cmd check_rt_env } function spm_build_fetch() { local filename local tmpdir if (( $# < 2 )); then msg_error "spm_build_fetch: {url} {destination}" spm_build_cleanup exit 1 fi tmpdir=$(mktemp -d) if [[ ! -d ${tmpdir} ]]; then msg_error "could not create temporary directory" spm_build_cleanup exit 1 fi pushd ${tmpdir} # file already exists? if [[ -f "${2}/$(basename ${1})" ]]; then return fi # download file curl -LO "${1}" filename="$(find . -type f)" mkdir -p "${2}" mv "${filename}" "${2}" popd rm -rf "${tmpdir}" } function spm_build_do_sources() { local name="${1}" local remote for remote in "${sources[@]}"; do spm_build_fetch "${remote}" ${SPM_BUILD_STORE_SOURCES}/${name} done } function spm_build_mkdepends() { local filename="${1}" shift local deps=("$@") > "${filename}" for dep in "${deps[@]}"; do echo "${dep}" >> "${filename}" done } function spm_build_mkdesc() { local filename="${1}" if [[ -z ${filename} ]]; then msg_error "spm_build_mkdesc requires a path" exit 1 fi echo "# SPM PACKAGE DESCRIPTOR name=${name:-*} version=${version:-*} revision=${revision:-*} license=${license:-*} homepage=${homepage:-*} summary=${summary:-*} " > "${filename}" } function spm_build_mkfilelist() { local filename="${1}" if [[ -z ${filename} ]]; then msg_error "spmbuild_mkfilelist requires an output filename" exit 1 fi echo "# SPM PACKAGE FILE LIST" > "${filename}" find . -type f -not -name ".SPM_*" >> "${filename}" } function prepare() { # stub : } function build() { # stub : } function package() { msg_error "package() function missing" exit 1 } function spm_build_do_stage_prepare() { prepare set +x } function spm_build_do_stage_build() { build set +x } function spm_build_do_stage_package() { package set +x } function spm_build_tar() { local archive="${1}" bsdtar -c -z -f "${archive}" \ --exclude '..' \ --exclude '.' \ .* * } function spm_build_do_stage_archive() { local archive="${1}" pushd "${SPM_BUILD_PKGDIR}" # Enter the "prefix" (i.e. strip off the leading slash) pushd "${SPM_BUILD_PREFIX#\/}" msg2 "Recording binary prefixes..." spm_build_mkprefixbin "${SPM_META_PREFIX_BIN}" . ${SPM_BUILD_CRUMBS} msg2 "Recording text prefixes..." spm_build_mkprefixtext "${SPM_META_PREFIX_TEXT}" . ${SPM_BUILD_CRUMBS} msg2 "Recording dependencies..." spm_build_mkdepends "${SPM_META_DEPENDS}" "${depends[@]}" msg2 "Recording package data..." spm_build_mkdesc "${SPM_META_DESCRIPTOR}" msg2 "Recording paths..." spm_build_mkfilelist "${SPM_META_FILELIST}" msg2 "Archiving files..." spm_build_tar "${archive}" popd popd } function spm_build_declare_metadata() { # Declare package metadata we need at this level package_name="${name}-${version}-${revision}" package_archive="${package_name}$(spm_build_get_package_ext)" package_target="${SPM_BUILD_STORE_PACKAGES}/${SPM_BUILD_SYS_TARGET}" package_final="${package_target}/${package_archive}" } function spm_build_update_metadata() { spm_build_declare_metadata } # -- main -- export keep=${keep:-0} export SPM_BUILD_SCRIPT="build.sh" export SPM_BUILD_SCRIPT_ROOT="$1" if [[ ! -d ${SPM_BUILD_SCRIPT_ROOT} ]] || [[ ! -f ${SPM_BUILD_SCRIPT_ROOT}/${SPM_BUILD_SCRIPT} ]]; then msg_error "Need a directory containing a build script (${SPM_BUILD_SCRIPT})" exit 1 fi msg "Initializing..." spm_build_initialize_stage1 spm_build_initialize_stage2 # post-runtime activation msg2 "Sourcing build script..." source "${SPM_BUILD_SCRIPT_ROOT}/${SPM_BUILD_SCRIPT}" spm_build_declare_metadata if [[ ! -d "${package_target}" ]]; then mkdir -p ${package_target} spm_build_mkmanifest "${package_target}" fi msg2 "Downloading source files..." spm_build_do_sources "${package_name}" msg3 "Copying source files..." rsync -avi "${SPM_BUILD_STORE_SOURCES}/${package_name}/" "${SPM_BUILD_ROOT}" rsync -avi --exclude "${SPM_BUILD_SCRIPT}" "${SPM_BUILD_SCRIPT_ROOT}/" "${SPM_BUILD_ROOT}" ls -l "${SPM_BUILD_ROOT}" msg "Environment data" printenv | grep -E '^SPM_' | sort printenv | grep -E '^[A-Z]{0,3}FLAGS' | sort msg "Processing ${package_name}" set -e if [[ ${#build_depends[@]} != 0 ]]; then msg2 "Installing build dependencies" spm_build_install "${build_depends[@]}" fi if [[ ${#depends[@]} != 0 ]]; then msg2 "Installing runtime dependencies" spm_build_install "${depends[@]}" fi if [[ ${name} != "filesystem" ]]; then depends=(filesystem "${depends[@]}") fi pushd "${SPM_BUILD_ROOT}" msg2 "prepare()" spm_build_do_stage_prepare msg2 "build()" spm_build_do_stage_build msg2 "package()" spm_build_do_stage_package popd msg "Create package: ${package_final}" spm_build_do_stage_archive "${package_final}" msg "Update manifest" spm_build_mkmanifest "${package_target}" msg "Clean" spm_build_cleanup