diff options
author | Joseph Hunkeler <jhunkeler@gmail.com> | 2019-07-17 15:16:47 -0400 |
---|---|---|
committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2019-07-17 15:16:47 -0400 |
commit | 4d96198479e427788656c2d4d6e586908f24d1c6 (patch) | |
tree | efe37c025160642e96c8e4946fb828aed2512d0f | |
download | sfpm-4d96198479e427788656c2d4d6e586908f24d1c6.tar.gz |
Initial commit
-rwxr-xr-x | bin/sfpm | 62 | ||||
-rwxr-xr-x | bin/sfpm_makepkg | 78 | ||||
-rw-r--r-- | share/sfpm/build.sh | 347 | ||||
-rw-r--r-- | share/sfpm/common.sh | 17 | ||||
-rw-r--r-- | share/sfpm/download.sh | 109 | ||||
-rw-r--r-- | share/sfpm/env.sh | 233 | ||||
-rw-r--r-- | share/sfpm/index.sh | 129 | ||||
-rw-r--r-- | share/sfpm/msg.sh | 29 | ||||
-rw-r--r-- | share/sfpm/pkg_read.sh | 37 | ||||
-rw-r--r-- | share/sfpm/sfpm.sh | 88 |
10 files changed, 1129 insertions, 0 deletions
diff --git a/bin/sfpm b/bin/sfpm new file mode 100755 index 0000000..36bebbf --- /dev/null +++ b/bin/sfpm @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source "$(dirname ${BASH_SOURCE[0]})/../share/sfpm/common.sh" +sfpm_gen_sysroot + +# ---- +env_name= +mode_install=0 +mode_search=0 +mode_update=0 +mode_verify=0 +packages=() + +while (( "${#}" )); do + case "${1}" in + -e|--env) + env_name="${2}" + shift 2 + ;; + -i|--install) + mode_install=1 + shift + ;; + -s|--search) + mode_search=1 + shift + ;; + -u|--update|--upgrade) + mode_update=1 + shift + ;; + -v|--verify) + mode_verify=1 + shift + ;; + --) + shift + break + ;; + -*|--*) + msg_error "Invalid argument: ${1}" >&2 + exit 1 + ;; + *) + packages+=("${2}") + shift + ;; + esac +done + +set -- "${packages[@]}" + +if [[ -z ${env_name} ]] && [[ ${mode_install} == 0 ]]; then + msg_error "No environment specified. (-e {name}, required)" + exit 1 +fi + +sfpm_index_create +sfpm_env_create "${env_name}" +sfpm_install "${env_name}" $(sfpm_index_search nasm) # "nasm" "2.14.02" "1" +sfpm_install "${env_name}" $(sfpm_index_search zlib) +sfpm_install "${env_name}" $(sfpm_index_search expat) # "expat" "2.2.6" "1" +sfpm_install "${env_name}" $(sfpm_index_search python) # "python" "3.7.2" "1" diff --git a/bin/sfpm_makepkg b/bin/sfpm_makepkg new file mode 100755 index 0000000..ff6d2be --- /dev/null +++ b/bin/sfpm_makepkg @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Deny execution as root user +uid=$(id -u) +username=$(whoami) +if [[ ${uid} == 0 ]] || [[ ${username} == root ]]; then + msg_error "$(basename ${0}) should be executed by a regular user, not with root." + exit 1 +fi + +unset uid +unset username + +export SFPM_MAKEPKG_ENV=$(basename $(mktemp -u sfpm.makepkg.XXXXXX)) +export sfpm_build_scriptdir=$(pwd) + +source "$(dirname ${BASH_SOURCE[0]})/../share/sfpm/common.sh" +source "${sfpm_internal_include}/build.sh" + +# --- Functions + +cleanup() { + if [[ ${buildroot} == *${sfpm_tmpdir}* ]]; then + msg "Removing ${buildroot}" + rm -rf "${buildroot}" + fi + + if [[ $(sfpm_env_exists ${SFPM_MAKEPKG_ENV}) ]]; then + deactivate + sfpm_env_remove "${SFPM_MAKEPKG_ENV}" + fi +} +trap cleanup EXIT SIGINT SIGTERM + +# --- Main + +# Assimilate build definition +msg "Sourcing build script" +source ${1} + +# Sanity checks +msg "Validating build script" +sfpm_check_required_keys + +# Create buildroot +buildroot=$(sfpm_gen_buildroot) +root="${buildroot}/root" +pkgdir="${buildroot}/pkg" +srcdir="${buildroot}/src" + +mkdir -p "${root}" "${pkgdir}" "${srcdir}" + +# Generate temporary sfpm build environment +sfpm_env_create "${SFPM_MAKEPKG_ENV}" + +# Activate temporary sfpm build environment (provides ${SFPM_ENV}) +source ${sfpm_envdir}/${SFPM_MAKEPKG_ENV}/bin/activate + +# Setup compiler/linker flags +sfpm_build_cflags="-I${SFPM_ENV}/include ${sfpm_build_cflags}" +sfpm_build_ldflags="-L${SFPM_ENV}/lib ${sfpm_build_ldflags}" + +# Ensure local dependencies resolve during build +export LD_LIBRARY_PATH="${SFPM_ENV}/lib" + +msg "Executing tasks" +pushd "${root}" + sfpm_build_env_do_depends "${SFPM_MAKEPKG_ENV}" + sources_fetch + sfpm_sources_cmp_sha256 + sources_extract "${srcdir}" + prepare + build + check + package + sfpm_gen_packages +popd + diff --git a/share/sfpm/build.sh b/share/sfpm/build.sh new file mode 100644 index 0000000..9f76d8e --- /dev/null +++ b/share/sfpm/build.sh @@ -0,0 +1,347 @@ +required_keys=( + name + version + release +) + +function sfpm_CPUS() { + local count=$(getconf _NPROCESSORS_ONLN) + + ((count--)) + if (( ${count} <= 0 )); then + count=1 + fi + + echo ${count} +} + +function sfpm_gen_srcdir() { + local path="${sfpm_srcdir}/${name}-${version}-${release}" + if [[ ! -d ${path} ]]; then + mkdir -p ${path} + fi + echo ${path} +} + + +function sfpm_rm_srcdir() { + local path="${sfpm_srcdir}/${name}-${version}-${release}" + if [[ -d ${path} ]]; then + rm -rf "${path}" + fi +} + + +function sfpm_check_required_keys() { + die=0 + for key in "${required_keys[@]}" + do + if [[ ! ${!key} ]]; then + msg_error "Package '${key}' undefined!" + die=1 + fi + done + + if (( ${die} )); then + exit 1 + fi +} + + +function sfpm_cmp_sha256() { + # is a FILE + local sum_file="${1}" + if [[ ! -f ${sum_file} ]]; then + msg_error "${sum_file} is not a file" + exit 1 + fi + + # sha256 hashes + local sum_left=$(sha256sum ${sum_file} | awk '{ print $1 }') + local sum_right="${2}" + + # compare hashes + if [[ ${sum_left} != ${sum_right} ]]; then + return 1 + fi + + return 0 +} + +function sfpm_verify_gpg() { + msg_warn "sfpm_verify_gpg(): Not implemented" +} + +function prepare() { + msg_warn "prepare() function undefined" +} + + +function build() { + msg_warn "build() function undefined" +} + +function check() { + msg_warn "check() function undefined" +} + +function package() { + msg_error "package() function undefined" + exit 1 +} + + +function sources_fetch() { + local path=$(sfpm_gen_srcdir) + for src in "${sources[@]}" + do + fetch --checksum --skip-exists --redirect --output ${path} ${src} + done +} + + +function sources_extract() { + local destdir="${1}" + if [[ ! ${destdir} ]]; then + msg_error "sources_extract() destination undefined: ${destdir}" + exit 1 + fi + + if [[ ! -d ${destdir} ]]; then + mkdir -p "${destdir}" + fi + + local srcdir="$(sfpm_gen_srcdir)" + if [[ ! -d ${srcdir} ]]; then + msg_error "${srcdir} does not exist!" + exit 1 + fi + + archives_tar=$(find ${srcdir} -maxdepth 1 -type f \( -name "*.tar*" -o -name "*.t*" \) -and -not -name "*.sha256") + archives_zip=$(find ${srcdir} -maxdepth 1 -type f \( -name "*.zip" \) -and -not -name "*.sha256") + + msg2 "Extracting" + pushd "${srcdir}" + if [[ ${archives_tar} ]]; then + for archive in ${archives_tar} + do + msg3 "${archive}" + tar xf "${archive}" -C "${destdir}" + done + fi + + if [[ ${archives_zip} ]]; then + for archive in ${archives_zip} + do + msg3 "${archive}" + unzip "${archive}" -d "${destdir}" + done + fi + popd +} + + +function sfpm_sources_cmp_sha256() { + source_count=${#sources[@]} + sha256_count=${#sha256sums[@]} + + if (( ! ${sha256_count} )); then + return 0 + fi + + msg2 "Comparing sha256 checksums" + if [[ ${source_count} != ${sha256_count} ]]; then + msg_error "Total sources (${source_count}) does not match total of hashes (${sha256_count})" \ + "HINT: Place 'null' for each source without a hash." + exit 1 + fi + + for url in "${sources[@]}" + do + for sha in "${sha256sums[@]}" + do + if [[ ${sha} == null ]] || [[ ${sha} == NULL ]]; then + continue + fi + + archive="${sfpm_srcdir}/${name}-${version}-${release}/$(basename ${url})" + sfpm_cmp_sha256 ${archive} ${sha} + if (( ${?} )); then + msg_error "${sha} does not match $(basename ${archive})" + exit 1 + fi + done + done +} + +function sfpm_gen_buildroot() { + export buildroot=$(mktemp -d ${TMPDIR}/sfpm.buildroot.XXXXXX) + if [[ ! -d ${buildroot} ]]; then + msg_error "Failed to create buildroot: ${buildroot}" + exit 1 + fi + + echo ${buildroot} +} + + +function sfpm_rm_buildroot() { + if [[ ${buildroot} == *${sfpm_tmpdir}* ]]; then + msg "Removing ${buildroot}" + rm -rf "${buildroot}" + fi +} + +function sfpm_build_env_do_depends() { + local env_name="${1}" + if [[ ${depends} ]]; then + for dep in "${depends[@]}" + do + local pkg=$(sfpm_index_search ${dep}) + sfpm_install "${env_name}" "${pkg}" + done + fi +} + +function sfpm_gen_package_manifest() { + pushd "${pkgdir}" + find . -type f -not -name ".SFPM-*" | xargs -I'{}' sha256sum "{}" > .SFPM-MANIFEST + popd +} + + +# Don't use this. Getting rid of it. +function sfpm_gen_package_sizes() { + pushd "${pkgdir}" + find . -type f -not -name ".SFPM-*" | xargs -I'{}' -n1 du -b "{}" > .SFPM-SIZES + popd +} + +function sfpm_gen_package_depends_manifest() { + pushd "${pkgdir}" + >.SFPM-DEPENDS + for dep in "${depends[@]}" + do + echo "${dep}" >> .SFPM-DEPENDS + done + popd +} + +function sfpm_gen_package_rpath() { + pushd "${pkgdir}" + local rpath_orig + local rpath_new + local rpath_cache="$(mktemp ${TMPDIR}/sfpm.rpath_cache.XXXXXX)" + + # Assimilate all file paths that contain an RPATH + for path in $(find . -type f -not -name '.SFPM-*') + do + readelf -d "${path}" 2>/dev/null | grep RPATH &>/dev/null + if (( $? )); then + continue + fi + echo "${path}" >> "${rpath_cache}" + done + + msg2 "Adjusting depth of RPATHs" + while read line + do + rpath_orig="$(readelf -d ${line} | grep RPATH | awk -F'[][]' '{ print $2 }')" + rpath_new='$ORIGIN/'"$(sfpm_rpath_nearest ${line})" + msg3 "${line}: ${rpath_orig} -> ${rpath_new}" + patchelf --set-rpath "${rpath_new}" "${line}" + done < "${rpath_cache}" + [[ -f "${rpath_cache}" ]] && rm -f "${rpath_cache}" + popd +} + +function sfpm_gen_package_prefixes() { + msg "Generating build prefix manifest" + pushd "${pkgdir}" + # Create record files + >.SFPM-PREFIX-TEXT + >.SFPM-PREFIX-BIN + + # Assimilate file path for anything containing our prefix + local count_text=0 + local count_bin=0 + local count_total=0 + + for path in $(find . -type f -not -name ".SFPM-*") + do + # Check for prefix + grep -l "${sfpm_build_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=.SFPM-PREFIX-TEXT + (( count_text++ )) + else + outfile=.SFPM-PREFIX-BIN + (( count_bin++ )) + fi + + echo "${path}" >> "${outfile}" + + done + + count_total=$(( count_text + count_bin )) + if (( ${count_total} )); then + msg2 "Text: ${count_text}" + msg2 "Binary: ${count_bin}" + msg2 "Total: ${count_total}" + else + msg2 "No prefixes detected" + fi + popd +} + +function sfpm_gen_packages() { + local funcs=$(compgen -A function | grep ^package) + local name_old="${name}" + local pkgdir_old="${pkgdir}" + for fn in ${funcs} + do + # Don't modify main package() behavior + if [[ ${fn} != package ]]; then + name=${fn#package_} + pkgdir_child=$(mktemp -d ${TMPDIR}/sfpm.pkgdir_child.XXXXXX) + pkgdir="${pkgdir_child}" + fi + + ${fn} + sfpm_gen_package + + if [[ -d ${pkgdir_child} ]]; then + rm -rf "${pkgdir_child}" + name="${name_old}" + pkgdir="${pkgdir_old}" + fi + done +} + +function sfpm_gen_package() { + if [[ ! ${sfpm_build_scriptdir} ]]; then + msg_error "Refusing to generate package outside of sfpm_makepkg" + exit 1 + fi + + archive=${name}-${version}-${release}.tar.bz2 + pushd "${pkgdir}" + msg "Generating ${archive}" + sfpm_gen_package_manifest + sfpm_gen_package_depends_manifest + sfpm_gen_package_prefixes + sfpm_gen_package_rpath + tar cfj "${sfpm_build_scriptdir}/${archive}" .SFPM-* * + popd +} diff --git a/share/sfpm/common.sh b/share/sfpm/common.sh new file mode 100644 index 0000000..8b7f33a --- /dev/null +++ b/share/sfpm/common.sh @@ -0,0 +1,17 @@ +# Override default directory stack output behavior +pushd () { + command pushd "$@" > /dev/null +} + +popd () { + command popd "$@" > /dev/null +} + +sfpm_root="$(readlink -f $(dirname ${BASH_SOURCE[0]})/../..)" +sfpm_internal_include=${sfpm_root}/share/sfpm +source ${sfpm_internal_include}/msg.sh +source ${sfpm_internal_include}/sfpm.sh +source ${sfpm_internal_include}/env.sh +source ${sfpm_internal_include}/pkg_read.sh +source ${sfpm_internal_include}/index.sh +source ${sfpm_internal_include}/download.sh diff --git a/share/sfpm/download.sh b/share/sfpm/download.sh new file mode 100644 index 0000000..0f3a0ec --- /dev/null +++ b/share/sfpm/download.sh @@ -0,0 +1,109 @@ +DOWNLOADERS=( + curl + wget +) + + +function fetch_select() +{ + # Good chance this will not be used. cURL is sufficient as it is... + for prog in "${DOWNLOADERS[@]}" + do + fetcher=$(type -p ${prog}) + if [[ ${fetcher} ]]; then + break + fi + done + + if [[ ! ${fetcher} ]]; then + msg_error "Cannot continue; no program available to download files with." + exit 1 + fi + echo ${fetcher} +} + + +function fetch() +{ + args=( --fail ) + while (( "${#}" )); do + case "${1}" in + -r|--redirect) + args+=( -L ) + shift + ;; + -O|--remote-name) + args+=( -O ) + shift + ;; + -o|--output) + output="${2}" + shift 2 + ;; + -s|--skip-exists) + skip=1 + shift + ;; + -c|--checksum) + checksum=1 + shift + ;; + --) + shift + break + ;; + -*|--*) + msg_error "Invalid argument: ${1}" >&2 + exit 1 + ;; + *) + url="${1}" + shift + ;; + esac + done + + set -- "${args[@]}" + + filename="$(basename ${url})" + msg "Fetching ${filename}" + + if [[ ${output} ]]; then + output="${output}/${filename}" + filename_checksum="${output}.sha256" + + if [[ -f ${filename_checksum} ]]; then + msg2 "Verifying checksum: ${filename_checksum}" + for line in "$(sha256sum -c ${filename_checksum})" + do + msg3 "${line}" + done + + if (( $? )); then + exit 1 + fi + fi + + if (( ${skip} )) && [[ -f ${output} ]]; then + msg2 "Source exists: ${output}" + return 0 + fi + args+=( -o ${output} ) + fi + + $(fetch_select) ${args[@]} ${url} + fetch_retval=$? + if (( ${fetch_retval} )); then + msg_error "Failed to fetch: (${fetch_retval}): ${url}" + exit 1 + fi + + if [[ ${output} ]]; then + if (( ${checksum} )); then + if [[ ! -f ${filename_checksum} ]]; then + sha256sum "${output}" > "${filename_checksum}" + fi + fi + fi +} + diff --git a/share/sfpm/env.sh b/share/sfpm/env.sh new file mode 100644 index 0000000..e9127a6 --- /dev/null +++ b/share/sfpm/env.sh @@ -0,0 +1,233 @@ +function sfpm_env_exists() { + # Returns empty string on failure (implicit) + local name="${1}" + if [[ ! ${name} ]]; then + msg_error "sfpm_env_exists(): missing environment name" + exit 1 + fi + + local path="${sfpm_envdir}/${name}" + if [[ ! -d ${path} ]]; then + return 1 + fi + echo ${path} +} + + +function sfpm_env_create() { + local name="${1}" + local path="${sfpm_envdir}/${name}" + if [[ ! $(sfpm_env_exists ${name}) ]]; then + msg "Creating environment root: ${name}" + sfpm_gen_sysroot "${path}" + fi + + if [[ ! -f ${path}/bin/activate ]]; then + sed -e "s|__SFPM_ENV__|${path}|g" \ + ${sfpm_sysconfdir}/sfpm.activate.sh > ${path}/bin/activate + fi + +} + + +function sfpm_env_remove() { + local name="${1}" + local path="${sfpm_envdir}/${name}" + if [[ $(sfpm_env_exists ${name}) ]]; then + msg "Removing environment root: ${name}" + rm -rf "${path}" + fi +} + +function sfpm_rpath_nearest() { + local cwd="$(pwd)" + local start=$(dirname $(sfpm_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) == ${SFPM_ENV} ]] || [[ $(pwd) == *${sfpm_build_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 sfpm_install() { + local env_name="${1}" + local env_path=$(sfpm_env_exists "${env_name}") + local pkg_name="${2}" + local pkg_version="${3}" + local pkg_release="${4}" + local pkg="${pkg_name}-${pkg_version}-${pkg_release}" + local pkg_path + local metadata + local staging + local have_prefix_text + local have_prefix_bin + + msg "Installing ${pkg_name} [env: ${env_name}]" + if [[ ! ${env_path} ]]; then + msg_error "sfpm_install(): Environment does not exist: ${env_name}" + exit 1 + fi + + if [[ ${pkg_name} == */*.tar.bz2 ]]; then + pkg_path="${pkg_name}" + else + pkg_path=$(sfpm_package_exists "${pkg}.tar.bz2") + fi + + if [[ ! -f ${pkg_path} ]]; then + msg_error "sfpm_install(): Package does not exist: ${pkg}" + exit 1 + fi + + + msg2 "Extracting metadata" + metadata=$(mktemp -d ${TMPDIR}/sfpm.metadata.XXXXXX) + tar -xf "${pkg_path}" \ + -C "${metadata}" \ + --wildcards "\.SFPM-*" + + msg2 "Extracting package" + staging=$(mktemp -d ${TMPDIR}/sfpm.staging.XXXXXX) + tar -xf "${pkg_path}" \ + -C "${staging}" \ + --strip-components=1 \ + --wildcards "${sfpm_build_prefix/\//}*" + + have_prefix_text=$(wc -l ${metadata}/.SFPM-PREFIX-TEXT | cut -d ' ' -f 1) + if [[ ${have_prefix_text} != 0 ]]; then + msg2 "Relocating text paths" + while read filename + do + msg3 "${filename}" + sfpm_file_relocate --text --env "${env_name}" --path "${filename}" + done < <(sed -e "s|.${sfpm_build_prefix}|${staging}|g" "${metadata}/.SFPM-PREFIX-TEXT") + fi + + # TODO: Not implemented + #have_prefix_bin=$(wc -l ${metadata}/.SFPM-PREFIX-BIN | cut -d ' ' -f 1) + #if [[ ${have_prefix_bin} != 0 ]]; then + # msg2 "Relocating binary paths" + # while read filename + # do + # msg3 "${filename}" + # sfpm_file_relocate --bin --env "${env_name}" --path "${filename}" + # done < <(sed -e "s|.${sfpm_build_prefix}|${staging}|g" "${metadata}/.SFPM-PREFIX-BIN") + #fi + rsync -a "${staging}/" "${env_path}" + + rm -rf "${staging}" + rm -rf "${metadata}" +} + + +function sfpm_file_relocate() { + # TODO: binary relocation + # easy peasy... already wrote sfpm_rpath_nearest() + #local env_name="${1}" + #local env_path=$(sfpm_env_exists "${env_name}") + #local path="${2}" + + local path + local env_name + local mode_text=0 + local mode_bin=0 + + while (( "${#}" )); do + case "${1}" in + -t|--text) + mode_text=1 + shift + ;; + -b|--bin) + # TODO: Not implemented + mode_bin=1 + shift + ;; + -e|--env) + env_name="${2}" + shift 2 + ;; + -p|--path) + path="${2}" + shift 2 + ;; + --) + shift + break + ;; + -*|--*) + msg_error "Invalid argument: ${1}" >&2 + exit 1 + ;; + *) + # do nothing with positional args + shift + ;; + esac + done + + if (( ${mode_text} )) && (( ${mode_bin} )); then + msg_error "-t/--text and -b/--bin are mutually exclusive arguments" + exit 1 + fi + + local env_path=$(sfpm_env_exists "${env_name}") + if [[ ! ${env_path} ]]; then + msg_error "sfpm_file_relocate(): Environment does not exist: ${env_name}" + exit 1 + fi + + if [[ ! -f ${path} ]] && [[ ! -L ${path} ]]; then + msg_warn "sfpm_file_relocate(): ${path}: not a file or symbolic link" + return 1 + fi + + tmpfile=$(mktemp ${TMPDIR}/sfpm.relocate.XXXXXX) + if [[ ! -f ${tmpfile} ]]; then + msg_error "sfpm_file_relocate(): Failed to create temporary relocation file." + exit 1 + fi + + filemode=$(stat -c '%a' "${path}") + if (( ${mode_text} )); then + sed -e "s|${sfpm_build_prefix}|${env_path}|g" < "${path}" > "${tmpfile}" + elif (( ${mode_bin} )); then + # TODO + : + else + msg_error "sfpm_file_relocate(): Invalid modification mode. --text nor --bin specified" + exit 1 + fi + + chmod ${filemode} "${tmpfile}" + mv "${tmpfile}" "${path}" + + if (( $? )); then + msg_error "sfpm_file_relocate(): Failed to move temporary relocation file. Purging." + rm -f "${tmpfile}" + fi +} + diff --git a/share/sfpm/index.sh b/share/sfpm/index.sh new file mode 100644 index 0000000..3d7d112 --- /dev/null +++ b/share/sfpm/index.sh @@ -0,0 +1,129 @@ +function sfpm_index_lock() { + local hostname=$(hostname) + local lockfile=${sfpm_pkgdir}/.LOCK + + if [[ -f ${lockfile} ]]; then + local lockhost=$(awk -F':' '{ print $1 }' ${lockfile}) + local lockpid=$(awk -F':' '{ print $2 }' ${lockfile}) + + if [[ ! ${lockhost} ]] || [[ ! ${lockpid} ]]; then + msg_error "Invalid lock file detected (contents follow):" + msg_error "---- BEGIN ----" "$(cat ${lockfile})" "---- END ----" + exit 1 + fi + + msg_warn "Index locked by ${lockhost} with PID ${lockpid}" \ + "Waiting for lock to clear..." + + if [[ ${lockhost} == ${hostname} ]]; then + if ps -e ${lockpid} > /dev/null; then + msg_warn "PID on host is dead" \ + "Removing stale lock" + sfpm_index_unlock + fi + fi + + while true + do + [[ ! -f ${lockfile} ]] && break + sleep 1 + done + fi + + # Lock file format: + # hostname.domain.tld:1234 + echo "${hostname}:$$" > ${lockfile} +} + + +function sfpm_index_unlock() { + rm -f ${sfpm_pkgdir}/.LOCK +} + + +function sfpm_index_create() { + local index="${sfpm_pkgdir}/.SFPM-INDEX" + + msg "Creating package index" + sfpm_index_lock + > ${index} + + msg2 "Indexing" + # Aggregate list of packages, excluding hidden files + local pkgs=$(find ${sfpm_pkgdir} -type f -not -name '.*' 2>/dev/null) + + for pkg in ${pkgs} + do + local tarball=$(basename ${pkg}) + local no_ext=${tarball%%.[tTzZ]*} + local result=$(echo ${no_ext} | awk -F'-' '{ printf "%s,%s,%s\n", $1, $2, $3 }') + + msg3 "${tarball}" + echo "${tarball},${result}" >> "${index}" + done + + msg2 "Ordering index" + tmpfile=$(mktemp ${TMPDIR}/sfpm.index.sorted.XXXXXX) + sort -n "${index}" > ${tmpfile} + mv "${tmpfile}" "${index}" + + sfpm_index_unlock +} + +# Sometimes it's just easier... +# https://stackoverflow.com/questions/229551/how-to-check-if-a-string-contains-a-substring-in-bash#229585 +has_substr() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ] ;} ; } + +function sfpm_index_search() { + local index="${sfpm_pkgdir}/.SFPM-INDEX" + local name="${1}" # required + local version="${2}" # optional + local release="${3}" # optional + + if [[ ! ${2} ]]; then + verison= + fi + + if [[ ! ${3} ]]; then + release= + fi + + # TODO: Rewrite this... barf + local result= + while read line + do + pkg_tarball=$(echo ${line} | awk -F',' '{ print $1 }') + pkg_name=$(echo ${line} | awk -F',' '{ print $2 }') + pkg_version=$(echo ${line} | awk -F',' '{ print $3 }') + pkg_release=$(echo ${line} | awk -F',' '{ print $4 }') + + if has_substr "${name}" "${pkg_name}"; then + found_name=1 + fi + + if (( ${found_name} )); then + if has_substr "${version}" "${pkg_version}"; then + found_version=1 + fi + + if has_substr "${release}" "${pkg_release}"; then + found_release=1 # Barely... + fi + + if (( ${found_name} )); then + if [[ ${version} ]] && ! (( ${found_version} )); then + continue + fi + + if [[ ${release} ]] && ! (( ${found_release} )); then + continue; + fi + + result=$(sfpm_package_exists "${pkg_tarball}") + break + fi + fi + done < "${index}" + echo "${result}" +} + diff --git a/share/sfpm/msg.sh b/share/sfpm/msg.sh new file mode 100644 index 0000000..3f6efb9 --- /dev/null +++ b/share/sfpm/msg.sh @@ -0,0 +1,29 @@ +function _msg() { + printf "$@" +} + +function plain() { + _msg " %s\n" "$@" +} + +function msg() { + _msg "==> %s\n" "$@" +} + + +function msg2() { + _msg " -> %s\n" "$@" +} + + +function msg3() { + _msg " . %s\n" "$@" +} + +function msg_error() { + _msg "[ERROR] %s\n" "$@" >&2 +} + +function msg_warn() { + _msg "[WARNING] %s\n" "$@" >&2 +} diff --git a/share/sfpm/pkg_read.sh b/share/sfpm/pkg_read.sh new file mode 100644 index 0000000..68f073a --- /dev/null +++ b/share/sfpm/pkg_read.sh @@ -0,0 +1,37 @@ +function sfpm_package_exists() { + # Returns empty string on failure (implicit) + local path="${sfpm_pkgdir}/${1}" + if [[ ! -f ${path} ]]; then + return 1 + fi + echo ${path} +} + +function sfpm_package_verify() { + local name="$(basename ${1})" + local path="${sfpm_pkgdir}/${name}" + + if [[ ! -f $(sfpm_package_exists ${name}) ]]; then + msg_error "sfpm_package_verify(): Package does not exist: ${path}" + exit 1 + fi + + local vdir=$(mktemp -d ${TMPDIR}/sfpm.verify.XXXXXX) + pushd "${vdir}" + msg2 "Verifying Package: ${name}" + tar -xf "${path}" + + while read line + do + msg3 "${line}" + done < <(sha256sum -c .SFPM-MANIFEST) + + while read line + do + msg3 "${line}" + done < <(find . -type f | xargs file | grep ELF | awk -F':' '{ print $1 }' | xargs readelf -d | grep rpath) + popd + if [[ -d ${vdir} ]] && [[ ${vdir} == *${TMPDIR}* ]]; then + rm -rf "${vdir}" + fi +} diff --git a/share/sfpm/sfpm.sh b/share/sfpm/sfpm.sh new file mode 100644 index 0000000..1e86c5c --- /dev/null +++ b/share/sfpm/sfpm.sh @@ -0,0 +1,88 @@ +sfpm_bindir=${sfpm_root}/bin +sfpm_sbindir=${sfpm_root}/sbin +sfpm_sysconfdir=${sfpm_root}/etc +sfpm_libdir=${sfpm_root}/lib +sfpm_libexecdir=${sfpm_root}/libexec +sfpm_datarootdir=${sfpm_root}/share +sfpm_datadir=${sfpm_datarootdir} # alias for GNU sake +sfpm_docdir=${sfpm_datarootdir}/doc +sfpm_mandir=${sfpm_datadir}/man +sfpm_infodir=${sfpm_datadir}/info +sfpm_localstatedir=${sfpm_root}/var +sfpm_pkgdir=${sfpm_localstatedir}/lib/pkgs +sfpm_cache=${sfpm_localstatedir}/cache +sfpm_srcdir=${sfpm_cache}/src +sfpm_runstatedir=${sfpm_localstatedir}/run +sfpm_includedir=${sfpm_root}/include +sfpm_tmpdir=${sfpm_root}/tmp +sfpm_envdir=${sfpm_root}/envs + +# Set default prefix. It happens to be the name of the variable... +sfpm_build_prefix="/sfpm_build_prefix" +sfpm_build_cflags="-I${sfpm_includedir}" +sfpm_build_ldflags="-L${sfpm_libdir} -Wl,-rpath="'\$$ORIGIN'/../lib + +sfpm_INTERNAL_PATHS=( + sfpm_root + sfpm_bindir + sfpm_sbindir + sfpm_sysconfdir + sfpm_libdir + sfpm_libexecdir + sfpm_datarootdir + sfpm_datadir + sfpm_docdir + sfpm_mandir + sfpm_infodir + sfpm_localstatedir + sfpm_pkgdir + sfpm_cache + sfpm_srcdir + sfpm_runstatedir + sfpm_includedir + sfpm_tmpdir + sfpm_envdir +) + + +export TMPDIR="${sfpm_tmpdir}" + + +function sfpm_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 sfpm_gen_sysroot() { + local env_path="${1}" + + for envpath in "${sfpm_INTERNAL_PATHS[@]}" + do + p="${!envpath}" + + # For environment creation, override sysroot with new root path + if [[ ${env_path} ]]; then + # Environments will not be chained + if [[ ${envpath} == sfpm_envdir ]]; then + continue + fi + p="${p/${sfpm_root}/${env_path}}" + fi + + [[ ! -d ${p} ]] && mkdir -p "${p}" + done +} + + + |