#!/bin/bash # MKit - simple install helper # See LICENSE file for copyright and license details. mkit_import ini mkit_import facts __build1() { # # Process one skeleton $1 of type $3 (or guessed) to path $2 # local srcpath=$1 # skeleton path local dstpath=$2 # destination meaty animal path local ftype=$3 # file/builder type test -n "$dstpath" || dstpath=${srcpath%.skel} test -n "$ftype" || ftype=$(__guess_ftype "$dstpath") debug_var srcpath dstpath ftype <"$srcpath" __build1_ftype "$ftype" >"$dstpath" __rec_built "$dstpath" } __build1_ftype() { # # Build a file of type $1; fom stdin to stdout # local ftype=$1 # file/builder type case $ftype in MKIT_COMMON) __expand_macros "macros" ;; rpmstuff) __expand_macros "macros" "rpmstuff:macros" ;; debstuff) __expand_macros "macros" "debstuff:macros" ;; *) die "unknown file type: $ftype" ;; esac } __expand_line() { # # Expand macro from $MacroMap in single line $1 # # If macro value has multiple lines, repeat original line with # different substitution. # # E.g. if macro value is "foo\nbar" and macro name is __FOO__, # line `see: "__FOO__"` will expand to two lines: `see: "foo"` # and `see: "bar"`. # local line=$1 # line to process local mname # macro name local mvline # line of macro value local xline # expanded line xline=$line for mname in "${!MacroMap[@]}"; do if ! test "${line//$mname}" == "$line"; then xline=$( while IFS= read -r mvline; do echo "${line//$mname/$mvline}" done <<<"${MacroMap[$mname]}" ) fi line=$xline done echo "$xline" return 1 } __expand_macros() { # # Read stdin, expanding macros from sections $@ # local section # each section to expand macros from local line # each line on stdin local mname # each macro name local -A MacroMap # macro value map MacroMap[__MKIT_MKIT_VERSION__]=$MKIT_VERSION MacroMap[__MKIT_PROJ_NAME__]=$(ini 1value project:name) MacroMap[__MKIT_PROJ_CODENAME__]=$(ini 1value project:codename) MacroMap[__MKIT_PROJ_LICENSE__]=$(ini 1value project:license) MacroMap[__MKIT_PROJ_PKGNAME__]=$(ini 1value project:pkgname) MacroMap[__MKIT_PROJ_TAGLINE__]=$(ini 1value project:tagline) MacroMap[__MKIT_PROJ_MAINTAINER__]=$(ini 1value project:maintainer) MacroMap[__MKIT_PROJ_VCS_BROWSER__]=$(ini 1value project:vcs_browser) MacroMap[__MKIT_PROJ_GIT_LASTHASH__]=$(__cached git_lasthash) MacroMap[__MKIT_PROJ_GIT_LASTSUMMARY__]=$(__cached git_lastsummary) MacroMap[__MKIT_PROJ_VERSION__]=$(__cached semver) for section in "$@"; do for mname in $(ini lskeys "$section"); do MacroMap[$mname]=$(ini values "$section:$mname") done done debug_var MacroMap while IFS= read -r line; do __expand_line "$line" done } __guess_ftype() { # # Guess file type from destination path $1 # local dstpath=$1 # destination path case $dstpath in *) echo MKIT_COMMON ;; esac } __qfs() { # # Quote for our sed scipt's RHS # sed ' s:\\:\\\\:g s:|:\\|:g ' } __cached() { # # Cached value $1 of function $1() # # In order to support git-less builds, some values might be cached # in $MKIT_LOCAL. This function gets file $1 from that cache (cache # hit) or re-creates it (cache miss), but prints its body in either # case. # # The command to re-create file is the same as the key (ie. no # arguments). # local name=$1 __local_get "$name" && return 0 "$name" | __local_putb "$name" __local_get "$name" } __local_putb() { # # Make file $1 in $MKIT_LOCAL from stdin and mark as built # local fpath=$1 __local_put "$fpath" && __rec_built "$MKIT_LOCAL/$fpath" } __local_put() { # # Make file $1 in $MKIT_LOCAL from stdin # local fpath="$MKIT_LOCAL/$1" { mkdir -p "${fpath%/*}" && cat >"$fpath"; } \ || die "cannot write to local cache: $fpath" } __local_get() { # # Read file $1 in $MKIT_LOCAL # local fpath="$MKIT_LOCAL/$1" cat "$fpath" 2>/dev/null } __rec_built() { # # Record file $1 for deletion on `clean` # local file=$1 mkdir -p "$MKIT_LOCAL" echo "1:$file" >> "$MKIT_LOCAL/built.lst" } _mkit_data() { # # Build sampler showing all macro values # local macro local section local sections sections=() ini lskeys macros | grep -q . && sections=(macros) sections+=( $(ini lssect | grep ':macros$') ) { echo "(builtin):" echo " x_MKIT_MKIT_VERSION__ => '__MKIT_MKIT_VERSION__'" echo " x_MKIT_PROJ_NAME__ => '__MKIT_PROJ_NAME__'" echo " x_MKIT_PROJ_CODENAME__ => '__MKIT_PROJ_CODENAME__'" echo " x_MKIT_PROJ_LICENSE__ => '__MKIT_PROJ_LICENSE__'" echo " x_MKIT_PROJ_PKGNAME__ => '__MKIT_PROJ_PKGNAME__'" echo " x_MKIT_PROJ_TAGLINE__ => '__MKIT_PROJ_TAGLINE__'" echo " x_MKIT_PROJ_MAINTAINER__ => '__MKIT_PROJ_MAINTAINER__'" echo " x_MKIT_PROJ_VCS_BROWSER__ => '__MKIT_PROJ_VCS_BROWSER__'" echo " x_MKIT_PROJ_GIT_LASTHASH__ => '__MKIT_PROJ_GIT_LASTHASH__'" echo " x_MKIT_PROJ_GIT_LASTSUMMARY__ => '__MKIT_PROJ_GIT_LASTSUMMARY__'" echo " x_MKIT_PROJ_VERSION__ => '__MKIT_PROJ_VERSION__'" for section in "${sections[@]}"; do echo "$section:" for macro in $(ini lskeys "$section"); do echo " x${macro:1} => '$macro'" done done } \ | __expand_macros "MKIT_BUILTIN" "${sections[@]}" \ | sed '/^ x/ s|x|_|' } build() { # # Add meat to all skeletons # local srcpath # each source path find . -type f -name '*.skel' \ | while read -r srcpath; do __build1 "$srcpath" done } clean() { # # Clean up tree after building # local path local line local depth test -f "$MKIT_LOCAL/built.lst" || return 0 { cat "$MKIT_LOCAL/built.lst" echo "1:$MKIT_LOCAL/built.lst" echo "1:$MKIT_LOCAL" } \ | grep -v -e '\.\.' -e ^/ -e '^~' \ | while IFS=: read -r depth path; do test -e "$path" || continue case $depth in 1) warn "removing: $path" test -d "$path" \ && rmdir -p --ignore-fail-on-non-empty "$path" test -f "$path" && rm "$path" ;; r) warn "removing recursively: $path" rm -r "$path" ;; *) warn "invalid built.lst format!" ;; esac done true } debstuff() { # # Build Debian stuff (eamed tarball, debian dir) # local version # package version local debian_skel # 'debian' folder skeleton local dfsrc # each source file from ^^ local dftgt # each built packaging file version=$(__cached semver) # tarball - we should already have by means of 'dist' # mv "${MKIT_PROJ_PKGNAME}-$version.tar.gz" \ "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" \ || die "could not rename tarball" __rec_built "${MKIT_PROJ_PKGNAME}_$version.orig.tar.gz" # read content of each mandatory file from debian_skel # debian_skel=$(ini 1value dist:debstuff) test -n "$debian_skel" || die "dist:debstuff not specified" test -d "$debian_skel" || die "debian directory template found: $debian_skel" mkdir -p debian/source find "$debian_skel" -type f \ | while read -r dfsrc; do dftgt="debian/${dfsrc#$debian_skel}" mkdir -p "$(dirname "$dftgt")" __build1 "$dfsrc" "$dftgt" debstuff done __rec_built debian } dist() { # # Create distributable tarball # #FIXME: lacking Makefile skills, we do this step twice for # rpmstuff, hence -f hack for gzip # local version # tarball version local git_lasthash # last git commit hash local dirname # directory and tarball name version=$(semver) dirname=$MKIT_PROJ_PKGNAME-$version git_lasthash=$(git_lasthash) mkdir -p "$dirname" ini values "dist:tarball" | xargs -I DIST_ITEM cp -R DIST_ITEM "$dirname" mkdir -p "$dirname/.mkit" echo -n "$version" > "$dirname/.mkit/semver" echo -n "$git_lasthash" > "$dirname/.mkit/git_lasthash" tar -cf "$dirname.tar" "$dirname" gzip -f "$dirname.tar" # see above FIXME __rec_built "$dirname.tar.gz" rm -rf "$dirname" } rpmstuff() { # # Build specfile # local specname=$MKIT_PROJ_PKGNAME.spec # .spec filename local specsrc # source of specfile specsrc="$(ini 1value "dist:rpmstuff")" test -n "$specsrc" || die "dist:rpmstuff not specified" test -f "$specsrc" || die "specfile template not found: $specsrc" __build1 "$specsrc" "$specname" rpmstuff }